@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.
Files changed (95) hide show
  1. package/README.md +9 -7
  2. package/dist/bin/agentic-ontology-backfill.js +54 -11
  3. package/dist/bin/agentic-reflection-backfill.js +29 -1
  4. package/dist/bin/agentic-semantic-label.js +29 -1
  5. package/dist/bin/backfill-conversations.js +53 -10
  6. package/dist/bin/backfill-responses.js +54 -11
  7. package/dist/bin/backfill-vectors.js +29 -1
  8. package/dist/bin/bulk-sync-postgres.js +55 -12
  9. package/dist/bin/cleanup-stale-review-tasks.js +75 -15
  10. package/dist/bin/cli.js +293 -76
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +28 -2
  13. package/dist/bin/exe-assign.js +54 -11
  14. package/dist/bin/exe-boot.js +481 -147
  15. package/dist/bin/exe-call.js +45 -4
  16. package/dist/bin/exe-cloud.js +93 -15
  17. package/dist/bin/exe-dispatch.js +369 -24
  18. package/dist/bin/exe-doctor.js +53 -10
  19. package/dist/bin/exe-export-behaviors.js +54 -11
  20. package/dist/bin/exe-forget.js +54 -11
  21. package/dist/bin/exe-gateway.js +128 -23
  22. package/dist/bin/exe-heartbeat.js +75 -15
  23. package/dist/bin/exe-kill.js +54 -11
  24. package/dist/bin/exe-launch-agent.js +70 -12
  25. package/dist/bin/exe-new-employee.js +175 -7
  26. package/dist/bin/exe-pending-messages.js +75 -15
  27. package/dist/bin/exe-pending-notifications.js +75 -15
  28. package/dist/bin/exe-pending-reviews.js +75 -15
  29. package/dist/bin/exe-rename.js +54 -11
  30. package/dist/bin/exe-review.js +54 -11
  31. package/dist/bin/exe-search.js +54 -11
  32. package/dist/bin/exe-session-cleanup.js +491 -146
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +524 -245
  35. package/dist/bin/exe-start-opencode.js +534 -165
  36. package/dist/bin/exe-status.js +75 -15
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +54 -11
  39. package/dist/bin/git-sweep.js +369 -24
  40. package/dist/bin/graph-backfill.js +54 -11
  41. package/dist/bin/graph-export.js +54 -11
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +491 -146
  44. package/dist/bin/pre-publish.js +13 -1
  45. package/dist/bin/scan-tasks.js +369 -24
  46. package/dist/bin/setup.js +91 -13
  47. package/dist/bin/shard-migrate.js +54 -11
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +128 -23
  51. package/dist/hooks/bug-report-worker.js +128 -23
  52. package/dist/hooks/codex-stop-task-finalizer.js +512 -140
  53. package/dist/hooks/commit-complete.js +369 -24
  54. package/dist/hooks/error-recall.js +54 -11
  55. package/dist/hooks/ingest.js +4575 -252
  56. package/dist/hooks/instructions-loaded.js +54 -11
  57. package/dist/hooks/notification.js +54 -11
  58. package/dist/hooks/post-compact.js +75 -15
  59. package/dist/hooks/post-tool-combined.js +75 -15
  60. package/dist/hooks/pre-compact.js +449 -104
  61. package/dist/hooks/pre-tool-use.js +90 -15
  62. package/dist/hooks/prompt-submit.js +129 -24
  63. package/dist/hooks/session-end.js +451 -109
  64. package/dist/hooks/session-start.js +104 -16
  65. package/dist/hooks/stop.js +74 -14
  66. package/dist/hooks/subagent-stop.js +75 -15
  67. package/dist/hooks/summary-worker.js +73 -7
  68. package/dist/index.js +128 -23
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +38 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +16 -0
  73. package/dist/lib/db.js +16 -0
  74. package/dist/lib/device-registry.js +16 -0
  75. package/dist/lib/employee-templates.js +29 -3
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +268 -42
  78. package/dist/lib/hybrid-search.js +54 -11
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +29 -1
  82. package/dist/lib/skill-learning.js +458 -70
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +54 -11
  85. package/dist/lib/tasks.js +393 -91
  86. package/dist/lib/tmux-routing.js +316 -14
  87. package/dist/mcp/server.js +169 -30
  88. package/dist/mcp/tools/create-task.js +75 -13
  89. package/dist/mcp/tools/deactivate-behavior.js +33 -24
  90. package/dist/mcp/tools/list-tasks.js +21 -4
  91. package/dist/mcp/tools/send-message.js +21 -4
  92. package/dist/mcp/tools/update-task.js +390 -91
  93. package/dist/runtime/index.js +446 -101
  94. package/dist/tui/App.js +208 -54
  95. package/package.json +1 -1
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
7
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
8
  }) : x)(function(x) {
@@ -14,6 +16,15 @@ var __export = (target, all) => {
14
16
  for (var name in all)
15
17
  __defProp(target, name, { get: all[name], enumerable: true });
16
18
  };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
17
28
 
18
29
  // src/lib/secure-files.ts
19
30
  import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
@@ -177,6 +188,7 @@ __export(agent_config_exports, {
177
188
  getAgentRuntime: () => getAgentRuntime,
178
189
  loadAgentConfig: () => loadAgentConfig,
179
190
  saveAgentConfig: () => saveAgentConfig,
191
+ setAgentMcps: () => setAgentMcps,
180
192
  setAgentRuntime: () => setAgentRuntime
181
193
  });
182
194
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4 } from "fs";
@@ -203,7 +215,7 @@ function getAgentRuntime(agentId) {
203
215
  if (orgDefault) return orgDefault;
204
216
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
205
217
  }
206
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
218
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
207
219
  const knownModels = KNOWN_RUNTIMES[runtime];
208
220
  if (!knownModels) {
209
221
  return {
@@ -218,12 +230,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
218
230
  };
219
231
  }
220
232
  const config = loadAgentConfig();
233
+ const existing = config[agentId];
221
234
  const entry = { runtime, model };
222
235
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
236
+ if (mcps !== void 0) {
237
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
238
+ } else if (existing?.mcps) {
239
+ entry.mcps = existing.mcps;
240
+ }
223
241
  config[agentId] = entry;
224
242
  saveAgentConfig(config);
225
243
  return { ok: true };
226
244
  }
