@askexenow/exe-os 0.9.112 → 0.9.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/bin/agentic-ontology-backfill.js +54 -11
- package/dist/bin/agentic-reflection-backfill.js +29 -1
- package/dist/bin/agentic-semantic-label.js +29 -1
- package/dist/bin/backfill-conversations.js +53 -10
- package/dist/bin/backfill-responses.js +54 -11
- package/dist/bin/backfill-vectors.js +29 -1
- package/dist/bin/bulk-sync-postgres.js +55 -12
- package/dist/bin/cleanup-stale-review-tasks.js +75 -15
- package/dist/bin/cli.js +293 -76
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +28 -2
- package/dist/bin/exe-assign.js +54 -11
- package/dist/bin/exe-boot.js +481 -147
- package/dist/bin/exe-call.js +45 -4
- package/dist/bin/exe-cloud.js +93 -15
- package/dist/bin/exe-dispatch.js +369 -24
- package/dist/bin/exe-doctor.js +53 -10
- package/dist/bin/exe-export-behaviors.js +54 -11
- package/dist/bin/exe-forget.js +54 -11
- package/dist/bin/exe-gateway.js +128 -23
- package/dist/bin/exe-heartbeat.js +75 -15
- package/dist/bin/exe-kill.js +54 -11
- package/dist/bin/exe-launch-agent.js +70 -12
- package/dist/bin/exe-new-employee.js +175 -7
- package/dist/bin/exe-pending-messages.js +75 -15
- package/dist/bin/exe-pending-notifications.js +75 -15
- package/dist/bin/exe-pending-reviews.js +75 -15
- package/dist/bin/exe-rename.js +54 -11
- package/dist/bin/exe-review.js +54 -11
- package/dist/bin/exe-search.js +54 -11
- package/dist/bin/exe-session-cleanup.js +491 -146
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +524 -245
- package/dist/bin/exe-start-opencode.js +534 -165
- package/dist/bin/exe-status.js +75 -15
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +54 -11
- package/dist/bin/git-sweep.js +369 -24
- package/dist/bin/graph-backfill.js +54 -11
- package/dist/bin/graph-export.js +54 -11
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +491 -146
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +369 -24
- package/dist/bin/setup.js +91 -13
- package/dist/bin/shard-migrate.js +54 -11
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +128 -23
- package/dist/hooks/bug-report-worker.js +128 -23
- package/dist/hooks/codex-stop-task-finalizer.js +512 -140
- package/dist/hooks/commit-complete.js +369 -24
- package/dist/hooks/error-recall.js +54 -11
- package/dist/hooks/ingest.js +4575 -252
- package/dist/hooks/instructions-loaded.js +54 -11
- package/dist/hooks/notification.js +54 -11
- package/dist/hooks/post-compact.js +75 -15
- package/dist/hooks/post-tool-combined.js +75 -15
- package/dist/hooks/pre-compact.js +449 -104
- package/dist/hooks/pre-tool-use.js +90 -15
- package/dist/hooks/prompt-submit.js +129 -24
- package/dist/hooks/session-end.js +451 -109
- package/dist/hooks/session-start.js +104 -16
- package/dist/hooks/stop.js +74 -14
- package/dist/hooks/subagent-stop.js +75 -15
- package/dist/hooks/summary-worker.js +73 -7
- package/dist/index.js +128 -23
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +38 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +16 -0
- package/dist/lib/db.js +16 -0
- package/dist/lib/device-registry.js +16 -0
- package/dist/lib/employee-templates.js +29 -3
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +268 -42
- package/dist/lib/hybrid-search.js +54 -11
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +29 -1
- package/dist/lib/skill-learning.js +458 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +54 -11
- package/dist/lib/tasks.js +393 -91
- package/dist/lib/tmux-routing.js +316 -14
- package/dist/mcp/server.js +169 -30
- package/dist/mcp/tools/create-task.js +75 -13
- package/dist/mcp/tools/deactivate-behavior.js +33 -24
- package/dist/mcp/tools/list-tasks.js +21 -4
- package/dist/mcp/tools/send-message.js +21 -4
- package/dist/mcp/tools/update-task.js +390 -91
- package/dist/runtime/index.js +446 -101
- package/dist/tui/App.js +208 -54
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -330,11 +330,168 @@ var init_config = __esm({
|
|
|
330
330
|
}
|
|
331
331
|
});
|
|
332
332
|
|
|
333
|
+
// src/lib/runtime-table.ts
|
|
334
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
335
|
+
var init_runtime_table = __esm({
|
|
336
|
+
"src/lib/runtime-table.ts"() {
|
|
337
|
+
"use strict";
|
|
338
|
+
RUNTIME_TABLE = {
|
|
339
|
+
codex: {
|
|
340
|
+
binary: "codex",
|
|
341
|
+
launchMode: "interactive",
|
|
342
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
343
|
+
inlineFlag: "--no-alt-screen",
|
|
344
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
345
|
+
defaultModel: "gpt-5.5"
|
|
346
|
+
},
|
|
347
|
+
opencode: {
|
|
348
|
+
binary: "opencode",
|
|
349
|
+
launchMode: "exec",
|
|
350
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
351
|
+
inlineFlag: "",
|
|
352
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
353
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
DEFAULT_RUNTIME = "claude";
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// src/lib/agent-config.ts
|
|
361
|
+
var agent_config_exports = {};
|
|
362
|
+
__export(agent_config_exports, {
|
|
363
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
364
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
365
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
366
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
367
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
368
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
369
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
370
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
371
|
+
setAgentMcps: () => setAgentMcps,
|
|
372
|
+
setAgentRuntime: () => setAgentRuntime
|
|
373
|
+
});
|
|
374
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
375
|
+
import path3 from "path";
|
|
376
|
+
function loadAgentConfig() {
|
|
377
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
378
|
+
try {
|
|
379
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
380
|
+
} catch {
|
|
381
|
+
return {};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function saveAgentConfig(config) {
|
|
385
|
+
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
386
|
+
ensurePrivateDirSync(dir);
|
|
387
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
388
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
389
|
+
}
|
|
390
|
+
function getAgentRuntime(agentId) {
|
|
391
|
+
const config = loadAgentConfig();
|
|
392
|
+
const entry = config[agentId];
|
|
393
|
+
if (entry) return entry;
|
|
394
|
+
const orgDefault = config["default"];
|
|
395
|
+
if (orgDefault) return orgDefault;
|
|
396
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
397
|
+
}
|
|
398
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
399
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
400
|
+
if (!knownModels) {
|
|
401
|
+
return {
|
|
402
|
+
ok: false,
|
|
403
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
if (!knownModels.includes(model)) {
|
|
407
|
+
return {
|
|
408
|
+
ok: false,
|
|
409
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const config = loadAgentConfig();
|
|
413
|
+
const existing = config[agentId];
|
|
414
|
+
const entry = { runtime, model };
|
|
415
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
416
|
+
if (mcps !== void 0) {
|
|
417
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
418
|
+
} else if (existing?.mcps) {
|
|
419
|
+
entry.mcps = existing.mcps;
|
|
420
|
+
}
|
|
421
|
+
config[agentId] = entry;
|
|
422
|
+
saveAgentConfig(config);
|
|
423
|
+
return { ok: true };
|
|
424
|
+
}
|
|
425
|
+
function setAgentMcps(agentId, mcps) {
|
|
426
|
+
const config = loadAgentConfig();
|
|
427
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
428
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
429
|
+
config[agentId] = existing;
|
|
430
|
+
saveAgentConfig(config);
|
|
431
|
+
return { ok: true };
|
|
432
|
+
}
|
|
433
|
+
function clearAgentRuntime(agentId) {
|
|
434
|
+
const config = loadAgentConfig();
|
|
435
|
+
delete config[agentId];
|
|
436
|
+
saveAgentConfig(config);
|
|
437
|
+
}
|
|
438
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
439
|
+
var init_agent_config = __esm({
|
|
440
|
+
"src/lib/agent-config.ts"() {
|
|
441
|
+
"use strict";
|
|
442
|
+
init_config();
|
|
443
|
+
init_runtime_table();
|
|
444
|
+
init_secure_files();
|
|
445
|
+
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
446
|
+
KNOWN_RUNTIMES = {
|
|
447
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
448
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
449
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
450
|
+
};
|
|
451
|
+
RUNTIME_LABELS = {
|
|
452
|
+
claude: "Claude Code (Anthropic)",
|
|
453
|
+
codex: "Codex (OpenAI)",
|
|
454
|
+
opencode: "OpenCode (open source)"
|
|
455
|
+
};
|
|
456
|
+
DEFAULT_MODELS = {
|
|
457
|
+
claude: "claude-opus-4.6",
|
|
458
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
459
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
333
464
|
// src/lib/employees.ts
|
|
465
|
+
var employees_exports = {};
|
|
466
|
+
__export(employees_exports, {
|
|
467
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
468
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
469
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
470
|
+
addEmployee: () => addEmployee,
|
|
471
|
+
baseAgentName: () => baseAgentName,
|
|
472
|
+
canCoordinate: () => canCoordinate,
|
|
473
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
474
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
475
|
+
getEmployee: () => getEmployee,
|
|
476
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
477
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
478
|
+
hasRole: () => hasRole,
|
|
479
|
+
hireEmployee: () => hireEmployee,
|
|
480
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
481
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
482
|
+
isMultiInstance: () => isMultiInstance,
|
|
483
|
+
loadEmployees: () => loadEmployees,
|
|
484
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
485
|
+
normalizeRole: () => normalizeRole,
|
|
486
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
487
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
488
|
+
saveEmployees: () => saveEmployees,
|
|
489
|
+
validateEmployeeName: () => validateEmployeeName
|
|
490
|
+
});
|
|
334
491
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
335
|
-
import { existsSync as
|
|
492
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
336
493
|
import { execSync } from "child_process";
|
|
337
|
-
import
|
|
494
|
+
import path4 from "path";
|
|
338
495
|
import os3 from "os";
|
|
339
496
|
function normalizeRole(role) {
|
|
340
497
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -352,8 +509,26 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
352
509
|
if (!agentName) return false;
|
|
353
510
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
354
511
|
}
|
|
512
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
513
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
514
|
+
}
|
|
515
|
+
function validateEmployeeName(name) {
|
|
516
|
+
if (!name) {
|
|
517
|
+
return { valid: false, error: "Name is required" };
|
|
518
|
+
}
|
|
519
|
+
if (name.length > 32) {
|
|
520
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
521
|
+
}
|
|
522
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
523
|
+
return {
|
|
524
|
+
valid: false,
|
|
525
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return { valid: true };
|
|
529
|
+
}
|
|
355
530
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
356
|
-
if (!
|
|
531
|
+
if (!existsSync4(employeesPath)) {
|
|
357
532
|
return [];
|
|
358
533
|
}
|
|
359
534
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -363,10 +538,14 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
363
538
|
return [];
|
|
364
539
|
}
|
|
365
540
|
}
|
|
541
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
542
|
+
await mkdir2(path4.dirname(employeesPath), { recursive: true });
|
|
543
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
544
|
+
}
|
|
366
545
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
367
|
-
if (!
|
|
546
|
+
if (!existsSync4(employeesPath)) return [];
|
|
368
547
|
try {
|
|
369
|
-
return JSON.parse(
|
|
548
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
370
549
|
} catch {
|
|
371
550
|
return [];
|
|
372
551
|
}
|
|
@@ -374,6 +553,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
374
553
|
function getEmployee(employees, name) {
|
|
375
554
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
376
555
|
}
|
|
556
|
+
function getEmployeeByRole(employees, role) {
|
|
557
|
+
const lower = role.toLowerCase();
|
|
558
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
559
|
+
}
|
|
560
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
561
|
+
const lower = role.toLowerCase();
|
|
562
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
563
|
+
}
|
|
564
|
+
function hasRole(agentName, role) {
|
|
565
|
+
const employees = loadEmployeesSync();
|
|
566
|
+
const emp = getEmployee(employees, agentName);
|
|
567
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
568
|
+
}
|
|
377
569
|
function baseAgentName(name, employees) {
|
|
378
570
|
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
379
571
|
if (!match) return name;
|
|
@@ -388,16 +580,141 @@ function isMultiInstance(agentName, employees) {
|
|
|
388
580
|
if (!emp) return false;
|
|
389
581
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
390
582
|
}
|
|
391
|
-
|
|
583
|
+
function addEmployee(employees, employee) {
|
|
584
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
585
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
586
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
587
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
588
|
+
}
|
|
589
|
+
return [...employees, normalized];
|
|
590
|
+
}
|
|
591
|
+
function appendToCoordinatorTeam(employee) {
|
|
592
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
593
|
+
if (!coordinator) return;
|
|
594
|
+
const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
595
|
+
if (!existsSync4(idPath)) return;
|
|
596
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
597
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
598
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
599
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
600
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
601
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
602
|
+
const entry = `
|
|
603
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
604
|
+
`;
|
|
605
|
+
let updated;
|
|
606
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
607
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
608
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
609
|
+
} else {
|
|
610
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
611
|
+
}
|
|
612
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
613
|
+
}
|
|
614
|
+
function capitalize(s) {
|
|
615
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
616
|
+
}
|
|
617
|
+
async function hireEmployee(employee) {
|
|
618
|
+
const employees = await loadEmployees();
|
|
619
|
+
const updated = addEmployee(employees, employee);
|
|
620
|
+
await saveEmployees(updated);
|
|
621
|
+
try {
|
|
622
|
+
appendToCoordinatorTeam(employee);
|
|
623
|
+
} catch {
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
627
|
+
const config = loadAgentConfig2();
|
|
628
|
+
const name = employee.name.toLowerCase();
|
|
629
|
+
if (!config[name] && config["default"]) {
|
|
630
|
+
config[name] = { ...config["default"] };
|
|
631
|
+
saveAgentConfig2(config);
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
return updated;
|
|
636
|
+
}
|
|
637
|
+
async function normalizeRosterCase(rosterPath) {
|
|
638
|
+
const employees = await loadEmployees(rosterPath);
|
|
639
|
+
let changed = false;
|
|
640
|
+
for (const emp of employees) {
|
|
641
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
642
|
+
const oldName = emp.name;
|
|
643
|
+
emp.name = emp.name.toLowerCase();
|
|
644
|
+
changed = true;
|
|
645
|
+
try {
|
|
646
|
+
const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
|
|
647
|
+
const oldPath = path4.join(identityDir, `${oldName}.md`);
|
|
648
|
+
const newPath = path4.join(identityDir, `${emp.name}.md`);
|
|
649
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
650
|
+
renameSync2(oldPath, newPath);
|
|
651
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
652
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
653
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
654
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
655
|
+
unlinkSync(oldPath);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (changed) {
|
|
663
|
+
await saveEmployees(employees, rosterPath);
|
|
664
|
+
}
|
|
665
|
+
return changed;
|
|
666
|
+
}
|
|
667
|
+
function findExeBin() {
|
|
668
|
+
try {
|
|
669
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
670
|
+
} catch {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function registerBinSymlinks(name) {
|
|
675
|
+
const created = [];
|
|
676
|
+
const skipped = [];
|
|
677
|
+
const errors = [];
|
|
678
|
+
const exeBinPath = findExeBin();
|
|
679
|
+
if (!exeBinPath) {
|
|
680
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
681
|
+
return { created, skipped, errors };
|
|
682
|
+
}
|
|
683
|
+
const binDir = path4.dirname(exeBinPath);
|
|
684
|
+
let target;
|
|
685
|
+
try {
|
|
686
|
+
target = readlinkSync(exeBinPath);
|
|
687
|
+
} catch {
|
|
688
|
+
errors.push("Could not read 'exe' symlink");
|
|
689
|
+
return { created, skipped, errors };
|
|
690
|
+
}
|
|
691
|
+
for (const suffix of ["", "-opencode"]) {
|
|
692
|
+
const linkName = `${name}${suffix}`;
|
|
693
|
+
const linkPath = path4.join(binDir, linkName);
|
|
694
|
+
if (existsSync4(linkPath)) {
|
|
695
|
+
skipped.push(linkName);
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
symlinkSync(target, linkPath);
|
|
700
|
+
created.push(linkName);
|
|
701
|
+
} catch (err) {
|
|
702
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return { created, skipped, errors };
|
|
706
|
+
}
|
|
707
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
392
708
|
var init_employees = __esm({
|
|
393
709
|
"src/lib/employees.ts"() {
|
|
394
710
|
"use strict";
|
|
395
711
|
init_config();
|
|
396
|
-
EMPLOYEES_PATH =
|
|
712
|
+
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
397
713
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
398
714
|
COORDINATOR_ROLE = "COO";
|
|
399
715
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
400
|
-
IDENTITY_DIR =
|
|
716
|
+
IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
|
|
717
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
401
718
|
}
|
|
402
719
|
});
|
|
403
720
|
|
|
@@ -409,13 +726,13 @@ __export(session_registry_exports, {
|
|
|
409
726
|
refreshSessionProject: () => refreshSessionProject,
|
|
410
727
|
registerSession: () => registerSession
|
|
411
728
|
});
|
|
412
|
-
import { readFileSync as
|
|
729
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
|
|
413
730
|
import { execSync as execSync2 } from "child_process";
|
|
414
|
-
import
|
|
731
|
+
import path5 from "path";
|
|
415
732
|
import os4 from "os";
|
|
416
733
|
function registerSession(entry) {
|
|
417
|
-
const dir =
|
|
418
|
-
if (!
|
|
734
|
+
const dir = path5.dirname(REGISTRY_PATH);
|
|
735
|
+
if (!existsSync5(dir)) {
|
|
419
736
|
mkdirSync2(dir, { recursive: true });
|
|
420
737
|
}
|
|
421
738
|
const sessions = listSessions();
|
|
@@ -425,7 +742,7 @@ function registerSession(entry) {
|
|
|
425
742
|
} else {
|
|
426
743
|
sessions.push(entry);
|
|
427
744
|
}
|
|
428
|
-
|
|
745
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
429
746
|
}
|
|
430
747
|
function refreshSessionProject(windowName, projectDir) {
|
|
431
748
|
const sessions = listSessions();
|
|
@@ -433,13 +750,13 @@ function refreshSessionProject(windowName, projectDir) {
|
|
|
433
750
|
if (!entry || entry.projectDir === projectDir) return;
|
|
434
751
|
entry.projectDir = projectDir;
|
|
435
752
|
try {
|
|
436
|
-
|
|
753
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
437
754
|
} catch {
|
|
438
755
|
}
|
|
439
756
|
}
|
|
440
757
|
function listSessions() {
|
|
441
758
|
try {
|
|
442
|
-
const raw =
|
|
759
|
+
const raw = readFileSync4(REGISTRY_PATH, "utf8");
|
|
443
760
|
return JSON.parse(raw);
|
|
444
761
|
} catch {
|
|
445
762
|
return [];
|
|
@@ -460,7 +777,7 @@ function pruneStaleSessions() {
|
|
|
460
777
|
const alive = sessions.filter((s) => liveSet.has(s.windowName));
|
|
461
778
|
const pruned = sessions.length - alive.length;
|
|
462
779
|
if (pruned > 0) {
|
|
463
|
-
|
|
780
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(alive, null, 2));
|
|
464
781
|
}
|
|
465
782
|
return pruned;
|
|
466
783
|
}
|
|
@@ -468,7 +785,7 @@ var REGISTRY_PATH;
|
|
|
468
785
|
var init_session_registry = __esm({
|
|
469
786
|
"src/lib/session-registry.ts"() {
|
|
470
787
|
"use strict";
|
|
471
|
-
REGISTRY_PATH =
|
|
788
|
+
REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
472
789
|
}
|
|
473
790
|
});
|
|
474
791
|
|
|
@@ -740,68 +1057,6 @@ var init_provider_table = __esm({
|
|
|
740
1057
|
}
|
|
741
1058
|
});
|
|
742
1059
|
|
|
743
|
-
// src/lib/runtime-table.ts
|
|
744
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
745
|
-
var init_runtime_table = __esm({
|
|
746
|
-
"src/lib/runtime-table.ts"() {
|
|
747
|
-
"use strict";
|
|
748
|
-
RUNTIME_TABLE = {
|
|
749
|
-
codex: {
|
|
750
|
-
binary: "codex",
|
|
751
|
-
launchMode: "interactive",
|
|
752
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
753
|
-
inlineFlag: "--no-alt-screen",
|
|
754
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
755
|
-
defaultModel: "gpt-5.5"
|
|
756
|
-
},
|
|
757
|
-
opencode: {
|
|
758
|
-
binary: "opencode",
|
|
759
|
-
launchMode: "exec",
|
|
760
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
761
|
-
inlineFlag: "",
|
|
762
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
763
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
DEFAULT_RUNTIME = "claude";
|
|
767
|
-
}
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
// src/lib/agent-config.ts
|
|
771
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
772
|
-
import path5 from "path";
|
|
773
|
-
function loadAgentConfig() {
|
|
774
|
-
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
775
|
-
try {
|
|
776
|
-
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
777
|
-
} catch {
|
|
778
|
-
return {};
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
function getAgentRuntime(agentId) {
|
|
782
|
-
const config = loadAgentConfig();
|
|
783
|
-
const entry = config[agentId];
|
|
784
|
-
if (entry) return entry;
|
|
785
|
-
const orgDefault = config["default"];
|
|
786
|
-
if (orgDefault) return orgDefault;
|
|
787
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
788
|
-
}
|
|
789
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
790
|
-
var init_agent_config = __esm({
|
|
791
|
-
"src/lib/agent-config.ts"() {
|
|
792
|
-
"use strict";
|
|
793
|
-
init_config();
|
|
794
|
-
init_runtime_table();
|
|
795
|
-
init_secure_files();
|
|
796
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
797
|
-
DEFAULT_MODELS = {
|
|
798
|
-
claude: "claude-opus-4.6",
|
|
799
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
800
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
|
|
805
1060
|
// src/lib/intercom-queue.ts
|
|
806
1061
|
var intercom_queue_exports = {};
|
|
807
1062
|
__export(intercom_queue_exports, {
|
|
@@ -3818,6 +4073,22 @@ async function ensureSchema() {
|
|
|
3818
4073
|
} catch (e) {
|
|
3819
4074
|
logCatchDebug("migration", e);
|
|
3820
4075
|
}
|
|
4076
|
+
try {
|
|
4077
|
+
await client.execute({
|
|
4078
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
4079
|
+
args: []
|
|
4080
|
+
});
|
|
4081
|
+
} catch (e) {
|
|
4082
|
+
logCatchDebug("migration", e);
|
|
4083
|
+
}
|
|
4084
|
+
try {
|
|
4085
|
+
await client.execute({
|
|
4086
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
4087
|
+
args: []
|
|
4088
|
+
});
|
|
4089
|
+
} catch (e) {
|
|
4090
|
+
logCatchDebug("migration", e);
|
|
4091
|
+
}
|
|
3821
4092
|
}
|
|
3822
4093
|
async function disposeDatabase() {
|
|
3823
4094
|
if (_walCheckpointTimer) {
|
|
@@ -4236,7 +4507,7 @@ async function assertVpsLicense(opts) {
|
|
|
4236
4507
|
}
|
|
4237
4508
|
if (!transientFailure) {
|
|
4238
4509
|
throw new Error(
|
|
4239
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
4510
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
4240
4511
|
);
|
|
4241
4512
|
}
|
|
4242
4513
|
const fresh = await getCachedLicense();
|
|
@@ -4273,7 +4544,7 @@ async function assertVpsLicense(opts) {
|
|
|
4273
4544
|
} catch {
|
|
4274
4545
|
}
|
|
4275
4546
|
throw new Error(
|
|
4276
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
4547
|
+
`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.`
|
|
4277
4548
|
);
|
|
4278
4549
|
}
|
|
4279
4550
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -4305,7 +4576,7 @@ var init_license = __esm({
|
|
|
4305
4576
|
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
4306
4577
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
4307
4578
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
4308
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
4579
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
4309
4580
|
RETRY_DELAY_MS = 500;
|
|
4310
4581
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
4311
4582
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -4849,6 +5120,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
4849
5120
|
args: [identifier, ...scope.args]
|
|
4850
5121
|
});
|
|
4851
5122
|
if (result.rows.length === 1) return result.rows[0];
|
|
5123
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
5124
|
+
result = await client.execute({
|
|
5125
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
5126
|
+
args: [`${identifier}%`]
|
|
5127
|
+
});
|
|
5128
|
+
if (result.rows.length === 1) return result.rows[0];
|
|
5129
|
+
if (result.rows.length > 1) {
|
|
5130
|
+
const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
5131
|
+
throw new Error(
|
|
5132
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
5133
|
+
);
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
4852
5136
|
result = await client.execute({
|
|
4853
5137
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
4854
5138
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -5703,12 +5987,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5703
5987
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
5704
5988
|
args: [now, taskId]
|
|
5705
5989
|
});
|
|
5706
|
-
if (
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5990
|
+
if (unblocked.rowsAffected === 0) return;
|
|
5991
|
+
const ubScope = sessionScopeFilter();
|
|
5992
|
+
const unblockedRows = await client.execute({
|
|
5993
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
5994
|
+
args: [now, ...ubScope.args]
|
|
5995
|
+
});
|
|
5996
|
+
if (baseDir) {
|
|
5712
5997
|
for (const ur of unblockedRows.rows) {
|
|
5713
5998
|
try {
|
|
5714
5999
|
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
@@ -5720,6 +6005,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5720
6005
|
}
|
|
5721
6006
|
}
|
|
5722
6007
|
}
|
|
6008
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
6009
|
+
try {
|
|
6010
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
6011
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
6012
|
+
for (const ur of unblockedRows.rows) {
|
|
6013
|
+
const assignee = String(ur.assigned_to);
|
|
6014
|
+
if (dispatched.has(assignee)) continue;
|
|
6015
|
+
dispatched.add(assignee);
|
|
6016
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
6017
|
+
}
|
|
6018
|
+
} catch {
|
|
6019
|
+
}
|
|
6020
|
+
}
|
|
5723
6021
|
}
|
|
5724
6022
|
async function findNextTask(assignedTo) {
|
|
5725
6023
|
const client = getClient();
|
|
@@ -5936,6 +6234,15 @@ __export(behaviors_exports, {
|
|
|
5936
6234
|
});
|
|
5937
6235
|
import crypto5 from "crypto";
|
|
5938
6236
|
async function storeBehavior(opts) {
|
|
6237
|
+
try {
|
|
6238
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
6239
|
+
const roster = loadEmployeesSync2();
|
|
6240
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
6241
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
6242
|
+
}
|
|
6243
|
+
} catch (e) {
|
|
6244
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
6245
|
+
}
|
|
5939
6246
|
const client = getClient();
|
|
5940
6247
|
const id = crypto5.randomUUID();
|
|
5941
6248
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6459,6 +6766,12 @@ async function updateTask(input) {
|
|
|
6459
6766
|
}
|
|
6460
6767
|
}
|
|
6461
6768
|
}
|
|
6769
|
+
if (input.status === "cancelled") {
|
|
6770
|
+
try {
|
|
6771
|
+
await cascadeUnblock(taskId, input.baseDir, now);
|
|
6772
|
+
} catch {
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6462
6775
|
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6463
6776
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6464
6777
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -6990,11 +7303,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
6990
7303
|
}
|
|
6991
7304
|
}
|
|
6992
7305
|
function resolveExeSession() {
|
|
7306
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
7307
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
7308
|
+
if (fromEnv) return fromEnv;
|
|
7309
|
+
}
|
|
6993
7310
|
const mySession = getMySession();
|
|
6994
7311
|
if (!mySession) {
|
|
6995
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
6996
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
6997
|
-
}
|
|
6998
7312
|
return null;
|
|
6999
7313
|
}
|
|
7000
7314
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -7009,6 +7323,10 @@ function resolveExeSession() {
|
|
|
7009
7323
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
7010
7324
|
`
|
|
7011
7325
|
);
|
|
7326
|
+
try {
|
|
7327
|
+
registerParentExe(key, fromSessionName);
|
|
7328
|
+
} catch {
|
|
7329
|
+
}
|
|
7012
7330
|
candidate = fromSessionName;
|
|
7013
7331
|
} else {
|
|
7014
7332
|
candidate = fromCache;
|
|
@@ -8703,11 +9021,17 @@ var init_platform_procedures = __esm({
|
|
|
8703
9021
|
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."
|
|
8704
9022
|
},
|
|
8705
9023
|
{
|
|
8706
|
-
title: "
|
|
9024
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
8707
9025
|
domain: "workflow",
|
|
8708
9026
|
priority: "p1",
|
|
8709
9027
|
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."
|
|
8710
9028
|
},
|
|
9029
|
+
{
|
|
9030
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
9031
|
+
domain: "identity",
|
|
9032
|
+
priority: "p0",
|
|
9033
|
+
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."
|
|
9034
|
+
},
|
|
8711
9035
|
{
|
|
8712
9036
|
title: "Single dispatch path \u2014 create_task only",
|
|
8713
9037
|
domain: "workflow",
|
|
@@ -8741,6 +9065,12 @@ var init_platform_procedures = __esm({
|
|
|
8741
9065
|
priority: "p0",
|
|
8742
9066
|
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."
|
|
8743
9067
|
},
|
|
9068
|
+
{
|
|
9069
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
9070
|
+
domain: "security",
|
|
9071
|
+
priority: "p0",
|
|
9072
|
+
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."
|
|
9073
|
+
},
|
|
8744
9074
|
{
|
|
8745
9075
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
8746
9076
|
domain: "support",
|
|
@@ -9026,10 +9356,24 @@ function stableId(memoryId, type, content) {
|
|
|
9026
9356
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
9027
9357
|
}
|
|
9028
9358
|
function cleanText(text) {
|
|
9029
|
-
|
|
9359
|
+
let cleaned = text.replace(
|
|
9360
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
9361
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
9362
|
+
);
|
|
9363
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9364
|
+
return cleaned;
|
|
9030
9365
|
}
|
|
9031
|
-
function
|
|
9032
|
-
|
|
9366
|
+
function splitSegments(text) {
|
|
9367
|
+
const cleaned = cleanText(text);
|
|
9368
|
+
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);
|
|
9369
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
9370
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
9371
|
+
if (lines.length > 0) return lines;
|
|
9372
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
9373
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
9374
|
+
}
|
|
9375
|
+
}
|
|
9376
|
+
return segments;
|
|
9033
9377
|
}
|
|
9034
9378
|
function inferCardType(sentence, toolName) {
|
|
9035
9379
|
const lower = sentence.toLowerCase();
|
|
@@ -9061,12 +9405,12 @@ function predicateFor(type) {
|
|
|
9061
9405
|
}
|
|
9062
9406
|
}
|
|
9063
9407
|
function extractMemoryCards(row) {
|
|
9064
|
-
const
|
|
9408
|
+
const segments = splitSegments(row.raw_text);
|
|
9065
9409
|
const cards = [];
|
|
9066
|
-
for (const sentence of
|
|
9410
|
+
for (const sentence of segments) {
|
|
9067
9411
|
const type = inferCardType(sentence, row.tool_name);
|
|
9068
9412
|
const subject = extractSubject(sentence, row.agent_id);
|
|
9069
|
-
const content = sentence.length >
|
|
9413
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
9070
9414
|
cards.push({
|
|
9071
9415
|
id: stableId(row.id, type, content),
|
|
9072
9416
|
memory_id: row.id,
|
|
@@ -9162,13 +9506,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
9162
9506
|
last_accessed: String(row.timestamp)
|
|
9163
9507
|
}));
|
|
9164
9508
|
}
|
|
9165
|
-
var MAX_CARDS_PER_MEMORY,
|
|
9509
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
9166
9510
|
var init_memory_cards = __esm({
|
|
9167
9511
|
"src/lib/memory-cards.ts"() {
|
|
9168
9512
|
"use strict";
|
|
9169
9513
|
init_database();
|
|
9170
|
-
MAX_CARDS_PER_MEMORY =
|
|
9171
|
-
|
|
9514
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
9515
|
+
MAX_SEGMENT_CHARS = 500;
|
|
9516
|
+
MIN_SEGMENT_CHARS = 20;
|
|
9172
9517
|
}
|
|
9173
9518
|
});
|
|
9174
9519
|
|