@askexenow/exe-os 0.9.80 → 0.9.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deploy/stack-manifests/v0.9.json +138 -5
- package/dist/bin/cli.js +7 -6
- package/dist/bin/customer-readiness.js +23 -0
- package/dist/bin/exe-gateway.js +123 -10
- package/dist/bin/registry-proxy.js +1 -1
- package/dist/bin/stack-update.js +163 -41
- package/dist/lib/exe-daemon.js +1308 -612
- package/dist/mcp/server.js +1379 -449
- package/package.json +1 -1
- package/stack.release.json +5 -4
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 {
|
|
@@ -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, path53, 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,7 +15588,7 @@ 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 (!
|
|
15591
|
+
if (!existsSync42(dir)) mkdirSync24(dir, { recursive: true });
|
|
15366
15592
|
writeFileSync25(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
15367
15593
|
}
|
|
15368
15594
|
}
|
|
@@ -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: {
|
|
@@ -17718,7 +17944,7 @@ init_config();
|
|
|
17718
17944
|
import { z as z39 } from "zod";
|
|
17719
17945
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
17720
17946
|
var QUERY_SCOPE = z39.enum(["raw", "crm", "wiki", "memory", "gateway", "all"]);
|
|
17721
|
-
var QUERY_ACTION = z39.enum(["search", "list_sources"]);
|
|
17947
|
+
var QUERY_ACTION = z39.enum(["search", "list_sources", "sql"]);
|
|
17722
17948
|
var LOCAL_GATEWAY_HTTP_HOSTS = /^(localhost|127\.0\.0\.1|::1|exe-gateway|gateway)$/i;
|
|
17723
17949
|
function assertSecureGatewayUrlForToken(gatewayUrl, authToken) {
|
|
17724
17950
|
if (!authToken) return;
|
|
@@ -17753,14 +17979,15 @@ function registerQueryCompanyBrain(server2) {
|
|
|
17753
17979
|
title: "Query Company Brain",
|
|
17754
17980
|
description: "Search the Company Brain \u2014 queries across all data (raw events, wiki docs, agent memories, gateway messages).",
|
|
17755
17981
|
inputSchema: {
|
|
17756
|
-
action: QUERY_ACTION.default("search").describe("search = search exe-db; list_sources = list available
|
|
17982
|
+
action: QUERY_ACTION.default("search").describe("search = search exe-db; list_sources = list available sources; sql = run read-only SQL through gateway"),
|
|
17757
17983
|
query: z39.string().optional().describe("Search text. Required for action=search."),
|
|
17984
|
+
sql: z39.string().optional().describe("Read-only SELECT/WITH SQL. Required for action=sql."),
|
|
17758
17985
|
scope: QUERY_SCOPE.default("all").describe("Which data scope to search/read (raw, crm, wiki, memory, gateway, all)"),
|
|
17759
17986
|
source: z39.string().optional().describe("Filter raw events by source (shopify, asana, etc.)"),
|
|
17760
17987
|
limit: z39.coerce.number().int().min(1).max(100).default(20).describe("Max results per scope")
|
|
17761
17988
|
}
|
|
17762
17989
|
},
|
|
17763
|
-
async ({ action, query, scope, source, limit }) => {
|
|
17990
|
+
async ({ action, query, sql, scope, source, limit }) => {
|
|
17764
17991
|
const resolvedAction = action ?? "search";
|
|
17765
17992
|
if (resolvedAction === "search" && !query?.trim()) {
|
|
17766
17993
|
return {
|
|
@@ -17768,6 +17995,12 @@ function registerQueryCompanyBrain(server2) {
|
|
|
17768
17995
|
isError: true
|
|
17769
17996
|
};
|
|
17770
17997
|
}
|
|
17998
|
+
if (resolvedAction === "sql" && !sql?.trim()) {
|
|
17999
|
+
return {
|
|
18000
|
+
content: [{ type: "text", text: "sql is required when action=sql" }],
|
|
18001
|
+
isError: true
|
|
18002
|
+
};
|
|
18003
|
+
}
|
|
17771
18004
|
const config2 = await loadConfig();
|
|
17772
18005
|
const gatewayUrl = config2.gateway?.url || process.env.EXE_GATEWAY_URL;
|
|
17773
18006
|
const authToken = config2.gateway?.authToken || process.env.EXE_GATEWAY_QUERY_AUTH_TOKEN || process.env.EXE_COMPANY_BRAIN_AUTH_TOKEN || process.env.EXE_GATEWAY_AUTH_TOKEN;
|
|
@@ -17796,7 +18029,10 @@ function registerQueryCompanyBrain(server2) {
|
|
|
17796
18029
|
}
|
|
17797
18030
|
let url;
|
|
17798
18031
|
try {
|
|
17799
|
-
url = new URL(
|
|
18032
|
+
url = new URL(
|
|
18033
|
+
resolvedAction === "list_sources" ? "/query/sources" : resolvedAction === "sql" ? "/query/sql" : "/query",
|
|
18034
|
+
gatewayUrl
|
|
18035
|
+
);
|
|
17800
18036
|
} catch (err) {
|
|
17801
18037
|
return {
|
|
17802
18038
|
content: [
|
|
@@ -17816,7 +18052,12 @@ function registerQueryCompanyBrain(server2) {
|
|
|
17816
18052
|
}
|
|
17817
18053
|
try {
|
|
17818
18054
|
const response = await fetch(url.toString(), {
|
|
17819
|
-
|
|
18055
|
+
method: resolvedAction === "sql" ? "POST" : "GET",
|
|
18056
|
+
headers: {
|
|
18057
|
+
...authToken ? { Authorization: `Bearer ${authToken}` } : {},
|
|
18058
|
+
...resolvedAction === "sql" ? { "Content-Type": "application/json" } : {}
|
|
18059
|
+
},
|
|
18060
|
+
body: resolvedAction === "sql" ? JSON.stringify({ sql: sql.trim(), max_rows: limit }) : void 0,
|
|
17820
18061
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
17821
18062
|
});
|
|
17822
18063
|
if (!response.ok) {
|
|
@@ -18282,8 +18523,8 @@ async function generateGraphReport(client, projectName) {
|
|
|
18282
18523
|
}).slice(0, 10);
|
|
18283
18524
|
let hyperedgeCount = 0;
|
|
18284
18525
|
try {
|
|
18285
|
-
const
|
|
18286
|
-
hyperedgeCount = Number(
|
|
18526
|
+
const result3 = await client.execute("SELECT COUNT(*) as cnt FROM hyperedges");
|
|
18527
|
+
hyperedgeCount = Number(result3.rows[0]?.cnt ?? 0);
|
|
18287
18528
|
} catch {
|
|
18288
18529
|
}
|
|
18289
18530
|
const nodeLabels = new Map(nodes.map((n) => [n.id, n.label]));
|
|
@@ -18326,10 +18567,10 @@ function registerExportGraph(server2) {
|
|
|
18326
18567
|
format: z44.enum(["html", "markdown"]).default("markdown").describe("Export format: markdown (inline) or html (file)")
|
|
18327
18568
|
}
|
|
18328
18569
|
},
|
|
18329
|
-
async ({ format }) => {
|
|
18570
|
+
async ({ format: format2 }) => {
|
|
18330
18571
|
try {
|
|
18331
18572
|
const client = getClient();
|
|
18332
|
-
if (
|
|
18573
|
+
if (format2 === "markdown") {
|
|
18333
18574
|
const report = await generateGraphReport(client);
|
|
18334
18575
|
return {
|
|
18335
18576
|
content: [{ type: "text", text: report }]
|
|
@@ -18337,12 +18578,12 @@ function registerExportGraph(server2) {
|
|
|
18337
18578
|
}
|
|
18338
18579
|
const html = await exportGraphHTML(client);
|
|
18339
18580
|
const fs = await import("fs");
|
|
18340
|
-
const
|
|
18581
|
+
const path55 = await import("path");
|
|
18341
18582
|
const os21 = await import("os");
|
|
18342
|
-
const outDir =
|
|
18583
|
+
const outDir = path55.join(os21.homedir(), ".exe-os", "exports");
|
|
18343
18584
|
fs.mkdirSync(outDir, { recursive: true });
|
|
18344
18585
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18345
|
-
const filePath =
|
|
18586
|
+
const filePath = path55.join(outDir, `graph-${timestamp}.html`);
|
|
18346
18587
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
18347
18588
|
return {
|
|
18348
18589
|
content: [
|
|
@@ -18711,14 +18952,14 @@ function registerMergeEntities(server2) {
|
|
|
18711
18952
|
};
|
|
18712
18953
|
}
|
|
18713
18954
|
try {
|
|
18714
|
-
const
|
|
18955
|
+
const result3 = await mergeEntities(client, sourceId, targetId);
|
|
18715
18956
|
return {
|
|
18716
18957
|
content: [
|
|
18717
18958
|
{
|
|
18718
18959
|
type: "text",
|
|
18719
18960
|
text: `Merged "${source_name}" \u2192 "${target_name}":
|
|
18720
|
-
- ${
|
|
18721
|
-
- ${
|
|
18961
|
+
- ${result3.relationshipsMoved} relationships moved
|
|
18962
|
+
- ${result3.memoriesMoved} memory links moved
|
|
18722
18963
|
- "${source_name}" registered as alias for "${target_name}"`
|
|
18723
18964
|
}
|
|
18724
18965
|
]
|
|
@@ -19001,10 +19242,10 @@ function registerSetAgentConfig(server2) {
|
|
|
19001
19242
|
isError: true
|
|
19002
19243
|
};
|
|
19003
19244
|
}
|
|
19004
|
-
const
|
|
19005
|
-
if (!
|
|
19245
|
+
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort);
|
|
19246
|
+
if (!result3.ok) {
|
|
19006
19247
|
return {
|
|
19007
|
-
content: [{ type: "text", text:
|
|
19248
|
+
content: [{ type: "text", text: result3.error }],
|
|
19008
19249
|
isError: true
|
|
19009
19250
|
};
|
|
19010
19251
|
}
|
|
@@ -19181,7 +19422,7 @@ async function getAgentSpend(period = "7d") {
|
|
|
19181
19422
|
}
|
|
19182
19423
|
}
|
|
19183
19424
|
}
|
|
19184
|
-
const
|
|
19425
|
+
const result3 = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
|
|
19185
19426
|
agentId,
|
|
19186
19427
|
inputTokens: t.input,
|
|
19187
19428
|
outputTokens: t.output,
|
|
@@ -19191,8 +19432,8 @@ async function getAgentSpend(period = "7d") {
|
|
|
19191
19432
|
sessions: t.sessions.size,
|
|
19192
19433
|
period
|
|
19193
19434
|
})).sort((a, b) => b.costUSD - a.costUSD);
|
|
19194
|
-
_spendCache.set(period, { result:
|
|
19195
|
-
return
|
|
19435
|
+
_spendCache.set(period, { result: result3, expires: Date.now() + CACHE_TTL_MS });
|
|
19436
|
+
return result3;
|
|
19196
19437
|
}
|
|
19197
19438
|
async function extractSessionUsage(jsonlPath) {
|
|
19198
19439
|
let input = 0;
|
|
@@ -19685,13 +19926,13 @@ function registerMcpPing(server2) {
|
|
|
19685
19926
|
const daemon = isDaemonAlive2();
|
|
19686
19927
|
const summary = summarizeMcpTransport();
|
|
19687
19928
|
writeMcpTransportSummary();
|
|
19688
|
-
const
|
|
19929
|
+
const status2 = daemon.alive ? "ok" : "degraded";
|
|
19689
19930
|
return {
|
|
19690
19931
|
content: [
|
|
19691
19932
|
{
|
|
19692
19933
|
type: "text",
|
|
19693
19934
|
text: JSON.stringify({
|
|
19694
|
-
status,
|
|
19935
|
+
status: status2,
|
|
19695
19936
|
daemon,
|
|
19696
19937
|
mcpTransport: summary,
|
|
19697
19938
|
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 +20190,14 @@ async function fetchMemoriesWithVectors(client, projectFilter, agentFilter2) {
|
|
|
19949
20190
|
args.push(agentFilter2);
|
|
19950
20191
|
}
|
|
19951
20192
|
const where = " WHERE " + clauses.join(" AND ");
|
|
19952
|
-
const
|
|
20193
|
+
const result3 = await client.execute({
|
|
19953
20194
|
sql: `SELECT id, agent_id, raw_text, timestamp, project_name, vector
|
|
19954
20195
|
FROM memories${where}
|
|
19955
20196
|
ORDER BY project_name, timestamp DESC`,
|
|
19956
20197
|
args
|
|
19957
20198
|
});
|
|
19958
20199
|
const byProject = /* @__PURE__ */ new Map();
|
|
19959
|
-
for (const row of
|
|
20200
|
+
for (const row of result3.rows) {
|
|
19960
20201
|
const project = row.project_name;
|
|
19961
20202
|
const vec = row.vector == null ? null : Array.isArray(row.vector) ? row.vector : Array.from(row.vector);
|
|
19962
20203
|
if (!vec || vec.length === 0) continue;
|
|
@@ -20373,11 +20614,11 @@ async function auditStats(client, flags) {
|
|
|
20373
20614
|
async function auditNullVectors(client, flags) {
|
|
20374
20615
|
const { clause, args } = agentFilter(flags);
|
|
20375
20616
|
const where = clause ? clause + " AND vector IS NULL" : " WHERE vector IS NULL";
|
|
20376
|
-
const
|
|
20617
|
+
const result3 = await client.execute({
|
|
20377
20618
|
sql: `SELECT COUNT(*) as cnt FROM memories${where}`,
|
|
20378
20619
|
args
|
|
20379
20620
|
});
|
|
20380
|
-
return Number(
|
|
20621
|
+
return Number(result3.rows[0].cnt);
|
|
20381
20622
|
}
|
|
20382
20623
|
async function auditDuplicates(client, flags) {
|
|
20383
20624
|
const { clause, args } = agentFilter(flags);
|
|
@@ -20426,14 +20667,14 @@ async function auditDuplicates(client, flags) {
|
|
|
20426
20667
|
async function auditBloated(client, flags) {
|
|
20427
20668
|
const { clause, args } = agentFilter(flags);
|
|
20428
20669
|
const where = clause ? clause + " AND LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'" : " WHERE LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'";
|
|
20429
|
-
const
|
|
20670
|
+
const result3 = await client.execute({
|
|
20430
20671
|
sql: `SELECT id, agent_id, LENGTH(raw_text) as size, tool_name
|
|
20431
20672
|
FROM memories${where}
|
|
20432
20673
|
ORDER BY size DESC
|
|
20433
20674
|
LIMIT 100`,
|
|
20434
20675
|
args
|
|
20435
20676
|
});
|
|
20436
|
-
return
|
|
20677
|
+
return result3.rows.map((r) => ({
|
|
20437
20678
|
id: r.id,
|
|
20438
20679
|
agent_id: r.agent_id,
|
|
20439
20680
|
size: Number(r.size),
|
|
@@ -20448,7 +20689,7 @@ async function auditFts(client) {
|
|
|
20448
20689
|
return { memoryCount: mc, ftsCount: fc, inSync: mc === fc };
|
|
20449
20690
|
}
|
|
20450
20691
|
async function auditOrphanedProjects(client) {
|
|
20451
|
-
const
|
|
20692
|
+
const result3 = await client.execute(
|
|
20452
20693
|
`SELECT project_name, COUNT(*) as cnt
|
|
20453
20694
|
FROM memories
|
|
20454
20695
|
WHERE tool_name != 'ConversationBackfill'
|
|
@@ -20457,7 +20698,7 @@ async function auditOrphanedProjects(client) {
|
|
|
20457
20698
|
);
|
|
20458
20699
|
const orphans = [];
|
|
20459
20700
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20460
|
-
for (const row of
|
|
20701
|
+
for (const row of result3.rows) {
|
|
20461
20702
|
const name = row.project_name;
|
|
20462
20703
|
const count = Number(row.cnt);
|
|
20463
20704
|
const exists = existsSync30(path35.join(home, name)) || existsSync30(path35.join(home, "..", name)) || existsSync30(path35.join(process.cwd(), "..", name));
|
|
@@ -20908,12 +21149,12 @@ async function fixBloated(client, bloated, dryRun) {
|
|
|
20908
21149
|
let chunksCreated = 0;
|
|
20909
21150
|
const CHUNK_SIZE = 2048;
|
|
20910
21151
|
for (const b of bloated) {
|
|
20911
|
-
const
|
|
21152
|
+
const result3 = await client.execute({
|
|
20912
21153
|
sql: `SELECT * FROM memories WHERE id = ?`,
|
|
20913
21154
|
args: [b.id]
|
|
20914
21155
|
});
|
|
20915
|
-
if (
|
|
20916
|
-
const row =
|
|
21156
|
+
if (result3.rows.length === 0) continue;
|
|
21157
|
+
const row = result3.rows[0];
|
|
20917
21158
|
const text3 = row.raw_text;
|
|
20918
21159
|
const chunks = splitAtSentences(text3, CHUNK_SIZE);
|
|
20919
21160
|
if (chunks.length <= 1) continue;
|
|
@@ -21147,15 +21388,15 @@ function registerRunConsolidation(server2) {
|
|
|
21147
21388
|
};
|
|
21148
21389
|
}
|
|
21149
21390
|
const config2 = await loadConfig();
|
|
21150
|
-
const
|
|
21391
|
+
const result3 = await runConsolidation(client, {
|
|
21151
21392
|
model: config2.consolidationModel || "claude-haiku-4-5-20251001",
|
|
21152
21393
|
maxCalls: 10
|
|
21153
21394
|
});
|
|
21154
21395
|
const lines = [];
|
|
21155
21396
|
lines.push("## Consolidation Complete\n");
|
|
21156
|
-
lines.push(`- **Clusters processed:** ${
|
|
21157
|
-
lines.push(`- **Memories consolidated:** ${
|
|
21158
|
-
lines.push(`- **Remaining unconsolidated:** ${(pending -
|
|
21397
|
+
lines.push(`- **Clusters processed:** ${result3.clustersProcessed}`);
|
|
21398
|
+
lines.push(`- **Memories consolidated:** ${result3.memoriesConsolidated}`);
|
|
21399
|
+
lines.push(`- **Remaining unconsolidated:** ${(pending - result3.memoriesConsolidated).toLocaleString()}`);
|
|
21159
21400
|
return {
|
|
21160
21401
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
21161
21402
|
};
|
|
@@ -21319,12 +21560,12 @@ function loadPgClient() {
|
|
|
21319
21560
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
21320
21561
|
return {
|
|
21321
21562
|
async $queryRawUnsafe(query, ...values) {
|
|
21322
|
-
const
|
|
21323
|
-
return
|
|
21563
|
+
const result3 = await pool.query(query, values);
|
|
21564
|
+
return result3.rows;
|
|
21324
21565
|
},
|
|
21325
21566
|
async $executeRawUnsafe(query, ...values) {
|
|
21326
|
-
const
|
|
21327
|
-
return
|
|
21567
|
+
const result3 = await pool.query(query, values);
|
|
21568
|
+
return result3.rowCount ?? 0;
|
|
21328
21569
|
},
|
|
21329
21570
|
async $disconnect() {
|
|
21330
21571
|
await pool.end();
|
|
@@ -21519,8 +21760,8 @@ var CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device
|
|
|
21519
21760
|
async function getCloudReuploadRequired(client = getClient()) {
|
|
21520
21761
|
try {
|
|
21521
21762
|
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
21522
|
-
const
|
|
21523
|
-
return
|
|
21763
|
+
const result3 = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
|
|
21764
|
+
return result3.rows.some((row) => String(row.value ?? "") === "1");
|
|
21524
21765
|
} catch {
|
|
21525
21766
|
return false;
|
|
21526
21767
|
}
|
|
@@ -22131,8 +22372,8 @@ async function cloudPullBlob(route, config2) {
|
|
|
22131
22372
|
}
|
|
22132
22373
|
async function cloudPushGlobalProcedures(config2) {
|
|
22133
22374
|
const client = getClient();
|
|
22134
|
-
const
|
|
22135
|
-
const rows =
|
|
22375
|
+
const result3 = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
|
|
22376
|
+
const rows = result3.rows;
|
|
22136
22377
|
const { ok } = await cloudPushBlob(
|
|
22137
22378
|
"/sync/push-global-procedures",
|
|
22138
22379
|
rows,
|
|
@@ -22176,8 +22417,8 @@ async function cloudPullGlobalProcedures(config2) {
|
|
|
22176
22417
|
}
|
|
22177
22418
|
async function cloudPushBehaviors(config2) {
|
|
22178
22419
|
const client = getClient();
|
|
22179
|
-
const
|
|
22180
|
-
const rows =
|
|
22420
|
+
const result3 = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
22421
|
+
const rows = result3.rows;
|
|
22181
22422
|
const { ok } = await cloudPushBlob(
|
|
22182
22423
|
"/sync/push-behaviors",
|
|
22183
22424
|
rows,
|
|
@@ -22332,8 +22573,8 @@ async function cloudPullGraphRAG(config2) {
|
|
|
22332
22573
|
}
|
|
22333
22574
|
async function cloudPushTasks(config2) {
|
|
22334
22575
|
const client = getClient();
|
|
22335
|
-
const
|
|
22336
|
-
const rows =
|
|
22576
|
+
const result3 = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
22577
|
+
const rows = result3.rows;
|
|
22337
22578
|
const { ok } = await cloudPushBlob(
|
|
22338
22579
|
"/sync/push-tasks",
|
|
22339
22580
|
rows,
|
|
@@ -22378,8 +22619,8 @@ async function cloudPullTasks(config2) {
|
|
|
22378
22619
|
}
|
|
22379
22620
|
async function cloudPushConversations(config2) {
|
|
22380
22621
|
const client = getClient();
|
|
22381
|
-
const
|
|
22382
|
-
const rows =
|
|
22622
|
+
const result3 = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
22623
|
+
const rows = result3.rows;
|
|
22383
22624
|
const { ok } = await cloudPushBlob(
|
|
22384
22625
|
"/sync/push-conversations",
|
|
22385
22626
|
rows,
|
|
@@ -23148,14 +23389,14 @@ function registerBackupVps(server2) {
|
|
|
23148
23389
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
23149
23390
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
23150
23391
|
};
|
|
23151
|
-
const
|
|
23392
|
+
const result3 = await createBackup2(options);
|
|
23152
23393
|
return {
|
|
23153
23394
|
content: [
|
|
23154
23395
|
{
|
|
23155
23396
|
type: "text",
|
|
23156
|
-
text: `Backup complete: ${
|
|
23157
|
-
${
|
|
23158
|
-
${
|
|
23397
|
+
text: `Backup complete: ${result3.key}
|
|
23398
|
+
${result3.sizeBytes} bytes
|
|
23399
|
+
${result3.timestamp}`
|
|
23159
23400
|
}
|
|
23160
23401
|
]
|
|
23161
23402
|
};
|
|
@@ -23188,18 +23429,18 @@ ${result2.timestamp}`
|
|
|
23188
23429
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
23189
23430
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
23190
23431
|
};
|
|
23191
|
-
const
|
|
23432
|
+
const result3 = await listBackups2(options);
|
|
23192
23433
|
return {
|
|
23193
23434
|
content: [
|
|
23194
23435
|
{
|
|
23195
23436
|
type: "text",
|
|
23196
|
-
text: formatBackups(
|
|
23437
|
+
text: formatBackups(result3)
|
|
23197
23438
|
}
|
|
23198
23439
|
]
|
|
23199
23440
|
};
|
|
23200
23441
|
}
|
|
23201
23442
|
case "health": {
|
|
23202
|
-
const
|
|
23443
|
+
const result3 = await backupHealth({
|
|
23203
23444
|
r2Bucket: input.r2Bucket,
|
|
23204
23445
|
r2Endpoint: input.r2Endpoint,
|
|
23205
23446
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
@@ -23210,10 +23451,10 @@ ${result2.timestamp}`
|
|
|
23210
23451
|
{
|
|
23211
23452
|
type: "text",
|
|
23212
23453
|
text: [
|
|
23213
|
-
`last_backup: ${
|
|
23214
|
-
`backup_count: ${
|
|
23215
|
-
`total_size_bytes: ${
|
|
23216
|
-
`oldest_backup: ${
|
|
23454
|
+
`last_backup: ${result3.lastBackup ?? "none"}`,
|
|
23455
|
+
`backup_count: ${result3.backupCount}`,
|
|
23456
|
+
`total_size_bytes: ${result3.totalSizeBytes}`,
|
|
23457
|
+
`oldest_backup: ${result3.oldestBackup ?? "none"}`
|
|
23217
23458
|
].join("\n")
|
|
23218
23459
|
}
|
|
23219
23460
|
]
|
|
@@ -23321,9 +23562,9 @@ var HostingerApiClient = class {
|
|
|
23321
23562
|
}
|
|
23322
23563
|
this.lastRequestTime = Date.now();
|
|
23323
23564
|
}
|
|
23324
|
-
async request(method,
|
|
23565
|
+
async request(method, path55, body) {
|
|
23325
23566
|
await this.rateLimit();
|
|
23326
|
-
const url = `${this.baseUrl}${
|
|
23567
|
+
const url = `${this.baseUrl}${path55}`;
|
|
23327
23568
|
const headers = {
|
|
23328
23569
|
Authorization: `Bearer ${this.apiKey}`,
|
|
23329
23570
|
"Content-Type": "application/json",
|
|
@@ -23353,10 +23594,10 @@ var HostingerApiClient = class {
|
|
|
23353
23594
|
var HostingerError = class extends Error {
|
|
23354
23595
|
status;
|
|
23355
23596
|
errorCode;
|
|
23356
|
-
constructor(
|
|
23597
|
+
constructor(status2, errorCode, message) {
|
|
23357
23598
|
super(message);
|
|
23358
23599
|
this.name = "HostingerError";
|
|
23359
|
-
this.status =
|
|
23600
|
+
this.status = status2;
|
|
23360
23601
|
this.errorCode = errorCode;
|
|
23361
23602
|
}
|
|
23362
23603
|
};
|
|
@@ -23396,8 +23637,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
23396
23637
|
}
|
|
23397
23638
|
return envelope.result;
|
|
23398
23639
|
}
|
|
23399
|
-
function buildUrl(zoneId,
|
|
23400
|
-
const normalizedPath =
|
|
23640
|
+
function buildUrl(zoneId, path55 = "/dns_records", query) {
|
|
23641
|
+
const normalizedPath = path55.startsWith("/") ? path55 : `/${path55}`;
|
|
23401
23642
|
const url = new URL(
|
|
23402
23643
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
23403
23644
|
);
|
|
@@ -23447,10 +23688,10 @@ function mapDnsRecord(record) {
|
|
|
23447
23688
|
var CloudflareError = class extends Error {
|
|
23448
23689
|
status;
|
|
23449
23690
|
errorCode;
|
|
23450
|
-
constructor(
|
|
23691
|
+
constructor(status2, errorCode, message) {
|
|
23451
23692
|
super(message);
|
|
23452
23693
|
this.name = "CloudflareError";
|
|
23453
|
-
this.status =
|
|
23694
|
+
this.status = status2;
|
|
23454
23695
|
this.errorCode = errorCode;
|
|
23455
23696
|
}
|
|
23456
23697
|
};
|
|
@@ -23503,7 +23744,7 @@ async function executeDeployment(params, client) {
|
|
|
23503
23744
|
process.stderr.write(`[deploy_client] Playbook complete. Verifying health...
|
|
23504
23745
|
`);
|
|
23505
23746
|
const healthy = await verifyHealth(domain);
|
|
23506
|
-
const
|
|
23747
|
+
const status2 = healthy ? DEPLOYED_STATUS : PLAYBOOK_COMPLETE_HEALTH_PENDING_STATUS;
|
|
23507
23748
|
process.stderr.write("[deploy_client] Recording VPS inventory...\n");
|
|
23508
23749
|
const inventory = buildInventoryRecord({
|
|
23509
23750
|
userId: user_id,
|
|
@@ -23524,7 +23765,7 @@ async function executeDeployment(params, client) {
|
|
|
23524
23765
|
plan: vps.plan,
|
|
23525
23766
|
ssh_port: vps.ssh_port,
|
|
23526
23767
|
ssh_access: `ssh exeai@${vps.ip_address}`,
|
|
23527
|
-
status,
|
|
23768
|
+
status: status2,
|
|
23528
23769
|
ansible_output: playbookResult,
|
|
23529
23770
|
dns_record_id: dnsRecordId,
|
|
23530
23771
|
inventory
|
|
@@ -23561,7 +23802,7 @@ function registerDeployClient(server2) {
|
|
|
23561
23802
|
};
|
|
23562
23803
|
}
|
|
23563
23804
|
try {
|
|
23564
|
-
const
|
|
23805
|
+
const result3 = await executeDeployment({
|
|
23565
23806
|
client_name,
|
|
23566
23807
|
domain,
|
|
23567
23808
|
region,
|
|
@@ -23576,7 +23817,7 @@ function registerDeployClient(server2) {
|
|
|
23576
23817
|
content: [
|
|
23577
23818
|
{
|
|
23578
23819
|
type: "text",
|
|
23579
|
-
text: JSON.stringify(
|
|
23820
|
+
text: JSON.stringify(result3, null, 2)
|
|
23580
23821
|
}
|
|
23581
23822
|
]
|
|
23582
23823
|
};
|
|
@@ -24309,12 +24550,12 @@ function registerListTriggers(server2) {
|
|
|
24309
24550
|
};
|
|
24310
24551
|
}
|
|
24311
24552
|
const lines = triggers.map((t) => {
|
|
24312
|
-
const
|
|
24553
|
+
const status2 = t.enabled ? "active" : "disabled";
|
|
24313
24554
|
const condCount = t.conditions.length;
|
|
24314
24555
|
const actionTypes = t.actions.map((a) => a.type).join(", ");
|
|
24315
24556
|
const schedulePart = isScheduledTrigger(t) ? ` | schedule: ${t.schedule}` : "";
|
|
24316
24557
|
const projectPart = t.project ? ` | project: ${t.project}` : "";
|
|
24317
|
-
return `- **${t.name}** (${t.id}) [${
|
|
24558
|
+
return `- **${t.name}** (${t.id}) [${status2}]
|
|
24318
24559
|
Event: ${t.event} | Conditions: ${condCount} | Actions: ${actionTypes}` + schedulePart + projectPart;
|
|
24319
24560
|
});
|
|
24320
24561
|
const text3 = `${triggers.length} trigger(s) configured:
|
|
@@ -24688,8 +24929,8 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
24688
24929
|
]
|
|
24689
24930
|
};
|
|
24690
24931
|
}
|
|
24691
|
-
const
|
|
24692
|
-
if (!
|
|
24932
|
+
const result3 = applyPack(industry, project);
|
|
24933
|
+
if (!result3) {
|
|
24693
24934
|
return {
|
|
24694
24935
|
content: [
|
|
24695
24936
|
{
|
|
@@ -24702,22 +24943,22 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
24702
24943
|
const lines = [
|
|
24703
24944
|
`Applied "${industry}" starter pack to project "${project}":
|
|
24704
24945
|
`,
|
|
24705
|
-
`**CRM Custom Objects** (${
|
|
24706
|
-
...
|
|
24946
|
+
`**CRM Custom Objects** (${result3.customObjects.count}):`,
|
|
24947
|
+
...result3.customObjects.names.map((n) => ` - ${n}`),
|
|
24707
24948
|
"",
|
|
24708
|
-
`**Triggers Created** (${
|
|
24709
|
-
...
|
|
24949
|
+
`**Triggers Created** (${result3.triggers.created}):`,
|
|
24950
|
+
...result3.triggers.ids.map((id, i) => {
|
|
24710
24951
|
const t = pack.triggers[i];
|
|
24711
24952
|
return ` - ${t?.name ?? "Unknown"} (${id})`;
|
|
24712
24953
|
}),
|
|
24713
24954
|
"",
|
|
24714
|
-
`**Wiki Seeds** (${
|
|
24715
|
-
...
|
|
24955
|
+
`**Wiki Seeds** (${result3.wikiSeeds.count}):`,
|
|
24956
|
+
...result3.wikiSeeds.titles.map((t) => ` - ${t}`)
|
|
24716
24957
|
];
|
|
24717
|
-
if (
|
|
24958
|
+
if (result3.pendingVariables.length > 0) {
|
|
24718
24959
|
lines.push("");
|
|
24719
24960
|
lines.push("**Requires manual configuration:**");
|
|
24720
|
-
for (const pv of
|
|
24961
|
+
for (const pv of result3.pendingVariables) {
|
|
24721
24962
|
const vars = Object.entries(pv.variables).map(([k, v]) => ` - {{${k}}}: ${v}`).join("\n");
|
|
24722
24963
|
lines.push(` ${pv.trigger}:
|
|
24723
24964
|
${vars}`);
|
|
@@ -25108,11 +25349,11 @@ async function importIdentities(identities, updatedBy) {
|
|
|
25108
25349
|
}
|
|
25109
25350
|
async function getActiveProcedureTitles() {
|
|
25110
25351
|
const client = getClient();
|
|
25111
|
-
const
|
|
25352
|
+
const result3 = await client.execute({
|
|
25112
25353
|
sql: "SELECT title FROM company_procedures WHERE active = 1",
|
|
25113
25354
|
args: []
|
|
25114
25355
|
});
|
|
25115
|
-
return new Set(
|
|
25356
|
+
return new Set(result3.rows.map((row) => String(row.title)));
|
|
25116
25357
|
}
|
|
25117
25358
|
async function exportOrchestration(createdBy) {
|
|
25118
25359
|
const client = getClient();
|
|
@@ -25302,11 +25543,11 @@ function registerImportOrchestration(server2) {
|
|
|
25302
25543
|
await initStore();
|
|
25303
25544
|
const raw = readFileSync30(package_path, "utf-8");
|
|
25304
25545
|
const pkg = validatePackage(JSON.parse(raw));
|
|
25305
|
-
const
|
|
25546
|
+
const result3 = await importOrchestration(pkg, merge_strategy);
|
|
25306
25547
|
return {
|
|
25307
25548
|
content: [{
|
|
25308
25549
|
type: "text",
|
|
25309
|
-
text: `Imported ${
|
|
25550
|
+
text: `Imported ${result3.imported.roster} roster entries, ${result3.imported.identities} identities, ${result3.imported.behaviors} behaviors, ${result3.imported.procedures} procedures using ${merge_strategy} strategy`
|
|
25310
25551
|
}]
|
|
25311
25552
|
};
|
|
25312
25553
|
} catch (err) {
|
|
@@ -25417,11 +25658,11 @@ Domain: ${domain ?? "none"}`
|
|
|
25417
25658
|
};
|
|
25418
25659
|
}
|
|
25419
25660
|
const client = getClient();
|
|
25420
|
-
const
|
|
25661
|
+
const result3 = await client.execute({
|
|
25421
25662
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
25422
25663
|
args: [procedure_id]
|
|
25423
25664
|
});
|
|
25424
|
-
if (
|
|
25665
|
+
if (result3.rows.length === 0) {
|
|
25425
25666
|
return {
|
|
25426
25667
|
content: [{
|
|
25427
25668
|
type: "text",
|
|
@@ -25430,7 +25671,7 @@ Domain: ${domain ?? "none"}`
|
|
|
25430
25671
|
isError: true
|
|
25431
25672
|
};
|
|
25432
25673
|
}
|
|
25433
|
-
const row =
|
|
25674
|
+
const row = result3.rows[0];
|
|
25434
25675
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
25435
25676
|
if (!wasActive) {
|
|
25436
25677
|
return {
|
|
@@ -25981,7 +26222,7 @@ function registerBehavior(server2) {
|
|
|
25981
26222
|
args.push(proj);
|
|
25982
26223
|
}
|
|
25983
26224
|
const where = conditions.join(" AND ");
|
|
25984
|
-
const
|
|
26225
|
+
const result4 = await client2.execute({
|
|
25985
26226
|
sql: `SELECT id, agent_id, project_name, domain, content, active, created_at, updated_at
|
|
25986
26227
|
FROM behaviors
|
|
25987
26228
|
WHERE ${where}
|
|
@@ -25989,7 +26230,7 @@ function registerBehavior(server2) {
|
|
|
25989
26230
|
LIMIT 50`,
|
|
25990
26231
|
args
|
|
25991
26232
|
});
|
|
25992
|
-
const behaviors =
|
|
26233
|
+
const behaviors = result4.rows.map((r) => rowToBehavior2(r));
|
|
25993
26234
|
if (behaviors.length === 0) {
|
|
25994
26235
|
return {
|
|
25995
26236
|
content: [{ type: "text", text: "No behaviors found." }]
|
|
@@ -26088,11 +26329,11 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
26088
26329
|
};
|
|
26089
26330
|
}
|
|
26090
26331
|
const client = getClient();
|
|
26091
|
-
const
|
|
26332
|
+
const result3 = await client.execute({
|
|
26092
26333
|
sql: "SELECT id, agent_id, content, domain, priority FROM behaviors WHERE id = ?",
|
|
26093
26334
|
args: [behavior_id]
|
|
26094
26335
|
});
|
|
26095
|
-
if (
|
|
26336
|
+
if (result3.rows.length === 0) {
|
|
26096
26337
|
return {
|
|
26097
26338
|
content: [{
|
|
26098
26339
|
type: "text",
|
|
@@ -26101,7 +26342,7 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
26101
26342
|
isError: true
|
|
26102
26343
|
};
|
|
26103
26344
|
}
|
|
26104
|
-
const row =
|
|
26345
|
+
const row = result3.rows[0];
|
|
26105
26346
|
const wasActive = await deactivateBehavior(behavior_id);
|
|
26106
26347
|
if (!wasActive) {
|
|
26107
26348
|
return {
|
|
@@ -26307,11 +26548,11 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
26307
26548
|
};
|
|
26308
26549
|
}
|
|
26309
26550
|
const client = getClient();
|
|
26310
|
-
const
|
|
26551
|
+
const result3 = await client.execute({
|
|
26311
26552
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
26312
26553
|
args: [procedure_id]
|
|
26313
26554
|
});
|
|
26314
|
-
if (
|
|
26555
|
+
if (result3.rows.length === 0) {
|
|
26315
26556
|
return {
|
|
26316
26557
|
content: [{
|
|
26317
26558
|
type: "text",
|
|
@@ -26320,7 +26561,7 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
26320
26561
|
isError: true
|
|
26321
26562
|
};
|
|
26322
26563
|
}
|
|
26323
|
-
const row =
|
|
26564
|
+
const row = result3.rows[0];
|
|
26324
26565
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
26325
26566
|
if (!wasActive) {
|
|
26326
26567
|
return {
|
|
@@ -26449,14 +26690,14 @@ function registerGetDecision(server2) {
|
|
|
26449
26690
|
},
|
|
26450
26691
|
async ({ domain }) => {
|
|
26451
26692
|
const client = getClient();
|
|
26452
|
-
const
|
|
26693
|
+
const result3 = await client.execute({
|
|
26453
26694
|
sql: `SELECT id, agent_id, timestamp, raw_text, status, supersedes_id, project_name
|
|
26454
26695
|
FROM memories
|
|
26455
26696
|
WHERE memory_type = 'decision' AND source_path = ?
|
|
26456
26697
|
ORDER BY timestamp DESC`,
|
|
26457
26698
|
args: [domain]
|
|
26458
26699
|
});
|
|
26459
|
-
if (
|
|
26700
|
+
if (result3.rows.length === 0) {
|
|
26460
26701
|
return {
|
|
26461
26702
|
content: [
|
|
26462
26703
|
{
|
|
@@ -26466,7 +26707,7 @@ function registerGetDecision(server2) {
|
|
|
26466
26707
|
]
|
|
26467
26708
|
};
|
|
26468
26709
|
}
|
|
26469
|
-
const decisions =
|
|
26710
|
+
const decisions = result3.rows.map((r) => ({
|
|
26470
26711
|
id: String(r.id),
|
|
26471
26712
|
agent_id: String(r.agent_id),
|
|
26472
26713
|
timestamp: String(r.timestamp),
|
|
@@ -26823,23 +27064,89 @@ function registerRawData(server2) {
|
|
|
26823
27064
|
});
|
|
26824
27065
|
}
|
|
26825
27066
|
|
|
27067
|
+
// src/mcp/tools/company-actions.ts
|
|
27068
|
+
init_config();
|
|
27069
|
+
import { z as z87 } from "zod";
|
|
27070
|
+
var FETCH_TIMEOUT_MS5 = 15e3;
|
|
27071
|
+
var ACTION = z87.enum(["list_connectors", "describe_connector", "preview", "execute", "list_runs", "get_run"]);
|
|
27072
|
+
function format(payload) {
|
|
27073
|
+
return ["# Company Actions", "", JSON.stringify(payload, null, 2)].join("\n");
|
|
27074
|
+
}
|
|
27075
|
+
function registerCompanyActions(server2) {
|
|
27076
|
+
server2.registerTool(
|
|
27077
|
+
"company_actions",
|
|
27078
|
+
{
|
|
27079
|
+
title: "Company Actions",
|
|
27080
|
+
description: "Preview and execute external connector actions (Asana, WhatsApp) through exe-gateway with audit/safety gates.",
|
|
27081
|
+
inputSchema: {
|
|
27082
|
+
action: ACTION.describe("list_connectors, describe_connector, preview, execute, list_runs, get_run"),
|
|
27083
|
+
connector: z87.string().optional().describe("Connector to use, e.g. asana, whatsapp, airtable, notion"),
|
|
27084
|
+
connector_ref: z87.string().optional().describe("Exact connector ref from list_connectors when connector names overlap"),
|
|
27085
|
+
operation: z87.string().optional().describe("Connector operation, e.g. create_task or send_message"),
|
|
27086
|
+
payload: z87.record(z87.string(), z87.unknown()).optional().describe("Operation payload"),
|
|
27087
|
+
idempotency_key: z87.string().optional().describe("Stable key to prevent duplicate execution"),
|
|
27088
|
+
confirm_token: z87.string().optional().describe("Optional future approval token"),
|
|
27089
|
+
run_id: z87.string().optional().describe("Run id for get_run")
|
|
27090
|
+
}
|
|
27091
|
+
},
|
|
27092
|
+
async (input) => {
|
|
27093
|
+
const config2 = await loadConfig();
|
|
27094
|
+
const gatewayUrl = config2.gateway?.url || process.env.EXE_GATEWAY_URL;
|
|
27095
|
+
const authToken = config2.gateway?.authToken || process.env.EXE_GATEWAY_QUERY_AUTH_TOKEN || process.env.EXE_COMPANY_BRAIN_AUTH_TOKEN || process.env.EXE_GATEWAY_AUTH_TOKEN;
|
|
27096
|
+
if (!gatewayUrl) {
|
|
27097
|
+
return { content: [{ type: "text", text: "Company Actions not configured. Set gateway.url in ~/.exe-os/config.json" }] };
|
|
27098
|
+
}
|
|
27099
|
+
try {
|
|
27100
|
+
assertSecureGatewayUrlForToken(gatewayUrl, authToken);
|
|
27101
|
+
} catch (err) {
|
|
27102
|
+
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
27103
|
+
}
|
|
27104
|
+
let url;
|
|
27105
|
+
try {
|
|
27106
|
+
url = new URL("/actions", gatewayUrl);
|
|
27107
|
+
} catch (err) {
|
|
27108
|
+
return { content: [{ type: "text", text: `Company Actions not configured. Invalid gateway.url: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
27109
|
+
}
|
|
27110
|
+
try {
|
|
27111
|
+
const response = await fetch(url.toString(), {
|
|
27112
|
+
method: "POST",
|
|
27113
|
+
headers: {
|
|
27114
|
+
"Content-Type": "application/json",
|
|
27115
|
+
...authToken ? { Authorization: `Bearer ${authToken}` } : {}
|
|
27116
|
+
},
|
|
27117
|
+
body: JSON.stringify(input),
|
|
27118
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS5)
|
|
27119
|
+
});
|
|
27120
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
27121
|
+
const payload = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
27122
|
+
return {
|
|
27123
|
+
content: [{ type: "text", text: response.ok ? format(payload) : `Company Actions failed (${response.status}): ${typeof payload === "string" ? payload : JSON.stringify(payload)}` }],
|
|
27124
|
+
...response.ok ? {} : { isError: true }
|
|
27125
|
+
};
|
|
27126
|
+
} catch {
|
|
27127
|
+
return { content: [{ type: "text", text: `Gateway unreachable at ${url.origin}` }], isError: true };
|
|
27128
|
+
}
|
|
27129
|
+
}
|
|
27130
|
+
);
|
|
27131
|
+
}
|
|
27132
|
+
|
|
26826
27133
|
// src/mcp/tools/create-bug-report.ts
|
|
26827
27134
|
init_embedder();
|
|
26828
27135
|
init_active_agent();
|
|
26829
27136
|
init_config();
|
|
26830
27137
|
init_license();
|
|
26831
27138
|
init_store();
|
|
26832
|
-
import { z as
|
|
27139
|
+
import { z as z88 } from "zod";
|
|
26833
27140
|
import crypto18 from "crypto";
|
|
26834
27141
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
|
|
26835
27142
|
import path49 from "path";
|
|
26836
|
-
var CLASSIFICATION =
|
|
27143
|
+
var CLASSIFICATION = z88.enum([
|
|
26837
27144
|
"upstream_bug",
|
|
26838
27145
|
"customer_customization",
|
|
26839
27146
|
"emergency_hotfix",
|
|
26840
27147
|
"unclear"
|
|
26841
27148
|
]);
|
|
26842
|
-
var SEVERITY =
|
|
27149
|
+
var SEVERITY = z88.enum(["p0", "p1", "p2", "p3"]);
|
|
26843
27150
|
function slugify2(input) {
|
|
26844
27151
|
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "bug-report";
|
|
26845
27152
|
}
|
|
@@ -26919,22 +27226,22 @@ function registerCreateBugReport(server2) {
|
|
|
26919
27226
|
title: "Create Bug Report",
|
|
26920
27227
|
description: "Classify and file an exe-os issue as upstream_bug, customer_customization, emergency_hotfix, or unclear. Writes a local report, stores memory, and optionally sends to AskExe support when a support endpoint is configured.",
|
|
26921
27228
|
inputSchema: {
|
|
26922
|
-
title:
|
|
27229
|
+
title: z88.string().min(3).describe("Short descriptive title"),
|
|
26923
27230
|
classification: CLASSIFICATION.describe(
|
|
26924
27231
|
"upstream_bug = platform defect; customer_customization = local preference; emergency_hotfix = temporary local patch; unclear = needs maintainer triage"
|
|
26925
27232
|
),
|
|
26926
27233
|
severity: SEVERITY.default("p2").describe("p0 critical \u2192 p3 low"),
|
|
26927
|
-
summary:
|
|
26928
|
-
customer_impact:
|
|
26929
|
-
reproduction_steps:
|
|
26930
|
-
expected:
|
|
26931
|
-
actual:
|
|
26932
|
-
files_changed:
|
|
26933
|
-
workaround:
|
|
26934
|
-
local_patch_diff:
|
|
26935
|
-
package_version:
|
|
26936
|
-
project_name:
|
|
26937
|
-
send_upstream:
|
|
27234
|
+
summary: z88.string().min(10).describe("What happened and why it matters"),
|
|
27235
|
+
customer_impact: z88.string().optional().describe("How this affects the customer/founder"),
|
|
27236
|
+
reproduction_steps: z88.array(z88.string()).optional().describe("Steps to reproduce"),
|
|
27237
|
+
expected: z88.string().optional().describe("Expected behavior"),
|
|
27238
|
+
actual: z88.string().optional().describe("Actual behavior"),
|
|
27239
|
+
files_changed: z88.array(z88.string()).optional().describe("Files changed or suspected"),
|
|
27240
|
+
workaround: z88.string().optional().describe("Temporary local workaround/hotfix, if any"),
|
|
27241
|
+
local_patch_diff: z88.string().optional().describe("Small local diff or patch summary"),
|
|
27242
|
+
package_version: z88.string().optional().describe("Installed @askexenow/exe-os version"),
|
|
27243
|
+
project_name: z88.string().optional().describe("Project/customer context"),
|
|
27244
|
+
send_upstream: z88.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
|
|
26938
27245
|
}
|
|
26939
27246
|
},
|
|
26940
27247
|
async ({
|
|
@@ -27043,7 +27350,7 @@ Upstream status: ${upstreamStatus}`
|
|
|
27043
27350
|
}
|
|
27044
27351
|
|
|
27045
27352
|
// src/mcp/tools/support.ts
|
|
27046
|
-
import { z as
|
|
27353
|
+
import { z as z89 } from "zod";
|
|
27047
27354
|
|
|
27048
27355
|
// src/bin/exe-support.ts
|
|
27049
27356
|
init_config();
|
|
@@ -27254,14 +27561,14 @@ async function safeJson2(res) {
|
|
|
27254
27561
|
function hasFailures(rows) {
|
|
27255
27562
|
return rows.some((row) => row.level === "fail");
|
|
27256
27563
|
}
|
|
27257
|
-
function summarizeHttpFailure(
|
|
27564
|
+
function summarizeHttpFailure(status2, data) {
|
|
27258
27565
|
const error = data?.error;
|
|
27259
|
-
return `${
|
|
27566
|
+
return `${status2} ${error?.message ?? JSON.stringify(data)}`;
|
|
27260
27567
|
}
|
|
27261
|
-
function nextForPostFailure(
|
|
27262
|
-
if (
|
|
27263
|
-
if (
|
|
27264
|
-
if (
|
|
27568
|
+
function nextForPostFailure(status2) {
|
|
27569
|
+
if (status2 === 401) return "License credentials were not sent. Run `exe-os support health` and check license_key_present.";
|
|
27570
|
+
if (status2 === 403) return "License is valid locally but not accepted by support intake. AskExe must check server-side license provisioning.";
|
|
27571
|
+
if (status2 >= 500) return "AskExe support intake is unhealthy server-side. Try again shortly and report the local file path.";
|
|
27265
27572
|
return "Run `exe-os support health`; if it passes, send this output to AskExe.";
|
|
27266
27573
|
}
|
|
27267
27574
|
|
|
@@ -27306,7 +27613,7 @@ function registerSupportTools(server2) {
|
|
|
27306
27613
|
title: "Support Test",
|
|
27307
27614
|
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.",
|
|
27308
27615
|
inputSchema: {
|
|
27309
|
-
project:
|
|
27616
|
+
project: z89.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
|
|
27310
27617
|
}
|
|
27311
27618
|
},
|
|
27312
27619
|
async ({ project }) => {
|
|
@@ -27320,10 +27627,629 @@ function registerSupportTools(server2) {
|
|
|
27320
27627
|
);
|
|
27321
27628
|
}
|
|
27322
27629
|
|
|
27630
|
+
// src/mcp/tools/cli-parity.ts
|
|
27631
|
+
import { execFile as execFile2 } from "child_process";
|
|
27632
|
+
import { promisify as promisify2 } from "util";
|
|
27633
|
+
import { z as z90 } from "zod";
|
|
27634
|
+
|
|
27635
|
+
// src/bin/exe-status.ts
|
|
27636
|
+
init_employees();
|
|
27637
|
+
init_fast_db_init();
|
|
27638
|
+
init_database();
|
|
27639
|
+
init_tmux_status();
|
|
27640
|
+
init_task_scope();
|
|
27641
|
+
async function status(targetEmployee) {
|
|
27642
|
+
if (!inTmux()) {
|
|
27643
|
+
return "Not in a tmux session. Start tmux first: tmux new-session -s work";
|
|
27644
|
+
}
|
|
27645
|
+
const employees = await loadEmployees();
|
|
27646
|
+
const names = employees.filter((e) => !isCoordinatorRole(e.role)).map((e) => e.name);
|
|
27647
|
+
const roles = new Map(employees.map((e) => [e.name, e.role ?? ""]));
|
|
27648
|
+
await fastDbInit();
|
|
27649
|
+
const client = getClient();
|
|
27650
|
+
const taskMap = /* @__PURE__ */ new Map();
|
|
27651
|
+
try {
|
|
27652
|
+
const esScope = sessionScopeFilter();
|
|
27653
|
+
const result3 = await client.execute({
|
|
27654
|
+
sql: `SELECT assigned_to, title, priority FROM tasks
|
|
27655
|
+
WHERE status = 'in_progress'${esScope.sql}
|
|
27656
|
+
ORDER BY priority ASC, created_at ASC`,
|
|
27657
|
+
args: [...esScope.args]
|
|
27658
|
+
});
|
|
27659
|
+
for (const row of result3.rows) {
|
|
27660
|
+
const assignee = String(row.assigned_to);
|
|
27661
|
+
if (!taskMap.has(assignee)) {
|
|
27662
|
+
taskMap.set(assignee, {
|
|
27663
|
+
title: String(row.title),
|
|
27664
|
+
priority: String(row.priority)
|
|
27665
|
+
});
|
|
27666
|
+
}
|
|
27667
|
+
}
|
|
27668
|
+
} catch {
|
|
27669
|
+
}
|
|
27670
|
+
if (targetEmployee) {
|
|
27671
|
+
if (!names.includes(targetEmployee)) {
|
|
27672
|
+
return `Employee "${targetEmployee}" not found. Available: ${names.join(", ")}`;
|
|
27673
|
+
}
|
|
27674
|
+
const statuses2 = getEmployeeStatuses([targetEmployee]);
|
|
27675
|
+
const s = statuses2[0];
|
|
27676
|
+
return formatStatusDeep(s, taskMap.get(targetEmployee), roles.get(targetEmployee) ?? "");
|
|
27677
|
+
}
|
|
27678
|
+
const statuses = getEmployeeStatuses(names);
|
|
27679
|
+
return formatStatusAll(statuses, taskMap, roles);
|
|
27680
|
+
}
|
|
27681
|
+
if (isMainModule(import.meta.url)) {
|
|
27682
|
+
const target = process.argv[2];
|
|
27683
|
+
status(target).then((output) => {
|
|
27684
|
+
process.stdout.write(output + "\n");
|
|
27685
|
+
}).catch((err) => {
|
|
27686
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
27687
|
+
`);
|
|
27688
|
+
process.exit(1);
|
|
27689
|
+
});
|
|
27690
|
+
}
|
|
27691
|
+
|
|
27692
|
+
// src/bin/exe-healthcheck.ts
|
|
27693
|
+
import { existsSync as existsSync39, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "fs";
|
|
27694
|
+
import path51 from "path";
|
|
27695
|
+
import { execSync as execSync13 } from "child_process";
|
|
27696
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
27697
|
+
init_config();
|
|
27698
|
+
function findPackageRoot2() {
|
|
27699
|
+
let dir = path51.dirname(fileURLToPath6(import.meta.url));
|
|
27700
|
+
const { root } = path51.parse(dir);
|
|
27701
|
+
while (dir !== root) {
|
|
27702
|
+
if (existsSync39(path51.join(dir, "package.json"))) return dir;
|
|
27703
|
+
dir = path51.dirname(dir);
|
|
27704
|
+
}
|
|
27705
|
+
throw new Error("Cannot find package root");
|
|
27706
|
+
}
|
|
27707
|
+
function checkBuildIntegrity(pkgRoot) {
|
|
27708
|
+
const results = [];
|
|
27709
|
+
const tsupConfig = path51.join(pkgRoot, "tsup.config.ts");
|
|
27710
|
+
if (!existsSync39(tsupConfig)) {
|
|
27711
|
+
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
27712
|
+
}
|
|
27713
|
+
const configContent = readFileSync33(tsupConfig, "utf-8");
|
|
27714
|
+
const entryMatches = configContent.matchAll(/"([^"]+)":\s*"src\//g);
|
|
27715
|
+
const missing = [];
|
|
27716
|
+
let total = 0;
|
|
27717
|
+
for (const match of entryMatches) {
|
|
27718
|
+
const outputKey = match[1];
|
|
27719
|
+
const expectedPath = path51.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
27720
|
+
total++;
|
|
27721
|
+
if (!existsSync39(expectedPath)) {
|
|
27722
|
+
missing.push(`dist/${outputKey}.js`);
|
|
27723
|
+
}
|
|
27724
|
+
}
|
|
27725
|
+
if (missing.length > 0) {
|
|
27726
|
+
results.push({
|
|
27727
|
+
name: "build/entry-points",
|
|
27728
|
+
pass: false,
|
|
27729
|
+
detail: `${missing.length}/${total} entry points missing:
|
|
27730
|
+
${missing.join("\n ")}`
|
|
27731
|
+
});
|
|
27732
|
+
} else {
|
|
27733
|
+
results.push({
|
|
27734
|
+
name: "build/entry-points",
|
|
27735
|
+
pass: true,
|
|
27736
|
+
detail: `${total} entry points verified`
|
|
27737
|
+
});
|
|
27738
|
+
}
|
|
27739
|
+
return results;
|
|
27740
|
+
}
|
|
27741
|
+
function checkEmbedPipeline(pkgRoot) {
|
|
27742
|
+
const results = [];
|
|
27743
|
+
const daemonPath = path51.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
27744
|
+
if (!existsSync39(daemonPath)) {
|
|
27745
|
+
results.push({
|
|
27746
|
+
name: "exed/daemon-exists",
|
|
27747
|
+
pass: false,
|
|
27748
|
+
detail: `exe-daemon.js not found at ${daemonPath}`
|
|
27749
|
+
});
|
|
27750
|
+
return results;
|
|
27751
|
+
}
|
|
27752
|
+
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
27753
|
+
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
27754
|
+
for (const dir of entryDirs) {
|
|
27755
|
+
const fullDir = path51.join(pkgRoot, dir);
|
|
27756
|
+
if (!existsSync39(fullDir)) continue;
|
|
27757
|
+
let walkDir = fullDir;
|
|
27758
|
+
const { root } = path51.parse(walkDir);
|
|
27759
|
+
let foundRoot = null;
|
|
27760
|
+
while (walkDir !== root) {
|
|
27761
|
+
if (existsSync39(path51.join(walkDir, "package.json"))) {
|
|
27762
|
+
foundRoot = walkDir;
|
|
27763
|
+
break;
|
|
27764
|
+
}
|
|
27765
|
+
walkDir = path51.dirname(walkDir);
|
|
27766
|
+
}
|
|
27767
|
+
if (!foundRoot) {
|
|
27768
|
+
results.push({
|
|
27769
|
+
name: `exed/reachable-from-${dir}`,
|
|
27770
|
+
pass: false,
|
|
27771
|
+
detail: `Cannot find package root from ${dir}`
|
|
27772
|
+
});
|
|
27773
|
+
continue;
|
|
27774
|
+
}
|
|
27775
|
+
const resolvedDaemon = path51.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
27776
|
+
const reachable = existsSync39(resolvedDaemon);
|
|
27777
|
+
results.push({
|
|
27778
|
+
name: `exed/reachable-from-${dir}`,
|
|
27779
|
+
pass: reachable,
|
|
27780
|
+
detail: reachable ? `Resolves to ${resolvedDaemon}` : `NOT FOUND: ${resolvedDaemon}`
|
|
27781
|
+
});
|
|
27782
|
+
}
|
|
27783
|
+
return results;
|
|
27784
|
+
}
|
|
27785
|
+
function checkTaskSystem(pkgRoot) {
|
|
27786
|
+
const results = [];
|
|
27787
|
+
const scannerPath = path51.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
27788
|
+
if (!existsSync39(scannerPath)) {
|
|
27789
|
+
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
27790
|
+
return results;
|
|
27791
|
+
}
|
|
27792
|
+
try {
|
|
27793
|
+
execSync13(`node "${scannerPath}" /tmp/nonexistent-healthcheck-test --format=json 2>/dev/null`, {
|
|
27794
|
+
timeout: 1e4,
|
|
27795
|
+
encoding: "utf-8"
|
|
27796
|
+
});
|
|
27797
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs without import errors" });
|
|
27798
|
+
} catch (err) {
|
|
27799
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27800
|
+
if (msg.includes("Cannot find module") || msg.includes("ERR_MODULE_NOT_FOUND") || msg.includes("SyntaxError")) {
|
|
27801
|
+
results.push({ name: "tasks/scanner", pass: false, detail: `Import error: ${msg.slice(0, 200)}` });
|
|
27802
|
+
} else {
|
|
27803
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs (no import errors)" });
|
|
27804
|
+
}
|
|
27805
|
+
}
|
|
27806
|
+
return results;
|
|
27807
|
+
}
|
|
27808
|
+
function checkWorkerSpawning(pkgRoot) {
|
|
27809
|
+
const results = [];
|
|
27810
|
+
const workerPath = path51.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
27811
|
+
if (!existsSync39(workerPath)) {
|
|
27812
|
+
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
27813
|
+
return results;
|
|
27814
|
+
}
|
|
27815
|
+
try {
|
|
27816
|
+
execSync13(`node --check "${workerPath}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27817
|
+
results.push({ name: "workers/ingest-worker", pass: true, detail: "ingest-worker.js parses OK" });
|
|
27818
|
+
} catch (err) {
|
|
27819
|
+
results.push({
|
|
27820
|
+
name: "workers/ingest-worker",
|
|
27821
|
+
pass: false,
|
|
27822
|
+
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
27823
|
+
});
|
|
27824
|
+
}
|
|
27825
|
+
const hooksDir = path51.join(pkgRoot, "dist", "hooks");
|
|
27826
|
+
if (existsSync39(hooksDir)) {
|
|
27827
|
+
const hookFiles = readdirSync14(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
27828
|
+
let hooksPassed = 0;
|
|
27829
|
+
const hooksFailed = [];
|
|
27830
|
+
for (const hook of hookFiles) {
|
|
27831
|
+
try {
|
|
27832
|
+
execSync13(`node --check "${path51.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27833
|
+
hooksPassed++;
|
|
27834
|
+
} catch {
|
|
27835
|
+
hooksFailed.push(hook);
|
|
27836
|
+
}
|
|
27837
|
+
}
|
|
27838
|
+
if (hooksFailed.length > 0) {
|
|
27839
|
+
results.push({
|
|
27840
|
+
name: "workers/hooks-parse",
|
|
27841
|
+
pass: false,
|
|
27842
|
+
detail: `${hooksFailed.length}/${hookFiles.length} hooks fail to parse: ${hooksFailed.join(", ")}`
|
|
27843
|
+
});
|
|
27844
|
+
} else {
|
|
27845
|
+
results.push({
|
|
27846
|
+
name: "workers/hooks-parse",
|
|
27847
|
+
pass: true,
|
|
27848
|
+
detail: `${hooksPassed} hook entry points parse OK`
|
|
27849
|
+
});
|
|
27850
|
+
}
|
|
27851
|
+
}
|
|
27852
|
+
return results;
|
|
27853
|
+
}
|
|
27854
|
+
function checkMcpTransport() {
|
|
27855
|
+
const results = [];
|
|
27856
|
+
const pidPath = path51.join(EXE_AI_DIR, "exed.pid");
|
|
27857
|
+
const tokenPath = path51.join(EXE_AI_DIR, "exed.token");
|
|
27858
|
+
let daemonAlive = false;
|
|
27859
|
+
if (existsSync39(pidPath)) {
|
|
27860
|
+
try {
|
|
27861
|
+
const pid = parseInt(readFileSync33(pidPath, "utf8").trim(), 10);
|
|
27862
|
+
process.kill(pid, 0);
|
|
27863
|
+
daemonAlive = true;
|
|
27864
|
+
results.push({ name: "mcp/daemon-process", pass: true, detail: `exed process alive (pid ${pid})` });
|
|
27865
|
+
} catch {
|
|
27866
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "exed.pid exists but process is not alive; restart exe-os/exed" });
|
|
27867
|
+
}
|
|
27868
|
+
} else {
|
|
27869
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "No exed.pid found; daemon is not running" });
|
|
27870
|
+
}
|
|
27871
|
+
if (daemonAlive && existsSync39(tokenPath)) {
|
|
27872
|
+
try {
|
|
27873
|
+
const token = readFileSync33(tokenPath, "utf8").trim();
|
|
27874
|
+
const response = execSync13(
|
|
27875
|
+
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
27876
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
27877
|
+
);
|
|
27878
|
+
const jsonRpcError = response.includes("Content-Type: application/json") && response.includes("missing MCP session");
|
|
27879
|
+
results.push({
|
|
27880
|
+
name: "mcp/http-endpoint",
|
|
27881
|
+
pass: jsonRpcError,
|
|
27882
|
+
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"
|
|
27883
|
+
});
|
|
27884
|
+
} catch (err) {
|
|
27885
|
+
results.push({
|
|
27886
|
+
name: "mcp/http-endpoint",
|
|
27887
|
+
pass: false,
|
|
27888
|
+
detail: `MCP HTTP endpoint not reachable: ${err instanceof Error ? err.message.slice(0, 180) : String(err)}`
|
|
27889
|
+
});
|
|
27890
|
+
}
|
|
27891
|
+
} else if (daemonAlive) {
|
|
27892
|
+
results.push({ name: "mcp/http-endpoint", pass: false, detail: "Daemon is alive but exed.token is missing; cannot probe MCP HTTP" });
|
|
27893
|
+
}
|
|
27894
|
+
const events = readMcpHttpEvents(200);
|
|
27895
|
+
const summary = summarizeMcpTransport(events);
|
|
27896
|
+
results.push({
|
|
27897
|
+
name: "mcp/local-event-log",
|
|
27898
|
+
pass: true,
|
|
27899
|
+
detail: `${events.length} recent event(s); activeSessions=${summary.activeSessions ?? "unknown"}; lastHandshake=${summary.lastSuccessfulHandshake ?? "none"}; lastTool=${summary.lastSuccessfulToolCall ?? "none"}`
|
|
27900
|
+
});
|
|
27901
|
+
results.push({
|
|
27902
|
+
name: "mcp/monitor-summary",
|
|
27903
|
+
pass: true,
|
|
27904
|
+
detail: `Privacy-safe local monitor summary: ${path51.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
27905
|
+
});
|
|
27906
|
+
return results;
|
|
27907
|
+
}
|
|
27908
|
+
function checkClaudeCodeInstall() {
|
|
27909
|
+
const results = [];
|
|
27910
|
+
const execPath = process.env.CLAUDE_CODE_EXECPATH ?? "";
|
|
27911
|
+
if (execPath.length > 0 && (execPath.includes("claude/versions/") || execPath.includes("claude.exe") || execPath.includes("claude-native"))) {
|
|
27912
|
+
results.push({
|
|
27913
|
+
name: "cc/execpath-clean",
|
|
27914
|
+
pass: false,
|
|
27915
|
+
detail: `CLAUDE_CODE_EXECPATH points to native binary: "${execPath}" \u2014 20K phantom billing risk. Unset it: unset CLAUDE_CODE_EXECPATH`
|
|
27916
|
+
});
|
|
27917
|
+
} else {
|
|
27918
|
+
results.push({
|
|
27919
|
+
name: "cc/execpath-clean",
|
|
27920
|
+
pass: true,
|
|
27921
|
+
detail: execPath ? `CLAUDE_CODE_EXECPATH=${execPath} (node runtime, OK)` : "CLAUDE_CODE_EXECPATH is unset"
|
|
27922
|
+
});
|
|
27923
|
+
}
|
|
27924
|
+
try {
|
|
27925
|
+
const claudePath = execSync13("which claude 2>/dev/null || true", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
27926
|
+
if (!claudePath) {
|
|
27927
|
+
results.push({
|
|
27928
|
+
name: "cc/cli-binary",
|
|
27929
|
+
pass: false,
|
|
27930
|
+
detail: "claude not found in PATH"
|
|
27931
|
+
});
|
|
27932
|
+
} else {
|
|
27933
|
+
let resolved = claudePath;
|
|
27934
|
+
try {
|
|
27935
|
+
resolved = execSync13(`readlink -f "${claudePath}" 2>/dev/null || readlink "${claudePath}" 2>/dev/null || echo "${claudePath}"`, {
|
|
27936
|
+
encoding: "utf8",
|
|
27937
|
+
timeout: 5e3
|
|
27938
|
+
}).trim();
|
|
27939
|
+
} catch {
|
|
27940
|
+
}
|
|
27941
|
+
if (resolved.includes("bin/claude.exe") || resolved.includes("bin/claude-native")) {
|
|
27942
|
+
results.push({
|
|
27943
|
+
name: "cc/cli-binary",
|
|
27944
|
+
pass: false,
|
|
27945
|
+
detail: `claude resolves to native binary: ${resolved}. Should be cli.js. Run: npm install -g @anthropic-ai/claude-code@2.1.98`
|
|
27946
|
+
});
|
|
27947
|
+
} else {
|
|
27948
|
+
results.push({
|
|
27949
|
+
name: "cc/cli-binary",
|
|
27950
|
+
pass: true,
|
|
27951
|
+
detail: `claude resolves to: ${resolved}`
|
|
27952
|
+
});
|
|
27953
|
+
}
|
|
27954
|
+
}
|
|
27955
|
+
} catch {
|
|
27956
|
+
results.push({
|
|
27957
|
+
name: "cc/cli-binary",
|
|
27958
|
+
pass: false,
|
|
27959
|
+
detail: "Failed to check claude binary path"
|
|
27960
|
+
});
|
|
27961
|
+
}
|
|
27962
|
+
const versionsDir = path51.join(
|
|
27963
|
+
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
27964
|
+
".local",
|
|
27965
|
+
"share",
|
|
27966
|
+
"claude",
|
|
27967
|
+
"versions"
|
|
27968
|
+
);
|
|
27969
|
+
if (existsSync39(versionsDir)) {
|
|
27970
|
+
try {
|
|
27971
|
+
const entries = readdirSync14(versionsDir);
|
|
27972
|
+
if (entries.length > 0) {
|
|
27973
|
+
results.push({
|
|
27974
|
+
name: "cc/native-cache-clean",
|
|
27975
|
+
pass: false,
|
|
27976
|
+
detail: `${entries.length} cached native version(s) found in ${versionsDir}: ${entries.slice(0, 3).join(", ")}${entries.length > 3 ? "..." : ""}. Remove: rm -rf "${versionsDir}"`
|
|
27977
|
+
});
|
|
27978
|
+
} else {
|
|
27979
|
+
results.push({
|
|
27980
|
+
name: "cc/native-cache-clean",
|
|
27981
|
+
pass: true,
|
|
27982
|
+
detail: `${versionsDir} is empty`
|
|
27983
|
+
});
|
|
27984
|
+
}
|
|
27985
|
+
} catch {
|
|
27986
|
+
results.push({
|
|
27987
|
+
name: "cc/native-cache-clean",
|
|
27988
|
+
pass: true,
|
|
27989
|
+
detail: `${versionsDir} not readable (OK)`
|
|
27990
|
+
});
|
|
27991
|
+
}
|
|
27992
|
+
} else {
|
|
27993
|
+
results.push({
|
|
27994
|
+
name: "cc/native-cache-clean",
|
|
27995
|
+
pass: true,
|
|
27996
|
+
detail: `${versionsDir} does not exist`
|
|
27997
|
+
});
|
|
27998
|
+
}
|
|
27999
|
+
const disableOld = process.env.DISABLE_AUTOUPDATER === "1";
|
|
28000
|
+
const disableNew = process.env.CLAUDE_CODE_AUTOUPDATER_DISABLED === "1";
|
|
28001
|
+
if (!disableOld && !disableNew) {
|
|
28002
|
+
results.push({
|
|
28003
|
+
name: "cc/autoupdater-disabled",
|
|
28004
|
+
pass: false,
|
|
28005
|
+
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."
|
|
28006
|
+
});
|
|
28007
|
+
} else {
|
|
28008
|
+
const which = [
|
|
28009
|
+
disableOld ? "DISABLE_AUTOUPDATER=1" : null,
|
|
28010
|
+
disableNew ? "CLAUDE_CODE_AUTOUPDATER_DISABLED=1" : null
|
|
28011
|
+
].filter(Boolean).join(" + ");
|
|
28012
|
+
results.push({
|
|
28013
|
+
name: "cc/autoupdater-disabled",
|
|
28014
|
+
pass: true,
|
|
28015
|
+
detail: `Auto-updater disabled via ${which}`
|
|
28016
|
+
});
|
|
28017
|
+
}
|
|
28018
|
+
try {
|
|
28019
|
+
const ccVersion = execSync13("claude --version 2>/dev/null || echo unknown", {
|
|
28020
|
+
encoding: "utf8",
|
|
28021
|
+
timeout: 5e3
|
|
28022
|
+
}).trim();
|
|
28023
|
+
const versionMatch = ccVersion.match(/(\d+\.\d+\.\d+)/);
|
|
28024
|
+
if (versionMatch) {
|
|
28025
|
+
const ver = versionMatch[1];
|
|
28026
|
+
const [, minor] = ver.split(".").map(Number);
|
|
28027
|
+
const isRisky = minor !== void 0 && minor >= 1 && parseInt(ver.split(".")[2] ?? "0") >= 119;
|
|
28028
|
+
results.push({
|
|
28029
|
+
name: "cc/version",
|
|
28030
|
+
pass: !isRisky,
|
|
28031
|
+
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}`
|
|
28032
|
+
});
|
|
28033
|
+
}
|
|
28034
|
+
} catch {
|
|
28035
|
+
}
|
|
28036
|
+
return results;
|
|
28037
|
+
}
|
|
28038
|
+
function runHealthCheck() {
|
|
28039
|
+
const pkgRoot = findPackageRoot2();
|
|
28040
|
+
const results = [
|
|
28041
|
+
...checkBuildIntegrity(pkgRoot),
|
|
28042
|
+
...checkEmbedPipeline(pkgRoot),
|
|
28043
|
+
...checkTaskSystem(pkgRoot),
|
|
28044
|
+
...checkWorkerSpawning(pkgRoot),
|
|
28045
|
+
...checkMcpTransport(),
|
|
28046
|
+
...checkClaudeCodeInstall()
|
|
28047
|
+
];
|
|
28048
|
+
const passed = results.filter((r) => r.pass).length;
|
|
28049
|
+
const failed = results.filter((r) => !r.pass).length;
|
|
28050
|
+
return { results, passed, failed };
|
|
28051
|
+
}
|
|
28052
|
+
if (isMainModule(import.meta.url)) {
|
|
28053
|
+
const { results, passed, failed } = runHealthCheck();
|
|
28054
|
+
console.log("\n exe-os Health Check\n");
|
|
28055
|
+
for (const r of results) {
|
|
28056
|
+
const icon = r.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
28057
|
+
console.log(` ${icon} ${r.name}`);
|
|
28058
|
+
console.log(` ${r.detail}`);
|
|
28059
|
+
}
|
|
28060
|
+
console.log(`
|
|
28061
|
+
${passed} passed, ${failed} failed
|
|
28062
|
+
`);
|
|
28063
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
28064
|
+
}
|
|
28065
|
+
|
|
28066
|
+
// src/lib/update-check.ts
|
|
28067
|
+
import { execSync as execSync14 } from "child_process";
|
|
28068
|
+
import { readFileSync as readFileSync34 } from "fs";
|
|
28069
|
+
import path52 from "path";
|
|
28070
|
+
function getLocalVersion(packageRoot) {
|
|
28071
|
+
const pkgPath = path52.join(packageRoot, "package.json");
|
|
28072
|
+
const pkg = JSON.parse(readFileSync34(pkgPath, "utf-8"));
|
|
28073
|
+
return pkg.version;
|
|
28074
|
+
}
|
|
28075
|
+
function getRemoteVersion() {
|
|
28076
|
+
try {
|
|
28077
|
+
const output = execSync14("npm view @askexenow/exe-os version", {
|
|
28078
|
+
encoding: "utf-8",
|
|
28079
|
+
timeout: 15e3,
|
|
28080
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
28081
|
+
});
|
|
28082
|
+
return output.trim();
|
|
28083
|
+
} catch {
|
|
28084
|
+
return null;
|
|
28085
|
+
}
|
|
28086
|
+
}
|
|
28087
|
+
function checkForUpdate(packageRoot) {
|
|
28088
|
+
const localVersion = getLocalVersion(packageRoot);
|
|
28089
|
+
const remoteVersion = getRemoteVersion();
|
|
28090
|
+
if (!remoteVersion) {
|
|
28091
|
+
return {
|
|
28092
|
+
updateAvailable: false,
|
|
28093
|
+
localVersion,
|
|
28094
|
+
error: "Could not reach npm registry or package not published yet"
|
|
28095
|
+
};
|
|
28096
|
+
}
|
|
28097
|
+
if (remoteVersion === localVersion) {
|
|
28098
|
+
return { updateAvailable: false, localVersion, remoteVersion };
|
|
28099
|
+
}
|
|
28100
|
+
return { updateAvailable: true, localVersion, remoteVersion };
|
|
28101
|
+
}
|
|
28102
|
+
|
|
28103
|
+
// src/mcp/tools/cli-parity.ts
|
|
28104
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
28105
|
+
async function runCommand(command, args, timeout = 6e4) {
|
|
28106
|
+
const printable = [command, ...args].join(" ");
|
|
28107
|
+
try {
|
|
28108
|
+
const { stdout, stderr } = await execFileAsync2(command, args, {
|
|
28109
|
+
timeout,
|
|
28110
|
+
maxBuffer: 1024 * 1024,
|
|
28111
|
+
env: process.env
|
|
28112
|
+
});
|
|
28113
|
+
return { ok: true, command: printable, text: `${stdout}${stderr ? `
|
|
28114
|
+
${stderr}` : ""}`.trim() || "OK" };
|
|
28115
|
+
} catch (err) {
|
|
28116
|
+
const e = err;
|
|
28117
|
+
const text3 = `${e.stdout ?? ""}${e.stderr ? `
|
|
28118
|
+
${e.stderr}` : ""}`.trim() || e.message || "Command failed";
|
|
28119
|
+
return { ok: false, command: printable, text: text3 };
|
|
28120
|
+
}
|
|
28121
|
+
}
|
|
28122
|
+
function result2(text3, structured, isError = false) {
|
|
28123
|
+
return { content: [{ type: "text", text: text3 }], structuredContent: structured, isError };
|
|
28124
|
+
}
|
|
28125
|
+
function registerCliParityTools(server2) {
|
|
28126
|
+
server2.registerTool("healthcheck", {
|
|
28127
|
+
title: "Health Check",
|
|
28128
|
+
description: "Run the exe-os product health check and return structured pass/fail results.",
|
|
28129
|
+
inputSchema: {}
|
|
28130
|
+
}, async () => {
|
|
28131
|
+
const out = runHealthCheck();
|
|
28132
|
+
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");
|
|
28133
|
+
return result2(text3, out, out.failed > 0);
|
|
28134
|
+
});
|
|
28135
|
+
server2.registerTool("doctor", {
|
|
28136
|
+
title: "Doctor",
|
|
28137
|
+
description: "Run exe-os doctor audit. Defaults to read-only diagnostics; optional dry-run/fix flags mirror CLI.",
|
|
28138
|
+
inputSchema: {
|
|
28139
|
+
agent: z90.string().optional(),
|
|
28140
|
+
project: z90.string().optional(),
|
|
28141
|
+
verbose: z90.boolean().default(false),
|
|
28142
|
+
conflicts: z90.boolean().default(false),
|
|
28143
|
+
dry_run: z90.boolean().default(false),
|
|
28144
|
+
fix: z90.boolean().default(false)
|
|
28145
|
+
}
|
|
28146
|
+
}, async ({ agent, project, verbose, conflicts, dry_run, fix }) => {
|
|
28147
|
+
const args = [];
|
|
28148
|
+
if (agent) args.push("--agent", agent);
|
|
28149
|
+
if (project) args.push("--project", project);
|
|
28150
|
+
if (verbose) args.push("--verbose");
|
|
28151
|
+
if (conflicts) args.push("--conflicts");
|
|
28152
|
+
if (dry_run) args.push("--dry-run");
|
|
28153
|
+
if (fix) args.push("--fix");
|
|
28154
|
+
const out = await runCommand("exe-doctor", args, 12e4);
|
|
28155
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28156
|
+
});
|
|
28157
|
+
server2.registerTool("rename_employee", {
|
|
28158
|
+
title: "Rename Employee",
|
|
28159
|
+
description: "Rename an employee using the same path as `exe-os rename <old> <new>`. Use for customer roster/identity renames.",
|
|
28160
|
+
inputSchema: {
|
|
28161
|
+
old_name: z90.string().min(1),
|
|
28162
|
+
new_name: z90.string().min(1),
|
|
28163
|
+
dry_run: z90.boolean().default(false)
|
|
28164
|
+
}
|
|
28165
|
+
}, async ({ old_name, new_name, dry_run }) => {
|
|
28166
|
+
if (dry_run) {
|
|
28167
|
+
const text3 = `Would run: exe-os rename ${old_name} ${new_name}`;
|
|
28168
|
+
return result2(text3, { ok: true, dry_run: true, old_name, new_name });
|
|
28169
|
+
}
|
|
28170
|
+
const out = await runCommand("exe-os", ["rename", old_name, new_name], 6e4);
|
|
28171
|
+
return result2(out.text, { ok: out.ok, command: out.command, old_name, new_name }, !out.ok);
|
|
28172
|
+
});
|
|
28173
|
+
server2.registerTool("status_brief", {
|
|
28174
|
+
title: "Status Brief",
|
|
28175
|
+
description: "Return current employee/tmux status. Mirrors `exe-status` and supports optional deep view for one employee.",
|
|
28176
|
+
inputSchema: { employee: z90.string().optional() }
|
|
28177
|
+
}, async ({ employee }) => {
|
|
28178
|
+
const text3 = await status(employee);
|
|
28179
|
+
return result2(text3, { ok: true, employee: employee ?? null });
|
|
28180
|
+
});
|
|
28181
|
+
server2.registerTool("pending_work_summary", {
|
|
28182
|
+
title: "Pending Work Summary",
|
|
28183
|
+
description: "Return pending reviews, messages, and notifications using the same summaries as the CLI tools.",
|
|
28184
|
+
inputSchema: { agent: z90.string().optional() }
|
|
28185
|
+
}, async ({ agent }) => {
|
|
28186
|
+
const parts = await Promise.all([
|
|
28187
|
+
runCommand("exe-pending-reviews", [], 3e4),
|
|
28188
|
+
runCommand("exe-pending-messages", agent ? [agent] : [], 3e4),
|
|
28189
|
+
runCommand("exe-pending-notifications", agent ? [agent] : [], 3e4)
|
|
28190
|
+
]);
|
|
28191
|
+
const text3 = ["Pending work summary", "", "## Reviews", parts[0].text, "", "## Messages", parts[1].text, "", "## Notifications", parts[2].text].join("\n");
|
|
28192
|
+
return result2(text3, { ok: parts.every((p) => p.ok), commands: parts.map((p) => p.command) }, parts.some((p) => !p.ok));
|
|
28193
|
+
});
|
|
28194
|
+
server2.registerTool("key_status", {
|
|
28195
|
+
title: "Key Status",
|
|
28196
|
+
description: "Read-only Exe OS key status. Does not reveal recovery phrase or rotate keys.",
|
|
28197
|
+
inputSchema: {}
|
|
28198
|
+
}, async () => {
|
|
28199
|
+
const out = await runCommand("exe-os", ["key", "status"], 3e4);
|
|
28200
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28201
|
+
});
|
|
28202
|
+
server2.registerTool("key_rotation_preflight", {
|
|
28203
|
+
title: "Key Rotation Preflight",
|
|
28204
|
+
description: "Dry-run key rotation/update preflight. No destructive changes.",
|
|
28205
|
+
inputSchema: { mode: z90.enum(["rotate", "update"]).default("rotate") }
|
|
28206
|
+
}, async ({ mode }) => {
|
|
28207
|
+
const out = await runCommand("exe-os", ["key", mode, "--dry-run"], 6e4);
|
|
28208
|
+
return result2(out.text, { ok: out.ok, command: out.command, mode }, !out.ok);
|
|
28209
|
+
});
|
|
28210
|
+
server2.registerTool("check_update", {
|
|
28211
|
+
title: "Check Update",
|
|
28212
|
+
description: "Check npm for an available exe-os update without installing it.",
|
|
28213
|
+
inputSchema: {}
|
|
28214
|
+
}, async () => {
|
|
28215
|
+
const packageRoot = new URL("../../..", import.meta.url).pathname;
|
|
28216
|
+
const info = checkForUpdate(packageRoot);
|
|
28217
|
+
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})`;
|
|
28218
|
+
return result2(text3, { ok: !info.error, ...info }, Boolean(info.error));
|
|
28219
|
+
});
|
|
28220
|
+
server2.registerTool("stack_update_check", {
|
|
28221
|
+
title: "Stack Update Check",
|
|
28222
|
+
description: "Plan/check a customer stack update without applying Docker changes. Mirrors `exe-os stack-update --check`.",
|
|
28223
|
+
inputSchema: {
|
|
28224
|
+
target: z90.string().optional(),
|
|
28225
|
+
manifest: z90.string().optional(),
|
|
28226
|
+
compose_file: z90.string().optional(),
|
|
28227
|
+
env_file: z90.string().optional(),
|
|
28228
|
+
deployment_persona: z90.enum(["customer", "askexe-control-plane"]).default("customer")
|
|
28229
|
+
}
|
|
28230
|
+
}, async ({ target, manifest, compose_file, env_file, deployment_persona }) => {
|
|
28231
|
+
const args = ["stack-update", "--check", "--deployment-persona", deployment_persona];
|
|
28232
|
+
if (target) args.push("--target", target);
|
|
28233
|
+
if (manifest) args.push("--manifest", manifest);
|
|
28234
|
+
if (compose_file) args.push("--compose-file", compose_file);
|
|
28235
|
+
if (env_file) args.push("--env-file", env_file);
|
|
28236
|
+
const out = await runCommand("exe-os", args, 9e4);
|
|
28237
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28238
|
+
});
|
|
28239
|
+
server2.registerTool("cloud_status", {
|
|
28240
|
+
title: "Cloud Status",
|
|
28241
|
+
description: "Read-only cloud sync status. Does not reveal recovery phrase.",
|
|
28242
|
+
inputSchema: {}
|
|
28243
|
+
}, async () => {
|
|
28244
|
+
const out = await runCommand("exe-os", ["cloud", "status"], 45e3);
|
|
28245
|
+
return result2(out.text, { ok: out.ok, command: out.command }, !out.ok);
|
|
28246
|
+
});
|
|
28247
|
+
}
|
|
28248
|
+
|
|
27323
28249
|
// src/mcp/tools/get-session-events.ts
|
|
27324
28250
|
init_active_agent();
|
|
27325
28251
|
init_fast_db_init();
|
|
27326
|
-
import { z as
|
|
28252
|
+
import { z as z91 } from "zod";
|
|
27327
28253
|
|
|
27328
28254
|
// src/lib/session-events.ts
|
|
27329
28255
|
init_task_scope();
|
|
@@ -27381,7 +28307,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27381
28307
|
const where = `WHERE ${conditions.join(" AND ")}${scope.sql}`;
|
|
27382
28308
|
args.push(...scope.args);
|
|
27383
28309
|
args.push(Math.min(Math.max(options.limit ?? 20, 1), 100));
|
|
27384
|
-
const
|
|
28310
|
+
const result3 = await client.execute({
|
|
27385
28311
|
sql: `SELECT id, agent_id, agent_role, session_id, session_scope,
|
|
27386
28312
|
project_name, event_index, event_type, tool_name, tool_use_id,
|
|
27387
28313
|
content, payload_json, has_error, created_at
|
|
@@ -27391,7 +28317,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27391
28317
|
LIMIT ?`,
|
|
27392
28318
|
args
|
|
27393
28319
|
});
|
|
27394
|
-
return
|
|
28320
|
+
return result3.rows.map((row) => ({
|
|
27395
28321
|
id: String(row.id),
|
|
27396
28322
|
agentId: String(row.agent_id),
|
|
27397
28323
|
agentRole: String(row.agent_role),
|
|
@@ -27410,7 +28336,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
27410
28336
|
}
|
|
27411
28337
|
|
|
27412
28338
|
// src/mcp/tools/get-session-events.ts
|
|
27413
|
-
var EVENT_TYPE =
|
|
28339
|
+
var EVENT_TYPE = z91.enum([
|
|
27414
28340
|
"user_prompt",
|
|
27415
28341
|
"assistant_response",
|
|
27416
28342
|
"tool_call",
|
|
@@ -27440,11 +28366,11 @@ function registerGetSessionEvents(server2) {
|
|
|
27440
28366
|
title: "Get Session Events",
|
|
27441
28367
|
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.",
|
|
27442
28368
|
inputSchema: {
|
|
27443
|
-
agent_id:
|
|
27444
|
-
session_id:
|
|
28369
|
+
agent_id: z91.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28370
|
+
session_id: z91.string().optional().describe("Optional exact runtime session id."),
|
|
27445
28371
|
event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
|
|
27446
|
-
project_name:
|
|
27447
|
-
limit:
|
|
28372
|
+
project_name: z91.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
|
|
28373
|
+
limit: z91.number().int().min(1).max(100).default(20).describe("Number of events to return.")
|
|
27448
28374
|
}
|
|
27449
28375
|
},
|
|
27450
28376
|
async ({ agent_id, session_id, event_type, project_name, limit }) => {
|
|
@@ -27480,8 +28406,8 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
27480
28406
|
title: "Get Last Assistant Response",
|
|
27481
28407
|
description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
|
|
27482
28408
|
inputSchema: {
|
|
27483
|
-
agent_id:
|
|
27484
|
-
project_name:
|
|
28409
|
+
agent_id: z91.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28410
|
+
project_name: z91.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
|
|
27485
28411
|
}
|
|
27486
28412
|
},
|
|
27487
28413
|
async ({ agent_id, project_name }) => {
|
|
@@ -27511,36 +28437,36 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
27511
28437
|
}
|
|
27512
28438
|
|
|
27513
28439
|
// src/mcp/tools/code-context.ts
|
|
27514
|
-
import { z as
|
|
28440
|
+
import { z as z92 } from "zod";
|
|
27515
28441
|
|
|
27516
28442
|
// src/lib/code-context-index.ts
|
|
27517
28443
|
init_config();
|
|
27518
28444
|
import crypto19 from "crypto";
|
|
27519
|
-
import
|
|
27520
|
-
import { existsSync as
|
|
28445
|
+
import path53 from "path";
|
|
28446
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync22, readFileSync as readFileSync35, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync24 } from "fs";
|
|
27521
28447
|
import { spawnSync } from "child_process";
|
|
27522
28448
|
var INDEX_VERSION = 2;
|
|
27523
28449
|
var DEFAULT_MAX_FILES = 5e3;
|
|
27524
28450
|
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
27525
28451
|
function normalizeProjectRoot(projectRoot) {
|
|
27526
|
-
return
|
|
28452
|
+
return path53.resolve(projectRoot || process.cwd());
|
|
27527
28453
|
}
|
|
27528
28454
|
function hashText(text3) {
|
|
27529
28455
|
return crypto19.createHash("sha256").update(text3).digest("hex");
|
|
27530
28456
|
}
|
|
27531
28457
|
function indexDir() {
|
|
27532
|
-
const dir =
|
|
28458
|
+
const dir = path53.join(EXE_AI_DIR, "code-context");
|
|
27533
28459
|
mkdirSync22(dir, { recursive: true });
|
|
27534
28460
|
return dir;
|
|
27535
28461
|
}
|
|
27536
28462
|
function getCodeContextIndexPath(projectRoot) {
|
|
27537
28463
|
const root = normalizeProjectRoot(projectRoot);
|
|
27538
28464
|
const rootHash = hashText(root).slice(0, 16);
|
|
27539
|
-
return
|
|
28465
|
+
return path53.join(indexDir(), `${rootHash}.json`);
|
|
27540
28466
|
}
|
|
27541
28467
|
function currentBranch(projectRoot) {
|
|
27542
|
-
const
|
|
27543
|
-
const branch =
|
|
28468
|
+
const result3 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
28469
|
+
const branch = result3.status === 0 ? result3.stdout.trim() : "";
|
|
27544
28470
|
return branch || "detached-or-unknown";
|
|
27545
28471
|
}
|
|
27546
28472
|
function shouldIgnore(relPath) {
|
|
@@ -27548,9 +28474,9 @@ function shouldIgnore(relPath) {
|
|
|
27548
28474
|
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
27549
28475
|
}
|
|
27550
28476
|
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
27551
|
-
for (const entry of
|
|
27552
|
-
const abs =
|
|
27553
|
-
const rel =
|
|
28477
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
28478
|
+
const abs = path53.join(dir, entry.name);
|
|
28479
|
+
const rel = path53.relative(projectRoot, abs).replaceAll(path53.sep, "/");
|
|
27554
28480
|
if (shouldIgnore(rel)) continue;
|
|
27555
28481
|
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
27556
28482
|
else if (entry.isFile()) out.push(rel);
|
|
@@ -27566,7 +28492,7 @@ function listCodeFiles(projectRoot, maxFiles) {
|
|
|
27566
28492
|
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
27567
28493
|
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
27568
28494
|
}
|
|
27569
|
-
return files.map((file) => file.replaceAll(
|
|
28495
|
+
return files.map((file) => file.replaceAll(path53.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
27570
28496
|
}
|
|
27571
28497
|
function parseImportPaths(importText) {
|
|
27572
28498
|
const paths = [];
|
|
@@ -27579,13 +28505,13 @@ function parseImportPaths(importText) {
|
|
|
27579
28505
|
}
|
|
27580
28506
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
27581
28507
|
if (!importPath.startsWith(".")) return null;
|
|
27582
|
-
const base =
|
|
28508
|
+
const base = path53.posix.normalize(path53.posix.join(path53.posix.dirname(fromFile.replaceAll(path53.sep, "/")), importPath));
|
|
27583
28509
|
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
27584
28510
|
const candidates = [base];
|
|
27585
28511
|
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"]) {
|
|
27586
28512
|
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
27587
28513
|
}
|
|
27588
|
-
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(
|
|
28514
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path53.posix.join(base, indexName));
|
|
27589
28515
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
27590
28516
|
}
|
|
27591
28517
|
function symbolId(filePath, chunk) {
|
|
@@ -27593,9 +28519,9 @@ function symbolId(filePath, chunk) {
|
|
|
27593
28519
|
}
|
|
27594
28520
|
function loadIndex(projectRoot) {
|
|
27595
28521
|
const file = getCodeContextIndexPath(projectRoot);
|
|
27596
|
-
if (!
|
|
28522
|
+
if (!existsSync40(file)) return null;
|
|
27597
28523
|
try {
|
|
27598
|
-
const parsed = JSON.parse(
|
|
28524
|
+
const parsed = JSON.parse(readFileSync35(file, "utf8"));
|
|
27599
28525
|
if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
|
|
27600
28526
|
return parsed;
|
|
27601
28527
|
} catch {
|
|
@@ -27606,7 +28532,7 @@ function saveIndex(index) {
|
|
|
27606
28532
|
writeFileSync24(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
27607
28533
|
}
|
|
27608
28534
|
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
27609
|
-
const absPath =
|
|
28535
|
+
const absPath = path53.join(projectRoot, relPath);
|
|
27610
28536
|
let stat;
|
|
27611
28537
|
try {
|
|
27612
28538
|
stat = statSync9(absPath);
|
|
@@ -27616,7 +28542,7 @@ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
|
27616
28542
|
if (!stat.isFile()) return { record: null, reused: false };
|
|
27617
28543
|
const language = languageForFile(relPath);
|
|
27618
28544
|
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
27619
|
-
const source =
|
|
28545
|
+
const source = readFileSync35(absPath, "utf8");
|
|
27620
28546
|
const hash = hashText(source);
|
|
27621
28547
|
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
|
|
27622
28548
|
return { record: previous, reused: true };
|
|
@@ -27644,13 +28570,13 @@ function buildCodeContextIndex(options = {}) {
|
|
|
27644
28570
|
const branch = currentBranch(projectRoot);
|
|
27645
28571
|
const previous = options.force ? null : loadIndex(projectRoot);
|
|
27646
28572
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
27647
|
-
const allFiles = new Set(files.map((file) => file.replaceAll(
|
|
28573
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path53.sep, "/")));
|
|
27648
28574
|
const fileRecords = {};
|
|
27649
28575
|
let rebuiltFiles = 0;
|
|
27650
28576
|
let reusedFiles = 0;
|
|
27651
28577
|
let skippedFiles = 0;
|
|
27652
28578
|
for (const rel of files) {
|
|
27653
|
-
const normalized = rel.replaceAll(
|
|
28579
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
27654
28580
|
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
27655
28581
|
if (record) {
|
|
27656
28582
|
fileRecords[normalized] = record;
|
|
@@ -27679,11 +28605,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
27679
28605
|
if (loaded) {
|
|
27680
28606
|
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
27681
28607
|
const unchanged = currentFiles.every((rel) => {
|
|
27682
|
-
const normalized = rel.replaceAll(
|
|
28608
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
27683
28609
|
const existing = loaded.files[normalized];
|
|
27684
28610
|
if (!existing) return false;
|
|
27685
28611
|
try {
|
|
27686
|
-
const stat = statSync9(
|
|
28612
|
+
const stat = statSync9(path53.join(projectRoot, normalized));
|
|
27687
28613
|
return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
|
|
27688
28614
|
} catch {
|
|
27689
28615
|
return false;
|
|
@@ -27736,9 +28662,9 @@ function globToRegex(pattern) {
|
|
|
27736
28662
|
}
|
|
27737
28663
|
function matchesPath(filePath, patterns) {
|
|
27738
28664
|
if (!patterns || patterns.length === 0) return true;
|
|
27739
|
-
const normalized = filePath.replaceAll(
|
|
28665
|
+
const normalized = filePath.replaceAll(path53.sep, "/");
|
|
27740
28666
|
return patterns.some((pattern) => {
|
|
27741
|
-
const p = pattern.replaceAll(
|
|
28667
|
+
const p = pattern.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
27742
28668
|
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
27743
28669
|
});
|
|
27744
28670
|
}
|
|
@@ -27856,7 +28782,7 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
27856
28782
|
}
|
|
27857
28783
|
function resolveTargetFile(index, input) {
|
|
27858
28784
|
if (input.filePath) {
|
|
27859
|
-
const normalized = input.filePath.replaceAll(
|
|
28785
|
+
const normalized = input.filePath.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
27860
28786
|
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
27861
28787
|
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
27862
28788
|
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
@@ -27886,7 +28812,7 @@ function analyzeBlastRadius(input) {
|
|
|
27886
28812
|
}
|
|
27887
28813
|
}
|
|
27888
28814
|
}
|
|
27889
|
-
const targetBase =
|
|
28815
|
+
const targetBase = path53.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
27890
28816
|
const symbolLower = input.symbol?.toLowerCase();
|
|
27891
28817
|
const tests = Object.keys(index.files).filter((file) => {
|
|
27892
28818
|
const lower = file.toLowerCase();
|
|
@@ -27927,19 +28853,19 @@ function registerCodeContext(server2) {
|
|
|
27927
28853
|
title: "Code Context",
|
|
27928
28854
|
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
27929
28855
|
inputSchema: {
|
|
27930
|
-
action:
|
|
27931
|
-
project_root:
|
|
27932
|
-
query:
|
|
27933
|
-
symbol:
|
|
27934
|
-
file_path:
|
|
27935
|
-
force:
|
|
27936
|
-
limit:
|
|
27937
|
-
offset:
|
|
27938
|
-
refresh_index:
|
|
27939
|
-
languages:
|
|
27940
|
-
paths:
|
|
27941
|
-
depth:
|
|
27942
|
-
max_files:
|
|
28856
|
+
action: z92.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
|
|
28857
|
+
project_root: z92.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
28858
|
+
query: z92.string().optional().describe("Search query for action=search"),
|
|
28859
|
+
symbol: z92.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
28860
|
+
file_path: z92.string().optional().describe("File path for blast_radius"),
|
|
28861
|
+
force: z92.boolean().optional().describe("Force rebuild before answering"),
|
|
28862
|
+
limit: z92.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
28863
|
+
offset: z92.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
28864
|
+
refresh_index: z92.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
28865
|
+
languages: z92.array(z92.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
28866
|
+
paths: z92.array(z92.string()).optional().describe("Path/glob filters"),
|
|
28867
|
+
depth: z92.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
28868
|
+
max_files: z92.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
|
|
27943
28869
|
}
|
|
27944
28870
|
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
|
|
27945
28871
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
@@ -27975,18 +28901,18 @@ function registerCodeContext(server2) {
|
|
|
27975
28901
|
}
|
|
27976
28902
|
if (action === "blast_radius") {
|
|
27977
28903
|
if (!symbol && !file_path) return errorResult10('code_context action "blast_radius" requires symbol or file_path');
|
|
27978
|
-
const
|
|
27979
|
-
if (!
|
|
27980
|
-
return jsonResult(
|
|
28904
|
+
const result3 = analyzeBlastRadius({ projectRoot: project_root, force, symbol, filePath: file_path, depth });
|
|
28905
|
+
if (!result3) return errorResult10(`No code context target found for ${symbol || file_path}`);
|
|
28906
|
+
return jsonResult(result3);
|
|
27981
28907
|
}
|
|
27982
28908
|
return errorResult10(`Unknown code_context action: ${String(action)}`);
|
|
27983
28909
|
});
|
|
27984
28910
|
}
|
|
27985
28911
|
|
|
27986
28912
|
// src/mcp/tools/support-inbox.ts
|
|
27987
|
-
import { z as
|
|
28913
|
+
import { z as z93 } from "zod";
|
|
27988
28914
|
var DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
27989
|
-
var STATUS =
|
|
28915
|
+
var STATUS = z93.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
27990
28916
|
function adminToken() {
|
|
27991
28917
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
27992
28918
|
}
|
|
@@ -28025,14 +28951,14 @@ function registerListBugReports(server2) {
|
|
|
28025
28951
|
title: "List Bug Reports",
|
|
28026
28952
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
28027
28953
|
inputSchema: {
|
|
28028
|
-
status:
|
|
28029
|
-
severity:
|
|
28030
|
-
limit:
|
|
28954
|
+
status: z93.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
28955
|
+
severity: z93.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
28956
|
+
limit: z93.number().int().min(1).max(100).default(25)
|
|
28031
28957
|
}
|
|
28032
28958
|
},
|
|
28033
|
-
async ({ status, severity, limit }) => {
|
|
28959
|
+
async ({ status: status2, severity, limit }) => {
|
|
28034
28960
|
const url = new URL(endpoint());
|
|
28035
|
-
url.searchParams.set("status",
|
|
28961
|
+
url.searchParams.set("status", status2);
|
|
28036
28962
|
url.searchParams.set("limit", String(limit));
|
|
28037
28963
|
if (severity) url.searchParams.set("severity", severity);
|
|
28038
28964
|
const data = await requestJson(url.toString());
|
|
@@ -28046,7 +28972,7 @@ function registerGetBugReport(server2) {
|
|
|
28046
28972
|
{
|
|
28047
28973
|
title: "Get Bug Report",
|
|
28048
28974
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
28049
|
-
inputSchema: { id:
|
|
28975
|
+
inputSchema: { id: z93.string().min(8) }
|
|
28050
28976
|
},
|
|
28051
28977
|
async ({ id }) => {
|
|
28052
28978
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -28061,18 +28987,18 @@ function registerTriageBugReport(server2) {
|
|
|
28061
28987
|
title: "Triage Bug Report",
|
|
28062
28988
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
28063
28989
|
inputSchema: {
|
|
28064
|
-
id:
|
|
28990
|
+
id: z93.string().min(8),
|
|
28065
28991
|
status: STATUS.optional(),
|
|
28066
|
-
triage_notes:
|
|
28067
|
-
linked_task_id:
|
|
28068
|
-
linked_commit:
|
|
28069
|
-
fixed_version:
|
|
28992
|
+
triage_notes: z93.string().optional(),
|
|
28993
|
+
linked_task_id: z93.string().optional(),
|
|
28994
|
+
linked_commit: z93.string().optional(),
|
|
28995
|
+
fixed_version: z93.string().optional()
|
|
28070
28996
|
}
|
|
28071
28997
|
},
|
|
28072
|
-
async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
28998
|
+
async ({ id, status: status2, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
28073
28999
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`, {
|
|
28074
29000
|
method: "PATCH",
|
|
28075
|
-
body: JSON.stringify({ status, triage_notes, linked_task_id, linked_commit, fixed_version })
|
|
29001
|
+
body: JSON.stringify({ status: status2, triage_notes, linked_task_id, linked_commit, fixed_version })
|
|
28076
29002
|
});
|
|
28077
29003
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
28078
29004
|
}
|
|
@@ -28102,7 +29028,7 @@ var TOOL_GATES = {
|
|
|
28102
29028
|
documents: ["COO", "CTO", "Wiki Agent"],
|
|
28103
29029
|
// ingest, list, purge, set_importance, rerank
|
|
28104
29030
|
gateway: ["COO", "Gateway Agent"],
|
|
28105
|
-
// send_whatsapp, query_conversations, query_company_brain
|
|
29031
|
+
// send_whatsapp, query_conversations, query_company_brain, company_actions
|
|
28106
29032
|
admin: ["COO"],
|
|
28107
29033
|
// deploy, backup, daemon health, auto-wake, worker gate, memory audit, consolidation, cloud sync, agent config, list employees, agent spend, sessions, session kills
|
|
28108
29034
|
licensing: ["COO"],
|
|
@@ -28130,6 +29056,7 @@ var TOOL_CATEGORIES = {
|
|
|
28130
29056
|
registerGetDecision: "core",
|
|
28131
29057
|
registerCreateBugReport: "core",
|
|
28132
29058
|
registerSupportTools: "core",
|
|
29059
|
+
registerCliParityTools: "admin",
|
|
28133
29060
|
registerGetSessionEvents: "core",
|
|
28134
29061
|
registerGetLastAssistantResponse: "core",
|
|
28135
29062
|
registerCodeContext: "graph-read",
|
|
@@ -28190,6 +29117,7 @@ var TOOL_CATEGORIES = {
|
|
|
28190
29117
|
registerSendWhatsapp: "gateway",
|
|
28191
29118
|
registerQueryConversations: "gateway",
|
|
28192
29119
|
registerQueryCompanyBrain: "gateway",
|
|
29120
|
+
registerCompanyActions: "gateway",
|
|
28193
29121
|
// admin
|
|
28194
29122
|
registerConfig: "admin",
|
|
28195
29123
|
registerDeployClient: "admin",
|
|
@@ -28400,6 +29328,7 @@ function registerAllTools(server2) {
|
|
|
28400
29328
|
gate("registerGetDecision", registerGetDecision);
|
|
28401
29329
|
gate("registerCreateBugReport", registerCreateBugReport);
|
|
28402
29330
|
gate("registerSupportTools", registerSupportTools);
|
|
29331
|
+
gate("registerCliParityTools", registerCliParityTools);
|
|
28403
29332
|
gate("registerGetSessionEvents", registerGetSessionEvents);
|
|
28404
29333
|
gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
|
|
28405
29334
|
gate("registerCodeContext", registerCodeContext);
|
|
@@ -28453,6 +29382,7 @@ function registerAllTools(server2) {
|
|
|
28453
29382
|
}
|
|
28454
29383
|
if (exposeLegacyGateway) {
|
|
28455
29384
|
gate("registerQueryCompanyBrain", registerQueryCompanyBrain);
|
|
29385
|
+
gate("registerCompanyActions", registerCompanyActions);
|
|
28456
29386
|
}
|
|
28457
29387
|
}
|
|
28458
29388
|
|
|
@@ -28486,9 +29416,9 @@ async function withTrace(toolName, fn) {
|
|
|
28486
29416
|
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
28487
29417
|
span.setAttribute("mcp.tool.name", toolName);
|
|
28488
29418
|
try {
|
|
28489
|
-
const
|
|
29419
|
+
const result3 = await fn();
|
|
28490
29420
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
28491
|
-
return
|
|
29421
|
+
return result3;
|
|
28492
29422
|
} catch (err) {
|
|
28493
29423
|
span.setStatus({
|
|
28494
29424
|
code: SpanStatusCode.ERROR,
|
|
@@ -28573,16 +29503,16 @@ try {
|
|
|
28573
29503
|
}
|
|
28574
29504
|
}, 3e4);
|
|
28575
29505
|
_ppidWatchdog.unref();
|
|
28576
|
-
const MCP_VERSION_PATH =
|
|
29506
|
+
const MCP_VERSION_PATH = path54.join(os20.homedir(), ".exe-os", "mcp-version");
|
|
28577
29507
|
let _currentMcpVersion = null;
|
|
28578
29508
|
try {
|
|
28579
|
-
_currentMcpVersion =
|
|
29509
|
+
_currentMcpVersion = existsSync41(MCP_VERSION_PATH) ? readFileSync36(MCP_VERSION_PATH, "utf8").trim() : null;
|
|
28580
29510
|
} catch {
|
|
28581
29511
|
}
|
|
28582
29512
|
const _versionWatchdog = setInterval(() => {
|
|
28583
29513
|
try {
|
|
28584
|
-
if (!
|
|
28585
|
-
const diskVersion =
|
|
29514
|
+
if (!existsSync41(MCP_VERSION_PATH)) return;
|
|
29515
|
+
const diskVersion = readFileSync36(MCP_VERSION_PATH, "utf8").trim();
|
|
28586
29516
|
if (_currentMcpVersion && diskVersion !== _currentMcpVersion) {
|
|
28587
29517
|
process.stderr.write(
|
|
28588
29518
|
`[exe-os] MCP version changed (${_currentMcpVersion} \u2192 ${diskVersion}). Hot-reloading...
|
|
@@ -28599,10 +29529,10 @@ try {
|
|
|
28599
29529
|
_backfillTimer = setInterval(async () => {
|
|
28600
29530
|
try {
|
|
28601
29531
|
const client = getClient();
|
|
28602
|
-
const
|
|
29532
|
+
const result3 = await client.execute(
|
|
28603
29533
|
"SELECT COUNT(*) as cnt FROM memories WHERE vector IS NULL"
|
|
28604
29534
|
);
|
|
28605
|
-
const nullCount = Number(
|
|
29535
|
+
const nullCount = Number(result3.rows[0]?.cnt) || 0;
|
|
28606
29536
|
if (nullCount === 0) return;
|
|
28607
29537
|
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
28608
29538
|
if (!tryAcquireWorkerSlot2()) {
|
|
@@ -28614,15 +29544,15 @@ try {
|
|
|
28614
29544
|
`[exe-os] Periodic backfill: ${nullCount} NULL vectors \u2014 spawning job
|
|
28615
29545
|
`
|
|
28616
29546
|
);
|
|
28617
|
-
const thisFile =
|
|
28618
|
-
const backfillPath =
|
|
28619
|
-
|
|
29547
|
+
const thisFile = fileURLToPath7(import.meta.url);
|
|
29548
|
+
const backfillPath = path54.resolve(
|
|
29549
|
+
path54.dirname(thisFile),
|
|
28620
29550
|
"../bin/backfill-vectors.js"
|
|
28621
29551
|
);
|
|
28622
|
-
if (
|
|
29552
|
+
if (existsSync41(backfillPath)) {
|
|
28623
29553
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
28624
|
-
const logPath =
|
|
28625
|
-
mkdirSync23(
|
|
29554
|
+
const logPath = path54.join(exeDir, "workers.log");
|
|
29555
|
+
mkdirSync23(path54.dirname(logPath), { recursive: true });
|
|
28626
29556
|
let logFd = "ignore";
|
|
28627
29557
|
try {
|
|
28628
29558
|
logFd = openSync4(logPath, "a");
|