@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/bin/exe-boot.js
CHANGED
|
@@ -331,11 +331,168 @@ var init_config = __esm({
|
|
|
331
331
|
}
|
|
332
332
|
});
|
|
333
333
|
|
|
334
|
+
// src/lib/runtime-table.ts
|
|
335
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
336
|
+
var init_runtime_table = __esm({
|
|
337
|
+
"src/lib/runtime-table.ts"() {
|
|
338
|
+
"use strict";
|
|
339
|
+
RUNTIME_TABLE = {
|
|
340
|
+
codex: {
|
|
341
|
+
binary: "codex",
|
|
342
|
+
launchMode: "interactive",
|
|
343
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
344
|
+
inlineFlag: "--no-alt-screen",
|
|
345
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
346
|
+
defaultModel: "gpt-5.5"
|
|
347
|
+
},
|
|
348
|
+
opencode: {
|
|
349
|
+
binary: "opencode",
|
|
350
|
+
launchMode: "exec",
|
|
351
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
352
|
+
inlineFlag: "",
|
|
353
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
354
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
DEFAULT_RUNTIME = "claude";
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// src/lib/agent-config.ts
|
|
362
|
+
var agent_config_exports = {};
|
|
363
|
+
__export(agent_config_exports, {
|
|
364
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
365
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
366
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
367
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
368
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
369
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
370
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
371
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
372
|
+
setAgentMcps: () => setAgentMcps,
|
|
373
|
+
setAgentRuntime: () => setAgentRuntime
|
|
374
|
+
});
|
|
375
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
376
|
+
import path2 from "path";
|
|
377
|
+
function loadAgentConfig() {
|
|
378
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
379
|
+
try {
|
|
380
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
381
|
+
} catch {
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function saveAgentConfig(config) {
|
|
386
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
387
|
+
ensurePrivateDirSync(dir);
|
|
388
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
389
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
390
|
+
}
|
|
391
|
+
function getAgentRuntime(agentId) {
|
|
392
|
+
const config = loadAgentConfig();
|
|
393
|
+
const entry = config[agentId];
|
|
394
|
+
if (entry) return entry;
|
|
395
|
+
const orgDefault = config["default"];
|
|
396
|
+
if (orgDefault) return orgDefault;
|
|
397
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
398
|
+
}
|
|
399
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
400
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
401
|
+
if (!knownModels) {
|
|
402
|
+
return {
|
|
403
|
+
ok: false,
|
|
404
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (!knownModels.includes(model)) {
|
|
408
|
+
return {
|
|
409
|
+
ok: false,
|
|
410
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const config = loadAgentConfig();
|
|
414
|
+
const existing = config[agentId];
|
|
415
|
+
const entry = { runtime, model };
|
|
416
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
417
|
+
if (mcps !== void 0) {
|
|
418
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
419
|
+
} else if (existing?.mcps) {
|
|
420
|
+
entry.mcps = existing.mcps;
|
|
421
|
+
}
|
|
422
|
+
config[agentId] = entry;
|
|
423
|
+
saveAgentConfig(config);
|
|
424
|
+
return { ok: true };
|
|
425
|
+
}
|
|
426
|
+
function setAgentMcps(agentId, mcps) {
|
|
427
|
+
const config = loadAgentConfig();
|
|
428
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
429
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
430
|
+
config[agentId] = existing;
|
|
431
|
+
saveAgentConfig(config);
|
|
432
|
+
return { ok: true };
|
|
433
|
+
}
|
|
434
|
+
function clearAgentRuntime(agentId) {
|
|
435
|
+
const config = loadAgentConfig();
|
|
436
|
+
delete config[agentId];
|
|
437
|
+
saveAgentConfig(config);
|
|
438
|
+
}
|
|
439
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
440
|
+
var init_agent_config = __esm({
|
|
441
|
+
"src/lib/agent-config.ts"() {
|
|
442
|
+
"use strict";
|
|
443
|
+
init_config();
|
|
444
|
+
init_runtime_table();
|
|
445
|
+
init_secure_files();
|
|
446
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
447
|
+
KNOWN_RUNTIMES = {
|
|
448
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
449
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
450
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
451
|
+
};
|
|
452
|
+
RUNTIME_LABELS = {
|
|
453
|
+
claude: "Claude Code (Anthropic)",
|
|
454
|
+
codex: "Codex (OpenAI)",
|
|
455
|
+
opencode: "OpenCode (open source)"
|
|
456
|
+
};
|
|
457
|
+
DEFAULT_MODELS = {
|
|
458
|
+
claude: "claude-opus-4.6",
|
|
459
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
460
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
334
465
|
// src/lib/employees.ts
|
|
466
|
+
var employees_exports = {};
|
|
467
|
+
__export(employees_exports, {
|
|
468
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
469
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
470
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
471
|
+
addEmployee: () => addEmployee,
|
|
472
|
+
baseAgentName: () => baseAgentName,
|
|
473
|
+
canCoordinate: () => canCoordinate,
|
|
474
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
475
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
476
|
+
getEmployee: () => getEmployee,
|
|
477
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
478
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
479
|
+
hasRole: () => hasRole,
|
|
480
|
+
hireEmployee: () => hireEmployee,
|
|
481
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
482
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
483
|
+
isMultiInstance: () => isMultiInstance,
|
|
484
|
+
loadEmployees: () => loadEmployees,
|
|
485
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
486
|
+
normalizeRole: () => normalizeRole,
|
|
487
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
488
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
489
|
+
saveEmployees: () => saveEmployees,
|
|
490
|
+
validateEmployeeName: () => validateEmployeeName
|
|
491
|
+
});
|
|
335
492
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
336
|
-
import { existsSync as
|
|
493
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
337
494
|
import { execSync } from "child_process";
|
|
338
|
-
import
|
|
495
|
+
import path3 from "path";
|
|
339
496
|
import os2 from "os";
|
|
340
497
|
function normalizeRole(role) {
|
|
341
498
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -353,8 +510,26 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
353
510
|
if (!agentName) return false;
|
|
354
511
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
355
512
|
}
|
|
513
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
514
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
515
|
+
}
|
|
516
|
+
function validateEmployeeName(name) {
|
|
517
|
+
if (!name) {
|
|
518
|
+
return { valid: false, error: "Name is required" };
|
|
519
|
+
}
|
|
520
|
+
if (name.length > 32) {
|
|
521
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
522
|
+
}
|
|
523
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
524
|
+
return {
|
|
525
|
+
valid: false,
|
|
526
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return { valid: true };
|
|
530
|
+
}
|
|
356
531
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
357
|
-
if (!
|
|
532
|
+
if (!existsSync4(employeesPath)) {
|
|
358
533
|
return [];
|
|
359
534
|
}
|
|
360
535
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -365,13 +540,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
365
540
|
}
|
|
366
541
|
}
|
|
367
542
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
368
|
-
await mkdir2(
|
|
543
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
369
544
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
370
545
|
}
|
|
371
546
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
372
|
-
if (!
|
|
547
|
+
if (!existsSync4(employeesPath)) return [];
|
|
373
548
|
try {
|
|
374
|
-
return JSON.parse(
|
|
549
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
375
550
|
} catch {
|
|
376
551
|
return [];
|
|
377
552
|
}
|
|
@@ -379,6 +554,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
379
554
|
function getEmployee(employees, name) {
|
|
380
555
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
381
556
|
}
|
|
557
|
+
function getEmployeeByRole(employees, role) {
|
|
558
|
+
const lower = role.toLowerCase();
|
|
559
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
560
|
+
}
|
|
561
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
562
|
+
const lower = role.toLowerCase();
|
|
563
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
564
|
+
}
|
|
565
|
+
function hasRole(agentName, role) {
|
|
566
|
+
const employees = loadEmployeesSync();
|
|
567
|
+
const emp = getEmployee(employees, agentName);
|
|
568
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
569
|
+
}
|
|
382
570
|
function baseAgentName(name, employees) {
|
|
383
571
|
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
384
572
|
if (!match) return name;
|
|
@@ -393,6 +581,90 @@ function isMultiInstance(agentName, employees) {
|
|
|
393
581
|
if (!emp) return false;
|
|
394
582
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
395
583
|
}
|
|
584
|
+
function addEmployee(employees, employee) {
|
|
585
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
586
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
587
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
588
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
589
|
+
}
|
|
590
|
+
return [...employees, normalized];
|
|
591
|
+
}
|
|
592
|
+
function appendToCoordinatorTeam(employee) {
|
|
593
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
594
|
+
if (!coordinator) return;
|
|
595
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
596
|
+
if (!existsSync4(idPath)) return;
|
|
597
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
598
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
599
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
600
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
601
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
602
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
603
|
+
const entry = `
|
|
604
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
605
|
+
`;
|
|
606
|
+
let updated;
|
|
607
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
608
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
609
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
610
|
+
} else {
|
|
611
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
612
|
+
}
|
|
613
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
614
|
+
}
|
|
615
|
+
function capitalize(s) {
|
|
616
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
617
|
+
}
|
|
618
|
+
async function hireEmployee(employee) {
|
|
619
|
+
const employees = await loadEmployees();
|
|
620
|
+
const updated = addEmployee(employees, employee);
|
|
621
|
+
await saveEmployees(updated);
|
|
622
|
+
try {
|
|
623
|
+
appendToCoordinatorTeam(employee);
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
628
|
+
const config = loadAgentConfig2();
|
|
629
|
+
const name = employee.name.toLowerCase();
|
|
630
|
+
if (!config[name] && config["default"]) {
|
|
631
|
+
config[name] = { ...config["default"] };
|
|
632
|
+
saveAgentConfig2(config);
|
|
633
|
+
}
|
|
634
|
+
} catch {
|
|
635
|
+
}
|
|
636
|
+
return updated;
|
|
637
|
+
}
|
|
638
|
+
async function normalizeRosterCase(rosterPath) {
|
|
639
|
+
const employees = await loadEmployees(rosterPath);
|
|
640
|
+
let changed = false;
|
|
641
|
+
for (const emp of employees) {
|
|
642
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
643
|
+
const oldName = emp.name;
|
|
644
|
+
emp.name = emp.name.toLowerCase();
|
|
645
|
+
changed = true;
|
|
646
|
+
try {
|
|
647
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
648
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
649
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
650
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
651
|
+
renameSync2(oldPath, newPath);
|
|
652
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
653
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
654
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
655
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
656
|
+
unlinkSync(oldPath);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (changed) {
|
|
664
|
+
await saveEmployees(employees, rosterPath);
|
|
665
|
+
}
|
|
666
|
+
return changed;
|
|
667
|
+
}
|
|
396
668
|
function findExeBin() {
|
|
397
669
|
try {
|
|
398
670
|
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
@@ -409,7 +681,7 @@ function registerBinSymlinks(name) {
|
|
|
409
681
|
errors.push("Could not find 'exe-os' in PATH");
|
|
410
682
|
return { created, skipped, errors };
|
|
411
683
|
}
|
|
412
|
-
const binDir =
|
|
684
|
+
const binDir = path3.dirname(exeBinPath);
|
|
413
685
|
let target;
|
|
414
686
|
try {
|
|
415
687
|
target = readlinkSync(exeBinPath);
|
|
@@ -419,8 +691,8 @@ function registerBinSymlinks(name) {
|
|
|
419
691
|
}
|
|
420
692
|
for (const suffix of ["", "-opencode"]) {
|
|
421
693
|
const linkName = `${name}${suffix}`;
|
|
422
|
-
const linkPath =
|
|
423
|
-
if (
|
|
694
|
+
const linkPath = path3.join(binDir, linkName);
|
|
695
|
+
if (existsSync4(linkPath)) {
|
|
424
696
|
skipped.push(linkName);
|
|
425
697
|
continue;
|
|
426
698
|
}
|
|
@@ -433,16 +705,17 @@ function registerBinSymlinks(name) {
|
|
|
433
705
|
}
|
|
434
706
|
return { created, skipped, errors };
|
|
435
707
|
}
|
|
436
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
|
|
708
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
437
709
|
var init_employees = __esm({
|
|
438
710
|
"src/lib/employees.ts"() {
|
|
439
711
|
"use strict";
|
|
440
712
|
init_config();
|
|
441
|
-
EMPLOYEES_PATH =
|
|
713
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
442
714
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
443
715
|
COORDINATOR_ROLE = "COO";
|
|
444
716
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
445
|
-
IDENTITY_DIR =
|
|
717
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
718
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
446
719
|
}
|
|
447
720
|
});
|
|
448
721
|
|
|
@@ -503,7 +776,7 @@ var init_db_retry = __esm({
|
|
|
503
776
|
|
|
504
777
|
// src/lib/database-adapter.ts
|
|
505
778
|
import os3 from "os";
|
|
506
|
-
import
|
|
779
|
+
import path4 from "path";
|
|
507
780
|
import { createRequire } from "module";
|
|
508
781
|
import { pathToFileURL } from "url";
|
|
509
782
|
function quotedIdentifier(identifier) {
|
|
@@ -814,8 +1087,8 @@ async function loadPrismaClient() {
|
|
|
814
1087
|
}
|
|
815
1088
|
return new PrismaClient2();
|
|
816
1089
|
}
|
|
817
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
818
|
-
const requireFromExeDb = createRequire(
|
|
1090
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1091
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
819
1092
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
820
1093
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
821
1094
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -1096,8 +1369,8 @@ var init_memory = __esm({
|
|
|
1096
1369
|
|
|
1097
1370
|
// src/lib/daemon-auth.ts
|
|
1098
1371
|
import crypto from "crypto";
|
|
1099
|
-
import
|
|
1100
|
-
import { existsSync as
|
|
1372
|
+
import path5 from "path";
|
|
1373
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1101
1374
|
function normalizeToken(token) {
|
|
1102
1375
|
if (!token) return null;
|
|
1103
1376
|
const trimmed = token.trim();
|
|
@@ -1105,8 +1378,8 @@ function normalizeToken(token) {
|
|
|
1105
1378
|
}
|
|
1106
1379
|
function readDaemonToken() {
|
|
1107
1380
|
try {
|
|
1108
|
-
if (!
|
|
1109
|
-
return normalizeToken(
|
|
1381
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1382
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
1110
1383
|
} catch {
|
|
1111
1384
|
return null;
|
|
1112
1385
|
}
|
|
@@ -1116,7 +1389,7 @@ function ensureDaemonToken(seed) {
|
|
|
1116
1389
|
if (existing) return existing;
|
|
1117
1390
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1118
1391
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1119
|
-
|
|
1392
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1120
1393
|
`, "utf8");
|
|
1121
1394
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1122
1395
|
return token;
|
|
@@ -1127,7 +1400,7 @@ var init_daemon_auth = __esm({
|
|
|
1127
1400
|
"use strict";
|
|
1128
1401
|
init_config();
|
|
1129
1402
|
init_secure_files();
|
|
1130
|
-
DAEMON_TOKEN_PATH =
|
|
1403
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1131
1404
|
}
|
|
1132
1405
|
});
|
|
1133
1406
|
|
|
@@ -1136,8 +1409,8 @@ import net from "net";
|
|
|
1136
1409
|
import os4 from "os";
|
|
1137
1410
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1138
1411
|
import { randomUUID } from "crypto";
|
|
1139
|
-
import { existsSync as
|
|
1140
|
-
import
|
|
1412
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1413
|
+
import path6 from "path";
|
|
1141
1414
|
import { fileURLToPath } from "url";
|
|
1142
1415
|
function handleData(chunk) {
|
|
1143
1416
|
_buffer += chunk.toString();
|
|
@@ -1173,9 +1446,9 @@ function isZombie(pid) {
|
|
|
1173
1446
|
}
|
|
1174
1447
|
}
|
|
1175
1448
|
function cleanupStaleFiles() {
|
|
1176
|
-
if (
|
|
1449
|
+
if (existsSync6(PID_PATH)) {
|
|
1177
1450
|
try {
|
|
1178
|
-
const pid = parseInt(
|
|
1451
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1179
1452
|
if (pid > 0) {
|
|
1180
1453
|
try {
|
|
1181
1454
|
process.kill(pid, 0);
|
|
@@ -1200,11 +1473,11 @@ function cleanupStaleFiles() {
|
|
|
1200
1473
|
}
|
|
1201
1474
|
}
|
|
1202
1475
|
function findPackageRoot() {
|
|
1203
|
-
let dir =
|
|
1204
|
-
const { root } =
|
|
1476
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1477
|
+
const { root } = path6.parse(dir);
|
|
1205
1478
|
while (dir !== root) {
|
|
1206
|
-
if (
|
|
1207
|
-
dir =
|
|
1479
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1480
|
+
dir = path6.dirname(dir);
|
|
1208
1481
|
}
|
|
1209
1482
|
return null;
|
|
1210
1483
|
}
|
|
@@ -1222,8 +1495,8 @@ function spawnDaemon() {
|
|
|
1222
1495
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1223
1496
|
return;
|
|
1224
1497
|
}
|
|
1225
|
-
const daemonPath =
|
|
1226
|
-
if (!
|
|
1498
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1499
|
+
if (!existsSync6(daemonPath)) {
|
|
1227
1500
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1228
1501
|
`);
|
|
1229
1502
|
return;
|
|
@@ -1232,7 +1505,7 @@ function spawnDaemon() {
|
|
|
1232
1505
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1233
1506
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1234
1507
|
`);
|
|
1235
|
-
const logPath =
|
|
1508
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1236
1509
|
let stderrFd = "ignore";
|
|
1237
1510
|
try {
|
|
1238
1511
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1397,9 +1670,9 @@ function killAndRespawnDaemon() {
|
|
|
1397
1670
|
}
|
|
1398
1671
|
try {
|
|
1399
1672
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1400
|
-
if (
|
|
1673
|
+
if (existsSync6(PID_PATH)) {
|
|
1401
1674
|
try {
|
|
1402
|
-
const pid = parseInt(
|
|
1675
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1403
1676
|
if (pid > 0) {
|
|
1404
1677
|
try {
|
|
1405
1678
|
process.kill(pid, "SIGKILL");
|
|
@@ -1525,9 +1798,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1525
1798
|
"use strict";
|
|
1526
1799
|
init_config();
|
|
1527
1800
|
init_daemon_auth();
|
|
1528
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1529
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1530
|
-
SPAWN_LOCK_PATH =
|
|
1801
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1802
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1803
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1531
1804
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1532
1805
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1533
1806
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1760,7 +2033,7 @@ __export(database_exports, {
|
|
|
1760
2033
|
isInitialized: () => isInitialized,
|
|
1761
2034
|
setExternalClient: () => setExternalClient
|
|
1762
2035
|
});
|
|
1763
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
2036
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1764
2037
|
import { createClient } from "@libsql/client";
|
|
1765
2038
|
import { homedir } from "os";
|
|
1766
2039
|
import { join } from "path";
|
|
@@ -1813,11 +2086,11 @@ function releaseDbLock() {
|
|
|
1813
2086
|
}
|
|
1814
2087
|
async function initDatabase(config) {
|
|
1815
2088
|
acquireDbLock();
|
|
1816
|
-
if (
|
|
2089
|
+
if (existsSync7(config.dbPath)) {
|
|
1817
2090
|
const dbStat = statSync2(config.dbPath);
|
|
1818
2091
|
if (dbStat.size === 0) {
|
|
1819
2092
|
const walPath = config.dbPath + "-wal";
|
|
1820
|
-
if (
|
|
2093
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1821
2094
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1822
2095
|
copyFileSync(config.dbPath, backupPath);
|
|
1823
2096
|
unlinkSync3(config.dbPath);
|
|
@@ -2113,6 +2386,13 @@ async function ensureSchema() {
|
|
|
2113
2386
|
} catch (e) {
|
|
2114
2387
|
logCatchDebug("migration", e);
|
|
2115
2388
|
}
|
|
2389
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2390
|
+
try {
|
|
2391
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2392
|
+
} catch (e) {
|
|
2393
|
+
logCatchDebug("migration", e);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2116
2396
|
try {
|
|
2117
2397
|
await client.execute({
|
|
2118
2398
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3329,6 +3609,22 @@ async function ensureSchema() {
|
|
|
3329
3609
|
} catch (e) {
|
|
3330
3610
|
logCatchDebug("migration", e);
|
|
3331
3611
|
}
|
|
3612
|
+
try {
|
|
3613
|
+
await client.execute({
|
|
3614
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3615
|
+
args: []
|
|
3616
|
+
});
|
|
3617
|
+
} catch (e) {
|
|
3618
|
+
logCatchDebug("migration", e);
|
|
3619
|
+
}
|
|
3620
|
+
try {
|
|
3621
|
+
await client.execute({
|
|
3622
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3623
|
+
args: []
|
|
3624
|
+
});
|
|
3625
|
+
} catch (e) {
|
|
3626
|
+
logCatchDebug("migration", e);
|
|
3627
|
+
}
|
|
3332
3628
|
}
|
|
3333
3629
|
async function disposeDatabase() {
|
|
3334
3630
|
if (_walCheckpointTimer) {
|
|
@@ -3431,11 +3727,17 @@ var init_platform_procedures = __esm({
|
|
|
3431
3727
|
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."
|
|
3432
3728
|
},
|
|
3433
3729
|
{
|
|
3434
|
-
title: "
|
|
3730
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
3435
3731
|
domain: "workflow",
|
|
3436
3732
|
priority: "p1",
|
|
3437
3733
|
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."
|
|
3438
3734
|
},
|
|
3735
|
+
{
|
|
3736
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
3737
|
+
domain: "identity",
|
|
3738
|
+
priority: "p0",
|
|
3739
|
+
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."
|
|
3740
|
+
},
|
|
3439
3741
|
{
|
|
3440
3742
|
title: "Single dispatch path \u2014 create_task only",
|
|
3441
3743
|
domain: "workflow",
|
|
@@ -3469,6 +3771,12 @@ var init_platform_procedures = __esm({
|
|
|
3469
3771
|
priority: "p0",
|
|
3470
3772
|
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."
|
|
3471
3773
|
},
|
|
3774
|
+
{
|
|
3775
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
3776
|
+
domain: "security",
|
|
3777
|
+
priority: "p0",
|
|
3778
|
+
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."
|
|
3779
|
+
},
|
|
3472
3780
|
{
|
|
3473
3781
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
3474
3782
|
domain: "support",
|
|
@@ -3620,7 +3928,7 @@ var init_platform_procedures = __esm({
|
|
|
3620
3928
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
3621
3929
|
domain: "tool-use",
|
|
3622
3930
|
priority: "p0",
|
|
3623
|
-
content: 'exe-os MCP tools
|
|
3931
|
+
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.'
|
|
3624
3932
|
},
|
|
3625
3933
|
{
|
|
3626
3934
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -3753,15 +4061,15 @@ __export(keychain_exports, {
|
|
|
3753
4061
|
setMasterKey: () => setMasterKey
|
|
3754
4062
|
});
|
|
3755
4063
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3756
|
-
import { existsSync as
|
|
4064
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3757
4065
|
import { execSync as execSync3 } from "child_process";
|
|
3758
|
-
import
|
|
4066
|
+
import path7 from "path";
|
|
3759
4067
|
import os5 from "os";
|
|
3760
4068
|
function getKeyDir() {
|
|
3761
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4069
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3762
4070
|
}
|
|
3763
4071
|
function getKeyPath() {
|
|
3764
|
-
return
|
|
4072
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3765
4073
|
}
|
|
3766
4074
|
function nativeKeychainAllowed() {
|
|
3767
4075
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -3792,7 +4100,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
|
3792
4100
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3793
4101
|
if (uid === 0) return true;
|
|
3794
4102
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3795
|
-
return Boolean(exeOsDir &&
|
|
4103
|
+
return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
|
|
3796
4104
|
} catch {
|
|
3797
4105
|
return false;
|
|
3798
4106
|
}
|
|
@@ -3989,7 +4297,7 @@ async function getMasterKey() {
|
|
|
3989
4297
|
}
|
|
3990
4298
|
}
|
|
3991
4299
|
const keyPath = getKeyPath();
|
|
3992
|
-
if (!
|
|
4300
|
+
if (!existsSync8(keyPath)) {
|
|
3993
4301
|
process.stderr.write(
|
|
3994
4302
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3995
4303
|
`
|
|
@@ -4097,7 +4405,7 @@ async function getKeyStorageInfo() {
|
|
|
4097
4405
|
}
|
|
4098
4406
|
}
|
|
4099
4407
|
const keyPath = getKeyPath();
|
|
4100
|
-
if (!
|
|
4408
|
+
if (!existsSync8(keyPath)) {
|
|
4101
4409
|
return {
|
|
4102
4410
|
kind: "missing",
|
|
4103
4411
|
secure: false,
|
|
@@ -4193,7 +4501,7 @@ async function deleteMasterKey() {
|
|
|
4193
4501
|
}
|
|
4194
4502
|
}
|
|
4195
4503
|
const keyPath = getKeyPath();
|
|
4196
|
-
if (
|
|
4504
|
+
if (existsSync8(keyPath)) {
|
|
4197
4505
|
await unlink(keyPath);
|
|
4198
4506
|
}
|
|
4199
4507
|
}
|
|
@@ -4316,14 +4624,14 @@ __export(shard_manager_exports, {
|
|
|
4316
4624
|
listShards: () => listShards,
|
|
4317
4625
|
shardExists: () => shardExists
|
|
4318
4626
|
});
|
|
4319
|
-
import
|
|
4320
|
-
import { existsSync as
|
|
4627
|
+
import path8 from "path";
|
|
4628
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
4321
4629
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4322
4630
|
function initShardManager(encryptionKey) {
|
|
4323
4631
|
_encryptionKey = encryptionKey;
|
|
4324
4632
|
_keyValidated = false;
|
|
4325
4633
|
_keyValidationPromise = null;
|
|
4326
|
-
if (!
|
|
4634
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
4327
4635
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
4328
4636
|
}
|
|
4329
4637
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -4344,7 +4652,7 @@ async function validateEncryptionKey() {
|
|
|
4344
4652
|
return true;
|
|
4345
4653
|
}
|
|
4346
4654
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
4347
|
-
const dbPath =
|
|
4655
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
4348
4656
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
4349
4657
|
try {
|
|
4350
4658
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -4387,7 +4695,7 @@ function getShardClient(projectName) {
|
|
|
4387
4695
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
4388
4696
|
evictLRU();
|
|
4389
4697
|
}
|
|
4390
|
-
const dbPath =
|
|
4698
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4391
4699
|
const client = createClient2({
|
|
4392
4700
|
url: `file:${dbPath}`,
|
|
4393
4701
|
encryptionKey: _encryptionKey
|
|
@@ -4398,13 +4706,13 @@ function getShardClient(projectName) {
|
|
|
4398
4706
|
}
|
|
4399
4707
|
function shardExists(projectName) {
|
|
4400
4708
|
const safeName = safeShardName(projectName);
|
|
4401
|
-
return
|
|
4709
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
4402
4710
|
}
|
|
4403
4711
|
function safeShardName(projectName) {
|
|
4404
4712
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4405
4713
|
}
|
|
4406
4714
|
function listShards() {
|
|
4407
|
-
if (!
|
|
4715
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
4408
4716
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4409
4717
|
}
|
|
4410
4718
|
async function auditShardHealth(options = {}) {
|
|
@@ -4416,7 +4724,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4416
4724
|
const names = listShards();
|
|
4417
4725
|
const shards = [];
|
|
4418
4726
|
for (const name of names) {
|
|
4419
|
-
const dbPath =
|
|
4727
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4420
4728
|
const stat = statSync4(dbPath);
|
|
4421
4729
|
const item = {
|
|
4422
4730
|
name,
|
|
@@ -4451,7 +4759,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4451
4759
|
_shards.delete(name);
|
|
4452
4760
|
_shardLastAccess.delete(name);
|
|
4453
4761
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4454
|
-
const archivedPath =
|
|
4762
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
4455
4763
|
renameSync3(dbPath, archivedPath);
|
|
4456
4764
|
item.archivedPath = archivedPath;
|
|
4457
4765
|
}
|
|
@@ -4679,11 +4987,11 @@ async function getReadyShardClient(projectName) {
|
|
|
4679
4987
|
client.close();
|
|
4680
4988
|
_shards.delete(safeName);
|
|
4681
4989
|
_shardLastAccess.delete(safeName);
|
|
4682
|
-
const dbPath =
|
|
4683
|
-
if (
|
|
4990
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4991
|
+
if (existsSync9(dbPath)) {
|
|
4684
4992
|
const stat = statSync4(dbPath);
|
|
4685
4993
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4686
|
-
const archivedPath =
|
|
4994
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4687
4995
|
renameSync3(dbPath, archivedPath);
|
|
4688
4996
|
process.stderr.write(
|
|
4689
4997
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -4751,7 +5059,7 @@ var init_shard_manager = __esm({
|
|
|
4751
5059
|
"src/lib/shard-manager.ts"() {
|
|
4752
5060
|
"use strict";
|
|
4753
5061
|
init_config();
|
|
4754
|
-
SHARDS_DIR =
|
|
5062
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
4755
5063
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4756
5064
|
MAX_OPEN_SHARDS = 10;
|
|
4757
5065
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -4880,13 +5188,13 @@ __export(session_registry_exports, {
|
|
|
4880
5188
|
refreshSessionProject: () => refreshSessionProject,
|
|
4881
5189
|
registerSession: () => registerSession
|
|
4882
5190
|
});
|
|
4883
|
-
import { readFileSync as
|
|
5191
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync10 } from "fs";
|
|
4884
5192
|
import { execSync as execSync4 } from "child_process";
|
|
4885
|
-
import
|
|
5193
|
+
import path9 from "path";
|
|
4886
5194
|
import os6 from "os";
|
|
4887
5195
|
function registerSession(entry) {
|
|
4888
|
-
const dir =
|
|
4889
|
-
if (!
|
|
5196
|
+
const dir = path9.dirname(REGISTRY_PATH);
|
|
5197
|
+
if (!existsSync10(dir)) {
|
|
4890
5198
|
mkdirSync4(dir, { recursive: true });
|
|
4891
5199
|
}
|
|
4892
5200
|
const sessions = listSessions();
|
|
@@ -4896,7 +5204,7 @@ function registerSession(entry) {
|
|
|
4896
5204
|
} else {
|
|
4897
5205
|
sessions.push(entry);
|
|
4898
5206
|
}
|
|
4899
|
-
|
|
5207
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
4900
5208
|
}
|
|
4901
5209
|
function refreshSessionProject(windowName, projectDir) {
|
|
4902
5210
|
const sessions = listSessions();
|
|
@@ -4904,13 +5212,13 @@ function refreshSessionProject(windowName, projectDir) {
|
|
|
4904
5212
|
if (!entry || entry.projectDir === projectDir) return;
|
|
4905
5213
|
entry.projectDir = projectDir;
|
|
4906
5214
|
try {
|
|
4907
|
-
|
|
5215
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
4908
5216
|
} catch {
|
|
4909
5217
|
}
|
|
4910
5218
|
}
|
|
4911
5219
|
function listSessions() {
|
|
4912
5220
|
try {
|
|
4913
|
-
const raw =
|
|
5221
|
+
const raw = readFileSync6(REGISTRY_PATH, "utf8");
|
|
4914
5222
|
return JSON.parse(raw);
|
|
4915
5223
|
} catch {
|
|
4916
5224
|
return [];
|
|
@@ -4931,7 +5239,7 @@ function pruneStaleSessions() {
|
|
|
4931
5239
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
4932
5240
|
const pruned = sessions.length - alive.length;
|
|
4933
5241
|
if (pruned > 0) {
|
|
4934
|
-
|
|
5242
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
4935
5243
|
}
|
|
4936
5244
|
return pruned;
|
|
4937
5245
|
}
|
|
@@ -4939,7 +5247,7 @@ var REGISTRY_PATH;
|
|
|
4939
5247
|
var init_session_registry = __esm({
|
|
4940
5248
|
"src/lib/session-registry.ts"() {
|
|
4941
5249
|
"use strict";
|
|
4942
|
-
REGISTRY_PATH =
|
|
5250
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
4943
5251
|
}
|
|
4944
5252
|
});
|
|
4945
5253
|
|
|
@@ -5201,68 +5509,6 @@ var init_provider_table = __esm({
|
|
|
5201
5509
|
}
|
|
5202
5510
|
});
|
|
5203
5511
|
|
|
5204
|
-
// src/lib/runtime-table.ts
|
|
5205
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
5206
|
-
var init_runtime_table = __esm({
|
|
5207
|
-
"src/lib/runtime-table.ts"() {
|
|
5208
|
-
"use strict";
|
|
5209
|
-
RUNTIME_TABLE = {
|
|
5210
|
-
codex: {
|
|
5211
|
-
binary: "codex",
|
|
5212
|
-
launchMode: "interactive",
|
|
5213
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
5214
|
-
inlineFlag: "--no-alt-screen",
|
|
5215
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
5216
|
-
defaultModel: "gpt-5.5"
|
|
5217
|
-
},
|
|
5218
|
-
opencode: {
|
|
5219
|
-
binary: "opencode",
|
|
5220
|
-
launchMode: "exec",
|
|
5221
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
5222
|
-
inlineFlag: "",
|
|
5223
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
5224
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
5225
|
-
}
|
|
5226
|
-
};
|
|
5227
|
-
DEFAULT_RUNTIME = "claude";
|
|
5228
|
-
}
|
|
5229
|
-
});
|
|
5230
|
-
|
|
5231
|
-
// src/lib/agent-config.ts
|
|
5232
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync10 } from "fs";
|
|
5233
|
-
import path9 from "path";
|
|
5234
|
-
function loadAgentConfig() {
|
|
5235
|
-
if (!existsSync10(AGENT_CONFIG_PATH)) return {};
|
|
5236
|
-
try {
|
|
5237
|
-
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
5238
|
-
} catch {
|
|
5239
|
-
return {};
|
|
5240
|
-
}
|
|
5241
|
-
}
|
|
5242
|
-
function getAgentRuntime(agentId) {
|
|
5243
|
-
const config = loadAgentConfig();
|
|
5244
|
-
const entry = config[agentId];
|
|
5245
|
-
if (entry) return entry;
|
|
5246
|
-
const orgDefault = config["default"];
|
|
5247
|
-
if (orgDefault) return orgDefault;
|
|
5248
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
5249
|
-
}
|
|
5250
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
5251
|
-
var init_agent_config = __esm({
|
|
5252
|
-
"src/lib/agent-config.ts"() {
|
|
5253
|
-
"use strict";
|
|
5254
|
-
init_config();
|
|
5255
|
-
init_runtime_table();
|
|
5256
|
-
init_secure_files();
|
|
5257
|
-
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
5258
|
-
DEFAULT_MODELS = {
|
|
5259
|
-
claude: "claude-opus-4.6",
|
|
5260
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
5261
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
5262
|
-
};
|
|
5263
|
-
}
|
|
5264
|
-
});
|
|
5265
|
-
|
|
5266
5512
|
// src/lib/intercom-queue.ts
|
|
5267
5513
|
var intercom_queue_exports = {};
|
|
5268
5514
|
__export(intercom_queue_exports, {
|
|
@@ -5757,7 +6003,7 @@ async function assertVpsLicense(opts) {
|
|
|
5757
6003
|
}
|
|
5758
6004
|
if (!transientFailure) {
|
|
5759
6005
|
throw new Error(
|
|
5760
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
6006
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
5761
6007
|
);
|
|
5762
6008
|
}
|
|
5763
6009
|
const fresh = await getCachedLicense();
|
|
@@ -5794,7 +6040,7 @@ async function assertVpsLicense(opts) {
|
|
|
5794
6040
|
} catch {
|
|
5795
6041
|
}
|
|
5796
6042
|
throw new Error(
|
|
5797
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
6043
|
+
`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.`
|
|
5798
6044
|
);
|
|
5799
6045
|
}
|
|
5800
6046
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -5826,7 +6072,7 @@ var init_license = __esm({
|
|
|
5826
6072
|
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
5827
6073
|
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
5828
6074
|
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
5829
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
6075
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
5830
6076
|
RETRY_DELAY_MS = 500;
|
|
5831
6077
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
5832
6078
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -6483,6 +6729,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
6483
6729
|
args: [identifier, ...scope.args]
|
|
6484
6730
|
});
|
|
6485
6731
|
if (result.rows.length === 1) return result.rows[0];
|
|
6732
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
6733
|
+
result = await client.execute({
|
|
6734
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
6735
|
+
args: [`${identifier}%`]
|
|
6736
|
+
});
|
|
6737
|
+
if (result.rows.length === 1) return result.rows[0];
|
|
6738
|
+
if (result.rows.length > 1) {
|
|
6739
|
+
const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
6740
|
+
throw new Error(
|
|
6741
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
6742
|
+
);
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6486
6745
|
result = await client.execute({
|
|
6487
6746
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
6488
6747
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -7337,12 +7596,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
7337
7596
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
7338
7597
|
args: [now, taskId]
|
|
7339
7598
|
});
|
|
7340
|
-
if (
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7599
|
+
if (unblocked.rowsAffected === 0) return;
|
|
7600
|
+
const ubScope = sessionScopeFilter();
|
|
7601
|
+
const unblockedRows = await client.execute({
|
|
7602
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
7603
|
+
args: [now, ...ubScope.args]
|
|
7604
|
+
});
|
|
7605
|
+
if (baseDir) {
|
|
7346
7606
|
for (const ur of unblockedRows.rows) {
|
|
7347
7607
|
try {
|
|
7348
7608
|
const ubFile = path18.join(baseDir, String(ur.task_file));
|
|
@@ -7354,6 +7614,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
7354
7614
|
}
|
|
7355
7615
|
}
|
|
7356
7616
|
}
|
|
7617
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
7618
|
+
try {
|
|
7619
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
7620
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
7621
|
+
for (const ur of unblockedRows.rows) {
|
|
7622
|
+
const assignee = String(ur.assigned_to);
|
|
7623
|
+
if (dispatched.has(assignee)) continue;
|
|
7624
|
+
dispatched.add(assignee);
|
|
7625
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
7626
|
+
}
|
|
7627
|
+
} catch {
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7357
7630
|
}
|
|
7358
7631
|
async function findNextTask(assignedTo) {
|
|
7359
7632
|
const client = getClient();
|
|
@@ -7563,6 +7836,15 @@ var init_embedder = __esm({
|
|
|
7563
7836
|
// src/lib/behaviors.ts
|
|
7564
7837
|
import crypto5 from "crypto";
|
|
7565
7838
|
async function storeBehavior(opts) {
|
|
7839
|
+
try {
|
|
7840
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
7841
|
+
const roster = loadEmployeesSync2();
|
|
7842
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
7843
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
7844
|
+
}
|
|
7845
|
+
} catch (e) {
|
|
7846
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
7847
|
+
}
|
|
7566
7848
|
const client = getClient();
|
|
7567
7849
|
const id = crypto5.randomUUID();
|
|
7568
7850
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7573,10 +7855,18 @@ async function storeBehavior(opts) {
|
|
|
7573
7855
|
vector = new Float32Array(vec);
|
|
7574
7856
|
} catch {
|
|
7575
7857
|
}
|
|
7858
|
+
let createdByDevice = null;
|
|
7859
|
+
try {
|
|
7860
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
7861
|
+
createdByDevice = loadDeviceId2() ?? null;
|
|
7862
|
+
} catch {
|
|
7863
|
+
}
|
|
7864
|
+
const createdByAgent = process.env.AGENT_ID ?? null;
|
|
7865
|
+
const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
|
|
7576
7866
|
await client.execute({
|
|
7577
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
7578
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
7579
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
7867
|
+
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)
|
|
7868
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
|
|
7869
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
|
|
7580
7870
|
});
|
|
7581
7871
|
return id;
|
|
7582
7872
|
}
|
|
@@ -8008,6 +8298,12 @@ async function updateTask(input) {
|
|
|
8008
8298
|
}
|
|
8009
8299
|
}
|
|
8010
8300
|
}
|
|
8301
|
+
if (input.status === "cancelled") {
|
|
8302
|
+
try {
|
|
8303
|
+
await cascadeUnblock(taskId, input.baseDir, now);
|
|
8304
|
+
} catch {
|
|
8305
|
+
}
|
|
8306
|
+
}
|
|
8011
8307
|
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
8012
8308
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
8013
8309
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -8539,11 +8835,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
8539
8835
|
}
|
|
8540
8836
|
}
|
|
8541
8837
|
function resolveExeSession() {
|
|
8838
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
8839
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
8840
|
+
if (fromEnv) return fromEnv;
|
|
8841
|
+
}
|
|
8542
8842
|
const mySession = getMySession();
|
|
8543
8843
|
if (!mySession) {
|
|
8544
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
8545
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
8546
|
-
}
|
|
8547
8844
|
return null;
|
|
8548
8845
|
}
|
|
8549
8846
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -8558,6 +8855,10 @@ function resolveExeSession() {
|
|
|
8558
8855
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
8559
8856
|
`
|
|
8560
8857
|
);
|
|
8858
|
+
try {
|
|
8859
|
+
registerParentExe(key, fromSessionName);
|
|
8860
|
+
} catch {
|
|
8861
|
+
}
|
|
8561
8862
|
candidate = fromSessionName;
|
|
8562
8863
|
} else {
|
|
8563
8864
|
candidate = fromCache;
|
|
@@ -10246,6 +10547,27 @@ async function cloudSync(config) {
|
|
|
10246
10547
|
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
10247
10548
|
pulled = pullResult.records.length;
|
|
10248
10549
|
} else {
|
|
10550
|
+
try {
|
|
10551
|
+
const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
|
|
10552
|
+
if (incomingIds.length > 0) {
|
|
10553
|
+
const ph = incomingIds.map(() => "?").join(",");
|
|
10554
|
+
const existing = await client.execute({
|
|
10555
|
+
sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
|
|
10556
|
+
args: incomingIds
|
|
10557
|
+
});
|
|
10558
|
+
const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
|
|
10559
|
+
for (const rec of pullResult.records) {
|
|
10560
|
+
const local = localMap.get(String(rec.id));
|
|
10561
|
+
if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
|
|
10562
|
+
process.stderr.write(
|
|
10563
|
+
`[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
|
|
10564
|
+
`
|
|
10565
|
+
);
|
|
10566
|
+
}
|
|
10567
|
+
}
|
|
10568
|
+
}
|
|
10569
|
+
} catch {
|
|
10570
|
+
}
|
|
10249
10571
|
const stmts = pullResult.records.map((rec) => ({
|
|
10250
10572
|
sql: `INSERT OR REPLACE INTO memories
|
|
10251
10573
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -11593,9 +11915,23 @@ var PROCEDURES_MARKER = "EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES";
|
|
|
11593
11915
|
function getSessionPrompt(storedPrompt) {
|
|
11594
11916
|
const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
|
|
11595
11917
|
const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
|
|
11918
|
+
let titlePrefix = "";
|
|
11919
|
+
const frontmatterMatch = withoutProcedures.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
11920
|
+
if (frontmatterMatch) {
|
|
11921
|
+
const titleMatch = frontmatterMatch[1].match(/^title:\s*(.+)$/m);
|
|
11922
|
+
const roleMatch = frontmatterMatch[1].match(/^role:\s*(.+)$/m);
|
|
11923
|
+
if (titleMatch) {
|
|
11924
|
+
const title = titleMatch[1].trim();
|
|
11925
|
+
const role = roleMatch ? roleMatch[1].trim() : "";
|
|
11926
|
+
if (title && role && title.toLowerCase() !== role.toLowerCase()) {
|
|
11927
|
+
titlePrefix = `## Your Identity
|
|
11928
|
+
You are **${title}** (specialist). `;
|
|
11929
|
+
}
|
|
11930
|
+
}
|
|
11931
|
+
}
|
|
11596
11932
|
const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
|
|
11597
11933
|
const globalBlock = getGlobalProceduresBlock();
|
|
11598
|
-
return `${globalBlock}${rolePrompt}
|
|
11934
|
+
return `${globalBlock}${titlePrefix}${rolePrompt}
|
|
11599
11935
|
${BASE_OPERATING_PROCEDURES}`;
|
|
11600
11936
|
}
|
|
11601
11937
|
|
|
@@ -11802,7 +12138,20 @@ function buildActionRequired(data) {
|
|
|
11802
12138
|
const blockedTasks = data.globalTasks.filter((t) => t.status === "blocked");
|
|
11803
12139
|
if (blockedTasks.length > 0) {
|
|
11804
12140
|
hasIssues = true;
|
|
11805
|
-
|
|
12141
|
+
const ESCALATION_MS = 48 * 60 * 60 * 1e3;
|
|
12142
|
+
const stale = blockedTasks.filter((t) => {
|
|
12143
|
+
if (!t.updatedAt) return false;
|
|
12144
|
+
return Date.now() - new Date(t.updatedAt).getTime() > ESCALATION_MS;
|
|
12145
|
+
});
|
|
12146
|
+
if (stale.length > 0) {
|
|
12147
|
+
lines.push(` \u{1F534} ESCALATION: ${stale.length} task${stale.length === 1 ? "" : "s"} blocked >48h:`);
|
|
12148
|
+
for (const t of stale) {
|
|
12149
|
+
const ageH = Math.round((Date.now() - new Date(t.updatedAt).getTime()) / 36e5);
|
|
12150
|
+
lines.push(` - "${t.title}" (${t.assignedTo}) \u2014 blocked ${ageH}h`);
|
|
12151
|
+
}
|
|
12152
|
+
} else {
|
|
12153
|
+
lines.push(` \u{1F534} ${blockedTasks.length} blocked task${blockedTasks.length === 1 ? "" : "s"} \u2014 needs your decision`);
|
|
12154
|
+
}
|
|
11806
12155
|
}
|
|
11807
12156
|
if (data.flaggedIssues.length > 0) {
|
|
11808
12157
|
hasIssues = true;
|