245
+ function setAgentMcps(agentId, mcps) {
246
+ const config = loadAgentConfig();
247
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
248
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
249
+ config[agentId] = existing;
250
+ saveAgentConfig(config);
251
+ return { ok: true };
252
+ }
227
253
  function clearAgentRuntime(agentId) {
228
254
  const config = loadAgentConfig();
229
255
  delete config[agentId];
@@ -256,6 +282,32 @@ var init_agent_config = __esm({
256
282
  });
257
283
 
258
284
  // src/lib/employees.ts
285
+ var employees_exports = {};
286
+ __export(employees_exports, {
287
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
288
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
289
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
290
+ addEmployee: () => addEmployee,
291
+ baseAgentName: () => baseAgentName,
292
+ canCoordinate: () => canCoordinate,
293
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
294
+ getCoordinatorName: () => getCoordinatorName,
295
+ getEmployee: () => getEmployee,
296
+ getEmployeeByRole: () => getEmployeeByRole,
297
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
298
+ hasRole: () => hasRole,
299
+ hireEmployee: () => hireEmployee,
300
+ isCoordinatorName: () => isCoordinatorName,
301
+ isCoordinatorRole: () => isCoordinatorRole,
302
+ isMultiInstance: () => isMultiInstance,
303
+ loadEmployees: () => loadEmployees,
304
+ loadEmployeesSync: () => loadEmployeesSync,
305
+ normalizeRole: () => normalizeRole,
306
+ normalizeRosterCase: () => normalizeRosterCase,
307
+ registerBinSymlinks: () => registerBinSymlinks,
308
+ saveEmployees: () => saveEmployees,
309
+ validateEmployeeName: () => validateEmployeeName
310
+ });
259
311
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
260
312
  import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
261
313
  import { execSync as execSync2 } from "child_process";
@@ -270,6 +322,16 @@ function isCoordinatorRole(role) {
270
322
  function getCoordinatorEmployee(employees) {
271
323
  return employees.find((e) => isCoordinatorRole(e.role));
272
324
  }
325
+ function getCoordinatorName(employees = loadEmployeesSync()) {
326
+ return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
327
+ }
328
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
329
+ if (!agentName) return false;
330
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
331
+ }
332
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
333
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
334
+ }
273
335
  function validateEmployeeName(name) {
274
336
  if (!name) {
275
337
  return { valid: false, error: "Name is required" };
@@ -308,6 +370,36 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
308
370
  return [];
309
371
  }
310
372
  }
