@askexenow/exe-os 0.9.112 → 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 +54 -11
- package/dist/bin/agentic-reflection-backfill.js +29 -1
- package/dist/bin/agentic-semantic-label.js +29 -1
- package/dist/bin/backfill-conversations.js +53 -10
- package/dist/bin/backfill-responses.js +54 -11
- package/dist/bin/backfill-vectors.js +29 -1
- package/dist/bin/bulk-sync-postgres.js +55 -12
- package/dist/bin/cleanup-stale-review-tasks.js +75 -15
- package/dist/bin/cli.js +293 -76
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +28 -2
- package/dist/bin/exe-assign.js +54 -11
- package/dist/bin/exe-boot.js +481 -147
- package/dist/bin/exe-call.js +45 -4
- package/dist/bin/exe-cloud.js +93 -15
- package/dist/bin/exe-dispatch.js +369 -24
- package/dist/bin/exe-doctor.js +53 -10
- package/dist/bin/exe-export-behaviors.js +54 -11
- package/dist/bin/exe-forget.js +54 -11
- package/dist/bin/exe-gateway.js +128 -23
- package/dist/bin/exe-heartbeat.js +75 -15
- package/dist/bin/exe-kill.js +54 -11
- package/dist/bin/exe-launch-agent.js +70 -12
- package/dist/bin/exe-new-employee.js +175 -7
- package/dist/bin/exe-pending-messages.js +75 -15
- package/dist/bin/exe-pending-notifications.js +75 -15
- package/dist/bin/exe-pending-reviews.js +75 -15
- package/dist/bin/exe-rename.js +54 -11
- package/dist/bin/exe-review.js +54 -11
- package/dist/bin/exe-search.js +54 -11
- package/dist/bin/exe-session-cleanup.js +491 -146
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +524 -245
- package/dist/bin/exe-start-opencode.js +534 -165
- package/dist/bin/exe-status.js +75 -15
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +54 -11
- package/dist/bin/git-sweep.js +369 -24
- package/dist/bin/graph-backfill.js +54 -11
- package/dist/bin/graph-export.js +54 -11
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +491 -146
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +369 -24
- package/dist/bin/setup.js +91 -13
- package/dist/bin/shard-migrate.js +54 -11
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +128 -23
- package/dist/hooks/bug-report-worker.js +128 -23
- package/dist/hooks/codex-stop-task-finalizer.js +512 -140
- package/dist/hooks/commit-complete.js +369 -24
- package/dist/hooks/error-recall.js +54 -11
- package/dist/hooks/ingest.js +4575 -252
- package/dist/hooks/instructions-loaded.js +54 -11
- package/dist/hooks/notification.js +54 -11
- package/dist/hooks/post-compact.js +75 -15
- package/dist/hooks/post-tool-combined.js +75 -15
- package/dist/hooks/pre-compact.js +449 -104
- package/dist/hooks/pre-tool-use.js +90 -15
- package/dist/hooks/prompt-submit.js +129 -24
- package/dist/hooks/session-end.js +451 -109
- package/dist/hooks/session-start.js +104 -16
- package/dist/hooks/stop.js +74 -14
- package/dist/hooks/subagent-stop.js +75 -15
- package/dist/hooks/summary-worker.js +73 -7
- package/dist/index.js +128 -23
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +38 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +16 -0
- package/dist/lib/db.js +16 -0
- package/dist/lib/device-registry.js +16 -0
- package/dist/lib/employee-templates.js +29 -3
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +268 -42
- package/dist/lib/hybrid-search.js +54 -11
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +29 -1
- package/dist/lib/skill-learning.js +458 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +54 -11
- package/dist/lib/tasks.js +393 -91
- package/dist/lib/tmux-routing.js +316 -14
- package/dist/mcp/server.js +169 -30
- package/dist/mcp/tools/create-task.js +75 -13
- 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 +390 -91
- package/dist/runtime/index.js +446 -101
- package/dist/tui/App.js +208 -54
- 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];
|
|
@@ -3659,6 +3674,22 @@ async function ensureSchema() {
|
|
|
3659
3674
|
} catch (e) {
|
|
3660
3675
|
logCatchDebug("migration", e);
|
|
3661
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
|
+
}
|
|
3662
3693
|
}
|
|
3663
3694
|
async function disposeDatabase() {
|
|
3664
3695
|
if (_walCheckpointTimer) {
|
|
@@ -6072,11 +6103,17 @@ var init_platform_procedures = __esm({
|
|
|
6072
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."
|
|
6073
6104
|
},
|
|
6074
6105
|
{
|
|
6075
|
-
title: "
|
|
6106
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
6076
6107
|
domain: "workflow",
|
|
6077
6108
|
priority: "p1",
|
|
6078
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."
|
|
6079
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
|
+
},
|
|
6080
6117
|
{
|
|
6081
6118
|
title: "Single dispatch path \u2014 create_task only",
|
|
6082
6119
|
domain: "workflow",
|
|
@@ -6110,6 +6147,12 @@ var init_platform_procedures = __esm({
|
|
|
6110
6147
|
priority: "p0",
|
|
6111
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."
|
|
6112
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
|
+
},
|
|
6113
6156
|
{
|
|
6114
6157
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
6115
6158
|
domain: "support",
|
|
@@ -6395,10 +6438,24 @@ function stableId3(memoryId, type, content) {
|
|
|
6395
6438
|
return createHash3("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
6396
6439
|
}
|
|
6397
6440
|
function cleanText(text3) {
|
|
6398
|
-
|
|
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;
|
|
6399
6447
|
}
|
|
6400
|
-
function
|
|
6401
|
-
|
|
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;
|
|
6402
6459
|
}
|
|
6403
6460
|
function inferCardType(sentence, toolName) {
|
|
6404
6461
|
const lower = sentence.toLowerCase();
|
|
@@ -6430,12 +6487,12 @@ function predicateFor(type) {
|
|
|
6430
6487
|
}
|
|
6431
6488
|
}
|
|
6432
6489
|
function extractMemoryCards(row) {
|
|
6433
|
-
const
|
|
6490
|
+
const segments = splitSegments(row.raw_text);
|
|
6434
6491
|
const cards = [];
|
|
6435
|
-
for (const sentence of
|
|
6492
|
+
for (const sentence of segments) {
|
|
6436
6493
|
const type = inferCardType(sentence, row.tool_name);
|
|
6437
6494
|
const subject = extractSubject(sentence, row.agent_id);
|
|
6438
|
-
const content = sentence.length >
|
|
6495
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
6439
6496
|
cards.push({
|
|
6440
6497
|
id: stableId3(row.id, type, content),
|
|
6441
6498
|
memory_id: row.id,
|
|
@@ -6531,13 +6588,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
6531
6588
|
last_accessed: String(row.timestamp)
|
|
6532
6589
|
}));
|
|
6533
6590
|
}
|
|
6534
|
-
var MAX_CARDS_PER_MEMORY,
|
|
6591
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
6535
6592
|
var init_memory_cards = __esm({
|
|
6536
6593
|
"src/lib/memory-cards.ts"() {
|
|
6537
6594
|
"use strict";
|
|
6538
6595
|
init_database();
|
|
6539
|
-
MAX_CARDS_PER_MEMORY =
|
|
6540
|
-
|
|
6596
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
6597
|
+
MAX_SEGMENT_CHARS = 500;
|
|
6598
|
+
MIN_SEGMENT_CHARS = 20;
|
|
6541
6599
|
}
|
|
6542
6600
|
});
|
|
6543
6601
|
|
|
@@ -8728,7 +8786,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
8728
8786
|
try {
|
|
8729
8787
|
const client = getClient();
|
|
8730
8788
|
void client.execute({
|
|
8731
|
-
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})`,
|
|
8732
8790
|
args: [now2, ...ids]
|
|
8733
8791
|
}).catch(() => {
|
|
8734
8792
|
});
|
|
@@ -10093,7 +10151,7 @@ async function assertVpsLicense(opts) {
|
|
|
10093
10151
|
}
|
|
10094
10152
|
if (!transientFailure) {
|
|
10095
10153
|
throw new Error(
|
|
10096
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
10154
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
10097
10155
|
);
|
|
10098
10156
|
}
|
|
10099
10157
|
const fresh = await getCachedLicense();
|
|
@@ -10130,7 +10188,7 @@ async function assertVpsLicense(opts) {
|
|
|
10130
10188
|
} catch {
|
|
10131
10189
|
}
|
|
10132
10190
|
throw new Error(
|
|
10133
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
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.`
|
|
10134
10192
|
);
|
|
10135
10193
|
}
|
|
10136
10194
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -10162,7 +10220,7 @@ var init_license = __esm({
|
|
|
10162
10220
|
LICENSE_PATH = path17.join(EXE_AI_DIR, "license.key");
|
|
10163
10221
|
CACHE_PATH = path17.join(EXE_AI_DIR, "license-cache.json");
|
|
10164
10222
|
DEVICE_ID_PATH = path17.join(EXE_AI_DIR, "device-id");
|
|
10165
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
10223
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
10166
10224
|
RETRY_DELAY_MS = 500;
|
|
10167
10225
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
10168
10226
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -13344,11 +13402,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
13344
13402
|
}
|
|
13345
13403
|
}
|
|
13346
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
|
+
}
|
|
13347
13409
|
const mySession = getMySession();
|
|
13348
13410
|
if (!mySession) {
|
|
13349
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
13350
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
13351
|
-
}
|
|
13352
13411
|
return null;
|
|
13353
13412
|
}
|
|
13354
13413
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -13363,6 +13422,10 @@ function resolveExeSession() {
|
|
|
13363
13422
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
13364
13423
|
`
|
|
13365
13424
|
);
|
|
13425
|
+
try {
|
|
13426
|
+
registerParentExe(key, fromSessionName);
|
|
13427
|
+
} catch {
|
|
13428
|
+
}
|
|
13366
13429
|
candidate = fromSessionName;
|
|
13367
13430
|
} else {
|
|
13368
13431
|
candidate = fromCache;
|
|
@@ -14296,6 +14359,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
14296
14359
|
args: [identifier, ...scope.args]
|
|
14297
14360
|
});
|
|
14298
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
|
+
}
|
|
14299
14375
|
result3 = await client.execute({
|
|
14300
14376
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
14301
14377
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -14842,12 +14918,13 @@ async function cascadeUnblock(taskId, baseDir, now2) {
|
|
|
14842
14918
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
14843
14919
|
args: [now2, taskId]
|
|
14844
14920
|
});
|
|
14845
|
-
if (
|
|
14846
|
-
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
|
|
14850
|
-
|
|
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) {
|
|
14851
14928
|
for (const ur of unblockedRows.rows) {
|
|
14852
14929
|
try {
|
|
14853
14930
|
const ubFile = path28.join(baseDir, String(ur.task_file));
|
|
@@ -14859,6 +14936,19 @@ async function cascadeUnblock(taskId, baseDir, now2) {
|
|
|
14859
14936
|
}
|
|
14860
14937
|
}
|
|
14861
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
|
+
}
|
|
14862
14952
|
}
|
|
14863
14953
|
async function findNextTask(assignedTo) {
|
|
14864
14954
|
const client = getClient();
|
|
@@ -15000,6 +15090,15 @@ var init_tasks_notify = __esm({
|
|
|
15000
15090
|
// src/lib/behaviors.ts
|
|
15001
15091
|
import crypto9 from "crypto";
|
|
15002
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
|
+
}
|
|
15003
15102
|
const client = getClient();
|
|
15004
15103
|
const id = crypto9.randomUUID();
|
|
15005
15104
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15485,6 +15584,12 @@ async function updateTask(input) {
|
|
|
15485
15584
|
}
|
|
15486
15585
|
}
|
|
15487
15586
|
}
|
|
15587
|
+
if (input.status === "cancelled") {
|
|
15588
|
+
try {
|
|
15589
|
+
await cascadeUnblock(taskId, input.baseDir, now2);
|
|
15590
|
+
} catch {
|
|
15591
|
+
}
|
|
15592
|
+
}
|
|
15488
15593
|
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
15489
15594
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
15490
15595
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -20926,15 +21031,16 @@ function registerSetAgentConfig(server) {
|
|
|
20926
21031
|
"set_agent_config",
|
|
20927
21032
|
{
|
|
20928
21033
|
title: "Set Agent Config",
|
|
20929
|
-
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).",
|
|
20930
21035
|
inputSchema: {
|
|
20931
21036
|
agent_id: z48.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
|
|
20932
21037
|
runtime: z48.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
|
|
20933
21038
|
model: z48.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)"),
|
|
20934
|
-
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.")
|
|
20935
21041
|
}
|
|
20936
21042
|
},
|
|
20937
|
-
async ({ agent_id, runtime, model, reasoning_effort }) => {
|
|
21043
|
+
async ({ agent_id, runtime, model, reasoning_effort, mcps }) => {
|
|
20938
21044
|
const { agentId, agentRole } = getActiveAgent();
|
|
20939
21045
|
const isCoordinator = isCoordinatorRole(agentRole) || (() => {
|
|
20940
21046
|
try {
|
|
@@ -20966,15 +21072,26 @@ function registerSetAgentConfig(server) {
|
|
|
20966
21072
|
isError: true
|
|
20967
21073
|
};
|
|
20968
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
|
+
}
|
|
20969
21085
|
if (!runtime || !model) {
|
|
20970
21086
|
if (agent_id) {
|
|
20971
21087
|
const cfg = getAgentRuntime(agent_id);
|
|
20972
21088
|
const label = RUNTIME_LABELS[cfg.runtime] ?? cfg.runtime;
|
|
21089
|
+
const mcpInfo = cfg.mcps ? ` | MCPs: [${cfg.mcps.join(", ")}]` : " | MCPs: all";
|
|
20973
21090
|
return {
|
|
20974
21091
|
content: [
|
|
20975
21092
|
{
|
|
20976
21093
|
type: "text",
|
|
20977
|
-
text: `${agent_id}: ${label} / ${cfg.model}`
|
|
21094
|
+
text: `${agent_id}: ${label} / ${cfg.model}${mcpInfo}`
|
|
20978
21095
|
}
|
|
20979
21096
|
]
|
|
20980
21097
|
};
|
|
@@ -21021,7 +21138,7 @@ function registerSetAgentConfig(server) {
|
|
|
21021
21138
|
isError: true
|
|
21022
21139
|
};
|
|
21023
21140
|
}
|
|
21024
|
-
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort);
|
|
21141
|
+
const result3 = setAgentRuntime(agent_id, runtime, model, reasoning_effort, mcps);
|
|
21025
21142
|
if (!result3.ok) {
|
|
21026
21143
|
return {
|
|
21027
21144
|
content: [{ type: "text", text: result3.error }],
|
|
@@ -21030,6 +21147,7 @@ function registerSetAgentConfig(server) {
|
|
|
21030
21147
|
}
|
|
21031
21148
|
const parts = [`Set ${agent_id} \u2192 runtime=${runtime} model=${model}`];
|
|
21032
21149
|
if (reasoning_effort) parts[0] += ` reasoning_effort=${reasoning_effort}`;
|
|
21150
|
+
if (mcps) parts[0] += ` mcps=[${mcps.join(", ")}]`;
|
|
21033
21151
|
return {
|
|
21034
21152
|
content: [{ type: "text", text: parts[0] }]
|
|
21035
21153
|
};
|
|
@@ -22629,10 +22747,18 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
|
|
|
22629
22747
|
} catch {
|
|
22630
22748
|
return [];
|
|
22631
22749
|
}
|
|
22750
|
+
const coordinatorSet = new Set(coordinatorSessions);
|
|
22632
22751
|
const agentTasks = /* @__PURE__ */ new Map();
|
|
22633
22752
|
for (const t of tasksByAgent) {
|
|
22634
22753
|
const scope = t.sessionScope || coordinatorSessions[0] || null;
|
|
22635
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
|
+
}
|
|
22636
22762
|
const key = `${t.agentId}::${scope}`;
|
|
22637
22763
|
const existing = agentTasks.get(key) ?? [];
|
|
22638
22764
|
existing.push({ taskId: t.taskId, priority: t.priority, sessionScope: scope });
|
|
@@ -25359,6 +25485,27 @@ async function cloudSync(config2) {
|
|
|
25359
25485
|
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
25360
25486
|
pulled = pullResult.records.length;
|
|
25361
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
|
+
}
|
|
25362
25509
|
const stmts = pullResult.records.map((rec) => ({
|
|
25363
25510
|
sql: `INSERT OR REPLACE INTO memories
|
|
25364
25511
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -28413,7 +28560,7 @@ var init_starter_packs = __esm({
|
|
|
28413
28560
|
|
|
28414
28561
|
// src/lib/employee-templates.ts
|
|
28415
28562
|
function renderClientCOOTemplate(vars) {
|
|
28416
|
-
const resolved = { ...vars, title: vars.title || "Chief
|
|
28563
|
+
const resolved = { ...vars, title: vars.title || "Chief of Staff" };
|
|
28417
28564
|
for (const key of CLIENT_COO_PLACEHOLDERS) {
|
|
28418
28565
|
const value = resolved[key];
|
|
28419
28566
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -35300,12 +35447,14 @@ __export(task_enforcement_exports, {
|
|
|
35300
35447
|
IDLE_PATTERNS: () => IDLE_PATTERNS,
|
|
35301
35448
|
MANAGER_GRACE_PERIOD_MS: () => MANAGER_GRACE_PERIOD_MS,
|
|
35302
35449
|
MANAGER_ROLES: () => MANAGER_ROLES,
|
|
35450
|
+
NUDGE_ECHO_PATTERNS: () => NUDGE_ECHO_PATTERNS,
|
|
35303
35451
|
TASK_ENFORCEMENT_DEBOUNCE_MS: () => TASK_ENFORCEMENT_DEBOUNCE_MS,
|
|
35304
35452
|
TASK_ENFORCEMENT_INTERVAL_MS: () => TASK_ENFORCEMENT_INTERVAL_MS,
|
|
35305
35453
|
_resetNudgeState: () => _resetNudgeState,
|
|
35306
35454
|
decideManagerAction: () => decideManagerAction,
|
|
35307
35455
|
decideWorkerAction: () => decideWorkerAction,
|
|
35308
35456
|
isIdlePane: () => isIdlePane,
|
|
35457
|
+
isNudgeEcho: () => isNudgeEcho,
|
|
35309
35458
|
runTaskEnforcementTick: () => runTaskEnforcementTick,
|
|
35310
35459
|
sendNudge: () => sendNudge
|
|
35311
35460
|
});
|
|
@@ -35318,20 +35467,26 @@ function writeAuditEntry(entry) {
|
|
|
35318
35467
|
} catch {
|
|
35319
35468
|
}
|
|
35320
35469
|
}
|
|
35321
|
-
function decideManagerAction(counts, isIdle, withinGracePeriod) {
|
|
35470
|
+
function decideManagerAction(counts, isIdle, withinGracePeriod, nudgeEcho) {
|
|
35322
35471
|
if (counts.openTasks === 0) return "skip";
|
|
35323
35472
|
if (withinGracePeriod) return "grace_period";
|
|
35324
35473
|
if (!isIdle) return "active";
|
|
35474
|
+
if (nudgeEcho) return "active";
|
|
35475
|
+
if ((counts.inProgressTasks ?? 0) > 0) return "active";
|
|
35325
35476
|
if (counts.activeSubtasks === 0) return "nudge";
|
|
35326
35477
|
return "soft_nudge";
|
|
35327
35478
|
}
|
|
35328
|
-
function decideWorkerAction(taskCount, isIdle) {
|
|
35479
|
+
function decideWorkerAction(taskCount, isIdle, nudgeEcho) {
|
|
35329
35480
|
if (taskCount === 0) return "skip";
|
|
35481
|
+
if (nudgeEcho) return "idle";
|
|
35330
35482
|
return isIdle ? "nudge" : "idle";
|
|
35331
35483
|
}
|
|
35332
35484
|
function isIdlePane(paneText) {
|
|
35333
35485
|
return IDLE_PATTERNS.some((p) => paneText.includes(p));
|
|
35334
35486
|
}
|
|
35487
|
+
function isNudgeEcho(paneText) {
|
|
35488
|
+
return NUDGE_ECHO_PATTERNS.some((p) => paneText.includes(p));
|
|
35489
|
+
}
|
|
35335
35490
|
function sendNudge(transport, session, runtime, action) {
|
|
35336
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.";
|
|
35337
35492
|
if (transport.sendKeysLiteral) {
|
|
@@ -35351,16 +35506,21 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35351
35506
|
const { transport, employees, client, scopeFilter } = deps;
|
|
35352
35507
|
const now2 = deps.now ?? Date.now();
|
|
35353
35508
|
const sessions = transport.listSessions();
|
|
35509
|
+
let processedAnyEmployee = false;
|
|
35354
35510
|
for (const session of sessions) {
|
|
35355
35511
|
if (!session.includes("-")) continue;
|
|
35356
35512
|
const agentName = session.split("-")[0]?.replace(/\d+$/, "");
|
|
35357
35513
|
if (!agentName) continue;
|
|
35514
|
+
if ("isAlive" in transport && typeof transport.isAlive === "function") {
|
|
35515
|
+
if (!transport.isAlive(session)) continue;
|
|
35516
|
+
}
|
|
35358
35517
|
const lastNudgeTime = _lastNudge.get(session) ?? 0;
|
|
35359
35518
|
if (now2 - lastNudgeTime < TASK_ENFORCEMENT_DEBOUNCE_MS) {
|
|
35360
35519
|
continue;
|
|
35361
35520
|
}
|
|
35362
35521
|
const employee = employees.find((e) => e.name === agentName);
|
|
35363
35522
|
if (!employee) continue;
|
|
35523
|
+
processedAnyEmployee = true;
|
|
35364
35524
|
let effectiveScope = scopeFilter;
|
|
35365
35525
|
const dashIndex = session.indexOf("-");
|
|
35366
35526
|
if (dashIndex > 0) {
|
|
@@ -35408,12 +35568,21 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35408
35568
|
}
|
|
35409
35569
|
}
|
|
35410
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
|
+
}
|
|
35411
35579
|
const pane = transport.capturePane(session, 20);
|
|
35412
35580
|
paneIdle = isIdlePane(pane);
|
|
35413
|
-
|
|
35581
|
+
const nudgeEcho = isNudgeEcho(pane);
|
|
35582
|
+
action = decideManagerAction({ openTasks, activeSubtasks, inProgressTasks }, paneIdle, withinGracePeriod, nudgeEcho);
|
|
35414
35583
|
if (action === "skip") reason = "no open tasks";
|
|
35415
35584
|
else if (action === "grace_period") reason = `task assigned <10min ago (${graceRemaining}s remaining)`;
|
|
35416
|
-
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";
|
|
35417
35586
|
else if (action === "nudge") reason = `${openTasks} open tasks, 0 delegated, pane idle`;
|
|
35418
35587
|
else if (action === "soft_nudge") reason = `${openTasks} open tasks, ${activeSubtasks} delegated, pane idle`;
|
|
35419
35588
|
writeAuditEntry({
|
|
@@ -35442,7 +35611,8 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35442
35611
|
if (taskCount > 0) {
|
|
35443
35612
|
const pane = transport.capturePane(session, 20);
|
|
35444
35613
|
paneIdle = isIdlePane(pane);
|
|
35445
|
-
|
|
35614
|
+
const workerNudgeEcho = isNudgeEcho(pane);
|
|
35615
|
+
action = decideWorkerAction(taskCount, paneIdle, workerNudgeEcho);
|
|
35446
35616
|
}
|
|
35447
35617
|
if (action === "skip") reason = "no tasks";
|
|
35448
35618
|
else if (action === "idle") reason = `${taskCount} tasks but pane active`;
|
|
@@ -35468,8 +35638,32 @@ async function runTaskEnforcementTick(deps) {
|
|
|
35468
35638
|
} catch {
|
|
35469
35639
|
}
|
|
35470
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
|
+
}
|
|
35471
35665
|
}
|
|
35472
|
-
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;
|
|
35473
35667
|
var init_task_enforcement = __esm({
|
|
35474
35668
|
"src/lib/task-enforcement.ts"() {
|
|
35475
35669
|
"use strict";
|
|
@@ -35484,6 +35678,13 @@ var init_task_enforcement = __esm({
|
|
|
35484
35678
|
"Anything else?",
|
|
35485
35679
|
"What do you need?"
|
|
35486
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
|
+
];
|
|
35487
35688
|
MANAGER_ROLES = ["COO", "CTO"];
|
|
35488
35689
|
AUDIT_LOG_PATH = path59.join(
|
|
35489
35690
|
process.env.HOME ?? process.env.USERPROFILE ?? "/tmp",
|
|
@@ -36227,8 +36428,8 @@ async function loadModel() {
|
|
|
36227
36428
|
const { getLlama } = await import("node-llama-cpp");
|
|
36228
36429
|
_llama = await getLlama();
|
|
36229
36430
|
_model = await _llama.loadModel({ modelPath });
|
|
36230
|
-
_context = await _model.createEmbeddingContext();
|
|
36231
|
-
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");
|
|
36232
36433
|
} catch (err) {
|
|
36233
36434
|
process.stderr.write(`[exed] Model load failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
36234
36435
|
`);
|
|
@@ -36403,6 +36604,11 @@ async function handleHealthCheck(socket, requestId) {
|
|
|
36403
36604
|
heap_used_mb: Math.round(mem.heapUsed / 1024 / 1024),
|
|
36404
36605
|
external_mb: Math.round(mem.external / 1024 / 1024),
|
|
36405
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
|
|
36406
36612
|
}
|
|
36407
36613
|
}
|
|
36408
36614
|
} : { error: "unhealthy: model not loaded or test embed failed" }
|
|
@@ -36782,10 +36988,12 @@ function startServer() {
|
|
|
36782
36988
|
}
|
|
36783
36989
|
});
|
|
36784
36990
|
socket.on("close", () => {
|
|
36991
|
+
buffer = "";
|
|
36785
36992
|
_activeConnections--;
|
|
36786
36993
|
checkIdle();
|
|
36787
36994
|
});
|
|
36788
36995
|
socket.on("error", () => {
|
|
36996
|
+
buffer = "";
|
|
36789
36997
|
_activeConnections--;
|
|
36790
36998
|
checkIdle();
|
|
36791
36999
|
});
|
|
@@ -36922,7 +37130,7 @@ async function startMcpHttpServer() {
|
|
|
36922
37130
|
const httpServer = createHttpServer(async (req, res) => {
|
|
36923
37131
|
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1");
|
|
36924
37132
|
res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
|
|
36925
|
-
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");
|
|
36926
37134
|
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");
|
|
36927
37135
|
if (req.method === "OPTIONS") {
|
|
36928
37136
|
res.writeHead(204);
|
|
@@ -36930,9 +37138,19 @@ async function startMcpHttpServer() {
|
|
|
36930
37138
|
return;
|
|
36931
37139
|
}
|
|
36932
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
|
+
}
|
|
36933
37151
|
if (url.pathname !== "/mcp") {
|
|
36934
|
-
res.writeHead(404, { "Content-Type": "
|
|
36935
|
-
res.end("
|
|
37152
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
37153
|
+
res.end(JSON.stringify({ error: "not_found" }));
|
|
36936
37154
|
return;
|
|
36937
37155
|
}
|
|
36938
37156
|
const authHeader = req.headers.authorization;
|
|
@@ -36945,6 +37163,7 @@ async function startMcpHttpServer() {
|
|
|
36945
37163
|
}
|
|
36946
37164
|
const agentId = req.headers["x-agent-id"] || "default";
|
|
36947
37165
|
const agentRole = req.headers["x-agent-role"] || "employee";
|
|
37166
|
+
const sessionHint = req.headers["x-exe-session"] || "";
|
|
36948
37167
|
const runtime = inferMcpRuntime({
|
|
36949
37168
|
runtimeHeader: req.headers["x-agent-runtime"] || req.headers["x-runtime"],
|
|
36950
37169
|
userAgent: req.headers["user-agent"],
|
|
@@ -36981,7 +37200,7 @@ async function startMcpHttpServer() {
|
|
|
36981
37200
|
onsessioninitialized: (sid) => {
|
|
36982
37201
|
transports.set(sid, transport);
|
|
36983
37202
|
sessionLastSeen.set(sid, Date.now());
|
|
36984
|
-
sessionDetails.set(sid, { agentId, agentRole, runtime });
|
|
37203
|
+
sessionDetails.set(sid, { agentId, agentRole, runtime, sessionHint });
|
|
36985
37204
|
recordMcpHttpEvent({
|
|
36986
37205
|
level: "info",
|
|
36987
37206
|
message: "session_initialized",
|
|
@@ -37016,6 +37235,8 @@ async function startMcpHttpServer() {
|
|
|
37016
37235
|
});
|
|
37017
37236
|
return;
|
|
37018
37237
|
}
|
|
37238
|
+
const prevSession = process.env.EXE_SESSION_NAME;
|
|
37239
|
+
if (sessionHint) process.env.EXE_SESSION_NAME = sessionHint;
|
|
37019
37240
|
const startedAt = Date.now();
|
|
37020
37241
|
const parsedRequest = parsedBody;
|
|
37021
37242
|
const toolName = parsedRequest?.method === "tools/call" ? parsedRequest.params?.name : void 0;
|
|
@@ -37072,6 +37293,11 @@ async function startMcpHttpServer() {
|
|
|
37072
37293
|
runtime
|
|
37073
37294
|
});
|
|
37074
37295
|
}
|
|
37296
|
+
} finally {
|
|
37297
|
+
if (sessionHint) {
|
|
37298
|
+
if (prevSession) process.env.EXE_SESSION_NAME = prevSession;
|
|
37299
|
+
else delete process.env.EXE_SESSION_NAME;
|
|
37300
|
+
}
|
|
37075
37301
|
}
|
|
37076
37302
|
});
|
|
37077
37303
|
const MCP_HTTP_HOST = process.env.EXE_MCP_HOST || process.env.EXED_HOST || "127.0.0.1";
|