@askexenow/exe-os 0.9.111 → 0.9.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +9 -7
  2. package/dist/bin/agentic-ontology-backfill.js +62 -12
  3. package/dist/bin/agentic-reflection-backfill.js +37 -2
  4. package/dist/bin/agentic-semantic-label.js +37 -2
  5. package/dist/bin/backfill-conversations.js +61 -11
  6. package/dist/bin/backfill-responses.js +62 -12
  7. package/dist/bin/backfill-vectors.js +37 -2
  8. package/dist/bin/bulk-sync-postgres.js +63 -13
  9. package/dist/bin/cleanup-stale-review-tasks.js +83 -16
  10. package/dist/bin/cli.js +312 -80
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +29 -3
  13. package/dist/bin/exe-assign.js +62 -12
  14. package/dist/bin/exe-boot.js +500 -151
  15. package/dist/bin/exe-call.js +46 -5
  16. package/dist/bin/exe-cloud.js +101 -16
  17. package/dist/bin/exe-dispatch.js +827 -27
  18. package/dist/bin/exe-doctor.js +61 -11
  19. package/dist/bin/exe-export-behaviors.js +67 -14
  20. package/dist/bin/exe-forget.js +62 -12
  21. package/dist/bin/exe-gateway.js +147 -27
  22. package/dist/bin/exe-heartbeat.js +83 -16
  23. package/dist/bin/exe-kill.js +62 -12
  24. package/dist/bin/exe-launch-agent.js +83 -15
  25. package/dist/bin/exe-new-employee.js +176 -8
  26. package/dist/bin/exe-pending-messages.js +83 -16
  27. package/dist/bin/exe-pending-notifications.js +83 -16
  28. package/dist/bin/exe-pending-reviews.js +83 -16
  29. package/dist/bin/exe-rename.js +62 -12
  30. package/dist/bin/exe-review.js +62 -12
  31. package/dist/bin/exe-search.js +62 -12
  32. package/dist/bin/exe-session-cleanup.js +949 -149
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +537 -248
  35. package/dist/bin/exe-start-opencode.js +547 -168
  36. package/dist/bin/exe-status.js +83 -16
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +62 -12
  39. package/dist/bin/git-sweep.js +827 -27
  40. package/dist/bin/graph-backfill.js +62 -12
  41. package/dist/bin/graph-export.js +62 -12
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +949 -149
  44. package/dist/bin/pre-publish.js +14 -2
  45. package/dist/bin/scan-tasks.js +827 -27
  46. package/dist/bin/setup.js +99 -14
  47. package/dist/bin/shard-migrate.js +62 -12
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +586 -26
  51. package/dist/hooks/bug-report-worker.js +586 -26
  52. package/dist/hooks/codex-stop-task-finalizer.js +977 -143
  53. package/dist/hooks/commit-complete.js +827 -27
  54. package/dist/hooks/error-recall.js +62 -12
  55. package/dist/hooks/ingest.js +4579 -249
  56. package/dist/hooks/instructions-loaded.js +62 -12
  57. package/dist/hooks/notification.js +62 -12
  58. package/dist/hooks/post-compact.js +83 -16
  59. package/dist/hooks/post-tool-combined.js +83 -16
  60. package/dist/hooks/pre-compact.js +907 -107
  61. package/dist/hooks/pre-tool-use.js +98 -16
  62. package/dist/hooks/prompt-submit.js +596 -30
  63. package/dist/hooks/session-end.js +909 -112
  64. package/dist/hooks/session-start.js +112 -17
  65. package/dist/hooks/stop.js +82 -15
  66. package/dist/hooks/subagent-stop.js +83 -16
  67. package/dist/hooks/summary-worker.js +81 -8
  68. package/dist/index.js +595 -29
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +45 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +23 -0
  73. package/dist/lib/db.js +23 -0
  74. package/dist/lib/device-registry.js +23 -0
  75. package/dist/lib/employee-templates.js +30 -4
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +482 -52
  78. package/dist/lib/hybrid-search.js +62 -12
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +37 -2
  82. package/dist/lib/skill-learning.js +910 -41
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +62 -12
  85. package/dist/lib/tasks.js +843 -93
  86. package/dist/lib/tmux-routing.js +766 -16
  87. package/dist/mcp/server.js +238 -41
  88. package/dist/mcp/tools/create-task.js +525 -15
  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 +840 -93
  93. package/dist/runtime/index.js +913 -107
  94. package/dist/tui/App.js +227 -58
  95. package/package.json +1 -1