373
+ function getEmployee(employees, name) {
374
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
375
+ }
376
+ function getEmployeeByRole(employees, role) {
377
+ const lower = role.toLowerCase();
378
+ return employees.find((e) => e.role.toLowerCase() === lower);
379
+ }
380
+ function getEmployeeNamesByRole(employees, role) {
381
+ const lower = role.toLowerCase();
382
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
383
+ }
384
+ function hasRole(agentName, role) {
385
+ const employees = loadEmployeesSync();
386
+ const emp = getEmployee(employees, agentName);
387
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
388
+ }
389
+ function baseAgentName(name, employees) {
390
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
391
+ if (!match) return name;
392
+ const base = match[1];
393
+ const roster = employees ?? loadEmployeesSync();
394
+ if (getEmployee(roster, base)) return base;
395
+ return name;
396
+ }
397
+ function isMultiInstance(agentName, employees) {
398
+ const roster = employees ?? loadEmployeesSync();
399
+ const emp = getEmployee(roster, agentName);
400
+ if (!emp) return false;
401
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
402
+ }
311
403
  function addEmployee(employees, employee) {
312
404
  const { systemPrompt: _legacyPrompt, ...rest } = employee;
313
405
  const normalized = { ...rest, name: employee.name.toLowerCase() };
@@ -362,6 +454,36 @@ async function hireEmployee(employee) {
362
454
  }
363
455
  return updated;
364
456
  }
