@askexenow/exe-os 0.9.93 → 0.9.94
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/compose/docker-compose.yml +1 -0
- package/dist/bin/agentic-ontology-backfill.js +65 -8
- package/dist/bin/agentic-reflection-backfill.js +54 -3
- package/dist/bin/agentic-semantic-label.js +54 -3
- package/dist/bin/backfill-conversations.js +69 -9
- package/dist/bin/backfill-responses.js +69 -9
- package/dist/bin/backfill-vectors.js +54 -3
- package/dist/bin/bulk-sync-postgres.js +66 -8
- package/dist/bin/cleanup-stale-review-tasks.js +118 -13
- package/dist/bin/cli.js +1558 -466
- package/dist/bin/customer-readiness.js +51 -0
- package/dist/bin/exe-agent.js +17 -3
- package/dist/bin/exe-assign.js +75 -9
- package/dist/bin/exe-boot.js +111 -12
- package/dist/bin/exe-call.js +17 -3
- package/dist/bin/exe-cloud.js +76 -10
- package/dist/bin/exe-dispatch.js +133 -18
- package/dist/bin/exe-doctor.js +75 -9
- package/dist/bin/exe-export-behaviors.js +75 -9
- package/dist/bin/exe-forget.js +94 -9
- package/dist/bin/exe-gateway.js +132 -18
- package/dist/bin/exe-heartbeat.js +118 -13
- package/dist/bin/exe-kill.js +75 -9
- package/dist/bin/exe-launch-agent.js +75 -9
- package/dist/bin/exe-new-employee.js +18 -4
- package/dist/bin/exe-pending-messages.js +118 -13
- package/dist/bin/exe-pending-notifications.js +118 -13
- package/dist/bin/exe-pending-reviews.js +118 -13
- package/dist/bin/exe-rename.js +75 -9
- package/dist/bin/exe-review.js +75 -9
- package/dist/bin/exe-search.js +100 -9
- package/dist/bin/exe-session-cleanup.js +133 -18
- package/dist/bin/exe-settings.js +1 -0
- package/dist/bin/exe-start-codex.js +65 -8
- package/dist/bin/exe-start-opencode.js +65 -8
- package/dist/bin/exe-status.js +118 -13
- package/dist/bin/exe-support.js +1 -0
- package/dist/bin/exe-team.js +75 -9
- package/dist/bin/git-sweep.js +133 -18
- package/dist/bin/graph-backfill.js +65 -8
- package/dist/bin/graph-export.js +75 -9
- package/dist/bin/intercom-check.js +133 -18
- package/dist/bin/scan-tasks.js +133 -18
- package/dist/bin/setup.js +55 -4
- package/dist/bin/shard-migrate.js +65 -8
- package/dist/bin/stack-update.js +5 -6
- package/dist/bin/update.js +1 -1
- package/dist/gateway/index.js +133 -18
- package/dist/hooks/bug-report-worker.js +133 -18
- package/dist/hooks/codex-stop-task-finalizer.js +123 -14
- package/dist/hooks/commit-complete.js +133 -18
- package/dist/hooks/error-recall.js +100 -9
- package/dist/hooks/ingest.js +75 -9
- package/dist/hooks/instructions-loaded.js +75 -9
- package/dist/hooks/notification.js +75 -9
- package/dist/hooks/post-compact.js +310 -50
- package/dist/hooks/post-tool-combined.js +433 -13
- package/dist/hooks/pre-compact.js +133 -18
- package/dist/hooks/pre-tool-use.js +118 -13
- package/dist/hooks/prompt-submit.js +191 -19
- package/dist/hooks/session-end.js +133 -18
- package/dist/hooks/session-start.js +143 -13
- package/dist/hooks/stop.js +118 -13
- package/dist/hooks/subagent-stop.js +118 -13
- package/dist/hooks/summary-worker.js +96 -7
- package/dist/index.js +133 -18
- package/dist/lib/cloud-sync.js +38 -0
- package/dist/lib/consolidation.js +3 -1
- package/dist/lib/database.js +37 -0
- package/dist/lib/db.js +37 -0
- package/dist/lib/device-registry.js +37 -0
- package/dist/lib/employee-templates.js +17 -3
- package/dist/lib/exe-daemon.js +913 -42
- package/dist/lib/hybrid-search.js +100 -9
- package/dist/lib/license.js +1 -1
- package/dist/lib/messaging.js +40 -4
- package/dist/lib/schedules.js +54 -3
- package/dist/lib/store.js +75 -9
- package/dist/lib/tasks.js +58 -9
- package/dist/lib/tmux-routing.js +58 -9
- package/dist/mcp/server.js +875 -42
- package/dist/mcp/tools/create-task.js +67 -12
- package/dist/mcp/tools/list-tasks.js +46 -5
- package/dist/mcp/tools/send-message.js +40 -4
- package/dist/mcp/tools/update-task.js +58 -9
- package/dist/runtime/index.js +133 -18
- package/dist/tui/App.js +132 -18
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -3243,6 +3243,20 @@ async function ensureSchema() {
|
|
|
3243
3243
|
});
|
|
3244
3244
|
} catch {
|
|
3245
3245
|
}
|
|
3246
|
+
try {
|
|
3247
|
+
await client.execute({
|
|
3248
|
+
sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
|
|
3249
|
+
args: []
|
|
3250
|
+
});
|
|
3251
|
+
} catch {
|
|
3252
|
+
}
|
|
3253
|
+
try {
|
|
3254
|
+
await client.execute({
|
|
3255
|
+
sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
|
|
3256
|
+
args: []
|
|
3257
|
+
});
|
|
3258
|
+
} catch {
|
|
3259
|
+
}
|
|
3246
3260
|
await client.executeMultiple(`
|
|
3247
3261
|
CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
|
|
3248
3262
|
content_text,
|
|
@@ -3494,6 +3508,22 @@ async function ensureSchema() {
|
|
|
3494
3508
|
);
|
|
3495
3509
|
} catch {
|
|
3496
3510
|
}
|
|
3511
|
+
for (const col of [
|
|
3512
|
+
"ALTER TABLE memories ADD COLUMN valid_from TEXT",
|
|
3513
|
+
"ALTER TABLE memories ADD COLUMN invalid_at TEXT"
|
|
3514
|
+
]) {
|
|
3515
|
+
try {
|
|
3516
|
+
await client.execute(col);
|
|
3517
|
+
} catch {
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
try {
|
|
3521
|
+
await client.execute({
|
|
3522
|
+
sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
|
|
3523
|
+
args: []
|
|
3524
|
+
});
|
|
3525
|
+
} catch {
|
|
3526
|
+
}
|
|
3497
3527
|
try {
|
|
3498
3528
|
await client.execute({
|
|
3499
3529
|
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
@@ -3536,6 +3566,13 @@ async function ensureSchema() {
|
|
|
3536
3566
|
} catch {
|
|
3537
3567
|
}
|
|
3538
3568
|
}
|
|
3569
|
+
try {
|
|
3570
|
+
await client.execute({
|
|
3571
|
+
sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
|
|
3572
|
+
args: []
|
|
3573
|
+
});
|
|
3574
|
+
} catch {
|
|
3575
|
+
}
|
|
3539
3576
|
try {
|
|
3540
3577
|
await client.execute({
|
|
3541
3578
|
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
@@ -4843,6 +4880,20 @@ var init_platform_procedures = __esm({
|
|
|
4843
4880
|
priority: "p1",
|
|
4844
4881
|
content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
4845
4882
|
},
|
|
4883
|
+
// --- Tool guidance ---
|
|
4884
|
+
{
|
|
4885
|
+
title: "How to use company_actions \u2014 execute business actions through gateway connectors",
|
|
4886
|
+
domain: "tools",
|
|
4887
|
+
priority: "p2",
|
|
4888
|
+
content: "The company_actions tool executes business actions through gateway connectors (e.g. send WhatsApp, trigger workflows, update CRM). It routes through the exe-gateway on the VPS. Actions are defined by the customer's gateway configuration \u2014 each connector (WhatsApp, Shopify, email, etc.) exposes specific actions. Use query_company_brain to find available data first, then company_actions to act on it. Requires gateway auth token. Read-only founders should NOT use this \u2014 it mutates external state."
|
|
4889
|
+
},
|
|
4890
|
+
// --- Release awareness ---
|
|
4891
|
+
{
|
|
4892
|
+
title: "What's New check \u2014 surface new features after update",
|
|
4893
|
+
domain: "support",
|
|
4894
|
+
priority: "p1",
|
|
4895
|
+
content: "Once per session (COO boot only, never repeat), check if the installed exe-os version is newer than the last session. If it is, read the bundled release-notes.json (at the package root) and surface a brief summary to the founder: 'Updated to exe-os vX.Y.Z \u2014 N new features, M fixes.' List the top 3 features by name. This helps the founder know what they got from the update. If release-notes.json doesn't exist or the version hasn't changed, skip silently. Never repeat this check in the same session."
|
|
4896
|
+
},
|
|
4846
4897
|
// --- Platform vs Customer ownership ---
|
|
4847
4898
|
{
|
|
4848
4899
|
title: "What the platform provides vs what you customize",
|
|
@@ -4931,13 +4982,13 @@ var init_platform_procedures = __esm({
|
|
|
4931
4982
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
4932
4983
|
domain: "tool-use",
|
|
4933
4984
|
priority: "p1",
|
|
4934
|
-
content: `memory(action="recall") / recall_my_memory: search your own memories (semantic + FTS). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. memory(action="commit") / commit_memory: high-importance memory that survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal memory window. Requires session_id + target_timestamp. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
|
|
4985
|
+
content: `memory(action="recall") / recall_my_memory: search your own memories (semantic + FTS). Supports as_of param for bi-temporal queries (what did I know at time X?), kind param to filter by memory type (decision, procedure, observation, raw, conversation, behavior). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. Supports kind param and procedure_for domain tag for procedure-type memories. memory(action="commit") / commit_memory: high-importance memory that survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal memory window. Requires session_id + target_timestamp. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. memory(action="supersede") / supersede: replace an old memory with a new version (old_id + new text). decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
|
|
4935
4986
|
},
|
|
4936
4987
|
{
|
|
4937
4988
|
title: "MCP tools \u2014 task orchestration",
|
|
4938
4989
|
domain: "tool-use",
|
|
4939
4990
|
priority: "p1",
|
|
4940
|
-
content:
|
|
4991
|
+
content: `task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Supports spawn_runtime and spawn_model params to override the agent's default runtime/model for a specific task. task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.`
|
|
4941
4992
|
},
|
|
4942
4993
|
{
|
|
4943
4994
|
title: "MCP tools \u2014 knowledge graph (GraphRAG)",
|
|
@@ -4967,7 +5018,7 @@ var init_platform_procedures = __esm({
|
|
|
4967
5018
|
title: "MCP tools \u2014 admin, config, and operations",
|
|
4968
5019
|
domain: "tool-use",
|
|
4969
5020
|
priority: "p1",
|
|
4970
|
-
content: 'config(action="list_employees"): view roster. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. mcp_ping(): daemon health + license status + tool usage stats.'
|
|
5021
|
+
content: 'config(action="list_employees"): view roster. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. Returns top-K tools ranked by relevance. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role identity. Returns drift score + recommendations. mcp_ping(): daemon health + license status + tool usage stats.'
|
|
4971
5022
|
}
|
|
4972
5023
|
];
|
|
4973
5024
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -5651,6 +5702,8 @@ async function writeMemory(record) {
|
|
|
5651
5702
|
source_type: record.source_type ?? null,
|
|
5652
5703
|
tier: record.tier ?? classifyTier(record),
|
|
5653
5704
|
supersedes_id: record.supersedes_id ?? null,
|
|
5705
|
+
valid_from: record.valid_from ?? record.timestamp,
|
|
5706
|
+
invalid_at: record.invalid_at ?? null,
|
|
5654
5707
|
draft: record.draft ? 1 : 0,
|
|
5655
5708
|
memory_type: memoryType,
|
|
5656
5709
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
@@ -5669,7 +5722,8 @@ async function writeMemory(record) {
|
|
|
5669
5722
|
token_cost: record.token_cost ?? null,
|
|
5670
5723
|
audience: record.audience ?? null,
|
|
5671
5724
|
language_type: record.language_type ?? inferLanguageType(record),
|
|
5672
|
-
parent_memory_id: record.parent_memory_id ?? null
|
|
5725
|
+
parent_memory_id: record.parent_memory_id ?? null,
|
|
5726
|
+
procedure_for: record.procedure_for ?? null
|
|
5673
5727
|
};
|
|
5674
5728
|
_pendingRecords.push(dbRow);
|
|
5675
5729
|
orgBus.emit({
|
|
@@ -5724,6 +5778,8 @@ async function flushBatch() {
|
|
|
5724
5778
|
const sourceType = row.source_type ?? null;
|
|
5725
5779
|
const tier = row.tier ?? 3;
|
|
5726
5780
|
const supersedesId = row.supersedes_id ?? null;
|
|
5781
|
+
const validFrom = row.valid_from ?? row.timestamp;
|
|
5782
|
+
const invalidAt = row.invalid_at ?? null;
|
|
5727
5783
|
const draft = row.draft ? 1 : 0;
|
|
5728
5784
|
const memoryType = row.memory_type ?? "raw";
|
|
5729
5785
|
const trajectory = row.trajectory ?? null;
|
|
@@ -5743,15 +5799,16 @@ async function flushBatch() {
|
|
|
5743
5799
|
const audience = row.audience ?? null;
|
|
5744
5800
|
const languageType = row.language_type ?? null;
|
|
5745
5801
|
const parentMemoryId = row.parent_memory_id ?? null;
|
|
5802
|
+
const procedureFor = row.procedure_for ?? null;
|
|
5746
5803
|
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
5747
5804
|
tool_name, project_name,
|
|
5748
5805
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
5749
5806
|
confidence, last_accessed,
|
|
5750
5807
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
5751
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
5808
|
+
source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
|
|
5752
5809
|
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
5753
5810
|
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
5754
|
-
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
5811
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
|
|
5755
5812
|
const metaArgs = [
|
|
5756
5813
|
intent,
|
|
5757
5814
|
outcome,
|
|
@@ -5767,7 +5824,8 @@ async function flushBatch() {
|
|
|
5767
5824
|
tokenCost,
|
|
5768
5825
|
audience,
|
|
5769
5826
|
languageType,
|
|
5770
|
-
parentMemoryId
|
|
5827
|
+
parentMemoryId,
|
|
5828
|
+
procedureFor
|
|
5771
5829
|
];
|
|
5772
5830
|
const baseArgs = [
|
|
5773
5831
|
row.id,
|
|
@@ -5796,6 +5854,8 @@ async function flushBatch() {
|
|
|
5796
5854
|
sourceType,
|
|
5797
5855
|
tier,
|
|
5798
5856
|
supersedesId,
|
|
5857
|
+
validFrom,
|
|
5858
|
+
invalidAt,
|
|
5799
5859
|
draft,
|
|
5800
5860
|
memoryType,
|
|
5801
5861
|
trajectory,
|
|
@@ -5803,8 +5863,8 @@ async function flushBatch() {
|
|
|
5803
5863
|
];
|
|
5804
5864
|
return {
|
|
5805
5865
|
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
5806
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
5807
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5866
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
5867
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5808
5868
|
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
5809
5869
|
};
|
|
5810
5870
|
};
|
|
@@ -5920,6 +5980,12 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
5920
5980
|
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
5921
5981
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
5922
5982
|
const args = [agentId];
|
|
5983
|
+
if (options?.asOf) {
|
|
5984
|
+
sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
|
|
5985
|
+
args.push(options.asOf, options.asOf);
|
|
5986
|
+
} else {
|
|
5987
|
+
sql += ` AND invalid_at IS NULL`;
|
|
5988
|
+
}
|
|
5923
5989
|
const scope = buildWikiScopeFilter(options, "");
|
|
5924
5990
|
sql += scope.clause;
|
|
5925
5991
|
args.push(...scope.args);
|
|
@@ -7176,6 +7242,19 @@ __export(hybrid_search_exports, {
|
|
|
7176
7242
|
rrfMerge: () => rrfMerge,
|
|
7177
7243
|
rrfMergeMulti: () => rrfMergeMulti
|
|
7178
7244
|
});
|
|
7245
|
+
function buildTemporalFilter(options, columnPrefix) {
|
|
7246
|
+
const asOf = options?.asOf;
|
|
7247
|
+
if (asOf) {
|
|
7248
|
+
return {
|
|
7249
|
+
clause: ` AND (${columnPrefix}valid_from IS NULL OR ${columnPrefix}valid_from <= ?) AND (${columnPrefix}invalid_at IS NULL OR ${columnPrefix}invalid_at > ?)`,
|
|
7250
|
+
args: [asOf, asOf]
|
|
7251
|
+
};
|
|
7252
|
+
}
|
|
7253
|
+
return {
|
|
7254
|
+
clause: ` AND ${columnPrefix}invalid_at IS NULL`,
|
|
7255
|
+
args: []
|
|
7256
|
+
};
|
|
7257
|
+
}
|
|
7179
7258
|
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
7180
7259
|
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
7181
7260
|
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
@@ -7408,6 +7487,9 @@ async function estimateCardinality(agentId, options) {
|
|
|
7408
7487
|
AND COALESCE(status, 'active') = 'active'
|
|
7409
7488
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
7410
7489
|
const args = [agentId];
|
|
7490
|
+
const temporal = buildTemporalFilter(options, "");
|
|
7491
|
+
sql += temporal.clause;
|
|
7492
|
+
args.push(...temporal.args);
|
|
7411
7493
|
const rawVisibility = buildRawVisibilityFilter(options, "");
|
|
7412
7494
|
sql += rawVisibility.clause;
|
|
7413
7495
|
args.push(...rawVisibility.args);
|
|
@@ -7536,6 +7618,9 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
7536
7618
|
AND m.agent_id = ?${statusFilter}${draftFilter}
|
|
7537
7619
|
AND COALESCE(m.confidence, 0.7) >= 0.3`;
|
|
7538
7620
|
const args = [matchExpr, agentId];
|
|
7621
|
+
const temporal = buildTemporalFilter(options, "m.");
|
|
7622
|
+
sql += temporal.clause;
|
|
7623
|
+
args.push(...temporal.args);
|
|
7539
7624
|
const scope = buildWikiScopeFilter(options, "m.");
|
|
7540
7625
|
sql += scope.clause;
|
|
7541
7626
|
args.push(...scope.args);
|
|
@@ -7648,6 +7733,9 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
7648
7733
|
WHERE agent_id = ?${statusFilter}${draftFilter}
|
|
7649
7734
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
7650
7735
|
const args = [agentId];
|
|
7736
|
+
const temporal = buildTemporalFilter(options, "");
|
|
7737
|
+
sql += temporal.clause;
|
|
7738
|
+
args.push(...temporal.args);
|
|
7651
7739
|
const scope = buildWikiScopeFilter(options, "");
|
|
7652
7740
|
sql += scope.clause;
|
|
7653
7741
|
args.push(...scope.args);
|
|
@@ -7748,6 +7836,9 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
|
7748
7836
|
AND json_extract(trajectory, '$.tool') = ?
|
|
7749
7837
|
AND agent_id = ?${statusFilter}${draftFilter}`;
|
|
7750
7838
|
const args = [toolName, agentId];
|
|
7839
|
+
const temporal = buildTemporalFilter(options, "");
|
|
7840
|
+
sql += temporal.clause;
|
|
7841
|
+
args.push(...temporal.args);
|
|
7751
7842
|
const rawVisibility = buildRawVisibilityFilter(options, "");
|
|
7752
7843
|
sql += rawVisibility.clause;
|
|
7753
7844
|
args.push(...rawVisibility.args);
|
|
@@ -8509,7 +8600,7 @@ var init_license = __esm({
|
|
|
8509
8600
|
LICENSE_PATH = path13.join(EXE_AI_DIR, "license.key");
|
|
8510
8601
|
CACHE_PATH = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
8511
8602
|
DEVICE_ID_PATH = path13.join(EXE_AI_DIR, "device-id");
|
|
8512
|
-
API_BASE = "https://askexe.com/cloud";
|
|
8603
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
|
|
8513
8604
|
RETRY_DELAY_MS = 500;
|
|
8514
8605
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
8515
8606
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -8825,6 +8916,7 @@ async function selectUnconsolidated(client, limit = 200) {
|
|
|
8825
8916
|
sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
|
|
8826
8917
|
FROM memories
|
|
8827
8918
|
WHERE consolidated = 0
|
|
8919
|
+
AND COALESCE(memory_type, 'raw') != 'procedure'
|
|
8828
8920
|
ORDER BY timestamp DESC
|
|
8829
8921
|
LIMIT ?`,
|
|
8830
8922
|
args: [limit]
|
|
@@ -9168,7 +9260,8 @@ async function isUserIdle(client, idleMinutes = 30) {
|
|
|
9168
9260
|
async function countUnconsolidated(client) {
|
|
9169
9261
|
const result3 = await client.execute({
|
|
9170
9262
|
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
9171
|
-
WHERE consolidated = 0
|
|
9263
|
+
WHERE consolidated = 0
|
|
9264
|
+
AND COALESCE(memory_type, 'raw') != 'procedure'`,
|
|
9172
9265
|
args: []
|
|
9173
9266
|
});
|
|
9174
9267
|
return Number(result3.rows[0]?.cnt ?? 0);
|
|
@@ -10459,6 +10552,7 @@ function resolveExeSession() {
|
|
|
10459
10552
|
const mySession = getMySession();
|
|
10460
10553
|
if (!mySession) return null;
|
|
10461
10554
|
const fromSessionName = extractRootExe(mySession);
|
|
10555
|
+
let candidate = null;
|
|
10462
10556
|
try {
|
|
10463
10557
|
const key = getSessionKey();
|
|
10464
10558
|
const parentExe = getParentExe(key);
|
|
@@ -10469,13 +10563,47 @@ function resolveExeSession() {
|
|
|
10469
10563
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
10470
10564
|
`
|
|
10471
10565
|
);
|
|
10472
|
-
|
|
10566
|
+
candidate = fromSessionName;
|
|
10567
|
+
} else {
|
|
10568
|
+
candidate = fromCache;
|
|
10473
10569
|
}
|
|
10474
|
-
return fromCache;
|
|
10475
10570
|
}
|
|
10476
10571
|
} catch {
|
|
10477
10572
|
}
|
|
10478
|
-
|
|
10573
|
+
if (!candidate) {
|
|
10574
|
+
candidate = fromSessionName ?? mySession;
|
|
10575
|
+
}
|
|
10576
|
+
if (candidate && isRootSession(candidate)) {
|
|
10577
|
+
try {
|
|
10578
|
+
const transport = getTransport();
|
|
10579
|
+
const liveSessions = transport.listSessions();
|
|
10580
|
+
if (!liveSessions.includes(candidate)) {
|
|
10581
|
+
const liveRoots = liveSessions.filter((s) => isRootSession(s));
|
|
10582
|
+
if (liveRoots.length === 1) {
|
|
10583
|
+
process.stderr.write(
|
|
10584
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
|
|
10585
|
+
`
|
|
10586
|
+
);
|
|
10587
|
+
return liveRoots[0];
|
|
10588
|
+
} else if (liveRoots.length > 1) {
|
|
10589
|
+
const base = candidate.replace(/\d+$/, "");
|
|
10590
|
+
const match = liveRoots.find((s) => s.startsWith(base));
|
|
10591
|
+
const chosen = match ?? liveRoots[0];
|
|
10592
|
+
process.stderr.write(
|
|
10593
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
|
|
10594
|
+
`
|
|
10595
|
+
);
|
|
10596
|
+
return chosen;
|
|
10597
|
+
}
|
|
10598
|
+
process.stderr.write(
|
|
10599
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
|
|
10600
|
+
`
|
|
10601
|
+
);
|
|
10602
|
+
}
|
|
10603
|
+
} catch {
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10606
|
+
return candidate;
|
|
10479
10607
|
}
|
|
10480
10608
|
function isEmployeeAlive(sessionName) {
|
|
10481
10609
|
return getTransport().isAlive(sessionName);
|
|
@@ -10877,7 +11005,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
10877
11005
|
}
|
|
10878
11006
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
10879
11007
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
10880
|
-
const
|
|
11008
|
+
const baseRtConfig = getAgentRuntime(employeeName);
|
|
11009
|
+
const agentRtConfig = {
|
|
11010
|
+
...baseRtConfig,
|
|
11011
|
+
...opts?.runtimeOverride ? { runtime: opts.runtimeOverride } : {},
|
|
11012
|
+
...opts?.modelOverride ? { model: opts.modelOverride } : {}
|
|
11013
|
+
};
|
|
10881
11014
|
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
10882
11015
|
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
10883
11016
|
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
@@ -11489,8 +11622,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
11489
11622
|
const complexity = input.complexity ?? "standard";
|
|
11490
11623
|
const sessionScope = earlySessionScope;
|
|
11491
11624
|
await client.execute({
|
|
11492
|
-
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
|
|
11493
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
11625
|
+
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, spawn_runtime, spawn_model, created_at, updated_at)
|
|
11626
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
11494
11627
|
args: [
|
|
11495
11628
|
id,
|
|
11496
11629
|
input.title,
|
|
@@ -11510,6 +11643,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
11510
11643
|
0,
|
|
11511
11644
|
null,
|
|
11512
11645
|
sessionScope,
|
|
11646
|
+
input.spawnRuntime ?? null,
|
|
11647
|
+
input.spawnModel ?? null,
|
|
11513
11648
|
now,
|
|
11514
11649
|
now
|
|
11515
11650
|
]
|
|
@@ -11566,7 +11701,9 @@ ${input.context}
|
|
|
11566
11701
|
budgetTokens: input.budgetTokens ?? null,
|
|
11567
11702
|
budgetFallbackModel: input.budgetFallbackModel ?? null,
|
|
11568
11703
|
tokensUsed: 0,
|
|
11569
|
-
tokensWarnedAt: null
|
|
11704
|
+
tokensWarnedAt: null,
|
|
11705
|
+
spawnRuntime: input.spawnRuntime ?? null,
|
|
11706
|
+
spawnModel: input.spawnModel ?? null
|
|
11570
11707
|
};
|
|
11571
11708
|
}
|
|
11572
11709
|
async function listTasks(input) {
|
|
@@ -11616,7 +11753,9 @@ async function listTasks(input) {
|
|
|
11616
11753
|
budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
|
|
11617
11754
|
budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
|
|
11618
11755
|
tokensUsed: Number(r.tokens_used ?? 0),
|
|
11619
|
-
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
|
|
11756
|
+
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
|
|
11757
|
+
spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
|
|
11758
|
+
spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
|
|
11620
11759
|
}));
|
|
11621
11760
|
}
|
|
11622
11761
|
function isTmuxSessionAlive(identifier) {
|
|
@@ -12566,6 +12705,8 @@ async function updateTask(input) {
|
|
|
12566
12705
|
budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
|
|
12567
12706
|
tokensUsed: Number(row.tokens_used ?? 0),
|
|
12568
12707
|
tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
|
|
12708
|
+
spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
|
|
12709
|
+
spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
|
|
12569
12710
|
nextTask
|
|
12570
12711
|
};
|
|
12571
12712
|
}
|
|
@@ -14397,7 +14538,11 @@ function registerRecallMyMemory(server2) {
|
|
|
14397
14538
|
include_source: z.boolean().optional().default(false).describe(
|
|
14398
14539
|
"When true, attach parent document metadata (filename, mime, source_type) to each result. Default false."
|
|
14399
14540
|
),
|
|
14400
|
-
retrieval_mode: z.enum(RETRIEVAL_MODES).optional().default("all").describe(`Typed retrieval mode. ${formatRetrievalModes()}`)
|
|
14541
|
+
retrieval_mode: z.enum(RETRIEVAL_MODES).optional().default("all").describe(`Typed retrieval mode. ${formatRetrievalModes()}`),
|
|
14542
|
+
kind: z.string().optional().describe(
|
|
14543
|
+
"Filter by memory_type: decision, procedure, observation, raw, conversation, behavior."
|
|
14544
|
+
),
|
|
14545
|
+
as_of: z.string().optional().describe("ISO 8601 timestamp for time-travel query. Returns facts valid at that point in time.")
|
|
14401
14546
|
}
|
|
14402
14547
|
},
|
|
14403
14548
|
async ({
|
|
@@ -14414,7 +14559,9 @@ function registerRecallMyMemory(server2) {
|
|
|
14414
14559
|
workspace_id,
|
|
14415
14560
|
user_id,
|
|
14416
14561
|
include_source,
|
|
14417
|
-
retrieval_mode
|
|
14562
|
+
retrieval_mode,
|
|
14563
|
+
kind,
|
|
14564
|
+
as_of
|
|
14418
14565
|
}) => {
|
|
14419
14566
|
try {
|
|
14420
14567
|
if (!recent && !query) {
|
|
@@ -14434,7 +14581,9 @@ function registerRecallMyMemory(server2) {
|
|
|
14434
14581
|
workspaceId: workspace_id,
|
|
14435
14582
|
includeSource: include_source,
|
|
14436
14583
|
includeDrafts: true,
|
|
14437
|
-
...user_id !== void 0 ? { userId: user_id } : {}
|
|
14584
|
+
...user_id !== void 0 ? { userId: user_id } : {},
|
|
14585
|
+
...kind ? { memoryType: kind } : {},
|
|
14586
|
+
...as_of ? { asOf: as_of } : {}
|
|
14438
14587
|
}, retrieval_mode);
|
|
14439
14588
|
let results;
|
|
14440
14589
|
if (recent) {
|
|
@@ -14614,10 +14763,12 @@ function registerStoreMemory(server2) {
|
|
|
14614
14763
|
project_name: z3.string().optional().describe("Project name"),
|
|
14615
14764
|
has_error: z3.boolean().optional().default(false).describe("Whether this memory is error-related"),
|
|
14616
14765
|
source_path: z3.string().optional().describe("Original file path, URL, or document reference"),
|
|
14617
|
-
source_type: z3.string().optional().describe("Content type: text, pdf, image, audio, video, url, document")
|
|
14766
|
+
source_type: z3.string().optional().describe("Content type: text, pdf, image, audio, video, url, document"),
|
|
14767
|
+
kind: z3.string().optional().describe("Memory type: decision, procedure, observation, raw, conversation, behavior"),
|
|
14768
|
+
procedure_for: z3.string().optional().describe("Domain this procedure applies to (only for kind='procedure')")
|
|
14618
14769
|
}
|
|
14619
14770
|
},
|
|
14620
|
-
async ({ text: text3, query, tool_name, project_name, has_error, source_path, source_type }) => {
|
|
14771
|
+
async ({ text: text3, query, tool_name, project_name, has_error, source_path, source_type, kind, procedure_for }) => {
|
|
14621
14772
|
const resolvedText = text3 ?? query;
|
|
14622
14773
|
if (!resolvedText) {
|
|
14623
14774
|
return {
|
|
@@ -14662,7 +14813,9 @@ function registerStoreMemory(server2) {
|
|
|
14662
14813
|
raw_text: resolvedText,
|
|
14663
14814
|
vector,
|
|
14664
14815
|
source_path: source_path ?? null,
|
|
14665
|
-
source_type: source_type ?? null
|
|
14816
|
+
source_type: source_type ?? null,
|
|
14817
|
+
memory_type: kind ?? void 0,
|
|
14818
|
+
procedure_for: kind === "procedure" ? procedure_for ?? null : null
|
|
14666
14819
|
});
|
|
14667
14820
|
await flushBatch();
|
|
14668
14821
|
if (needsBackfill) {
|
|
@@ -14813,9 +14966,9 @@ function truncate(text3, max) {
|
|
|
14813
14966
|
const marker = ` \u2026 [truncated; +${text3.length - max} chars]`;
|
|
14814
14967
|
return text3.slice(0, Math.max(0, max - marker.length)) + marker;
|
|
14815
14968
|
}
|
|
14816
|
-
async function searchMemories2(query, agentId, limit) {
|
|
14969
|
+
async function searchMemories2(query, agentId, limit, memoryType) {
|
|
14817
14970
|
const { hybridSearch: hybridSearch2 } = await Promise.resolve().then(() => (init_hybrid_search(), hybrid_search_exports));
|
|
14818
|
-
const results = await hybridSearch2(query, agentId, { limit });
|
|
14971
|
+
const results = await hybridSearch2(query, agentId, { limit, memoryType });
|
|
14819
14972
|
return results.map((r, i) => ({
|
|
14820
14973
|
source: "memory",
|
|
14821
14974
|
score: 1 - i / Math.max(results.length, 1),
|
|
@@ -14939,7 +15092,7 @@ async function unifiedSearch(query, agentId, opts) {
|
|
|
14939
15092
|
const perSourceLimit = Math.max(Math.ceil(limit * 1.5), 5);
|
|
14940
15093
|
const promises = [];
|
|
14941
15094
|
if (sources.includes("memory")) {
|
|
14942
|
-
promises.push(searchMemories2(query, agentId, perSourceLimit));
|
|
15095
|
+
promises.push(searchMemories2(query, agentId, perSourceLimit, opts?.memoryType));
|
|
14943
15096
|
}
|
|
14944
15097
|
if (sources.includes("conversations")) {
|
|
14945
15098
|
promises.push(searchConversations(query, perSourceLimit));
|
|
@@ -14974,7 +15127,8 @@ function registerSearchEverything(server2) {
|
|
|
14974
15127
|
sources: z5.array(z5.enum(["memory", "conversations", "wiki"])).optional().describe(
|
|
14975
15128
|
"Which sources to search (default: all). Options: memory, conversations, wiki"
|
|
14976
15129
|
),
|
|
14977
|
-
limit: z5.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return (default 10, max 50)")
|
|
15130
|
+
limit: z5.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return (default 10, max 50)"),
|
|
15131
|
+
kind: z5.string().optional().describe("Filter memories by memory_type: decision, procedure, observation, raw, conversation, behavior.")
|
|
14978
15132
|
}
|
|
14979
15133
|
},
|
|
14980
15134
|
async (params) => {
|
|
@@ -14983,7 +15137,8 @@ function registerSearchEverything(server2) {
|
|
|
14983
15137
|
const sources = params.sources;
|
|
14984
15138
|
const results = await unifiedSearch(params.query, agentId, {
|
|
14985
15139
|
limit: params.limit,
|
|
14986
|
-
sources
|
|
15140
|
+
sources,
|
|
15141
|
+
memoryType: params.kind
|
|
14987
15142
|
});
|
|
14988
15143
|
if (results.length === 0) {
|
|
14989
15144
|
return {
|
|
@@ -15356,7 +15511,8 @@ var ACTION_TO_LEGACY_TOOL = {
|
|
|
15356
15511
|
session_context: "get_session_context",
|
|
15357
15512
|
get_by_id: "get_memory_by_id",
|
|
15358
15513
|
consolidate: "consolidate_memories",
|
|
15359
|
-
cardinality: "get_memory_cardinality"
|
|
15514
|
+
cardinality: "get_memory_cardinality",
|
|
15515
|
+
supersede: "__inline_supersede__"
|
|
15360
15516
|
};
|
|
15361
15517
|
var REQUIRED_FIELDS = {
|
|
15362
15518
|
recall: [],
|
|
@@ -15367,7 +15523,8 @@ var REQUIRED_FIELDS = {
|
|
|
15367
15523
|
session_context: ["session_id", "target_timestamp"],
|
|
15368
15524
|
get_by_id: ["id"],
|
|
15369
15525
|
consolidate: [],
|
|
15370
|
-
cardinality: []
|
|
15526
|
+
cardinality: [],
|
|
15527
|
+
supersede: ["old_id", "text"]
|
|
15371
15528
|
};
|
|
15372
15529
|
function errorResult(text3) {
|
|
15373
15530
|
return {
|
|
@@ -15396,15 +15553,74 @@ function buildLegacyMemoryHandlers() {
|
|
|
15396
15553
|
registerGetMemoryCardinality(localServer);
|
|
15397
15554
|
return tools;
|
|
15398
15555
|
}
|
|
15556
|
+
async function handleSupersede(input) {
|
|
15557
|
+
const oldId = input.old_id;
|
|
15558
|
+
const text3 = input.text ?? input.query;
|
|
15559
|
+
if (!oldId || !text3) {
|
|
15560
|
+
return errorResult('memory action "supersede" requires old_id and text');
|
|
15561
|
+
}
|
|
15562
|
+
try {
|
|
15563
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
15564
|
+
const { getActiveAgent: getActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent(), active_agent_exports));
|
|
15565
|
+
const { writeMemory: writeMemory2, flushBatch: flushBatch2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
15566
|
+
const { randomUUID: randomUUID11 } = await import("crypto");
|
|
15567
|
+
const client = getClient2();
|
|
15568
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15569
|
+
const oldRow = await client.execute({
|
|
15570
|
+
sql: `SELECT id, agent_id, agent_role, project_name FROM memories WHERE id = ?`,
|
|
15571
|
+
args: [oldId]
|
|
15572
|
+
});
|
|
15573
|
+
if (oldRow.rows.length === 0) {
|
|
15574
|
+
return errorResult(`Memory not found: ${oldId}`);
|
|
15575
|
+
}
|
|
15576
|
+
await client.execute({
|
|
15577
|
+
sql: `UPDATE memories SET invalid_at = ? WHERE id = ?`,
|
|
15578
|
+
args: [now, oldId]
|
|
15579
|
+
});
|
|
15580
|
+
const { agentId, agentRole } = getActiveAgent2();
|
|
15581
|
+
const sessionId = process.env.SESSION_ID ?? "manual";
|
|
15582
|
+
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
15583
|
+
const projectName = input.project_name ?? getProjectName2(process.cwd());
|
|
15584
|
+
const newId = randomUUID11();
|
|
15585
|
+
await writeMemory2({
|
|
15586
|
+
id: newId,
|
|
15587
|
+
agent_id: agentId,
|
|
15588
|
+
agent_role: agentRole,
|
|
15589
|
+
session_id: sessionId,
|
|
15590
|
+
timestamp: now,
|
|
15591
|
+
tool_name: "manual",
|
|
15592
|
+
project_name: projectName,
|
|
15593
|
+
has_error: false,
|
|
15594
|
+
raw_text: text3,
|
|
15595
|
+
vector: null,
|
|
15596
|
+
valid_from: now,
|
|
15597
|
+
supersedes_id: oldId,
|
|
15598
|
+
importance: 7,
|
|
15599
|
+
memory_type: "adr"
|
|
15600
|
+
});
|
|
15601
|
+
await flushBatch2();
|
|
15602
|
+
return {
|
|
15603
|
+
content: [{
|
|
15604
|
+
type: "text",
|
|
15605
|
+
text: `Superseded memory ${oldId.slice(0, 8)}\u2026 \u2192 new memory ${newId.slice(0, 8)}\u2026
|
|
15606
|
+
Old memory invalid_at set to ${now}.
|
|
15607
|
+
New memory id: ${newId}`
|
|
15608
|
+
}]
|
|
15609
|
+
};
|
|
15610
|
+
} catch (err) {
|
|
15611
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15612
|
+
return errorResult(`Supersede failed: ${msg}`);
|
|
15613
|
+
}
|
|
15614
|
+
}
|
|
15399
15615
|
function registerMemory(server2) {
|
|
15400
15616
|
const legacyTools = buildLegacyMemoryHandlers();
|
|
15401
15617
|
server2.registerTool(
|
|
15402
15618
|
"memory",
|
|
15403
15619
|
{
|
|
15404
15620
|
title: "Memory",
|
|
15405
|
-
description: "Consolidated memory domain tool. Actions: recall, ask_team, store, commit, search, session_context, get_by_id, consolidate, cardinality. Legacy memory tools remain available during migration.",
|
|
15621
|
+
description: "Consolidated memory domain tool. Actions: recall, ask_team, store, commit, search, session_context, get_by_id, consolidate, cardinality, supersede. Legacy memory tools remain available during migration.",
|
|
15406
15622
|
inputSchema: {
|
|
15407
|
-
action: z10.enum(["recall", "ask_team", "store", "commit", "search", "session_context", "consolidate", "cardinality"]).describe("Memory operation to perform"),
|
|
15623
|
+
action: z10.enum(["recall", "ask_team", "store", "commit", "search", "session_context", "consolidate", "cardinality", "supersede"]).describe("Memory operation to perform"),
|
|
15408
15624
|
query: z10.string().optional().describe("Search query. Also accepted as store text alias."),
|
|
15409
15625
|
text: z10.string().optional().describe("Memory text for action=store"),
|
|
15410
15626
|
summary: z10.string().optional().describe("High-importance summary for action=commit"),
|
|
@@ -15437,7 +15653,11 @@ function registerMemory(server2) {
|
|
|
15437
15653
|
window_size: z10.number().optional().describe("Window size for action=session_context"),
|
|
15438
15654
|
max_clusters: z10.coerce.number().optional().describe("Max clusters for action=consolidate"),
|
|
15439
15655
|
model: z10.string().optional().describe("Model for action=consolidate"),
|
|
15440
|
-
agent_id: z10.string().optional().describe("Agent filter for action=cardinality")
|
|
15656
|
+
agent_id: z10.string().optional().describe("Agent filter for action=cardinality"),
|
|
15657
|
+
kind: z10.string().optional().describe("Filter by memory_type: decision, procedure, observation, raw, conversation, behavior. For action=recall and action=search."),
|
|
15658
|
+
procedure_for: z10.string().optional().describe("Domain this procedure applies to (only for storing procedure-type memories)."),
|
|
15659
|
+
as_of: z10.string().optional().describe("ISO 8601 timestamp for time-travel query. Returns facts valid at that point in time. For action=recall."),
|
|
15660
|
+
old_id: z10.string().optional().describe("UUID of memory to supersede. For action=supersede.")
|
|
15441
15661
|
}
|
|
15442
15662
|
},
|
|
15443
15663
|
async (input, extra) => {
|
|
@@ -15446,6 +15666,9 @@ function registerMemory(server2) {
|
|
|
15446
15666
|
if (!legacyToolName) {
|
|
15447
15667
|
return errorResult(`Unknown memory action: ${String(input.action)}`);
|
|
15448
15668
|
}
|
|
15669
|
+
if (action === "supersede") {
|
|
15670
|
+
return handleSupersede(input);
|
|
15671
|
+
}
|
|
15449
15672
|
const { action: _action, ...legacyArgs } = input;
|
|
15450
15673
|
const missing = missingRequiredFields(legacyArgs, REQUIRED_FIELDS[action]);
|
|
15451
15674
|
if (missing.length > 0) {
|
|
@@ -15631,10 +15854,12 @@ function registerCreateTask(server2) {
|
|
|
15631
15854
|
parent_task_id: z12.string().optional().describe("Parent task ID or slug. Links this task as a subtask."),
|
|
15632
15855
|
reviewer: z12.string().optional().describe("Who should review this task when done. Defaults to assigner."),
|
|
15633
15856
|
budget_tokens: z12.number().optional().describe("Max tokens allowed for this task (null = unlimited)"),
|
|
15634
|
-
budget_fallback_model: z12.string().optional().describe("Model to route to when budget is exhausted")
|
|
15857
|
+
budget_fallback_model: z12.string().optional().describe("Model to route to when budget is exhausted"),
|
|
15858
|
+
spawn_runtime: z12.string().optional().describe("Override runtime for spawned session (e.g., 'claude', 'codex', 'opencode')"),
|
|
15859
|
+
spawn_model: z12.string().optional().describe("Override model for spawned session (e.g., 'claude-sonnet-4.6', 'claude-haiku-4.5')")
|
|
15635
15860
|
}
|
|
15636
15861
|
},
|
|
15637
|
-
async ({ title, assigned_to, project_name, priority, complexity, context, blocked_by, parent_task_id, reviewer, budget_tokens, budget_fallback_model }) => {
|
|
15862
|
+
async ({ title, assigned_to, project_name, priority, complexity, context, blocked_by, parent_task_id, reviewer, budget_tokens, budget_fallback_model, spawn_runtime, spawn_model }) => {
|
|
15638
15863
|
if (!isCoordinatorName(assigned_to)) {
|
|
15639
15864
|
const license = getLicenseSync();
|
|
15640
15865
|
if (license.plan === "free") {
|
|
@@ -15662,6 +15887,8 @@ function registerCreateTask(server2) {
|
|
|
15662
15887
|
reviewer,
|
|
15663
15888
|
budgetTokens: budget_tokens,
|
|
15664
15889
|
budgetFallbackModel: budget_fallback_model,
|
|
15890
|
+
spawnRuntime: spawn_runtime,
|
|
15891
|
+
spawnModel: spawn_model,
|
|
15665
15892
|
// Skip internal dispatch — we handle it below with autoInstance
|
|
15666
15893
|
// support. Without this, createTask fires dispatchTaskToEmployee
|
|
15667
15894
|
// (no autoInstance) in parallel, racing with our ensureEmployee
|
|
@@ -15699,7 +15926,9 @@ function registerCreateTask(server2) {
|
|
|
15699
15926
|
const cfg = loadConfigSync2();
|
|
15700
15927
|
const result3 = ensureEmployee(assigned_to, exeSession, process.cwd(), {
|
|
15701
15928
|
autoInstance: useAutoInstance,
|
|
15702
|
-
maxAutoInstances: useAutoInstance ? cfg.sessionLifecycle.maxAutoInstances : void 0
|
|
15929
|
+
maxAutoInstances: useAutoInstance ? cfg.sessionLifecycle.maxAutoInstances : void 0,
|
|
15930
|
+
runtimeOverride: spawn_runtime ?? void 0,
|
|
15931
|
+
modelOverride: spawn_model ?? void 0
|
|
15703
15932
|
});
|
|
15704
15933
|
switch (result3.status) {
|
|
15705
15934
|
case "intercom_sent":
|
|
@@ -15929,6 +16158,14 @@ function registerGetTask(server2) {
|
|
|
15929
16158
|
}
|
|
15930
16159
|
}
|
|
15931
16160
|
}
|
|
16161
|
+
const spawnRuntime = row.spawn_runtime ? String(row.spawn_runtime) : null;
|
|
16162
|
+
const spawnModel = row.spawn_model ? String(row.spawn_model) : null;
|
|
16163
|
+
if (spawnRuntime || spawnModel) {
|
|
16164
|
+
lines.push("");
|
|
16165
|
+
lines.push("**Spawn Override:**");
|
|
16166
|
+
if (spawnRuntime) lines.push(`Runtime: ${spawnRuntime}`);
|
|
16167
|
+
if (spawnModel) lines.push(`Model: ${spawnModel}`);
|
|
16168
|
+
}
|
|
15932
16169
|
if (row.context) {
|
|
15933
16170
|
lines.push("", "## Context", "", contextText);
|
|
15934
16171
|
}
|
|
@@ -16377,6 +16614,8 @@ function registerTask(server2) {
|
|
|
16377
16614
|
reviewer: z19.string().optional().describe("Reviewer for action=create"),
|
|
16378
16615
|
budget_tokens: z19.number().optional().describe("Max token budget for action=create"),
|
|
16379
16616
|
budget_fallback_model: z19.string().optional().describe("Fallback model for action=create"),
|
|
16617
|
+
spawn_runtime: z19.string().optional().describe("Override runtime for spawned session (e.g., 'claude', 'codex', 'opencode') for action=create"),
|
|
16618
|
+
spawn_model: z19.string().optional().describe("Override model for spawned session (e.g., 'claude-sonnet-4.6') for action=create"),
|
|
16380
16619
|
status: z19.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).optional().describe("Status for action=update/list/close"),
|
|
16381
16620
|
result: z19.string().optional().describe("Result summary for action=update or action=close"),
|
|
16382
16621
|
step: z19.string().optional().describe("Checkpoint step for action=checkpoint"),
|
|
@@ -27607,6 +27846,15 @@ function registerCreateBugReport(server2) {
|
|
|
27607
27846
|
project_name,
|
|
27608
27847
|
send_upstream
|
|
27609
27848
|
}) => {
|
|
27849
|
+
if (!summary || summary.trim().length === 0) {
|
|
27850
|
+
return {
|
|
27851
|
+
content: [{
|
|
27852
|
+
type: "text",
|
|
27853
|
+
text: "Error: summary is required but was empty. If calling via support(action='create_bug'), use 'description' which maps to 'summary'."
|
|
27854
|
+
}],
|
|
27855
|
+
isError: true
|
|
27856
|
+
};
|
|
27857
|
+
}
|
|
27610
27858
|
const { agentId, agentRole } = getActiveAgent();
|
|
27611
27859
|
const id = crypto18.randomUUID();
|
|
27612
27860
|
const version = package_version ?? "unknown";
|
|
@@ -30096,6 +30344,25 @@ function registerSupportConsolidated(server2) {
|
|
|
30096
30344
|
const handler = handlers.get(toolName);
|
|
30097
30345
|
if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
|
|
30098
30346
|
const { action: _, ...args } = input;
|
|
30347
|
+
if (action === "create_bug") {
|
|
30348
|
+
if (args.description && !args.summary) {
|
|
30349
|
+
args.summary = args.description;
|
|
30350
|
+
delete args.description;
|
|
30351
|
+
}
|
|
30352
|
+
if (args.steps_to_reproduce && !args.reproduction_steps) {
|
|
30353
|
+
const raw = args.steps_to_reproduce;
|
|
30354
|
+
args.reproduction_steps = raw.split(/\n/).map((s) => s.replace(/^\d+\.\s*/, "").trim()).filter(Boolean);
|
|
30355
|
+
delete args.steps_to_reproduce;
|
|
30356
|
+
}
|
|
30357
|
+
if (args.expected_behavior && !args.expected) {
|
|
30358
|
+
args.expected = args.expected_behavior;
|
|
30359
|
+
delete args.expected_behavior;
|
|
30360
|
+
}
|
|
30361
|
+
if (args.actual_behavior && !args.actual) {
|
|
30362
|
+
args.actual = args.actual_behavior;
|
|
30363
|
+
delete args.actual_behavior;
|
|
30364
|
+
}
|
|
30365
|
+
}
|
|
30099
30366
|
if ((action === "triage_bug" || action === "triage_feature") && args.notes && !args.triage_notes) {
|
|
30100
30367
|
args.triage_notes = args.notes;
|
|
30101
30368
|
delete args.notes;
|
|
@@ -30110,6 +30377,498 @@ function registerSupportConsolidated(server2) {
|
|
|
30110
30377
|
|
|
30111
30378
|
// src/mcp/tools/diagnostics.ts
|
|
30112
30379
|
import { z as z98 } from "zod";
|
|
30380
|
+
|
|
30381
|
+
// src/lib/tool-capability-index.ts
|
|
30382
|
+
init_memory();
|
|
30383
|
+
var ALWAYS_ON = /* @__PURE__ */ new Set([
|
|
30384
|
+
"mcp_ping",
|
|
30385
|
+
"memory",
|
|
30386
|
+
"task",
|
|
30387
|
+
"message",
|
|
30388
|
+
"identity",
|
|
30389
|
+
"diagnostics",
|
|
30390
|
+
"session"
|
|
30391
|
+
]);
|
|
30392
|
+
function categoryForToolName(toolName, categoryMap) {
|
|
30393
|
+
const normalized = toolName.replace(/[_-]/g, "").toLowerCase();
|
|
30394
|
+
for (const [registerFn, cat] of Object.entries(categoryMap)) {
|
|
30395
|
+
const fnNormalized = registerFn.replace(/^register/, "").toLowerCase();
|
|
30396
|
+
if (fnNormalized === normalized) return cat;
|
|
30397
|
+
}
|
|
30398
|
+
return "unknown";
|
|
30399
|
+
}
|
|
30400
|
+
function extractParamNames(inputSchema) {
|
|
30401
|
+
if (!inputSchema || typeof inputSchema !== "object") return "";
|
|
30402
|
+
return Object.keys(inputSchema).join(", ");
|
|
30403
|
+
}
|
|
30404
|
+
function cosineSimilarity4(a, b) {
|
|
30405
|
+
if (a.length !== b.length || a.length === 0) return 0;
|
|
30406
|
+
let dot = 0, normA = 0, normB = 0;
|
|
30407
|
+
for (let i = 0; i < a.length; i++) {
|
|
30408
|
+
dot += a[i] * b[i];
|
|
30409
|
+
normA += a[i] * a[i];
|
|
30410
|
+
normB += b[i] * b[i];
|
|
30411
|
+
}
|
|
30412
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
30413
|
+
return denom === 0 ? 0 : dot / denom;
|
|
30414
|
+
}
|
|
30415
|
+
function keywordScore(query, tool) {
|
|
30416
|
+
const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
30417
|
+
if (words.length === 0) return 0;
|
|
30418
|
+
const searchText = `${tool.name} ${tool.description} ${tool.category} ${tool.paramSummary}`.toLowerCase();
|
|
30419
|
+
let matches = 0;
|
|
30420
|
+
for (const word of words) {
|
|
30421
|
+
if (searchText.includes(word)) matches++;
|
|
30422
|
+
}
|
|
30423
|
+
return matches / words.length;
|
|
30424
|
+
}
|
|
30425
|
+
var ToolCapabilityIndex = class {
|
|
30426
|
+
capabilities = [];
|
|
30427
|
+
initialized = false;
|
|
30428
|
+
/**
|
|
30429
|
+
* Build the index by intercepting registerAllTools via a fake McpServer.
|
|
30430
|
+
*
|
|
30431
|
+
* @param registerFn - function that registers tools on a server (e.g. registerAllTools)
|
|
30432
|
+
* @param categoryMap - optional map of registerFnName → category (from tool-gates TOOL_CATEGORIES)
|
|
30433
|
+
*/
|
|
30434
|
+
async buildIndex(registerFn, categoryMap) {
|
|
30435
|
+
const captured = [];
|
|
30436
|
+
const fakeServer = {
|
|
30437
|
+
registerTool(name, config2, _handler) {
|
|
30438
|
+
captured.push({
|
|
30439
|
+
name,
|
|
30440
|
+
description: config2.description ?? "",
|
|
30441
|
+
inputSchema: config2.inputSchema ?? {}
|
|
30442
|
+
});
|
|
30443
|
+
}
|
|
30444
|
+
};
|
|
30445
|
+
registerFn(fakeServer);
|
|
30446
|
+
let embedFn = null;
|
|
30447
|
+
try {
|
|
30448
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
30449
|
+
const test = await embed2("test");
|
|
30450
|
+
if (test.length === EMBEDDING_DIM) {
|
|
30451
|
+
embedFn = embed2;
|
|
30452
|
+
}
|
|
30453
|
+
} catch {
|
|
30454
|
+
}
|
|
30455
|
+
const capabilities = [];
|
|
30456
|
+
for (const tool of captured) {
|
|
30457
|
+
const paramSummary = extractParamNames(tool.inputSchema);
|
|
30458
|
+
const category = categoryMap ? categoryForToolName(tool.name, categoryMap) : "unknown";
|
|
30459
|
+
const capabilityDoc = `${tool.name}: ${tool.description}. Parameters: ${paramSummary}`;
|
|
30460
|
+
let vector = null;
|
|
30461
|
+
if (embedFn) {
|
|
30462
|
+
try {
|
|
30463
|
+
vector = await embedFn(capabilityDoc.slice(0, 500));
|
|
30464
|
+
} catch {
|
|
30465
|
+
}
|
|
30466
|
+
}
|
|
30467
|
+
capabilities.push({
|
|
30468
|
+
name: tool.name,
|
|
30469
|
+
description: tool.description,
|
|
30470
|
+
category,
|
|
30471
|
+
vector,
|
|
30472
|
+
paramSummary
|
|
30473
|
+
});
|
|
30474
|
+
}
|
|
30475
|
+
this.capabilities = capabilities;
|
|
30476
|
+
this.initialized = true;
|
|
30477
|
+
process.stderr.write(
|
|
30478
|
+
`[tool-capability-index] Indexed ${capabilities.length} tools (${capabilities.filter((c) => c.vector).length} with vectors)
|
|
30479
|
+
`
|
|
30480
|
+
);
|
|
30481
|
+
}
|
|
30482
|
+
/**
|
|
30483
|
+
* Semantic search for tools matching a natural-language query.
|
|
30484
|
+
* Returns tools sorted by relevance, with always-on tools boosted.
|
|
30485
|
+
*/
|
|
30486
|
+
async search(query, limit = 20) {
|
|
30487
|
+
if (!this.initialized || this.capabilities.length === 0) return [];
|
|
30488
|
+
let queryVector = null;
|
|
30489
|
+
try {
|
|
30490
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
30491
|
+
queryVector = await embed2(query);
|
|
30492
|
+
} catch {
|
|
30493
|
+
}
|
|
30494
|
+
const scored = [];
|
|
30495
|
+
for (const tool of this.capabilities) {
|
|
30496
|
+
let relevance;
|
|
30497
|
+
if (queryVector && tool.vector) {
|
|
30498
|
+
const vecScore = cosineSimilarity4(queryVector, tool.vector);
|
|
30499
|
+
const kwScore = keywordScore(query, tool);
|
|
30500
|
+
relevance = vecScore * 0.8 + kwScore * 0.2;
|
|
30501
|
+
} else {
|
|
30502
|
+
relevance = keywordScore(query, tool);
|
|
30503
|
+
}
|
|
30504
|
+
if (ALWAYS_ON.has(tool.name)) {
|
|
30505
|
+
relevance = Math.max(relevance, 0.5);
|
|
30506
|
+
}
|
|
30507
|
+
scored.push({ ...tool, relevance });
|
|
30508
|
+
}
|
|
30509
|
+
scored.sort((a, b) => b.relevance - a.relevance);
|
|
30510
|
+
return scored.slice(0, limit);
|
|
30511
|
+
}
|
|
30512
|
+
/** Whether the index has been built. */
|
|
30513
|
+
isReady() {
|
|
30514
|
+
return this.initialized;
|
|
30515
|
+
}
|
|
30516
|
+
/** Total number of indexed tools. */
|
|
30517
|
+
get toolCount() {
|
|
30518
|
+
return this.capabilities.length;
|
|
30519
|
+
}
|
|
30520
|
+
};
|
|
30521
|
+
var _instance = null;
|
|
30522
|
+
function getToolCapabilityIndex() {
|
|
30523
|
+
if (!_instance) {
|
|
30524
|
+
_instance = new ToolCapabilityIndex();
|
|
30525
|
+
}
|
|
30526
|
+
return _instance;
|
|
30527
|
+
}
|
|
30528
|
+
|
|
30529
|
+
// src/lib/drift-probes.ts
|
|
30530
|
+
init_identity();
|
|
30531
|
+
init_employees();
|
|
30532
|
+
var DRIFT_THRESHOLD = 80;
|
|
30533
|
+
async function getRecentMemories(agentId, limit) {
|
|
30534
|
+
try {
|
|
30535
|
+
const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
30536
|
+
if (!isInitialized2()) return { count: 0, texts: [] };
|
|
30537
|
+
const client = getClient2();
|
|
30538
|
+
const result3 = await client.execute({
|
|
30539
|
+
sql: `SELECT text FROM memories WHERE agent_id = ? ORDER BY created_at DESC LIMIT ?`,
|
|
30540
|
+
args: [agentId, limit]
|
|
30541
|
+
});
|
|
30542
|
+
const texts = result3.rows.map((r) => String(r.text ?? ""));
|
|
30543
|
+
return { count: texts.length, texts };
|
|
30544
|
+
} catch {
|
|
30545
|
+
return { count: 0, texts: [] };
|
|
30546
|
+
}
|
|
30547
|
+
}
|
|
30548
|
+
async function getDecisionCount(agentId) {
|
|
30549
|
+
try {
|
|
30550
|
+
const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
30551
|
+
if (!isInitialized2()) return 0;
|
|
30552
|
+
const client = getClient2();
|
|
30553
|
+
const result3 = await client.execute({
|
|
30554
|
+
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND memory_type = 'decision'`,
|
|
30555
|
+
args: [agentId]
|
|
30556
|
+
});
|
|
30557
|
+
return Number(result3.rows[0]?.cnt ?? 0);
|
|
30558
|
+
} catch {
|
|
30559
|
+
return 0;
|
|
30560
|
+
}
|
|
30561
|
+
}
|
|
30562
|
+
function probeContinuity(ctx) {
|
|
30563
|
+
const memCount = ctx.recentMemoryCount;
|
|
30564
|
+
let score;
|
|
30565
|
+
let detail;
|
|
30566
|
+
if (memCount >= 20) {
|
|
30567
|
+
score = 95;
|
|
30568
|
+
detail = `${memCount} recent memories \u2014 strong continuity`;
|
|
30569
|
+
} else if (memCount >= 10) {
|
|
30570
|
+
score = 85;
|
|
30571
|
+
detail = `${memCount} recent memories \u2014 adequate continuity`;
|
|
30572
|
+
} else if (memCount >= 3) {
|
|
30573
|
+
score = 70;
|
|
30574
|
+
detail = `${memCount} recent memories \u2014 limited continuity`;
|
|
30575
|
+
} else if (memCount >= 1) {
|
|
30576
|
+
score = 50;
|
|
30577
|
+
detail = `${memCount} recent memories \u2014 weak continuity`;
|
|
30578
|
+
} else {
|
|
30579
|
+
score = 30;
|
|
30580
|
+
detail = "No recent memories \u2014 agent may lack session context";
|
|
30581
|
+
}
|
|
30582
|
+
if (ctx.storedDecisionCount > 0) {
|
|
30583
|
+
score = Math.min(100, score + 5);
|
|
30584
|
+
detail += ` (+${ctx.storedDecisionCount} decisions)`;
|
|
30585
|
+
}
|
|
30586
|
+
return { axis: "continuity", score, detail };
|
|
30587
|
+
}
|
|
30588
|
+
function probeConsistency(ctx) {
|
|
30589
|
+
if (!ctx.identityBody || ctx.recentMemoryTexts.length === 0) {
|
|
30590
|
+
return {
|
|
30591
|
+
axis: "consistency",
|
|
30592
|
+
score: ctx.identityBody ? 80 : 50,
|
|
30593
|
+
detail: ctx.identityBody ? "No memories to check against identity" : "No identity file \u2014 cannot assess consistency"
|
|
30594
|
+
};
|
|
30595
|
+
}
|
|
30596
|
+
const identityTerms = extractKeyTerms(ctx.identityBody);
|
|
30597
|
+
if (identityTerms.length === 0) {
|
|
30598
|
+
return { axis: "consistency", score: 75, detail: "Identity has no extractable key terms" };
|
|
30599
|
+
}
|
|
30600
|
+
const memoryText = ctx.recentMemoryTexts.join(" ").toLowerCase();
|
|
30601
|
+
let matched = 0;
|
|
30602
|
+
for (const term of identityTerms) {
|
|
30603
|
+
if (memoryText.includes(term.toLowerCase())) matched++;
|
|
30604
|
+
}
|
|
30605
|
+
const ratio = matched / identityTerms.length;
|
|
30606
|
+
const score = Math.round(50 + ratio * 50);
|
|
30607
|
+
const detail = `${matched}/${identityTerms.length} identity terms found in recent memories`;
|
|
30608
|
+
return { axis: "consistency", score, detail };
|
|
30609
|
+
}
|
|
30610
|
+
function probeRoleFidelity(ctx) {
|
|
30611
|
+
const flags = [];
|
|
30612
|
+
if (!ctx.identityBody) {
|
|
30613
|
+
return {
|
|
30614
|
+
axis: "role-fidelity",
|
|
30615
|
+
score: 40,
|
|
30616
|
+
detail: `No identity file for ${ctx.agent.name}`
|
|
30617
|
+
};
|
|
30618
|
+
}
|
|
30619
|
+
let score = 100;
|
|
30620
|
+
const identityLower = ctx.identityBody.toLowerCase();
|
|
30621
|
+
const rosterRole = ctx.agent.role.toLowerCase();
|
|
30622
|
+
if (!identityLower.includes(rosterRole)) {
|
|
30623
|
+
score -= 15;
|
|
30624
|
+
flags.push(`Identity does not mention role "${ctx.agent.role}"`);
|
|
30625
|
+
}
|
|
30626
|
+
if (!identityLower.includes(ctx.agent.name.toLowerCase())) {
|
|
30627
|
+
score -= 10;
|
|
30628
|
+
flags.push(`Identity does not mention agent name "${ctx.agent.name}"`);
|
|
30629
|
+
}
|
|
30630
|
+
const hasScopeBoundary = identityLower.includes("do not") || identityLower.includes("don't") || identityLower.includes("not your") || identityLower.includes("outside your") || identityLower.includes("you do not");
|
|
30631
|
+
if (!hasScopeBoundary) {
|
|
30632
|
+
score -= 10;
|
|
30633
|
+
flags.push("Identity lacks explicit scope boundaries (what NOT to do)");
|
|
30634
|
+
}
|
|
30635
|
+
if (ctx.agent.systemPrompt) {
|
|
30636
|
+
const promptTerms = extractKeyTerms(ctx.agent.systemPrompt);
|
|
30637
|
+
const identityText = ctx.identityBody.toLowerCase();
|
|
30638
|
+
let promptMatched = 0;
|
|
30639
|
+
for (const term of promptTerms.slice(0, 20)) {
|
|
30640
|
+
if (identityText.includes(term.toLowerCase())) promptMatched++;
|
|
30641
|
+
}
|
|
30642
|
+
const promptRatio = promptTerms.length > 0 ? promptMatched / Math.min(promptTerms.length, 20) : 1;
|
|
30643
|
+
if (promptRatio < 0.3) {
|
|
30644
|
+
score -= 15;
|
|
30645
|
+
flags.push("Identity content diverges significantly from roster systemPrompt");
|
|
30646
|
+
}
|
|
30647
|
+
}
|
|
30648
|
+
if (ctx.recentMemoryTexts.length > 5) {
|
|
30649
|
+
const roleKeywords = extractRoleKeywords(ctx.agent.role);
|
|
30650
|
+
const memoryText = ctx.recentMemoryTexts.join(" ").toLowerCase();
|
|
30651
|
+
let domainHits = 0;
|
|
30652
|
+
for (const kw of roleKeywords) {
|
|
30653
|
+
if (memoryText.includes(kw)) domainHits++;
|
|
30654
|
+
}
|
|
30655
|
+
const domainRatio = roleKeywords.length > 0 ? domainHits / roleKeywords.length : 1;
|
|
30656
|
+
if (domainRatio < 0.2) {
|
|
30657
|
+
score -= 10;
|
|
30658
|
+
flags.push(`Recent work shows little overlap with ${ctx.agent.role} domain keywords`);
|
|
30659
|
+
}
|
|
30660
|
+
}
|
|
30661
|
+
score = Math.max(0, Math.min(100, score));
|
|
30662
|
+
const detail = flags.length > 0 ? flags.join("; ") : "Identity aligns with roster definition";
|
|
30663
|
+
return { axis: "role-fidelity", score, detail };
|
|
30664
|
+
}
|
|
30665
|
+
function probeWorldModel(ctx) {
|
|
30666
|
+
if (!ctx.identityBody) {
|
|
30667
|
+
return {
|
|
30668
|
+
axis: "world-model",
|
|
30669
|
+
score: 50,
|
|
30670
|
+
detail: "No identity file \u2014 cannot assess org awareness"
|
|
30671
|
+
};
|
|
30672
|
+
}
|
|
30673
|
+
const identityLower = ctx.identityBody.toLowerCase();
|
|
30674
|
+
const flags = [];
|
|
30675
|
+
let score = 100;
|
|
30676
|
+
const otherAgents = ctx.allEmployees.filter((e) => e.name !== ctx.agent.name);
|
|
30677
|
+
let mentionedAgents = 0;
|
|
30678
|
+
for (const other of otherAgents) {
|
|
30679
|
+
if (identityLower.includes(other.name.toLowerCase())) mentionedAgents++;
|
|
30680
|
+
}
|
|
30681
|
+
if (otherAgents.length > 0) {
|
|
30682
|
+
const mentionRatio = mentionedAgents / otherAgents.length;
|
|
30683
|
+
if (mentionRatio < 0.3) {
|
|
30684
|
+
score -= 15;
|
|
30685
|
+
flags.push(`Identity mentions ${mentionedAgents}/${otherAgents.length} team members`);
|
|
30686
|
+
}
|
|
30687
|
+
}
|
|
30688
|
+
const hasReportingChain = identityLower.includes("report") || identityLower.includes("manager") || identityLower.includes("coo") || identityLower.includes("coordinator");
|
|
30689
|
+
if (!hasReportingChain) {
|
|
30690
|
+
score -= 10;
|
|
30691
|
+
flags.push("Identity does not reference reporting chain");
|
|
30692
|
+
}
|
|
30693
|
+
const orgTerms = ["exe-os", "exe-wiki", "exe-crm", "askexe"];
|
|
30694
|
+
let orgMentions = 0;
|
|
30695
|
+
for (const term of orgTerms) {
|
|
30696
|
+
if (identityLower.includes(term)) orgMentions++;
|
|
30697
|
+
}
|
|
30698
|
+
if (orgMentions === 0) {
|
|
30699
|
+
score -= 5;
|
|
30700
|
+
flags.push("Identity does not reference any org products");
|
|
30701
|
+
}
|
|
30702
|
+
score = Math.max(0, Math.min(100, score));
|
|
30703
|
+
const detail = flags.length > 0 ? flags.join("; ") : "Identity reflects org structure";
|
|
30704
|
+
return { axis: "world-model", score, detail };
|
|
30705
|
+
}
|
|
30706
|
+
function extractKeyTerms(text3) {
|
|
30707
|
+
const stopwords = /* @__PURE__ */ new Set([
|
|
30708
|
+
"the",
|
|
30709
|
+
"and",
|
|
30710
|
+
"for",
|
|
30711
|
+
"are",
|
|
30712
|
+
"but",
|
|
30713
|
+
"not",
|
|
30714
|
+
"you",
|
|
30715
|
+
"all",
|
|
30716
|
+
"any",
|
|
30717
|
+
"can",
|
|
30718
|
+
"had",
|
|
30719
|
+
"her",
|
|
30720
|
+
"was",
|
|
30721
|
+
"one",
|
|
30722
|
+
"our",
|
|
30723
|
+
"out",
|
|
30724
|
+
"has",
|
|
30725
|
+
"his",
|
|
30726
|
+
"how",
|
|
30727
|
+
"its",
|
|
30728
|
+
"may",
|
|
30729
|
+
"new",
|
|
30730
|
+
"now",
|
|
30731
|
+
"old",
|
|
30732
|
+
"see",
|
|
30733
|
+
"way",
|
|
30734
|
+
"who",
|
|
30735
|
+
"did",
|
|
30736
|
+
"get",
|
|
30737
|
+
"got",
|
|
30738
|
+
"let",
|
|
30739
|
+
"say",
|
|
30740
|
+
"she",
|
|
30741
|
+
"too",
|
|
30742
|
+
"use",
|
|
30743
|
+
"with",
|
|
30744
|
+
"this",
|
|
30745
|
+
"that",
|
|
30746
|
+
"from",
|
|
30747
|
+
"they",
|
|
30748
|
+
"been",
|
|
30749
|
+
"have",
|
|
30750
|
+
"will",
|
|
30751
|
+
"your",
|
|
30752
|
+
"what",
|
|
30753
|
+
"when",
|
|
30754
|
+
"make",
|
|
30755
|
+
"like",
|
|
30756
|
+
"just",
|
|
30757
|
+
"over",
|
|
30758
|
+
"such",
|
|
30759
|
+
"take",
|
|
30760
|
+
"than",
|
|
30761
|
+
"them",
|
|
30762
|
+
"very",
|
|
30763
|
+
"some",
|
|
30764
|
+
"could",
|
|
30765
|
+
"into",
|
|
30766
|
+
"then",
|
|
30767
|
+
"more",
|
|
30768
|
+
"also",
|
|
30769
|
+
"after",
|
|
30770
|
+
"should",
|
|
30771
|
+
"would",
|
|
30772
|
+
"about",
|
|
30773
|
+
"their",
|
|
30774
|
+
"which",
|
|
30775
|
+
"these",
|
|
30776
|
+
"other",
|
|
30777
|
+
"every",
|
|
30778
|
+
"does",
|
|
30779
|
+
"being",
|
|
30780
|
+
"those",
|
|
30781
|
+
"never",
|
|
30782
|
+
"before",
|
|
30783
|
+
"through"
|
|
30784
|
+
]);
|
|
30785
|
+
const words = text3.replace(/[^\w\s-]/g, " ").split(/\s+/).filter((w) => w.length > 3 && !stopwords.has(w.toLowerCase()));
|
|
30786
|
+
return [...new Set(words.map((w) => w.toLowerCase()))].slice(0, 50);
|
|
30787
|
+
}
|
|
30788
|
+
function extractRoleKeywords(role) {
|
|
30789
|
+
const roleKeywordMap = {
|
|
30790
|
+
"coo": ["coordinate", "review", "status", "team", "task", "priority", "dispatch"],
|
|
30791
|
+
"cto": ["architecture", "code", "technical", "system", "design", "review", "security"],
|
|
30792
|
+
"cmo": ["marketing", "brand", "content", "design", "seo", "social", "campaign"],
|
|
30793
|
+
"principal engineer": ["code", "implement", "test", "fix", "feature", "refactor", "build"],
|
|
30794
|
+
"staff code reviewer": ["review", "code", "quality", "issue", "fix", "pattern"],
|
|
30795
|
+
"content production specialist": ["video", "image", "render", "content", "produce", "media"],
|
|
30796
|
+
"ai product lead": ["competitive", "analysis", "feature", "product", "research", "repo"]
|
|
30797
|
+
};
|
|
30798
|
+
const normalized = role.toLowerCase();
|
|
30799
|
+
return roleKeywordMap[normalized] ?? normalized.split(/\s+/).filter((w) => w.length > 2);
|
|
30800
|
+
}
|
|
30801
|
+
async function runDriftProbes(options = {}) {
|
|
30802
|
+
const employees = loadEmployeesSync();
|
|
30803
|
+
const targetAgents = options.agentId ? employees.filter((e) => e.name === options.agentId) : employees;
|
|
30804
|
+
if (targetAgents.length === 0) {
|
|
30805
|
+
return [];
|
|
30806
|
+
}
|
|
30807
|
+
const axes = options.axes ?? ["continuity", "consistency", "role-fidelity", "world-model"];
|
|
30808
|
+
const results = [];
|
|
30809
|
+
for (const agent of targetAgents) {
|
|
30810
|
+
const identity = getIdentity(agent.name);
|
|
30811
|
+
const { count: memCount, texts: memTexts } = await getRecentMemories(agent.name, 50);
|
|
30812
|
+
const decisionCount = await getDecisionCount(agent.name);
|
|
30813
|
+
const ctx = {
|
|
30814
|
+
agent,
|
|
30815
|
+
identityBody: identity?.body ?? null,
|
|
30816
|
+
identityRole: identity?.frontmatter.role ?? null,
|
|
30817
|
+
recentMemoryCount: memCount,
|
|
30818
|
+
recentMemoryTexts: memTexts,
|
|
30819
|
+
storedDecisionCount: decisionCount,
|
|
30820
|
+
allEmployees: employees
|
|
30821
|
+
};
|
|
30822
|
+
const probes = [];
|
|
30823
|
+
const probeFns = {
|
|
30824
|
+
continuity: probeContinuity,
|
|
30825
|
+
consistency: probeConsistency,
|
|
30826
|
+
"role-fidelity": probeRoleFidelity,
|
|
30827
|
+
"world-model": probeWorldModel
|
|
30828
|
+
};
|
|
30829
|
+
for (const axis of axes) {
|
|
30830
|
+
probes.push(probeFns[axis](ctx));
|
|
30831
|
+
}
|
|
30832
|
+
const scores = {
|
|
30833
|
+
continuity: 0,
|
|
30834
|
+
consistency: 0,
|
|
30835
|
+
"role-fidelity": 0,
|
|
30836
|
+
"world-model": 0
|
|
30837
|
+
};
|
|
30838
|
+
for (const probe of probes) {
|
|
30839
|
+
scores[probe.axis] = probe.score;
|
|
30840
|
+
}
|
|
30841
|
+
const weights = {
|
|
30842
|
+
continuity: 0.2,
|
|
30843
|
+
consistency: 0.2,
|
|
30844
|
+
"role-fidelity": 0.35,
|
|
30845
|
+
"world-model": 0.25
|
|
30846
|
+
};
|
|
30847
|
+
let weightedSum = 0;
|
|
30848
|
+
let weightTotal = 0;
|
|
30849
|
+
for (const axis of axes) {
|
|
30850
|
+
weightedSum += scores[axis] * weights[axis];
|
|
30851
|
+
weightTotal += weights[axis];
|
|
30852
|
+
}
|
|
30853
|
+
const overall = Math.round(weightTotal > 0 ? weightedSum / weightTotal : 0);
|
|
30854
|
+
const flags = [];
|
|
30855
|
+
for (const probe of probes) {
|
|
30856
|
+
if (probe.score < DRIFT_THRESHOLD) {
|
|
30857
|
+
flags.push(`${probe.axis} at ${probe.score}% \u2014 ${probe.detail}`);
|
|
30858
|
+
}
|
|
30859
|
+
}
|
|
30860
|
+
results.push({
|
|
30861
|
+
agent: agent.name,
|
|
30862
|
+
scores,
|
|
30863
|
+
overall,
|
|
30864
|
+
drifting: flags.length > 0,
|
|
30865
|
+
flags
|
|
30866
|
+
});
|
|
30867
|
+
}
|
|
30868
|
+
return results;
|
|
30869
|
+
}
|
|
30870
|
+
|
|
30871
|
+
// src/mcp/tools/diagnostics.ts
|
|
30113
30872
|
function buildHandlers9() {
|
|
30114
30873
|
const tools = /* @__PURE__ */ new Map();
|
|
30115
30874
|
const fake = {
|
|
@@ -30123,23 +30882,31 @@ function buildHandlers9() {
|
|
|
30123
30882
|
function registerDiagnostics(server2) {
|
|
30124
30883
|
const handlers = buildHandlers9();
|
|
30125
30884
|
const toolNames = Array.from(handlers.keys());
|
|
30126
|
-
|
|
30885
|
+
toolNames.push("tool_search", "drift");
|
|
30127
30886
|
server2.registerTool(
|
|
30128
30887
|
"diagnostics",
|
|
30129
30888
|
{
|
|
30130
30889
|
title: "Diagnostics",
|
|
30131
|
-
description: `System diagnostics and admin operations. Actions: ${toolNames.join(", ")}. Covers health checks, update status, cloud status, key management, and
|
|
30890
|
+
description: `System diagnostics and admin operations. Actions: ${toolNames.join(", ")}. Covers health checks, update status, cloud status, key management, employee rename, semantic tool discovery (tool_search), and identity drift detection (drift).`,
|
|
30132
30891
|
inputSchema: {
|
|
30133
30892
|
action: z98.string().describe(`Diagnostic operation: ${toolNames.join(", ")}`),
|
|
30134
30893
|
// Pass-through params used by various sub-tools
|
|
30894
|
+
agent_id: z98.string().optional().describe("Agent to probe (drift). Defaults to all agents."),
|
|
30135
30895
|
name: z98.string().optional().describe("Employee name (rename_employee)"),
|
|
30136
30896
|
new_name: z98.string().optional().describe("New employee name (rename_employee)"),
|
|
30137
|
-
query: z98.string().optional().describe("Search/filter query"),
|
|
30138
|
-
format: z98.string().optional().describe("Output format")
|
|
30897
|
+
query: z98.string().optional().describe("Search/filter query, or natural language for tool_search"),
|
|
30898
|
+
format: z98.string().optional().describe("Output format"),
|
|
30899
|
+
limit: z98.coerce.number().int().min(1).max(100).optional().describe("Max results for tool_search (default 20)")
|
|
30139
30900
|
}
|
|
30140
30901
|
},
|
|
30141
30902
|
async (input, extra) => {
|
|
30142
30903
|
const action = input.action;
|
|
30904
|
+
if (action === "tool_search") {
|
|
30905
|
+
return handleToolSearch(input);
|
|
30906
|
+
}
|
|
30907
|
+
if (action === "drift") {
|
|
30908
|
+
return handleDrift(input);
|
|
30909
|
+
}
|
|
30143
30910
|
const handler = handlers.get(action);
|
|
30144
30911
|
if (!handler) {
|
|
30145
30912
|
return {
|
|
@@ -30152,6 +30919,72 @@ function registerDiagnostics(server2) {
|
|
|
30152
30919
|
}
|
|
30153
30920
|
);
|
|
30154
30921
|
}
|
|
30922
|
+
async function handleToolSearch(input) {
|
|
30923
|
+
const query = input.query ?? "";
|
|
30924
|
+
const limit = input.limit ?? 20;
|
|
30925
|
+
if (!query) {
|
|
30926
|
+
return {
|
|
30927
|
+
content: [{ type: "text", text: JSON.stringify({ error: "query is required for tool_search" }) }],
|
|
30928
|
+
isError: true
|
|
30929
|
+
};
|
|
30930
|
+
}
|
|
30931
|
+
const index = getToolCapabilityIndex();
|
|
30932
|
+
if (!index.isReady()) {
|
|
30933
|
+
return {
|
|
30934
|
+
content: [{
|
|
30935
|
+
type: "text",
|
|
30936
|
+
text: JSON.stringify({
|
|
30937
|
+
error: "Tool capability index not initialized yet. It is built asynchronously at daemon boot.",
|
|
30938
|
+
query
|
|
30939
|
+
})
|
|
30940
|
+
}],
|
|
30941
|
+
isError: true
|
|
30942
|
+
};
|
|
30943
|
+
}
|
|
30944
|
+
const results = await index.search(query, limit);
|
|
30945
|
+
const tools = results.map((r) => ({
|
|
30946
|
+
name: r.name,
|
|
30947
|
+
description: r.description,
|
|
30948
|
+
category: r.category,
|
|
30949
|
+
relevance: Math.round(r.relevance * 1e3) / 1e3
|
|
30950
|
+
}));
|
|
30951
|
+
return {
|
|
30952
|
+
content: [{
|
|
30953
|
+
type: "text",
|
|
30954
|
+
text: JSON.stringify({
|
|
30955
|
+
tools,
|
|
30956
|
+
total_tools: index.toolCount,
|
|
30957
|
+
query
|
|
30958
|
+
}, null, 2)
|
|
30959
|
+
}]
|
|
30960
|
+
};
|
|
30961
|
+
}
|
|
30962
|
+
async function handleDrift(input) {
|
|
30963
|
+
const agentId = input.agent_id;
|
|
30964
|
+
const axesRaw = input.query;
|
|
30965
|
+
let axes;
|
|
30966
|
+
if (axesRaw) {
|
|
30967
|
+
const valid = ["continuity", "consistency", "role-fidelity", "world-model"];
|
|
30968
|
+
const parsed = axesRaw.split(",").map((a) => a.trim()).filter((a) => valid.includes(a));
|
|
30969
|
+
if (parsed.length > 0) axes = parsed;
|
|
30970
|
+
}
|
|
30971
|
+
const results = await runDriftProbes({ agentId, axes });
|
|
30972
|
+
if (results.length === 0) {
|
|
30973
|
+
return {
|
|
30974
|
+
content: [{
|
|
30975
|
+
type: "text",
|
|
30976
|
+
text: JSON.stringify({ error: agentId ? `Agent "${agentId}" not found` : "No agents found" })
|
|
30977
|
+
}],
|
|
30978
|
+
isError: true
|
|
30979
|
+
};
|
|
30980
|
+
}
|
|
30981
|
+
return {
|
|
30982
|
+
content: [{
|
|
30983
|
+
type: "text",
|
|
30984
|
+
text: JSON.stringify(results.length === 1 ? results[0] : results, null, 2)
|
|
30985
|
+
}]
|
|
30986
|
+
};
|
|
30987
|
+
}
|
|
30155
30988
|
|
|
30156
30989
|
// src/mcp/tool-gates.ts
|
|
30157
30990
|
var TOOL_GATES = {
|