@askexenow/exe-os 0.9.86 → 0.9.88

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 (103) hide show
  1. package/deploy/compose/docker-compose.yml +3 -3
  2. package/dist/bin/age-ontology-load.js +8 -2
  3. package/dist/bin/agentic-ontology-backfill.js +29 -0
  4. package/dist/bin/agentic-reflection-backfill.js +29 -0
  5. package/dist/bin/agentic-semantic-label.js +29 -0
  6. package/dist/bin/backfill-conversations.js +30 -0
  7. package/dist/bin/backfill-responses.js +30 -0
  8. package/dist/bin/backfill-vectors.js +30 -0
  9. package/dist/bin/bulk-sync-postgres.js +47 -1
  10. package/dist/bin/cc-doctor.js +3 -2
  11. package/dist/bin/cleanup-stale-review-tasks.js +30 -0
  12. package/dist/bin/cli.js +357 -19
  13. package/dist/bin/exe-agent.js +19 -0
  14. package/dist/bin/exe-assign.js +30 -0
  15. package/dist/bin/exe-boot.js +157 -4
  16. package/dist/bin/exe-call.js +20 -0
  17. package/dist/bin/exe-cloud.js +156 -3
  18. package/dist/bin/exe-dispatch.js +30 -1
  19. package/dist/bin/exe-doctor.js +30 -0
  20. package/dist/bin/exe-export-behaviors.js +29 -0
  21. package/dist/bin/exe-forget.js +30 -0
  22. package/dist/bin/exe-gateway.js +150 -35
  23. package/dist/bin/exe-healthcheck.js +2 -1
  24. package/dist/bin/exe-heartbeat.js +30 -0
  25. package/dist/bin/exe-kill.js +29 -0
  26. package/dist/bin/exe-launch-agent.js +29 -0
  27. package/dist/bin/exe-new-employee.js +37 -4
  28. package/dist/bin/exe-pending-messages.js +29 -0
  29. package/dist/bin/exe-pending-notifications.js +30 -0
  30. package/dist/bin/exe-pending-reviews.js +30 -0
  31. package/dist/bin/exe-rename.js +30 -0
  32. package/dist/bin/exe-review.js +30 -0
  33. package/dist/bin/exe-search.js +30 -0
  34. package/dist/bin/exe-session-cleanup.js +30 -1
  35. package/dist/bin/exe-settings.js +3 -0
  36. package/dist/bin/exe-start-codex.js +31 -2
  37. package/dist/bin/exe-start-opencode.js +31 -2
  38. package/dist/bin/exe-status.js +30 -0
  39. package/dist/bin/exe-team.js +30 -0
  40. package/dist/bin/git-sweep.js +30 -1
  41. package/dist/bin/graph-backfill.js +29 -0
  42. package/dist/bin/graph-export.js +29 -0
  43. package/dist/bin/graph-layer-benchmark.js +9 -1
  44. package/dist/bin/install.js +9 -0
  45. package/dist/bin/intercom-check.js +31 -1
  46. package/dist/bin/list-providers.js +1 -0
  47. package/dist/bin/postgres-agentic-reflection-backfill.js +7 -1
  48. package/dist/bin/postgres-agentic-semantic-backfill.js +7 -1
  49. package/dist/bin/registry-proxy.js +1 -0
  50. package/dist/bin/scan-tasks.js +31 -1
  51. package/dist/bin/setup.js +165 -9
  52. package/dist/bin/shard-migrate.js +29 -0
  53. package/dist/bin/stack-update.js +24 -7
  54. package/dist/bin/update.js +5 -0
  55. package/dist/gateway/index.js +30 -1
  56. package/dist/hooks/bug-report-worker.js +30 -1
  57. package/dist/hooks/codex-stop-task-finalizer.js +30 -1
  58. package/dist/hooks/commit-complete.js +30 -1
  59. package/dist/hooks/error-recall.js +29 -0
  60. package/dist/hooks/ingest.js +29 -0
  61. package/dist/hooks/instructions-loaded.js +29 -0
  62. package/dist/hooks/notification.js +29 -0
  63. package/dist/hooks/post-compact.js +29 -0
  64. package/dist/hooks/post-tool-combined.js +29 -0
  65. package/dist/hooks/pre-compact.js +30 -1
  66. package/dist/hooks/pre-tool-use.js +29 -0
  67. package/dist/hooks/prompt-submit.js +30 -1
  68. package/dist/hooks/session-end.js +30 -1
  69. package/dist/hooks/session-start.js +29 -0
  70. package/dist/hooks/stop.js +29 -0
  71. package/dist/hooks/subagent-stop.js +29 -0
  72. package/dist/hooks/summary-worker.js +155 -3
  73. package/dist/index.js +30 -1
  74. package/dist/lib/cloud-sync.js +136 -2
  75. package/dist/lib/consolidation.js +1 -0
  76. package/dist/lib/database.js +11 -0
  77. package/dist/lib/db.js +11 -0
  78. package/dist/lib/device-registry.js +11 -0
  79. package/dist/lib/employee-templates.js +19 -0
  80. package/dist/lib/exe-daemon.js +1455 -208
  81. package/dist/lib/hybrid-search.js +29 -0
  82. package/dist/lib/identity-templates.js +6 -2
  83. package/dist/lib/identity.js +1 -0
  84. package/dist/lib/messaging.js +2 -1
  85. package/dist/lib/reminders.js +1 -0
  86. package/dist/lib/schedules.js +29 -0
  87. package/dist/lib/skill-learning.js +1 -0
  88. package/dist/lib/store.js +29 -0
  89. package/dist/lib/tasks.js +2 -1
  90. package/dist/lib/tmux-routing.js +2 -1
  91. package/dist/lib/token-spend.js +1 -0
  92. package/dist/mcp/server.js +1278 -165
  93. package/dist/mcp/tools/complete-reminder.js +1 -0
  94. package/dist/mcp/tools/create-reminder.js +1 -0
  95. package/dist/mcp/tools/create-task.js +8 -3
  96. package/dist/mcp/tools/deactivate-behavior.js +1 -0
  97. package/dist/mcp/tools/list-reminders.js +1 -0
  98. package/dist/mcp/tools/list-tasks.js +1 -0
  99. package/dist/mcp/tools/send-message.js +2 -1
  100. package/dist/mcp/tools/update-task.js +2 -1
  101. package/dist/runtime/index.js +30 -1
  102. package/dist/tui/App.js +30 -1
  103. package/package.json +2 -2