457
+ async function normalizeRosterCase(rosterPath) {
458
+ const employees = await loadEmployees(rosterPath);
459
+ let changed = false;
460
+ for (const emp of employees) {
461
+ if (emp.name !== emp.name.toLowerCase()) {
462
+ const oldName = emp.name;
463
+ emp.name = emp.name.toLowerCase();
464
+ changed = true;
465
+ try {
466
+ const identityDir = path4.join(os2.homedir(), ".exe-os", "identity");
467
+ const oldPath = path4.join(identityDir, `${oldName}.md`);
468
+ const newPath = path4.join(identityDir, `${emp.name}.md`);
469
+ if (existsSync5(oldPath) && !existsSync5(newPath)) {
470
+ renameSync2(oldPath, newPath);
471
+ } else if (existsSync5(oldPath) && oldPath !== newPath) {
472
+ const content = readFileSync4(oldPath, "utf-8");
473
+ writeFileSync3(newPath, content, "utf-8");
474
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
475
+ unlinkSync2(oldPath);
476
+ }
477
+ }
478
+ } catch {
479
+ }
480
+ }
481
+ }
482
+ if (changed) {
483
+ await saveEmployees(employees, rosterPath);
484
+ }
485
+ return changed;
486
+ }
365
487
  function findExeBin() {
366
488
  try {
367
489
  return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
@@ -402,13 +524,15 @@ function registerBinSymlinks(name) {
402
524
  }
403
525
  return { created, skipped, errors };
404
526
  }
405
- var EMPLOYEES_PATH, COORDINATOR_ROLE, IDENTITY_DIR, TEAM_SECTION_RE;
527
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
406
528
  var init_employees = __esm({
407
529
  "src/lib/employees.ts"() {
408
530
  "use strict";
409
531
  init_config();
410
532
  EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
533
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
411
534
  COORDINATOR_ROLE = "COO";
535
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
412
536
  IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
413
537
  TEAM_SECTION_RE = /^## Team\b.*$/m;
414
538
  }
@@ -1295,6 +1419,16 @@ import { chmodSync as chmodSync4, existsSync as existsSync12, mkdirSync as mkdir
1295
1419
  import { randomBytes } from "crypto";
1296
1420
  import path11 from "path";
1297
1421
  import os7 from "os";
1422
+ function resolveDefaultAgentId() {
1423
+ if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
1424
+ try {
1425
+ const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
1426
+ const coo = loadEmployeesSync2().find((e) => e.role === "COO");
1427
+ if (coo) return coo.name;
1428
+ } catch {
1429
+ }
1430
+ return "exe";
1431
+ }
1298
1432
  function mcpHttpPort() {
1299
1433
  return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
1300
1434
  }
@@ -1324,11 +1458,14 @@ function readOrCreateDaemonToken(homeDir = os7.homedir()) {
1324
1458
  function buildMcpHttpHeaders(homeDir = os7.homedir(), opts = {}) {
1325
1459
  const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
1326
1460
  const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
1327
- return {
1461
+ const sessionName = opts.useShellPlaceholders ? "$(tmux display-message -p '#{session_name}' 2>/dev/null || echo '')" : process.env.EXE_SESSION_NAME ?? "";
1462
+ const headers = {
1328
1463
  Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
1329
1464
  "X-Agent-Id": agentId,
1330
1465
  "X-Agent-Role": agentRole
1331
1466
  };
1467
+ if (sessionName) headers["X-Exe-Session"] = sessionName;
1468
+ return headers;
1332
1469
  }
1333
1470
  function buildClaudeHttpMcpEntry(homeDir = os7.homedir()) {
1334
1471
  return {
@@ -1342,19 +1479,22 @@ var init_mcp_http_config = __esm({
1342
1479
  "src/adapters/mcp-http-config.ts"() {
1343
1480
  "use strict";
1344
1481
  DEFAULT_MCP_HTTP_PORT = "48739";
1345
- DEFAULT_MCP_HTTP_AGENT_ID = "exe";
1482
+ DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
1346
1483
  DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
1347
1484
  }
1348
1485
  });
1349
1486
 
1350
1487
  // src/adapters/runtime-hook-manifest.ts
1488
+ function isLegacyHomeDirHookCommand(command) {
1489
+ return LEGACY_HOME_DIR_HOOK_PATTERNS.some((pattern) => command.includes(pattern));
1490
+ }
1351
1491
  function commandHasAnyMarker(command, markers) {
1352
1492
  return markers.some((marker) => command.includes(marker));
1353
1493
  }
1354
1494
  function isLegacySplitPostToolCommand(command) {
1355
1495
  return commandHasAnyMarker(command, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS);
1356
1496
  }
1357
- var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
1497
+ var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS, LEGACY_HOME_DIR_HOOK_PATTERNS;
1358
1498
  var init_runtime_hook_manifest = __esm({
1359
1499
  "src/adapters/runtime-hook-manifest.ts"() {
1360
1500
  "use strict";
@@ -1487,6 +1627,14 @@ var init_runtime_hook_manifest = __esm({
1487
1627
  "dist/hooks/error-recall.js",
1488
1628
  "dist/hooks/ingest-worker.js"
1489
1629
  ];
1630
+ LEGACY_HOME_DIR_HOOK_PATTERNS = [
1631
+ ".exe-os/hooks/",
1632
+ // Old hook storage path (pre-npm-package era)
1633
+ "log-prefix.js",
1634
+ // Removed hook from early versions
1635
+ "approval-bridge.log"
1636
+ // Removed log redirect from early versions
1637
+ ];
1490
1638
  }
1491
1639
  });
1492
1640
 
@@ -1966,6 +2114,14 @@ async function mergeHooks(packageRoot, homeDir = os8.homedir()) {
1966
2114
  ];
1967
2115
  let added = 0;
1968
2116
  let skipped = 0;
2117
+ for (const eventKey of Object.keys(settings.hooks)) {
2118
+ const groups = settings.hooks[eventKey];
2119
+ if (!Array.isArray(groups)) continue;
2120
+ settings.hooks[eventKey] = groups.map((g) => ({
2121
+ ...g,
2122
+ hooks: g.hooks.filter((h) => !isLegacyHomeDirHookCommand(h.command))
2123
+ })).filter((g) => g.hooks.length > 0);
2124
+ }
1969
2125
  const postToolGroups = settings.hooks["PostToolUse"];
1970
2126
  if (Array.isArray(postToolGroups)) {
1971
2127
  settings.hooks["PostToolUse"] = postToolGroups.map((g) => ({
@@ -2680,11 +2836,17 @@ var PLATFORM_PROCEDURES = [
2680
2836
  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."
2681
2837
  },
2682
2838
  {
2683
- title: "Customer orchestration maturity \u2014 recommend, never trap",
2839
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
2684
2840
  domain: "workflow",
2685
2841
  priority: "p1",
2686
2842
  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."
2687
2843
  },
2844
+ {
2845
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
2846
+ domain: "identity",
2847
+ priority: "p0",
2848
+ 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."
2849
+ },
2688
2850
  {
2689
2851
  title: "Single dispatch path \u2014 create_task only",
2690
2852
  domain: "workflow",
@@ -2718,6 +2880,12 @@ var PLATFORM_PROCEDURES = [
2718
2880
  priority: "p0",
2719
2881
  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."
2720
2882
  },
2883
+ {
2884
+ title: "Destructive operations \u2014 mandatory reviewer gate",
2885
+ domain: "security",
2886
+ priority: "p0",
2887
+ 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."
2888
+ },
2721
2889
  {
2722
2890
  title: "Customer patch triage \u2014 upstream bug vs customization",
2723
2891
  domain: "support",
@@ -3305,7 +3473,7 @@ import { jwtVerify, importSPKI } from "jose";
3305
3473
  var LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
3306
3474
  var CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
3307
3475
  var DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
3308
- var API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3476
+ var API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
3309
3477
  var RETRY_DELAY_MS = 500;
3310
3478
  async function fetchRetry(url, init) {
3311
3479
  try {
@@ -3259,6 +3259,22 @@ async function ensureSchema() {
3259
3259
  } catch (e) {
3260
3260
  logCatchDebug("migration", e);
3261
3261
  }
3262
+ try {
3263
+ await client.execute({
3264
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
3265
+ args: []
3266
+ });
3267
+ } catch (e) {
3268
+ logCatchDebug("migration", e);
3269
+ }
3270
+ try {
3271
+ await client.execute({
3272
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
3273
+ args: []
3274
+ });
3275
+ } catch (e) {
3276
+ logCatchDebug("migration", e);
3277
+ }
3262
3278
  }
3263
3279
  async function disposeDatabase() {
3264
3280
  if (_walCheckpointTimer) {
@@ -3609,7 +3625,7 @@ var init_license = __esm({
3609
3625
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3610
3626
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3611
3627
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3612
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3628
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
3613
3629
  }
3614
3630
  });
3615
3631
 
@@ -3662,6 +3678,18 @@ function extractRootExe(name) {
3662
3678
  const parts = name.split("-").filter(Boolean);
3663
3679
  return parts.length > 0 ? parts[parts.length - 1] : null;
3664
3680
  }
3681
+ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3682
+ if (!existsSync12(SESSION_CACHE)) {
3683
+ mkdirSync6(SESSION_CACHE, { recursive: true });
3684
+ }
3685
+ const rootExe = extractRootExe(parentExe) ?? parentExe;
3686
+ const filePath = path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3687
+ writeFileSync6(filePath, JSON.stringify({
3688
+ parentExe: rootExe,
3689
+ dispatchedBy: dispatchedBy || rootExe,
3690
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString()
3691
+ }));
3692
+ }
3665
3693
  function getParentExe(sessionKey) {
3666
3694
  try {
3667
3695
  const data = JSON.parse(readFileSync9(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
@@ -3671,11 +3699,12 @@ function getParentExe(sessionKey) {
3671
3699
  }
3672
3700
  }
3673
3701
  function resolveExeSession() {
3702
+ if (process.env.EXE_SESSION_NAME) {
3703
+ const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3704
+ if (fromEnv) return fromEnv;
3705
+ }
3674
3706
  const mySession = getMySession();
3675
3707
  if (!mySession) {
3676
- if (process.env.EXE_SESSION_NAME) {
3677
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3678
- }
3679
3708
  return null;
3680
3709
  }
3681
3710
  const fromSessionName = extractRootExe(mySession);
@@ -3690,6 +3719,10 @@ function resolveExeSession() {
3690
3719
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3691
3720
  `
3692
3721
  );
3722
+ try {
3723
+ registerParentExe(key, fromSessionName);
3724
+ } catch {
3725
+ }
3693
3726
  candidate = fromSessionName;
3694
3727
  } else {
3695
3728
  candidate = fromCache;
@@ -4853,11 +4886,17 @@ var init_platform_procedures = __esm({
4853
4886
  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."
4854
4887
  },
4855
4888
  {
4856
- title: "Customer orchestration maturity \u2014 recommend, never trap",
4889
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
4857
4890
  domain: "workflow",
4858
4891
  priority: "p1",
4859
4892
  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."
4860
4893
  },
4894
+ {
4895
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
4896
+ domain: "identity",
4897
+ priority: "p0",
4898
+ 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."
4899
+ },
4861
4900
  {
4862
4901
  title: "Single dispatch path \u2014 create_task only",
4863
4902
  domain: "workflow",
@@ -4891,6 +4930,12 @@ var init_platform_procedures = __esm({
4891
4930
  priority: "p0",
4892
4931
  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."
4893
4932
  },
4933
+ {
4934
+ title: "Destructive operations \u2014 mandatory reviewer gate",
4935
+ domain: "security",
4936
+ priority: "p0",
4937
+ 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."
4938
+ },
4894
4939
  {
4895
4940
  title: "Customer patch triage \u2014 upstream bug vs customization",
4896
4941
  domain: "support",
@@ -5176,10 +5221,24 @@ function stableId(memoryId, type, content) {
5176
5221
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
5177
5222
  }
5178
5223
  function cleanText(text) {
5179
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
5180
- }
5181
- function splitSentences(text) {
5182
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
5224
+ let cleaned = text.replace(
5225
+ /```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
5226
+ (_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
5227
+ );
5228
+ cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
5229
+ return cleaned;
5230
+ }
5231
+ function splitSegments(text) {
5232
+ const cleaned = cleanText(text);
5233
+ 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);
5234
+ if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
5235
+ const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
5236
+ if (lines.length > 0) return lines;
5237
+ if (cleaned.length >= MIN_SEGMENT_CHARS) {
5238
+ return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
5239
+ }
5240
+ }
5241
+ return segments;
5183
5242
  }
5184
5243
  function inferCardType(sentence, toolName) {
5185
5244
  const lower = sentence.toLowerCase();
@@ -5211,12 +5270,12 @@ function predicateFor(type) {
5211
5270
  }
5212
5271
  }
5213
5272
  function extractMemoryCards(row) {
5214
- const sentences = splitSentences(row.raw_text);
5273
+ const segments = splitSegments(row.raw_text);
5215
5274
  const cards = [];
5216
- for (const sentence of sentences) {
5275
+ for (const sentence of segments) {
5217
5276
  const type = inferCardType(sentence, row.tool_name);
5218
5277
  const subject = extractSubject(sentence, row.agent_id);
5219
- const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
5278
+ const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
5220
5279
  cards.push({
5221
5280
  id: stableId(row.id, type, content),
5222
5281
  memory_id: row.id,
@@ -5312,13 +5371,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
5312
5371
  last_accessed: String(row.timestamp)
5313
5372
  }));
5314
5373
  }
5315
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
5374
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
5316
5375
  var init_memory_cards = __esm({
5317
5376
  "src/lib/memory-cards.ts"() {
5318
5377
  "use strict";
5319
5378
  init_database();
5320
- MAX_CARDS_PER_MEMORY = 6;
5321
- MAX_SENTENCE_CHARS = 360;
5379
+ MAX_CARDS_PER_MEMORY = 8;
5380
+ MAX_SEGMENT_CHARS = 500;
5381
+ MIN_SEGMENT_CHARS = 20;
5322
5382
  }
5323
5383
  });
5324
5384