@askexenow/exe-os 0.9.111 → 0.9.113
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/README.md +9 -7
- package/dist/bin/agentic-ontology-backfill.js +62 -12
- package/dist/bin/agentic-reflection-backfill.js +37 -2
- package/dist/bin/agentic-semantic-label.js +37 -2
- package/dist/bin/backfill-conversations.js +61 -11
- package/dist/bin/backfill-responses.js +62 -12
- package/dist/bin/backfill-vectors.js +37 -2
- package/dist/bin/bulk-sync-postgres.js +63 -13
- package/dist/bin/cleanup-stale-review-tasks.js +83 -16
- package/dist/bin/cli.js +312 -80
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +29 -3
- package/dist/bin/exe-assign.js +62 -12
- package/dist/bin/exe-boot.js +500 -151
- package/dist/bin/exe-call.js +46 -5
- package/dist/bin/exe-cloud.js +101 -16
- package/dist/bin/exe-dispatch.js +827 -27
- package/dist/bin/exe-doctor.js +61 -11
- package/dist/bin/exe-export-behaviors.js +67 -14
- package/dist/bin/exe-forget.js +62 -12
- package/dist/bin/exe-gateway.js +147 -27
- package/dist/bin/exe-heartbeat.js +83 -16
- package/dist/bin/exe-kill.js +62 -12
- package/dist/bin/exe-launch-agent.js +83 -15
- package/dist/bin/exe-new-employee.js +176 -8
- package/dist/bin/exe-pending-messages.js +83 -16
- package/dist/bin/exe-pending-notifications.js +83 -16
- package/dist/bin/exe-pending-reviews.js +83 -16
- package/dist/bin/exe-rename.js +62 -12
- package/dist/bin/exe-review.js +62 -12
- package/dist/bin/exe-search.js +62 -12
- package/dist/bin/exe-session-cleanup.js +949 -149
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +537 -248
- package/dist/bin/exe-start-opencode.js +547 -168
- package/dist/bin/exe-status.js +83 -16
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +62 -12
- package/dist/bin/git-sweep.js +827 -27
- package/dist/bin/graph-backfill.js +62 -12
- package/dist/bin/graph-export.js +62 -12
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +949 -149
- package/dist/bin/pre-publish.js +14 -2
- package/dist/bin/scan-tasks.js +827 -27
- package/dist/bin/setup.js +99 -14
- package/dist/bin/shard-migrate.js +62 -12
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +586 -26
- package/dist/hooks/bug-report-worker.js +586 -26
- package/dist/hooks/codex-stop-task-finalizer.js +977 -143
- package/dist/hooks/commit-complete.js +827 -27
- package/dist/hooks/error-recall.js +62 -12
- package/dist/hooks/ingest.js +4579 -249
- package/dist/hooks/instructions-loaded.js +62 -12
- package/dist/hooks/notification.js +62 -12
- package/dist/hooks/post-compact.js +83 -16
- package/dist/hooks/post-tool-combined.js +83 -16
- package/dist/hooks/pre-compact.js +907 -107
- package/dist/hooks/pre-tool-use.js +98 -16
- package/dist/hooks/prompt-submit.js +596 -30
- package/dist/hooks/session-end.js +909 -112
- package/dist/hooks/session-start.js +112 -17
- package/dist/hooks/stop.js +82 -15
- package/dist/hooks/subagent-stop.js +83 -16
- package/dist/hooks/summary-worker.js +81 -8
- package/dist/index.js +595 -29
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +45 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +23 -0
- package/dist/lib/db.js +23 -0
- package/dist/lib/device-registry.js +23 -0
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +482 -52
- package/dist/lib/hybrid-search.js +62 -12
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +37 -2
- package/dist/lib/skill-learning.js +910 -41
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +62 -12
- package/dist/lib/tasks.js +843 -93
- package/dist/lib/tmux-routing.js +766 -16
- package/dist/mcp/server.js +238 -41
- package/dist/mcp/tools/create-task.js +525 -15
- package/dist/mcp/tools/deactivate-behavior.js +33 -24
- package/dist/mcp/tools/list-tasks.js +21 -4
- package/dist/mcp/tools/send-message.js +21 -4
- package/dist/mcp/tools/update-task.js +840 -93
- package/dist/runtime/index.js +913 -107
- package/dist/tui/App.js +227 -58
- package/package.json +1 -1
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -568,6 +568,7 @@ __export(agent_config_exports, {
|
|
|
568
568
|
getAgentRuntime: () => getAgentRuntime,
|
|
569
569
|
loadAgentConfig: () => loadAgentConfig,
|
|
570
570
|
saveAgentConfig: () => saveAgentConfig,
|
|
571
|
+
setAgentMcps: () => setAgentMcps,
|
|
571
572
|
setAgentRuntime: () => setAgentRuntime
|
|
572
573
|
});
|
|
573
574
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
@@ -594,7 +595,7 @@ function getAgentRuntime(agentId) {
|
|
|
594
595
|
if (orgDefault) return orgDefault;
|
|
595
596
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
596
597
|
}
|
|
597
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
598
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
598
599
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
599
600
|
if (!knownModels) {
|
|
600
601
|
return {
|
|
@@ -609,12 +610,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
|
609
610
|
};
|
|
610
611
|
}
|
|
611
612
|
const config2 = loadAgentConfig();
|
|
613
|
+
const existing = config2[agentId];
|
|
612
614
|
const entry = { runtime, model };
|
|
613
615
|
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
616
|
+
if (mcps !== void 0) {
|
|
617
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
618
|
+
} else if (existing?.mcps) {
|
|
619
|
+
entry.mcps = existing.mcps;
|
|
620
|
+
}
|
|
614
621
|
config2[agentId] = entry;
|
|
615
622
|
saveAgentConfig(config2);
|
|
616
623
|
return { ok: true };
|
|
617
624
|
}
|
|
625
|
+
function setAgentMcps(agentId, mcps) {
|
|
626
|
+
const config2 = loadAgentConfig();
|
|
627
|
+
const existing = config2[agentId] ?? getAgentRuntime(agentId);
|
|
628
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
629
|
+
config2[agentId] = existing;
|
|
630
|
+
saveAgentConfig(config2);
|
|
631
|
+
return { ok: true };
|
|
632
|
+
}
|
|
618
633
|
function clearAgentRuntime(agentId) {
|
|
619
634
|
const config2 = loadAgentConfig();
|
|
620
635
|
delete config2[agentId];
|
|
@@ -2436,6 +2451,13 @@ async function ensureSchema() {
|
|
|
2436
2451
|
} catch (e) {
|
|
2437
2452
|
logCatchDebug("migration", e);
|
|
2438
2453
|
}
|
|
2454
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2455
|
+
try {
|
|
2456
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2457
|
+
} catch (e) {
|
|
2458
|
+
logCatchDebug("migration", e);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2439
2461
|
try {
|
|
2440
2462
|
await client.execute({
|
|
2441
2463
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3652,6 +3674,22 @@ async function ensureSchema() {
|
|
|
3652
3674
|
} catch (e) {
|
|
3653
3675
|
logCatchDebug("migration", e);
|
|
3654
3676
|
}
|
|
3677
|
+
try {
|
|
3678
|
+
await client.execute({
|
|
3679
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3680
|
+
args: []
|
|
3681
|
+
});
|
|
3682
|
+
} catch (e) {
|
|
3683
|
+
logCatchDebug("migration", e);
|
|
3684
|
+
}
|
|
3685
|
+
try {
|
|
3686
|
+
await client.execute({
|
|
3687
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3688
|
+
args: []
|
|
3689
|
+
});
|
|
3690
|
+
} catch (e) {
|
|
3691
|
+
logCatchDebug("migration", e);
|
|
3692
|
+
}
|
|
3655
3693
|
}
|
|
3656
3694
|
async function disposeDatabase() {
|
|
3657
3695
|
if (_walCheckpointTimer) {
|
|
@@ -6065,11 +6103,17 @@ var init_platform_procedures = __esm({
|
|
|
6065
6103
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
6066
6104
|
},
|
|
6067
6105
|
{
|
|
6068
|
-
title: "
|
|
6106
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
6069
6107
|
domain: "workflow",
|
|
6070
6108
|
priority: "p1",
|
|
6071
6109
|
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
6072
6110
|
},
|
|
6111
|
+
{
|
|
6112
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
6113
|
+
domain: "identity",
|
|
6114
|
+
priority: "p0",
|
|
6115
|
+
content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
|
|
6116
|
+
},
|
|
6073
6117
|
{
|
|
6074
6118
|
title: "Single dispatch path \u2014 create_task only",
|
|
6075
6119
|
domain: "workflow",
|
|
@@ -6103,6 +6147,12 @@ var init_platform_procedures = __esm({
|
|
|
6103
6147
|
priority: "p0",
|
|
6104
6148
|
content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
|
|
6105
6149
|
},
|
|
6150
|
+
{
|
|
6151
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
6152
|
+
domain: "security",
|
|
6153
|
+
priority: "p0",
|
|
6154
|
+
content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
|
|
6155
|
+
},
|
|
6106
6156
|
{
|
|
6107
6157
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
6108
6158
|
domain: "support",
|
|
@@ -6254,7 +6304,7 @@ var init_platform_procedures = __esm({
|
|
|
6254
6304
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
6255
6305
|
domain: "tool-use",
|
|
6256
6306
|
priority: "p0",
|
|
6257
|
-
content: 'exe-os MCP tools
|
|
6307
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
6258
6308
|
},
|
|
6259
6309
|
{
|
|
6260
6310
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -6388,10 +6438,24 @@ function stableId3(memoryId, type, content) {
|
|
|
6388
6438
|
return createHash3("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
6389
6439
|
}
|
|
6390
6440
|
function cleanText(text3) {
|
|
6391
|
-
|
|
6441
|
+
let cleaned = text3.replace(
|
|
6442
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
6443
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
6444
|
+
);
|
|
6445
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
6446
|
+
return cleaned;
|
|
6392
6447
|
}
|
|
6393
|
-
function
|
|
6394
|
-
|
|
6448
|
+
function splitSegments(text3) {
|
|
6449
|
+
const cleaned = cleanText(text3);
|
|
6450
|
+
const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
|
|
6451
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
6452
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
6453
|
+
if (lines.length > 0) return lines;
|
|
6454
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
6455
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
return segments;
|
|
6395
6459
|
}
|
|
6396
6460
|
function inferCardType(sentence, toolName) {
|
|
6397
6461
|
const lower = sentence.toLowerCase();
|
|
@@ -6423,12 +6487,12 @@ function predicateFor(type) {
|
|
|
6423
6487
|
}
|
|
6424
6488
|
}
|
|
6425
6489
|
function extractMemoryCards(row) {
|
|
6426
|
-
const
|
|
6490
|
+
const segments = splitSegments(row.raw_text);
|
|
6427
6491
|
const cards = [];
|
|
6428
|
-
for (const sentence of
|
|
6492
|
+
for (const sentence of segments) {
|
|
6429
6493
|
const type = inferCardType(sentence, row.tool_name);
|
|
6430
6494
|
const subject = extractSubject(sentence, row.agent_id);
|
|
6431
|
-
const content = sentence.length >
|
|
6495
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
6432
6496
|
cards.push({
|
|
6433
6497
|
id: stableId3(row.id, type, content),
|
|
6434
6498
|
memory_id: row.id,
|
|
@@ -6524,13 +6588,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
6524
6588
|
last_accessed: String(row.timestamp)
|
|
6525
6589
|
}));
|
|
6526
6590
|
}
|
|
6527
|
-
var MAX_CARDS_PER_MEMORY,
|
|
6591
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
6528
6592
|
var init_memory_cards = __esm({
|
|
6529
6593
|
"src/lib/memory-cards.ts"() {
|
|
6530
6594
|
"use strict";
|
|
6531
6595
|
init_database();
|
|
6532
|
-
MAX_CARDS_PER_MEMORY =
|
|
6533
|
-
|
|
6596
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
6597
|
+
MAX_SEGMENT_CHARS = 500;
|
|
6598
|
+
MIN_SEGMENT_CHARS = 20;
|
|
6534
6599
|
}
|
|
6535
6600
|
});
|
|
6536
6601
|
|
|
@@ -8721,7 +8786,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
8721
8786
|
try {
|
|
8722
8787
|
const client = getClient();
|
|
8723
8788
|
void client.execute({
|
|
8724
|
-
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1 WHERE id IN (${placeholders})`,
|
|
8789
|
+
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1, strength = MIN(1.0, COALESCE(strength, 1.0) + 0.1) WHERE id IN (${placeholders})`,
|
|
8725
8790
|
args: [now2, ...ids]
|
|
8726
8791
|
}).catch(() => {
|
|
8727
8792
|
});
|
|
@@ -9719,6 +9784,23 @@ var init_ask_team_memory = __esm({
|
|
|
9719
9784
|
});
|
|
9720
9785
|
|
|
9721
9786
|
// src/lib/license.ts
|
|
9787
|
+
var license_exports = {};
|
|
9788
|
+
__export(license_exports, {
|
|
9789
|
+
LICENSE_PUBLIC_KEY_PEM: () => LICENSE_PUBLIC_KEY_PEM,
|
|
9790
|
+
PLAN_LIMITS: () => PLAN_LIMITS,
|
|
9791
|
+
assertVpsLicense: () => assertVpsLicense,
|
|
9792
|
+
checkLicense: () => checkLicense,
|
|
9793
|
+
getCachedLicense: () => getCachedLicense,
|
|
9794
|
+
isFeatureAllowed: () => isFeatureAllowed,
|
|
9795
|
+
loadDeviceId: () => loadDeviceId,
|
|
9796
|
+
loadLicense: () => loadLicense,
|
|
9797
|
+
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
9798
|
+
readCachedLicenseToken: () => readCachedLicenseToken,
|
|
9799
|
+
saveLicense: () => saveLicense,
|
|
9800
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
9801
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
9802
|
+
validateLicense: () => validateLicense
|
|
9803
|
+
});
|
|
9722
9804
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync16, mkdirSync as mkdirSync6 } from "fs";
|
|
9723
9805
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
9724
9806
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -10002,7 +10084,135 @@ function isFeatureAllowed(license, feature) {
|
|
|
10002
10084
|
return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
|
|
10003
10085
|
}
|
|
10004
10086
|
}
|
|
10005
|
-
|
|
10087
|
+
function mirrorLicenseKey(apiKey) {
|
|
10088
|
+
const trimmed = apiKey.trim();
|
|
10089
|
+
if (!trimmed) return;
|
|
10090
|
+
saveLicense(trimmed);
|
|
10091
|
+
}
|
|
10092
|
+
async function assertVpsLicense(opts) {
|
|
10093
|
+
const env = opts?.env ?? process.env;
|
|
10094
|
+
const inProduction = env.NODE_ENV === "production";
|
|
10095
|
+
if (!opts?.force && !inProduction) {
|
|
10096
|
+
return { ...FREE_LICENSE, plan: "free" };
|
|
10097
|
+
}
|
|
10098
|
+
const envKey = env.EXE_LICENSE_KEY?.trim();
|
|
10099
|
+
if (envKey) {
|
|
10100
|
+
saveLicense(envKey);
|
|
10101
|
+
}
|
|
10102
|
+
const apiKey = envKey ?? loadLicense();
|
|
10103
|
+
if (!apiKey) {
|
|
10104
|
+
throw new Error(
|
|
10105
|
+
"License required: set EXE_LICENSE_KEY env var with your exe_sk_* key. Purchase at https://askexe.com. This VPS image refuses to boot without a valid license."
|
|
10106
|
+
);
|
|
10107
|
+
}
|
|
10108
|
+
const deviceId = loadDeviceId();
|
|
10109
|
+
let backendResponse = null;
|
|
10110
|
+
let explicitRejection = false;
|
|
10111
|
+
let transientFailure = false;
|
|
10112
|
+
try {
|
|
10113
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
10114
|
+
method: "POST",
|
|
10115
|
+
headers: { "Content-Type": "application/json" },
|
|
10116
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
10117
|
+
signal: AbortSignal.timeout(1e4)
|
|
10118
|
+
});
|
|
10119
|
+
if (res.ok) {
|
|
10120
|
+
backendResponse = await res.json();
|
|
10121
|
+
if (!backendResponse.valid) explicitRejection = true;
|
|
10122
|
+
} else if (res.status === 401 || res.status === 403) {
|
|
10123
|
+
explicitRejection = true;
|
|
10124
|
+
} else {
|
|
10125
|
+
transientFailure = true;
|
|
10126
|
+
}
|
|
10127
|
+
} catch {
|
|
10128
|
+
transientFailure = true;
|
|
10129
|
+
}
|
|
10130
|
+
if (backendResponse?.valid) {
|
|
10131
|
+
if (backendResponse.token) {
|
|
10132
|
+
cacheResponse(backendResponse.token);
|
|
10133
|
+
const verified = await verifyLicenseJwt(backendResponse.token);
|
|
10134
|
+
if (verified) return verified;
|
|
10135
|
+
}
|
|
10136
|
+
const limits = PLAN_LIMITS[backendResponse.plan] ?? PLAN_LIMITS.free;
|
|
10137
|
+
return {
|
|
10138
|
+
valid: true,
|
|
10139
|
+
plan: backendResponse.plan,
|
|
10140
|
+
email: backendResponse.email,
|
|
10141
|
+
expiresAt: backendResponse.expiresAt,
|
|
10142
|
+
deviceLimit: limits.devices,
|
|
10143
|
+
employeeLimit: limits.employees,
|
|
10144
|
+
memoryLimit: limits.memories
|
|
10145
|
+
};
|
|
10146
|
+
}
|
|
10147
|
+
if (explicitRejection) {
|
|
10148
|
+
throw new Error(
|
|
10149
|
+
`License invalid or expired. Renew at https://askexe.com, then restart. Backend rejected key ending in ...${apiKey.slice(-4)}.`
|
|
10150
|
+
);
|
|
10151
|
+
}
|
|
10152
|
+
if (!transientFailure) {
|
|
10153
|
+
throw new Error(
|
|
10154
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
10155
|
+
);
|
|
10156
|
+
}
|
|
10157
|
+
const fresh = await getCachedLicense();
|
|
10158
|
+
if (fresh && fresh.valid) return fresh;
|
|
10159
|
+
const graceDays = opts?.offlineGraceDays ?? 7;
|
|
10160
|
+
const graceMs = graceDays * 24 * 60 * 60 * 1e3;
|
|
10161
|
+
try {
|
|
10162
|
+
const token = readCachedLicenseToken();
|
|
10163
|
+
if (token) {
|
|
10164
|
+
const payloadB64 = token.split(".")[1];
|
|
10165
|
+
if (payloadB64) {
|
|
10166
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
10167
|
+
const expMs = (payload.exp ?? 0) * 1e3;
|
|
10168
|
+
if (Date.now() < expMs + graceMs) {
|
|
10169
|
+
const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
|
|
10170
|
+
const { payload: verified } = await jwtVerify(token, key, {
|
|
10171
|
+
algorithms: [LICENSE_JWT_ALG],
|
|
10172
|
+
clockTolerance: graceDays * 24 * 60 * 60
|
|
10173
|
+
});
|
|
10174
|
+
const plan = verified.plan ?? "free";
|
|
10175
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
10176
|
+
return {
|
|
10177
|
+
valid: true,
|
|
10178
|
+
plan,
|
|
10179
|
+
email: verified.sub ?? "",
|
|
10180
|
+
expiresAt: verified.exp ? new Date(verified.exp * 1e3).toISOString() : null,
|
|
10181
|
+
deviceLimit: limits.devices,
|
|
10182
|
+
employeeLimit: limits.employees,
|
|
10183
|
+
memoryLimit: limits.memories
|
|
10184
|
+
};
|
|
10185
|
+
}
|
|
10186
|
+
}
|
|
10187
|
+
}
|
|
10188
|
+
} catch {
|
|
10189
|
+
}
|
|
10190
|
+
throw new Error(
|
|
10191
|
+
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
|
|
10192
|
+
);
|
|
10193
|
+
}
|
|
10194
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
10195
|
+
if (_revalTimer) return;
|
|
10196
|
+
_revalTimer = setInterval(async () => {
|
|
10197
|
+
try {
|
|
10198
|
+
const license = await checkLicense();
|
|
10199
|
+
if (!license.valid) {
|
|
10200
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
10201
|
+
}
|
|
10202
|
+
} catch {
|
|
10203
|
+
}
|
|
10204
|
+
}, intervalMs);
|
|
10205
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
10206
|
+
_revalTimer.unref();
|
|
10207
|
+
}
|
|
10208
|
+
}
|
|
10209
|
+
function stopLicenseRevalidation() {
|
|
10210
|
+
if (_revalTimer) {
|
|
10211
|
+
clearInterval(_revalTimer);
|
|
10212
|
+
_revalTimer = null;
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
|
|
10006
10216
|
var init_license = __esm({
|
|
10007
10217
|
"src/lib/license.ts"() {
|
|
10008
10218
|
"use strict";
|
|
@@ -10010,7 +10220,7 @@ var init_license = __esm({
|
|
|
10010
10220
|
LICENSE_PATH = path17.join(EXE_AI_DIR, "license.key");
|
|
10011
10221
|
CACHE_PATH = path17.join(EXE_AI_DIR, "license-cache.json");
|
|
10012
10222
|
DEVICE_ID_PATH = path17.join(EXE_AI_DIR, "device-id");
|
|
10013
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
10223
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
10014
10224
|
RETRY_DELAY_MS = 500;
|
|
10015
10225
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
10016
10226
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -10036,6 +10246,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
10036
10246
|
_prismaPromise = null;
|
|
10037
10247
|
_prismaFailed = false;
|
|
10038
10248
|
CACHE_MAX_AGE_MS = 36e5;
|
|
10249
|
+
_revalTimer = null;
|
|
10039
10250
|
}
|
|
10040
10251
|
});
|
|
10041
10252
|
|
|
@@ -13191,11 +13402,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
13191
13402
|
}
|
|
13192
13403
|
}
|
|
13193
13404
|
function resolveExeSession() {
|
|
13405
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
13406
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
13407
|
+
if (fromEnv) return fromEnv;
|
|
13408
|
+
}
|
|
13194
13409
|
const mySession = getMySession();
|
|
13195
13410
|
if (!mySession) {
|
|
13196
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
13197
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
13198
|
-
}
|
|
13199
13411
|
return null;
|
|
13200
13412
|
}
|
|
13201
13413
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -13210,6 +13422,10 @@ function resolveExeSession() {
|
|
|
13210
13422
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
13211
13423
|
`
|
|
13212
13424
|
);
|
|
13425
|
+
try {
|
|
13426
|
+
registerParentExe(key, fromSessionName);
|
|
13427
|
+
} catch {
|
|
13428
|
+
}
|
|
13213
13429
|
candidate = fromSessionName;
|
|
13214
13430
|
} else {
|
|
13215
13431
|
candidate = fromCache;
|
|
@@ -14143,6 +14359,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
14143
14359
|
args: [identifier, ...scope.args]
|
|
14144
14360
|
});
|
|
14145
14361
|
if (result3.rows.length === 1) return result3.rows[0];
|
|
14362
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
14363
|
+
result3 = await client.execute({
|
|
14364
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
14365
|
+
args: [`${identifier}%`]
|
|
14366
|
+
});
|
|
14367
|
+
if (result3.rows.length === 1) return result3.rows[0];
|
|
14368
|
+
if (result3.rows.length > 1) {
|
|
14369
|
+
const matches = result3.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
14370
|
+
throw new Error(
|
|
14371
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
14372
|
+
);
|
|
14373
|
+
}
|
|
14374
|
+
}
|
|
14146
14375
|
result3 = await client.execute({
|
|
14147
14376
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
14148
14377
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -14689,12 +14918,13 @@ async function cascadeUnblock(taskId, baseDir, now2) {
|
|
|
14689
14918
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
14690
14919
|
args: [now2, taskId]
|
|
14691
14920
|
});
|
|
14692
|
-
if (
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14921
|
+
if (unblocked.rowsAffected === 0) return;
|
|
14922
|
+
const ubScope = sessionScopeFilter();
|
|
14923
|
+
const unblockedRows = await client.execute({
|
|
14924
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
14925
|
+
args: [now2, ...ubScope.args]
|
|
14926
|
+
});
|
|
14927
|
+
if (baseDir) {
|
|
14698
14928
|
for (const ur of unblockedRows.rows) {
|
|
14699
14929
|
try {
|
|
14700
14930
|
const ubFile = path28.join(baseDir, String(ur.task_file));
|
|
@@ -14706,6 +14936,19 @@ async function cascadeUnblock(taskId, baseDir, now2) {
|
|
|
14706
14936
|
}
|
|
14707
14937
|
}
|
|
14708
14938
|
}
|
|
14939
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
14940
|
+
try {
|
|
14941
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
14942
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
14943
|
+
for (const ur of unblockedRows.rows) {
|
|
14944
|
+
const assignee = String(ur.assigned_to);
|
|
14945
|
+
if (dispatched.has(assignee)) continue;
|
|
14946
|
+
dispatched.add(assignee);
|
|
14947
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
14948
|
+
}
|
|
14949
|
+
} catch {
|
|
14950
|
+
}
|
|
14951
|
+
}
|
|
14709
14952
|
}
|
|
14710
14953
|
async function findNextTask(assignedTo) {
|
|
14711
14954
|
const client = getClient();
|
|
@@ -14847,6 +15090,15 @@ var init_tasks_notify = __esm({
|
|
|
14847
15090
|
// src/lib/behaviors.ts
|
|
14848
15091
|
import crypto9 from "crypto";
|
|
14849
15092
|
async function storeBehavior(opts) {
|
|
15093
|
+
try {
|
|
15094
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
15095
|
+
const roster = loadEmployeesSync2();
|
|
15096
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
15097
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
15098
|
+
}
|
|
15099
|
+
} catch (e) {
|
|
15100
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
15101
|
+
}
|
|
14850
15102
|
const client = getClient();
|
|
14851
15103
|
const id = crypto9.randomUUID();
|
|
14852
15104
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -14857,10 +15109,18 @@ async function storeBehavior(opts) {
|
|
|
14857
15109
|
vector = new Float32Array(vec);
|
|
14858
15110
|
} catch {
|
|
14859
15111
|
}
|
|
15112
|
+
let createdByDevice = null;
|
|
15113
|
+
try {
|
|
15114
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
15115
|
+
createdByDevice = loadDeviceId2() ?? null;
|
|
15116
|
+
} catch {
|
|
15117
|
+
}
|
|
15118
|
+
const createdByAgent = process.env.AGENT_ID ?? null;
|
|
15119
|
+
const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
|
|
14860
15120
|
await client.execute({
|
|
14861
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
14862
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
14863
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now2, now2, vector ? vector.buffer : null]
|
|
15121
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
|
|
15122
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
|
|
15123
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now2, now2, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
|
|
14864
15124
|
});
|
|
14865
15125
|
return id;
|
|
14866
15126
|
}
|
|
@@ -14882,7 +15142,10 @@ async function listBehaviorsByDomain(agentId, domain) {
|
|
|
14882
15142
|
active: Number(r.active),
|
|
14883
15143
|
created_at: String(r.created_at),
|
|
14884
15144
|
updated_at: String(r.updated_at),
|
|
14885
|
-
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
|
|
15145
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
|
|
15146
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
15147
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
15148
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
14886
15149
|
}));
|
|
14887
15150
|
}
|
|
14888
15151
|
async function deactivateBehavior(id) {
|
|
@@ -15321,6 +15584,12 @@ async function updateTask(input) {
|
|
|
15321
15584
|
}
|
|
15322
15585
|
}
|
|
15323
15586
|
}
|
|
15587
|
+
if (input.status === "cancelled") {
|
|
15588
|
+
try {
|
|
15589
|
+
await cascadeUnblock(taskId, input.baseDir, now2);
|
|
15590
|
+
} catch {
|
|
15591
|
+
}
|
|
15592
|
+
}
|
|
15324
15593
|
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
15325
15594
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
15326
15595
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -17589,8 +17858,11 @@ function rowToBehavior(r) {
|
|
|
17589
17858
|
active: Number(r.active),
|
|
17590
17859
|
created_at: String(r.created_at),
|
|
17591
17860
|
updated_at: String(r.updated_at),
|
|
17592
|
-
vector: null
|
|
17861
|
+
vector: null,
|
|
17593
17862
|
// Not needed for listing
|
|
17863
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
17864
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
17865
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
17594
17866
|
};
|
|
17595
17867
|
}
|
|
17596
17868
|
function registerListBehaviors(server) {
|
|
@@ -20759,15 +21031,16 @@ function registerSetAgentConfig(server) {
|
|
|
20759
21031
|
"set_agent_config",
|
|
20760
21032
|
{
|
|
20761
21033
|
title: "Set Agent Config",
|
|
20762
|
-
description: "Set or view per-agent runtime
|
|
21034
|
+
description: "Set or view per-agent runtime, model, and MCP configuration. Controls which runtime (claude, codex, opencode), model, and MCP servers each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents. Pass mcps to restrict which MCP servers an agent loads (exe-os always included).",
|
|
20763
21035
|
inputSchema: {
|
|
20764
21036
|
agent_id: z48.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
|
|
20765
21037
|
runtime: z48.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
|
|
20766
21038
|
model: z48.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)"),
|
|
20767
|
-
reasoning_effort: z48.string().optional().describe("Codex reasoning effort: low, medium, high, xhigh. Per-agent override for thinking/reasoning depth.")
|
|
21039
|
+
reasoning_effort: z48.string().optional().describe("Codex reasoning effort: low, medium, high, xhigh. Per-agent override for thinking/reasoning depth."),
|
|
21040
|
+
mcps: z48.array(z48.string()).optional().describe("MCP server allowlist. Only these servers load for this agent. exe-os always included. Omit to load all MCPs.")
|
|
20768
21041
|
}
|
|
20769
21042
|
},
|
|
20770
|
-
async ({ agent_id, runtime, model, reasoning_effort }) => {
|
|
21043
|
+
async ({ agent_id, runtime, model, reasoning_effort, mcps }) => {
|
|
20771
21044
|
const { agentId, agentRole } = getActiveAgent();
|
|
20772
21045
|
const isCoordinator = isCoordinatorRole(agentRole) || (() => {
|
|
20773
21046
|
try {
|
|
@@ -20799,15 +21072,26 @@ function registerSetAgentConfig(server) {
|
|
|
20799
21072
|
isError: true
|
|
20800
21073
|
};
|
|
20801
21074
|
}
|
|
21075
|
+
if (mcps && agent_id && !runtime && !model) {
|
|
21076
|
+
const { setAgentMcps: setAgentMcps2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
21077
|
+
const result4 = setAgentMcps2(agent_id, mcps);
|
|
21078
|
+
if (!result4.ok) {
|
|
21079
|
+
return { content: [{ type: "text", text: result4.error }], isError: true };
|
|
21080
|
+
}
|
|
21081
|
+
return {
|
|
21082
|
+
content: [{ type: "text", text: `Set ${agent_id} MCPs \u2192 [${mcps.join(", ")}] (exe-os always included)` }]
|
|
21083
|
+
};
|
|
21084
|
+
}
|
|
20802
21085
|
if (!runtime || !model) {
|
|
20803
21086
|
if (agent_id) {
|
|
20804
21087
|
const cfg = getAgentRuntime(agent_id);
|
|
20805
21088
|
const label = RUNTIME_LABELS[cfg.runtime] ?? cfg.runtime;
|
|
21089
|
+
const mcpInfo = cfg.mcps ? ` | MCPs: [${cfg.mcps.join(", ")}]` : " | MCPs: all";
|
|
20806
21090
|
return {
|
|
20807
21091
|
content: [
|
|
20808
21092
|
{
|
|
20809
21093
|
type: "text",
|
|
20810
|
-
text: `${agent_id}: ${label} / ${cfg.model}`
|
|
21094
|
+
text: `${agent_id}: ${label} / ${cfg.model}${mcpInfo}`
|
|
20811
21095
|
}
|
|
20812
21096
|
]
|
|
20813
21097
|
};
|
|
@@ -20854,7 +21138,7 @@ function registerSetAgentConfig(server) {
|
|
|
20854
21138
|
isError: true
|
|
20855
21139
|
};
|
|
20856
21140
|
}
|
|
20857
|
-
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort);
|
|
21141
|
+
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort, mcps);
|
|
20858
21142
|
if (!result3.ok) {
|
|
20859
21143
|
return {
|
|
20860
21144
|
content: [{ type: "text", text: result3.error }],
|
|
@@ -20863,6 +21147,7 @@ function registerSetAgentConfig(server) {
|
|
|
20863
21147
|
}
|
|
20864
21148
|
const parts = [`Set ${agent_id} \u2192 runtime=${runtime} model=${model}`];
|
|
20865
21149
|
if (reasoning_effort) parts[0] += ` reasoning_effort=${reasoning_effort}`;
|
|
21150
|
+
if (mcps) parts[0] += ` mcps=[${mcps.join(", ")}]`;
|
|
20866
21151
|
return {
|
|
20867
21152
|
content: [{ type: "text", text: parts[0] }]
|
|
20868
21153
|
};
|
|
@@ -22462,10 +22747,18 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
|
|
|
22462
22747
|
} catch {
|
|
22463
22748
|
return [];
|
|
22464
22749
|
}
|
|
22750
|
+
const coordinatorSet = new Set(coordinatorSessions);
|
|
22465
22751
|
const agentTasks = /* @__PURE__ */ new Map();
|
|
22466
22752
|
for (const t of tasksByAgent) {
|
|
22467
22753
|
const scope = t.sessionScope || coordinatorSessions[0] || null;
|
|
22468
22754
|
if (!scope) continue;
|
|
22755
|
+
if (t.sessionScope && !coordinatorSet.has(t.sessionScope)) {
|
|
22756
|
+
process.stderr.write(
|
|
22757
|
+
`[auto-wake] Skipping ${t.agentId} task ${t.taskId} \u2014 coordinator session "${t.sessionScope}" is dead
|
|
22758
|
+
`
|
|
22759
|
+
);
|
|
22760
|
+
continue;
|
|
22761
|
+
}
|
|
22469
22762
|
const key = `${t.agentId}::${scope}`;
|
|
22470
22763
|
const existing = agentTasks.get(key) ?? [];
|
|
22471
22764
|
existing.push({ taskId: t.taskId, priority: t.priority, sessionScope: scope });
|
|
@@ -25192,6 +25485,27 @@ async function cloudSync(config2) {
|
|
|
25192
25485
|
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
25193
25486
|
pulled = pullResult.records.length;
|
|
25194
25487
|
} else {
|
|
25488
|
+
try {
|
|
25489
|
+
const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
|
|
25490
|
+
if (incomingIds.length > 0) {
|
|
25491
|
+
const ph = incomingIds.map(() => "?").join(",");
|
|
25492
|
+
const existing = await client.execute({
|
|
25493
|
+
sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
|
|
25494
|
+
args: incomingIds
|
|
25495
|
+
});
|
|
25496
|
+
const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
|
|
25497
|
+
for (const rec of pullResult.records) {
|
|
25498
|
+
const local = localMap.get(String(rec.id));
|
|
25499
|
+
if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
|
|
25500
|
+
process.stderr.write(
|
|
25501
|
+
`[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
|
|
25502
|
+
`
|
|
25503
|
+
);
|
|
25504
|
+
}
|
|
25505
|
+
}
|
|
25506
|
+
}
|
|
25507
|
+
} catch {
|
|
25508
|
+
}
|
|
25195
25509
|
const stmts = pullResult.records.map((rec) => ({
|
|
25196
25510
|
sql: `INSERT OR REPLACE INTO memories
|
|
25197
25511
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -28246,7 +28560,7 @@ var init_starter_packs = __esm({
|
|
|
28246
28560
|
|
|
28247
28561
|
// src/lib/employee-templates.ts
|
|
28248
28562
|
function renderClientCOOTemplate(vars) {
|
|
28249
|
-
const resolved = { ...vars, title: vars.title || "Chief
|
|
28563
|
+
const resolved = { ...vars, title: vars.title || "Chief of Staff" };
|
|
28250
28564
|
for (const key of CLIENT_COO_PLACEHOLDERS) {
|
|
28251
28565
|
const value = resolved[key];
|
|
28252
28566
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -29828,7 +30142,10 @@ function rowToBehavior2(r) {
|
|
|
29828
30142
|
active: Number(r.active),
|
|
29829
30143
|
created_at: String(r.created_at),
|
|
29830
30144
|
updated_at: String(r.updated_at),
|
|
29831
|
-
vector: null
|
|
30145
|
+
vector: null,
|
|
30146
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
30147
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
30148
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
29832
30149
|
};
|
|
29833
30150
|
}
|
|
29834
30151
|
function registerBehavior(server) {
|
|
@@ -29836,9 +30153,9 @@ function registerBehavior(server) {
|
|
|
29836
30153
|
"behavior",
|
|
29837
30154
|
{
|
|
29838
30155
|
title: "Behavior",
|
|
29839
|
-
description: "Manage behavioral patterns, corrections, and reusable procedures for employees. Actions: store (create new), list (query existing), deactivate (soft-delete, restricted).",
|
|
30156
|
+
description: "Manage behavioral patterns, corrections, and reusable procedures for employees. Actions: store (create new), list (query existing), audit (creation context), deactivate (soft-delete, restricted).",
|
|
29840
30157
|
inputSchema: {
|
|
29841
|
-
action: z78.enum(["store", "list", "deactivate"]).describe("Action to perform"),
|
|
30158
|
+
action: z78.enum(["store", "list", "deactivate", "audit"]).describe("Action to perform"),
|
|
29842
30159
|
content: z78.string().max(500).optional().describe("The behavioral instruction \u2014 one clear sentence (store)"),
|
|
29843
30160
|
domain: z78.string().optional().describe("Category: workflow, code-style, tool-use, communication, architecture, testing"),
|
|
29844
30161
|
priority: z78.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always included. p1 = standard (default). p2 = nice-to-have."),
|
|
@@ -29887,7 +30204,8 @@ function registerBehavior(server) {
|
|
|
29887
30204
|
const lines = behaviors.map((b) => {
|
|
29888
30205
|
const scope = b.project_name ?? "global";
|
|
29889
30206
|
const dom = b.domain ?? "general";
|
|
29890
|
-
|
|
30207
|
+
const createdBy = b.created_by_agent ? ` (by: ${b.created_by_agent})` : "";
|
|
30208
|
+
return `- [${b.agent_id}] [${dom}] (${scope})${createdBy} ${b.content}
|
|
29891
30209
|
ID: ${b.id}`;
|
|
29892
30210
|
});
|
|
29893
30211
|
return {
|
|
@@ -29972,6 +30290,39 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
29972
30290
|
content: [{ type: "text", text: responseText }]
|
|
29973
30291
|
};
|
|
29974
30292
|
}
|
|
30293
|
+
if (action === "audit") {
|
|
30294
|
+
if (!behavior_id) {
|
|
30295
|
+
return {
|
|
30296
|
+
content: [{ type: "text", text: "Missing required field: behavior_id is required for audit action." }],
|
|
30297
|
+
isError: true
|
|
30298
|
+
};
|
|
30299
|
+
}
|
|
30300
|
+
const client2 = getClient();
|
|
30301
|
+
const auditResult = await client2.execute({
|
|
30302
|
+
sql: `SELECT id, agent_id, content, domain, priority, project_name, active,
|
|
30303
|
+
created_at, updated_at, created_by_agent, created_by_device, source_session_id
|
|
30304
|
+
FROM behaviors WHERE id = ?`,
|
|
30305
|
+
args: [behavior_id]
|
|
30306
|
+
});
|
|
30307
|
+
if (auditResult.rows.length === 0) {
|
|
30308
|
+
return { content: [{ type: "text", text: `Behavior not found: ${behavior_id}` }], isError: true };
|
|
30309
|
+
}
|
|
30310
|
+
const b = auditResult.rows[0];
|
|
30311
|
+
const auditText = [
|
|
30312
|
+
`## Behavior Audit: ${b.id}`,
|
|
30313
|
+
`**Agent:** ${b.agent_id}`,
|
|
30314
|
+
`**Content:** ${b.content}`,
|
|
30315
|
+
`**Domain:** ${b.domain ?? "none"} | **Priority:** ${b.priority} | **Active:** ${b.active ? "yes" : "no"}`,
|
|
30316
|
+
`**Project:** ${b.project_name ?? "global"}`,
|
|
30317
|
+
`**Created:** ${b.created_at} | **Updated:** ${b.updated_at}`,
|
|
30318
|
+
``,
|
|
30319
|
+
`### Creation context`,
|
|
30320
|
+
`**Created by agent:** ${b.created_by_agent ?? "unknown (pre-audit)"}`,
|
|
30321
|
+
`**Created from device:** ${b.created_by_device ?? "unknown (pre-audit)"}`,
|
|
30322
|
+
`**Source session:** ${b.source_session_id ?? "unknown (pre-audit)"}`
|
|
30323
|
+
].join("\n");
|
|
30324
|
+
return { content: [{ type: "text", text: auditText }] };
|
|
30325
|
+
}
|
|
29975
30326
|
const caller = getActiveAgent();
|
|
29976
30327
|
const allowed = canCoordinate(caller.agentId, caller.agentRole);
|
|
29977
30328
|
if (!allowed) {
|
|
@@ -34547,7 +34898,7 @@ function registerAllTools(server, license, hasKey) {
|
|
|
34547
34898
|
if (license && !isToolAllowedForPlan(name, license.plan)) return;
|
|
34548
34899
|
fn(server);
|
|
34549
34900
|
};
|
|
34550
|
-
const mcpToolSurface = process.env.EXE_MCP_TOOL_SURFACE ?? "
|
|
34901
|
+
const mcpToolSurface = process.env.EXE_MCP_TOOL_SURFACE ?? "consolidated";
|
|
34551
34902
|
const exposeConsolidatedTasks = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
34552
34903
|
const exposeLegacyTasks = mcpToolSurface !== "consolidated";
|
|
34553
34904
|
const exposeConsolidatedMemory = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
@@ -35096,12 +35447,14 @@ __export(task_enforcement_exports, {
|
|
|
35096
35447
|
IDLE_PATTERNS: () => IDLE_PATTERNS,
|
|
35097
35448
|
MANAGER_GRACE_PERIOD_MS: () => MANAGER_GRACE_PERIOD_MS,
|
|
35098
35449
|
MANAGER_ROLES: () => MANAGER_ROLES,
|
|
35450
|
+
NUDGE_ECHO_PATTERNS: () => NUDGE_ECHO_PATTERNS,
|
|
35099
35451
|
TASK_ENFORCEMENT_DEBOUNCE_MS: () => TASK_ENFORCEMENT_DEBOUNCE_MS,
|
|
35100
35452
|
TASK_ENFORCEMENT_INTERVAL_MS: () => TASK_ENFORCEMENT_INTERVAL_MS,
|
|
35101
35453
|
_resetNudgeState: () => _resetNudgeState,
|
|
35102
35454
|
decideManagerAction: () => decideManagerAction,
|
|
35103
35455
|
decideWorkerAction: () => decideWorkerAction,
|
|
35104
35456
|
isIdlePane: () => isIdlePane,
|
|
35457
|
+
isNudgeEcho: () => isNudgeEcho,
|
|
35105
35458
|
runTaskEnforcementTick: () => runTaskEnforcementTick,
|
|
35106
35459
|
sendNudge: () => sendNudge
|
|
35107
35460
|
});
|
|
@@ -35114,20 +35467,26 @@ function writeAuditEntry(entry) {
|
|
|
35114
35467
|
} catch {
|
|
35115
35468
|
}
|
|
35116
35469
|
}
|
|
35117
|
-
function decideManagerAction(counts, isIdle, withinGracePeriod) {
|
|
35470
|
+
function decideManagerAction(counts, isIdle, withinGracePeriod, nudgeEcho) {
|
|
35118
35471
|
if (counts.openTasks === 0) return "skip";
|
|
35119
35472
|
if (withinGracePeriod) return "grace_period";
|
|
35120
35473
|
if (!isIdle) return "active";
|
|
35474
|
+
if (nudgeEcho) return "active";
|
|
35475
|
+
if ((counts.inProgressTasks ?? 0) > 0) return "active";
|
|
35121
35476
|
if (counts.activeSubtasks === 0) return "nudge";
|
|
35122
35477
|
return "soft_nudge";
|
|
35123
35478
|
}
|
|
35124
|
-
function decideWorkerAction(taskCount, isIdle) {
|
|
35479
|
+
function decideWorkerAction(taskCount, isIdle, nudgeEcho) {
|
|
35125
35480
|
if (taskCount === 0) return "skip";
|
|
35481
|
+
if (nudgeEcho) return "idle";
|
|
35126
35482
|
return isIdle ? "nudge" : "idle";
|
|
35127
35483
|
}
|
|
35128
35484
|
function isIdlePane(paneText) {
|
|
35129
35485
|
return IDLE_PATTERNS.some((p) => paneText.includes(p));
|
|
35130
35486
|
}
|
|
35487
|
+
function isNudgeEcho(paneText) {
|
|
35488
|
+
return NUDGE_ECHO_PATTERNS.some((p) => paneText.includes(p));
|
|
35489
|
+
}
|
|
35131
35490
|
function sendNudge(transport, session, runtime, action) {
|
|
35132
35491
|
const message = action === "nudge" ? "You have open tasks. Run list_tasks to find them." : "You have more open tasks to dispatch. Run list_tasks.";
|
|
35133
35492
|
if (transport.sendKeysLiteral) {
|
|
@@ -35147,16 +35506,21 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35147
35506
|
const { transport, employees, client, scopeFilter } = deps;
|
|
35148
35507
|
const now2 = deps.now ?? Date.now();
|
|
35149
35508
|
const sessions = transport.listSessions();
|
|
35509
|
+
let processedAnyEmployee = false;
|
|
35150
35510
|
for (const session of sessions) {
|
|
35151
35511
|
if (!session.includes("-")) continue;
|
|
35152
35512
|
const agentName = session.split("-")[0]?.replace(/\d+$/, "");
|
|
35153
35513
|
if (!agentName) continue;
|
|
35514
|
+
if ("isAlive" in transport && typeof transport.isAlive === "function") {
|
|
35515
|
+
if (!transport.isAlive(session)) continue;
|
|
35516
|
+
}
|
|
35154
35517
|
const lastNudgeTime = _lastNudge.get(session) ?? 0;
|
|
35155
35518
|
if (now2 - lastNudgeTime < TASK_ENFORCEMENT_DEBOUNCE_MS) {
|
|
35156
35519
|
continue;
|
|
35157
35520
|
}
|
|
35158
35521
|
const employee = employees.find((e) => e.name === agentName);
|
|
35159
35522
|
if (!employee) continue;
|
|
35523
|
+
processedAnyEmployee = true;
|
|
35160
35524
|
let effectiveScope = scopeFilter;
|
|
35161
35525
|
const dashIndex = session.indexOf("-");
|
|
35162
35526
|
if (dashIndex > 0) {
|
|
@@ -35204,12 +35568,21 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35204
35568
|
}
|
|
35205
35569
|
}
|
|
35206
35570
|
}
|
|
35571
|
+
let inProgressTasks = 0;
|
|
35572
|
+
if (openTasks > 0) {
|
|
35573
|
+
const ipResult = await client.execute({
|
|
35574
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks WHERE assigned_to = ? AND status = 'in_progress'${effectiveScope.sql}`,
|
|
35575
|
+
args: [agentName, ...effectiveScope.args]
|
|
35576
|
+
});
|
|
35577
|
+
inProgressTasks = Number(ipResult.rows[0]?.cnt ?? 0);
|
|
35578
|
+
}
|
|
35207
35579
|
const pane = transport.capturePane(session, 20);
|
|
35208
35580
|
paneIdle = isIdlePane(pane);
|
|
35209
|
-
|
|
35581
|
+
const nudgeEcho = isNudgeEcho(pane);
|
|
35582
|
+
action = decideManagerAction({ openTasks, activeSubtasks, inProgressTasks }, paneIdle, withinGracePeriod, nudgeEcho);
|
|
35210
35583
|
if (action === "skip") reason = "no open tasks";
|
|
35211
35584
|
else if (action === "grace_period") reason = `task assigned <10min ago (${graceRemaining}s remaining)`;
|
|
35212
|
-
else if (action === "active") reason = "pane shows active work";
|
|
35585
|
+
else if (action === "active") reason = nudgeEcho ? "pane shows previous nudge (anti-loop)" : inProgressTasks > 0 ? `${inProgressTasks} tasks in_progress` : "pane shows active work";
|
|
35213
35586
|
else if (action === "nudge") reason = `${openTasks} open tasks, 0 delegated, pane idle`;
|
|
35214
35587
|
else if (action === "soft_nudge") reason = `${openTasks} open tasks, ${activeSubtasks} delegated, pane idle`;
|
|
35215
35588
|
writeAuditEntry({
|
|
@@ -35238,7 +35611,8 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35238
35611
|
if (taskCount > 0) {
|
|
35239
35612
|
const pane = transport.capturePane(session, 20);
|
|
35240
35613
|
paneIdle = isIdlePane(pane);
|
|
35241
|
-
|
|
35614
|
+
const workerNudgeEcho = isNudgeEcho(pane);
|
|
35615
|
+
action = decideWorkerAction(taskCount, paneIdle, workerNudgeEcho);
|
|
35242
35616
|
}
|
|
35243
35617
|
if (action === "skip") reason = "no tasks";
|
|
35244
35618
|
else if (action === "idle") reason = `${taskCount} tasks but pane active`;
|
|
@@ -35264,8 +35638,32 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35264
35638
|
} catch {
|
|
35265
35639
|
}
|
|
35266
35640
|
}
|
|
35641
|
+
if (processedAnyEmployee) {
|
|
35642
|
+
try {
|
|
35643
|
+
const staleBlocked = await client.execute({
|
|
35644
|
+
sql: `SELECT t.id, t.title, b.status as blocker_status
|
|
35645
|
+
FROM tasks t
|
|
35646
|
+
JOIN tasks b ON t.blocked_by = b.id
|
|
35647
|
+
WHERE t.status = 'blocked'
|
|
35648
|
+
AND b.status IN ('done', 'cancelled', 'closed')${scopeFilter.sql}`,
|
|
35649
|
+
args: [...scopeFilter.args]
|
|
35650
|
+
});
|
|
35651
|
+
const nowIso = new Date(now2).toISOString();
|
|
35652
|
+
for (const row of staleBlocked.rows) {
|
|
35653
|
+
await client.execute({
|
|
35654
|
+
sql: `UPDATE tasks SET status = 'open', blocked_by = NULL, updated_at = ? WHERE id = ?`,
|
|
35655
|
+
args: [nowIso, String(row.id)]
|
|
35656
|
+
});
|
|
35657
|
+
process.stderr.write(
|
|
35658
|
+
`[exed] Auto-unblocked "${String(row.title)}" \u2014 blocker already ${String(row.blocker_status)}
|
|
35659
|
+
`
|
|
35660
|
+
);
|
|
35661
|
+
}
|
|
35662
|
+
} catch {
|
|
35663
|
+
}
|
|
35664
|
+
}
|
|
35267
35665
|
}
|
|
35268
|
-
var TASK_ENFORCEMENT_INTERVAL_MS, TASK_ENFORCEMENT_DEBOUNCE_MS, MANAGER_GRACE_PERIOD_MS, IDLE_PATTERNS, MANAGER_ROLES, AUDIT_LOG_PATH, _lastNudge;
|
|
35666
|
+
var TASK_ENFORCEMENT_INTERVAL_MS, TASK_ENFORCEMENT_DEBOUNCE_MS, MANAGER_GRACE_PERIOD_MS, IDLE_PATTERNS, NUDGE_ECHO_PATTERNS, MANAGER_ROLES, AUDIT_LOG_PATH, _lastNudge;
|
|
35269
35667
|
var init_task_enforcement = __esm({
|
|
35270
35668
|
"src/lib/task-enforcement.ts"() {
|
|
35271
35669
|
"use strict";
|
|
@@ -35280,6 +35678,13 @@ var init_task_enforcement = __esm({
|
|
|
35280
35678
|
"Anything else?",
|
|
35281
35679
|
"What do you need?"
|
|
35282
35680
|
];
|
|
35681
|
+
NUDGE_ECHO_PATTERNS = [
|
|
35682
|
+
"You have pending notifications",
|
|
35683
|
+
"You have open tasks. Run list_tasks",
|
|
35684
|
+
"You have more open tasks to dispatch",
|
|
35685
|
+
"Run list_tasks to check for assigned work",
|
|
35686
|
+
"Run list_tasks to find them"
|
|
35687
|
+
];
|
|
35283
35688
|
MANAGER_ROLES = ["COO", "CTO"];
|
|
35284
35689
|
AUDIT_LOG_PATH = path59.join(
|
|
35285
35690
|
process.env.HOME ?? process.env.USERPROFILE ?? "/tmp",
|
|
@@ -36023,8 +36428,8 @@ async function loadModel() {
|
|
|
36023
36428
|
const { getLlama } = await import("node-llama-cpp");
|
|
36024
36429
|
_llama = await getLlama();
|
|
36025
36430
|
_model = await _llama.loadModel({ modelPath });
|
|
36026
|
-
_context = await _model.createEmbeddingContext();
|
|
36027
|
-
process.stderr.write("[exed] Model loaded and ready.\n");
|
|
36431
|
+
_context = await _model.createEmbeddingContext({ contextSize: 8192 });
|
|
36432
|
+
process.stderr.write("[exed] Model loaded and ready (contextSize=8192).\n");
|
|
36028
36433
|
} catch (err) {
|
|
36029
36434
|
process.stderr.write(`[exed] Model load failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
36030
36435
|
`);
|
|
@@ -36199,6 +36604,11 @@ async function handleHealthCheck(socket, requestId) {
|
|
|
36199
36604
|
heap_used_mb: Math.round(mem.heapUsed / 1024 / 1024),
|
|
36200
36605
|
external_mb: Math.round(mem.external / 1024 / 1024),
|
|
36201
36606
|
array_buffers_mb: Math.round(mem.arrayBuffers / 1024 / 1024)
|
|
36607
|
+
},
|
|
36608
|
+
connections: {
|
|
36609
|
+
active: _activeConnections,
|
|
36610
|
+
high_queue: highQueue.length,
|
|
36611
|
+
low_queue: lowQueue.length
|
|
36202
36612
|
}
|
|
36203
36613
|
}
|
|
36204
36614
|
} : { error: "unhealthy: model not loaded or test embed failed" }
|
|
@@ -36578,10 +36988,12 @@ function startServer() {
|
|
|
36578
36988
|
}
|
|
36579
36989
|
});
|
|
36580
36990
|
socket.on("close", () => {
|
|
36991
|
+
buffer = "";
|
|
36581
36992
|
_activeConnections--;
|
|
36582
36993
|
checkIdle();
|
|
36583
36994
|
});
|
|
36584
36995
|
socket.on("error", () => {
|
|
36996
|
+
buffer = "";
|
|
36585
36997
|
_activeConnections--;
|
|
36586
36998
|
checkIdle();
|
|
36587
36999
|
});
|
|
@@ -36718,7 +37130,7 @@ async function startMcpHttpServer() {
|
|
|
36718
37130
|
const httpServer = createHttpServer(async (req, res) => {
|
|
36719
37131
|
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1");
|
|
36720
37132
|
res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
|
|
36721
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Agent-Id, X-Agent-Role, Mcp-Session-Id");
|
|
37133
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Agent-Id, X-Agent-Role, X-Exe-Session, Mcp-Session-Id");
|
|
36722
37134
|
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");
|
|
36723
37135
|
if (req.method === "OPTIONS") {
|
|
36724
37136
|
res.writeHead(204);
|
|
@@ -36726,9 +37138,19 @@ async function startMcpHttpServer() {
|
|
|
36726
37138
|
return;
|
|
36727
37139
|
}
|
|
36728
37140
|
const url = new URL(req.url || "/", `http://127.0.0.1:${MCP_HTTP_PORT}`);
|
|
37141
|
+
if (url.pathname === "/.well-known/oauth-protected-resource" || url.pathname === "/.well-known/oauth-protected-resource/mcp") {
|
|
37142
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
37143
|
+
res.end(JSON.stringify({
|
|
37144
|
+
resource: `http://127.0.0.1:${MCP_HTTP_PORT}/mcp`,
|
|
37145
|
+
authorization_servers: [],
|
|
37146
|
+
scopes_supported: [],
|
|
37147
|
+
bearer_methods_supported: ["header"]
|
|
37148
|
+
}));
|
|
37149
|
+
return;
|
|
37150
|
+
}
|
|
36729
37151
|
if (url.pathname !== "/mcp") {
|
|
36730
|
-
res.writeHead(404, { "Content-Type": "
|
|
36731
|
-
res.end("
|
|
37152
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
37153
|
+
res.end(JSON.stringify({ error: "not_found" }));
|
|
36732
37154
|
return;
|
|
36733
37155
|
}
|
|
36734
37156
|
const authHeader = req.headers.authorization;
|
|
@@ -36741,6 +37163,7 @@ async function startMcpHttpServer() {
|
|
|
36741
37163
|
}
|
|
36742
37164
|
const agentId = req.headers["x-agent-id"] || "default";
|
|
36743
37165
|
const agentRole = req.headers["x-agent-role"] || "employee";
|
|
37166
|
+
const sessionHint = req.headers["x-exe-session"] || "";
|
|
36744
37167
|
const runtime = inferMcpRuntime({
|
|
36745
37168
|
runtimeHeader: req.headers["x-agent-runtime"] || req.headers["x-runtime"],
|
|
36746
37169
|
userAgent: req.headers["user-agent"],
|
|
@@ -36777,7 +37200,7 @@ async function startMcpHttpServer() {
|
|
|
36777
37200
|
onsessioninitialized: (sid) => {
|
|
36778
37201
|
transports.set(sid, transport);
|
|
36779
37202
|
sessionLastSeen.set(sid, Date.now());
|
|
36780
|
-
sessionDetails.set(sid, { agentId, agentRole, runtime });
|
|
37203
|
+
sessionDetails.set(sid, { agentId, agentRole, runtime, sessionHint });
|
|
36781
37204
|
recordMcpHttpEvent({
|
|
36782
37205
|
level: "info",
|
|
36783
37206
|
message: "session_initialized",
|
|
@@ -36812,6 +37235,8 @@ async function startMcpHttpServer() {
|
|
|
36812
37235
|
});
|
|
36813
37236
|
return;
|
|
36814
37237
|
}
|
|
37238
|
+
const prevSession = process.env.EXE_SESSION_NAME;
|
|
37239
|
+
if (sessionHint) process.env.EXE_SESSION_NAME = sessionHint;
|
|
36815
37240
|
const startedAt = Date.now();
|
|
36816
37241
|
const parsedRequest = parsedBody;
|
|
36817
37242
|
const toolName = parsedRequest?.method === "tools/call" ? parsedRequest.params?.name : void 0;
|
|
@@ -36868,6 +37293,11 @@ async function startMcpHttpServer() {
|
|
|
36868
37293
|
runtime
|
|
36869
37294
|
});
|
|
36870
37295
|
}
|
|
37296
|
+
} finally {
|
|
37297
|
+
if (sessionHint) {
|
|
37298
|
+
if (prevSession) process.env.EXE_SESSION_NAME = prevSession;
|
|
37299
|
+
else delete process.env.EXE_SESSION_NAME;
|
|
37300
|
+
}
|
|
36871
37301
|
}
|
|
36872
37302
|
});
|
|
36873
37303
|
const MCP_HTTP_HOST = process.env.EXE_MCP_HOST || process.env.EXED_HOST || "127.0.0.1";
|