@askexenow/exe-os 0.9.79 → 0.9.81
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/dist/bin/agentic-ontology-backfill.js +2 -2
- package/dist/bin/agentic-reflection-backfill.js +2 -2
- package/dist/bin/agentic-semantic-label.js +2 -2
- package/dist/bin/backfill-conversations.js +2 -2
- package/dist/bin/backfill-responses.js +2 -2
- package/dist/bin/backfill-vectors.js +2 -2
- package/dist/bin/bulk-sync-postgres.js +2 -2
- package/dist/bin/cleanup-stale-review-tasks.js +2 -2
- package/dist/bin/cli.js +6 -3
- package/dist/bin/customer-readiness.js +27 -0
- package/dist/bin/exe-agent.js +2 -2
- package/dist/bin/exe-assign.js +2 -2
- package/dist/bin/exe-boot.js +2 -2
- package/dist/bin/exe-call.js +2 -2
- package/dist/bin/exe-cloud.js +2 -2
- package/dist/bin/exe-dispatch.js +2 -2
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +2 -2
- package/dist/bin/exe-forget.js +2 -2
- package/dist/bin/exe-gateway.js +2 -2
- package/dist/bin/exe-heartbeat.js +2 -2
- package/dist/bin/exe-kill.js +2 -2
- package/dist/bin/exe-launch-agent.js +2 -2
- package/dist/bin/exe-new-employee.js +2 -2
- package/dist/bin/exe-pending-messages.js +2 -2
- package/dist/bin/exe-pending-notifications.js +2 -2
- package/dist/bin/exe-pending-reviews.js +2 -2
- package/dist/bin/exe-rename.js +2 -2
- package/dist/bin/exe-review.js +2 -2
- package/dist/bin/exe-search.js +2 -2
- package/dist/bin/exe-session-cleanup.js +2 -2
- package/dist/bin/exe-start-codex.js +2 -2
- package/dist/bin/exe-start-opencode.js +2 -2
- package/dist/bin/exe-status.js +2 -2
- package/dist/bin/exe-support.js +4 -1
- package/dist/bin/exe-team.js +2 -2
- package/dist/bin/git-sweep.js +2 -2
- package/dist/bin/graph-backfill.js +2 -2
- package/dist/bin/graph-export.js +2 -2
- package/dist/bin/intercom-check.js +2 -2
- package/dist/bin/scan-tasks.js +2 -2
- package/dist/bin/setup.js +2 -2
- package/dist/bin/shard-migrate.js +2 -2
- package/dist/gateway/index.js +2 -2
- package/dist/hooks/bug-report-worker.js +2 -2
- package/dist/hooks/codex-stop-task-finalizer.js +2 -2
- package/dist/hooks/commit-complete.js +2 -2
- package/dist/hooks/error-recall.js +2 -2
- package/dist/hooks/ingest.js +2 -2
- package/dist/hooks/instructions-loaded.js +2 -2
- package/dist/hooks/notification.js +2 -2
- package/dist/hooks/post-compact.js +2 -2
- package/dist/hooks/post-tool-combined.js +2 -2
- package/dist/hooks/pre-compact.js +2 -2
- package/dist/hooks/pre-tool-use.js +2 -2
- package/dist/hooks/prompt-submit.js +2 -2
- package/dist/hooks/session-end.js +2 -2
- package/dist/hooks/session-start.js +2 -2
- package/dist/hooks/stop.js +2 -2
- package/dist/hooks/subagent-stop.js +2 -2
- package/dist/hooks/summary-worker.js +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/employee-templates.js +2 -2
- package/dist/lib/exe-daemon.js +1511 -615
- package/dist/lib/hybrid-search.js +2 -2
- package/dist/lib/schedules.js +2 -2
- package/dist/lib/store.js +2 -2
- package/dist/mcp/server.js +1551 -424
- package/dist/runtime/index.js +2 -2
- package/dist/tui/App.js +2 -2
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -455,8 +455,8 @@ function findPackageRoot() {
|
|
|
455
455
|
function getAvailableMemoryGB() {
|
|
456
456
|
if (process.platform === "darwin") {
|
|
457
457
|
try {
|
|
458
|
-
const { execSync:
|
|
459
|
-
const vmstat =
|
|
458
|
+
const { execSync: execSync15 } = __require("child_process");
|
|
459
|
+
const vmstat = execSync15("vm_stat", { encoding: "utf8" });
|
|
460
460
|
const pageSize = 16384;
|
|
461
461
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
462
462
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -710,15 +710,15 @@ function isDaemonTooYoung() {
|
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
712
|
async function retryThenRestart(doRequest, label) {
|
|
713
|
-
const
|
|
714
|
-
if (!
|
|
713
|
+
const result3 = await doRequest();
|
|
714
|
+
if (!result3.error) {
|
|
715
715
|
_consecutiveFailures = 0;
|
|
716
|
-
return
|
|
716
|
+
return result3;
|
|
717
717
|
}
|
|
718
718
|
_consecutiveFailures++;
|
|
719
719
|
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
720
720
|
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
721
|
-
process.stderr.write(`[exed-client] ${label} failed (${
|
|
721
|
+
process.stderr.write(`[exed-client] ${label} failed (${result3.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
722
722
|
`);
|
|
723
723
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
724
724
|
if (!_connected) {
|
|
@@ -734,7 +734,7 @@ async function retryThenRestart(doRequest, label) {
|
|
|
734
734
|
if (isDaemonTooYoung()) {
|
|
735
735
|
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
736
736
|
`);
|
|
737
|
-
return { error:
|
|
737
|
+
return { error: result3.error };
|
|
738
738
|
}
|
|
739
739
|
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
740
740
|
`);
|
|
@@ -770,20 +770,20 @@ async function embedViaClient(text3, priority = "high") {
|
|
|
770
770
|
if (!_connected) return null;
|
|
771
771
|
}
|
|
772
772
|
}
|
|
773
|
-
const
|
|
773
|
+
const result3 = await retryThenRestart(
|
|
774
774
|
() => sendRequest([text3], priority),
|
|
775
775
|
"Embed"
|
|
776
776
|
);
|
|
777
|
-
return !
|
|
777
|
+
return !result3.error && result3.vectors?.[0] ? result3.vectors[0] : null;
|
|
778
778
|
}
|
|
779
779
|
async function embedBatchViaClient(texts, priority = "high") {
|
|
780
780
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
781
781
|
_requestCount++;
|
|
782
|
-
const
|
|
782
|
+
const result3 = await retryThenRestart(
|
|
783
783
|
() => sendRequest(texts, priority),
|
|
784
784
|
"Batch embed"
|
|
785
785
|
);
|
|
786
|
-
return !
|
|
786
|
+
return !result3.error && result3.vectors ? result3.vectors : null;
|
|
787
787
|
}
|
|
788
788
|
function disconnectClient() {
|
|
789
789
|
if (_socket) {
|
|
@@ -876,10 +876,10 @@ async function disposeEmbedder() {
|
|
|
876
876
|
async function embedDirect(text3) {
|
|
877
877
|
const llamaCpp = await import("node-llama-cpp");
|
|
878
878
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
879
|
-
const { existsSync:
|
|
880
|
-
const
|
|
881
|
-
const modelPath =
|
|
882
|
-
if (!
|
|
879
|
+
const { existsSync: existsSync42 } = await import("fs");
|
|
880
|
+
const path55 = await import("path");
|
|
881
|
+
const modelPath = path55.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
882
|
+
if (!existsSync42(modelPath)) {
|
|
883
883
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
884
884
|
}
|
|
885
885
|
const llama = await llamaCpp.getLlama();
|
|
@@ -3721,8 +3721,8 @@ function deriveMachineKey() {
|
|
|
3721
3721
|
}
|
|
3722
3722
|
function readMachineId() {
|
|
3723
3723
|
try {
|
|
3724
|
-
const { readFileSync:
|
|
3725
|
-
return
|
|
3724
|
+
const { readFileSync: readFileSync37 } = __require("fs");
|
|
3725
|
+
return readFileSync37("/etc/machine-id", "utf-8").trim();
|
|
3726
3726
|
} catch {
|
|
3727
3727
|
return "";
|
|
3728
3728
|
}
|
|
@@ -4181,8 +4181,8 @@ async function findScopedDuplicate(input) {
|
|
|
4181
4181
|
args.push(input.excludeId);
|
|
4182
4182
|
}
|
|
4183
4183
|
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
4184
|
-
const
|
|
4185
|
-
return
|
|
4184
|
+
const result3 = await client.execute({ sql, args });
|
|
4185
|
+
return result3.rows[0]?.id ? String(result3.rows[0].id) : null;
|
|
4186
4186
|
}
|
|
4187
4187
|
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4188
4188
|
try {
|
|
@@ -4781,7 +4781,7 @@ var init_platform_procedures = __esm({
|
|
|
4781
4781
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4782
4782
|
domain: "support",
|
|
4783
4783
|
priority: "p0",
|
|
4784
|
-
content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails,
|
|
4784
|
+
content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
|
|
4785
4785
|
},
|
|
4786
4786
|
// --- Operations ---
|
|
4787
4787
|
{
|
|
@@ -4863,7 +4863,7 @@ var init_platform_procedures = __esm({
|
|
|
4863
4863
|
title: "MCP tools \u2014 identity, behavior, and decisions",
|
|
4864
4864
|
domain: "tool-use",
|
|
4865
4865
|
priority: "p1",
|
|
4866
|
-
content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query. create_bug_report: customer-facing bug/support intake; use whenever an Exe OS bug or emergency hotfix is encountered so the report reaches AskExe directly. Customers only get report access; internal list/get/triage support tools are AskExe-only. If a customer-side agent cannot send upstream,
|
|
4866
|
+
content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query. create_bug_report: customer-facing bug/support intake; use whenever an Exe OS bug or emergency hotfix is encountered so the report reaches AskExe directly. support_health: check local support readiness and AskExe support server health without filing a report. support_test: file a safe end-to-end smoke report. Customers only get report/test access; internal list/get/triage support tools are AskExe-only. If a customer-side agent cannot send upstream, call support_test first; only fall back to the terminal command `exe-os support test` when MCP is disconnected."
|
|
4867
4867
|
},
|
|
4868
4868
|
{
|
|
4869
4869
|
title: "MCP tools \u2014 communication and messaging",
|
|
@@ -4913,11 +4913,11 @@ __export(global_procedures_exports, {
|
|
|
4913
4913
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4914
4914
|
async function loadGlobalProcedures() {
|
|
4915
4915
|
const client = getClient();
|
|
4916
|
-
const
|
|
4916
|
+
const result3 = await client.execute({
|
|
4917
4917
|
sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
4918
4918
|
args: []
|
|
4919
4919
|
});
|
|
4920
|
-
const allRows =
|
|
4920
|
+
const allRows = result3.rows;
|
|
4921
4921
|
const customerOnly = allRows.filter((p) => !PLATFORM_PROCEDURE_TITLES.has(p.title));
|
|
4922
4922
|
if (customerOnly.length > 0) {
|
|
4923
4923
|
_customerCache = customerOnly.map((p) => `### ${p.title}
|
|
@@ -4953,12 +4953,12 @@ async function storeGlobalProcedure(input) {
|
|
|
4953
4953
|
async function deactivateGlobalProcedure(id) {
|
|
4954
4954
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4955
4955
|
const client = getClient();
|
|
4956
|
-
const
|
|
4956
|
+
const result3 = await client.execute({
|
|
4957
4957
|
sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
4958
4958
|
args: [now, id]
|
|
4959
4959
|
});
|
|
4960
4960
|
await loadGlobalProcedures();
|
|
4961
|
-
return
|
|
4961
|
+
return result3.rowsAffected > 0;
|
|
4962
4962
|
}
|
|
4963
4963
|
var _customerCache, _cacheLoaded, _platformCache;
|
|
4964
4964
|
var init_global_procedures = __esm({
|
|
@@ -5102,8 +5102,8 @@ async function searchMemoryCards(queryText, agentId, options) {
|
|
|
5102
5102
|
}
|
|
5103
5103
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
5104
5104
|
args.push(limit);
|
|
5105
|
-
const
|
|
5106
|
-
return
|
|
5105
|
+
const result3 = await getClient().execute({ sql, args });
|
|
5106
|
+
return result3.rows.map((row) => ({
|
|
5107
5107
|
id: `card:${String(row.id)}`,
|
|
5108
5108
|
agent_id: String(row.agent_id),
|
|
5109
5109
|
agent_role: "memory_card",
|
|
@@ -5638,7 +5638,7 @@ async function flushBatch() {
|
|
|
5638
5638
|
const hasVector = row.vector !== null;
|
|
5639
5639
|
const taskId = row.task_id ?? null;
|
|
5640
5640
|
const importance = row.importance ?? 5;
|
|
5641
|
-
const
|
|
5641
|
+
const status2 = row.status ?? "active";
|
|
5642
5642
|
const confidence = row.confidence ?? 0.7;
|
|
5643
5643
|
const lastAccessed = row.last_accessed ?? row.timestamp;
|
|
5644
5644
|
const workspaceId = row.workspace_id ?? null;
|
|
@@ -5710,7 +5710,7 @@ async function flushBatch() {
|
|
|
5710
5710
|
row.version,
|
|
5711
5711
|
taskId,
|
|
5712
5712
|
importance,
|
|
5713
|
-
|
|
5713
|
+
status2,
|
|
5714
5714
|
confidence,
|
|
5715
5715
|
lastAccessed,
|
|
5716
5716
|
workspaceId,
|
|
@@ -5880,8 +5880,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
5880
5880
|
args.push(vectorToBlob(queryVector));
|
|
5881
5881
|
sql += ` LIMIT ?`;
|
|
5882
5882
|
args.push(limit);
|
|
5883
|
-
const
|
|
5884
|
-
return
|
|
5883
|
+
const result3 = await client.execute({ sql, args });
|
|
5884
|
+
return result3.rows.map((row) => ({
|
|
5885
5885
|
id: row.id,
|
|
5886
5886
|
agent_id: row.agent_id,
|
|
5887
5887
|
agent_role: row.agent_role,
|
|
@@ -5915,14 +5915,14 @@ async function attachDocumentMetadata(records) {
|
|
|
5915
5915
|
try {
|
|
5916
5916
|
const client = getClient();
|
|
5917
5917
|
const placeholders = docIds.map(() => "?").join(",");
|
|
5918
|
-
const
|
|
5918
|
+
const result3 = await client.execute({
|
|
5919
5919
|
sql: `SELECT id, filename, mime, source_type, uploaded_at
|
|
5920
5920
|
FROM documents
|
|
5921
5921
|
WHERE id IN (${placeholders})`,
|
|
5922
5922
|
args: docIds
|
|
5923
5923
|
});
|
|
5924
5924
|
const byId = /* @__PURE__ */ new Map();
|
|
5925
|
-
for (const row of
|
|
5925
|
+
for (const row of result3.rows) {
|
|
5926
5926
|
const id = row.id;
|
|
5927
5927
|
byId.set(id, {
|
|
5928
5928
|
document_id: id,
|
|
@@ -5945,19 +5945,19 @@ async function flushTier3(agentId, options) {
|
|
|
5945
5945
|
const maxAge = options?.maxAgeHours ?? 72;
|
|
5946
5946
|
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
5947
5947
|
if (options?.dryRun) {
|
|
5948
|
-
const
|
|
5948
|
+
const result4 = await client.execute({
|
|
5949
5949
|
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
5950
5950
|
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5951
5951
|
args: [agentId, cutoff]
|
|
5952
5952
|
});
|
|
5953
|
-
return { archived: Number(
|
|
5953
|
+
return { archived: Number(result4.rows[0]?.cnt ?? 0) };
|
|
5954
5954
|
}
|
|
5955
|
-
const
|
|
5955
|
+
const result3 = await client.execute({
|
|
5956
5956
|
sql: `UPDATE memories SET status = 'archived'
|
|
5957
5957
|
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5958
5958
|
args: [agentId, cutoff]
|
|
5959
5959
|
});
|
|
5960
|
-
return { archived:
|
|
5960
|
+
return { archived: result3.rowsAffected };
|
|
5961
5961
|
}
|
|
5962
5962
|
async function disposeStore() {
|
|
5963
5963
|
if (_flushTimer !== null) {
|
|
@@ -5975,11 +5975,11 @@ function vectorToBlob(vector) {
|
|
|
5975
5975
|
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
5976
5976
|
return JSON.stringify(Array.from(f32));
|
|
5977
5977
|
}
|
|
5978
|
-
async function updateMemoryStatus(id,
|
|
5978
|
+
async function updateMemoryStatus(id, status2) {
|
|
5979
5979
|
const client = getClient();
|
|
5980
5980
|
await client.execute({
|
|
5981
5981
|
sql: `UPDATE memories SET status = ? WHERE id = ?`,
|
|
5982
|
-
args: [
|
|
5982
|
+
args: [status2, id]
|
|
5983
5983
|
});
|
|
5984
5984
|
}
|
|
5985
5985
|
function reserveVersions(count) {
|
|
@@ -5992,11 +5992,11 @@ function reserveVersions(count) {
|
|
|
5992
5992
|
async function getMemoryCardinality(agentId) {
|
|
5993
5993
|
try {
|
|
5994
5994
|
const client = getClient();
|
|
5995
|
-
const
|
|
5995
|
+
const result3 = await client.execute({
|
|
5996
5996
|
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
5997
5997
|
args: [agentId]
|
|
5998
5998
|
});
|
|
5999
|
-
return Number(
|
|
5999
|
+
return Number(result3.rows[0]?.cnt) || 0;
|
|
6000
6000
|
} catch {
|
|
6001
6001
|
return 0;
|
|
6002
6002
|
}
|
|
@@ -6531,9 +6531,9 @@ __export(graph_query_exports, {
|
|
|
6531
6531
|
async function getEntityByName(client, name, type) {
|
|
6532
6532
|
const sql = type ? `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) AND type = ? LIMIT 1` : `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) LIMIT 1`;
|
|
6533
6533
|
const args = type ? [name, type] : [name];
|
|
6534
|
-
const
|
|
6535
|
-
if (
|
|
6536
|
-
const row =
|
|
6534
|
+
const result3 = await client.execute({ sql, args });
|
|
6535
|
+
if (result3.rows.length === 0) return null;
|
|
6536
|
+
const row = result3.rows[0];
|
|
6537
6537
|
return {
|
|
6538
6538
|
id: String(row.id),
|
|
6539
6539
|
name: String(row.name),
|
|
@@ -6544,11 +6544,11 @@ async function getEntityByName(client, name, type) {
|
|
|
6544
6544
|
};
|
|
6545
6545
|
}
|
|
6546
6546
|
async function searchEntities(client, query, limit = 10) {
|
|
6547
|
-
const
|
|
6547
|
+
const result3 = await client.execute({
|
|
6548
6548
|
sql: `SELECT * FROM entities WHERE LOWER(name) LIKE ? ORDER BY last_seen DESC LIMIT ?`,
|
|
6549
6549
|
args: [`%${query.toLowerCase()}%`, limit]
|
|
6550
6550
|
});
|
|
6551
|
-
return
|
|
6551
|
+
return result3.rows.map((row) => ({
|
|
6552
6552
|
id: String(row.id),
|
|
6553
6553
|
name: String(row.name),
|
|
6554
6554
|
type: String(row.type),
|
|
@@ -6588,8 +6588,8 @@ async function getRelationships(client, entityId2, options) {
|
|
|
6588
6588
|
args.push(options.type);
|
|
6589
6589
|
}
|
|
6590
6590
|
sql += ` ORDER BY r.weight DESC, r.timestamp DESC`;
|
|
6591
|
-
const
|
|
6592
|
-
return
|
|
6591
|
+
const result3 = await client.execute({ sql, args });
|
|
6592
|
+
return result3.rows.map((row) => ({
|
|
6593
6593
|
id: String(row.id),
|
|
6594
6594
|
sourceEntityId: String(row.source_entity_id),
|
|
6595
6595
|
targetEntityId: String(row.target_entity_id),
|
|
@@ -6604,7 +6604,7 @@ async function getRelationships(client, entityId2, options) {
|
|
|
6604
6604
|
}));
|
|
6605
6605
|
}
|
|
6606
6606
|
async function traverseChain(client, startEntityId, relationshipType, maxDepth = 3) {
|
|
6607
|
-
const
|
|
6607
|
+
const result3 = await client.execute({
|
|
6608
6608
|
sql: `WITH RECURSIVE chain(entity_id, depth, rel_type) AS (
|
|
6609
6609
|
SELECT ?, 0, ''
|
|
6610
6610
|
UNION ALL
|
|
@@ -6619,7 +6619,7 @@ async function traverseChain(client, startEntityId, relationshipType, maxDepth =
|
|
|
6619
6619
|
ORDER BY chain.depth ASC`,
|
|
6620
6620
|
args: [startEntityId, relationshipType, maxDepth]
|
|
6621
6621
|
});
|
|
6622
|
-
return
|
|
6622
|
+
return result3.rows.map((row) => ({
|
|
6623
6623
|
entity: {
|
|
6624
6624
|
id: String(row.id),
|
|
6625
6625
|
name: String(row.name),
|
|
@@ -6633,7 +6633,7 @@ async function traverseChain(client, startEntityId, relationshipType, maxDepth =
|
|
|
6633
6633
|
}));
|
|
6634
6634
|
}
|
|
6635
6635
|
async function getEntityNeighbors(client, entityId2, maxHops = 2) {
|
|
6636
|
-
const
|
|
6636
|
+
const result3 = await client.execute({
|
|
6637
6637
|
sql: `WITH RECURSIVE neighborhood(entity_id, depth, rel_type, rel_confidence) AS (
|
|
6638
6638
|
SELECT ?, 0, '', 1.0
|
|
6639
6639
|
UNION ALL
|
|
@@ -6655,7 +6655,7 @@ async function getEntityNeighbors(client, entityId2, maxHops = 2) {
|
|
|
6655
6655
|
LIMIT 50`,
|
|
6656
6656
|
args: [entityId2, maxHops]
|
|
6657
6657
|
});
|
|
6658
|
-
return
|
|
6658
|
+
return result3.rows.map((row) => ({
|
|
6659
6659
|
entity: {
|
|
6660
6660
|
id: String(row.id),
|
|
6661
6661
|
name: String(row.name),
|
|
@@ -6692,7 +6692,7 @@ async function getEntityTimeline(client, entityId2, since) {
|
|
|
6692
6692
|
sinceClause = " AND r.timestamp >= ?";
|
|
6693
6693
|
args.push(since.toISOString());
|
|
6694
6694
|
}
|
|
6695
|
-
const
|
|
6695
|
+
const result3 = await client.execute({
|
|
6696
6696
|
sql: `SELECT r.*, s.name as source_name, t.name as target_name
|
|
6697
6697
|
FROM relationships r
|
|
6698
6698
|
JOIN entities s ON r.source_entity_id = s.id
|
|
@@ -6701,7 +6701,7 @@ async function getEntityTimeline(client, entityId2, since) {
|
|
|
6701
6701
|
ORDER BY r.timestamp DESC`,
|
|
6702
6702
|
args
|
|
6703
6703
|
});
|
|
6704
|
-
return
|
|
6704
|
+
return result3.rows.map((row) => ({
|
|
6705
6705
|
relationship: {
|
|
6706
6706
|
id: String(row.id),
|
|
6707
6707
|
sourceEntityId: String(row.source_entity_id),
|
|
@@ -6735,7 +6735,7 @@ async function getRelationshipFrequency(client, entityId2, options) {
|
|
|
6735
6735
|
default:
|
|
6736
6736
|
bucketExpr = `strftime('%Y-%m-%d', r.timestamp)`;
|
|
6737
6737
|
}
|
|
6738
|
-
const
|
|
6738
|
+
const result3 = await client.execute({
|
|
6739
6739
|
sql: `SELECT ${bucketExpr} as bucket, COUNT(*) as cnt, r.type as rel_type
|
|
6740
6740
|
FROM relationships r
|
|
6741
6741
|
WHERE (r.source_entity_id = ? OR r.target_entity_id = ?)${typeClause}
|
|
@@ -6743,7 +6743,7 @@ async function getRelationshipFrequency(client, entityId2, options) {
|
|
|
6743
6743
|
ORDER BY bucket DESC`,
|
|
6744
6744
|
args
|
|
6745
6745
|
});
|
|
6746
|
-
return
|
|
6746
|
+
return result3.rows.map((row) => ({
|
|
6747
6747
|
bucket: String(row.bucket),
|
|
6748
6748
|
count: Number(row.cnt),
|
|
6749
6749
|
relationshipType: options?.type ? options.type : String(row.rel_type)
|
|
@@ -6756,7 +6756,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6756
6756
|
sinceClause = " AND r.timestamp >= ?";
|
|
6757
6757
|
args.push(since.toISOString());
|
|
6758
6758
|
}
|
|
6759
|
-
const
|
|
6759
|
+
const result3 = await client.execute({
|
|
6760
6760
|
sql: `SELECT
|
|
6761
6761
|
CASE
|
|
6762
6762
|
WHEN r.source_entity_id = ? THEN r.target_entity_id
|
|
@@ -6771,7 +6771,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6771
6771
|
args: [contactEntityId, ...args]
|
|
6772
6772
|
});
|
|
6773
6773
|
const partners = [];
|
|
6774
|
-
for (const row of
|
|
6774
|
+
for (const row of result3.rows) {
|
|
6775
6775
|
const partnerId = String(row.partner_id);
|
|
6776
6776
|
const entityResult = await client.execute({
|
|
6777
6777
|
sql: "SELECT * FROM entities WHERE id = ?",
|
|
@@ -6796,7 +6796,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6796
6796
|
}
|
|
6797
6797
|
async function getHotEntities(client, since, limit = 10) {
|
|
6798
6798
|
const sinceISO = since.toISOString();
|
|
6799
|
-
const
|
|
6799
|
+
const result3 = await client.execute({
|
|
6800
6800
|
sql: `SELECT entity_id, COUNT(*) as rel_count
|
|
6801
6801
|
FROM (
|
|
6802
6802
|
SELECT source_entity_id as entity_id FROM relationships WHERE timestamp >= ?
|
|
@@ -6809,7 +6809,7 @@ async function getHotEntities(client, since, limit = 10) {
|
|
|
6809
6809
|
args: [sinceISO, sinceISO, limit]
|
|
6810
6810
|
});
|
|
6811
6811
|
const hotEntities = [];
|
|
6812
|
-
for (const row of
|
|
6812
|
+
for (const row of result3.rows) {
|
|
6813
6813
|
const eid = String(row.entity_id);
|
|
6814
6814
|
const entityResult = await client.execute({
|
|
6815
6815
|
sql: "SELECT * FROM entities WHERE id = ?",
|
|
@@ -6856,7 +6856,7 @@ async function matchEntities(query, client) {
|
|
|
6856
6856
|
if (words.length === 0) return [];
|
|
6857
6857
|
try {
|
|
6858
6858
|
const matchExpr = words.map((w) => `${w}*`).join(" OR ");
|
|
6859
|
-
const
|
|
6859
|
+
const result3 = await client.execute({
|
|
6860
6860
|
sql: `SELECT e.id, e.name FROM entities e
|
|
6861
6861
|
JOIN entities_fts fts ON e.rowid = fts.rowid
|
|
6862
6862
|
WHERE entities_fts MATCH ?
|
|
@@ -6864,8 +6864,8 @@ async function matchEntities(query, client) {
|
|
|
6864
6864
|
LIMIT ?`,
|
|
6865
6865
|
args: [matchExpr, MAX_ENTITY_MATCHES]
|
|
6866
6866
|
});
|
|
6867
|
-
if (
|
|
6868
|
-
return
|
|
6867
|
+
if (result3.rows.length > 0) {
|
|
6868
|
+
return result3.rows.map((row) => ({
|
|
6869
6869
|
entityId: String(row.id),
|
|
6870
6870
|
name: String(row.name)
|
|
6871
6871
|
}));
|
|
@@ -6875,13 +6875,13 @@ async function matchEntities(query, client) {
|
|
|
6875
6875
|
const conditions = words.map(() => `LOWER(name) LIKE ?`);
|
|
6876
6876
|
const args = words.map((w) => `%${w}%`);
|
|
6877
6877
|
try {
|
|
6878
|
-
const
|
|
6878
|
+
const result3 = await client.execute({
|
|
6879
6879
|
sql: `SELECT id, name FROM entities
|
|
6880
6880
|
WHERE ${conditions.join(" OR ")}
|
|
6881
6881
|
LIMIT ?`,
|
|
6882
6882
|
args: [...args, MAX_ENTITY_MATCHES]
|
|
6883
6883
|
});
|
|
6884
|
-
return
|
|
6884
|
+
return result3.rows.map((row) => ({
|
|
6885
6885
|
entityId: String(row.id),
|
|
6886
6886
|
name: String(row.name)
|
|
6887
6887
|
}));
|
|
@@ -6894,12 +6894,12 @@ async function getLinkedMemories(entityIds, client) {
|
|
|
6894
6894
|
if (entityIds.length === 0) return linked;
|
|
6895
6895
|
const placeholders = entityIds.map(() => "?").join(",");
|
|
6896
6896
|
try {
|
|
6897
|
-
const
|
|
6897
|
+
const result3 = await client.execute({
|
|
6898
6898
|
sql: `SELECT entity_id, memory_id FROM entity_memories
|
|
6899
6899
|
WHERE entity_id IN (${placeholders})`,
|
|
6900
6900
|
args: entityIds
|
|
6901
6901
|
});
|
|
6902
|
-
for (const row of
|
|
6902
|
+
for (const row of result3.rows) {
|
|
6903
6903
|
const entityId2 = String(row.entity_id);
|
|
6904
6904
|
const memoryId = String(row.memory_id);
|
|
6905
6905
|
const entry = linked.get(entityId2) ?? {
|
|
@@ -6972,13 +6972,13 @@ async function applyHyperedgeBoost(entities, client, boostMap, resultIds) {
|
|
|
6972
6972
|
const entityIds = entities.map((e) => e.entityId);
|
|
6973
6973
|
const placeholders = entityIds.map(() => "?").join(",");
|
|
6974
6974
|
try {
|
|
6975
|
-
const
|
|
6975
|
+
const result3 = await client.execute({
|
|
6976
6976
|
sql: `SELECT hyperedge_id, entity_id FROM hyperedge_nodes
|
|
6977
6977
|
WHERE entity_id IN (${placeholders})`,
|
|
6978
6978
|
args: entityIds
|
|
6979
6979
|
});
|
|
6980
6980
|
const hyperedgeEntities = /* @__PURE__ */ new Map();
|
|
6981
|
-
for (const row of
|
|
6981
|
+
for (const row of result3.rows) {
|
|
6982
6982
|
const hid = String(row.hyperedge_id);
|
|
6983
6983
|
const eid = String(row.entity_id);
|
|
6984
6984
|
const set = hyperedgeEntities.get(hid) ?? /* @__PURE__ */ new Set();
|
|
@@ -7303,10 +7303,10 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
7303
7303
|
};
|
|
7304
7304
|
try {
|
|
7305
7305
|
const fs = await import("fs");
|
|
7306
|
-
const
|
|
7306
|
+
const path55 = await import("path");
|
|
7307
7307
|
const os21 = await import("os");
|
|
7308
|
-
const logPath =
|
|
7309
|
-
fs.mkdirSync(
|
|
7308
|
+
const logPath = path55.join(os21.homedir(), ".exe-os", "search-quality.jsonl");
|
|
7309
|
+
fs.mkdirSync(path55.dirname(logPath), { recursive: true });
|
|
7310
7310
|
fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
|
|
7311
7311
|
} catch {
|
|
7312
7312
|
}
|
|
@@ -7355,8 +7355,8 @@ async function estimateCardinality(agentId, options) {
|
|
|
7355
7355
|
}
|
|
7356
7356
|
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
7357
7357
|
try {
|
|
7358
|
-
const
|
|
7359
|
-
return Number(
|
|
7358
|
+
const result3 = await client.execute({ sql, args });
|
|
7359
|
+
return Number(result3.rows[0]?.cnt) || 0;
|
|
7360
7360
|
} catch {
|
|
7361
7361
|
return 0;
|
|
7362
7362
|
}
|
|
@@ -7487,8 +7487,8 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
7487
7487
|
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
7488
7488
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
7489
7489
|
args.push(limit);
|
|
7490
|
-
const
|
|
7491
|
-
return
|
|
7490
|
+
const result3 = await client.execute({ sql, args });
|
|
7491
|
+
return result3.rows.map((row) => ({
|
|
7492
7492
|
id: row.id,
|
|
7493
7493
|
agent_id: row.agent_id,
|
|
7494
7494
|
agent_role: row.agent_role,
|
|
@@ -7603,8 +7603,8 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
7603
7603
|
}
|
|
7604
7604
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
7605
7605
|
args.push(limit);
|
|
7606
|
-
const
|
|
7607
|
-
const recentResults =
|
|
7606
|
+
const result3 = await client.execute({ sql, args });
|
|
7607
|
+
const recentResults = result3.rows.map((row) => rowToMemoryRecord(row));
|
|
7608
7608
|
if (sessionBoundaryMemories.length > 0) {
|
|
7609
7609
|
const seenIds = new Set(recentResults.map((r) => r.id));
|
|
7610
7610
|
for (const bm of sessionBoundaryMemories) {
|
|
@@ -7687,9 +7687,9 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
|
7687
7687
|
}
|
|
7688
7688
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
7689
7689
|
args.push(limit);
|
|
7690
|
-
const
|
|
7691
|
-
if (
|
|
7692
|
-
return
|
|
7690
|
+
const result3 = await client.execute({ sql, args });
|
|
7691
|
+
if (result3.rows.length < 3) return null;
|
|
7692
|
+
return result3.rows.map((row) => ({
|
|
7693
7693
|
id: row.id,
|
|
7694
7694
|
agent_id: row.agent_id,
|
|
7695
7695
|
agent_role: row.agent_role,
|
|
@@ -8516,10 +8516,10 @@ function freeLicense() {
|
|
|
8516
8516
|
async function countActiveMemories() {
|
|
8517
8517
|
if (!isInitialized()) return 0;
|
|
8518
8518
|
const client = getClient();
|
|
8519
|
-
const
|
|
8519
|
+
const result3 = await client.execute(
|
|
8520
8520
|
"SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL"
|
|
8521
8521
|
);
|
|
8522
|
-
const row =
|
|
8522
|
+
const row = result3.rows[0];
|
|
8523
8523
|
return Number(row?.cnt ?? 0);
|
|
8524
8524
|
}
|
|
8525
8525
|
async function assertMemoryLimit() {
|
|
@@ -8599,8 +8599,8 @@ __export(wiki_client_exports, {
|
|
|
8599
8599
|
listDocuments: () => listDocuments,
|
|
8600
8600
|
listWorkspaces: () => listWorkspaces
|
|
8601
8601
|
});
|
|
8602
|
-
async function wikiFetch(config2,
|
|
8603
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
8602
|
+
async function wikiFetch(config2, path55, method = "GET", body) {
|
|
8603
|
+
const url = `${config2.baseUrl}/api/v1${path55}`;
|
|
8604
8604
|
const headers = {
|
|
8605
8605
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
8606
8606
|
"Content-Type": "application/json"
|
|
@@ -8633,7 +8633,7 @@ async function wikiFetch(config2, path52, method = "GET", body) {
|
|
|
8633
8633
|
}
|
|
8634
8634
|
}
|
|
8635
8635
|
if (!response.ok) {
|
|
8636
|
-
throw new Error(`Wiki API ${method} ${
|
|
8636
|
+
throw new Error(`Wiki API ${method} ${path55}: ${response.status} ${response.statusText}`);
|
|
8637
8637
|
}
|
|
8638
8638
|
return response.json();
|
|
8639
8639
|
} finally {
|
|
@@ -8747,7 +8747,7 @@ __export(consolidation_exports, {
|
|
|
8747
8747
|
});
|
|
8748
8748
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8749
8749
|
async function selectUnconsolidated(client, limit = 200) {
|
|
8750
|
-
const
|
|
8750
|
+
const result3 = await client.execute({
|
|
8751
8751
|
sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
|
|
8752
8752
|
FROM memories
|
|
8753
8753
|
WHERE consolidated = 0
|
|
@@ -8755,7 +8755,7 @@ async function selectUnconsolidated(client, limit = 200) {
|
|
|
8755
8755
|
LIMIT ?`,
|
|
8756
8756
|
args: [limit]
|
|
8757
8757
|
});
|
|
8758
|
-
return
|
|
8758
|
+
return result3.rows.map((row) => ({
|
|
8759
8759
|
id: row.id,
|
|
8760
8760
|
agent_id: row.agent_id,
|
|
8761
8761
|
project_name: row.project_name,
|
|
@@ -8995,10 +8995,10 @@ async function runConsolidation(client, options) {
|
|
|
8995
8995
|
if (dedupCount > 0) clustersProcessed++;
|
|
8996
8996
|
continue;
|
|
8997
8997
|
}
|
|
8998
|
-
const
|
|
8998
|
+
const result3 = await storeConsolidation(client, cluster, synthesis, options.embedFn);
|
|
8999
8999
|
if (isCoordinator && options.wikiConfig) {
|
|
9000
9000
|
await pushToWiki(
|
|
9001
|
-
{ ...
|
|
9001
|
+
{ ...result3, projectName: cluster.projectName },
|
|
9002
9002
|
options.wikiConfig
|
|
9003
9003
|
).catch((err) => {
|
|
9004
9004
|
process.stderr.write(
|
|
@@ -9008,7 +9008,7 @@ async function runConsolidation(client, options) {
|
|
|
9008
9008
|
});
|
|
9009
9009
|
}
|
|
9010
9010
|
if (isCoordinator) {
|
|
9011
|
-
const sourceIds =
|
|
9011
|
+
const sourceIds = result3.sourceIds;
|
|
9012
9012
|
if (sourceIds.length > 0) {
|
|
9013
9013
|
const placeholders = sourceIds.map(() => "?").join(",");
|
|
9014
9014
|
await client.execute({
|
|
@@ -9078,26 +9078,26 @@ function cosineSimilarity(a, b) {
|
|
|
9078
9078
|
return denom === 0 ? 0 : dot / denom;
|
|
9079
9079
|
}
|
|
9080
9080
|
async function isUserIdle(client, idleMinutes = 30) {
|
|
9081
|
-
const
|
|
9081
|
+
const result3 = await client.execute({
|
|
9082
9082
|
sql: `SELECT MAX(timestamp) as last_activity
|
|
9083
9083
|
FROM memories
|
|
9084
9084
|
WHERE tool_name != 'consolidation'
|
|
9085
9085
|
AND timestamp >= datetime('now', '-1 day')`,
|
|
9086
9086
|
args: []
|
|
9087
9087
|
});
|
|
9088
|
-
const lastActivity =
|
|
9088
|
+
const lastActivity = result3.rows[0]?.last_activity;
|
|
9089
9089
|
if (!lastActivity) return true;
|
|
9090
9090
|
const lastMs = new Date(lastActivity).getTime();
|
|
9091
9091
|
const now = Date.now();
|
|
9092
9092
|
return now - lastMs >= idleMinutes * 60 * 1e3;
|
|
9093
9093
|
}
|
|
9094
9094
|
async function countUnconsolidated(client) {
|
|
9095
|
-
const
|
|
9095
|
+
const result3 = await client.execute({
|
|
9096
9096
|
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
9097
9097
|
WHERE consolidated = 0`,
|
|
9098
9098
|
args: []
|
|
9099
9099
|
});
|
|
9100
|
-
return Number(
|
|
9100
|
+
return Number(result3.rows[0]?.cnt ?? 0);
|
|
9101
9101
|
}
|
|
9102
9102
|
var ROLE_PROMPTS, DEFAULT_ROLE_PROMPT, WIKI_FETCH_TIMEOUT_MS;
|
|
9103
9103
|
var init_consolidation = __esm({
|
|
@@ -9236,12 +9236,12 @@ var init_tmux_transport = __esm({
|
|
|
9236
9236
|
}
|
|
9237
9237
|
isPaneInCopyMode(target) {
|
|
9238
9238
|
try {
|
|
9239
|
-
const
|
|
9239
|
+
const result3 = execFileSync(
|
|
9240
9240
|
"tmux",
|
|
9241
9241
|
["display-message", "-p", "-t", target, "#{pane_in_mode}"],
|
|
9242
9242
|
{ ...QUIET, timeout: 3e3 }
|
|
9243
9243
|
).trim();
|
|
9244
|
-
return
|
|
9244
|
+
return result3 === "1";
|
|
9245
9245
|
} catch {
|
|
9246
9246
|
return false;
|
|
9247
9247
|
}
|
|
@@ -9583,11 +9583,11 @@ async function recordSessionKill(input) {
|
|
|
9583
9583
|
async function countKillsSince(sinceISO) {
|
|
9584
9584
|
try {
|
|
9585
9585
|
const client = getClient();
|
|
9586
|
-
const
|
|
9586
|
+
const result3 = await client.execute({
|
|
9587
9587
|
sql: `SELECT COUNT(*) AS n FROM session_kills WHERE killed_at >= ?`,
|
|
9588
9588
|
args: [sinceISO]
|
|
9589
9589
|
});
|
|
9590
|
-
const row =
|
|
9590
|
+
const row = result3.rows[0];
|
|
9591
9591
|
return row ? Number(row.n) : 0;
|
|
9592
9592
|
} catch {
|
|
9593
9593
|
return 0;
|
|
@@ -9596,13 +9596,13 @@ async function countKillsSince(sinceISO) {
|
|
|
9596
9596
|
async function sumTokensSavedSince(sinceISO) {
|
|
9597
9597
|
try {
|
|
9598
9598
|
const client = getClient();
|
|
9599
|
-
const
|
|
9599
|
+
const result3 = await client.execute({
|
|
9600
9600
|
sql: `SELECT COALESCE(SUM(estimated_tokens_saved), 0) AS total
|
|
9601
9601
|
FROM session_kills
|
|
9602
9602
|
WHERE killed_at >= ?`,
|
|
9603
9603
|
args: [sinceISO]
|
|
9604
9604
|
});
|
|
9605
|
-
const row =
|
|
9605
|
+
const row = result3.rows[0];
|
|
9606
9606
|
return row ? Number(row.total) : 0;
|
|
9607
9607
|
} catch {
|
|
9608
9608
|
return 0;
|
|
@@ -9694,13 +9694,13 @@ function _resetLastRelaunchCache() {
|
|
|
9694
9694
|
async function lastResumeCreatedAtMs(agentId) {
|
|
9695
9695
|
const client = getClient();
|
|
9696
9696
|
const cmScope = sessionScopeFilter(null);
|
|
9697
|
-
const
|
|
9697
|
+
const result3 = await client.execute({
|
|
9698
9698
|
sql: `SELECT MAX(created_at) AS last_created_at
|
|
9699
9699
|
FROM tasks
|
|
9700
9700
|
WHERE assigned_to = ? AND title LIKE ?${cmScope.sql}`,
|
|
9701
9701
|
args: [agentId, `${RESUME_TITLE_PREFIX} %`, ...cmScope.args]
|
|
9702
9702
|
});
|
|
9703
|
-
const raw =
|
|
9703
|
+
const raw = result3.rows[0]?.last_created_at;
|
|
9704
9704
|
if (raw === null || raw === void 0) return null;
|
|
9705
9705
|
const parsed = Date.parse(String(raw));
|
|
9706
9706
|
return Number.isNaN(parsed) ? null : parsed;
|
|
@@ -9937,37 +9937,37 @@ async function countPendingReviews(sessionScope) {
|
|
|
9937
9937
|
const scope = strictSessionScopeFilter(
|
|
9938
9938
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9939
9939
|
);
|
|
9940
|
-
const
|
|
9940
|
+
const result3 = await client.execute({
|
|
9941
9941
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9942
9942
|
WHERE status = 'needs_review'${scope.sql}`,
|
|
9943
9943
|
args: [...scope.args]
|
|
9944
9944
|
});
|
|
9945
|
-
return Number(
|
|
9945
|
+
return Number(result3.rows[0]?.cnt) || 0;
|
|
9946
9946
|
}
|
|
9947
9947
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
9948
9948
|
const client = getClient();
|
|
9949
9949
|
const scope = strictSessionScopeFilter(
|
|
9950
9950
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9951
9951
|
);
|
|
9952
|
-
const
|
|
9952
|
+
const result3 = await client.execute({
|
|
9953
9953
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9954
9954
|
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
9955
9955
|
args: [sinceIso, ...scope.args]
|
|
9956
9956
|
});
|
|
9957
|
-
return Number(
|
|
9957
|
+
return Number(result3.rows[0]?.cnt) || 0;
|
|
9958
9958
|
}
|
|
9959
9959
|
async function listPendingReviews(limit, sessionScope) {
|
|
9960
9960
|
const client = getClient();
|
|
9961
9961
|
const scope = strictSessionScopeFilter(
|
|
9962
9962
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9963
9963
|
);
|
|
9964
|
-
const
|
|
9964
|
+
const result3 = await client.execute({
|
|
9965
9965
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
9966
9966
|
WHERE status = 'needs_review'${scope.sql}
|
|
9967
9967
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
9968
9968
|
args: [...scope.args, limit]
|
|
9969
9969
|
});
|
|
9970
|
-
return
|
|
9970
|
+
return result3.rows;
|
|
9971
9971
|
}
|
|
9972
9972
|
async function cleanupOrphanedReviews() {
|
|
9973
9973
|
const client = getClient();
|
|
@@ -10052,7 +10052,7 @@ function getReviewChecklist(role, agent, taskSlug) {
|
|
|
10052
10052
|
]
|
|
10053
10053
|
};
|
|
10054
10054
|
}
|
|
10055
|
-
async function createReviewForCompletedTask(row,
|
|
10055
|
+
async function createReviewForCompletedTask(row, result3, _baseDir, now) {
|
|
10056
10056
|
const taskFile = String(row.task_file);
|
|
10057
10057
|
const employees = await loadEmployees();
|
|
10058
10058
|
const coordinatorName = getCoordinatorName(employees);
|
|
@@ -10097,7 +10097,7 @@ async function createReviewForCompletedTask(row, result2, _baseDir, now) {
|
|
|
10097
10097
|
"- **Needs work:** re-open with notes"
|
|
10098
10098
|
].join("\n");
|
|
10099
10099
|
const originalTaskId = String(row.id);
|
|
10100
|
-
const updatedResult = (
|
|
10100
|
+
const updatedResult = (result3 ?? "No result summary provided") + reviewNotes;
|
|
10101
10101
|
await client.execute({
|
|
10102
10102
|
sql: `UPDATE tasks SET result = ?, status = 'needs_review', updated_at = ?
|
|
10103
10103
|
WHERE id = ?`,
|
|
@@ -10119,7 +10119,7 @@ async function createReviewForCompletedTask(row, result2, _baseDir, now) {
|
|
|
10119
10119
|
taskFile
|
|
10120
10120
|
});
|
|
10121
10121
|
const originalPriority = String(row.priority).toLowerCase();
|
|
10122
|
-
const autoApprove = originalPriority === "p2" &&
|
|
10122
|
+
const autoApprove = originalPriority === "p2" && result3?.toLowerCase().includes("tests pass");
|
|
10123
10123
|
if (!autoApprove) {
|
|
10124
10124
|
try {
|
|
10125
10125
|
const key = getSessionKey();
|
|
@@ -10148,11 +10148,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
10148
10148
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10149
10149
|
const parentId = row.parent_task_id ? String(row.parent_task_id) : null;
|
|
10150
10150
|
if (parentId) {
|
|
10151
|
-
const
|
|
10151
|
+
const result3 = await client.execute({
|
|
10152
10152
|
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ? AND status = 'needs_review'",
|
|
10153
10153
|
args: [now, parentId]
|
|
10154
10154
|
});
|
|
10155
|
-
if (
|
|
10155
|
+
if (result3.rowsAffected > 0) {
|
|
10156
10156
|
process.stderr.write(
|
|
10157
10157
|
`[review-cleanup] Cascaded original task to done via parent_task_id: ${parentId}
|
|
10158
10158
|
`
|
|
@@ -10166,11 +10166,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
10166
10166
|
const agent = parts[1];
|
|
10167
10167
|
const slug = parts.slice(2).join("-");
|
|
10168
10168
|
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
10169
|
-
const
|
|
10169
|
+
const result3 = await client.execute({
|
|
10170
10170
|
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
10171
10171
|
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
10172
10172
|
});
|
|
10173
|
-
if (
|
|
10173
|
+
if (result3.rowsAffected > 0) {
|
|
10174
10174
|
process.stderr.write(
|
|
10175
10175
|
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
10176
10176
|
`
|
|
@@ -10620,8 +10620,8 @@ function notifyParentExe(sessionKey) {
|
|
|
10620
10620
|
}
|
|
10621
10621
|
process.stderr.write(`[intercom] notifyParentExe \u2192 ${target}
|
|
10622
10622
|
`);
|
|
10623
|
-
const
|
|
10624
|
-
if (
|
|
10623
|
+
const result3 = sendIntercom(target);
|
|
10624
|
+
if (result3 === "failed") {
|
|
10625
10625
|
const rootExe = resolveExeSession();
|
|
10626
10626
|
if (rootExe && rootExe !== target) {
|
|
10627
10627
|
process.stderr.write(`[intercom] notifyParentExe: dispatcher ${target} dead, falling back to root coordinator session ${rootExe}
|
|
@@ -10711,19 +10711,19 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
10711
10711
|
}
|
|
10712
10712
|
const sessionName = employeeSessionName(employeeName, exeSession, effectiveInstance);
|
|
10713
10713
|
if (isEmployeeAlive(sessionName)) {
|
|
10714
|
-
const
|
|
10715
|
-
if (
|
|
10714
|
+
const result4 = sendIntercom(sessionName);
|
|
10715
|
+
if (result4 === "acknowledged" || result4 === "skipped_exe" || result4 === "debounced" || result4 === "queued") {
|
|
10716
10716
|
return { status: "intercom_sent", sessionName };
|
|
10717
10717
|
}
|
|
10718
|
-
if (
|
|
10718
|
+
if (result4 === "delivered") {
|
|
10719
10719
|
return { status: "intercom_unprocessed", sessionName };
|
|
10720
10720
|
}
|
|
10721
10721
|
return { status: "failed", sessionName, error: "intercom delivery failed" };
|
|
10722
10722
|
}
|
|
10723
10723
|
const spawnOpts = { ...opts, instance: effectiveInstance };
|
|
10724
|
-
const
|
|
10725
|
-
if (
|
|
10726
|
-
return { status: "failed", sessionName, error:
|
|
10724
|
+
const result3 = spawnEmployee(employeeName, exeSession, projectDir, spawnOpts);
|
|
10725
|
+
if (result3.error) {
|
|
10726
|
+
return { status: "failed", sessionName, error: result3.error };
|
|
10727
10727
|
}
|
|
10728
10728
|
return { status: "spawned", sessionName };
|
|
10729
10729
|
}
|
|
@@ -11223,11 +11223,11 @@ async function writeCheckpoint(input) {
|
|
|
11223
11223
|
blocked_by_ids: blockedByIds,
|
|
11224
11224
|
last_checkpoint_at: now
|
|
11225
11225
|
};
|
|
11226
|
-
const
|
|
11226
|
+
const result3 = await client.execute({
|
|
11227
11227
|
sql: `UPDATE tasks SET checkpoint = ?, checkpoint_count = checkpoint_count + 1, updated_at = ? WHERE id = ?`,
|
|
11228
11228
|
args: [JSON.stringify(checkpoint), now, taskId]
|
|
11229
11229
|
});
|
|
11230
|
-
if (
|
|
11230
|
+
if (result3.rowsAffected === 0) {
|
|
11231
11231
|
throw new Error(`Checkpoint write failed: task ${taskId} not found`);
|
|
11232
11232
|
}
|
|
11233
11233
|
const countResult = await client.execute({
|
|
@@ -11278,22 +11278,22 @@ function checkLaneAffinity(title, context, assigneeName) {
|
|
|
11278
11278
|
}
|
|
11279
11279
|
async function resolveTask(client, identifier, scopeSession) {
|
|
11280
11280
|
const scope = sessionScopeFilter(scopeSession);
|
|
11281
|
-
let
|
|
11281
|
+
let result3 = await client.execute({
|
|
11282
11282
|
sql: `SELECT * FROM tasks WHERE id = ?${scope.sql}`,
|
|
11283
11283
|
args: [identifier, ...scope.args]
|
|
11284
11284
|
});
|
|
11285
|
-
if (
|
|
11286
|
-
|
|
11285
|
+
if (result3.rows.length === 1) return result3.rows[0];
|
|
11286
|
+
result3 = await client.execute({
|
|
11287
11287
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
11288
11288
|
args: [`%${identifier}%`, ...scope.args]
|
|
11289
11289
|
});
|
|
11290
|
-
if (
|
|
11291
|
-
if (
|
|
11292
|
-
const exact =
|
|
11290
|
+
if (result3.rows.length === 1) return result3.rows[0];
|
|
11291
|
+
if (result3.rows.length > 1) {
|
|
11292
|
+
const exact = result3.rows.filter(
|
|
11293
11293
|
(r) => String(r.task_file).endsWith(`/${identifier}.md`)
|
|
11294
11294
|
);
|
|
11295
11295
|
if (exact.length === 1) return exact[0];
|
|
11296
|
-
const candidates = exact.length > 1 ? exact :
|
|
11296
|
+
const candidates = exact.length > 1 ? exact : result3.rows;
|
|
11297
11297
|
const active = candidates.filter(
|
|
11298
11298
|
(r) => !["done", "cancelled"].includes(String(r.status))
|
|
11299
11299
|
);
|
|
@@ -11303,17 +11303,17 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
11303
11303
|
`Multiple tasks match "${identifier}": ${matches}. Use a UUID to disambiguate.`
|
|
11304
11304
|
);
|
|
11305
11305
|
}
|
|
11306
|
-
|
|
11306
|
+
result3 = await client.execute({
|
|
11307
11307
|
sql: `SELECT * FROM tasks WHERE title LIKE ?${scope.sql}`,
|
|
11308
11308
|
args: [`%${identifier}%`, ...scope.args]
|
|
11309
11309
|
});
|
|
11310
|
-
if (
|
|
11311
|
-
if (
|
|
11312
|
-
const active =
|
|
11310
|
+
if (result3.rows.length === 1) return result3.rows[0];
|
|
11311
|
+
if (result3.rows.length > 1) {
|
|
11312
|
+
const active = result3.rows.filter(
|
|
11313
11313
|
(r) => !["done", "cancelled"].includes(String(r.status))
|
|
11314
11314
|
);
|
|
11315
11315
|
if (active.length === 1) return active[0];
|
|
11316
|
-
const matches = (active.length > 1 ? active :
|
|
11316
|
+
const matches = (active.length > 1 ? active : result3.rows).map((r) => `"${String(r.title)}" (${String(r.status)}, ${String(r.id)})`).join(", ");
|
|
11317
11317
|
throw new Error(
|
|
11318
11318
|
`Multiple tasks match "${identifier}": ${matches}. Use a UUID to disambiguate.`
|
|
11319
11319
|
);
|
|
@@ -11523,11 +11523,11 @@ async function listTasks(input) {
|
|
|
11523
11523
|
args.push(...scope.args);
|
|
11524
11524
|
}
|
|
11525
11525
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
11526
|
-
const
|
|
11526
|
+
const result3 = await client.execute({
|
|
11527
11527
|
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`,
|
|
11528
11528
|
args
|
|
11529
11529
|
});
|
|
11530
|
-
return
|
|
11530
|
+
return result3.rows.map((r) => ({
|
|
11531
11531
|
id: String(r.id),
|
|
11532
11532
|
title: String(r.title),
|
|
11533
11533
|
assignedTo: String(r.assigned_to),
|
|
@@ -11919,30 +11919,30 @@ async function dispatchTaskToEmployee(input) {
|
|
|
11919
11919
|
if (!exeSession) return { dispatched: "session_missing" };
|
|
11920
11920
|
const sessionName = employeeSessionName(input.assignedTo, exeSession);
|
|
11921
11921
|
if (transport.isAlive(sessionName)) {
|
|
11922
|
-
const
|
|
11923
|
-
const dispatched =
|
|
11922
|
+
const result3 = sendIntercom(sessionName);
|
|
11923
|
+
const dispatched = result3 === "acknowledged" || result3 === "debounced" || result3 === "queued" ? "verified" : result3 === "delivered" ? "sent_unverified" : "session_dead";
|
|
11924
11924
|
process.stderr.write(
|
|
11925
|
-
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${
|
|
11925
|
+
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result3})
|
|
11926
11926
|
`
|
|
11927
11927
|
);
|
|
11928
11928
|
return { dispatched, session: sessionName, crossProject };
|
|
11929
11929
|
} else {
|
|
11930
11930
|
const projectDir = input.projectDir ?? process.cwd();
|
|
11931
|
-
const
|
|
11931
|
+
const result3 = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
11932
11932
|
autoInstance: isMultiInstance(input.assignedTo)
|
|
11933
11933
|
});
|
|
11934
|
-
if (
|
|
11934
|
+
if (result3.status === "failed") {
|
|
11935
11935
|
process.stderr.write(
|
|
11936
|
-
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${
|
|
11936
|
+
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result3.error}
|
|
11937
11937
|
`
|
|
11938
11938
|
);
|
|
11939
11939
|
return { dispatched: "session_missing" };
|
|
11940
11940
|
}
|
|
11941
11941
|
process.stderr.write(
|
|
11942
|
-
`[dispatch-audit] SPAWNED \u2192 ${
|
|
11942
|
+
`[dispatch-audit] SPAWNED \u2192 ${result3.sessionName} | task="${input.title}" [${input.priority}]
|
|
11943
11943
|
`
|
|
11944
11944
|
);
|
|
11945
|
-
return { dispatched: "spawned", session:
|
|
11945
|
+
return { dispatched: "spawned", session: result3.sessionName, crossProject };
|
|
11946
11946
|
}
|
|
11947
11947
|
} catch {
|
|
11948
11948
|
return { dispatched: "session_missing" };
|
|
@@ -12000,13 +12000,13 @@ async function storeBehavior(opts) {
|
|
|
12000
12000
|
}
|
|
12001
12001
|
async function listBehaviorsByDomain(agentId, domain) {
|
|
12002
12002
|
const client = getClient();
|
|
12003
|
-
const
|
|
12003
|
+
const result3 = await client.execute({
|
|
12004
12004
|
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
12005
12005
|
FROM behaviors
|
|
12006
12006
|
WHERE agent_id = ? AND domain = ? AND active = 1`,
|
|
12007
12007
|
args: [agentId, domain]
|
|
12008
12008
|
});
|
|
12009
|
-
return
|
|
12009
|
+
return result3.rows.map((r) => ({
|
|
12010
12010
|
id: String(r.id),
|
|
12011
12011
|
agent_id: String(r.agent_id),
|
|
12012
12012
|
project_name: r.project_name ? String(r.project_name) : null,
|
|
@@ -12021,11 +12021,11 @@ async function listBehaviorsByDomain(agentId, domain) {
|
|
|
12021
12021
|
}
|
|
12022
12022
|
async function deactivateBehavior(id) {
|
|
12023
12023
|
const client = getClient();
|
|
12024
|
-
const
|
|
12024
|
+
const result3 = await client.execute({
|
|
12025
12025
|
sql: `UPDATE behaviors SET active = 0, updated_at = ? WHERE id = ? AND active = 1`,
|
|
12026
12026
|
args: [(/* @__PURE__ */ new Date()).toISOString(), id]
|
|
12027
12027
|
});
|
|
12028
|
-
return (
|
|
12028
|
+
return (result3.rowsAffected ?? 0) > 0;
|
|
12029
12029
|
}
|
|
12030
12030
|
var init_behaviors = __esm({
|
|
12031
12031
|
"src/lib/behaviors.ts"() {
|
|
@@ -12050,15 +12050,15 @@ __export(skill_learning_exports, {
|
|
|
12050
12050
|
import crypto9 from "crypto";
|
|
12051
12051
|
async function extractTrajectory(taskId, agentId) {
|
|
12052
12052
|
const client = getClient();
|
|
12053
|
-
const
|
|
12053
|
+
const result3 = await client.execute({
|
|
12054
12054
|
sql: `SELECT tool_name, raw_text
|
|
12055
12055
|
FROM memories
|
|
12056
12056
|
WHERE task_id = ? AND agent_id = ?
|
|
12057
12057
|
ORDER BY timestamp ASC`,
|
|
12058
12058
|
args: [taskId, agentId]
|
|
12059
12059
|
});
|
|
12060
|
-
if (
|
|
12061
|
-
const rawTools =
|
|
12060
|
+
if (result3.rows.length === 0) return [];
|
|
12061
|
+
const rawTools = result3.rows.map((r) => {
|
|
12062
12062
|
const toolName = String(r.tool_name);
|
|
12063
12063
|
if (toolName === "Bash") {
|
|
12064
12064
|
const text3 = String(r.raw_text);
|
|
@@ -12103,7 +12103,7 @@ async function storeTrajectory(opts) {
|
|
|
12103
12103
|
async function findSimilarTrajectories(signature, threshold = DEFAULT_SKILL_THRESHOLD) {
|
|
12104
12104
|
const client = getClient();
|
|
12105
12105
|
const hash = hashSignature(signature);
|
|
12106
|
-
const
|
|
12106
|
+
const result3 = await client.execute({
|
|
12107
12107
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
12108
12108
|
FROM trajectories
|
|
12109
12109
|
WHERE signature_hash = ?
|
|
@@ -12123,7 +12123,7 @@ async function findSimilarTrajectories(signature, threshold = DEFAULT_SKILL_THRE
|
|
|
12123
12123
|
skillId: r.skill_id ? String(r.skill_id) : null,
|
|
12124
12124
|
createdAt: String(r.created_at)
|
|
12125
12125
|
});
|
|
12126
|
-
const matches =
|
|
12126
|
+
const matches = result3.rows.map(mapRow);
|
|
12127
12127
|
if (matches.length >= threshold) return matches;
|
|
12128
12128
|
const nearResult = await client.execute({
|
|
12129
12129
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
@@ -12253,7 +12253,7 @@ async function sweepTrajectories(threshold, model) {
|
|
|
12253
12253
|
if (!config2.skillLearning) return { clustersProcessed: 0, skillsExtracted: 0 };
|
|
12254
12254
|
const t = threshold ?? config2.skillThreshold;
|
|
12255
12255
|
const client = getClient();
|
|
12256
|
-
const
|
|
12256
|
+
const result3 = await client.execute({
|
|
12257
12257
|
sql: `SELECT signature_hash, COUNT(*) as cnt
|
|
12258
12258
|
FROM trajectories
|
|
12259
12259
|
WHERE skill_id IS NULL
|
|
@@ -12265,7 +12265,7 @@ async function sweepTrajectories(threshold, model) {
|
|
|
12265
12265
|
});
|
|
12266
12266
|
let clustersProcessed = 0;
|
|
12267
12267
|
let skillsExtracted = 0;
|
|
12268
|
-
for (const row of
|
|
12268
|
+
for (const row of result3.rows) {
|
|
12269
12269
|
const hash = String(row.signature_hash);
|
|
12270
12270
|
const trajResult = await client.execute({
|
|
12271
12271
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, created_at
|
|
@@ -12348,18 +12348,18 @@ __export(tasks_exports, {
|
|
|
12348
12348
|
import path25 from "path";
|
|
12349
12349
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, unlinkSync as unlinkSync7 } from "fs";
|
|
12350
12350
|
async function createTask(input) {
|
|
12351
|
-
const
|
|
12352
|
-
if (!input.skipDispatch &&
|
|
12351
|
+
const result3 = await createTaskCore(input);
|
|
12352
|
+
if (!input.skipDispatch && result3.status !== "blocked" && !process.env.VITEST) {
|
|
12353
12353
|
dispatchTaskToEmployee({
|
|
12354
12354
|
assignedTo: input.assignedTo,
|
|
12355
12355
|
title: input.title,
|
|
12356
12356
|
priority: input.priority,
|
|
12357
|
-
taskFile:
|
|
12358
|
-
initialStatus:
|
|
12357
|
+
taskFile: result3.taskFile,
|
|
12358
|
+
initialStatus: result3.status,
|
|
12359
12359
|
projectName: input.projectName
|
|
12360
12360
|
});
|
|
12361
12361
|
}
|
|
12362
|
-
return
|
|
12362
|
+
return result3;
|
|
12363
12363
|
}
|
|
12364
12364
|
async function updateTask(input) {
|
|
12365
12365
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
@@ -13266,20 +13266,20 @@ async function sendMessage(input) {
|
|
|
13266
13266
|
} catch {
|
|
13267
13267
|
}
|
|
13268
13268
|
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
13269
|
-
const
|
|
13269
|
+
const result3 = await client.execute({
|
|
13270
13270
|
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
13271
13271
|
args: [id, ...sentScope.args]
|
|
13272
13272
|
});
|
|
13273
|
-
return rowToMessage(
|
|
13273
|
+
return rowToMessage(result3.rows[0]);
|
|
13274
13274
|
}
|
|
13275
13275
|
async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
13276
13276
|
const client = getClient();
|
|
13277
|
-
const
|
|
13277
|
+
const result3 = await client.execute({
|
|
13278
13278
|
sql: "SELECT * FROM messages WHERE id = ?",
|
|
13279
13279
|
args: [messageId]
|
|
13280
13280
|
});
|
|
13281
|
-
if (
|
|
13282
|
-
const msg = rowToMessage(
|
|
13281
|
+
if (result3.rows.length === 0) return false;
|
|
13282
|
+
const msg = rowToMessage(result3.rows[0]);
|
|
13283
13283
|
if (msg.status !== "pending") return false;
|
|
13284
13284
|
if (!_wsClientSend) {
|
|
13285
13285
|
return false;
|
|
@@ -13306,12 +13306,12 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
13306
13306
|
}
|
|
13307
13307
|
async function deliverLocalMessage(messageId) {
|
|
13308
13308
|
const client = getClient();
|
|
13309
|
-
const
|
|
13309
|
+
const result3 = await client.execute({
|
|
13310
13310
|
sql: "SELECT * FROM messages WHERE id = ?",
|
|
13311
13311
|
args: [messageId]
|
|
13312
13312
|
});
|
|
13313
|
-
if (
|
|
13314
|
-
const msg = rowToMessage(
|
|
13313
|
+
if (result3.rows.length === 0) return false;
|
|
13314
|
+
const msg = rowToMessage(result3.rows[0]);
|
|
13315
13315
|
if (msg.status !== "pending") return false;
|
|
13316
13316
|
const targetAgent = msg.targetAgent;
|
|
13317
13317
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13919,10 +13919,10 @@ function isCrdtSyncEnabled() {
|
|
|
13919
13919
|
async function rebuildFromDb() {
|
|
13920
13920
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
13921
13921
|
const client = getClient2();
|
|
13922
|
-
const
|
|
13922
|
+
const result3 = await client.execute(
|
|
13923
13923
|
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
13924
13924
|
);
|
|
13925
|
-
const memories =
|
|
13925
|
+
const memories = result3.rows.map((row) => ({
|
|
13926
13926
|
id: String(row.id),
|
|
13927
13927
|
agent_id: row.agent_id,
|
|
13928
13928
|
agent_role: row.agent_role,
|
|
@@ -13956,6 +13956,232 @@ var init_crdt_sync = __esm({
|
|
|
13956
13956
|
}
|
|
13957
13957
|
});
|
|
13958
13958
|
|
|
13959
|
+
// src/lib/tmux-status.ts
|
|
13960
|
+
import { execSync as execSync12 } from "child_process";
|
|
13961
|
+
function inTmux() {
|
|
13962
|
+
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
13963
|
+
const term = process.env.TERM ?? "";
|
|
13964
|
+
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
13965
|
+
try {
|
|
13966
|
+
execSync12("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
13967
|
+
encoding: "utf8",
|
|
13968
|
+
timeout: 2e3
|
|
13969
|
+
});
|
|
13970
|
+
return true;
|
|
13971
|
+
} catch {
|
|
13972
|
+
}
|
|
13973
|
+
try {
|
|
13974
|
+
let pid = process.ppid;
|
|
13975
|
+
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
13976
|
+
const comm = execSync12(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
13977
|
+
encoding: "utf8",
|
|
13978
|
+
timeout: 1e3
|
|
13979
|
+
}).trim();
|
|
13980
|
+
if (/tmux/.test(comm)) return true;
|
|
13981
|
+
const ppid = execSync12(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
13982
|
+
encoding: "utf8",
|
|
13983
|
+
timeout: 1e3
|
|
13984
|
+
}).trim();
|
|
13985
|
+
pid = parseInt(ppid, 10);
|
|
13986
|
+
if (isNaN(pid)) break;
|
|
13987
|
+
}
|
|
13988
|
+
} catch {
|
|
13989
|
+
}
|
|
13990
|
+
return false;
|
|
13991
|
+
}
|
|
13992
|
+
function listTmuxSessions() {
|
|
13993
|
+
try {
|
|
13994
|
+
const out = execSync12("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
13995
|
+
encoding: "utf8",
|
|
13996
|
+
timeout: 3e3
|
|
13997
|
+
});
|
|
13998
|
+
return out.trim().split("\n").filter(Boolean);
|
|
13999
|
+
} catch {
|
|
14000
|
+
return [];
|
|
14001
|
+
}
|
|
14002
|
+
}
|
|
14003
|
+
function capturePaneLines(windowName, lines = 10) {
|
|
14004
|
+
try {
|
|
14005
|
+
const out = execSync12(
|
|
14006
|
+
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
14007
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14008
|
+
);
|
|
14009
|
+
return out.split("\n").filter((l) => l.trim());
|
|
14010
|
+
} catch {
|
|
14011
|
+
return [];
|
|
14012
|
+
}
|
|
14013
|
+
}
|
|
14014
|
+
function getPaneCwd(windowName) {
|
|
14015
|
+
try {
|
|
14016
|
+
const out = execSync12(
|
|
14017
|
+
`tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
|
|
14018
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14019
|
+
);
|
|
14020
|
+
return out.trim() || void 0;
|
|
14021
|
+
} catch {
|
|
14022
|
+
return void 0;
|
|
14023
|
+
}
|
|
14024
|
+
}
|
|
14025
|
+
function projectFromPath(dir) {
|
|
14026
|
+
try {
|
|
14027
|
+
const root = execSync12("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
|
|
14028
|
+
encoding: "utf8",
|
|
14029
|
+
timeout: 3e3
|
|
14030
|
+
}).trim();
|
|
14031
|
+
return root.split("/").pop() ?? dir.split("/").pop() ?? "unknown";
|
|
14032
|
+
} catch {
|
|
14033
|
+
return dir.split("/").pop() ?? "unknown";
|
|
14034
|
+
}
|
|
14035
|
+
}
|
|
14036
|
+
function parseActivity(lines) {
|
|
14037
|
+
const joined = lines.join("\n");
|
|
14038
|
+
if (/npm\s+test|vitest|jest|pytest|cargo\s+test/i.test(joined)) {
|
|
14039
|
+
const passMatch = joined.match(/(\d+)\s*passed/i);
|
|
14040
|
+
if (passMatch) return `Running tests \u2014 ${passMatch[1]} passed`;
|
|
14041
|
+
return "Running tests...";
|
|
14042
|
+
}
|
|
14043
|
+
if (/Edit\(|Edit\s*\{/.test(joined)) {
|
|
14044
|
+
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
14045
|
+
if (fileMatch) return `Editing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
14046
|
+
return "Editing code...";
|
|
14047
|
+
}
|
|
14048
|
+
if (/Write\(|Write\s*\{/.test(joined)) {
|
|
14049
|
+
const fileMatch = joined.match(/file_path['":\s]+([^\s'"}\]]+)/);
|
|
14050
|
+
if (fileMatch) return `Writing ${fileMatch[1].split("/").slice(-2).join("/")}`;
|
|
14051
|
+
return "Writing file...";
|
|
14052
|
+
}
|
|
14053
|
+
if (/Bash\(|Bash\s*\{/.test(joined)) {
|
|
14054
|
+
const cmdMatch = joined.match(/command['":\s]+([^\n'"}{]{1,60})/);
|
|
14055
|
+
if (cmdMatch) return `Running: ${cmdMatch[1].trim().slice(0, 50)}`;
|
|
14056
|
+
return "Running command...";
|
|
14057
|
+
}
|
|
14058
|
+
if (/git\s+commit/.test(joined)) return "Committing...";
|
|
14059
|
+
if (/MANDATORY|START WORKING NOW/.test(joined)) return "Starting new task...";
|
|
14060
|
+
if (/All tasks complete|No open tasks/.test(joined)) return "Idle \u2014 queue empty";
|
|
14061
|
+
if (/store_memory|recall_my_memory/.test(joined)) return "Accessing memory...";
|
|
14062
|
+
if (/Agent\(/.test(joined)) return "Spawning sub-agent...";
|
|
14063
|
+
if (/Read\(|Read\s*\{/.test(joined)) return "Reading files...";
|
|
14064
|
+
const lastLine = lines.filter((l) => l.trim()).pop();
|
|
14065
|
+
if (lastLine && lastLine.trim().length > 5) {
|
|
14066
|
+
return lastLine.trim().slice(0, 60);
|
|
14067
|
+
}
|
|
14068
|
+
return "Working...";
|
|
14069
|
+
}
|
|
14070
|
+
function getEmployeeStatuses(employeeNames) {
|
|
14071
|
+
if (!inTmux() || process.env.VITEST) {
|
|
14072
|
+
return employeeNames.map((name) => ({
|
|
14073
|
+
name,
|
|
14074
|
+
hasWindow: false,
|
|
14075
|
+
icon: "\u26AA"
|
|
14076
|
+
}));
|
|
14077
|
+
}
|
|
14078
|
+
const sessions = new Set(listTmuxSessions());
|
|
14079
|
+
return employeeNames.map((name) => {
|
|
14080
|
+
const sessionName = [...sessions].find((s) => s.startsWith(`${name}-`));
|
|
14081
|
+
if (!sessionName) {
|
|
14082
|
+
return { name, hasWindow: false, icon: "\u26AA" };
|
|
14083
|
+
}
|
|
14084
|
+
let paneAlive = true;
|
|
14085
|
+
try {
|
|
14086
|
+
const paneStatus = execSync12(
|
|
14087
|
+
`tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
|
|
14088
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
14089
|
+
).trim();
|
|
14090
|
+
if (paneStatus === "1") paneAlive = false;
|
|
14091
|
+
} catch {
|
|
14092
|
+
paneAlive = false;
|
|
14093
|
+
}
|
|
14094
|
+
if (!paneAlive) {
|
|
14095
|
+
return { name, hasWindow: false, icon: "\u26AA" };
|
|
14096
|
+
}
|
|
14097
|
+
const rawLines = capturePaneLines(sessionName, 10);
|
|
14098
|
+
const cwd = getPaneCwd(sessionName);
|
|
14099
|
+
const project = cwd ? projectFromPath(cwd) : void 0;
|
|
14100
|
+
const activity = rawLines.length > 0 ? parseActivity(rawLines) : "Session active";
|
|
14101
|
+
return {
|
|
14102
|
+
name,
|
|
14103
|
+
hasWindow: true,
|
|
14104
|
+
project,
|
|
14105
|
+
activity,
|
|
14106
|
+
rawLines,
|
|
14107
|
+
icon: "\u{1F7E2}"
|
|
14108
|
+
};
|
|
14109
|
+
});
|
|
14110
|
+
}
|
|
14111
|
+
function formatStatusAll(statuses, tasks, roles) {
|
|
14112
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
14113
|
+
for (const s of statuses) {
|
|
14114
|
+
const proj = s.project ?? "(no session)";
|
|
14115
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
14116
|
+
byProject.get(proj).push(s);
|
|
14117
|
+
}
|
|
14118
|
+
const lines = [];
|
|
14119
|
+
lines.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
14120
|
+
lines.push("\u2502 REAL-TIME STATUS \u2502");
|
|
14121
|
+
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
14122
|
+
lines.push("");
|
|
14123
|
+
for (const [proj, emps] of byProject) {
|
|
14124
|
+
lines.push(`\u250C\u2500 ${proj} ${"\u2500".repeat(Math.max(0, 66 - proj.length))}\u2510`);
|
|
14125
|
+
if (emps.every((e) => !e.hasWindow)) {
|
|
14126
|
+
lines.push("\u2502 No active employees" + " ".repeat(49) + "\u2502");
|
|
14127
|
+
} else {
|
|
14128
|
+
for (let i = 0; i < emps.length; i++) {
|
|
14129
|
+
const e = emps[i];
|
|
14130
|
+
const role = roles.get(e.name) ?? "";
|
|
14131
|
+
const roleSuffix = role ? ` (${role})` : "";
|
|
14132
|
+
const nameCol = `${e.name}${roleSuffix}`.padEnd(14);
|
|
14133
|
+
const task = tasks.get(e.name);
|
|
14134
|
+
if (!e.hasWindow) {
|
|
14135
|
+
const statusLine = `\u2502 ${nameCol} \u2502 \u26AA No tmux session`;
|
|
14136
|
+
lines.push(statusLine + " ".repeat(Math.max(0, 70 - statusLine.length)) + "\u2502");
|
|
14137
|
+
} else {
|
|
14138
|
+
const actLine = `\u2502 ${nameCol} \u2502 ${e.icon} ${(e.activity ?? "Active").slice(0, 45)}`;
|
|
14139
|
+
lines.push(actLine + " ".repeat(Math.max(0, 70 - actLine.length)) + "\u2502");
|
|
14140
|
+
if (task) {
|
|
14141
|
+
const taskLine = `\u2502${" ".repeat(16)}\u2502 Task: "${task.title.slice(0, 40)}" [${task.priority.toUpperCase()}]`;
|
|
14142
|
+
lines.push(taskLine + " ".repeat(Math.max(0, 70 - taskLine.length)) + "\u2502");
|
|
14143
|
+
}
|
|
14144
|
+
}
|
|
14145
|
+
if (i < emps.length - 1) {
|
|
14146
|
+
lines.push("\u251C" + "\u2500".repeat(16) + "\u253C" + "\u2500".repeat(52) + "\u2524");
|
|
14147
|
+
}
|
|
14148
|
+
}
|
|
14149
|
+
}
|
|
14150
|
+
lines.push("\u2514" + "\u2500".repeat(16) + "\u2534" + "\u2500".repeat(52) + "\u2518");
|
|
14151
|
+
lines.push("");
|
|
14152
|
+
}
|
|
14153
|
+
return lines.join("\n");
|
|
14154
|
+
}
|
|
14155
|
+
function formatStatusDeep(status2, task, role) {
|
|
14156
|
+
const lines = [];
|
|
14157
|
+
const header = `${status2.name} (${role}) \u2014 ${status2.project ?? "unknown project"}`;
|
|
14158
|
+
lines.push(`\u250C\u2500 ${header} ${"\u2500".repeat(Math.max(0, 66 - header.length))}\u2510`);
|
|
14159
|
+
if (task) {
|
|
14160
|
+
const taskLine = `\u2502 Task: "${task.title.slice(0, 50)}" [${task.priority.toUpperCase()}]`;
|
|
14161
|
+
lines.push(taskLine + " ".repeat(Math.max(0, 70 - taskLine.length)) + "\u2502");
|
|
14162
|
+
lines.push("\u251C" + "\u2500".repeat(69) + "\u2524");
|
|
14163
|
+
}
|
|
14164
|
+
if (!status2.hasWindow) {
|
|
14165
|
+
lines.push("\u2502 No tmux session" + " ".repeat(53) + "\u2502");
|
|
14166
|
+
} else if (status2.rawLines && status2.rawLines.length > 0) {
|
|
14167
|
+
lines.push("\u2502 Recent activity:" + " ".repeat(52) + "\u2502");
|
|
14168
|
+
for (const line of status2.rawLines.slice(-10)) {
|
|
14169
|
+
const trimmed = line.slice(0, 65);
|
|
14170
|
+
const padded = `\u2502 ${trimmed}`;
|
|
14171
|
+
lines.push(padded + " ".repeat(Math.max(0, 70 - padded.length)) + "\u2502");
|
|
14172
|
+
}
|
|
14173
|
+
} else {
|
|
14174
|
+
lines.push("\u2502 Session active \u2014 no recent output" + " ".repeat(35) + "\u2502");
|
|
14175
|
+
}
|
|
14176
|
+
lines.push("\u2514" + "\u2500".repeat(69) + "\u2518");
|
|
14177
|
+
return lines.join("\n");
|
|
14178
|
+
}
|
|
14179
|
+
var init_tmux_status = __esm({
|
|
14180
|
+
"src/lib/tmux-status.ts"() {
|
|
14181
|
+
"use strict";
|
|
14182
|
+
}
|
|
14183
|
+
});
|
|
14184
|
+
|
|
13959
14185
|
// src/mcp/server.ts
|
|
13960
14186
|
init_embedder();
|
|
13961
14187
|
init_store();
|
|
@@ -13963,10 +14189,10 @@ init_database();
|
|
|
13963
14189
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13964
14190
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13965
14191
|
import { spawn as spawn4 } from "child_process";
|
|
13966
|
-
import { existsSync as
|
|
13967
|
-
import
|
|
14192
|
+
import { existsSync as existsSync41, openSync as openSync4, mkdirSync as mkdirSync23, closeSync as closeSync4, readFileSync as readFileSync36 } from "fs";
|
|
14193
|
+
import path54 from "path";
|
|
13968
14194
|
import os20 from "os";
|
|
13969
|
-
import { fileURLToPath as
|
|
14195
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
13970
14196
|
|
|
13971
14197
|
// src/mcp/tools/memory.ts
|
|
13972
14198
|
import { z as z10 } from "zod";
|
|
@@ -14517,7 +14743,7 @@ async function searchConversations(query, limit) {
|
|
|
14517
14743
|
try {
|
|
14518
14744
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
14519
14745
|
const client = getClient2();
|
|
14520
|
-
const
|
|
14746
|
+
const result3 = await client.execute({
|
|
14521
14747
|
sql: `SELECT c.id, c.platform, c.sender_id, c.sender_name,
|
|
14522
14748
|
c.content_text, c.agent_response, c.agent_name, c.timestamp
|
|
14523
14749
|
FROM conversations c
|
|
@@ -14526,9 +14752,9 @@ async function searchConversations(query, limit) {
|
|
|
14526
14752
|
LIMIT ?`,
|
|
14527
14753
|
args: [query, limit]
|
|
14528
14754
|
});
|
|
14529
|
-
return
|
|
14755
|
+
return result3.rows.map((row, i) => ({
|
|
14530
14756
|
source: "conversations",
|
|
14531
|
-
score: 1 - i / Math.max(
|
|
14757
|
+
score: 1 - i / Math.max(result3.rows.length, 1),
|
|
14532
14758
|
timestamp: row.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
14533
14759
|
snippet: truncate(
|
|
14534
14760
|
(row.content_text ?? "") + (row.agent_response ? ` \u2192 ${row.agent_response}` : ""),
|
|
@@ -14740,7 +14966,7 @@ function registerGetSessionContext(server2) {
|
|
|
14740
14966
|
},
|
|
14741
14967
|
async ({ session_id, target_timestamp, window_size, max_chars, body_chars }) => {
|
|
14742
14968
|
const client = getClient();
|
|
14743
|
-
const
|
|
14969
|
+
const result3 = await client.execute({
|
|
14744
14970
|
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
14745
14971
|
tool_name, project_name,
|
|
14746
14972
|
has_error, raw_text, vector, task_id
|
|
@@ -14750,7 +14976,7 @@ function registerGetSessionContext(server2) {
|
|
|
14750
14976
|
LIMIT 500`,
|
|
14751
14977
|
args: [session_id]
|
|
14752
14978
|
});
|
|
14753
|
-
if (
|
|
14979
|
+
if (result3.rows.length === 0) {
|
|
14754
14980
|
return {
|
|
14755
14981
|
content: [
|
|
14756
14982
|
{
|
|
@@ -14760,7 +14986,7 @@ function registerGetSessionContext(server2) {
|
|
|
14760
14986
|
]
|
|
14761
14987
|
};
|
|
14762
14988
|
}
|
|
14763
|
-
const sorted =
|
|
14989
|
+
const sorted = result3.rows.map((row) => ({
|
|
14764
14990
|
id: row.id,
|
|
14765
14991
|
agent_id: row.agent_id,
|
|
14766
14992
|
agent_role: row.agent_role,
|
|
@@ -14827,7 +15053,7 @@ function registerGetMemoryById(server2) {
|
|
|
14827
15053
|
const canReadOtherAgent = agentRole === "COO" || agentRole === "CTO";
|
|
14828
15054
|
const ownerAgent = requestedAgent && canReadOtherAgent ? requestedAgent : agentId;
|
|
14829
15055
|
const client = getClient();
|
|
14830
|
-
const
|
|
15056
|
+
const result3 = await client.execute({
|
|
14831
15057
|
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
14832
15058
|
tool_name, project_name, has_error, raw_text, task_id,
|
|
14833
15059
|
importance, status, memory_type, source_path, source_type
|
|
@@ -14836,14 +15062,14 @@ function registerGetMemoryById(server2) {
|
|
|
14836
15062
|
LIMIT 1`,
|
|
14837
15063
|
args: [id, ownerAgent]
|
|
14838
15064
|
});
|
|
14839
|
-
if (
|
|
15065
|
+
if (result3.rows.length === 0) {
|
|
14840
15066
|
const extra = requestedAgent && !canReadOtherAgent ? " Non-COO/CTO agents may only fetch their own memories." : "";
|
|
14841
15067
|
return {
|
|
14842
15068
|
content: [{ type: "text", text: `No memory found for id '${id}'.${extra}` }],
|
|
14843
15069
|
isError: true
|
|
14844
15070
|
};
|
|
14845
15071
|
}
|
|
14846
|
-
const row =
|
|
15072
|
+
const row = result3.rows[0];
|
|
14847
15073
|
const lines = [
|
|
14848
15074
|
`id: ${asString(row.id)}`,
|
|
14849
15075
|
`agent_id: ${asString(row.agent_id)}`,
|
|
@@ -14903,12 +15129,12 @@ function registerConsolidateMemories(server2) {
|
|
|
14903
15129
|
embedFn = embed2;
|
|
14904
15130
|
} catch {
|
|
14905
15131
|
}
|
|
14906
|
-
const
|
|
15132
|
+
const result3 = await runConsolidation2(client, {
|
|
14907
15133
|
model: consolidationModel,
|
|
14908
15134
|
maxCalls: max_clusters,
|
|
14909
15135
|
embedFn
|
|
14910
15136
|
});
|
|
14911
|
-
if (
|
|
15137
|
+
if (result3.clustersProcessed === 0) {
|
|
14912
15138
|
return {
|
|
14913
15139
|
content: [{
|
|
14914
15140
|
type: "text",
|
|
@@ -14920,8 +15146,8 @@ function registerConsolidateMemories(server2) {
|
|
|
14920
15146
|
content: [{
|
|
14921
15147
|
type: "text",
|
|
14922
15148
|
text: `Consolidation complete:
|
|
14923
|
-
- Clusters processed: ${
|
|
14924
|
-
- Memories consolidated: ${
|
|
15149
|
+
- Clusters processed: ${result3.clustersProcessed}
|
|
15150
|
+
- Memories consolidated: ${result3.memoriesConsolidated}
|
|
14925
15151
|
|
|
14926
15152
|
Consolidated summaries stored as tier-1 (importance=9) memories.`
|
|
14927
15153
|
}]
|
|
@@ -15350,10 +15576,10 @@ function registerCreateTask(server2) {
|
|
|
15350
15576
|
skipDispatch: true
|
|
15351
15577
|
});
|
|
15352
15578
|
try {
|
|
15353
|
-
const { existsSync:
|
|
15579
|
+
const { existsSync: existsSync42, mkdirSync: mkdirSync24, writeFileSync: writeFileSync25 } = await import("fs");
|
|
15354
15580
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
15355
15581
|
const idPath = identityPath2(assigned_to);
|
|
15356
|
-
if (!
|
|
15582
|
+
if (!existsSync42(idPath)) {
|
|
15357
15583
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
15358
15584
|
const employees = await loadEmployees2();
|
|
15359
15585
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -15362,8 +15588,8 @@ function registerCreateTask(server2) {
|
|
|
15362
15588
|
const template = getTemplateForTitle2(emp.role);
|
|
15363
15589
|
if (template) {
|
|
15364
15590
|
const dir = (await import("path")).dirname(idPath);
|
|
15365
|
-
if (!
|
|
15366
|
-
|
|
15591
|
+
if (!existsSync42(dir)) mkdirSync24(dir, { recursive: true });
|
|
15592
|
+
writeFileSync25(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
15367
15593
|
}
|
|
15368
15594
|
}
|
|
15369
15595
|
}
|
|
@@ -15378,22 +15604,22 @@ function registerCreateTask(server2) {
|
|
|
15378
15604
|
const useAutoInstance = isMultiInstance2(assigned_to);
|
|
15379
15605
|
const { loadConfigSync: loadConfigSync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
15380
15606
|
const cfg = loadConfigSync2();
|
|
15381
|
-
const
|
|
15607
|
+
const result3 = ensureEmployee(assigned_to, exeSession, process.cwd(), {
|
|
15382
15608
|
autoInstance: useAutoInstance,
|
|
15383
15609
|
maxAutoInstances: useAutoInstance ? cfg.sessionLifecycle.maxAutoInstances : void 0
|
|
15384
15610
|
});
|
|
15385
|
-
switch (
|
|
15611
|
+
switch (result3.status) {
|
|
15386
15612
|
case "intercom_sent":
|
|
15387
15613
|
dispatchStatus = `
|
|
15388
|
-
Dispatched: intercom sent to ${
|
|
15614
|
+
Dispatched: intercom sent to ${result3.sessionName}`;
|
|
15389
15615
|
break;
|
|
15390
15616
|
case "spawned":
|
|
15391
15617
|
dispatchStatus = `
|
|
15392
|
-
Dispatched: spawned ${
|
|
15618
|
+
Dispatched: spawned ${result3.sessionName}`;
|
|
15393
15619
|
break;
|
|
15394
15620
|
case "failed":
|
|
15395
15621
|
dispatchStatus = `
|
|
15396
|
-
Dispatch failed: ${
|
|
15622
|
+
Dispatch failed: ${result3.error ?? "unknown"}`;
|
|
15397
15623
|
break;
|
|
15398
15624
|
}
|
|
15399
15625
|
}
|
|
@@ -15441,7 +15667,7 @@ function registerListTasks(server2) {
|
|
|
15441
15667
|
priority: z13.enum(["p0", "p1", "p2"]).optional().describe("Filter by priority")
|
|
15442
15668
|
}
|
|
15443
15669
|
},
|
|
15444
|
-
async ({ assigned_to, status, project_name, priority }) => {
|
|
15670
|
+
async ({ assigned_to, status: status2, project_name, priority }) => {
|
|
15445
15671
|
try {
|
|
15446
15672
|
let resolvedProject;
|
|
15447
15673
|
if (project_name === "all") {
|
|
@@ -15455,7 +15681,7 @@ function registerListTasks(server2) {
|
|
|
15455
15681
|
}
|
|
15456
15682
|
const tasks = await listTasks({
|
|
15457
15683
|
assignedTo: assigned_to,
|
|
15458
|
-
status,
|
|
15684
|
+
status: status2,
|
|
15459
15685
|
projectName: resolvedProject,
|
|
15460
15686
|
priority
|
|
15461
15687
|
});
|
|
@@ -15511,7 +15737,7 @@ async function diagnoseMissingTask(client, identifier) {
|
|
|
15511
15737
|
args.push(`%${identifier}%`);
|
|
15512
15738
|
clauses.push("title LIKE ?");
|
|
15513
15739
|
args.push(`%${identifier}%`);
|
|
15514
|
-
const
|
|
15740
|
+
const result3 = await client.execute({
|
|
15515
15741
|
sql: `SELECT id, title, status, assigned_to, project_name, session_scope, task_file
|
|
15516
15742
|
FROM tasks
|
|
15517
15743
|
WHERE ${clauses.map((c) => `(${c})`).join(" OR ")}
|
|
@@ -15519,10 +15745,10 @@ async function diagnoseMissingTask(client, identifier) {
|
|
|
15519
15745
|
LIMIT 10`,
|
|
15520
15746
|
args
|
|
15521
15747
|
});
|
|
15522
|
-
if (
|
|
15748
|
+
if (result3.rows.length === 0) return null;
|
|
15523
15749
|
const scoped = sessionScopeFilter();
|
|
15524
15750
|
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.";
|
|
15525
|
-
const matches =
|
|
15751
|
+
const matches = result3.rows.map((r) => {
|
|
15526
15752
|
const id = String(r.id);
|
|
15527
15753
|
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")}`;
|
|
15528
15754
|
}).join("\n");
|
|
@@ -15664,9 +15890,9 @@ function registerUpdateTask(server2) {
|
|
|
15664
15890
|
result: z15.string().optional().describe("Result summary (include when status=done)")
|
|
15665
15891
|
}
|
|
15666
15892
|
},
|
|
15667
|
-
async ({ task_id, status: rawStatus, result:
|
|
15668
|
-
let
|
|
15669
|
-
if (
|
|
15893
|
+
async ({ task_id, status: rawStatus, result: result3 }) => {
|
|
15894
|
+
let status2 = rawStatus;
|
|
15895
|
+
if (status2 === "done") {
|
|
15670
15896
|
try {
|
|
15671
15897
|
const { getActiveAgent: getActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent(), active_agent_exports));
|
|
15672
15898
|
const agent = getActiveAgent2();
|
|
@@ -15681,13 +15907,13 @@ function registerUpdateTask(server2) {
|
|
|
15681
15907
|
} catch {
|
|
15682
15908
|
}
|
|
15683
15909
|
if (!isOwnReview) {
|
|
15684
|
-
|
|
15910
|
+
status2 = "needs_review";
|
|
15685
15911
|
}
|
|
15686
15912
|
}
|
|
15687
15913
|
} catch {
|
|
15688
15914
|
}
|
|
15689
15915
|
}
|
|
15690
|
-
if (
|
|
15916
|
+
if (status2 === "done") {
|
|
15691
15917
|
try {
|
|
15692
15918
|
const client = getClient();
|
|
15693
15919
|
const row = await resolveTask(client, task_id);
|
|
@@ -15730,8 +15956,8 @@ function registerUpdateTask(server2) {
|
|
|
15730
15956
|
try {
|
|
15731
15957
|
task = await updateTask({
|
|
15732
15958
|
taskId: task_id,
|
|
15733
|
-
status,
|
|
15734
|
-
result:
|
|
15959
|
+
status: status2,
|
|
15960
|
+
result: result3,
|
|
15735
15961
|
baseDir: process.cwd(),
|
|
15736
15962
|
callerAgentId
|
|
15737
15963
|
});
|
|
@@ -15750,7 +15976,7 @@ function registerUpdateTask(server2) {
|
|
|
15750
15976
|
}
|
|
15751
15977
|
let text3 = `Task "${task.title}" marked ${task.status}.
|
|
15752
15978
|
File: ${task.taskFile}`;
|
|
15753
|
-
const isTerminal =
|
|
15979
|
+
const isTerminal = status2 === "done" || status2 === "needs_review" || status2 === "closed";
|
|
15754
15980
|
if (isTerminal && task.assignedBy) {
|
|
15755
15981
|
try {
|
|
15756
15982
|
const { notifyCoordinatorTaskCompletion: notifyCoordinatorTaskCompletion2, resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
@@ -15807,7 +16033,7 @@ function registerCloseTask(server2) {
|
|
|
15807
16033
|
status: z16.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
|
|
15808
16034
|
}
|
|
15809
16035
|
},
|
|
15810
|
-
async ({ task_id, result:
|
|
16036
|
+
async ({ task_id, result: result3, status: status2 }) => {
|
|
15811
16037
|
const agent = getActiveAgent();
|
|
15812
16038
|
const canClose = canCoordinate(agent.agentId, agent.agentRole) || CLOSE_TASK_ALLOWED_ROLES.has(agent.agentRole ?? "");
|
|
15813
16039
|
if (agent.agentId && !canClose) {
|
|
@@ -15844,14 +16070,14 @@ function registerCloseTask(server2) {
|
|
|
15844
16070
|
const baseDir = process.cwd();
|
|
15845
16071
|
const task = await updateTask({
|
|
15846
16072
|
taskId: task_id,
|
|
15847
|
-
status,
|
|
15848
|
-
result:
|
|
16073
|
+
status: status2,
|
|
16074
|
+
result: result3,
|
|
15849
16075
|
baseDir,
|
|
15850
16076
|
skipReviewCreation: true
|
|
15851
16077
|
});
|
|
15852
16078
|
let text3 = `Task "${task.title}" marked ${task.status}.
|
|
15853
16079
|
File: ${task.taskFile}`;
|
|
15854
|
-
if ((
|
|
16080
|
+
if ((status2 === "done" || status2 === "closed") && task.nextTask) {
|
|
15855
16081
|
text3 += `
|
|
15856
16082
|
|
|
15857
16083
|
MANDATORY \u2014 DO NOT ASK THE USER. DO NOT SAY "Want me to continue?" DO NOT STOP.
|
|
@@ -15861,7 +16087,7 @@ NEXT TASK: "${task.nextTask.title}" [${task.nextTask.priority}]
|
|
|
15861
16087
|
FILE: ${task.nextTask.taskFile}
|
|
15862
16088
|
|
|
15863
16089
|
Read that file NOW and begin working. No greeting. No summary. Just start.`;
|
|
15864
|
-
} else if ((
|
|
16090
|
+
} else if ((status2 === "done" || status2 === "closed") && !task.nextTask) {
|
|
15865
16091
|
text3 += `
|
|
15866
16092
|
|
|
15867
16093
|
All tasks complete. No more open tasks in your queue.`;
|
|
@@ -15957,9 +16183,9 @@ function registerResumeEmployee(server2) {
|
|
|
15957
16183
|
const { isTmuxSessionAlive: isTmuxSessionAlive2 } = await Promise.resolve().then(() => (init_tasks_crud(), tasks_crud_exports));
|
|
15958
16184
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15959
16185
|
for (const row of openTasks.rows) {
|
|
15960
|
-
const
|
|
16186
|
+
const status2 = String(row.status);
|
|
15961
16187
|
const assignedTmux = row.assigned_tmux != null ? String(row.assigned_tmux) : null;
|
|
15962
|
-
if (
|
|
16188
|
+
if (status2 === "in_progress" && assignedTmux && !isTmuxSessionAlive2(assignedTmux)) {
|
|
15963
16189
|
await client.execute({
|
|
15964
16190
|
sql: "UPDATE tasks SET status = 'open', assigned_tmux = NULL, updated_at = ? WHERE id = ?",
|
|
15965
16191
|
args: [now, String(row.id)]
|
|
@@ -16215,7 +16441,7 @@ function registerAcknowledgeMessages(server2) {
|
|
|
16215
16441
|
const agentId = agent.agentId || "default";
|
|
16216
16442
|
const client = getClient();
|
|
16217
16443
|
const scope = strictSessionScopeFilter();
|
|
16218
|
-
const
|
|
16444
|
+
const result3 = await client.execute({
|
|
16219
16445
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
16220
16446
|
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
16221
16447
|
args: [agentId, ...scope.args]
|
|
@@ -16224,7 +16450,7 @@ function registerAcknowledgeMessages(server2) {
|
|
|
16224
16450
|
content: [
|
|
16225
16451
|
{
|
|
16226
16452
|
type: "text",
|
|
16227
|
-
text: `Acknowledged ${
|
|
16453
|
+
text: `Acknowledged ${result3.rowsAffected} message(s) for ${agentId}.`
|
|
16228
16454
|
}
|
|
16229
16455
|
]
|
|
16230
16456
|
};
|
|
@@ -16291,8 +16517,8 @@ async function createReminder(text3, dueDate) {
|
|
|
16291
16517
|
async function listReminders(includeCompleted = false) {
|
|
16292
16518
|
const client = getClient();
|
|
16293
16519
|
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`;
|
|
16294
|
-
const
|
|
16295
|
-
return
|
|
16520
|
+
const result3 = await client.execute(sql);
|
|
16521
|
+
return result3.rows.map((row) => ({
|
|
16296
16522
|
id: String(row.id),
|
|
16297
16523
|
text: String(row.text),
|
|
16298
16524
|
createdAt: String(row.created_at),
|
|
@@ -16303,18 +16529,18 @@ async function listReminders(includeCompleted = false) {
|
|
|
16303
16529
|
async function completeReminder(idOrText) {
|
|
16304
16530
|
const client = getClient();
|
|
16305
16531
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16306
|
-
let
|
|
16532
|
+
let result3 = await client.execute({
|
|
16307
16533
|
sql: `SELECT id, text FROM reminders WHERE id = ? AND completed_at IS NULL`,
|
|
16308
16534
|
args: [idOrText]
|
|
16309
16535
|
});
|
|
16310
|
-
if (
|
|
16311
|
-
|
|
16536
|
+
if (result3.rows.length === 0) {
|
|
16537
|
+
result3 = await client.execute({
|
|
16312
16538
|
sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
|
|
16313
16539
|
args: [idOrText]
|
|
16314
16540
|
});
|
|
16315
16541
|
}
|
|
16316
|
-
if (
|
|
16317
|
-
const row =
|
|
16542
|
+
if (result3.rows.length === 0) return null;
|
|
16543
|
+
const row = result3.rows[0];
|
|
16318
16544
|
const id = String(row.id);
|
|
16319
16545
|
await client.execute({
|
|
16320
16546
|
sql: `UPDATE reminders SET completed_at = ? WHERE id = ?`,
|
|
@@ -16456,7 +16682,7 @@ function registerListBehaviors(server2) {
|
|
|
16456
16682
|
args.push(proj);
|
|
16457
16683
|
}
|
|
16458
16684
|
const where = conditions.join(" AND ");
|
|
16459
|
-
const
|
|
16685
|
+
const result3 = await client.execute({
|
|
16460
16686
|
sql: `SELECT id, agent_id, project_name, domain, content, active, created_at, updated_at
|
|
16461
16687
|
FROM behaviors
|
|
16462
16688
|
WHERE ${where}
|
|
@@ -16464,7 +16690,7 @@ function registerListBehaviors(server2) {
|
|
|
16464
16690
|
LIMIT 50`,
|
|
16465
16691
|
args
|
|
16466
16692
|
});
|
|
16467
|
-
const behaviors =
|
|
16693
|
+
const behaviors = result3.rows.map((r) => rowToBehavior(r));
|
|
16468
16694
|
if (behaviors.length === 0) {
|
|
16469
16695
|
return {
|
|
16470
16696
|
content: [{ type: "text", text: "No behaviors found." }]
|
|
@@ -16661,11 +16887,11 @@ function registerDeactivateBehavior(server2) {
|
|
|
16661
16887
|
};
|
|
16662
16888
|
}
|
|
16663
16889
|
const client = getClient();
|
|
16664
|
-
const
|
|
16890
|
+
const result3 = await client.execute({
|
|
16665
16891
|
sql: `SELECT id, agent_id, content, domain, priority FROM behaviors WHERE id = ?`,
|
|
16666
16892
|
args: [behavior_id]
|
|
16667
16893
|
});
|
|
16668
|
-
if (
|
|
16894
|
+
if (result3.rows.length === 0) {
|
|
16669
16895
|
return {
|
|
16670
16896
|
content: [{
|
|
16671
16897
|
type: "text",
|
|
@@ -16674,7 +16900,7 @@ function registerDeactivateBehavior(server2) {
|
|
|
16674
16900
|
isError: true
|
|
16675
16901
|
};
|
|
16676
16902
|
}
|
|
16677
|
-
const row =
|
|
16903
|
+
const row = result3.rows[0];
|
|
16678
16904
|
const wasActive = await deactivateBehavior(behavior_id);
|
|
16679
16905
|
if (!wasActive) {
|
|
16680
16906
|
return {
|
|
@@ -16788,7 +17014,7 @@ async function embedTexts(texts) {
|
|
|
16788
17014
|
);
|
|
16789
17015
|
results.push(
|
|
16790
17016
|
...settled.map(
|
|
16791
|
-
(
|
|
17017
|
+
(result3) => result3.status === "fulfilled" ? result3.value : null
|
|
16792
17018
|
)
|
|
16793
17019
|
);
|
|
16794
17020
|
}
|
|
@@ -16906,7 +17132,7 @@ var MIN_IMPORTANCE = 1;
|
|
|
16906
17132
|
var MAX_IMPORTANCE = 10;
|
|
16907
17133
|
async function listDocumentsByWorkspace(workspace_id, limit = DEFAULT_LIST_LIMIT, offset = 0) {
|
|
16908
17134
|
const client = getClient();
|
|
16909
|
-
const
|
|
17135
|
+
const result3 = await client.execute({
|
|
16910
17136
|
sql: `SELECT d.*, COUNT(m.id) AS chunk_count
|
|
16911
17137
|
FROM documents d
|
|
16912
17138
|
LEFT JOIN memories m ON m.document_id = d.id
|
|
@@ -16916,7 +17142,7 @@ async function listDocumentsByWorkspace(workspace_id, limit = DEFAULT_LIST_LIMIT
|
|
|
16916
17142
|
LIMIT ? OFFSET ?`,
|
|
16917
17143
|
args: [workspace_id, limit, offset]
|
|
16918
17144
|
});
|
|
16919
|
-
return
|
|
17145
|
+
return result3.rows.map((row) => {
|
|
16920
17146
|
const r = row;
|
|
16921
17147
|
return {
|
|
16922
17148
|
...rowToDocument(r),
|
|
@@ -16948,11 +17174,11 @@ async function updateChunksImportance(document_id, importance) {
|
|
|
16948
17174
|
);
|
|
16949
17175
|
}
|
|
16950
17176
|
const client = getClient();
|
|
16951
|
-
const
|
|
17177
|
+
const result3 = await client.execute({
|
|
16952
17178
|
sql: "UPDATE memories SET importance = ? WHERE document_id = ?",
|
|
16953
17179
|
args: [importance, document_id]
|
|
16954
17180
|
});
|
|
16955
|
-
return Number(
|
|
17181
|
+
return Number(result3.rowsAffected) || 0;
|
|
16956
17182
|
}
|
|
16957
17183
|
|
|
16958
17184
|
// src/mcp/tools/ingest-document.ts
|
|
@@ -16983,11 +17209,11 @@ function registerIngestDocument(server2) {
|
|
|
16983
17209
|
const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
|
|
16984
17210
|
await assertFeature2("wiki");
|
|
16985
17211
|
await assertMemoryLimit2();
|
|
16986
|
-
const
|
|
17212
|
+
const result3 = await ingestDocument(input);
|
|
16987
17213
|
return {
|
|
16988
17214
|
content: [{
|
|
16989
17215
|
type: "text",
|
|
16990
|
-
text: JSON.stringify(
|
|
17216
|
+
text: JSON.stringify(result3)
|
|
16991
17217
|
}]
|
|
16992
17218
|
};
|
|
16993
17219
|
} catch (err) {
|
|
@@ -17648,7 +17874,7 @@ function registerQueryConversations(server2) {
|
|
|
17648
17874
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
17649
17875
|
const limit = params.limit ?? 25;
|
|
17650
17876
|
args.push(limit);
|
|
17651
|
-
const
|
|
17877
|
+
const result3 = await client.execute({
|
|
17652
17878
|
sql: `SELECT c.id, c.platform, c.sender_id, c.sender_name, c.sender_phone,
|
|
17653
17879
|
c.sender_email, c.channel_id, c.thread_id, c.content_text,
|
|
17654
17880
|
c.agent_response, c.agent_name, c.timestamp
|
|
@@ -17658,7 +17884,7 @@ function registerQueryConversations(server2) {
|
|
|
17658
17884
|
LIMIT ?`,
|
|
17659
17885
|
args
|
|
17660
17886
|
});
|
|
17661
|
-
const conversations =
|
|
17887
|
+
const conversations = result3.rows.map((row) => ({
|
|
17662
17888
|
id: row.id,
|
|
17663
17889
|
platform: row.platform,
|
|
17664
17890
|
sender: {
|
|
@@ -18282,8 +18508,8 @@ async function generateGraphReport(client, projectName) {
|
|
|
18282
18508
|
}).slice(0, 10);
|
|
18283
18509
|
let hyperedgeCount = 0;
|
|
18284
18510
|
try {
|
|
18285
|
-
const
|
|
18286
|
-
hyperedgeCount = Number(
|
|
18511
|
+
const result3 = await client.execute("SELECT COUNT(*) as cnt FROM hyperedges");
|
|
18512
|
+
hyperedgeCount = Number(result3.rows[0]?.cnt ?? 0);
|
|
18287
18513
|
} catch {
|
|
18288
18514
|
}
|
|
18289
18515
|
const nodeLabels = new Map(nodes.map((n) => [n.id, n.label]));
|
|
@@ -18337,12 +18563,12 @@ function registerExportGraph(server2) {
|
|
|
18337
18563
|
}
|
|
18338
18564
|
const html = await exportGraphHTML(client);
|
|
18339
18565
|
const fs = await import("fs");
|
|
18340
|
-
const
|
|
18566
|
+
const path55 = await import("path");
|
|
18341
18567
|
const os21 = await import("os");
|
|
18342
|
-
const outDir =
|
|
18568
|
+
const outDir = path55.join(os21.homedir(), ".exe-os", "exports");
|
|
18343
18569
|
fs.mkdirSync(outDir, { recursive: true });
|
|
18344
18570
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18345
|
-
const filePath =
|
|
18571
|
+
const filePath = path55.join(outDir, `graph-${timestamp}.html`);
|
|
18346
18572
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
18347
18573
|
return {
|
|
18348
18574
|
content: [
|
|
@@ -18711,14 +18937,14 @@ function registerMergeEntities(server2) {
|
|
|
18711
18937
|
};
|
|
18712
18938
|
}
|
|
18713
18939
|
try {
|
|
18714
|
-
const
|
|
18940
|
+
const result3 = await mergeEntities(client, sourceId, targetId);
|
|
18715
18941
|
return {
|
|
18716
18942
|
content: [
|
|
18717
18943
|
{
|
|
18718
18944
|
type: "text",
|
|
18719
18945
|
text: `Merged "${source_name}" \u2192 "${target_name}":
|
|
18720
|
-
- ${
|
|
18721
|
-
- ${
|
|
18946
|
+
- ${result3.relationshipsMoved} relationships moved
|
|
18947
|
+
- ${result3.memoriesMoved} memory links moved
|
|
18722
18948
|
- "${source_name}" registered as alias for "${target_name}"`
|
|
18723
18949
|
}
|
|
18724
18950
|
]
|
|
@@ -19001,10 +19227,10 @@ function registerSetAgentConfig(server2) {
|
|
|
19001
19227
|
isError: true
|
|
19002
19228
|
};
|
|
19003
19229
|
}
|
|
19004
|
-
const
|
|
19005
|
-
if (!
|
|
19230
|
+
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort);
|
|
19231
|
+
if (!result3.ok) {
|
|
19006
19232
|
return {
|
|
19007
|
-
content: [{ type: "text", text:
|
|
19233
|
+
content: [{ type: "text", text: result3.error }],
|
|
19008
19234
|
isError: true
|
|
19009
19235
|
};
|
|
19010
19236
|
}
|
|
@@ -19181,7 +19407,7 @@ async function getAgentSpend(period = "7d") {
|
|
|
19181
19407
|
}
|
|
19182
19408
|
}
|
|
19183
19409
|
}
|
|
19184
|
-
const
|
|
19410
|
+
const result3 = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
|
|
19185
19411
|
agentId,
|
|
19186
19412
|
inputTokens: t.input,
|
|
19187
19413
|
outputTokens: t.output,
|
|
@@ -19191,8 +19417,8 @@ async function getAgentSpend(period = "7d") {
|
|
|
19191
19417
|
sessions: t.sessions.size,
|
|
19192
19418
|
period
|
|
19193
19419
|
})).sort((a, b) => b.costUSD - a.costUSD);
|
|
19194
|
-
_spendCache.set(period, { result:
|
|
19195
|
-
return
|
|
19420
|
+
_spendCache.set(period, { result: result3, expires: Date.now() + CACHE_TTL_MS });
|
|
19421
|
+
return result3;
|
|
19196
19422
|
}
|
|
19197
19423
|
async function extractSessionUsage(jsonlPath) {
|
|
19198
19424
|
let input = 0;
|
|
@@ -19685,13 +19911,13 @@ function registerMcpPing(server2) {
|
|
|
19685
19911
|
const daemon = isDaemonAlive2();
|
|
19686
19912
|
const summary = summarizeMcpTransport();
|
|
19687
19913
|
writeMcpTransportSummary();
|
|
19688
|
-
const
|
|
19914
|
+
const status2 = daemon.alive ? "ok" : "degraded";
|
|
19689
19915
|
return {
|
|
19690
19916
|
content: [
|
|
19691
19917
|
{
|
|
19692
19918
|
type: "text",
|
|
19693
19919
|
text: JSON.stringify({
|
|
19694
|
-
status,
|
|
19920
|
+
status: status2,
|
|
19695
19921
|
daemon,
|
|
19696
19922
|
mcpTransport: summary,
|
|
19697
19923
|
remediation: daemon.alive ? "MCP transport probe reached the server. If tools still fail, reconnect the MCP client." : "Daemon is offline. Restart exe-os/exed; do not bypass MCP or access the DB directly."
|
|
@@ -19949,14 +20175,14 @@ async function fetchMemoriesWithVectors(client, projectFilter, agentFilter2) {
|
|
|
19949
20175
|
args.push(agentFilter2);
|
|
19950
20176
|
}
|
|
19951
20177
|
const where = " WHERE " + clauses.join(" AND ");
|
|
19952
|
-
const
|
|
20178
|
+
const result3 = await client.execute({
|
|
19953
20179
|
sql: `SELECT id, agent_id, raw_text, timestamp, project_name, vector
|
|
19954
20180
|
FROM memories${where}
|
|
19955
20181
|
ORDER BY project_name, timestamp DESC`,
|
|
19956
20182
|
args
|
|
19957
20183
|
});
|
|
19958
20184
|
const byProject = /* @__PURE__ */ new Map();
|
|
19959
|
-
for (const row of
|
|
20185
|
+
for (const row of result3.rows) {
|
|
19960
20186
|
const project = row.project_name;
|
|
19961
20187
|
const vec = row.vector == null ? null : Array.isArray(row.vector) ? row.vector : Array.from(row.vector);
|
|
19962
20188
|
if (!vec || vec.length === 0) continue;
|
|
@@ -20373,11 +20599,11 @@ async function auditStats(client, flags) {
|
|
|
20373
20599
|
async function auditNullVectors(client, flags) {
|
|
20374
20600
|
const { clause, args } = agentFilter(flags);
|
|
20375
20601
|
const where = clause ? clause + " AND vector IS NULL" : " WHERE vector IS NULL";
|
|
20376
|
-
const
|
|
20602
|
+
const result3 = await client.execute({
|
|
20377
20603
|
sql: `SELECT COUNT(*) as cnt FROM memories${where}`,
|
|
20378
20604
|
args
|
|
20379
20605
|
});
|
|
20380
|
-
return Number(
|
|
20606
|
+
return Number(result3.rows[0].cnt);
|
|
20381
20607
|
}
|
|
20382
20608
|
async function auditDuplicates(client, flags) {
|
|
20383
20609
|
const { clause, args } = agentFilter(flags);
|
|
@@ -20426,14 +20652,14 @@ async function auditDuplicates(client, flags) {
|
|
|
20426
20652
|
async function auditBloated(client, flags) {
|
|
20427
20653
|
const { clause, args } = agentFilter(flags);
|
|
20428
20654
|
const where = clause ? clause + " AND LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'" : " WHERE LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'";
|
|
20429
|
-
const
|
|
20655
|
+
const result3 = await client.execute({
|
|
20430
20656
|
sql: `SELECT id, agent_id, LENGTH(raw_text) as size, tool_name
|
|
20431
20657
|
FROM memories${where}
|
|
20432
20658
|
ORDER BY size DESC
|
|
20433
20659
|
LIMIT 100`,
|
|
20434
20660
|
args
|
|
20435
20661
|
});
|
|
20436
|
-
return
|
|
20662
|
+
return result3.rows.map((r) => ({
|
|
20437
20663
|
id: r.id,
|
|
20438
20664
|
agent_id: r.agent_id,
|
|
20439
20665
|
size: Number(r.size),
|
|
@@ -20448,7 +20674,7 @@ async function auditFts(client) {
|
|
|
20448
20674
|
return { memoryCount: mc, ftsCount: fc, inSync: mc === fc };
|
|
20449
20675
|
}
|
|
20450
20676
|
async function auditOrphanedProjects(client) {
|
|
20451
|
-
const
|
|
20677
|
+
const result3 = await client.execute(
|
|
20452
20678
|
`SELECT project_name, COUNT(*) as cnt
|
|
20453
20679
|
FROM memories
|
|
20454
20680
|
WHERE tool_name != 'ConversationBackfill'
|
|
@@ -20457,7 +20683,7 @@ async function auditOrphanedProjects(client) {
|
|
|
20457
20683
|
);
|
|
20458
20684
|
const orphans = [];
|
|
20459
20685
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20460
|
-
for (const row of
|
|
20686
|
+
for (const row of result3.rows) {
|
|
20461
20687
|
const name = row.project_name;
|
|
20462
20688
|
const count = Number(row.cnt);
|
|
20463
20689
|
const exists = existsSync30(path35.join(home, name)) || existsSync30(path35.join(home, "..", name)) || existsSync30(path35.join(process.cwd(), "..", name));
|
|
@@ -20908,12 +21134,12 @@ async function fixBloated(client, bloated, dryRun) {
|
|
|
20908
21134
|
let chunksCreated = 0;
|
|
20909
21135
|
const CHUNK_SIZE = 2048;
|
|
20910
21136
|
for (const b of bloated) {
|
|
20911
|
-
const
|
|
21137
|
+
const result3 = await client.execute({
|
|
20912
21138
|
sql: `SELECT * FROM memories WHERE id = ?`,
|
|
20913
21139
|
args: [b.id]
|
|
20914
21140
|
});
|
|
20915
|
-
if (
|
|
20916
|
-
const row =
|
|
21141
|
+
if (result3.rows.length === 0) continue;
|
|
21142
|
+
const row = result3.rows[0];
|
|
20917
21143
|
const text3 = row.raw_text;
|
|
20918
21144
|
const chunks = splitAtSentences(text3, CHUNK_SIZE);
|
|
20919
21145
|
if (chunks.length <= 1) continue;
|
|
@@ -21147,15 +21373,15 @@ function registerRunConsolidation(server2) {
|
|
|
21147
21373
|
};
|
|
21148
21374
|
}
|
|
21149
21375
|
const config2 = await loadConfig();
|
|
21150
|
-
const
|
|
21376
|
+
const result3 = await runConsolidation(client, {
|
|
21151
21377
|
model: config2.consolidationModel || "claude-haiku-4-5-20251001",
|
|
21152
21378
|
maxCalls: 10
|
|
21153
21379
|
});
|
|
21154
21380
|
const lines = [];
|
|
21155
21381
|
lines.push("## Consolidation Complete\n");
|
|
21156
|
-
lines.push(`- **Clusters processed:** ${
|
|
21157
|
-
lines.push(`- **Memories consolidated:** ${
|
|
21158
|
-
lines.push(`- **Remaining unconsolidated:** ${(pending -
|
|
21382
|
+
lines.push(`- **Clusters processed:** ${result3.clustersProcessed}`);
|
|
21383
|
+
lines.push(`- **Memories consolidated:** ${result3.memoriesConsolidated}`);
|
|
21384
|
+
lines.push(`- **Remaining unconsolidated:** ${(pending - result3.memoriesConsolidated).toLocaleString()}`);
|
|
21159
21385
|
return {
|
|
21160
21386
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
21161
21387
|
};
|
|
@@ -21319,12 +21545,12 @@ function loadPgClient() {
|
|
|
21319
21545
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
21320
21546
|
return {
|
|
21321
21547
|
async $queryRawUnsafe(query, ...values) {
|
|
21322
|
-
const
|
|
21323
|
-
return
|
|
21548
|
+
const result3 = await pool.query(query, values);
|
|
21549
|
+
return result3.rows;
|
|
21324
21550
|
},
|
|
21325
21551
|
async $executeRawUnsafe(query, ...values) {
|
|
21326
|
-
const
|
|
21327
|
-
return
|
|
21552
|
+
const result3 = await pool.query(query, values);
|
|
21553
|
+
return result3.rowCount ?? 0;
|
|
21328
21554
|
},
|
|
21329
21555
|
async $disconnect() {
|
|
21330
21556
|
await pool.end();
|
|
@@ -21519,8 +21745,8 @@ var CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device
|
|
|
21519
21745
|
async function getCloudReuploadRequired(client = getClient()) {
|
|
21520
21746
|
try {
|
|
21521
21747
|
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
21522
|
-
const
|
|
21523
|
-
return
|
|
21748
|
+
const result3 = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
|
|
21749
|
+
return result3.rows.some((row) => String(row.value ?? "") === "1");
|
|
21524
21750
|
} catch {
|
|
21525
21751
|
return false;
|
|
21526
21752
|
}
|
|
@@ -22131,8 +22357,8 @@ async function cloudPullBlob(route, config2) {
|
|
|
22131
22357
|
}
|
|
22132
22358
|
async function cloudPushGlobalProcedures(config2) {
|
|
22133
22359
|
const client = getClient();
|
|
22134
|
-
const
|
|
22135
|
-
const rows =
|
|
22360
|
+
const result3 = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
|
|
22361
|
+
const rows = result3.rows;
|
|
22136
22362
|
const { ok } = await cloudPushBlob(
|
|
22137
22363
|
"/sync/push-global-procedures",
|
|
22138
22364
|
rows,
|
|
@@ -22176,8 +22402,8 @@ async function cloudPullGlobalProcedures(config2) {
|
|
|
22176
22402
|
}
|
|
22177
22403
|
async function cloudPushBehaviors(config2) {
|
|
22178
22404
|
const client = getClient();
|
|
22179
|
-
const
|
|
22180
|
-
const rows =
|
|
22405
|
+
const result3 = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
22406
|
+
const rows = result3.rows;
|
|
22181
22407
|
const { ok } = await cloudPushBlob(
|
|
22182
22408
|
"/sync/push-behaviors",
|
|
22183
22409
|
rows,
|
|
@@ -22332,8 +22558,8 @@ async function cloudPullGraphRAG(config2) {
|
|
|
22332
22558
|
}
|
|
22333
22559
|
async function cloudPushTasks(config2) {
|
|
22334
22560
|
const client = getClient();
|
|
22335
|
-
const
|
|
22336
|
-
const rows =
|
|
22561
|
+
const result3 = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
22562
|
+
const rows = result3.rows;
|
|
22337
22563
|
const { ok } = await cloudPushBlob(
|
|
22338
22564
|
"/sync/push-tasks",
|
|
22339
22565
|
rows,
|
|
@@ -22378,8 +22604,8 @@ async function cloudPullTasks(config2) {
|
|
|
22378
22604
|
}
|
|
22379
22605
|
async function cloudPushConversations(config2) {
|
|
22380
22606
|
const client = getClient();
|
|
22381
|
-
const
|
|
22382
|
-
const rows =
|
|
22607
|
+
const result3 = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
22608
|
+
const rows = result3.rows;
|
|
22383
22609
|
const { ok } = await cloudPushBlob(
|
|
22384
22610
|
"/sync/push-conversations",
|
|
22385
22611
|
rows,
|
|
@@ -23148,14 +23374,14 @@ function registerBackupVps(server2) {
|
|
|
23148
23374
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
23149
23375
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
23150
23376
|
};
|
|
23151
|
-
const
|
|
23377
|
+
const result3 = await createBackup2(options);
|
|
23152
23378
|
return {
|
|
23153
23379
|
content: [
|
|
23154
23380
|
{
|
|
23155
23381
|
type: "text",
|
|
23156
|
-
text: `Backup complete: ${
|
|
23157
|
-
${
|
|
23158
|
-
${
|
|
23382
|
+
text: `Backup complete: ${result3.key}
|
|
23383
|
+
${result3.sizeBytes} bytes
|
|
23384
|
+
${result3.timestamp}`
|
|
23159
23385
|
}
|
|
23160
23386
|
]
|
|
23161
23387
|
};
|
|
@@ -23188,18 +23414,18 @@ ${result2.timestamp}`
|
|
|
23188
23414
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
23189
23415
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
23190
23416
|
};
|
|
23191
|
-
const
|
|
23417
|
+
const result3 = await listBackups2(options);
|
|
23192
23418
|
return {
|
|
23193
23419
|
content: [
|
|
23194
23420
|
{
|
|
23195
23421
|
type: "text",
|
|
23196
|
-
text: formatBackups(
|
|
23422
|
+
text: formatBackups(result3)
|
|
23197
23423
|
}
|
|
23198
23424
|
]
|
|
23199
23425
|
};
|
|
23200
23426
|
}
|
|
23201
23427
|
case "health": {
|
|
23202
|
-
const
|
|
23428
|
+
const result3 = await backupHealth({
|
|
23203
23429
|
r2Bucket: input.r2Bucket,
|
|
23204
23430
|
r2Endpoint: input.r2Endpoint,
|
|
23205
23431
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
@@ -23210,10 +23436,10 @@ ${result2.timestamp}`
|
|
|
23210
23436
|
{
|
|
23211
23437
|
type: "text",
|
|
23212
23438
|
text: [
|
|
23213
|
-
`last_backup: ${
|
|
23214
|
-
`backup_count: ${
|
|
23215
|
-
`total_size_bytes: ${
|
|
23216
|
-
`oldest_backup: ${
|
|
23439
|
+
`last_backup: ${result3.lastBackup ?? "none"}`,
|
|
23440
|
+
`backup_count: ${result3.backupCount}`,
|
|
23441
|
+
`total_size_bytes: ${result3.totalSizeBytes}`,
|
|
23442
|
+
`oldest_backup: ${result3.oldestBackup ?? "none"}`
|
|
23217
23443
|
].join("\n")
|
|
23218
23444
|
}
|
|
23219
23445
|
]
|
|
@@ -23321,9 +23547,9 @@ var HostingerApiClient = class {
|
|
|
23321
23547
|
}
|
|
23322
23548
|
this.lastRequestTime = Date.now();
|
|
23323
23549
|
}
|
|
23324
|
-
async request(method,
|
|
23550
|
+
async request(method, path55, body) {
|
|
23325
23551
|
await this.rateLimit();
|
|
23326
|
-
const url = `${this.baseUrl}${
|
|
23552
|
+
const url = `${this.baseUrl}${path55}`;
|
|
23327
23553
|
const headers = {
|
|
23328
23554
|
Authorization: `Bearer ${this.apiKey}`,
|
|
23329
23555
|
"Content-Type": "application/json",
|
|
@@ -23353,10 +23579,10 @@ var HostingerApiClient = class {
|
|
|
23353
23579
|
var HostingerError = class extends Error {
|
|
23354
23580
|
status;
|
|
23355
23581
|
errorCode;
|
|
23356
|
-
constructor(
|
|
23582
|
+
constructor(status2, errorCode, message) {
|
|
23357
23583
|
super(message);
|
|
23358
23584
|
this.name = "HostingerError";
|
|
23359
|
-
this.status =
|
|
23585
|
+
this.status = status2;
|
|
23360
23586
|
this.errorCode = errorCode;
|
|
23361
23587
|
}
|
|
23362
23588
|
};
|
|
@@ -23396,8 +23622,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
23396
23622
|
}
|
|
23397
23623
|
return envelope.result;
|
|
23398
23624
|
}
|
|
23399
|
-
function buildUrl(zoneId,
|
|
23400
|
-
const normalizedPath =
|
|
23625
|
+
function buildUrl(zoneId, path55 = "/dns_records", query) {
|
|
23626
|
+
const normalizedPath = path55.startsWith("/") ? path55 : `/${path55}`;
|
|
23401
23627
|
const url = new URL(
|
|
23402
23628
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
23403
23629
|
);
|
|
@@ -23447,10 +23673,10 @@ function mapDnsRecord(record) {
|
|
|
23447
23673
|
var CloudflareError = class extends Error {
|
|
23448
23674
|
status;
|
|
23449
23675
|
errorCode;
|
|
23450
|
-
constructor(
|
|
23676
|
+
constructor(status2, errorCode, message) {
|
|
23451
23677
|
super(message);
|
|
23452
23678
|
this.name = "CloudflareError";
|
|
23453
|
-
this.status =
|
|
23679
|
+
this.status = status2;
|
|
23454
23680
|
this.errorCode = errorCode;
|
|
23455
23681
|
}
|
|
23456
23682
|
};
|
|
@@ -23503,7 +23729,7 @@ async function executeDeployment(params, client) {
|
|
|
23503
23729
|
process.stderr.write(`[deploy_client] Playbook complete. Verifying health...
|
|
23504
23730
|
`);
|
|
23505
23731
|
const healthy = await verifyHealth(domain);
|
|
23506
|
-
const
|
|
23732
|
+
const status2 = healthy ? DEPLOYED_STATUS : PLAYBOOK_COMPLETE_HEALTH_PENDING_STATUS;
|
|
23507
23733
|
process.stderr.write("[deploy_client] Recording VPS inventory...\n");
|
|
23508
23734
|
const inventory = buildInventoryRecord({
|
|
23509
23735
|
userId: user_id,
|
|
@@ -23524,7 +23750,7 @@ async function executeDeployment(params, client) {
|
|
|
23524
23750
|
plan: vps.plan,
|
|
23525
23751
|
ssh_port: vps.ssh_port,
|
|
23526
23752
|
ssh_access: `ssh exeai@${vps.ip_address}`,
|
|
23527
|
-
status,
|
|
23753
|
+
status: status2,
|
|
23528
23754
|
ansible_output: playbookResult,
|
|
23529
23755
|
dns_record_id: dnsRecordId,
|
|
23530
23756
|
inventory
|
|
@@ -23561,7 +23787,7 @@ function registerDeployClient(server2) {
|
|
|
23561
23787
|
};
|
|
23562
23788
|
}
|
|
23563
23789
|
try {
|
|
23564
|
-
const
|
|
23790
|
+
const result3 = await executeDeployment({
|
|
23565
23791
|
client_name,
|
|
23566
23792
|
domain,
|
|
23567
23793
|
region,
|
|
@@ -23576,7 +23802,7 @@ function registerDeployClient(server2) {
|
|
|
23576
23802
|
content: [
|
|
23577
23803
|
{
|
|
23578
23804
|
type: "text",
|
|
23579
|
-
text: JSON.stringify(
|
|
23805
|
+
text: JSON.stringify(result3, null, 2)
|
|
23580
23806
|
}
|
|
23581
23807
|
]
|
|
23582
23808
|
};
|
|
@@ -24309,12 +24535,12 @@ function registerListTriggers(server2) {
|
|
|
24309
24535
|
};
|
|
24310
24536
|
}
|
|
24311
24537
|
const lines = triggers.map((t) => {
|
|
24312
|
-
const
|
|
24538
|
+
const status2 = t.enabled ? "active" : "disabled";
|
|
24313
24539
|
const condCount = t.conditions.length;
|
|
24314
24540
|
const actionTypes = t.actions.map((a) => a.type).join(", ");
|
|
24315
24541
|
const schedulePart = isScheduledTrigger(t) ? ` | schedule: ${t.schedule}` : "";
|
|
24316
24542
|
const projectPart = t.project ? ` | project: ${t.project}` : "";
|
|
24317
|
-
return `- **${t.name}** (${t.id}) [${
|
|
24543
|
+
return `- **${t.name}** (${t.id}) [${status2}]
|
|
24318
24544
|
Event: ${t.event} | Conditions: ${condCount} | Actions: ${actionTypes}` + schedulePart + projectPart;
|
|
24319
24545
|
});
|
|
24320
24546
|
const text3 = `${triggers.length} trigger(s) configured:
|
|
@@ -24688,8 +24914,8 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
24688
24914
|
]
|
|
24689
24915
|
};
|
|
24690
24916
|
}
|
|
24691
|
-
const
|
|
24692
|
-
if (!
|
|
24917
|
+
const result3 = applyPack(industry, project);
|
|
24918
|
+
if (!result3) {
|
|
24693
24919
|
return {
|
|
24694
24920
|
content: [
|
|
24695
24921
|
{
|
|
@@ -24702,22 +24928,22 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
24702
24928
|
const lines = [
|
|
24703
24929
|
`Applied "${industry}" starter pack to project "${project}":
|
|
24704
24930
|
`,
|
|
24705
|
-
`**CRM Custom Objects** (${
|
|
24706
|
-
...
|
|
24931
|
+
`**CRM Custom Objects** (${result3.customObjects.count}):`,
|
|
24932
|
+
...result3.customObjects.names.map((n) => ` - ${n}`),
|
|
24707
24933
|
"",
|
|
24708
|
-
`**Triggers Created** (${
|
|
24709
|
-
...
|
|
24934
|
+
`**Triggers Created** (${result3.triggers.created}):`,
|
|
24935
|
+
...result3.triggers.ids.map((id, i) => {
|
|
24710
24936
|
const t = pack.triggers[i];
|
|
24711
24937
|
return ` - ${t?.name ?? "Unknown"} (${id})`;
|
|
24712
24938
|
}),
|
|
24713
24939
|
"",
|
|
24714
|
-
`**Wiki Seeds** (${
|
|
24715
|
-
...
|
|
24940
|
+
`**Wiki Seeds** (${result3.wikiSeeds.count}):`,
|
|
24941
|
+
...result3.wikiSeeds.titles.map((t) => ` - ${t}`)
|
|
24716
24942
|
];
|
|
24717
|
-
if (
|
|
24943
|
+
if (result3.pendingVariables.length > 0) {
|
|
24718
24944
|
lines.push("");
|
|
24719
24945
|
lines.push("**Requires manual configuration:**");
|
|
24720
|
-
for (const pv of
|
|
24946
|
+
for (const pv of result3.pendingVariables) {
|
|
24721
24947
|
const vars = Object.entries(pv.variables).map(([k, v]) => ` - {{${k}}}: ${v}`).join("\n");
|
|
24722
24948
|
lines.push(` ${pv.trigger}:
|
|
24723
24949
|
${vars}`);
|
|
@@ -25108,11 +25334,11 @@ async function importIdentities(identities, updatedBy) {
|
|
|
25108
25334
|
}
|
|
25109
25335
|
async function getActiveProcedureTitles() {
|
|
25110
25336
|
const client = getClient();
|
|
25111
|
-
const
|
|
25337
|
+
const result3 = await client.execute({
|
|
25112
25338
|
sql: "SELECT title FROM company_procedures WHERE active = 1",
|
|
25113
25339
|
args: []
|
|
25114
25340
|
});
|
|
25115
|
-
return new Set(
|
|
25341
|
+
return new Set(result3.rows.map((row) => String(row.title)));
|
|
25116
25342
|
}
|
|
25117
25343
|
async function exportOrchestration(createdBy) {
|
|
25118
25344
|
const client = getClient();
|
|
@@ -25302,11 +25528,11 @@ function registerImportOrchestration(server2) {
|
|
|
25302
25528
|
await initStore();
|
|
25303
25529
|
const raw = readFileSync30(package_path, "utf-8");
|
|
25304
25530
|
const pkg = validatePackage(JSON.parse(raw));
|
|
25305
|
-
const
|
|
25531
|
+
const result3 = await importOrchestration(pkg, merge_strategy);
|
|
25306
25532
|
return {
|
|
25307
25533
|
content: [{
|
|
25308
25534
|
type: "text",
|
|
25309
|
-
text: `Imported ${
|
|
25535
|
+
text: `Imported ${result3.imported.roster} roster entries, ${result3.imported.identities} identities, ${result3.imported.behaviors} behaviors, ${result3.imported.procedures} procedures using ${merge_strategy} strategy`
|
|
25310
25536
|
}]
|
|
25311
25537
|
};
|
|
25312
25538
|
} catch (err) {
|
|
@@ -25417,11 +25643,11 @@ Domain: ${domain ?? "none"}`
|
|
|
25417
25643
|
};
|
|
25418
25644
|
}
|
|
25419
25645
|
const client = getClient();
|
|
25420
|
-
const
|
|
25646
|
+
const result3 = await client.execute({
|
|
25421
25647
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
25422
25648
|
args: [procedure_id]
|
|
25423
25649
|
});
|
|
25424
|
-
if (
|
|
25650
|
+
if (result3.rows.length === 0) {
|
|
25425
25651
|
return {
|
|
25426
25652
|
content: [{
|
|
25427
25653
|
type: "text",
|
|
@@ -25430,7 +25656,7 @@ Domain: ${domain ?? "none"}`
|
|
|
25430
25656
|
isError: true
|
|
25431
25657
|
};
|
|
25432
25658
|
}
|
|
25433
|
-
const row =
|
|
25659
|
+
const row = result3.rows[0];
|
|
25434
25660
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
25435
25661
|
if (!wasActive) {
|
|
25436
25662
|
return {
|
|
@@ -25981,7 +26207,7 @@ function registerBehavior(server2) {
|
|
|
25981
26207
|
args.push(proj);
|
|
25982
26208
|
}
|
|
25983
26209
|
const where = conditions.join(" AND ");
|
|
25984
|
-
const
|
|
26210
|
+
const result4 = await client2.execute({
|
|
25985
26211
|
sql: `SELECT id, agent_id, project_name, domain, content, active, created_at, updated_at
|
|
25986
26212
|
FROM behaviors
|
|
25987
26213
|
WHERE ${where}
|
|
@@ -25989,7 +26215,7 @@ function registerBehavior(server2) {
|
|
|
25989
26215
|
LIMIT 50`,
|
|
25990
26216
|
args
|
|
25991
26217
|
});
|
|
25992
|
-
const behaviors =
|
|
26218
|
+
const behaviors = result4.rows.map((r) => rowToBehavior2(r));
|
|
25993
26219
|
if (behaviors.length === 0) {
|
|
25994
26220
|
return {
|
|
25995
26221
|
content: [{ type: "text", text: "No behaviors found." }]
|
|
@@ -26088,11 +26314,11 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
26088
26314
|
};
|
|
26089
26315
|
}
|
|
26090
26316
|
const client = getClient();
|
|
26091
|
-
const
|
|
26317
|
+
const result3 = await client.execute({
|
|
26092
26318
|
sql: "SELECT id, agent_id, content, domain, priority FROM behaviors WHERE id = ?",
|
|
26093
26319
|
args: [behavior_id]
|
|
26094
26320
|
});
|
|
26095
|
-
if (
|
|
26321
|
+
if (result3.rows.length === 0) {
|
|
26096
26322
|
return {
|
|
26097
26323
|
content: [{
|
|
26098
26324
|
type: "text",
|
|
@@ -26101,7 +26327,7 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
26101
26327
|
isError: true
|
|
26102
26328
|
};
|
|
26103
26329
|
}
|
|
26104
|
-
const row =
|
|
26330
|
+
const row = result3.rows[0];
|
|
26105
26331
|
const wasActive = await deactivateBehavior(behavior_id);
|
|
26106
26332
|
if (!wasActive) {
|
|
26107
26333
|
return {
|
|
@@ -26307,11 +26533,11 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
26307
26533
|
};
|
|
26308
26534
|
}
|
|
26309
26535
|
const client = getClient();
|
|
26310
|
-
const
|
|
26536
|
+
const result3 = await client.execute({
|
|
26311
26537
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
26312
26538
|
args: [procedure_id]
|
|
26313
26539
|
});
|
|
26314
|
-
if (
|
|
26540
|
+
if (result3.rows.length === 0) {
|
|
26315
26541
|
return {
|
|
26316
26542
|
content: [{
|
|
26317
26543
|
type: "text",
|
|
@@ -26320,7 +26546,7 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
26320
26546
|
isError: true
|
|
26321
26547
|
};
|
|
26322
26548
|
}
|
|
26323
|
-
const row =
|
|
26549
|
+
const row = result3.rows[0];
|
|
26324
26550
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
26325
26551
|
if (!wasActive) {
|
|
26326
26552
|
return {
|
|
@@ -26449,14 +26675,14 @@ function registerGetDecision(server2) {
|
|
|
26449
26675
|
},
|
|
26450
26676
|
async ({ domain }) => {
|
|
26451
26677
|
const client = getClient();
|
|
26452
|
-
const
|
|
26678
|
+
const result3 = await client.execute({
|
|
26453
26679
|
sql: `SELECT id, agent_id, timestamp, raw_text, status, supersedes_id, project_name
|
|
26454
26680
|
FROM memories
|
|
26455
26681
|
WHERE memory_type = 'decision' AND source_path = ?
|
|
26456
26682
|
ORDER BY timestamp DESC`,
|
|
26457
26683
|
args: [domain]
|
|
26458
26684
|
});
|
|
26459
|
-
if (
|
|
26685
|
+
if (result3.rows.length === 0) {
|
|
26460
26686
|
return {
|
|
26461
26687
|
content: [
|
|
26462
26688
|
{
|
|
@@ -26466,7 +26692,7 @@ function registerGetDecision(server2) {
|
|
|
26466
26692
|
]
|
|
26467
26693
|
};
|
|
26468
26694
|
}
|
|
26469
|
-
const decisions =
|
|
26695
|
+
const decisions = result3.rows.map((r) => ({
|
|
26470
26696
|
id: String(r.id),
|
|
26471
26697
|
agent_id: String(r.agent_id),
|
|
26472
26698
|
timestamp: String(r.timestamp),
|
|
@@ -27042,15 +27268,912 @@ Upstream status: ${upstreamStatus}`
|
|
|
27042
27268
|
);
|
|
27043
27269
|
}
|
|
27044
27270
|
|
|
27271
|
+
// src/mcp/tools/support.ts
|
|
27272
|
+
import { z as z88 } from "zod";
|
|
27273
|
+
|
|
27274
|
+
// src/bin/exe-support.ts
|
|
27275
|
+
init_config();
|
|
27276
|
+
init_license();
|
|
27277
|
+
import { mkdirSync as mkdirSync21, readFileSync as readFileSync32, unlinkSync as unlinkSync12, writeFileSync as writeFileSync23 } from "fs";
|
|
27278
|
+
import path50 from "path";
|
|
27279
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
27280
|
+
var DEFAULT_BUG_ENDPOINT = "https://askexe.com/v1/support/bug-reports";
|
|
27281
|
+
var DEFAULT_ADMIN_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
27282
|
+
async function runHealth() {
|
|
27283
|
+
const checks = [];
|
|
27284
|
+
const endpoints = await resolveEndpoints();
|
|
27285
|
+
checks.push(checkLocalWrite());
|
|
27286
|
+
checks.push({
|
|
27287
|
+
check: "license_key_present",
|
|
27288
|
+
level: loadLicense() ? "pass" : "fail",
|
|
27289
|
+
detail: loadLicense() ? "license.key found" : "missing ~/.exe-os/license.key; run exe-os setup or exe-os cloud setup",
|
|
27290
|
+
next: loadLicense() ? void 0 : "Run `exe-os setup` or ask AskExe for the customer license key."
|
|
27291
|
+
});
|
|
27292
|
+
checks.push({
|
|
27293
|
+
check: "license_token_cached",
|
|
27294
|
+
level: readCachedLicenseToken() ? "pass" : "warn",
|
|
27295
|
+
detail: readCachedLicenseToken() ? "cached license token found" : "no cached token yet; support can still use license.key",
|
|
27296
|
+
next: readCachedLicenseToken() ? void 0 : "This is OK if license.key exists. It refreshes after cloud/license validation."
|
|
27297
|
+
});
|
|
27298
|
+
try {
|
|
27299
|
+
const res = await fetch(endpoints.healthEndpoint, { method: "GET", signal: AbortSignal.timeout(1e4) });
|
|
27300
|
+
const body = await safeJson2(res);
|
|
27301
|
+
checks.push({
|
|
27302
|
+
check: "support_health_endpoint",
|
|
27303
|
+
level: res.ok && body?.status !== "down" ? "pass" : "fail",
|
|
27304
|
+
detail: `${res.status} ${body?.status ?? res.statusText}`,
|
|
27305
|
+
next: res.ok ? void 0 : "Check internet access, DNS, or AskExe support status."
|
|
27306
|
+
});
|
|
27307
|
+
for (const remote of body?.checks ?? []) {
|
|
27308
|
+
const name = remote.name ?? "unknown";
|
|
27309
|
+
checks.push({
|
|
27310
|
+
check: `server_${name}`,
|
|
27311
|
+
level: remote.ok === true ? "pass" : name === "admin_inbox_configured" ? "warn" : "fail",
|
|
27312
|
+
detail: remote.detail ?? (remote.ok ? "ok" : "failed"),
|
|
27313
|
+
next: remote.ok ? void 0 : "AskExe must fix this server-side."
|
|
27314
|
+
});
|
|
27315
|
+
}
|
|
27316
|
+
} catch (err) {
|
|
27317
|
+
checks.push({
|
|
27318
|
+
check: "support_health_endpoint",
|
|
27319
|
+
level: "fail",
|
|
27320
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
27321
|
+
next: "Check internet access, then run `exe-os support health` again."
|
|
27322
|
+
});
|
|
27323
|
+
}
|
|
27324
|
+
return checks;
|
|
27325
|
+
}
|
|
27326
|
+
async function runTest(project) {
|
|
27327
|
+
const checks = await runHealth();
|
|
27328
|
+
const endpoints = await resolveEndpoints();
|
|
27329
|
+
const licenseKey = loadLicense();
|
|
27330
|
+
const licenseToken = readCachedLicenseToken();
|
|
27331
|
+
const id = randomUUID9();
|
|
27332
|
+
const version = readPackageVersion2();
|
|
27333
|
+
const reportPath = writeLocalTestReport(id, project, version);
|
|
27334
|
+
checks.push({ check: "local_report_file", level: "pass", detail: reportPath });
|
|
27335
|
+
if (!licenseKey && !licenseToken) {
|
|
27336
|
+
checks.push({
|
|
27337
|
+
check: "upstream_post",
|
|
27338
|
+
level: "fail",
|
|
27339
|
+
detail: "not sent because this device has no license key/token",
|
|
27340
|
+
next: "Run `exe-os setup` or ask AskExe to provision this customer device."
|
|
27341
|
+
});
|
|
27342
|
+
return checks;
|
|
27343
|
+
}
|
|
27344
|
+
const payload = {
|
|
27345
|
+
id,
|
|
27346
|
+
title: `TEST \u2014 ${project} support intake (${version})`,
|
|
27347
|
+
classification: "unclear",
|
|
27348
|
+
severity: "p3",
|
|
27349
|
+
summary: "Synthetic exe-os support intake smoke test. Safe to close.",
|
|
27350
|
+
customer_impact: "No customer impact; diagnostic only.",
|
|
27351
|
+
reproduction_steps: ["Run exe-os support test"],
|
|
27352
|
+
expected: "The report reaches AskExe support intake and can be triaged.",
|
|
27353
|
+
actual: "Smoke test submitted by exe-os support test.",
|
|
27354
|
+
package_version: `@askexenow/exe-os@${version}`,
|
|
27355
|
+
project_name: project,
|
|
27356
|
+
agent_id: "support-test",
|
|
27357
|
+
agent_role: "diagnostic",
|
|
27358
|
+
report_path: reportPath,
|
|
27359
|
+
markdown: readFileSync32(reportPath, "utf8")
|
|
27360
|
+
};
|
|
27361
|
+
try {
|
|
27362
|
+
const headers = { "content-type": "application/json" };
|
|
27363
|
+
if (licenseKey) headers["x-exe-license-key"] = licenseKey;
|
|
27364
|
+
if (licenseToken) headers["x-exe-license-token"] = licenseToken;
|
|
27365
|
+
const res = await fetch(endpoints.bugEndpoint, {
|
|
27366
|
+
method: "POST",
|
|
27367
|
+
headers,
|
|
27368
|
+
body: JSON.stringify(payload),
|
|
27369
|
+
signal: AbortSignal.timeout(15e3)
|
|
27370
|
+
});
|
|
27371
|
+
const data = await safeJson2(res);
|
|
27372
|
+
checks.push({
|
|
27373
|
+
check: "upstream_post",
|
|
27374
|
+
level: res.ok ? "pass" : "fail",
|
|
27375
|
+
detail: res.ok ? `sent id=${String(data?.id ?? id)}` : summarizeHttpFailure(res.status, data),
|
|
27376
|
+
next: res.ok ? void 0 : nextForPostFailure(res.status)
|
|
27377
|
+
});
|
|
27378
|
+
if (res.ok) {
|
|
27379
|
+
checks.push(await maybeCloseAdmin(String(data?.id ?? id), endpoints.adminEndpoint, version));
|
|
27380
|
+
}
|
|
27381
|
+
} catch (err) {
|
|
27382
|
+
checks.push({
|
|
27383
|
+
check: "upstream_post",
|
|
27384
|
+
level: "fail",
|
|
27385
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
27386
|
+
next: "Check internet access, then run `exe-os support test` again."
|
|
27387
|
+
});
|
|
27388
|
+
}
|
|
27389
|
+
return checks;
|
|
27390
|
+
}
|
|
27391
|
+
async function resolveEndpoints() {
|
|
27392
|
+
const config2 = await loadConfig();
|
|
27393
|
+
const bugEndpoint = process.env.EXE_SUPPORT_BUG_REPORT_ENDPOINT ?? config2.support?.bugReportEndpoint ?? DEFAULT_BUG_ENDPOINT;
|
|
27394
|
+
const healthEndpoint = process.env.EXE_SUPPORT_HEALTH_ENDPOINT ?? bugEndpoint.replace(/\/bug-reports\/?$/, "/health");
|
|
27395
|
+
const adminEndpoint = process.env.ASKEXE_SUPPORT_ADMIN_ENDPOINT ?? process.env.EXE_SUPPORT_ADMIN_ENDPOINT ?? DEFAULT_ADMIN_ENDPOINT;
|
|
27396
|
+
return { bugEndpoint, healthEndpoint, adminEndpoint };
|
|
27397
|
+
}
|
|
27398
|
+
function checkLocalWrite() {
|
|
27399
|
+
const dir = path50.join(EXE_AI_DIR, "bug-reports");
|
|
27400
|
+
const testPath = path50.join(dir, ".support-write-test");
|
|
27401
|
+
try {
|
|
27402
|
+
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27403
|
+
writeFileSync23(testPath, "ok\n", { mode: 384 });
|
|
27404
|
+
unlinkSync12(testPath);
|
|
27405
|
+
return { check: "local_bug_report_dir_writable", level: "pass", detail: dir };
|
|
27406
|
+
} catch (err) {
|
|
27407
|
+
return {
|
|
27408
|
+
check: "local_bug_report_dir_writable",
|
|
27409
|
+
level: "fail",
|
|
27410
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
27411
|
+
next: "Fix permissions on ~/.exe-os or rerun setup on a writable user account."
|
|
27412
|
+
};
|
|
27413
|
+
}
|
|
27414
|
+
}
|
|
27415
|
+
function writeLocalTestReport(id, project, version) {
|
|
27416
|
+
const dir = path50.join(EXE_AI_DIR, "bug-reports");
|
|
27417
|
+
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27418
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27419
|
+
const filePath = path50.join(dir, `${date}-support-intake-test-${id.slice(0, 8)}.md`);
|
|
27420
|
+
writeFileSync23(filePath, `# TEST \u2014 ${project} support intake
|
|
27421
|
+
|
|
27422
|
+
Report ID: ${id}
|
|
27423
|
+
Package: @askexenow/exe-os@${version}
|
|
27424
|
+
|
|
27425
|
+
Synthetic smoke test from \`exe-os support test\`. Safe to close.
|
|
27426
|
+
`, { mode: 384 });
|
|
27427
|
+
return filePath;
|
|
27428
|
+
}
|
|
27429
|
+
async function maybeCloseAdmin(id, adminEndpoint, version) {
|
|
27430
|
+
const token = process.env.ASKEXE_SUPPORT_ADMIN_TOKEN ?? process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
27431
|
+
if (!token) {
|
|
27432
|
+
return { check: "askexe_admin_autoclose", level: "warn", detail: "skipped; admin token is AskExe-only" };
|
|
27433
|
+
}
|
|
27434
|
+
try {
|
|
27435
|
+
const res = await fetch(`${adminEndpoint}/${encodeURIComponent(id)}`, {
|
|
27436
|
+
method: "PATCH",
|
|
27437
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
27438
|
+
body: JSON.stringify({
|
|
27439
|
+
status: "closed",
|
|
27440
|
+
triage_notes: "Auto-closed synthetic support smoke test.",
|
|
27441
|
+
fixed_version: version
|
|
27442
|
+
}),
|
|
27443
|
+
signal: AbortSignal.timeout(1e4)
|
|
27444
|
+
});
|
|
27445
|
+
return {
|
|
27446
|
+
check: "askexe_admin_autoclose",
|
|
27447
|
+
level: res.ok ? "pass" : "warn",
|
|
27448
|
+
detail: res.ok ? "closed" : `${res.status} ${await res.text()}`,
|
|
27449
|
+
next: res.ok ? void 0 : "Report was sent; AskExe can close the smoke report manually."
|
|
27450
|
+
};
|
|
27451
|
+
} catch (err) {
|
|
27452
|
+
return {
|
|
27453
|
+
check: "askexe_admin_autoclose",
|
|
27454
|
+
level: "warn",
|
|
27455
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
27456
|
+
next: "Report was sent; AskExe can close the smoke report manually."
|
|
27457
|
+
};
|
|
27458
|
+
}
|
|
27459
|
+
}
|
|
27460
|
+
function readPackageVersion2() {
|
|
27461
|
+
let dir = path50.dirname(new URL(import.meta.url).pathname);
|
|
27462
|
+
for (let i = 0; i < 6; i++) {
|
|
27463
|
+
const pkg = path50.join(dir, "package.json");
|
|
27464
|
+
try {
|
|
27465
|
+
const parsed = JSON.parse(readFileSync32(pkg, "utf8"));
|
|
27466
|
+
if (parsed.version) return parsed.version;
|
|
27467
|
+
} catch {
|
|
27468
|
+
}
|
|
27469
|
+
dir = path50.dirname(dir);
|
|
27470
|
+
}
|
|
27471
|
+
return "unknown";
|
|
27472
|
+
}
|
|
27473
|
+
async function safeJson2(res) {
|
|
27474
|
+
try {
|
|
27475
|
+
return await res.json();
|
|
27476
|
+
} catch {
|
|
27477
|
+
return null;
|
|
27478
|
+
}
|
|
27479
|
+
}
|
|
27480
|
+
function hasFailures(rows) {
|
|
27481
|
+
return rows.some((row) => row.level === "fail");
|
|
27482
|
+
}
|
|
27483
|
+
function summarizeHttpFailure(status2, data) {
|
|
27484
|
+
const error = data?.error;
|
|
27485
|
+
return `${status2} ${error?.message ?? JSON.stringify(data)}`;
|
|
27486
|
+
}
|
|
27487
|
+
function nextForPostFailure(status2) {
|
|
27488
|
+
if (status2 === 401) return "License credentials were not sent. Run `exe-os support health` and check license_key_present.";
|
|
27489
|
+
if (status2 === 403) return "License is valid locally but not accepted by support intake. AskExe must check server-side license provisioning.";
|
|
27490
|
+
if (status2 >= 500) return "AskExe support intake is unhealthy server-side. Try again shortly and report the local file path.";
|
|
27491
|
+
return "Run `exe-os support health`; if it passes, send this output to AskExe.";
|
|
27492
|
+
}
|
|
27493
|
+
|
|
27494
|
+
// src/mcp/tools/support.ts
|
|
27495
|
+
function formatRows(rows, mode) {
|
|
27496
|
+
const lines = [`exe-os support ${mode}`, ""];
|
|
27497
|
+
for (const row of rows) {
|
|
27498
|
+
const icon = row.level === "pass" ? "\u2705" : row.level === "warn" ? "\u26A0\uFE0F" : "\u274C";
|
|
27499
|
+
lines.push(`${icon} ${row.check}: ${row.detail}`);
|
|
27500
|
+
if (row.next) lines.push(` \u2192 ${row.next}`);
|
|
27501
|
+
}
|
|
27502
|
+
lines.push("");
|
|
27503
|
+
if (hasFailures(rows)) {
|
|
27504
|
+
lines.push("Result: not ready. Fix the failed item(s), then rerun this tool.");
|
|
27505
|
+
} else if (rows.some((row) => row.level === "warn")) {
|
|
27506
|
+
lines.push("Result: ready with warnings. Bug reports can be sent; warnings are informational unless AskExe asked for stricter validation.");
|
|
27507
|
+
} else {
|
|
27508
|
+
lines.push(mode === "test" ? "Result: ready. AskExe received the test report." : "Result: ready. Use support_test for an end-to-end send test.");
|
|
27509
|
+
}
|
|
27510
|
+
return lines.join("\n");
|
|
27511
|
+
}
|
|
27512
|
+
function registerSupportTools(server2) {
|
|
27513
|
+
server2.registerTool(
|
|
27514
|
+
"support_health",
|
|
27515
|
+
{
|
|
27516
|
+
title: "Support Health",
|
|
27517
|
+
description: "Customer-safe support intake health check. Verifies local bug-report directory, license presence, and AskExe support server health without filing a report.",
|
|
27518
|
+
inputSchema: {}
|
|
27519
|
+
},
|
|
27520
|
+
async () => {
|
|
27521
|
+
const checks = await runHealth();
|
|
27522
|
+
return {
|
|
27523
|
+
content: [{ type: "text", text: formatRows(checks, "health") }],
|
|
27524
|
+
structuredContent: { ok: !hasFailures(checks), checks },
|
|
27525
|
+
isError: hasFailures(checks)
|
|
27526
|
+
};
|
|
27527
|
+
}
|
|
27528
|
+
);
|
|
27529
|
+
server2.registerTool(
|
|
27530
|
+
"support_test",
|
|
27531
|
+
{
|
|
27532
|
+
title: "Support Test",
|
|
27533
|
+
description: "End-to-end support intake smoke test. Files a clearly marked test report upstream and auto-closes it on AskExe machines with admin credentials.",
|
|
27534
|
+
inputSchema: {
|
|
27535
|
+
project: z88.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
|
|
27536
|
+
}
|
|
27537
|
+
},
|
|
27538
|
+
async ({ project }) => {
|
|
27539
|
+
const checks = await runTest(project);
|
|
27540
|
+
return {
|
|
27541
|
+
content: [{ type: "text", text: formatRows(checks, "test") }],
|
|
27542
|
+
structuredContent: { ok: !hasFailures(checks), checks },
|
|
27543
|
+
isError: hasFailures(checks)
|
|
27544
|
+
};
|
|
27545
|
+
}
|
|
27546
|
+
);
|
|
27547
|
+
}
|
|
27548
|
+
|
|
27549
|
+
// src/mcp/tools/cli-parity.ts
|
|
27550
|
+
import { execFile as execFile2 } from "child_process";
|
|
27551
|
+
import { promisify as promisify2 } from "util";
|
|
27552
|
+
import { z as z89 } from "zod";
|
|
27553
|
+
|
|
27554
|
+
// src/bin/exe-status.ts
|
|
27555
|
+
init_employees();
|
|
27556
|
+
init_fast_db_init();
|
|
27557
|
+
init_database();
|
|
27558
|
+
init_tmux_status();
|
|
27559
|
+
init_task_scope();
|
|
27560
|
+
async function status(targetEmployee) {
|
|
27561
|
+
if (!inTmux()) {
|
|
27562
|
+
return "Not in a tmux session. Start tmux first: tmux new-session -s work";
|
|
27563
|
+
}
|
|
27564
|
+
const employees = await loadEmployees();
|
|
27565
|
+
const names = employees.filter((e) => !isCoordinatorRole(e.role)).map((e) => e.name);
|
|
27566
|
+
const roles = new Map(employees.map((e) => [e.name, e.role ?? ""]));
|
|
27567
|
+
await fastDbInit();
|
|
27568
|
+
const client = getClient();
|
|
27569
|
+
const taskMap = /* @__PURE__ */ new Map();
|
|
27570
|
+
try {
|
|
27571
|
+
const esScope = sessionScopeFilter();
|
|
27572
|
+
const result3 = await client.execute({
|
|
27573
|
+
sql: `SELECT assigned_to, title, priority FROM tasks
|
|
27574
|
+
WHERE status = 'in_progress'${esScope.sql}
|
|
27575
|
+
ORDER BY priority ASC, created_at ASC`,
|
|
27576
|
+
args: [...esScope.args]
|
|
27577
|
+
});
|
|
27578
|
+
for (const row of result3.rows) {
|
|
27579
|
+
const assignee = String(row.assigned_to);
|
|
27580
|
+
if (!taskMap.has(assignee)) {
|
|
27581
|
+
taskMap.set(assignee, {
|
|
27582
|
+
title: String(row.title),
|
|
27583
|
+
priority: String(row.priority)
|
|
27584
|
+
});
|
|
27585
|
+
}
|
|
27586
|
+
}
|
|
27587
|
+
} catch {
|
|
27588
|
+
}
|
|
27589
|
+
if (targetEmployee) {
|
|
27590
|
+
if (!names.includes(targetEmployee)) {
|
|
27591
|
+
return `Employee "${targetEmployee}" not found. Available: ${names.join(", ")}`;
|
|
27592
|
+
}
|
|
27593
|
+
const statuses2 = getEmployeeStatuses([targetEmployee]);
|
|
27594
|
+
const s = statuses2[0];
|
|
27595
|
+
return formatStatusDeep(s, taskMap.get(targetEmployee), roles.get(targetEmployee) ?? "");
|
|
27596
|
+
}
|
|
27597
|
+
const statuses = getEmployeeStatuses(names);
|
|
27598
|
+
return formatStatusAll(statuses, taskMap, roles);
|
|
27599
|
+
}
|
|
27600
|
+
if (isMainModule(import.meta.url)) {
|
|
27601
|
+
const target = process.argv[2];
|
|
27602
|
+
status(target).then((output) => {
|
|
27603
|
+
process.stdout.write(output + "\n");
|
|
27604
|
+
}).catch((err) => {
|
|
27605
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
27606
|
+
`);
|
|
27607
|
+
process.exit(1);
|
|
27608
|
+
});
|
|
27609
|
+
}
|
|
27610
|
+
|
|
27611
|
+
// src/bin/exe-healthcheck.ts
|
|
27612
|
+
import { existsSync as existsSync39, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "fs";
|
|
27613
|
+
import path51 from "path";
|
|
27614
|
+
import { execSync as execSync13 } from "child_process";
|
|
27615
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
27616
|
+
init_config();
|
|
27617
|
+
function findPackageRoot2() {
|
|
27618
|
+
let dir = path51.dirname(fileURLToPath6(import.meta.url));
|
|
27619
|
+
const { root } = path51.parse(dir);
|
|
27620
|
+
while (dir !== root) {
|
|
27621
|
+
if (existsSync39(path51.join(dir, "package.json"))) return dir;
|
|
27622
|
+
dir = path51.dirname(dir);
|
|
27623
|
+
}
|
|
27624
|
+
throw new Error("Cannot find package root");
|
|
27625
|
+
}
|
|
27626
|
+
function checkBuildIntegrity(pkgRoot) {
|
|
27627
|
+
const results = [];
|
|
27628
|
+
const tsupConfig = path51.join(pkgRoot, "tsup.config.ts");
|
|
27629
|
+
if (!existsSync39(tsupConfig)) {
|
|
27630
|
+
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
27631
|
+
}
|
|
27632
|
+
const configContent = readFileSync33(tsupConfig, "utf-8");
|
|
27633
|
+
const entryMatches = configContent.matchAll(/"([^"]+)":\s*"src\//g);
|
|
27634
|
+
const missing = [];
|
|
27635
|
+
let total = 0;
|
|
27636
|
+
for (const match of entryMatches) {
|
|
27637
|
+
const outputKey = match[1];
|
|
27638
|
+
const expectedPath = path51.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
27639
|
+
total++;
|
|
27640
|
+
if (!existsSync39(expectedPath)) {
|
|
27641
|
+
missing.push(`dist/${outputKey}.js`);
|
|
27642
|
+
}
|
|
27643
|
+
}
|
|
27644
|
+
if (missing.length > 0) {
|
|
27645
|
+
results.push({
|
|
27646
|
+
name: "build/entry-points",
|
|
27647
|
+
pass: false,
|
|
27648
|
+
detail: `${missing.length}/${total} entry points missing:
|
|
27649
|
+
${missing.join("\n ")}`
|
|
27650
|
+
});
|
|
27651
|
+
} else {
|
|
27652
|
+
results.push({
|
|
27653
|
+
name: "build/entry-points",
|
|
27654
|
+
pass: true,
|
|
27655
|
+
detail: `${total} entry points verified`
|
|
27656
|
+
});
|
|
27657
|
+
}
|
|
27658
|
+
return results;
|
|
27659
|
+
}
|
|
27660
|
+
function checkEmbedPipeline(pkgRoot) {
|
|
27661
|
+
const results = [];
|
|
27662
|
+
const daemonPath = path51.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
27663
|
+
if (!existsSync39(daemonPath)) {
|
|
27664
|
+
results.push({
|
|
27665
|
+
name: "exed/daemon-exists",
|
|
27666
|
+
pass: false,
|
|
27667
|
+
detail: `exe-daemon.js not found at ${daemonPath}`
|
|
27668
|
+
});
|
|
27669
|
+
return results;
|
|
27670
|
+
}
|
|
27671
|
+
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
27672
|
+
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
27673
|
+
for (const dir of entryDirs) {
|
|
27674
|
+
const fullDir = path51.join(pkgRoot, dir);
|
|
27675
|
+
if (!existsSync39(fullDir)) continue;
|
|
27676
|
+
let walkDir = fullDir;
|
|
27677
|
+
const { root } = path51.parse(walkDir);
|
|
27678
|
+
let foundRoot = null;
|
|
27679
|
+
while (walkDir !== root) {
|
|
27680
|
+
if (existsSync39(path51.join(walkDir, "package.json"))) {
|
|
27681
|
+
foundRoot = walkDir;
|
|
27682
|
+
break;
|
|
27683
|
+
}
|
|
27684
|
+
walkDir = path51.dirname(walkDir);
|
|
27685
|
+
}
|
|
27686
|
+
if (!foundRoot) {
|
|
27687
|
+
results.push({
|
|
27688
|
+
name: `exed/reachable-from-${dir}`,
|
|
27689
|
+
pass: false,
|
|
27690
|
+
detail: `Cannot find package root from ${dir}`
|
|
27691
|
+
});
|
|
27692
|
+
continue;
|
|
27693
|
+
}
|
|
27694
|
+
const resolvedDaemon = path51.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
27695
|
+
const reachable = existsSync39(resolvedDaemon);
|
|
27696
|
+
results.push({
|
|
27697
|
+
name: `exed/reachable-from-${dir}`,
|
|
27698
|
+
pass: reachable,
|
|
27699
|
+
detail: reachable ? `Resolves to ${resolvedDaemon}` : `NOT FOUND: ${resolvedDaemon}`
|
|
27700
|
+
});
|
|
27701
|
+
}
|
|
27702
|
+
return results;
|
|
27703
|
+
}
|
|
27704
|
+
function checkTaskSystem(pkgRoot) {
|
|
27705
|
+
const results = [];
|
|
27706
|
+
const scannerPath = path51.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
27707
|
+
if (!existsSync39(scannerPath)) {
|
|
27708
|
+
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
27709
|
+
return results;
|
|
27710
|
+
}
|
|
27711
|
+
try {
|
|
27712
|
+
execSync13(`node "${scannerPath}" /tmp/nonexistent-healthcheck-test --format=json 2>/dev/null`, {
|
|
27713
|
+
timeout: 1e4,
|
|
27714
|
+
encoding: "utf-8"
|
|
27715
|
+
});
|
|
27716
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs without import errors" });
|
|
27717
|
+
} catch (err) {
|
|
27718
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27719
|
+
if (msg.includes("Cannot find module") || msg.includes("ERR_MODULE_NOT_FOUND") || msg.includes("SyntaxError")) {
|
|
27720
|
+
results.push({ name: "tasks/scanner", pass: false, detail: `Import error: ${msg.slice(0, 200)}` });
|
|
27721
|
+
} else {
|
|
27722
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs (no import errors)" });
|
|
27723
|
+
}
|
|
27724
|
+
}
|
|
27725
|
+
return results;
|
|
27726
|
+
}
|
|
27727
|
+
function checkWorkerSpawning(pkgRoot) {
|
|
27728
|
+
const results = [];
|
|
27729
|
+
const workerPath = path51.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
27730
|
+
if (!existsSync39(workerPath)) {
|
|
27731
|
+
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
27732
|
+
return results;
|
|
27733
|
+
}
|
|
27734
|
+
try {
|
|
27735
|
+
execSync13(`node --check "${workerPath}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27736
|
+
results.push({ name: "workers/ingest-worker", pass: true, detail: "ingest-worker.js parses OK" });
|
|
27737
|
+
} catch (err) {
|
|
27738
|
+
results.push({
|
|
27739
|
+
name: "workers/ingest-worker",
|
|
27740
|
+
pass: false,
|
|
27741
|
+
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
27742
|
+
});
|
|
27743
|
+
}
|
|
27744
|
+
const hooksDir = path51.join(pkgRoot, "dist", "hooks");
|
|
27745
|
+
if (existsSync39(hooksDir)) {
|
|
27746
|
+
const hookFiles = readdirSync14(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
27747
|
+
let hooksPassed = 0;
|
|
27748
|
+
const hooksFailed = [];
|
|
27749
|
+
for (const hook of hookFiles) {
|
|
27750
|
+
try {
|
|
27751
|
+
execSync13(`node --check "${path51.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27752
|
+
hooksPassed++;
|
|
27753
|
+
} catch {
|
|
27754
|
+
hooksFailed.push(hook);
|
|
27755
|
+
}
|
|
27756
|
+
}
|
|
27757
|
+
if (hooksFailed.length > 0) {
|
|
27758
|
+
results.push({
|
|
27759
|
+
name: "workers/hooks-parse",
|
|
27760
|
+
pass: false,
|
|
27761
|
+
detail: `${hooksFailed.length}/${hookFiles.length} hooks fail to parse: ${hooksFailed.join(", ")}`
|
|
27762
|
+
});
|
|
27763
|
+
} else {
|
|
27764
|
+
results.push({
|
|
27765
|
+
name: "workers/hooks-parse",
|
|
27766
|
+
pass: true,
|
|
27767
|
+
detail: `${hooksPassed} hook entry points parse OK`
|
|
27768
|
+
});
|
|
27769
|
+
}
|
|
27770
|
+
}
|
|
27771
|
+
return results;
|
|
27772
|
+
}
|
|
27773
|
+
function checkMcpTransport() {
|
|
27774
|
+
const results = [];
|
|
27775
|
+
const pidPath = path51.join(EXE_AI_DIR, "exed.pid");
|
|
27776
|
+
const tokenPath = path51.join(EXE_AI_DIR, "exed.token");
|
|
27777
|
+
let daemonAlive = false;
|
|
27778
|
+
if (existsSync39(pidPath)) {
|
|
27779
|
+
try {
|
|
27780
|
+
const pid = parseInt(readFileSync33(pidPath, "utf8").trim(), 10);
|
|
27781
|
+
process.kill(pid, 0);
|
|
27782
|
+
daemonAlive = true;
|
|
27783
|
+
results.push({ name: "mcp/daemon-process", pass: true, detail: `exed process alive (pid ${pid})` });
|
|
27784
|
+
} catch {
|
|
27785
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "exed.pid exists but process is not alive; restart exe-os/exed" });
|
|
27786
|
+
}
|
|
27787
|
+
} else {
|
|
27788
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "No exed.pid found; daemon is not running" });
|
|
27789
|
+
}
|
|
27790
|
+
if (daemonAlive && existsSync39(tokenPath)) {
|
|
27791
|
+
try {
|
|
27792
|
+
const token = readFileSync33(tokenPath, "utf8").trim();
|
|
27793
|
+
const response = execSync13(
|
|
27794
|
+
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
27795
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
27796
|
+
);
|
|
27797
|
+
const jsonRpcError = response.includes("Content-Type: application/json") && response.includes("missing MCP session");
|
|
27798
|
+
results.push({
|
|
27799
|
+
name: "mcp/http-endpoint",
|
|
27800
|
+
pass: jsonRpcError,
|
|
27801
|
+
detail: jsonRpcError ? "MCP HTTP endpoint reachable; missing-session probe returns JSON-RPC (expected)" : "MCP HTTP endpoint reachable but did not return expected JSON-RPC missing-session response"
|
|
27802
|
+
});
|
|
27803
|
+
} catch (err) {
|
|
27804
|
+
results.push({
|
|
27805
|
+
name: "mcp/http-endpoint",
|
|
27806
|
+
pass: false,
|
|
27807
|
+
detail: `MCP HTTP endpoint not reachable: ${err instanceof Error ? err.message.slice(0, 180) : String(err)}`
|
|
27808
|
+
});
|
|
27809
|
+
}
|
|
27810
|
+
} else if (daemonAlive) {
|
|
27811
|
+
results.push({ name: "mcp/http-endpoint", pass: false, detail: "Daemon is alive but exed.token is missing; cannot probe MCP HTTP" });
|
|
27812
|
+
}
|
|
27813
|
+
const events = readMcpHttpEvents(200);
|
|
27814
|
+
const summary = summarizeMcpTransport(events);
|
|
27815
|
+
results.push({
|
|
27816
|
+
name: "mcp/local-event-log",
|
|
27817
|
+
pass: true,
|
|
27818
|
+
detail: `${events.length} recent event(s); activeSessions=${summary.activeSessions ?? "unknown"}; lastHandshake=${summary.lastSuccessfulHandshake ?? "none"}; lastTool=${summary.lastSuccessfulToolCall ?? "none"}`
|
|
27819
|
+
});
|
|
27820
|
+
results.push({
|
|
27821
|
+
name: "mcp/monitor-summary",
|
|
27822
|
+
pass: true,
|
|
27823
|
+
detail: `Privacy-safe local monitor summary: ${path51.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
27824
|
+
});
|
|
27825
|
+
return results;
|
|
27826
|
+
}
|
|
27827
|
+
function checkClaudeCodeInstall() {
|
|
27828
|
+
const results = [];
|
|
27829
|
+
const execPath = process.env.CLAUDE_CODE_EXECPATH ?? "";
|
|
27830
|
+
if (execPath.length > 0 && (execPath.includes("claude/versions/") || execPath.includes("claude.exe") || execPath.includes("claude-native"))) {
|
|
27831
|
+
results.push({
|
|
27832
|
+
name: "cc/execpath-clean",
|
|
27833
|
+
pass: false,
|
|
27834
|
+
detail: `CLAUDE_CODE_EXECPATH points to native binary: "${execPath}" \u2014 20K phantom billing risk. Unset it: unset CLAUDE_CODE_EXECPATH`
|
|
27835
|
+
});
|
|
27836
|
+
} else {
|
|
27837
|
+
results.push({
|
|
27838
|
+
name: "cc/execpath-clean",
|
|
27839
|
+
pass: true,
|
|
27840
|
+
detail: execPath ? `CLAUDE_CODE_EXECPATH=${execPath} (node runtime, OK)` : "CLAUDE_CODE_EXECPATH is unset"
|
|
27841
|
+
});
|
|
27842
|
+
}
|
|
27843
|
+
try {
|
|
27844
|
+
const claudePath = execSync13("which claude 2>/dev/null || true", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
27845
|
+
if (!claudePath) {
|
|
27846
|
+
results.push({
|
|
27847
|
+
name: "cc/cli-binary",
|
|
27848
|
+
pass: false,
|
|
27849
|
+
detail: "claude not found in PATH"
|
|
27850
|
+
});
|
|
27851
|
+
} else {
|
|
27852
|
+
let resolved = claudePath;
|
|
27853
|
+
try {
|
|
27854
|
+
resolved = execSync13(`readlink -f "${claudePath}" 2>/dev/null || readlink "${claudePath}" 2>/dev/null || echo "${claudePath}"`, {
|
|
27855
|
+
encoding: "utf8",
|
|
27856
|
+
timeout: 5e3
|
|
27857
|
+
}).trim();
|
|
27858
|
+
} catch {
|
|
27859
|
+
}
|
|
27860
|
+
if (resolved.includes("bin/claude.exe") || resolved.includes("bin/claude-native")) {
|
|
27861
|
+
results.push({
|
|
27862
|
+
name: "cc/cli-binary",
|
|
27863
|
+
pass: false,
|
|
27864
|
+
detail: `claude resolves to native binary: ${resolved}. Should be cli.js. Run: npm install -g @anthropic-ai/claude-code@2.1.98`
|
|
27865
|
+
});
|
|
27866
|
+
} else {
|
|
27867
|
+
results.push({
|
|
27868
|
+
name: "cc/cli-binary",
|
|
27869
|
+
pass: true,
|
|
27870
|
+
detail: `claude resolves to: ${resolved}`
|
|
27871
|
+
});
|
|
27872
|
+
}
|
|
27873
|
+
}
|
|
27874
|
+
} catch {
|
|
27875
|
+
results.push({
|
|
27876
|
+
name: "cc/cli-binary",
|
|
27877
|
+
pass: false,
|
|
27878
|
+
detail: "Failed to check claude binary path"
|
|
27879
|
+
});
|
|
27880
|
+
}
|
|
27881
|
+
const versionsDir = path51.join(
|
|
27882
|
+
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
27883
|
+
".local",
|
|
27884
|
+
"share",
|
|
27885
|
+
"claude",
|
|
27886
|
+
"versions"
|
|
27887
|
+
);
|
|
27888
|
+
if (existsSync39(versionsDir)) {
|
|
27889
|
+
try {
|
|
27890
|
+
const entries = readdirSync14(versionsDir);
|
|
27891
|
+
if (entries.length > 0) {
|
|
27892
|
+
results.push({
|
|
27893
|
+
name: "cc/native-cache-clean",
|
|
27894
|
+
pass: false,
|
|
27895
|
+
detail: `${entries.length} cached native version(s) found in ${versionsDir}: ${entries.slice(0, 3).join(", ")}${entries.length > 3 ? "..." : ""}. Remove: rm -rf "${versionsDir}"`
|
|
27896
|
+
});
|
|
27897
|
+
} else {
|
|
27898
|
+
results.push({
|
|
27899
|
+
name: "cc/native-cache-clean",
|
|
27900
|
+
pass: true,
|
|
27901
|
+
detail: `${versionsDir} is empty`
|
|
27902
|
+
});
|
|
27903
|
+
}
|
|
27904
|
+
} catch {
|
|
27905
|
+
results.push({
|
|
27906
|
+
name: "cc/native-cache-clean",
|
|
27907
|
+
pass: true,
|
|
27908
|
+
detail: `${versionsDir} not readable (OK)`
|
|
27909
|
+
});
|
|
27910
|
+
}
|
|
27911
|
+
} else {
|
|
27912
|
+
results.push({
|
|
27913
|
+
name: "cc/native-cache-clean",
|
|
27914
|
+
pass: true,
|
|
27915
|
+
detail: `${versionsDir} does not exist`
|
|
27916
|
+
});
|
|
27917
|
+
}
|
|
27918
|
+
const disableOld = process.env.DISABLE_AUTOUPDATER === "1";
|
|
27919
|
+
const disableNew = process.env.CLAUDE_CODE_AUTOUPDATER_DISABLED === "1";
|
|
27920
|
+
if (!disableOld && !disableNew) {
|
|
27921
|
+
results.push({
|
|
27922
|
+
name: "cc/autoupdater-disabled",
|
|
27923
|
+
pass: false,
|
|
27924
|
+
detail: "Neither DISABLE_AUTOUPDATER=1 nor CLAUDE_CODE_AUTOUPDATER_DISABLED=1 is set. Auto-updater may install infected native binary. Export both in your shell profile."
|
|
27925
|
+
});
|
|
27926
|
+
} else {
|
|
27927
|
+
const which = [
|
|
27928
|
+
disableOld ? "DISABLE_AUTOUPDATER=1" : null,
|
|
27929
|
+
disableNew ? "CLAUDE_CODE_AUTOUPDATER_DISABLED=1" : null
|
|
27930
|
+
].filter(Boolean).join(" + ");
|
|
27931
|
+
results.push({
|
|
27932
|
+
name: "cc/autoupdater-disabled",
|
|
27933
|
+
pass: true,
|
|
27934
|
+
detail: `Auto-updater disabled via ${which}`
|
|
27935
|
+
});
|
|
27936
|
+
}
|
|
27937
|
+
try {
|
|
27938
|
+
const ccVersion = execSync13("claude --version 2>/dev/null || echo unknown", {
|
|
27939
|
+
encoding: "utf8",
|
|
27940
|
+
timeout: 5e3
|
|
27941
|
+
}).trim();
|
|
27942
|
+
const versionMatch = ccVersion.match(/(\d+\.\d+\.\d+)/);
|
|
27943
|
+
if (versionMatch) {
|
|
27944
|
+
const ver = versionMatch[1];
|
|
27945
|
+
const [, minor] = ver.split(".").map(Number);
|
|
27946
|
+
const isRisky = minor !== void 0 && minor >= 1 && parseInt(ver.split(".")[2] ?? "0") >= 119;
|
|
27947
|
+
results.push({
|
|
27948
|
+
name: "cc/version",
|
|
27949
|
+
pass: !isRisky,
|
|
27950
|
+
detail: isRisky ? `CC version ${ver} \u2014 \u22652.1.119 ships native binary only. Pin: npm install -g @anthropic-ai/claude-code@2.1.98` : `CC version ${ver}`
|
|
27951
|
+
});
|
|
27952
|
+
}
|
|
27953
|
+
} catch {
|
|
27954
|
+
}
|
|
27955
|
+
return results;
|
|
27956
|
+
}
|
|
27957
|
+
function runHealthCheck() {
|
|
27958
|
+
const pkgRoot = findPackageRoot2();
|
|
27959
|
+
const results = [
|
|
27960
|
+
...checkBuildIntegrity(pkgRoot),
|
|
27961
|
+
...checkEmbedPipeline(pkgRoot),
|
|
27962
|
+
...checkTaskSystem(pkgRoot),
|
|
27963
|
+
...checkWorkerSpawning(pkgRoot),
|
|
27964
|
+
...checkMcpTransport(),
|
|
27965
|
+
...checkClaudeCodeInstall()
|
|
27966
|
+
];
|
|
27967
|
+
const passed = results.filter((r) => r.pass).length;
|
|
27968
|
+
const failed = results.filter((r) => !r.pass).length;
|
|
27969
|
+
return { results, passed, failed };
|
|
27970
|
+
}
|
|
27971
|
+
if (isMainModule(import.meta.url)) {
|
|
27972
|
+
const { results, passed, failed } = runHealthCheck();
|
|
27973
|
+
console.log("\n exe-os Health Check\n");
|
|
27974
|
+
for (const r of results) {
|
|
27975
|
+
const icon = r.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
27976
|
+
console.log(` ${icon} ${r.name}`);
|
|
27977
|
+
console.log(` ${r.detail}`);
|
|
27978
|
+
}
|
|
27979
|
+
console.log(`
|
|
27980
|
+
${passed} passed, ${failed} failed
|
|
27981
|
+
`);
|
|
27982
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
27983
|
+
}
|
|
27984
|
+
|
|
27985
|
+
// src/lib/update-check.ts
|
|
27986
|
+
import { execSync as execSync14 } from "child_process";
|
|
27987
|
+
import { readFileSync as readFileSync34 } from "fs";
|
|
27988
|
+
import path52 from "path";
|
|
27989
|
+
function getLocalVersion(packageRoot) {
|
|
27990
|
+
const pkgPath = path52.join(packageRoot, "package.json");
|
|
27991
|
+
const pkg = JSON.parse(readFileSync34(pkgPath, "utf-8"));
|
|
27992
|
+
return pkg.version;
|
|
27993
|
+
}
|
|
27994
|
+
function getRemoteVersion() {
|
|
27995
|
+
try {
|
|
27996
|
+
const output = execSync14("npm view @askexenow/exe-os version", {
|
|
27997
|
+
encoding: "utf-8",
|
|
27998
|
+
timeout: 15e3,
|
|
27999
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
28000
|
+
});
|
|
28001
|
+
return output.trim();
|
|
28002
|
+
} catch {
|
|
28003
|
+
return null;
|
|
28004
|
+
}
|
|
28005
|
+
}
|
|
28006
|
+
function checkForUpdate(packageRoot) {
|
|
28007
|
+
const localVersion = getLocalVersion(packageRoot);
|
|
28008
|
+
const remoteVersion = getRemoteVersion();
|
|
28009
|
+
if (!remoteVersion) {
|
|
28010
|
+
return {
|
|
28011
|
+
updateAvailable: false,
|
|
28012
|
+
localVersion,
|
|
28013
|
+
error: "Could not reach npm registry or package not published yet"
|
|
28014
|
+
};
|
|
28015
|
+
}
|
|
28016
|
+
if (remoteVersion === localVersion) {
|
|
28017
|
+
return { updateAvailable: false, localVersion, remoteVersion };
|
|
28018
|
+
}
|
|
28019
|
+
return { updateAvailable: true, localVersion, remoteVersion };
|
|
28020
|
+
}
|
|
28021
|
+
|
|
28022
|
+
// src/mcp/tools/cli-parity.ts
|
|
28023
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
28024
|
+
async function runCommand(command, args, timeout = 6e4) {
|
|
28025
|
+
const printable = [command, ...args].join(" ");
|
|
28026
|
+
try {
|
|
28027
|
+
const { stdout, stderr } = await execFileAsync2(command, args, {
|
|
28028
|
+
timeout,
|
|
28029
|
+
maxBuffer: 1024 * 1024,
|
|
28030
|
+
env: process.env
|
|
28031
|
+
});
|
|
28032
|
+
return { ok: true, command: printable, text: `${stdout}${stderr ? `
|
|
28033
|
+
${stderr}` : ""}`.trim() || "OK" };
|
|
28034
|
+
} catch (err) {
|
|
28035
|
+
const e = err;
|
|
28036
|
+
const text3 = `${e.stdout ?? ""}${e.stderr ? `
|
|
28037
|
+
${e.stderr}` : ""}`.trim() || e.message || "Command failed";
|
|
28038
|
+
return { ok: false, command: printable, text: text3 };
|
|
28039
|
+
}
|
|
28040
|
+
}
|
|
28041
|
+
function result2(text3, structured, isError = false) {
|
|
28042
|
+
return { content: [{ type: "text", text: text3 }], structuredContent: structured, isError };
|
|
28043
|
+
}
|
|
28044
|
+
function registerCliParityTools(server2) {
|
|
28045
|
+
server2.registerTool("healthcheck", {
|
|
28046
|
+
title: "Health Check",
|
|
28047
|
+
description: "Run the exe-os product health check and return structured pass/fail results.",
|
|
28048
|
+
inputSchema: {}
|
|
28049
|
+
}, async () => {
|
|
28050
|
+
const out = runHealthCheck();
|
|
28051
|
+
const text3 = ["exe-os healthcheck", "", ...out.results.map((r) => `${r.pass ? "\u2705" : "\u274C"} ${r.name}: ${r.detail}`), "", `${out.passed} passed, ${out.failed} failed`].join("\n");
|
|
28052
|
+
return result2(text3, out, out.failed > 0);
|
|
28053
|
+
});
|
|
28054
|
+
server2.registerTool("doctor", {
|
|
28055
|
+
title: "Doctor",
|
|
28056
|
+
description: "Run exe-os doctor audit. Defaults to read-only diagnostics; optional dry-run/fix flags mirror CLI.",
|
|
28057
|
+
inputSchema: {
|
|
28058
|
+
agent: z89.string().optional(),
|
|
28059
|
+
project: z89.string().optional(),
|
|
28060
|
+
verbose: z89.boolean().default(false),
|
|
28061
|
+
conflicts: z89.boolean().default(false),
|
|
28062
|
+
dry_run: z89.boolean().default(false),
|
|
28063
|
+
fix: z89.boolean().default(false)
|
|
28064
|
+
}
|
|
28065
|
+
}, async ({ agent, project, verbose, conflicts, dry_run, fix }) => {
|
|
28066
|
+
const args = [];
|
|
28067
|
+
if (agent) args.push("--agent", agent);
|
|
28068
|
+
if (project) args.push("--project", project);
|
|
28069
|
+
if (verbose) args.push("--verbose");
|
|
28070
|
+
if (conflicts) args.push("--conflicts");
|
|
28071
|
+
if (dry_run) args.push("--dry-run");
|
|
28072
|
+
if (fix) args.push("--fix");
|
|
28073
|
+
const out = await runCommand("exe-doctor", args, 12e4);
|
|
28074
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28075
|
+
});
|
|
28076
|
+
server2.registerTool("rename_employee", {
|
|
28077
|
+
title: "Rename Employee",
|
|
28078
|
+
description: "Rename an employee using the same path as `exe-os rename <old> <new>`. Use for customer roster/identity renames.",
|
|
28079
|
+
inputSchema: {
|
|
28080
|
+
old_name: z89.string().min(1),
|
|
28081
|
+
new_name: z89.string().min(1),
|
|
28082
|
+
dry_run: z89.boolean().default(false)
|
|
28083
|
+
}
|
|
28084
|
+
}, async ({ old_name, new_name, dry_run }) => {
|
|
28085
|
+
if (dry_run) {
|
|
28086
|
+
const text3 = `Would run: exe-os rename ${old_name} ${new_name}`;
|
|
28087
|
+
return result2(text3, { ok: true, dry_run: true, old_name, new_name });
|
|
28088
|
+
}
|
|
28089
|
+
const out = await runCommand("exe-os", ["rename", old_name, new_name], 6e4);
|
|
28090
|
+
return result2(out.text, { ok: out.ok, command: out.command, old_name, new_name }, !out.ok);
|
|
28091
|
+
});
|
|
28092
|
+
server2.registerTool("status_brief", {
|
|
28093
|
+
title: "Status Brief",
|
|
28094
|
+
description: "Return current employee/tmux status. Mirrors `exe-status` and supports optional deep view for one employee.",
|
|
28095
|
+
inputSchema: { employee: z89.string().optional() }
|
|
28096
|
+
}, async ({ employee }) => {
|
|
28097
|
+
const text3 = await status(employee);
|
|
28098
|
+
return result2(text3, { ok: true, employee: employee ?? null });
|
|
28099
|
+
});
|
|
28100
|
+
server2.registerTool("pending_work_summary", {
|
|
28101
|
+
title: "Pending Work Summary",
|
|
28102
|
+
description: "Return pending reviews, messages, and notifications using the same summaries as the CLI tools.",
|
|
28103
|
+
inputSchema: { agent: z89.string().optional() }
|
|
28104
|
+
}, async ({ agent }) => {
|
|
28105
|
+
const parts = await Promise.all([
|
|
28106
|
+
runCommand("exe-pending-reviews", [], 3e4),
|
|
28107
|
+
runCommand("exe-pending-messages", agent ? [agent] : [], 3e4),
|
|
28108
|
+
runCommand("exe-pending-notifications", agent ? [agent] : [], 3e4)
|
|
28109
|
+
]);
|
|
28110
|
+
const text3 = ["Pending work summary", "", "## Reviews", parts[0].text, "", "## Messages", parts[1].text, "", "## Notifications", parts[2].text].join("\n");
|
|
28111
|
+
return result2(text3, { ok: parts.every((p) => p.ok), commands: parts.map((p) => p.command) }, parts.some((p) => !p.ok));
|
|
28112
|
+
});
|
|
28113
|
+
server2.registerTool("key_status", {
|
|
28114
|
+
title: "Key Status",
|
|
28115
|
+
description: "Read-only Exe OS key status. Does not reveal recovery phrase or rotate keys.",
|
|
28116
|
+
inputSchema: {}
|
|
28117
|
+
}, async () => {
|
|
28118
|
+
const out = await runCommand("exe-os", ["key", "status"], 3e4);
|
|
28119
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28120
|
+
});
|
|
28121
|
+
server2.registerTool("key_rotation_preflight", {
|
|
28122
|
+
title: "Key Rotation Preflight",
|
|
28123
|
+
description: "Dry-run key rotation/update preflight. No destructive changes.",
|
|
28124
|
+
inputSchema: { mode: z89.enum(["rotate", "update"]).default("rotate") }
|
|
28125
|
+
}, async ({ mode }) => {
|
|
28126
|
+
const out = await runCommand("exe-os", ["key", mode, "--dry-run"], 6e4);
|
|
28127
|
+
return result2(out.text, { ok: out.ok, command: out.command, mode }, !out.ok);
|
|
28128
|
+
});
|
|
28129
|
+
server2.registerTool("check_update", {
|
|
28130
|
+
title: "Check Update",
|
|
28131
|
+
description: "Check npm for an available exe-os update without installing it.",
|
|
28132
|
+
inputSchema: {}
|
|
28133
|
+
}, async () => {
|
|
28134
|
+
const packageRoot = new URL("../../..", import.meta.url).pathname;
|
|
28135
|
+
const info = checkForUpdate(packageRoot);
|
|
28136
|
+
const text3 = info.error ? `Update check failed: ${info.error}` : info.updateAvailable ? `Update available: v${info.localVersion} \u2192 v${info.remoteVersion}` : `exe-os is up to date (v${info.localVersion})`;
|
|
28137
|
+
return result2(text3, { ok: !info.error, ...info }, Boolean(info.error));
|
|
28138
|
+
});
|
|
28139
|
+
server2.registerTool("stack_update_check", {
|
|
28140
|
+
title: "Stack Update Check",
|
|
28141
|
+
description: "Plan/check a customer stack update without applying Docker changes. Mirrors `exe-os stack-update --check`.",
|
|
28142
|
+
inputSchema: {
|
|
28143
|
+
target: z89.string().optional(),
|
|
28144
|
+
manifest: z89.string().optional(),
|
|
28145
|
+
compose_file: z89.string().optional(),
|
|
28146
|
+
env_file: z89.string().optional(),
|
|
28147
|
+
deployment_persona: z89.enum(["customer", "askexe-control-plane"]).default("customer")
|
|
28148
|
+
}
|
|
28149
|
+
}, async ({ target, manifest, compose_file, env_file, deployment_persona }) => {
|
|
28150
|
+
const args = ["stack-update", "--check", "--deployment-persona", deployment_persona];
|
|
28151
|
+
if (target) args.push("--target", target);
|
|
28152
|
+
if (manifest) args.push("--manifest", manifest);
|
|
28153
|
+
if (compose_file) args.push("--compose-file", compose_file);
|
|
28154
|
+
if (env_file) args.push("--env-file", env_file);
|
|
28155
|
+
const out = await runCommand("exe-os", args, 9e4);
|
|
28156
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28157
|
+
});
|
|
28158
|
+
server2.registerTool("cloud_status", {
|
|
28159
|
+
title: "Cloud Status",
|
|
28160
|
+
description: "Read-only cloud sync status. Does not reveal recovery phrase.",
|
|
28161
|
+
inputSchema: {}
|
|
28162
|
+
}, async () => {
|
|
28163
|
+
const out = await runCommand("exe-os", ["cloud", "status"], 45e3);
|
|
28164
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28165
|
+
});
|
|
28166
|
+
}
|
|
28167
|
+
|
|
27045
28168
|
// src/mcp/tools/get-session-events.ts
|
|
27046
28169
|
init_active_agent();
|
|
27047
28170
|
init_fast_db_init();
|
|
27048
|
-
import { z as
|
|
28171
|
+
import { z as z90 } from "zod";
|
|
27049
28172
|
|
|
27050
28173
|
// src/lib/session-events.ts
|
|
27051
28174
|
init_task_scope();
|
|
27052
28175
|
init_project_name();
|
|
27053
|
-
import { randomUUID as
|
|
28176
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
27054
28177
|
async function ensureSessionEventsTable(client) {
|
|
27055
28178
|
await client.execute(`
|
|
27056
28179
|
CREATE TABLE IF NOT EXISTS session_events (
|
|
@@ -27103,7 +28226,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27103
28226
|
const where = `WHERE ${conditions.join(" AND ")}${scope.sql}`;
|
|
27104
28227
|
args.push(...scope.args);
|
|
27105
28228
|
args.push(Math.min(Math.max(options.limit ?? 20, 1), 100));
|
|
27106
|
-
const
|
|
28229
|
+
const result3 = await client.execute({
|
|
27107
28230
|
sql: `SELECT id, agent_id, agent_role, session_id, session_scope,
|
|
27108
28231
|
project_name, event_index, event_type, tool_name, tool_use_id,
|
|
27109
28232
|
content, payload_json, has_error, created_at
|
|
@@ -27113,7 +28236,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27113
28236
|
LIMIT ?`,
|
|
27114
28237
|
args
|
|
27115
28238
|
});
|
|
27116
|
-
return
|
|
28239
|
+
return result3.rows.map((row) => ({
|
|
27117
28240
|
id: String(row.id),
|
|
27118
28241
|
agentId: String(row.agent_id),
|
|
27119
28242
|
agentRole: String(row.agent_role),
|
|
@@ -27132,7 +28255,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27132
28255
|
}
|
|
27133
28256
|
|
|
27134
28257
|
// src/mcp/tools/get-session-events.ts
|
|
27135
|
-
var EVENT_TYPE =
|
|
28258
|
+
var EVENT_TYPE = z90.enum([
|
|
27136
28259
|
"user_prompt",
|
|
27137
28260
|
"assistant_response",
|
|
27138
28261
|
"tool_call",
|
|
@@ -27162,11 +28285,11 @@ function registerGetSessionEvents(server2) {
|
|
|
27162
28285
|
title: "Get Session Events",
|
|
27163
28286
|
description: "Return exact recent chronological user prompts, assistant responses, and tool calls from the append-only session journal. Use this when asked what happened last, not semantic memory search.",
|
|
27164
28287
|
inputSchema: {
|
|
27165
|
-
agent_id:
|
|
27166
|
-
session_id:
|
|
28288
|
+
agent_id: z90.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28289
|
+
session_id: z90.string().optional().describe("Optional exact runtime session id."),
|
|
27167
28290
|
event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
|
|
27168
|
-
project_name:
|
|
27169
|
-
limit:
|
|
28291
|
+
project_name: z90.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
|
|
28292
|
+
limit: z90.number().int().min(1).max(100).default(20).describe("Number of events to return.")
|
|
27170
28293
|
}
|
|
27171
28294
|
},
|
|
27172
28295
|
async ({ agent_id, session_id, event_type, project_name, limit }) => {
|
|
@@ -27202,8 +28325,8 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
27202
28325
|
title: "Get Last Assistant Response",
|
|
27203
28326
|
description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
|
|
27204
28327
|
inputSchema: {
|
|
27205
|
-
agent_id:
|
|
27206
|
-
project_name:
|
|
28328
|
+
agent_id: z90.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28329
|
+
project_name: z90.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
|
|
27207
28330
|
}
|
|
27208
28331
|
},
|
|
27209
28332
|
async ({ agent_id, project_name }) => {
|
|
@@ -27233,36 +28356,36 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
27233
28356
|
}
|
|
27234
28357
|
|
|
27235
28358
|
// src/mcp/tools/code-context.ts
|
|
27236
|
-
import { z as
|
|
28359
|
+
import { z as z91 } from "zod";
|
|
27237
28360
|
|
|
27238
28361
|
// src/lib/code-context-index.ts
|
|
27239
28362
|
init_config();
|
|
27240
28363
|
import crypto19 from "crypto";
|
|
27241
|
-
import
|
|
27242
|
-
import { existsSync as
|
|
28364
|
+
import path53 from "path";
|
|
28365
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync22, readFileSync as readFileSync35, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync24 } from "fs";
|
|
27243
28366
|
import { spawnSync } from "child_process";
|
|
27244
28367
|
var INDEX_VERSION = 2;
|
|
27245
28368
|
var DEFAULT_MAX_FILES = 5e3;
|
|
27246
28369
|
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
27247
28370
|
function normalizeProjectRoot(projectRoot) {
|
|
27248
|
-
return
|
|
28371
|
+
return path53.resolve(projectRoot || process.cwd());
|
|
27249
28372
|
}
|
|
27250
28373
|
function hashText(text3) {
|
|
27251
28374
|
return crypto19.createHash("sha256").update(text3).digest("hex");
|
|
27252
28375
|
}
|
|
27253
28376
|
function indexDir() {
|
|
27254
|
-
const dir =
|
|
27255
|
-
|
|
28377
|
+
const dir = path53.join(EXE_AI_DIR, "code-context");
|
|
28378
|
+
mkdirSync22(dir, { recursive: true });
|
|
27256
28379
|
return dir;
|
|
27257
28380
|
}
|
|
27258
28381
|
function getCodeContextIndexPath(projectRoot) {
|
|
27259
28382
|
const root = normalizeProjectRoot(projectRoot);
|
|
27260
28383
|
const rootHash = hashText(root).slice(0, 16);
|
|
27261
|
-
return
|
|
28384
|
+
return path53.join(indexDir(), `${rootHash}.json`);
|
|
27262
28385
|
}
|
|
27263
28386
|
function currentBranch(projectRoot) {
|
|
27264
|
-
const
|
|
27265
|
-
const branch =
|
|
28387
|
+
const result3 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
28388
|
+
const branch = result3.status === 0 ? result3.stdout.trim() : "";
|
|
27266
28389
|
return branch || "detached-or-unknown";
|
|
27267
28390
|
}
|
|
27268
28391
|
function shouldIgnore(relPath) {
|
|
@@ -27270,9 +28393,9 @@ function shouldIgnore(relPath) {
|
|
|
27270
28393
|
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
27271
28394
|
}
|
|
27272
28395
|
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
27273
|
-
for (const entry of
|
|
27274
|
-
const abs =
|
|
27275
|
-
const rel =
|
|
28396
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
28397
|
+
const abs = path53.join(dir, entry.name);
|
|
28398
|
+
const rel = path53.relative(projectRoot, abs).replaceAll(path53.sep, "/");
|
|
27276
28399
|
if (shouldIgnore(rel)) continue;
|
|
27277
28400
|
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
27278
28401
|
else if (entry.isFile()) out.push(rel);
|
|
@@ -27288,7 +28411,7 @@ function listCodeFiles(projectRoot, maxFiles) {
|
|
|
27288
28411
|
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
27289
28412
|
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
27290
28413
|
}
|
|
27291
|
-
return files.map((file) => file.replaceAll(
|
|
28414
|
+
return files.map((file) => file.replaceAll(path53.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
27292
28415
|
}
|
|
27293
28416
|
function parseImportPaths(importText) {
|
|
27294
28417
|
const paths = [];
|
|
@@ -27301,13 +28424,13 @@ function parseImportPaths(importText) {
|
|
|
27301
28424
|
}
|
|
27302
28425
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
27303
28426
|
if (!importPath.startsWith(".")) return null;
|
|
27304
|
-
const base =
|
|
28427
|
+
const base = path53.posix.normalize(path53.posix.join(path53.posix.dirname(fromFile.replaceAll(path53.sep, "/")), importPath));
|
|
27305
28428
|
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
27306
28429
|
const candidates = [base];
|
|
27307
28430
|
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
27308
28431
|
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
27309
28432
|
}
|
|
27310
|
-
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(
|
|
28433
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path53.posix.join(base, indexName));
|
|
27311
28434
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
27312
28435
|
}
|
|
27313
28436
|
function symbolId(filePath, chunk) {
|
|
@@ -27315,9 +28438,9 @@ function symbolId(filePath, chunk) {
|
|
|
27315
28438
|
}
|
|
27316
28439
|
function loadIndex(projectRoot) {
|
|
27317
28440
|
const file = getCodeContextIndexPath(projectRoot);
|
|
27318
|
-
if (!
|
|
28441
|
+
if (!existsSync40(file)) return null;
|
|
27319
28442
|
try {
|
|
27320
|
-
const parsed = JSON.parse(
|
|
28443
|
+
const parsed = JSON.parse(readFileSync35(file, "utf8"));
|
|
27321
28444
|
if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
|
|
27322
28445
|
return parsed;
|
|
27323
28446
|
} catch {
|
|
@@ -27325,10 +28448,10 @@ function loadIndex(projectRoot) {
|
|
|
27325
28448
|
}
|
|
27326
28449
|
}
|
|
27327
28450
|
function saveIndex(index) {
|
|
27328
|
-
|
|
28451
|
+
writeFileSync24(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
27329
28452
|
}
|
|
27330
28453
|
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
27331
|
-
const absPath =
|
|
28454
|
+
const absPath = path53.join(projectRoot, relPath);
|
|
27332
28455
|
let stat;
|
|
27333
28456
|
try {
|
|
27334
28457
|
stat = statSync9(absPath);
|
|
@@ -27338,7 +28461,7 @@ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
|
27338
28461
|
if (!stat.isFile()) return { record: null, reused: false };
|
|
27339
28462
|
const language = languageForFile(relPath);
|
|
27340
28463
|
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
27341
|
-
const source =
|
|
28464
|
+
const source = readFileSync35(absPath, "utf8");
|
|
27342
28465
|
const hash = hashText(source);
|
|
27343
28466
|
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
|
|
27344
28467
|
return { record: previous, reused: true };
|
|
@@ -27366,13 +28489,13 @@ function buildCodeContextIndex(options = {}) {
|
|
|
27366
28489
|
const branch = currentBranch(projectRoot);
|
|
27367
28490
|
const previous = options.force ? null : loadIndex(projectRoot);
|
|
27368
28491
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
27369
|
-
const allFiles = new Set(files.map((file) => file.replaceAll(
|
|
28492
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path53.sep, "/")));
|
|
27370
28493
|
const fileRecords = {};
|
|
27371
28494
|
let rebuiltFiles = 0;
|
|
27372
28495
|
let reusedFiles = 0;
|
|
27373
28496
|
let skippedFiles = 0;
|
|
27374
28497
|
for (const rel of files) {
|
|
27375
|
-
const normalized = rel.replaceAll(
|
|
28498
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
27376
28499
|
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
27377
28500
|
if (record) {
|
|
27378
28501
|
fileRecords[normalized] = record;
|
|
@@ -27401,11 +28524,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
27401
28524
|
if (loaded) {
|
|
27402
28525
|
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
27403
28526
|
const unchanged = currentFiles.every((rel) => {
|
|
27404
|
-
const normalized = rel.replaceAll(
|
|
28527
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
27405
28528
|
const existing = loaded.files[normalized];
|
|
27406
28529
|
if (!existing) return false;
|
|
27407
28530
|
try {
|
|
27408
|
-
const stat = statSync9(
|
|
28531
|
+
const stat = statSync9(path53.join(projectRoot, normalized));
|
|
27409
28532
|
return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
|
|
27410
28533
|
} catch {
|
|
27411
28534
|
return false;
|
|
@@ -27458,9 +28581,9 @@ function globToRegex(pattern) {
|
|
|
27458
28581
|
}
|
|
27459
28582
|
function matchesPath(filePath, patterns) {
|
|
27460
28583
|
if (!patterns || patterns.length === 0) return true;
|
|
27461
|
-
const normalized = filePath.replaceAll(
|
|
28584
|
+
const normalized = filePath.replaceAll(path53.sep, "/");
|
|
27462
28585
|
return patterns.some((pattern) => {
|
|
27463
|
-
const p = pattern.replaceAll(
|
|
28586
|
+
const p = pattern.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
27464
28587
|
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
27465
28588
|
});
|
|
27466
28589
|
}
|
|
@@ -27578,7 +28701,7 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
27578
28701
|
}
|
|
27579
28702
|
function resolveTargetFile(index, input) {
|
|
27580
28703
|
if (input.filePath) {
|
|
27581
|
-
const normalized = input.filePath.replaceAll(
|
|
28704
|
+
const normalized = input.filePath.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
27582
28705
|
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
27583
28706
|
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
27584
28707
|
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
@@ -27608,7 +28731,7 @@ function analyzeBlastRadius(input) {
|
|
|
27608
28731
|
}
|
|
27609
28732
|
}
|
|
27610
28733
|
}
|
|
27611
|
-
const targetBase =
|
|
28734
|
+
const targetBase = path53.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
27612
28735
|
const symbolLower = input.symbol?.toLowerCase();
|
|
27613
28736
|
const tests = Object.keys(index.files).filter((file) => {
|
|
27614
28737
|
const lower = file.toLowerCase();
|
|
@@ -27649,19 +28772,19 @@ function registerCodeContext(server2) {
|
|
|
27649
28772
|
title: "Code Context",
|
|
27650
28773
|
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
27651
28774
|
inputSchema: {
|
|
27652
|
-
action:
|
|
27653
|
-
project_root:
|
|
27654
|
-
query:
|
|
27655
|
-
symbol:
|
|
27656
|
-
file_path:
|
|
27657
|
-
force:
|
|
27658
|
-
limit:
|
|
27659
|
-
offset:
|
|
27660
|
-
refresh_index:
|
|
27661
|
-
languages:
|
|
27662
|
-
paths:
|
|
27663
|
-
depth:
|
|
27664
|
-
max_files:
|
|
28775
|
+
action: z91.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
|
|
28776
|
+
project_root: z91.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
28777
|
+
query: z91.string().optional().describe("Search query for action=search"),
|
|
28778
|
+
symbol: z91.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
28779
|
+
file_path: z91.string().optional().describe("File path for blast_radius"),
|
|
28780
|
+
force: z91.boolean().optional().describe("Force rebuild before answering"),
|
|
28781
|
+
limit: z91.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
28782
|
+
offset: z91.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
28783
|
+
refresh_index: z91.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
28784
|
+
languages: z91.array(z91.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
28785
|
+
paths: z91.array(z91.string()).optional().describe("Path/glob filters"),
|
|
28786
|
+
depth: z91.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
28787
|
+
max_files: z91.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
|
|
27665
28788
|
}
|
|
27666
28789
|
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
|
|
27667
28790
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
@@ -27697,18 +28820,18 @@ function registerCodeContext(server2) {
|
|
|
27697
28820
|
}
|
|
27698
28821
|
if (action === "blast_radius") {
|
|
27699
28822
|
if (!symbol && !file_path) return errorResult10('code_context action "blast_radius" requires symbol or file_path');
|
|
27700
|
-
const
|
|
27701
|
-
if (!
|
|
27702
|
-
return jsonResult(
|
|
28823
|
+
const result3 = analyzeBlastRadius({ projectRoot: project_root, force, symbol, filePath: file_path, depth });
|
|
28824
|
+
if (!result3) return errorResult10(`No code context target found for ${symbol || file_path}`);
|
|
28825
|
+
return jsonResult(result3);
|
|
27703
28826
|
}
|
|
27704
28827
|
return errorResult10(`Unknown code_context action: ${String(action)}`);
|
|
27705
28828
|
});
|
|
27706
28829
|
}
|
|
27707
28830
|
|
|
27708
28831
|
// src/mcp/tools/support-inbox.ts
|
|
27709
|
-
import { z as
|
|
28832
|
+
import { z as z92 } from "zod";
|
|
27710
28833
|
var DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
27711
|
-
var STATUS =
|
|
28834
|
+
var STATUS = z92.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
27712
28835
|
function adminToken() {
|
|
27713
28836
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
27714
28837
|
}
|
|
@@ -27747,14 +28870,14 @@ function registerListBugReports(server2) {
|
|
|
27747
28870
|
title: "List Bug Reports",
|
|
27748
28871
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
27749
28872
|
inputSchema: {
|
|
27750
|
-
status:
|
|
27751
|
-
severity:
|
|
27752
|
-
limit:
|
|
28873
|
+
status: z92.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
28874
|
+
severity: z92.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
28875
|
+
limit: z92.number().int().min(1).max(100).default(25)
|
|
27753
28876
|
}
|
|
27754
28877
|
},
|
|
27755
|
-
async ({ status, severity, limit }) => {
|
|
28878
|
+
async ({ status: status2, severity, limit }) => {
|
|
27756
28879
|
const url = new URL(endpoint());
|
|
27757
|
-
url.searchParams.set("status",
|
|
28880
|
+
url.searchParams.set("status", status2);
|
|
27758
28881
|
url.searchParams.set("limit", String(limit));
|
|
27759
28882
|
if (severity) url.searchParams.set("severity", severity);
|
|
27760
28883
|
const data = await requestJson(url.toString());
|
|
@@ -27768,7 +28891,7 @@ function registerGetBugReport(server2) {
|
|
|
27768
28891
|
{
|
|
27769
28892
|
title: "Get Bug Report",
|
|
27770
28893
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
27771
|
-
inputSchema: { id:
|
|
28894
|
+
inputSchema: { id: z92.string().min(8) }
|
|
27772
28895
|
},
|
|
27773
28896
|
async ({ id }) => {
|
|
27774
28897
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -27783,18 +28906,18 @@ function registerTriageBugReport(server2) {
|
|
|
27783
28906
|
title: "Triage Bug Report",
|
|
27784
28907
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
27785
28908
|
inputSchema: {
|
|
27786
|
-
id:
|
|
28909
|
+
id: z92.string().min(8),
|
|
27787
28910
|
status: STATUS.optional(),
|
|
27788
|
-
triage_notes:
|
|
27789
|
-
linked_task_id:
|
|
27790
|
-
linked_commit:
|
|
27791
|
-
fixed_version:
|
|
28911
|
+
triage_notes: z92.string().optional(),
|
|
28912
|
+
linked_task_id: z92.string().optional(),
|
|
28913
|
+
linked_commit: z92.string().optional(),
|
|
28914
|
+
fixed_version: z92.string().optional()
|
|
27792
28915
|
}
|
|
27793
28916
|
},
|
|
27794
|
-
async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
28917
|
+
async ({ id, status: status2, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
27795
28918
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`, {
|
|
27796
28919
|
method: "PATCH",
|
|
27797
|
-
body: JSON.stringify({ status, triage_notes, linked_task_id, linked_commit, fixed_version })
|
|
28920
|
+
body: JSON.stringify({ status: status2, triage_notes, linked_task_id, linked_commit, fixed_version })
|
|
27798
28921
|
});
|
|
27799
28922
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
27800
28923
|
}
|
|
@@ -27851,6 +28974,8 @@ var TOOL_CATEGORIES = {
|
|
|
27851
28974
|
registerStoreDecision: "core",
|
|
27852
28975
|
registerGetDecision: "core",
|
|
27853
28976
|
registerCreateBugReport: "core",
|
|
28977
|
+
registerSupportTools: "core",
|
|
28978
|
+
registerCliParityTools: "admin",
|
|
27854
28979
|
registerGetSessionEvents: "core",
|
|
27855
28980
|
registerGetLastAssistantResponse: "core",
|
|
27856
28981
|
registerCodeContext: "graph-read",
|
|
@@ -28120,6 +29245,8 @@ function registerAllTools(server2) {
|
|
|
28120
29245
|
gate("registerStoreDecision", registerStoreDecision);
|
|
28121
29246
|
gate("registerGetDecision", registerGetDecision);
|
|
28122
29247
|
gate("registerCreateBugReport", registerCreateBugReport);
|
|
29248
|
+
gate("registerSupportTools", registerSupportTools);
|
|
29249
|
+
gate("registerCliParityTools", registerCliParityTools);
|
|
28123
29250
|
gate("registerGetSessionEvents", registerGetSessionEvents);
|
|
28124
29251
|
gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
|
|
28125
29252
|
gate("registerCodeContext", registerCodeContext);
|
|
@@ -28206,9 +29333,9 @@ async function withTrace(toolName, fn) {
|
|
|
28206
29333
|
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
28207
29334
|
span.setAttribute("mcp.tool.name", toolName);
|
|
28208
29335
|
try {
|
|
28209
|
-
const
|
|
29336
|
+
const result3 = await fn();
|
|
28210
29337
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
28211
|
-
return
|
|
29338
|
+
return result3;
|
|
28212
29339
|
} catch (err) {
|
|
28213
29340
|
span.setStatus({
|
|
28214
29341
|
code: SpanStatusCode.ERROR,
|
|
@@ -28293,16 +29420,16 @@ try {
|
|
|
28293
29420
|
}
|
|
28294
29421
|
}, 3e4);
|
|
28295
29422
|
_ppidWatchdog.unref();
|
|
28296
|
-
const MCP_VERSION_PATH =
|
|
29423
|
+
const MCP_VERSION_PATH = path54.join(os20.homedir(), ".exe-os", "mcp-version");
|
|
28297
29424
|
let _currentMcpVersion = null;
|
|
28298
29425
|
try {
|
|
28299
|
-
_currentMcpVersion =
|
|
29426
|
+
_currentMcpVersion = existsSync41(MCP_VERSION_PATH) ? readFileSync36(MCP_VERSION_PATH, "utf8").trim() : null;
|
|
28300
29427
|
} catch {
|
|
28301
29428
|
}
|
|
28302
29429
|
const _versionWatchdog = setInterval(() => {
|
|
28303
29430
|
try {
|
|
28304
|
-
if (!
|
|
28305
|
-
const diskVersion =
|
|
29431
|
+
if (!existsSync41(MCP_VERSION_PATH)) return;
|
|
29432
|
+
const diskVersion = readFileSync36(MCP_VERSION_PATH, "utf8").trim();
|
|
28306
29433
|
if (_currentMcpVersion && diskVersion !== _currentMcpVersion) {
|
|
28307
29434
|
process.stderr.write(
|
|
28308
29435
|
`[exe-os] MCP version changed (${_currentMcpVersion} \u2192 ${diskVersion}). Hot-reloading...
|
|
@@ -28319,10 +29446,10 @@ try {
|
|
|
28319
29446
|
_backfillTimer = setInterval(async () => {
|
|
28320
29447
|
try {
|
|
28321
29448
|
const client = getClient();
|
|
28322
|
-
const
|
|
29449
|
+
const result3 = await client.execute(
|
|
28323
29450
|
"SELECT COUNT(*) as cnt FROM memories WHERE vector IS NULL"
|
|
28324
29451
|
);
|
|
28325
|
-
const nullCount = Number(
|
|
29452
|
+
const nullCount = Number(result3.rows[0]?.cnt) || 0;
|
|
28326
29453
|
if (nullCount === 0) return;
|
|
28327
29454
|
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
28328
29455
|
if (!tryAcquireWorkerSlot2()) {
|
|
@@ -28334,15 +29461,15 @@ try {
|
|
|
28334
29461
|
`[exe-os] Periodic backfill: ${nullCount} NULL vectors \u2014 spawning job
|
|
28335
29462
|
`
|
|
28336
29463
|
);
|
|
28337
|
-
const thisFile =
|
|
28338
|
-
const backfillPath =
|
|
28339
|
-
|
|
29464
|
+
const thisFile = fileURLToPath7(import.meta.url);
|
|
29465
|
+
const backfillPath = path54.resolve(
|
|
29466
|
+
path54.dirname(thisFile),
|
|
28340
29467
|
"../bin/backfill-vectors.js"
|
|
28341
29468
|
);
|
|
28342
|
-
if (
|
|
29469
|
+
if (existsSync41(backfillPath)) {
|
|
28343
29470
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
28344
|
-
const logPath =
|
|
28345
|
-
|
|
29471
|
+
const logPath = path54.join(exeDir, "workers.log");
|
|
29472
|
+
mkdirSync23(path54.dirname(logPath), { recursive: true });
|
|
28346
29473
|
let logFd = "ignore";
|
|
28347
29474
|
try {
|
|
28348
29475
|
logFd = openSync4(logPath, "a");
|