@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
|
@@ -398,11 +398,168 @@ var init_session_key = __esm({
|
|
|
398
398
|
}
|
|
399
399
|
});
|
|
400
400
|
|
|
401
|
+
// src/lib/runtime-table.ts
|
|
402
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
403
|
+
var init_runtime_table = __esm({
|
|
404
|
+
"src/lib/runtime-table.ts"() {
|
|
405
|
+
"use strict";
|
|
406
|
+
RUNTIME_TABLE = {
|
|
407
|
+
codex: {
|
|
408
|
+
binary: "codex",
|
|
409
|
+
launchMode: "interactive",
|
|
410
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
411
|
+
inlineFlag: "--no-alt-screen",
|
|
412
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
413
|
+
defaultModel: "gpt-5.5"
|
|
414
|
+
},
|
|
415
|
+
opencode: {
|
|
416
|
+
binary: "opencode",
|
|
417
|
+
launchMode: "exec",
|
|
418
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
419
|
+
inlineFlag: "",
|
|
420
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
421
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
DEFAULT_RUNTIME = "claude";
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// src/lib/agent-config.ts
|
|
429
|
+
var agent_config_exports = {};
|
|
430
|
+
__export(agent_config_exports, {
|
|
431
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
432
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
433
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
434
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
435
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
436
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
437
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
438
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
439
|
+
setAgentMcps: () => setAgentMcps,
|
|
440
|
+
setAgentRuntime: () => setAgentRuntime
|
|
441
|
+
});
|
|
442
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
443
|
+
import path2 from "path";
|
|
444
|
+
function loadAgentConfig() {
|
|
445
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
446
|
+
try {
|
|
447
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
448
|
+
} catch {
|
|
449
|
+
return {};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function saveAgentConfig(config) {
|
|
453
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
454
|
+
ensurePrivateDirSync(dir);
|
|
455
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
456
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
457
|
+
}
|
|
458
|
+
function getAgentRuntime(agentId) {
|
|
459
|
+
const config = loadAgentConfig();
|
|
460
|
+
const entry = config[agentId];
|
|
461
|
+
if (entry) return entry;
|
|
462
|
+
const orgDefault = config["default"];
|
|
463
|
+
if (orgDefault) return orgDefault;
|
|
464
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
465
|
+
}
|
|
466
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
467
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
468
|
+
if (!knownModels) {
|
|
469
|
+
return {
|
|
470
|
+
ok: false,
|
|
471
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
if (!knownModels.includes(model)) {
|
|
475
|
+
return {
|
|
476
|
+
ok: false,
|
|
477
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const config = loadAgentConfig();
|
|
481
|
+
const existing = config[agentId];
|
|
482
|
+
const entry = { runtime, model };
|
|
483
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
484
|
+
if (mcps !== void 0) {
|
|
485
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
486
|
+
} else if (existing?.mcps) {
|
|
487
|
+
entry.mcps = existing.mcps;
|
|
488
|
+
}
|
|
489
|
+
config[agentId] = entry;
|
|
490
|
+
saveAgentConfig(config);
|
|
491
|
+
return { ok: true };
|
|
492
|
+
}
|
|
493
|
+
function setAgentMcps(agentId, mcps) {
|
|
494
|
+
const config = loadAgentConfig();
|
|
495
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
496
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
497
|
+
config[agentId] = existing;
|
|
498
|
+
saveAgentConfig(config);
|
|
499
|
+
return { ok: true };
|
|
500
|
+
}
|
|
501
|
+
function clearAgentRuntime(agentId) {
|
|
502
|
+
const config = loadAgentConfig();
|
|
503
|
+
delete config[agentId];
|
|
504
|
+
saveAgentConfig(config);
|
|
505
|
+
}
|
|
506
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
507
|
+
var init_agent_config = __esm({
|
|
508
|
+
"src/lib/agent-config.ts"() {
|
|
509
|
+
"use strict";
|
|
510
|
+
init_config();
|
|
511
|
+
init_runtime_table();
|
|
512
|
+
init_secure_files();
|
|
513
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
514
|
+
KNOWN_RUNTIMES = {
|
|
515
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
516
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
517
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
518
|
+
};
|
|
519
|
+
RUNTIME_LABELS = {
|
|
520
|
+
claude: "Claude Code (Anthropic)",
|
|
521
|
+
codex: "Codex (OpenAI)",
|
|
522
|
+
opencode: "OpenCode (open source)"
|
|
523
|
+
};
|
|
524
|
+
DEFAULT_MODELS = {
|
|
525
|
+
claude: "claude-opus-4.6",
|
|
526
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
527
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
401
532
|
// src/lib/employees.ts
|
|
533
|
+
var employees_exports = {};
|
|
534
|
+
__export(employees_exports, {
|
|
535
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
536
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
537
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
538
|
+
addEmployee: () => addEmployee,
|
|
539
|
+
baseAgentName: () => baseAgentName,
|
|
540
|
+
canCoordinate: () => canCoordinate,
|
|
541
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
542
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
543
|
+
getEmployee: () => getEmployee,
|
|
544
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
545
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
546
|
+
hasRole: () => hasRole,
|
|
547
|
+
hireEmployee: () => hireEmployee,
|
|
548
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
549
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
550
|
+
isMultiInstance: () => isMultiInstance,
|
|
551
|
+
loadEmployees: () => loadEmployees,
|
|
552
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
553
|
+
normalizeRole: () => normalizeRole,
|
|
554
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
555
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
556
|
+
saveEmployees: () => saveEmployees,
|
|
557
|
+
validateEmployeeName: () => validateEmployeeName
|
|
558
|
+
});
|
|
402
559
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
403
|
-
import { existsSync as
|
|
560
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
404
561
|
import { execSync as execSync2 } from "child_process";
|
|
405
|
-
import
|
|
562
|
+
import path3 from "path";
|
|
406
563
|
import os2 from "os";
|
|
407
564
|
function normalizeRole(role) {
|
|
408
565
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -423,8 +580,23 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
423
580
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
424
581
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
425
582
|
}
|
|
583
|
+
function validateEmployeeName(name) {
|
|
584
|
+
if (!name) {
|
|
585
|
+
return { valid: false, error: "Name is required" };
|
|
586
|
+
}
|
|
587
|
+
if (name.length > 32) {
|
|
588
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
589
|
+
}
|
|
590
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
591
|
+
return {
|
|
592
|
+
valid: false,
|
|
593
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
return { valid: true };
|
|
597
|
+
}
|
|
426
598
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
427
|
-
if (!
|
|
599
|
+
if (!existsSync4(employeesPath)) {
|
|
428
600
|
return [];
|
|
429
601
|
}
|
|
430
602
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -434,10 +606,14 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
434
606
|
return [];
|
|
435
607
|
}
|
|
436
608
|
}
|
|
609
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
610
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
611
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
612
|
+
}
|
|
437
613
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
438
|
-
if (!
|
|
614
|
+
if (!existsSync4(employeesPath)) return [];
|
|
439
615
|
try {
|
|
440
|
-
return JSON.parse(
|
|
616
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
441
617
|
} catch {
|
|
442
618
|
return [];
|
|
443
619
|
}
|
|
@@ -445,6 +621,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
445
621
|
function getEmployee(employees, name) {
|
|
446
622
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
447
623
|
}
|
|
624
|
+
function getEmployeeByRole(employees, role) {
|
|
625
|
+
const lower = role.toLowerCase();
|
|
626
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
627
|
+
}
|
|
628
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
629
|
+
const lower = role.toLowerCase();
|
|
630
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
631
|
+
}
|
|
632
|
+
function hasRole(agentName, role) {
|
|
633
|
+
const employees = loadEmployeesSync();
|
|
634
|
+
const emp = getEmployee(employees, agentName);
|
|
635
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
636
|
+
}
|
|
448
637
|
function baseAgentName(name, employees) {
|
|
449
638
|
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
450
639
|
if (!match) return name;
|
|
@@ -459,26 +648,151 @@ function isMultiInstance(agentName, employees) {
|
|
|
459
648
|
if (!emp) return false;
|
|
460
649
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
461
650
|
}
|
|
462
|
-
|
|
651
|
+
function addEmployee(employees, employee) {
|
|
652
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
653
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
654
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
655
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
656
|
+
}
|
|
657
|
+
return [...employees, normalized];
|
|
658
|
+
}
|
|
659
|
+
function appendToCoordinatorTeam(employee) {
|
|
660
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
661
|
+
if (!coordinator) return;
|
|
662
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
663
|
+
if (!existsSync4(idPath)) return;
|
|
664
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
665
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
666
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
667
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
668
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
669
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
670
|
+
const entry = `
|
|
671
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
672
|
+
`;
|
|
673
|
+
let updated;
|
|
674
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
675
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
676
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
677
|
+
} else {
|
|
678
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
679
|
+
}
|
|
680
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
681
|
+
}
|
|
682
|
+
function capitalize(s) {
|
|
683
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
684
|
+
}
|
|
685
|
+
async function hireEmployee(employee) {
|
|
686
|
+
const employees = await loadEmployees();
|
|
687
|
+
const updated = addEmployee(employees, employee);
|
|
688
|
+
await saveEmployees(updated);
|
|
689
|
+
try {
|
|
690
|
+
appendToCoordinatorTeam(employee);
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
695
|
+
const config = loadAgentConfig2();
|
|
696
|
+
const name = employee.name.toLowerCase();
|
|
697
|
+
if (!config[name] && config["default"]) {
|
|
698
|
+
config[name] = { ...config["default"] };
|
|
699
|
+
saveAgentConfig2(config);
|
|
700
|
+
}
|
|
701
|
+
} catch {
|
|
702
|
+
}
|
|
703
|
+
return updated;
|
|
704
|
+
}
|
|
705
|
+
async function normalizeRosterCase(rosterPath) {
|
|
706
|
+
const employees = await loadEmployees(rosterPath);
|
|
707
|
+
let changed = false;
|
|
708
|
+
for (const emp of employees) {
|
|
709
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
710
|
+
const oldName = emp.name;
|
|
711
|
+
emp.name = emp.name.toLowerCase();
|
|
712
|
+
changed = true;
|
|
713
|
+
try {
|
|
714
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
715
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
716
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
717
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
718
|
+
renameSync2(oldPath, newPath);
|
|
719
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
720
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
721
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
722
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
723
|
+
unlinkSync(oldPath);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
} catch {
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (changed) {
|
|
731
|
+
await saveEmployees(employees, rosterPath);
|
|
732
|
+
}
|
|
733
|
+
return changed;
|
|
734
|
+
}
|
|
735
|
+
function findExeBin() {
|
|
736
|
+
try {
|
|
737
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
738
|
+
} catch {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function registerBinSymlinks(name) {
|
|
743
|
+
const created = [];
|
|
744
|
+
const skipped = [];
|
|
745
|
+
const errors = [];
|
|
746
|
+
const exeBinPath = findExeBin();
|
|
747
|
+
if (!exeBinPath) {
|
|
748
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
749
|
+
return { created, skipped, errors };
|
|
750
|
+
}
|
|
751
|
+
const binDir = path3.dirname(exeBinPath);
|
|
752
|
+
let target;
|
|
753
|
+
try {
|
|
754
|
+
target = readlinkSync(exeBinPath);
|
|
755
|
+
} catch {
|
|
756
|
+
errors.push("Could not read 'exe' symlink");
|
|
757
|
+
return { created, skipped, errors };
|
|
758
|
+
}
|
|
759
|
+
for (const suffix of ["", "-opencode"]) {
|
|
760
|
+
const linkName = `${name}${suffix}`;
|
|
761
|
+
const linkPath = path3.join(binDir, linkName);
|
|
762
|
+
if (existsSync4(linkPath)) {
|
|
763
|
+
skipped.push(linkName);
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
symlinkSync(target, linkPath);
|
|
768
|
+
created.push(linkName);
|
|
769
|
+
} catch (err) {
|
|
770
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return { created, skipped, errors };
|
|
774
|
+
}
|
|
775
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
463
776
|
var init_employees = __esm({
|
|
464
777
|
"src/lib/employees.ts"() {
|
|
465
778
|
"use strict";
|
|
466
779
|
init_config();
|
|
467
|
-
EMPLOYEES_PATH =
|
|
780
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
468
781
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
469
782
|
COORDINATOR_ROLE = "COO";
|
|
470
783
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
471
|
-
IDENTITY_DIR =
|
|
784
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
785
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
472
786
|
}
|
|
473
787
|
});
|
|
474
788
|
|
|
475
789
|
// src/lib/session-registry.ts
|
|
476
|
-
import { readFileSync as
|
|
477
|
-
import
|
|
790
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
791
|
+
import path6 from "path";
|
|
478
792
|
import os4 from "os";
|
|
479
793
|
function registerSession(entry) {
|
|
480
|
-
const dir =
|
|
481
|
-
if (!
|
|
794
|
+
const dir = path6.dirname(REGISTRY_PATH);
|
|
795
|
+
if (!existsSync6(dir)) {
|
|
482
796
|
mkdirSync4(dir, { recursive: true });
|
|
483
797
|
}
|
|
484
798
|
const sessions = listSessions();
|
|
@@ -488,11 +802,11 @@ function registerSession(entry) {
|
|
|
488
802
|
} else {
|
|
489
803
|
sessions.push(entry);
|
|
490
804
|
}
|
|
491
|
-
|
|
805
|
+
writeFileSync5(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
492
806
|
}
|
|
493
807
|
function listSessions() {
|
|
494
808
|
try {
|
|
495
|
-
const raw =
|
|
809
|
+
const raw = readFileSync6(REGISTRY_PATH, "utf8");
|
|
496
810
|
return JSON.parse(raw);
|
|
497
811
|
} catch {
|
|
498
812
|
return [];
|
|
@@ -502,7 +816,7 @@ var REGISTRY_PATH;
|
|
|
502
816
|
var init_session_registry = __esm({
|
|
503
817
|
"src/lib/session-registry.ts"() {
|
|
504
818
|
"use strict";
|
|
505
|
-
REGISTRY_PATH =
|
|
819
|
+
REGISTRY_PATH = path6.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
506
820
|
}
|
|
507
821
|
});
|
|
508
822
|
|
|
@@ -696,68 +1010,6 @@ var init_provider_table = __esm({
|
|
|
696
1010
|
}
|
|
697
1011
|
});
|
|
698
1012
|
|
|
699
|
-
// src/lib/runtime-table.ts
|
|
700
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
701
|
-
var init_runtime_table = __esm({
|
|
702
|
-
"src/lib/runtime-table.ts"() {
|
|
703
|
-
"use strict";
|
|
704
|
-
RUNTIME_TABLE = {
|
|
705
|
-
codex: {
|
|
706
|
-
binary: "codex",
|
|
707
|
-
launchMode: "interactive",
|
|
708
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
709
|
-
inlineFlag: "--no-alt-screen",
|
|
710
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
711
|
-
defaultModel: "gpt-5.5"
|
|
712
|
-
},
|
|
713
|
-
opencode: {
|
|
714
|
-
binary: "opencode",
|
|
715
|
-
launchMode: "exec",
|
|
716
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
717
|
-
inlineFlag: "",
|
|
718
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
719
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
DEFAULT_RUNTIME = "claude";
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
// src/lib/agent-config.ts
|
|
727
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs";
|
|
728
|
-
import path6 from "path";
|
|
729
|
-
function loadAgentConfig() {
|
|
730
|
-
if (!existsSync6(AGENT_CONFIG_PATH)) return {};
|
|
731
|
-
try {
|
|
732
|
-
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
733
|
-
} catch {
|
|
734
|
-
return {};
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
function getAgentRuntime(agentId) {
|
|
738
|
-
const config = loadAgentConfig();
|
|
739
|
-
const entry = config[agentId];
|
|
740
|
-
if (entry) return entry;
|
|
741
|
-
const orgDefault = config["default"];
|
|
742
|
-
if (orgDefault) return orgDefault;
|
|
743
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
744
|
-
}
|
|
745
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
746
|
-
var init_agent_config = __esm({
|
|
747
|
-
"src/lib/agent-config.ts"() {
|
|
748
|
-
"use strict";
|
|
749
|
-
init_config();
|
|
750
|
-
init_runtime_table();
|
|
751
|
-
init_secure_files();
|
|
752
|
-
AGENT_CONFIG_PATH = path6.join(EXE_AI_DIR, "agent-config.json");
|
|
753
|
-
DEFAULT_MODELS = {
|
|
754
|
-
claude: "claude-opus-4.6",
|
|
755
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
756
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
});
|
|
760
|
-
|
|
761
1013
|
// src/lib/intercom-queue.ts
|
|
762
1014
|
var intercom_queue_exports = {};
|
|
763
1015
|
__export(intercom_queue_exports, {
|
|
@@ -3840,6 +4092,22 @@ async function ensureSchema() {
|
|
|
3840
4092
|
} catch (e) {
|
|
3841
4093
|
logCatchDebug("migration", e);
|
|
3842
4094
|
}
|
|
4095
|
+
try {
|
|
4096
|
+
await client.execute({
|
|
4097
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
4098
|
+
args: []
|
|
4099
|
+
});
|
|
4100
|
+
} catch (e) {
|
|
4101
|
+
logCatchDebug("migration", e);
|
|
4102
|
+
}
|
|
4103
|
+
try {
|
|
4104
|
+
await client.execute({
|
|
4105
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
4106
|
+
args: []
|
|
4107
|
+
});
|
|
4108
|
+
} catch (e) {
|
|
4109
|
+
logCatchDebug("migration", e);
|
|
4110
|
+
}
|
|
3843
4111
|
}
|
|
3844
4112
|
async function disposeDatabase() {
|
|
3845
4113
|
if (_walCheckpointTimer) {
|
|
@@ -4258,7 +4526,7 @@ async function assertVpsLicense(opts) {
|
|
|
4258
4526
|
}
|
|
4259
4527
|
if (!transientFailure) {
|
|
4260
4528
|
throw new Error(
|
|
4261
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
4529
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
4262
4530
|
);
|
|
4263
4531
|
}
|
|
4264
4532
|
const fresh = await getCachedLicense();
|
|
@@ -4295,7 +4563,7 @@ async function assertVpsLicense(opts) {
|
|
|
4295
4563
|
} catch {
|
|
4296
4564
|
}
|
|
4297
4565
|
throw new Error(
|
|
4298
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
4566
|
+
`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.`
|
|
4299
4567
|
);
|
|
4300
4568
|
}
|
|
4301
4569
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -4327,7 +4595,7 @@ var init_license = __esm({
|
|
|
4327
4595
|
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
4328
4596
|
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4329
4597
|
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4330
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
4598
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
4331
4599
|
RETRY_DELAY_MS = 500;
|
|
4332
4600
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
4333
4601
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -5038,6 +5306,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
5038
5306
|
args: [identifier, ...scope.args]
|
|
5039
5307
|
});
|
|
5040
5308
|
if (result.rows.length === 1) return result.rows[0];
|
|
5309
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
5310
|
+
result = await client.execute({
|
|
5311
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
5312
|
+
args: [`${identifier}%`]
|
|
5313
|
+
});
|
|
5314
|
+
if (result.rows.length === 1) return result.rows[0];
|
|
5315
|
+
if (result.rows.length > 1) {
|
|
5316
|
+
const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
5317
|
+
throw new Error(
|
|
5318
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
5319
|
+
);
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5041
5322
|
result = await client.execute({
|
|
5042
5323
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
5043
5324
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -5892,12 +6173,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5892
6173
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
5893
6174
|
args: [now, taskId]
|
|
5894
6175
|
});
|
|
5895
|
-
if (
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
6176
|
+
if (unblocked.rowsAffected === 0) return;
|
|
6177
|
+
const ubScope = sessionScopeFilter();
|
|
6178
|
+
const unblockedRows = await client.execute({
|
|
6179
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
6180
|
+
args: [now, ...ubScope.args]
|
|
6181
|
+
});
|
|
6182
|
+
if (baseDir) {
|
|
5901
6183
|
for (const ur of unblockedRows.rows) {
|
|
5902
6184
|
try {
|
|
5903
6185
|
const ubFile = path18.join(baseDir, String(ur.task_file));
|
|
@@ -5909,6 +6191,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5909
6191
|
}
|
|
5910
6192
|
}
|
|
5911
6193
|
}
|
|
6194
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
6195
|
+
try {
|
|
6196
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
6197
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
6198
|
+
for (const ur of unblockedRows.rows) {
|
|
6199
|
+
const assignee = String(ur.assigned_to);
|
|
6200
|
+
if (dispatched.has(assignee)) continue;
|
|
6201
|
+
dispatched.add(assignee);
|
|
6202
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
6203
|
+
}
|
|
6204
|
+
} catch {
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
5912
6207
|
}
|
|
5913
6208
|
async function findNextTask(assignedTo) {
|
|
5914
6209
|
const client = getClient();
|
|
@@ -6118,6 +6413,15 @@ var init_embedder = __esm({
|
|
|
6118
6413
|
// src/lib/behaviors.ts
|
|
6119
6414
|
import crypto5 from "crypto";
|
|
6120
6415
|
async function storeBehavior(opts) {
|
|
6416
|
+
try {
|
|
6417
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
6418
|
+
const roster = loadEmployeesSync2();
|
|
6419
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
6420
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
6421
|
+
}
|
|
6422
|
+
} catch (e) {
|
|
6423
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
6424
|
+
}
|
|
6121
6425
|
const client = getClient();
|
|
6122
6426
|
const id = crypto5.randomUUID();
|
|
6123
6427
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6571,6 +6875,12 @@ async function updateTask(input2) {
|
|
|
6571
6875
|
}
|
|
6572
6876
|
}
|
|
6573
6877
|
}
|
|
6878
|
+
if (input2.status === "cancelled") {
|
|
6879
|
+
try {
|
|
6880
|
+
await cascadeUnblock(taskId, input2.baseDir, now);
|
|
6881
|
+
} catch {
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6574
6884
|
if ((input2.status === "done" || input2.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6575
6885
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6576
6886
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -7102,11 +7412,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
7102
7412
|
}
|
|
7103
7413
|
}
|
|
7104
7414
|
function resolveExeSession() {
|
|
7415
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
7416
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
7417
|
+
if (fromEnv) return fromEnv;
|
|
7418
|
+
}
|
|
7105
7419
|
const mySession = getMySession();
|
|
7106
7420
|
if (!mySession) {
|
|
7107
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
7108
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
7109
|
-
}
|
|
7110
7421
|
return null;
|
|
7111
7422
|
}
|
|
7112
7423
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -7121,6 +7432,10 @@ function resolveExeSession() {
|
|
|
7121
7432
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
7122
7433
|
`
|
|
7123
7434
|
);
|
|
7435
|
+
try {
|
|
7436
|
+
registerParentExe(key, fromSessionName);
|
|
7437
|
+
} catch {
|
|
7438
|
+
}
|
|
7124
7439
|
candidate = fromSessionName;
|
|
7125
7440
|
} else {
|
|
7126
7441
|
candidate = fromCache;
|
|
@@ -8848,11 +9163,17 @@ var init_platform_procedures = __esm({
|
|
|
8848
9163
|
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."
|
|
8849
9164
|
},
|
|
8850
9165
|
{
|
|
8851
|
-
title: "
|
|
9166
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
8852
9167
|
domain: "workflow",
|
|
8853
9168
|
priority: "p1",
|
|
8854
9169
|
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."
|
|
8855
9170
|
},
|
|
9171
|
+
{
|
|
9172
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
9173
|
+
domain: "identity",
|
|
9174
|
+
priority: "p0",
|
|
9175
|
+
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."
|
|
9176
|
+
},
|
|
8856
9177
|
{
|
|
8857
9178
|
title: "Single dispatch path \u2014 create_task only",
|
|
8858
9179
|
domain: "workflow",
|
|
@@ -8886,6 +9207,12 @@ var init_platform_procedures = __esm({
|
|
|
8886
9207
|
priority: "p0",
|
|
8887
9208
|
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."
|
|
8888
9209
|
},
|
|
9210
|
+
{
|
|
9211
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
9212
|
+
domain: "security",
|
|
9213
|
+
priority: "p0",
|
|
9214
|
+
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."
|
|
9215
|
+
},
|
|
8889
9216
|
{
|
|
8890
9217
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
8891
9218
|
domain: "support",
|
|
@@ -9171,10 +9498,24 @@ function stableId(memoryId, type, content) {
|
|
|
9171
9498
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
9172
9499
|
}
|
|
9173
9500
|
function cleanText(text) {
|
|
9174
|
-
|
|
9501
|
+
let cleaned = text.replace(
|
|
9502
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
9503
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
9504
|
+
);
|
|
9505
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9506
|
+
return cleaned;
|
|
9175
9507
|
}
|
|
9176
|
-
function
|
|
9177
|
-
|
|
9508
|
+
function splitSegments(text) {
|
|
9509
|
+
const cleaned = cleanText(text);
|
|
9510
|
+
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);
|
|
9511
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
9512
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
9513
|
+
if (lines.length > 0) return lines;
|
|
9514
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
9515
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9518
|
+
return segments;
|
|
9178
9519
|
}
|
|
9179
9520
|
function inferCardType(sentence, toolName) {
|
|
9180
9521
|
const lower = sentence.toLowerCase();
|
|
@@ -9206,12 +9547,12 @@ function predicateFor(type) {
|
|
|
9206
9547
|
}
|
|
9207
9548
|
}
|
|
9208
9549
|
function extractMemoryCards(row) {
|
|
9209
|
-
const
|
|
9550
|
+
const segments = splitSegments(row.raw_text);
|
|
9210
9551
|
const cards = [];
|
|
9211
|
-
for (const sentence of
|
|
9552
|
+
for (const sentence of segments) {
|
|
9212
9553
|
const type = inferCardType(sentence, row.tool_name);
|
|
9213
9554
|
const subject = extractSubject(sentence, row.agent_id);
|
|
9214
|
-
const content = sentence.length >
|
|
9555
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
9215
9556
|
cards.push({
|
|
9216
9557
|
id: stableId(row.id, type, content),
|
|
9217
9558
|
memory_id: row.id,
|
|
@@ -9307,13 +9648,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
9307
9648
|
last_accessed: String(row.timestamp)
|
|
9308
9649
|
}));
|
|
9309
9650
|
}
|
|
9310
|
-
var MAX_CARDS_PER_MEMORY,
|
|
9651
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
9311
9652
|
var init_memory_cards = __esm({
|
|
9312
9653
|
"src/lib/memory-cards.ts"() {
|
|
9313
9654
|
"use strict";
|
|
9314
9655
|
init_database();
|
|
9315
|
-
MAX_CARDS_PER_MEMORY =
|
|
9316
|
-
|
|
9656
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
9657
|
+
MAX_SEGMENT_CHARS = 500;
|
|
9658
|
+
MIN_SEGMENT_CHARS = 20;
|
|
9317
9659
|
}
|
|
9318
9660
|
});
|
|
9319
9661
|
|
|
@@ -10650,9 +10992,9 @@ var init_git_task_sweep = __esm({
|
|
|
10650
10992
|
// src/lib/active-agent.ts
|
|
10651
10993
|
init_config();
|
|
10652
10994
|
init_session_key();
|
|
10653
|
-
import { readFileSync as
|
|
10995
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
10654
10996
|
import { execSync as execSync3 } from "child_process";
|
|
10655
|
-
import
|
|
10997
|
+
import path4 from "path";
|
|
10656
10998
|
|
|
10657
10999
|
// src/mcp/agent-context.ts
|
|
10658
11000
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -10663,7 +11005,7 @@ function getAgentContext() {
|
|
|
10663
11005
|
|
|
10664
11006
|
// src/lib/active-agent.ts
|
|
10665
11007
|
init_employees();
|
|
10666
|
-
var CACHE_DIR =
|
|
11008
|
+
var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
|
|
10667
11009
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
10668
11010
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
10669
11011
|
if (candidate === baseName) return true;
|
|
@@ -10708,7 +11050,7 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
10708
11050
|
return null;
|
|
10709
11051
|
}
|
|
10710
11052
|
function getMarkerPath() {
|
|
10711
|
-
return
|
|
11053
|
+
return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
10712
11054
|
}
|
|
10713
11055
|
function clearActiveAgent() {
|
|
10714
11056
|
try {
|
|
@@ -10721,7 +11063,7 @@ function getActiveAgent() {
|
|
|
10721
11063
|
if (httpCtx) return httpCtx;
|
|
10722
11064
|
try {
|
|
10723
11065
|
const markerPath = getMarkerPath();
|
|
10724
|
-
const raw =
|
|
11066
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
10725
11067
|
const data = JSON.parse(raw);
|
|
10726
11068
|
if (data.agentId) {
|
|
10727
11069
|
if (data.startedAt) {
|
|
@@ -10766,15 +11108,15 @@ init_session_key();
|
|
|
10766
11108
|
|
|
10767
11109
|
// src/lib/cache-warmth.ts
|
|
10768
11110
|
import os3 from "os";
|
|
10769
|
-
import
|
|
10770
|
-
import { existsSync as
|
|
11111
|
+
import path5 from "path";
|
|
11112
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
10771
11113
|
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
10772
|
-
var CACHE_DIR2 =
|
|
10773
|
-
process.env.EXE_OS_DIR ??
|
|
11114
|
+
var CACHE_DIR2 = path5.join(
|
|
11115
|
+
process.env.EXE_OS_DIR ?? path5.join(os3.homedir(), ".exe-os"),
|
|
10774
11116
|
"session-cache"
|
|
10775
11117
|
);
|
|
10776
11118
|
function getStatePath(sessionKey) {
|
|
10777
|
-
return
|
|
11119
|
+
return path5.join(CACHE_DIR2, `cache-warmth-${sessionKey}.json`);
|
|
10778
11120
|
}
|
|
10779
11121
|
function clearCacheState(sessionKey) {
|
|
10780
11122
|
try {
|