package/dist/bin/cli.js CHANGED
@@ -1229,6 +1229,15 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
1229
1229
  delete claudeJson.mcpServers[MCP_LEGACY_KEY];
1230
1230
  process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
1231
1231
  }
1232
+ if (claudeJson.projects) {
1233
+ for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
1234
+ if (projectConfig.mcpServers?.[MCP_LEGACY_KEY]) {
1235
+ delete projectConfig.mcpServers[MCP_LEGACY_KEY];
1236
+ process.stderr.write(`exe-os: removed stale project-level exe-mem from ${projectPath}
1237
+ `);
1238
+ }
1239
+ }
1240
+ }
1232
1241
  const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
1233
1242
  const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
1234
1243
  if (osMatches) {
@@ -4340,6 +4349,7 @@ __export(database_exports, {
4340
4349
  isInitialized: () => isInitialized,
4341
4350
  setExternalClient: () => setExternalClient
4342
4351
  });
4352
+ import { chmodSync as chmodSync3 } from "fs";
4343
4353
  import { createClient } from "@libsql/client";
4344
4354
  async function initDatabase(config) {
4345
4355
  if (_walCheckpointTimer) {
@@ -4381,6 +4391,16 @@ async function initDatabase(config) {
4381
4391
  if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
4382
4392
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
4383
4393
  }
4394
+ try {
4395
+ chmodSync3(config.dbPath, 384);
4396
+ for (const suffix of ["-wal", "-shm"]) {
4397
+ try {
4398
+ chmodSync3(config.dbPath + suffix, 384);
4399
+ } catch {
4400
+ }
4401
+ }
4402
+ } catch {
4403
+ }
4384
4404
  }
4385
4405
  function isInitialized() {
4386
4406
  return _adapterClient !== null || _client !== null;
@@ -5743,6 +5763,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
5743
5763
  function isMainModule(importMetaUrl) {
5744
5764
  if (process.argv[1] == null) return false;
5745
5765
  if (process.argv[1].includes("mcp/server")) return false;
5766
+ if (process.argv[1].includes("exe-daemon")) return false;
5746
5767
  try {
5747
5768
  const scriptPath = realpathSync(process.argv[1]);
5748
5769
  const modulePath = realpathSync(fileURLToPath3(importMetaUrl));
@@ -6556,6 +6577,21 @@ var init_crdt_sync = __esm({
6556
6577
  }
6557
6578
  });
6558
6579
 
6580
+ // src/lib/pg-ssl.ts
6581
+ var pg_ssl_exports = {};
6582
+ __export(pg_ssl_exports, {
6583
+ pgSslConfig: () => pgSslConfig
6584
+ });
6585
+ function pgSslConfig() {
6586
+ if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
6587
+ return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
6588
+ }
6589
+ var init_pg_ssl = __esm({
6590
+ "src/lib/pg-ssl.ts"() {
6591
+ "use strict";
6592
+ }
6593
+ });
6594
+
6559
6595
  // src/lib/db-backup.ts
6560
6596
  var db_backup_exports = {};
6561
6597
  __export(db_backup_exports, {
@@ -6669,6 +6705,7 @@ __export(cloud_sync_exports, {
6669
6705
  cloudPull: () => cloudPull,
6670
6706
  cloudPullBehaviors: () => cloudPullBehaviors,
6671
6707
  cloudPullBlob: () => cloudPullBlob,
6708
+ cloudPullCodeContext: () => cloudPullCodeContext,
6672
6709
  cloudPullConversations: () => cloudPullConversations,
6673
6710
  cloudPullDocuments: () => cloudPullDocuments,
6674
6711
  cloudPullGlobalProcedures: () => cloudPullGlobalProcedures,
@@ -6678,6 +6715,7 @@ __export(cloud_sync_exports, {
6678
6715
  cloudPush: () => cloudPush,
6679
6716
  cloudPushBehaviors: () => cloudPushBehaviors,
6680
6717
  cloudPushBlob: () => cloudPushBlob,
6718
+ cloudPushCodeContext: () => cloudPushCodeContext,
6681
6719
  cloudPushConversations: () => cloudPushConversations,
6682
6720
  cloudPushDocuments: () => cloudPushDocuments,
6683
6721
  cloudPushGlobalProcedures: () => cloudPushGlobalProcedures,
@@ -6756,7 +6794,8 @@ function loadPgClient() {
6756
6794
  return new Ctor();
6757
6795
  }
6758
6796
  const { Pool } = await import("pg");
6759
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
6797
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
6798
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig2() });
6760
6799
  return {
6761
6800
  async $queryRawUnsafe(query, ...values) {
6762
6801
  const result = await pool.query(query, values);
@@ -7301,6 +7340,17 @@ async function cloudSync(config) {
7301
7340
  } catch (err) {
7302
7341
  logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
7303
7342
  }
7343
+ let codeContextResult = { pushed: 0, pulled: 0 };
7344
+ try {
7345
+ codeContextResult.pushed = await cloudPushCodeContext(config);
7346
+ } catch (err) {
7347
+ logError(`[cloud-sync] Code context push: ${err instanceof Error ? err.message : String(err)}`);
7348
+ }
7349
+ try {
7350
+ codeContextResult.pulled = await cloudPullCodeContext(config);
7351
+ } catch (err) {
7352
+ logError(`[cloud-sync] Code context pull: ${err instanceof Error ? err.message : String(err)}`);
7353
+ }
7304
7354
  return {
7305
7355
  pushed,
7306
7356
  pulled,
@@ -7310,7 +7360,8 @@ async function cloudSync(config) {
7310
7360
  tasks: tasksResult,
7311
7361
  conversations: conversationsResult,
7312
7362
  documents: documentsResult,
7313
- roster: rosterResult
7363
+ roster: rosterResult,
7364
+ codeContext: codeContextResult
7314
7365
  };
7315
7366
  }
7316
7367
  function recordRosterDeletion(name) {
@@ -7948,7 +7999,99 @@ async function cloudPullDocuments(config) {
7948
7999
  }
7949
8000
  return { pulled };
7950
8001
  }
7951
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
8002
+ async function cloudPushCodeContext(config) {
8003
+ assertSecureEndpoint(config.endpoint);
8004
+ if (!existsSync16(CODE_CONTEXT_DIR)) return 0;
8005
+ const files = readdirSync2(CODE_CONTEXT_DIR).filter(
8006
+ (f) => f.endsWith(".json") && !f.endsWith(".vectors.json") && !f.startsWith(".")
8007
+ );
8008
+ if (files.length === 0) return 0;
8009
+ const metaPath = path16.join(CODE_CONTEXT_DIR, ".sync-meta.json");
8010
+ let syncMeta = {};
8011
+ if (existsSync16(metaPath)) {
8012
+ try {
8013
+ syncMeta = JSON.parse(readFileSync12(metaPath, "utf-8"));
8014
+ } catch {
8015
+ }
8016
+ }
8017
+ let pushed = 0;
8018
+ for (const file of files) {
8019
+ const filePath = path16.join(CODE_CONTEXT_DIR, file);
8020
+ try {
8021
+ const stat2 = statSync4(filePath);
8022
+ const lastPushed = syncMeta[file] ?? 0;
8023
+ if (stat2.mtimeMs <= lastPushed) continue;
8024
+ const content = readFileSync12(filePath, "utf-8");
8025
+ const header = content.substring(0, 300);
8026
+ if (header.includes("/tmp") || header.includes("/var/folders") || header.includes(".worktrees/")) continue;
8027
+ const compressed = compress(Buffer.from(content, "utf8"));
8028
+ const encrypted = encryptSyncBlob(compressed);
8029
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push-code-context`, {
8030
+ method: "POST",
8031
+ headers: {
8032
+ Authorization: `Bearer ${config.apiKey}`,
8033
+ "Content-Type": "application/json",
8034
+ "X-Device-Id": loadDeviceId()
8035
+ },
8036
+ body: JSON.stringify({ key: file, blob: encrypted })
8037
+ });
8038
+ if (resp.ok) {
8039
+ syncMeta[file] = stat2.mtimeMs;
8040
+ pushed++;
8041
+ }
8042
+ } catch {
8043
+ }
8044
+ }
8045
+ if (pushed > 0) {
8046
+ try {
8047
+ writeFileSync10(metaPath, JSON.stringify(syncMeta));
8048
+ } catch {
8049
+ }
8050
+ }
8051
+ return pushed;
8052
+ }
8053
+ async function cloudPullCodeContext(config) {
8054
+ assertSecureEndpoint(config.endpoint);
8055
+ try {
8056
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-code-context`, {
8057
+ method: "GET",
8058
+ headers: {
8059
+ Authorization: `Bearer ${config.apiKey}`,
8060
+ "X-Device-Id": loadDeviceId()
8061
+ }
8062
+ });
8063
+ if (!resp.ok) return 0;
8064
+ const data = await resp.json();
8065
+ if (!data.indexes || data.indexes.length === 0) return 0;
8066
+ mkdirSync9(CODE_CONTEXT_DIR, { recursive: true });
8067
+ let pulled = 0;
8068
+ for (const { key, blob } of data.indexes) {
8069
+ try {
8070
+ if (key.endsWith(".vectors.json")) continue;
8071
+ const localPath = path16.join(CODE_CONTEXT_DIR, key);
8072
+ const compressed = decryptSyncBlob(blob);
8073
+ const content = decompress(compressed).toString("utf8");
8074
+ if (!existsSync16(localPath)) {
8075
+ writeFileSync10(localPath, content, "utf-8");
8076
+ pulled++;
8077
+ } else {
8078
+ const localContent = readFileSync12(localPath, "utf-8");
8079
+ if (localContent.length !== content.length) {
8080
+ writeFileSync10(localPath, content, "utf-8");
8081
+ pulled++;
8082
+ }
8083
+ }
8084
+ } catch {
8085
+ }
8086
+ }
8087
+ return pulled;
8088
+ } catch (err) {
8089
+ process.stderr.write(`[cloud-sync] Code context pull failed: ${err instanceof Error ? err.message : String(err)}
8090
+ `);
8091
+ return 0;
8092
+ }
8093
+ }
8094
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH, CODE_CONTEXT_DIR;
7952
8095
  var init_cloud_sync = __esm({
7953
8096
  "src/lib/cloud-sync.ts"() {
7954
8097
  "use strict";
@@ -7969,6 +8112,7 @@ var init_cloud_sync = __esm({
7969
8112
  _pgFailed = false;
7970
8113
  CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
7971
8114
  ROSTER_DELETIONS_PATH = path16.join(EXE_AI_DIR, "roster-deletions.json");
8115
+ CODE_CONTEXT_DIR = path16.join(EXE_AI_DIR, "code-context");
7972
8116
  }
7973
8117
  });
7974
8118
 
@@ -8719,6 +8863,24 @@ var init_platform_procedures = __esm({
8719
8863
  priority: "p0",
8720
8864
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
8721
8865
  },
8866
+ {
8867
+ title: "Bug report status check \u2014 surface available fixes on boot",
8868
+ domain: "support",
8869
+ priority: "p1",
8870
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
8871
+ },
8872
+ {
8873
+ title: "Feature request triage \u2014 upstream feature vs local customization",
8874
+ domain: "support",
8875
+ priority: "p0",
8876
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
8877
+ },
8878
+ {
8879
+ title: "Feature request status check \u2014 surface shipped features on boot",
8880
+ domain: "support",
8881
+ priority: "p1",
8882
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
8883
+ },
8722
8884
  // --- Operations ---
8723
8885
  {
8724
8886
  title: "Managers must supervise deployed workers",
@@ -13300,7 +13462,7 @@ function readQueue() {
13300
13462
  function writeQueue(queue) {
13301
13463
  ensureDir2();
13302
13464
  const tmp = `${QUEUE_PATH}.tmp`;
13303
- writeFileSync18(tmp, JSON.stringify(queue, null, 2));
13465
+ writeFileSync18(tmp, JSON.stringify(queue, null, 2), { mode: 384 });
13304
13466
  renameSync5(tmp, QUEUE_PATH);
13305
13467
  }
13306
13468
  function queueIntercom(targetSession, reason) {
@@ -18017,12 +18179,14 @@ On EVERY new conversation, before doing anything else:
18017
18179
  1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
18018
18180
  2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
18019
18181
  3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
18020
- 4. **Present the brief**: Give the founder a concise status report:
18182
+ 4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
18183
+ 5. **Present the brief**: Give the founder a concise status report:
18021
18184
  - What's active and progressing
18022
18185
  - What's blocked and needs attention
18023
18186
  - What decisions are pending
18187
+ - Available bug fixes (from step 4, if any)
18024
18188
  - What you recommend doing next
18025
- 5. Then ask: "What's the priority?"
18189
+ 6. Then ask: "What's the priority?"
18026
18190
 
18027
18191
  If this is your FIRST ever conversation (few or no prior memories):
18028
18192
  - Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
@@ -18042,6 +18206,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
18042
18206
  - **get_identity** \u2014 read any agent's identity for coordination
18043
18207
  - **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
18044
18208
  - **send_message** \u2014 direct intercom to employees
18209
+ - **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
18210
+ - **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
18045
18211
  ${PLAN_MODE_COMPAT}
18046
18212
  ## Completion Workflow
18047
18213
 
@@ -18467,7 +18633,7 @@ import {
18467
18633
  readFileSync as readFileSync26,
18468
18634
  writeFileSync as writeFileSync23,
18469
18635
  mkdirSync as mkdirSync22,
18470
- chmodSync as chmodSync3,
18636
+ chmodSync as chmodSync4,
18471
18637
  readdirSync as readdirSync9,
18472
18638
  unlinkSync as unlinkSync15
18473
18639
  } from "fs";
@@ -18488,7 +18654,7 @@ function generateSessionWrappers(packageRoot, homeDir) {
18488
18654
  for (const src of candidates) {
18489
18655
  if (existsSync31(src)) {
18490
18656
  writeFileSync23(exeStartDst, readFileSync26(src));
18491
- chmodSync3(exeStartDst, 493);
18657
+ chmodSync4(exeStartDst, 493);
18492
18658
  break;
18493
18659
  }
18494
18660
  }
@@ -18552,7 +18718,7 @@ exec "${exeStartDst}" "$0" "$@"
18552
18718
  exec node "${codexLauncher}" --agent ${emp.name} "$@"
18553
18719
  `;
18554
18720
  writeFileSync23(wrapperPath, content);
18555
- chmodSync3(wrapperPath, 493);
18721
+ chmodSync4(wrapperPath, 493);
18556
18722
  created++;
18557
18723
  }
18558
18724
  }
@@ -18562,7 +18728,7 @@ exec node "${codexLauncher}" --agent ${emp.name} "$@"
18562
18728
  function writeWrapper(wrapperPath, content) {
18563
18729
  try {
18564
18730
  writeFileSync23(wrapperPath, content);
18565
- chmodSync3(wrapperPath, 493);
18731
+ chmodSync4(wrapperPath, 493);
18566
18732
  } catch {
18567
18733
  }
18568
18734
  }
@@ -19656,6 +19822,10 @@ async function runUpdate(cliArgs) {
19656
19822
  \u{1F4E6} Update available: v${result.localVersion} \u2192 v${result.remoteVersion}
19657
19823
  `);
19658
19824
  let proceed = autoMode;
19825
+ if (!proceed && !process.stdin.isTTY) {
19826
+ console.log("Non-interactive mode detected (no TTY). Use --yes to auto-confirm, or run interactively.");
19827
+ process.exit(1);
19828
+ }
19659
19829
  if (!proceed) {
19660
19830
  const rl = createInterface5({ input: process.stdin, output: process.stdout });
19661
19831
  const answer = await new Promise((resolve) => {
@@ -20107,8 +20277,9 @@ function hydrateEnv(raw, opts) {
20107
20277
  else if (key === "MONITOR_AGENT_KEY") continue;
20108
20278
  else if (key === "EXE_GATEWAY_WS_RELAY_AUTH_TOKEN") replacements[key] = randomHexSecret(24);
20109
20279
  else if (key.endsWith("_PASSWORD")) replacements[key] = randomSecret(24);
20110
- else if (key.endsWith("_SECRET") || key.endsWith("_TOKEN") || key.endsWith("_KEY") || key.endsWith("_SALT")) replacements[key] = value.replace(/CHANGEME[A-Z0-9_]*/g, randomSecret(32));
20111
- else if (key === "EXED_MCP_TOKEN") replacements[key] = randomSecret(32);
20280
+ else if (key.endsWith("_TOKEN")) replacements[key] = randomHexSecret(32);
20281
+ else if (key.endsWith("_SECRET") || key.endsWith("_SALT")) replacements[key] = randomSecret(32);
20282
+ else if (key.endsWith("_KEY") && key !== "EXE_LICENSE_KEY") continue;
20112
20283
  }
20113
20284
  if (domain) next = next.replaceAll("CHANGEME_DOMAIN", domain);
20114
20285
  next = patchEnv(next, replacements);
@@ -20505,8 +20676,8 @@ Options:
20505
20676
  }
20506
20677
  function printChanges(changes, composeFile, envFile) {
20507
20678
  if (changes.length === 0) {
20508
- const running = areCliContainersRunning(composeFile, envFile);
20509
- if (running) {
20679
+ const running2 = areCliContainersRunning(composeFile, envFile);
20680
+ if (running2) {
20510
20681
  console.log("Stack .env matches target manifest. Checking container health...\n");
20511
20682
  const unhealthy = printContainerHealth(composeFile, envFile);
20512
20683
  if (unhealthy > 0) {
@@ -20515,16 +20686,28 @@ function printChanges(changes, composeFile, envFile) {
20515
20686
  } else {
20516
20687
  console.log("\n\u2705 Stack already matches target manifest. All services healthy.");
20517
20688
  }
20689
+ return unhealthy;
20518
20690
  } else {
20519
20691
  console.log("\u26A0\uFE0F Stack .env matches target manifest but containers are not running. Will start them.");
20692
+ return 1;
20520
20693
  }
20521
- return;
20522
20694
  }
20523
20695
  console.log("Planned image tag changes:");
20524
20696
  for (const c of changes) {
20525
20697
  console.log(` - ${c.service}: ${c.key}`);
20526
20698
  console.log(` ${c.before ?? "<unset>"} \u2192 ${c.after}`);
20527
20699
  }
20700
+ const running = areCliContainersRunning(composeFile, envFile);
20701
+ if (running) {
20702
+ console.log("\nCurrent container health:");
20703
+ const unhealthy = printContainerHealth(composeFile, envFile);
20704
+ if (unhealthy > 0) {
20705
+ console.log(`
20706
+ \u{1F534} ${unhealthy} service(s) currently unhealthy or crashlooping.`);
20707
+ }
20708
+ return unhealthy;
20709
+ }
20710
+ return 0;
20528
20711
  }
20529
20712
  function areCliContainersRunning(composeFile, envFile) {
20530
20713
  try {
@@ -20634,9 +20817,12 @@ async function main7(args2 = process.argv.slice(2)) {
20634
20817
  console.log(`Compose: ${opts.composeFile}`);
20635
20818
  console.log(`Env: ${opts.envFile}
20636
20819
  `);
20637
- printChanges(plan.changes, opts.composeFile, opts.envFile);
20820
+ const unhealthyCount = printChanges(plan.changes, opts.composeFile, opts.envFile);
20638
20821
  printBreaking(plan.breakingChanges);
20639
- if (opts.check || opts.dryRun) return;
20822
+ if (opts.check || opts.dryRun) {
20823
+ if (unhealthyCount > 0) process.exitCode = 1;
20824
+ return;
20825
+ }
20640
20826
  if (!opts.yes) {
20641
20827
  console.error("\nRefusing to update without --yes. Re-run with --yes after reviewing the plan.");
20642
20828
  process.exit(2);
@@ -35190,16 +35376,89 @@ var code_context_index_exports = {};
35190
35376
  __export(code_context_index_exports, {
35191
35377
  analyzeBlastRadius: () => analyzeBlastRadius,
35192
35378
  buildCodeContextIndex: () => buildCodeContextIndex,
35379
+ buildCodeContextIndexWithEmbeddings: () => buildCodeContextIndexWithEmbeddings,
35193
35380
  getCodeContextIndexPath: () => getCodeContextIndexPath,
35194
35381
  getCodeContextStats: () => getCodeContextStats,
35195
35382
  loadOrBuildCodeContextIndex: () => loadOrBuildCodeContextIndex,
35196
35383
  searchCodeContext: () => searchCodeContext,
35384
+ searchCodeContextSemantic: () => searchCodeContextSemantic,
35197
35385
  traceCodeSymbol: () => traceCodeSymbol
35198
35386
  });
35199
35387
  import crypto14 from "crypto";
35200
35388
  import path51 from "path";
35201
35389
  import { existsSync as existsSync36, mkdirSync as mkdirSync25, readFileSync as readFileSync32, readdirSync as readdirSync11, statSync as statSync7, writeFileSync as writeFileSync26 } from "fs";
35202
35390
  import { spawnSync as spawnSync3 } from "child_process";
35391
+ function vectorStorePath(projectRoot) {
35392
+ const rootHash = hashText(projectRoot).slice(0, 16);
35393
+ return path51.join(indexDir(), `${rootHash}.vectors.json`);
35394
+ }
35395
+ function loadVectorStore(projectRoot) {
35396
+ const file = vectorStorePath(projectRoot);
35397
+ if (!existsSync36(file)) return null;
35398
+ try {
35399
+ const parsed = JSON.parse(readFileSync32(file, "utf8"));
35400
+ if (parsed.version !== VECTOR_STORE_VERSION) return null;
35401
+ return parsed;
35402
+ } catch {
35403
+ return null;
35404
+ }
35405
+ }
35406
+ function saveVectorStore(projectRoot, store) {
35407
+ writeFileSync26(vectorStorePath(projectRoot), JSON.stringify(store));
35408
+ }
35409
+ function cosineSimilarity(a, b) {
35410
+ let dot = 0, normA = 0, normB = 0;
35411
+ for (let i = 0; i < a.length; i++) {
35412
+ dot += a[i] * b[i];
35413
+ normA += a[i] * a[i];
35414
+ normB += b[i] * b[i];
35415
+ }
35416
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
35417
+ return denom === 0 ? 0 : dot / denom;
35418
+ }
35419
+ async function embedSymbols(index) {
35420
+ const rootHash = hashText(index.projectRoot).slice(0, 16);
35421
+ const existing = loadVectorStore(index.projectRoot);
35422
+ const store = {
35423
+ version: VECTOR_STORE_VERSION,
35424
+ projectRootHash: rootHash,
35425
+ vectors: {}
35426
+ };
35427
+ const allSymbols = [];
35428
+ for (const file of Object.values(index.files)) {
35429
+ for (const symbol of file.symbols) {
35430
+ if (existing?.vectors[symbol.id]) {
35431
+ store.vectors[symbol.id] = existing.vectors[symbol.id];
35432
+ } else {
35433
+ allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
35434
+ }
35435
+ }
35436
+ }
35437
+ if (allSymbols.length === 0) {
35438
+ saveVectorStore(index.projectRoot, store);
35439
+ return store;
35440
+ }
35441
+ const connected = await connectEmbedDaemon().catch(() => false);
35442
+ if (!connected) {
35443
+ saveVectorStore(index.projectRoot, store);
35444
+ return store;
35445
+ }
35446
+ for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
35447
+ const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
35448
+ const texts = batch.map((s) => s.text);
35449
+ try {
35450
+ const vectors = await embedBatchViaClient(texts, "low");
35451
+ if (vectors && vectors.length === batch.length) {
35452
+ for (let j = 0; j < batch.length; j++) {
35453
+ store.vectors[batch[j].id] = vectors[j];
35454
+ }
35455
+ }
35456
+ } catch {
35457
+ }
35458
+ }
35459
+ saveVectorStore(index.projectRoot, store);
35460
+ return store;
35461
+ }
35203
35462
  function normalizeProjectRoot(projectRoot) {
35204
35463
  return path51.resolve(projectRoot || process.cwd());
35205
35464
  }
@@ -35475,7 +35734,7 @@ function filteredFiles(index, options = {}) {
35475
35734
  return matchesPath(file.path, options.paths);
35476
35735
  });
35477
35736
  }
35478
- function searchCodeContext(query, options = {}) {
35737
+ function lexicalSearch(query, options = {}) {
35479
35738
  const terms = tokenize2(query);
35480
35739
  if (terms.length === 0) return [];
35481
35740
  const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
@@ -35501,6 +35760,81 @@ function searchCodeContext(query, options = {}) {
35501
35760
  const limit = options.limit ?? 20;
35502
35761
  return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
35503
35762
  }
35763
+ function searchCodeContext(query, options = {}) {
35764
+ return lexicalSearch(query, options);
35765
+ }
35766
+ async function searchCodeContextSemantic(query, options = {}) {
35767
+ const terms = tokenize2(query);
35768
+ if (terms.length === 0) return [];
35769
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
35770
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
35771
+ const vectorStore = loadVectorStore(projectRoot);
35772
+ if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
35773
+ return lexicalSearch(query, options);
35774
+ }
35775
+ let queryVector = null;
35776
+ try {
35777
+ const connected = await connectEmbedDaemon().catch(() => false);
35778
+ if (connected) {
35779
+ const result = await embedBatchViaClient([query], "high");
35780
+ if (result && result.length === 1) queryVector = result[0];
35781
+ }
35782
+ } catch {
35783
+ }
35784
+ if (!queryVector) return lexicalSearch(query, options);
35785
+ const files = filteredFiles(index, options);
35786
+ const candidates = [];
35787
+ for (const file of files) {
35788
+ for (const symbol of file.symbols) {
35789
+ const lexical = scoreSymbol(symbol, terms);
35790
+ const vec = vectorStore.vectors[symbol.id];
35791
+ const vectorScore = vec ? cosineSimilarity(queryVector, vec) : 0;
35792
+ if (lexical.score > 0 || vectorScore > 0.3) {
35793
+ candidates.push({
35794
+ symbol,
35795
+ lexicalScore: lexical.score,
35796
+ vectorScore,
35797
+ matches: lexical.matches
35798
+ });
35799
+ }
35800
+ }
35801
+ }
35802
+ const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
35803
+ const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
35804
+ const lexicalRank = /* @__PURE__ */ new Map();
35805
+ const vectorRank = /* @__PURE__ */ new Map();
35806
+ byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
35807
+ byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
35808
+ const VECTOR_WEIGHT = 0.6;
35809
+ const LEXICAL_WEIGHT = 0.4;
35810
+ const fused = candidates.map((c) => {
35811
+ const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
35812
+ const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
35813
+ const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K + lRank));
35814
+ return {
35815
+ symbol: c.symbol,
35816
+ score: rrfScore,
35817
+ matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
35818
+ filePath: c.symbol.filePath,
35819
+ language: c.symbol.language,
35820
+ content: c.symbol.text,
35821
+ startLine: c.symbol.startLine,
35822
+ endLine: c.symbol.endLine
35823
+ };
35824
+ });
35825
+ const offset = Math.max(0, options.offset ?? 0);
35826
+ const limit = options.limit ?? 20;
35827
+ return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
35828
+ }
35829
+ async function buildCodeContextIndexWithEmbeddings(options = {}) {
35830
+ const index = buildCodeContextIndex(options);
35831
+ const existingStore = loadVectorStore(index.projectRoot);
35832
+ const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
35833
+ const store = await embedSymbols(index);
35834
+ const vectorCount = Object.keys(store.vectors).length;
35835
+ const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
35836
+ return { index, vectorCount, newEmbeddings };
35837
+ }
35504
35838
  function dependentsMap(index) {
35505
35839
  const map = /* @__PURE__ */ new Map();
35506
35840
  for (const file of Object.values(index.files)) {
@@ -35592,15 +35926,19 @@ function getCodeContextStats(options = {}) {
35592
35926
  indexPath: getCodeContextIndexPath(index.projectRoot)
35593
35927
  };
35594
35928
  }
35595
- var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
35929
+ var VECTOR_STORE_VERSION, EMBED_BATCH_SIZE, INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS, RRF_K;
35596
35930
  var init_code_context_index = __esm({
35597
35931
  "src/lib/code-context-index.ts"() {
35598
35932
  "use strict";
35599
35933
  init_config();
35600
35934
  init_code_chunker();
35935
+ init_exe_daemon_client();
35936
+ VECTOR_STORE_VERSION = 1;
35937
+ EMBED_BATCH_SIZE = 64;
35601
35938
  INDEX_VERSION = 2;
35602
35939
  DEFAULT_MAX_FILES = 5e3;
35603
35940
  IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
35941
+ RRF_K = 60;
35604
35942
  }
35605
35943
  });
35606
35944
 
@@ -1271,6 +1271,7 @@ var OllamaProvider = class {
1271
1271
  import { randomUUID as randomUUID3 } from "crypto";
1272
1272
 
1273
1273
  // src/lib/database.ts
1274
+ import { chmodSync as chmodSync2 } from "fs";
1274
1275
  import { createClient } from "@libsql/client";
1275
1276
 
1276
1277
  // src/lib/employees.ts
@@ -1377,6 +1378,24 @@ var PLATFORM_PROCEDURES = [
1377
1378
  priority: "p0",
1378
1379
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
1379
1380
  },
1381
+ {
1382
+ title: "Bug report status check \u2014 surface available fixes on boot",
1383
+ domain: "support",
1384
+ priority: "p1",
1385
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
1386
+ },
1387
+ {
1388
+ title: "Feature request triage \u2014 upstream feature vs local customization",
1389
+ domain: "support",
1390
+ priority: "p0",
1391
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
1392
+ },
1393
+ {
1394
+ title: "Feature request status check \u2014 surface shipped features on boot",
1395
+ domain: "support",
1396
+ priority: "p1",
1397
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
1398
+ },
1380
1399
  // --- Operations ---
1381
1400
  {
1382
1401
  title: "Managers must supervise deployed workers",