@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
package/dist/tui/App.js CHANGED
@@ -719,6 +719,7 @@ __export(agent_config_exports, {
719
719
  getAgentRuntime: () => getAgentRuntime,
720
720
  loadAgentConfig: () => loadAgentConfig,
721
721
  saveAgentConfig: () => saveAgentConfig,
722
+ setAgentMcps: () => setAgentMcps,
722
723
  setAgentRuntime: () => setAgentRuntime
723
724
  });
724
725
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
@@ -745,7 +746,7 @@ function getAgentRuntime(agentId) {
745
746
  if (orgDefault) return orgDefault;
746
747
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
747
748
  }
748
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
749
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
749
750
  const knownModels = KNOWN_RUNTIMES[runtime];
750
751
  if (!knownModels) {
751
752
  return {
@@ -760,12 +761,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
760
761
  };
761
762
  }
762
763
  const config = loadAgentConfig();
764
+ const existing = config[agentId];
763
765
  const entry = { runtime, model };
764
766
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
767
+ if (mcps !== void 0) {
768
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
769
+ } else if (existing?.mcps) {
770
+ entry.mcps = existing.mcps;
771
+ }
765
772
  config[agentId] = entry;
766
773
  saveAgentConfig(config);
767
774
  return { ok: true };
768
775
  }