@@ -290,11 +290,17 @@ var init_platform_procedures = __esm({
290
290
  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."
291
291
  },
292
292
  {
293
- title: "Customer orchestration maturity \u2014 recommend, never trap",
293
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
294
294
  domain: "workflow",
295
295
  priority: "p1",
296
296
  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."
297
297
  },
298
+ {
299
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
300
+ domain: "identity",
301
+ priority: "p0",
302
+ 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."
303
+ },
298
304
  {
299
305
  title: "Single dispatch path \u2014 create_task only",
300
306
  domain: "workflow",
@@ -328,6 +334,12 @@ var init_platform_procedures = __esm({
328
334
  priority: "p0",
329
335
  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."
330
336
  },
337
+ {
338
+ title: "Destructive operations \u2014 mandatory reviewer gate",
339
+ domain: "security",
340
+ priority: "p0",
341
+ 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."
342
+ },
331
343
  {
332
344
  title: "Customer patch triage \u2014 upstream bug vs customization",
333
345
  domain: "support",
@@ -479,7 +491,7 @@ var init_platform_procedures = __esm({
479
491
  title: "MCP tool dispatch \u2014 all tools use action parameter",
480
492
  domain: "tool-use",
481
493
  priority: "p0",
482
- content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
494
+ content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
483
495
  },
484
496
  {
485
497
  title: "MCP tools \u2014 memory, decision, and search",
@@ -573,9 +585,23 @@ __export(employee_templates_exports, {
573
585
  function getSessionPrompt(storedPrompt) {
574
586
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
575
587
  const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
588
+ let titlePrefix = "";
589
+ const frontmatterMatch = withoutProcedures.match(/^---\r?\n([\s\S]*?)\r?\n---/);
590
+ if (frontmatterMatch) {
591
+ const titleMatch = frontmatterMatch[1].match(/^title:\s*(.+)$/m);
592
+ const roleMatch = frontmatterMatch[1].match(/^role:\s*(.+)$/m);
593
+ if (titleMatch) {
594
+ const title = titleMatch[1].trim();
595
+ const role = roleMatch ? roleMatch[1].trim() : "";
596
+ if (title && role && title.toLowerCase() !== role.toLowerCase()) {
597
+ titlePrefix = `## Your Identity
598
+ You are **${title}** (specialist). `;
599
+ }
600
+ }
601
+ }
576
602
  const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
577
603
  const globalBlock = getGlobalProceduresBlock();
578
- return `${globalBlock}${rolePrompt}
604
+ return `${globalBlock}${titlePrefix}${rolePrompt}
579
605
  ${BASE_OPERATING_PROCEDURES}`;
580
606
  }
581
607
  function buildCustomEmployeePrompt(name, role) {
@@ -594,7 +620,7 @@ function personalizePrompt(prompt, templateName, actualName) {
594
620
  return prompt.replace(new RegExp(`\\bYou are ${escaped}\\b`, "g"), `You are ${actualName}`);
595
621
  }
596
622
  function renderClientCOOTemplate(vars) {
597
- const resolved = { ...vars, title: vars.title || "Chief Operating Officer" };
623
+ const resolved = { ...vars, title: vars.title || "Chief of Staff" };
598
624
  for (const key of CLIENT_COO_PLACEHOLDERS) {
599
625
  const value = resolved[key];
600
626
  if (typeof value !== "string" || value.length === 0) {
@@ -1238,6 +1264,7 @@ __export(agent_config_exports, {
1238
1264
  getAgentRuntime: () => getAgentRuntime,
1239
1265
  loadAgentConfig: () => loadAgentConfig,
1240
1266
  saveAgentConfig: () => saveAgentConfig,
1267
+ setAgentMcps: () => setAgentMcps,
1241
1268
  setAgentRuntime: () => setAgentRuntime
1242
1269
  });
1243
1270
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
@@ -1264,7 +1291,7 @@ function getAgentRuntime(agentId) {
1264
1291
  if (orgDefault) return orgDefault;
1265
1292
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
1266
1293
  }
1267
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
1294
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
1268
1295
  const knownModels = KNOWN_RUNTIMES[runtime];
1269
1296
  if (!knownModels) {
1270
1297
  return {
@@ -1279,12 +1306,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
1279
1306
  };
1280
1307
  }
1281
1308
  const config = loadAgentConfig();
1309
+ const existing = config[agentId];
1282
1310
  const entry = { runtime, model };
1283
1311
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
1312
+ if (mcps !== void 0) {
1313
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
1314
+ } else if (existing?.mcps) {
1315
+ entry.mcps = existing.mcps;
1316
+ }
1284
1317
  config[agentId] = entry;
1285
1318
  saveAgentConfig(config);
1286
1319
  return { ok: true };
1287
1320
  }
1321
+ function setAgentMcps(agentId, mcps) {
1322
+ const config = loadAgentConfig();
1323
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
1324
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
1325
+ config[agentId] = existing;
1326
+ saveAgentConfig(config);
1327
+ return { ok: true };
1328
+ }
1288
1329
  function clearAgentRuntime(agentId) {
1289
1330
  const config = loadAgentConfig();
1290
1331
  delete config[agentId];
@@ -2549,6 +2549,13 @@ async function ensureSchema() {
2549
2549
  } catch (e) {
2550
2550
  logCatchDebug("migration", e);
2551
2551
  }
2552
+ for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
2553
+ try {
2554
+ await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
2555
+ } catch (e) {
2556
+ logCatchDebug("migration", e);
2557
+ }
2558
+ }
2552
2559
  try {
2553
2560
  await client.execute({
2554
2561
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -3765,6 +3772,22 @@ async function ensureSchema() {
3765
3772
  } catch (e) {
3766
3773
  logCatchDebug("migration", e);
3767
3774
  }
3775
+ try {
3776
+ await client.execute({
3777
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
3778
+ args: []
3779
+ });
3780
+ } catch (e) {
3781
+ logCatchDebug("migration", e);
3782
+ }
3783
+ try {
3784
+ await client.execute({
3785
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
3786
+ args: []
3787
+ });
3788
+ } catch (e) {
3789
+ logCatchDebug("migration", e);
3790
+ }
3768
3791
  }
3769
3792
  async function disposeDatabase() {
3770
3793
  if (_walCheckpointTimer) {
@@ -4262,7 +4285,7 @@ async function assertVpsLicense(opts) {
4262
4285
  }
4263
4286
  if (!transientFailure) {
4264
4287
  throw new Error(
4265
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
4288
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
4266
4289
  );
4267
4290
  }
4268
4291
  const fresh = await getCachedLicense();
@@ -4299,7 +4322,7 @@ async function assertVpsLicense(opts) {
4299
4322
  } catch {
4300
4323
  }
4301
4324
  throw new Error(
4302
- `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
4325
+ `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.`
4303
4326
  );
4304
4327
  }
4305
4328
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -4331,7 +4354,7 @@ var init_license = __esm({
4331
4354
  LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
4332
4355
  CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
4333
4356
  DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
4334
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
4357
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
4335
4358
  RETRY_DELAY_MS = 500;
4336
4359
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
4337
4360
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -5134,6 +5157,27 @@ async function cloudSync(config) {
5134
5157
  if (stmts.length > 0) await client.batch(stmts, "write");
5135
5158
  pulled = pullResult.records.length;
5136
5159
  } else {
5160
+ try {
5161
+ const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
5162
+ if (incomingIds.length > 0) {
5163
+ const ph = incomingIds.map(() => "?").join(",");
5164
+ const existing = await client.execute({
5165
+ sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
5166
+ args: incomingIds
5167
+ });
5168
+ const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
5169
+ for (const rec of pullResult.records) {
5170
+ const local = localMap.get(String(rec.id));
5171
+ if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
5172
+ process.stderr.write(
5173
+ `[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
5174
+ `
5175
+ );
5176
+ }
5177
+ }
5178
+ }
5179
+ } catch {
5180
+ }
5137
5181
  const stmts = pullResult.records.map((rec) => ({
5138
5182
  sql: `INSERT OR REPLACE INTO memories
5139
5183
  (id, agent_id, agent_role, session_id, timestamp,
@@ -6891,11 +6935,17 @@ var init_platform_procedures = __esm({
6891
6935
  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."
6892
6936
  },
6893
6937
  {
6894
- title: "Customer orchestration maturity \u2014 recommend, never trap",
6938
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
6895
6939
  domain: "workflow",
6896
6940
  priority: "p1",
6897
6941
  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."
6898
6942
  },
6943
+ {
6944
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
6945
+ domain: "identity",
6946
+ priority: "p0",
6947
+ 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."
6948
+ },
6899
6949
  {
6900
6950
  title: "Single dispatch path \u2014 create_task only",
6901
6951
  domain: "workflow",
@@ -6929,6 +6979,12 @@ var init_platform_procedures = __esm({
6929
6979
  priority: "p0",
6930
6980
  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."
6931
6981
  },
6982
+ {
6983
+ title: "Destructive operations \u2014 mandatory reviewer gate",
6984
+ domain: "security",
6985
+ priority: "p0",
6986
+ 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."
6987
+ },
6932
6988
  {
6933
6989
  title: "Customer patch triage \u2014 upstream bug vs customization",
6934
6990
  domain: "support",
@@ -7080,7 +7136,7 @@ var init_platform_procedures = __esm({
7080
7136
  title: "MCP tool dispatch \u2014 all tools use action parameter",
7081
7137
  domain: "tool-use",
7082
7138
  priority: "p0",
7083
- content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
7139
+ content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
7084
7140
  },
7085
7141
  {
7086
7142
  title: "MCP tools \u2014 memory, decision, and search",
@@ -7214,10 +7270,24 @@ function stableId(memoryId, type, content) {
7214
7270
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
7215
7271
  }
7216
7272
  function cleanText(text) {
7217
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
7273
+ let cleaned = text.replace(
7274
+ /```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
7275
+ (_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
7276
+ );
7277
+ cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
7278
+ return cleaned;
7218
7279
  }
7219
- function splitSentences(text) {
7220
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
7280
+ function splitSegments(text) {
7281
+ const cleaned = cleanText(text);
7282
+ 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);
7283
+ if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
7284
+ const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
7285
+ if (lines.length > 0) return lines;
7286
+ if (cleaned.length >= MIN_SEGMENT_CHARS) {
7287
+ return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
7288
+ }
7289
+ }
7290
+ return segments;
7221
7291
  }
7222
7292
  function inferCardType(sentence, toolName) {
7223
7293
  const lower = sentence.toLowerCase();
@@ -7249,12 +7319,12 @@ function predicateFor(type) {
7249
7319
  }
7250
7320
  }
7251
7321
  function extractMemoryCards(row) {
7252
- const sentences = splitSentences(row.raw_text);
7322
+ const segments = splitSegments(row.raw_text);
7253
7323
  const cards = [];
7254
- for (const sentence of sentences) {
7324
+ for (const sentence of segments) {
7255
7325
  const type = inferCardType(sentence, row.tool_name);
7256
7326
  const subject = extractSubject(sentence, row.agent_id);
7257
- const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
7327
+ const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
7258
7328
  cards.push({
7259
7329
  id: stableId(row.id, type, content),
7260
7330
  memory_id: row.id,
@@ -7350,13 +7420,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
7350
7420
  last_accessed: String(row.timestamp)
7351
7421
  }));
7352
7422
  }
7353
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
7423
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
7354
7424
  var init_memory_cards = __esm({
7355
7425
  "src/lib/memory-cards.ts"() {
7356
7426
  "use strict";
7357
7427
  init_database();
7358
- MAX_CARDS_PER_MEMORY = 6;
7359
- MAX_SENTENCE_CHARS = 360;
7428
+ MAX_CARDS_PER_MEMORY = 8;
7429
+ MAX_SEGMENT_CHARS = 500;
7430
+ MIN_SEGMENT_CHARS = 20;
7360
7431
  }
7361
7432
  });
7362
7433
 
@@ -8479,7 +8550,7 @@ async function setupMode() {
8479
8550
  rl.close();
8480
8551
  return;
8481
8552
  }
8482
- const endpoint = await ask(rl, " Endpoint [https://askexe.com/cloud]: ") || "https://askexe.com/cloud";
8553
+ const endpoint = await ask(rl, " Endpoint [https://cloud.askexe.com]: ") || "https://cloud.askexe.com";
8483
8554
  console.log(" Validating...");
8484
8555
  try {
8485
8556
  assertSecureEndpoint(endpoint);
@@ -8545,6 +8616,14 @@ async function setupMode() {
8545
8616
  }
8546
8617
  console.log("");
8547
8618
  }
8619
+ try {
8620
+ const keyInfo = await getKeyStorageInfo();
8621
+ if (keyInfo.kind !== "missing") {
8622
+ console.log(` \u2713 Encryption key: ${keyInfo.note}`);
8623
+ }
8624
+ } catch {
8625
+ }
8626
+ console.log("");
8548
8627
  console.log(BAR);
8549
8628
  console.log(latestConfig.cloud?.apiKey ? " Setup complete. Cloud sync is connected." : " Setup complete. Cloud sync is not configured yet.");
8550
8629
  console.log(BAR);
@@ -8636,7 +8715,13 @@ async function statusMode() {
8636
8715
  console.log("");
8637
8716
  if (key) {
8638
8717
  const fingerprint = key.toString("hex").slice(0, 8);
8639
- console.log(` Encryption key: \u2713 present (${fingerprint}...)`);
8718
+ let storageNote = "";
8719
+ try {
8720
+ const keyInfo = await getKeyStorageInfo();
8721
+ if (keyInfo.kind !== "missing") storageNote = ` [${keyInfo.note}]`;
8722
+ } catch {
8723
+ }
8724
+ console.log(` Encryption key: \u2713 present (${fingerprint}...)${storageNote}`);
8640
8725
  } else {
8641
8726
  console.log(" Encryption key: \u2717 not found \u2014 run /exe-setup");
8642
8727
  }