776
+ function setAgentMcps(agentId, mcps) {
777
+ const config = loadAgentConfig();
778
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
779
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
780
+ config[agentId] = existing;
781
+ saveAgentConfig(config);
782
+ return { ok: true };
783
+ }
769
784
  function clearAgentRuntime(agentId) {
770
785
  const config = loadAgentConfig();
771
786
  delete config[agentId];
@@ -2847,6 +2862,13 @@ async function ensureSchema() {
2847
2862
  } catch (e) {
2848
2863
  logCatchDebug("migration", e);
2849
2864
  }
2865
+ for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
2866
+ try {
2867
+ await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
2868
+ } catch (e) {
2869
+ logCatchDebug("migration", e);
2870
+ }
2871
+ }
2850
2872
  try {
2851
2873
  await client.execute({
2852
2874
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -4063,6 +4085,22 @@ async function ensureSchema() {
4063
4085
  } catch (e) {
4064
4086
  logCatchDebug("migration", e);
4065
4087
  }
4088
+ try {
4089
+ await client.execute({
4090
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
4091
+ args: []
4092
+ });
4093
+ } catch (e) {
4094
+ logCatchDebug("migration", e);
4095
+ }
4096
+ try {
4097
+ await client.execute({
4098
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
4099
+ args: []
4100
+ });
4101
+ } catch (e) {
4102
+ logCatchDebug("migration", e);
4103
+ }
4066
4104
  }
4067
4105
  async function disposeDatabase() {
4068
4106
  if (_walCheckpointTimer) {
@@ -4481,7 +4519,7 @@ async function assertVpsLicense(opts) {
4481
4519
  }
4482
4520
  if (!transientFailure) {
4483
4521
  throw new Error(
4484
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
4522
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
4485
4523
  );
4486
4524
  }
4487
4525
  const fresh = await getCachedLicense();
@@ -4518,7 +4556,7 @@ async function assertVpsLicense(opts) {
4518
4556
  } catch {
4519
4557
  }
4520
4558
  throw new Error(
4521
- `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.`
4559
+ `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.`
4522
4560
  );
4523
4561
  }
4524
4562
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -4550,7 +4588,7 @@ var init_license = __esm({
4550
4588
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
4551
4589
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
4552
4590
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
4553
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
4591
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
4554
4592
  RETRY_DELAY_MS = 500;
4555
4593
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
4556
4594
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -5071,6 +5109,19 @@ async function resolveTask(client, identifier, scopeSession) {
5071
5109
  args: [identifier, ...scope.args]
5072
5110
  });
5073
5111
  if (result.rows.length === 1) return result.rows[0];
5112
+ if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
5113
+ result = await client.execute({
5114
+ sql: `SELECT * FROM tasks WHERE id LIKE ?`,
5115
+ args: [`${identifier}%`]
5116
+ });
5117
+ if (result.rows.length === 1) return result.rows[0];
5118
+ if (result.rows.length > 1) {
5119
+ const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
5120
+ throw new Error(
5121
+ `Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
5122
+ );
5123
+ }
5124
+ }
5074
5125
  result = await client.execute({
5075
5126
  sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
5076
5127
  args: [`%${identifier}%`, ...scope.args]
@@ -5925,12 +5976,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
5925
5976
  WHERE blocked_by = ? AND status = 'blocked'`,
5926
5977
  args: [now, taskId]
5927
5978
  });
5928
- if (baseDir && unblocked.rowsAffected > 0) {
5929
- const ubScope = sessionScopeFilter();
5930
- const unblockedRows = await client.execute({
5931
- sql: `SELECT task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
5932
- args: [now, ...ubScope.args]
5933
- });
5979
+ if (unblocked.rowsAffected === 0) return;
5980
+ const ubScope = sessionScopeFilter();
5981
+ const unblockedRows = await client.execute({
5982
+ sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
5983
+ args: [now, ...ubScope.args]
5984
+ });
5985
+ if (baseDir) {
5934
5986
  for (const ur of unblockedRows.rows) {
5935
5987
  try {
5936
5988
  const ubFile = path16.join(baseDir, String(ur.task_file));
@@ -5942,6 +5994,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
5942
5994
  }
5943
5995
  }
5944
5996
  }
5997
+ if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
5998
+ try {
5999
+ const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
6000
+ const dispatched = /* @__PURE__ */ new Set();
6001
+ for (const ur of unblockedRows.rows) {
6002
+ const assignee = String(ur.assigned_to);
6003
+ if (dispatched.has(assignee)) continue;
6004
+ dispatched.add(assignee);
6005
+ queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
6006
+ }
6007
+ } catch {
6008
+ }
6009
+ }
5945
6010
  }
5946
6011
  async function findNextTask(assignedTo) {
5947
6012
  const client = getClient();
@@ -6151,6 +6216,15 @@ var init_embedder = __esm({
6151
6216
  // src/lib/behaviors.ts
6152
6217
  import crypto5 from "crypto";
6153
6218
  async function storeBehavior(opts) {
6219
+ try {
6220
+ const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
6221
+ const roster = loadEmployeesSync2();
6222
+ if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
6223
+ throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
6224
+ }
6225
+ } catch (e) {
6226
+ if (e instanceof Error && e.message.includes("not found in roster")) throw e;
6227
+ }
6154
6228
  const client = getClient();
6155
6229
  const id = crypto5.randomUUID();
6156
6230
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -6161,10 +6235,18 @@ async function storeBehavior(opts) {
6161
6235
  vector = new Float32Array(vec);
6162
6236
  } catch {
6163
6237
  }
6238
+ let createdByDevice = null;
6239
+ try {
6240
+ const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
6241
+ createdByDevice = loadDeviceId2() ?? null;
6242
+ } catch {
6243
+ }
6244
+ const createdByAgent = process.env.AGENT_ID ?? null;
6245
+ const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
6164
6246
  await client.execute({
6165
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
6166
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
6167
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
6247
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
6248
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
6249
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
6168
6250
  });
6169
6251
  return id;
6170
6252
  }
@@ -6596,6 +6678,12 @@ async function updateTask(input) {
6596
6678
  }
6597
6679
  }
6598
6680
  }
6681
+ if (input.status === "cancelled") {
6682
+ try {
6683
+ await cascadeUnblock(taskId, input.baseDir, now);
6684
+ } catch {
6685
+ }
6686
+ }
6599
6687
  if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
6600
6688
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
6601
6689
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
@@ -7127,11 +7215,12 @@ function getDispatchedBy(sessionKey) {
7127
7215
  }
7128
7216
  }
7129
7217
  function resolveExeSession() {
7218
+ if (process.env.EXE_SESSION_NAME) {
7219
+ const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
7220
+ if (fromEnv) return fromEnv;
7221
+ }
7130
7222
  const mySession = getMySession();
7131
7223
  if (!mySession) {
7132
- if (process.env.EXE_SESSION_NAME) {
7133
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
7134
- }
7135
7224
  return null;
7136
7225
  }
7137
7226
  const fromSessionName = extractRootExe(mySession);
@@ -7146,6 +7235,10 @@ function resolveExeSession() {
7146
7235
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
7147
7236
  `
7148
7237
  );
7238
+ try {
7239
+ registerParentExe(key, fromSessionName);
7240
+ } catch {
7241
+ }
7149
7242
  candidate = fromSessionName;
7150
7243
  } else {
7151
7244
  candidate = fromCache;
@@ -9164,11 +9257,17 @@ var init_platform_procedures = __esm({
9164
9257
  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."
9165
9258
  },
9166
9259
  {
9167
- title: "Customer orchestration maturity \u2014 recommend, never trap",
9260
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
9168
9261
  domain: "workflow",
9169
9262
  priority: "p1",
9170
9263
  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."
9171
9264
  },
9265
+ {
9266
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
9267
+ domain: "identity",
9268
+ priority: "p0",
9269
+ 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."
9270
+ },
9172
9271
  {
9173
9272
  title: "Single dispatch path \u2014 create_task only",
9174
9273
  domain: "workflow",
@@ -9202,6 +9301,12 @@ var init_platform_procedures = __esm({
9202
9301
  priority: "p0",
9203
9302
  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."
9204
9303
  },
9304
+ {
9305
+ title: "Destructive operations \u2014 mandatory reviewer gate",
9306
+ domain: "security",
9307
+ priority: "p0",
9308
+ 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."
9309
+ },
9205
9310
  {
9206
9311
  title: "Customer patch triage \u2014 upstream bug vs customization",
9207
9312
  domain: "support",
@@ -9353,7 +9458,7 @@ var init_platform_procedures = __esm({
9353
9458
  title: "MCP tool dispatch \u2014 all tools use action parameter",
9354
9459
  domain: "tool-use",
9355
9460
  priority: "p0",
9356
- 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.'
9461
+ 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.'
9357
9462
  },
9358
9463
  {
9359
9464
  title: "MCP tools \u2014 memory, decision, and search",
@@ -10965,6 +11070,8 @@ var tui_data_exports = {};
10965
11070
  __export(tui_data_exports, {
10966
11071
  loadMemoryDashboard: () => loadMemoryDashboard,
10967
11072
  loadTaskList: () => loadTaskList,
11073
+ loadTeamBadgeCounts: () => loadTeamBadgeCounts,
11074
+ loadTeamDetailMetrics: () => loadTeamDetailMetrics,
10968
11075
  loadTeamMetrics: () => loadTeamMetrics,
10969
11076
  searchWikiMemoryRows: () => searchWikiMemoryRows
10970
11077
  });
@@ -10993,16 +11100,20 @@ async function loadMemoryDashboard(limit) {
10993
11100
  }))
10994
11101
  };
10995
11102
  }
10996
- async function loadTeamMetrics(employeeNames) {
11103
+ async function loadTeamBadgeCounts() {
10997
11104
  const client = getClient();
10998
11105
  const memoryCounts = /* @__PURE__ */ new Map();
10999
- const projectsByEmployee = /* @__PURE__ */ new Map();
11000
- const currentTaskByEmployee = /* @__PURE__ */ new Map();
11001
- const scope = sessionScopeFilter();
11002
11106
  const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
11003
11107
  for (const row of memResult.rows) {
11004
11108
  memoryCounts.set(String(row.agent_id), Number(row.cnt));
11005
11109
  }
11110
+ return memoryCounts;
11111
+ }
11112
+ async function loadTeamDetailMetrics(employeeNames) {
11113
+ const client = getClient();
11114
+ const projectsByEmployee = /* @__PURE__ */ new Map();
11115
+ const currentTaskByEmployee = /* @__PURE__ */ new Map();
11116
+ const scope = sessionScopeFilter();
11006
11117
  for (const employeeName of employeeNames) {
11007
11118
  const [projectResult, taskResult] = await Promise.all([
11008
11119
  client.execute({
@@ -11035,6 +11146,13 @@ async function loadTeamMetrics(employeeNames) {
11035
11146
  currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
11036
11147
  }
11037
11148
  }
11149
+ return { projectsByEmployee, currentTaskByEmployee };
11150
+ }
11151
+ async function loadTeamMetrics(employeeNames) {
11152
+ const [memoryCounts, { projectsByEmployee, currentTaskByEmployee }] = await Promise.all([
11153
+ loadTeamBadgeCounts(),
11154
+ loadTeamDetailMetrics(employeeNames)
11155
+ ]);
11038
11156
  return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
11039
11157
  }
11040
11158
  async function loadTaskList() {
@@ -12692,10 +12810,24 @@ function stableId(memoryId, type, content) {
12692
12810
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
12693
12811
  }
12694
12812
  function cleanText(text) {
12695
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
12813
+ let cleaned = text.replace(
12814
+ /```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
12815
+ (_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
12816
+ );
12817
+ cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
12818
+ return cleaned;
12696
12819
  }
12697
- function splitSentences(text) {
12698
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
12820
+ function splitSegments(text) {
12821
+ const cleaned = cleanText(text);
12822
+ 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);
12823
+ if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
12824
+ const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
12825
+ if (lines.length > 0) return lines;
12826
+ if (cleaned.length >= MIN_SEGMENT_CHARS) {
12827
+ return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
12828
+ }
12829
+ }
12830
+ return segments;
12699
12831
  }
12700
12832
  function inferCardType(sentence, toolName) {
12701
12833
  const lower = sentence.toLowerCase();
@@ -12727,12 +12859,12 @@ function predicateFor(type) {
12727
12859
  }
12728
12860
  }
12729
12861
  function extractMemoryCards(row) {
12730
- const sentences = splitSentences(row.raw_text);
12862
+ const segments = splitSegments(row.raw_text);
12731
12863
  const cards = [];
12732
- for (const sentence of sentences) {
12864
+ for (const sentence of segments) {
12733
12865
  const type = inferCardType(sentence, row.tool_name);
12734
12866
  const subject = extractSubject(sentence, row.agent_id);
12735
- const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
12867
+ const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
12736
12868
  cards.push({
12737
12869
  id: stableId(row.id, type, content),
12738
12870
  memory_id: row.id,
@@ -12828,13 +12960,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
12828
12960
  last_accessed: String(row.timestamp)
12829
12961
  }));
12830
12962
  }
12831
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
12963
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
12832
12964
  var init_memory_cards = __esm({
12833
12965
  "src/lib/memory-cards.ts"() {
12834
12966
  "use strict";
12835
12967
  init_database();
12836
- MAX_CARDS_PER_MEMORY = 6;
12837
- MAX_SENTENCE_CHARS = 360;
12968
+ MAX_CARDS_PER_MEMORY = 8;
12969
+ MAX_SEGMENT_CHARS = 500;
12970
+ MIN_SEGMENT_CHARS = 20;
12838
12971
  }
12839
12972
  });
12840
12973
 
@@ -20530,9 +20663,23 @@ var PROCEDURES_MARKER = "EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES";
20530
20663
  function getSessionPrompt(storedPrompt) {
20531
20664
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
20532
20665
  const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
20666
+ let titlePrefix = "";
20667
+ const frontmatterMatch = withoutProcedures.match(/^---\r?\n([\s\S]*?)\r?\n---/);
20668
+ if (frontmatterMatch) {
20669
+ const titleMatch = frontmatterMatch[1].match(/^title:\s*(.+)$/m);
20670
+ const roleMatch = frontmatterMatch[1].match(/^role:\s*(.+)$/m);
20671
+ if (titleMatch) {
20672
+ const title = titleMatch[1].trim();
20673
+ const role = roleMatch ? roleMatch[1].trim() : "";
20674
+ if (title && role && title.toLowerCase() !== role.toLowerCase()) {
20675
+ titlePrefix = `## Your Identity
20676
+ You are **${title}** (specialist). `;
20677
+ }
20678
+ }
20679
+ }
20533
20680
  const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
20534
20681
  const globalBlock = getGlobalProceduresBlock();
20535
- return `${globalBlock}${rolePrompt}
20682
+ return `${globalBlock}${titlePrefix}${rolePrompt}
20536
20683
  ${BASE_OPERATING_PROCEDURES}`;
20537
20684
  }
20538
20685
 
@@ -21263,7 +21410,7 @@ function useOrchestrator(enabled = true) {
21263
21410
  const [pendingReviews, setPendingReviews] = useState8(0);
21264
21411
  const [isLoading, setIsLoading] = useState8(true);
21265
21412
  const orchestratorRef = useRef5(null);
21266
- const exeSessionRef = useRef5("exe1");
21413
+ const exeSessionRef = useRef5("");
21267
21414
  const coordinatorNameRef = useRef5(DEFAULT_COORDINATOR_TEMPLATE_NAME);
21268
21415
  useEffect10(() => {
21269
21416
  if (!enabled) return;
@@ -22725,7 +22872,6 @@ function getAgentStatus(agentId) {
22725
22872
 
22726
22873
  // src/tui/views/Team.tsx
22727
22874
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
22728
- var DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
22729
22875
  function roleBadgeColor(role) {
22730
22876
  switch (role.toLowerCase()) {
22731
22877
  case "coo":
@@ -22746,6 +22892,8 @@ function TeamView({ onBack, onViewSessions }) {
22746
22892
  const [members, setMembers] = useState12([]);
22747
22893
  const [externals, setExternals] = useState12([]);
22748
22894
  const [loading, setLoading] = useState12(!demo);
22895
+ const [badgeInFlight, setBadgeInFlight] = useState12(!demo);
22896
+ const [detailInFlight, setDetailInFlight] = useState12(!demo);
22749
22897
  const [dbError, setDbError] = useState12(null);
22750
22898
  const [selectedIdx, setSelectedIdx] = useState12(0);
22751
22899
  const [showDetail, setShowDetail] = useState12(false);
@@ -22758,6 +22906,8 @@ function TeamView({ onBack, onViewSessions }) {
22758
22906
  setMembers(DEMO_EMPLOYEES.map((e) => ({ ...e })));
22759
22907
  setExternals(DEMO_EXTERNAL_AGENTS);
22760
22908
  setLoading(false);
22909
+ setBadgeInFlight(false);
22910
+ setDetailInFlight(false);
22761
22911
  return;
22762
22912
  }
22763
22913
  loadTeam();
@@ -22796,33 +22946,48 @@ function TeamView({ onBack, onViewSessions }) {
22796
22946
  let projectsByEmployee = /* @__PURE__ */ new Map();
22797
22947
  let currentTaskByEmployee = /* @__PURE__ */ new Map();
22798
22948
  try {
22799
- const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
22800
- const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
22801
- memoryCounts = teamMetrics.memoryCounts;
22802
- projectsByEmployee = new Map(
22803
- Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
22804
- name,
22805
- projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
22806
- ])
22807
- );
22949
+ const { loadTeamBadgeCounts: loadTeamBadgeCounts2, loadTeamDetailMetrics: loadTeamDetailMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
22950
+ setBadgeInFlight(true);
22951
+ const badgeCountsPromise = loadTeamBadgeCounts2();
22952
+ const detailsPromise = loadTeamDetailMetrics2(roster.map((emp) => emp.name));
22953
+ memoryCounts = await badgeCountsPromise;
22954
+ setBadgeInFlight(false);
22955
+ const initialMembers = roster.map((emp) => {
22956
+ const agentSt = getAgentStatus(emp.name);
22957
+ return {
22958
+ name: emp.name,
22959
+ role: emp.role,
22960
+ status: agentSt.label,
22961
+ activity: agentSt.label === "active" ? "Processing..." : "",
22962
+ memoryCount: memoryCounts.get(emp.name) ?? 0,
22963
+ projects: void 0,
22964
+ currentTask: void 0,
22965
+ sessionName: agentSt.session
22966
+ };
22967
+ });
22968
+ setMembers(initialMembers);
22969
+ setDetailInFlight(true);
22970
+ const teamMetrics = await detailsPromise;
22971
+ projectsByEmployee = teamMetrics.projectsByEmployee;
22808
22972
  currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
22973
+ setDetailInFlight(false);
22974
+ const finalMembers = roster.map((emp) => {
22975
+ const agentSt = getAgentStatus(emp.name);
22976
+ return {
22977
+ name: emp.name,
22978
+ role: emp.role,
22979
+ status: agentSt.label,
22980
+ activity: agentSt.label === "active" ? "Processing..." : "",
22981
+ memoryCount: memoryCounts.get(emp.name) ?? 0,
22982
+ projects: projectsByEmployee.get(emp.name),
22983
+ currentTask: currentTaskByEmployee.get(emp.name),
22984
+ sessionName: agentSt.session
22985
+ };
22986
+ });
22987
+ setMembers(finalMembers);
22988
+ setDbError(null);
22809
22989
  } catch {
22810
22990
  }
22811
- const teamData = roster.map((emp) => {
22812
- const agentSt = getAgentStatus(emp.name);
22813
- return {
22814
- name: emp.name,
22815
- role: emp.role,
22816
- status: agentSt.label,
22817
- activity: agentSt.label === "active" ? "Processing..." : "",
22818
- memoryCount: memoryCounts.get(emp.name) ?? 0,
22819
- projects: projectsByEmployee.get(emp.name),
22820
- currentTask: currentTaskByEmployee.get(emp.name),
22821
- sessionName: agentSt.session
22822
- };
22823
- });
22824
- setMembers(teamData);
22825
- setDbError(null);
22826
22991
  try {
22827
22992
  const { existsSync: existsSync20, readFileSync: readFileSync14 } = await import("fs");
22828
22993
  const { join: join2 } = await import("path");
@@ -22844,6 +23009,8 @@ function TeamView({ onBack, onViewSessions }) {
22844
23009
  setDbError(err instanceof Error ? err.message : "Unknown error");
22845
23010
  } finally {
22846
23011
  setLoading(false);
23012
+ setBadgeInFlight(false);
23013
+ setDetailInFlight(false);
22847
23014
  }
22848
23015
  });
22849
23016
  }
@@ -22898,6 +23065,8 @@ function TeamView({ onBack, onViewSessions }) {
22898
23065
  orch.pendingReviews,
22899
23066
  " review(s) pending coordinator attention"
22900
23067
  ] }),
23068
+ badgeInFlight && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: " Loading memory counts..." }),
23069
+ detailInFlight && /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading employee details..." }),
22901
23070
  /* @__PURE__ */ jsx12(Text, { children: " " }),
22902
23071
  /* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
22903
23072
  /* @__PURE__ */ jsx12(Text, { children: " " }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.111",
3
+ "version": "0.9.113",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",