@askexenow/exe-os 0.9.65 → 0.9.67

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 (113) hide show
  1. package/deploy/stack-manifests/v0.9.json +54 -5
  2. package/dist/bin/age-ontology-load.js +61 -0
  3. package/dist/bin/agentic-ontology-backfill.js +4708 -0
  4. package/dist/bin/agentic-reflection-backfill.js +4144 -0
  5. package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1532 -2173
  6. package/dist/bin/backfill-conversations.js +528 -20
  7. package/dist/bin/backfill-responses.js +528 -20
  8. package/dist/bin/backfill-vectors.js +255 -20
  9. package/dist/bin/bulk-sync-postgres.js +4876 -0
  10. package/dist/bin/cleanup-stale-review-tasks.js +529 -21
  11. package/dist/bin/cli.js +3471 -1491
  12. package/dist/bin/exe-agent-config.js +4 -0
  13. package/dist/bin/exe-agent.js +16 -0
  14. package/dist/bin/exe-assign.js +528 -20
  15. package/dist/bin/exe-boot.js +492 -54
  16. package/dist/bin/exe-call.js +16 -0
  17. package/dist/bin/exe-cloud.js +7415 -518
  18. package/dist/bin/exe-dispatch.js +540 -22
  19. package/dist/bin/exe-doctor.js +3404 -1225
  20. package/dist/bin/exe-export-behaviors.js +542 -24
  21. package/dist/bin/exe-forget.js +529 -21
  22. package/dist/bin/exe-gateway.js +595 -25
  23. package/dist/bin/exe-heartbeat.js +541 -24
  24. package/dist/bin/exe-kill.js +529 -21
  25. package/dist/bin/exe-launch-agent.js +2334 -1067
  26. package/dist/bin/exe-new-employee.js +324 -166
  27. package/dist/bin/exe-pending-messages.js +529 -21
  28. package/dist/bin/exe-pending-notifications.js +529 -21
  29. package/dist/bin/exe-pending-reviews.js +529 -21
  30. package/dist/bin/exe-rename.js +529 -21
  31. package/dist/bin/exe-review.js +529 -21
  32. package/dist/bin/exe-search.js +542 -24
  33. package/dist/bin/exe-session-cleanup.js +540 -22
  34. package/dist/bin/exe-settings.js +14 -0
  35. package/dist/bin/exe-start-codex.js +817 -144
  36. package/dist/bin/exe-start-opencode.js +776 -80
  37. package/dist/bin/exe-status.js +529 -21
  38. package/dist/bin/exe-team.js +529 -21
  39. package/dist/bin/git-sweep.js +540 -22
  40. package/dist/bin/graph-backfill.js +580 -21
  41. package/dist/bin/graph-export.js +529 -21
  42. package/dist/bin/graph-layer-benchmark.js +109 -0
  43. package/dist/bin/install.js +420 -289
  44. package/dist/bin/intercom-check.js +540 -22
  45. package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
  46. package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
  47. package/dist/bin/scan-tasks.js +540 -22
  48. package/dist/bin/setup.js +790 -206
  49. package/dist/bin/shard-migrate.js +528 -20
  50. package/dist/bin/update.js +4 -0
  51. package/dist/gateway/index.js +593 -23
  52. package/dist/hooks/bug-report-worker.js +651 -64
  53. package/dist/hooks/codex-stop-task-finalizer.js +540 -22
  54. package/dist/hooks/commit-complete.js +540 -22
  55. package/dist/hooks/error-recall.js +542 -24
  56. package/dist/hooks/exe-heartbeat-hook.js +4 -0
  57. package/dist/hooks/ingest-worker.js +4 -0
  58. package/dist/hooks/ingest.js +539 -22
  59. package/dist/hooks/instructions-loaded.js +529 -21
  60. package/dist/hooks/notification.js +529 -21
  61. package/dist/hooks/post-compact.js +529 -21
  62. package/dist/hooks/post-tool-combined.js +543 -25
  63. package/dist/hooks/pre-compact.js +772 -127
  64. package/dist/hooks/pre-tool-use.js +529 -21
  65. package/dist/hooks/prompt-submit.js +543 -25
  66. package/dist/hooks/session-end.js +673 -140
  67. package/dist/hooks/session-start.js +662 -26
  68. package/dist/hooks/stop.js +540 -23
  69. package/dist/hooks/subagent-stop.js +529 -21
  70. package/dist/hooks/summary-worker.js +571 -126
  71. package/dist/index.js +593 -23
  72. package/dist/lib/agent-config.js +4 -0
  73. package/dist/lib/cloud-sync.js +408 -47
  74. package/dist/lib/config.js +25 -1
  75. package/dist/lib/consolidation.js +5 -1
  76. package/dist/lib/database.js +128 -0
  77. package/dist/lib/db-daemon-client.js +4 -0
  78. package/dist/lib/db.js +128 -0
  79. package/dist/lib/device-registry.js +128 -0
  80. package/dist/lib/embedder.js +25 -1
  81. package/dist/lib/employee-templates.js +16 -0
  82. package/dist/lib/employees.js +4 -0
  83. package/dist/lib/exe-daemon-client.js +4 -0
  84. package/dist/lib/exe-daemon.js +3158 -930
  85. package/dist/lib/hybrid-search.js +542 -24
  86. package/dist/lib/identity.js +7 -0
  87. package/dist/lib/keychain.js +178 -22
  88. package/dist/lib/license.js +4 -0
  89. package/dist/lib/messaging.js +7 -0
  90. package/dist/lib/reminders.js +7 -0
  91. package/dist/lib/schedules.js +255 -20
  92. package/dist/lib/skill-learning.js +28 -1
  93. package/dist/lib/status-brief.js +39 -0
  94. package/dist/lib/store.js +528 -20
  95. package/dist/lib/task-router.js +4 -0
  96. package/dist/lib/tasks.js +28 -1
  97. package/dist/lib/tmux-routing.js +28 -1
  98. package/dist/lib/token-spend.js +7 -0
  99. package/dist/mcp/server.js +2739 -813
  100. package/dist/mcp/tools/complete-reminder.js +7 -0
  101. package/dist/mcp/tools/create-reminder.js +7 -0
  102. package/dist/mcp/tools/create-task.js +28 -1
  103. package/dist/mcp/tools/deactivate-behavior.js +7 -0
  104. package/dist/mcp/tools/list-reminders.js +7 -0
  105. package/dist/mcp/tools/list-tasks.js +7 -0
  106. package/dist/mcp/tools/send-message.js +7 -0
  107. package/dist/mcp/tools/update-task.js +28 -1
  108. package/dist/runtime/index.js +540 -22
  109. package/dist/tui/App.js +618 -29
  110. package/package.json +9 -5
  111. package/src/commands/exe/cloud.md +11 -8
  112. package/stack.release.json +3 -3
  113. package/src/commands/exe/link.md +0 -17
package/dist/bin/setup.js CHANGED
@@ -139,6 +139,11 @@ function normalizeAutoUpdate(raw) {
139
139
  const userAU = raw.autoUpdate ?? {};
140
140
  raw.autoUpdate = { ...defaultAU, ...userAU };
141
141
  }
142
+ function normalizeOrchestration(raw) {
143
+ const defaultOrg = DEFAULT_CONFIG.orchestration;
144
+ const userOrg = raw.orchestration ?? {};
145
+ raw.orchestration = { ...defaultOrg, ...userOrg };
146
+ }
142
147
  async function loadConfig() {
143
148
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
144
149
  await ensurePrivateDir(dir);
@@ -163,10 +168,15 @@ async function loadConfig() {
163
168
  normalizeScalingRoadmap(migratedCfg);
164
169
  normalizeSessionLifecycle(migratedCfg);
165
170
  normalizeAutoUpdate(migratedCfg);
171
+ normalizeOrchestration(migratedCfg);
166
172
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
167
173
  if (config.dbPath.startsWith("~")) {
168
174
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
169
175
  }
176
+ const envDbPath = path.join(dir, "memories.db");
177
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
178
+ config.dbPath = envDbPath;
179
+ }
170
180
  return config;
171
181
  } catch {
172
182
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -186,7 +196,16 @@ function loadConfigSync() {
186
196
  normalizeScalingRoadmap(migratedCfg);
187
197
  normalizeSessionLifecycle(migratedCfg);
188
198
  normalizeAutoUpdate(migratedCfg);
189
- return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
199
+ normalizeOrchestration(migratedCfg);
200
+ const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
201
+ if (config.dbPath.startsWith("~")) {
202
+ config.dbPath = config.dbPath.replace(/^~/, os.homedir());
203
+ }
204
+ const envDbPath = path.join(dir, "memories.db");
205
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
206
+ config.dbPath = envDbPath;
207
+ }
208
+ return config;
190
209
  } catch {
191
210
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
192
211
  }
@@ -207,6 +226,7 @@ async function loadConfigFrom(configPath) {
207
226
  normalizeScalingRoadmap(migratedCfg);
208
227
  normalizeSessionLifecycle(migratedCfg);
209
228
  normalizeAutoUpdate(migratedCfg);
229
+ normalizeOrchestration(migratedCfg);
210
230
  return { ...DEFAULT_CONFIG, ...migratedCfg };
211
231
  } catch {
212
232
  return { ...DEFAULT_CONFIG };
@@ -278,6 +298,10 @@ var init_config = __esm({
278
298
  checkOnBoot: true,
279
299
  autoInstall: false,
280
300
  checkIntervalMs: 24 * 60 * 60 * 1e3
301
+ },
302
+ orchestration: {
303
+ phase: "phase_1_coo",
304
+ phaseSetBy: "default"
281
305
  }
282
306
  };
283
307
  CONFIG_MIGRATIONS = [
@@ -298,12 +322,13 @@ var keychain_exports = {};
298
322
  __export(keychain_exports, {
299
323
  deleteMasterKey: () => deleteMasterKey,
300
324
  exportMnemonic: () => exportMnemonic,
325
+ getKeyStorageInfo: () => getKeyStorageInfo,
301
326
  getMasterKey: () => getMasterKey,
302
327
  importMnemonic: () => importMnemonic,
303
328
  setMasterKey: () => setMasterKey
304
329
  });
305
330
  import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
306
- import { existsSync as existsSync3 } from "fs";
331
+ import { existsSync as existsSync3, statSync } from "fs";
307
332
  import { execSync } from "child_process";
308
333
  import path2 from "path";
309
334
  import os2 from "os";
@@ -313,29 +338,65 @@ function getKeyDir() {
313
338
  function getKeyPath() {
314
339
  return path2.join(getKeyDir(), "master.key");
315
340
  }
316
- function macKeychainGet() {
341
+ function nativeKeychainAllowed() {
342
+ return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
343
+ }
344
+ function linuxSecretAvailable() {
345
+ if (!nativeKeychainAllowed()) return false;
346
+ if (process.platform !== "linux") return false;
347
+ if (linuxSecretAvailability !== null) return linuxSecretAvailability;
348
+ try {
349
+ execSync("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
350
+ } catch {
351
+ linuxSecretAvailability = false;
352
+ return false;
353
+ }
354
+ try {
355
+ execSync("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
356
+ linuxSecretAvailability = true;
357
+ } catch {
358
+ linuxSecretAvailability = false;
359
+ }
360
+ return linuxSecretAvailability;
361
+ }
362
+ function isRootOnlyTrustedServerKeyFile(keyPath) {
363
+ if (process.platform !== "linux") return false;
364
+ try {
365
+ const uid = typeof os2.userInfo().uid === "number" ? os2.userInfo().uid : -1;
366
+ const st = statSync(keyPath);
367
+ if (!st.isFile() || (st.mode & 63) !== 0) return false;
368
+ if (uid === 0) return true;
369
+ const exeOsDir = process.env.EXE_OS_DIR;
370
+ return Boolean(exeOsDir && path2.resolve(keyPath).startsWith(path2.resolve(exeOsDir) + path2.sep));
371
+ } catch {
372
+ return false;
373
+ }
374
+ }
375
+ function macKeychainGet(service = SERVICE) {
376
+ if (!nativeKeychainAllowed()) return null;
317
377
  if (process.platform !== "darwin") return null;
318
378
  try {
319
379
  return execSync(
320
- `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
380
+ `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
321
381
  { encoding: "utf-8", timeout: 5e3 }
322
382
  ).trim();
323
383
  } catch {
324
384
  return null;
325
385
  }
326
386
  }
327
- function macKeychainSet(value) {
387
+ function macKeychainSet(value, service = SERVICE) {
388
+ if (!nativeKeychainAllowed()) return false;
328
389
  if (process.platform !== "darwin") return false;
329
390
  try {
330
391
  try {
331
392
  execSync(
332
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
393
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
333
394
  { timeout: 5e3 }
334
395
  );
335
396
  } catch {
336
397
  }
337
398
  execSync(
338
- `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
399
+ `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
339
400
  { timeout: 5e3 }
340
401
  );
341
402
  return true;
@@ -343,11 +404,12 @@ function macKeychainSet(value) {
343
404
  return false;
344
405
  }
345
406
  }
346
- function macKeychainDelete() {
407
+ function macKeychainDelete(service = SERVICE) {
408
+ if (!nativeKeychainAllowed()) return false;
347
409
  if (process.platform !== "darwin") return false;
348
410
  try {
349
411
  execSync(
350
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
412
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
351
413
  { timeout: 5e3 }
352
414
  );
353
415
  return true;
@@ -355,22 +417,22 @@ function macKeychainDelete() {
355
417
  return false;
356
418
  }
357
419
  }
358
- function linuxSecretGet() {
359
- if (process.platform !== "linux") return null;
420
+ function linuxSecretGet(service = SERVICE) {
421
+ if (!linuxSecretAvailable()) return null;
360
422
  try {
361
423
  return execSync(
362
- `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
424
+ `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
363
425
  { encoding: "utf-8", timeout: 5e3 }
364
426
  ).trim();
365
427
  } catch {
366
428
  return null;
367
429
  }
368
430
  }
369
- function linuxSecretSet(value) {
370
- if (process.platform !== "linux") return false;
431
+ function linuxSecretSet(value, service = SERVICE) {
432
+ if (!linuxSecretAvailable()) return false;
371
433
  try {
372
434
  execSync(
373
- `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
435
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
374
436
  { timeout: 5e3 }
375
437
  );
376
438
  return true;
@@ -378,11 +440,12 @@ function linuxSecretSet(value) {
378
440
  return false;
379
441
  }
380
442
  }
381
- function linuxSecretDelete() {
443
+ function linuxSecretDelete(service = SERVICE) {
444
+ if (!nativeKeychainAllowed()) return false;
382
445
  if (process.platform !== "linux") return false;
383
446
  try {
384
447
  execSync(
385
- `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
448
+ `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
386
449
  { timeout: 5e3 }
387
450
  );
388
451
  return true;
@@ -391,6 +454,7 @@ function linuxSecretDelete() {
391
454
  }
392
455
  }
393
456
  async function tryKeytar() {
457
+ if (!nativeKeychainAllowed()) return null;
394
458
  try {
395
459
  return await import("keytar");
396
460
  } catch {
@@ -415,8 +479,8 @@ function deriveMachineKey() {
415
479
  }
416
480
  function readMachineId() {
417
481
  try {
418
- const { readFileSync: readFileSync13 } = __require("fs");
419
- return readFileSync13("/etc/machine-id", "utf-8").trim();
482
+ const { readFileSync: readFileSync14 } = __require("fs");
483
+ return readFileSync14("/etc/machine-id", "utf-8").trim();
420
484
  } catch {
421
485
  return "";
422
486
  }
@@ -464,7 +528,19 @@ async function writeMachineBoundFileFallback(b64) {
464
528
  return "plaintext";
465
529
  }
466
530
  async function getMasterKey() {
467
- const nativeValue = macKeychainGet() ?? linuxSecretGet();
531
+ let nativeValue = macKeychainGet() ?? linuxSecretGet();
532
+ if (!nativeValue) {
533
+ const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
534
+ if (legacyValue) {
535
+ const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
536
+ if (migrated) {
537
+ macKeychainDelete(LEGACY_SERVICE);
538
+ linuxSecretDelete(LEGACY_SERVICE);
539
+ process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
540
+ }
541
+ nativeValue = legacyValue;
542
+ }
543
+ }
468
544
  if (nativeValue) {
469
545
  return Buffer.from(nativeValue, "base64");
470
546
  }
@@ -472,12 +548,17 @@ async function getMasterKey() {
472
548
  if (keytar) {
473
549
  try {
474
550
  const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
475
- if (keytarValue) {
476
- const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
551
+ const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
552
+ if (legacyKeytarValue) {
553
+ const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
477
554
  if (migrated) {
478
555
  process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
556
+ try {
557
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
558
+ } catch {
559
+ }
479
560
  }
480
- return Buffer.from(keytarValue, "base64");
561
+ return Buffer.from(legacyKeytarValue, "base64");
481
562
  }
482
563
  } catch {
483
564
  }
@@ -502,7 +583,7 @@ async function getMasterKey() {
502
583
  const decrypted = decryptWithMachineKey(content, machineKey);
503
584
  if (!decrypted) {
504
585
  process.stderr.write(
505
- "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
586
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
506
587
  );
507
588
  return null;
508
589
  }
@@ -511,6 +592,9 @@ async function getMasterKey() {
511
592
  b64Value = content;
512
593
  }
513
594
  const key = Buffer.from(b64Value, "base64");
595
+ if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
596
+ return key;
597
+ }
514
598
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
515
599
  if (migrated) {
516
600
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
@@ -538,6 +622,97 @@ async function getMasterKey() {
538
622
  return null;
539
623
  }
540
624
  }
625
+ async function getKeyStorageInfo() {
626
+ if (macKeychainGet()) {
627
+ return {
628
+ kind: "macos-keychain",
629
+ secure: true,
630
+ note: "stored in macOS Keychain via built-in security CLI"
631
+ };
632
+ }
633
+ if (macKeychainGet(LEGACY_SERVICE)) {
634
+ return {
635
+ kind: "macos-keychain",
636
+ secure: true,
637
+ note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
638
+ };
639
+ }
640
+ if (linuxSecretGet()) {
641
+ return {
642
+ kind: "linux-secret-service",
643
+ secure: true,
644
+ note: "stored in Linux Secret Service via secret-tool"
645
+ };
646
+ }
647
+ if (linuxSecretGet(LEGACY_SERVICE)) {
648
+ return {
649
+ kind: "linux-secret-service",
650
+ secure: true,
651
+ note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
652
+ };
653
+ }
654
+ const keytar = await tryKeytar();
655
+ if (keytar) {
656
+ try {
657
+ if (await keytar.getPassword(SERVICE, ACCOUNT)) {
658
+ return {
659
+ kind: "legacy-keytar",
660
+ secure: true,
661
+ note: "stored in legacy keytar backend; will migrate to native keychain when possible"
662
+ };
663
+ }
664
+ if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
665
+ return {
666
+ kind: "legacy-keytar",
667
+ secure: true,
668
+ note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
669
+ };
670
+ }
671
+ } catch {
672
+ }
673
+ }
674
+ const keyPath = getKeyPath();
675
+ if (!existsSync3(keyPath)) {
676
+ return {
677
+ kind: "missing",
678
+ secure: false,
679
+ path: keyPath,
680
+ note: "no key found in OS keychain, legacy keytar, or file fallback"
681
+ };
682
+ }
683
+ try {
684
+ const content = (await readFile2(keyPath, "utf-8")).trim();
685
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
686
+ return {
687
+ kind: "encrypted-file",
688
+ secure: true,
689
+ path: keyPath,
690
+ note: "stored in machine-bound encrypted file fallback"
691
+ };
692
+ }
693
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
694
+ return {
695
+ kind: "server-secret-file",
696
+ secure: true,
697
+ path: keyPath,
698
+ note: "stored as root-only trusted server secret file"
699
+ };
700
+ }
701
+ return {
702
+ kind: "plaintext-file",
703
+ secure: false,
704
+ path: keyPath,
705
+ note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
706
+ };
707
+ } catch {
708
+ return {
709
+ kind: "missing",
710
+ secure: false,
711
+ path: keyPath,
712
+ note: "key file exists but could not be read"
713
+ };
714
+ }
715
+ }
541
716
  async function setMasterKey(key) {
542
717
  const b64 = key.toString("base64");
543
718
  if (macKeychainSet(b64) || linuxSecretSet(b64)) {
@@ -563,10 +738,13 @@ async function setMasterKey(key) {
563
738
  async function deleteMasterKey() {
564
739
  macKeychainDelete();
565
740
  linuxSecretDelete();
741
+ macKeychainDelete(LEGACY_SERVICE);
742
+ linuxSecretDelete(LEGACY_SERVICE);
566
743
  const keytar = await tryKeytar();
567
744
  if (keytar) {
568
745
  try {
569
746
  await keytar.deletePassword(SERVICE, ACCOUNT);
747
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
570
748
  } catch {
571
749
  }
572
750
  }
@@ -604,12 +782,14 @@ async function importMnemonic(mnemonic) {
604
782
  const entropy = mnemonicToEntropy(trimmed);
605
783
  return Buffer.from(entropy, "hex");
606
784
  }
607
- var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
785
+ var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
608
786
  var init_keychain = __esm({
609
787
  "src/lib/keychain.ts"() {
610
788
  "use strict";
611
- SERVICE = "exe-mem";
789
+ SERVICE = "exe-os";
790
+ LEGACY_SERVICE = "exe-mem";
612
791
  ACCOUNT = "master-key";
792
+ linuxSecretAvailability = null;
613
793
  ENCRYPTED_PREFIX = "enc:";
614
794
  }
615
795
  });
@@ -665,7 +845,7 @@ import net from "net";
665
845
  import os3 from "os";
666
846
  import { spawn } from "child_process";
667
847
  import { randomUUID } from "crypto";
668
- import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
848
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync as statSync2 } from "fs";
669
849
  import path5 from "path";
670
850
  import { fileURLToPath } from "url";
671
851
  function handleData(chunk) {
@@ -815,7 +995,7 @@ function acquireSpawnLock() {
815
995
  return true;
816
996
  } catch {
817
997
  try {
818
- const stat = statSync(SPAWN_LOCK_PATH);
998
+ const stat = statSync2(SPAWN_LOCK_PATH);
819
999
  if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
820
1000
  try {
821
1001
  unlinkSync2(SPAWN_LOCK_PATH);
@@ -976,7 +1156,7 @@ function killAndRespawnDaemon() {
976
1156
  }
977
1157
  function isDaemonTooYoung() {
978
1158
  try {
979
- const stat = statSync(PID_PATH);
1159
+ const stat = statSync2(PID_PATH);
980
1160
  return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
981
1161
  } catch {
982
1162
  return false;
@@ -1129,10 +1309,10 @@ async function disposeEmbedder() {
1129
1309
  async function embedDirect(text) {
1130
1310
  const llamaCpp = await import("node-llama-cpp");
1131
1311
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1132
- const { existsSync: existsSync17 } = await import("fs");
1133
- const path17 = await import("path");
1134
- const modelPath = path17.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
1135
- if (!existsSync17(modelPath)) {
1312
+ const { existsSync: existsSync18 } = await import("fs");
1313
+ const path18 = await import("path");
1314
+ const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
1315
+ if (!existsSync18(modelPath)) {
1136
1316
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
1137
1317
  }
1138
1318
  const llama = await llamaCpp.getLlama();
@@ -1421,8 +1601,8 @@ async function validateLicense(apiKey, deviceId) {
1421
1601
  }
1422
1602
  function getCacheAgeMs() {
1423
1603
  try {
1424
- const { statSync: statSync4 } = __require("fs");
1425
- const s = statSync4(CACHE_PATH);
1604
+ const { statSync: statSync5 } = __require("fs");
1605
+ const s = statSync5(CACHE_PATH);
1426
1606
  return Date.now() - s.mtimeMs;
1427
1607
  } catch {
1428
1608
  return Infinity;
@@ -2972,6 +3152,9 @@ function getClient() {
2972
3152
  if (_daemonClient && _daemonClient._isDaemonActive()) {
2973
3153
  return _daemonClient;
2974
3154
  }
3155
+ if (!_resilientClient) {
3156
+ return _adapterClient;
3157
+ }
2975
3158
  return _resilientClient;
2976
3159
  }
2977
3160
  async function initDaemonClient() {
@@ -4004,6 +4187,127 @@ async function ensureSchema() {
4004
4187
  VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
4005
4188
  END;
4006
4189
  `);
4190
+ await client.executeMultiple(`
4191
+ CREATE TABLE IF NOT EXISTS agent_sessions (
4192
+ id TEXT PRIMARY KEY,
4193
+ agent_id TEXT NOT NULL,
4194
+ project_name TEXT,
4195
+ started_at TEXT NOT NULL,
4196
+ last_event_at TEXT NOT NULL,
4197
+ event_count INTEGER NOT NULL DEFAULT 0,
4198
+ properties TEXT DEFAULT '{}'
4199
+ );
4200
+
4201
+ CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
4202
+ ON agent_sessions(agent_id, started_at);
4203
+
4204
+ CREATE TABLE IF NOT EXISTS agent_goals (
4205
+ id TEXT PRIMARY KEY,
4206
+ statement TEXT NOT NULL,
4207
+ owner_agent_id TEXT,
4208
+ project_name TEXT,
4209
+ status TEXT NOT NULL DEFAULT 'open',
4210
+ priority INTEGER NOT NULL DEFAULT 5,
4211
+ success_criteria TEXT,
4212
+ parent_goal_id TEXT,
4213
+ due_at TEXT,
4214
+ achieved_at TEXT,
4215
+ supersedes_id TEXT,
4216
+ created_at TEXT NOT NULL,
4217
+ updated_at TEXT NOT NULL,
4218
+ source_memory_id TEXT
4219
+ );
4220
+
4221
+ CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
4222
+ ON agent_goals(project_name, status, priority);
4223
+
4224
+ CREATE TABLE IF NOT EXISTS agent_events (
4225
+ id TEXT PRIMARY KEY,
4226
+ event_type TEXT NOT NULL,
4227
+ occurred_at TEXT NOT NULL,
4228
+ sequence_index INTEGER NOT NULL,
4229
+ actor_agent_id TEXT,
4230
+ agent_role TEXT,
4231
+ project_name TEXT,
4232
+ session_id TEXT,
4233
+ task_id TEXT,
4234
+ goal_id TEXT,
4235
+ parent_event_id TEXT,
4236
+ intention TEXT,
4237
+ outcome TEXT,
4238
+ evidence_memory_id TEXT,
4239
+ impact TEXT,
4240
+ payload TEXT DEFAULT '{}',
4241
+ created_at TEXT NOT NULL
4242
+ );
4243
+
4244
+ CREATE INDEX IF NOT EXISTS idx_agent_events_time
4245
+ ON agent_events(occurred_at, sequence_index);
4246
+
4247
+ CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
4248
+ ON agent_events(session_id, sequence_index);
4249
+
4250
+ CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
4251
+ ON agent_events(goal_id, occurred_at);
4252
+
4253
+ CREATE INDEX IF NOT EXISTS idx_agent_events_memory
4254
+ ON agent_events(evidence_memory_id);
4255
+
4256
+ CREATE TABLE IF NOT EXISTS agent_goal_links (
4257
+ id TEXT PRIMARY KEY,
4258
+ goal_id TEXT NOT NULL,
4259
+ link_type TEXT NOT NULL,
4260
+ target_id TEXT NOT NULL,
4261
+ target_type TEXT NOT NULL,
4262
+ created_at TEXT NOT NULL
4263
+ );
4264
+
4265
+ CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
4266
+ ON agent_goal_links(goal_id, target_type);
4267
+
4268
+ CREATE TABLE IF NOT EXISTS agent_semantic_labels (
4269
+ id TEXT PRIMARY KEY,
4270
+ source_memory_id TEXT NOT NULL,
4271
+ event_id TEXT,
4272
+ labeler TEXT NOT NULL,
4273
+ schema_version INTEGER NOT NULL DEFAULT 1,
4274
+ confidence REAL NOT NULL DEFAULT 0,
4275
+ labels TEXT NOT NULL,
4276
+ created_at TEXT NOT NULL,
4277
+ updated_at TEXT NOT NULL
4278
+ );
4279
+
4280
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
4281
+ ON agent_semantic_labels(source_memory_id, labeler);
4282
+
4283
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
4284
+ ON agent_semantic_labels(event_id);
4285
+
4286
+ CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
4287
+ id TEXT PRIMARY KEY,
4288
+ project_name TEXT,
4289
+ session_id TEXT,
4290
+ window_start_at TEXT NOT NULL,
4291
+ window_end_at TEXT NOT NULL,
4292
+ event_count INTEGER NOT NULL DEFAULT 0,
4293
+ goal_count INTEGER NOT NULL DEFAULT 0,
4294
+ success_count INTEGER NOT NULL DEFAULT 0,
4295
+ failure_count INTEGER NOT NULL DEFAULT 0,
4296
+ risk_count INTEGER NOT NULL DEFAULT 0,
4297
+ summary TEXT NOT NULL,
4298
+ learnings TEXT NOT NULL DEFAULT '[]',
4299
+ next_actions TEXT NOT NULL DEFAULT '[]',
4300
+ evidence_event_ids TEXT NOT NULL DEFAULT '[]',
4301
+ confidence REAL NOT NULL DEFAULT 0,
4302
+ created_at TEXT NOT NULL
4303
+ );
4304
+
4305
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
4306
+ ON agent_reflection_checkpoints(project_name, window_end_at);
4307
+
4308
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
4309
+ ON agent_reflection_checkpoints(session_id, window_end_at);
4310
+ `);
4007
4311
  try {
4008
4312
  await client.execute({
4009
4313
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4408,7 +4712,7 @@ __export(db_backup_exports, {
4408
4712
  listBackups: () => listBackups,
4409
4713
  rotateBackups: () => rotateBackups
4410
4714
  });
4411
- import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync2 } from "fs";
4715
+ import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync3 } from "fs";
4412
4716
  import path11 from "path";
4413
4717
  function findActiveDb() {
4414
4718
  for (const name of DB_NAMES) {
@@ -4452,7 +4756,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
4452
4756
  if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
4453
4757
  const filePath = path11.join(BACKUP_DIR, file);
4454
4758
  try {
4455
- const stat = statSync2(filePath);
4759
+ const stat = statSync3(filePath);
4456
4760
  if (stat.mtimeMs < cutoff) {
4457
4761
  unlinkSync5(filePath);
4458
4762
  deleted++;
@@ -4470,7 +4774,7 @@ function listBackups() {
4470
4774
  const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
4471
4775
  return files.map((name) => {
4472
4776
  const p = path11.join(BACKUP_DIR, name);
4473
- const stat = statSync2(p);
4777
+ const stat = statSync3(p);
4474
4778
  return { path: p, name, size: stat.size, date: stat.mtime };
4475
4779
  }).sort((a, b) => b.date.getTime() - a.date.getTime());
4476
4780
  } catch {
@@ -4503,8 +4807,10 @@ var init_db_backup = __esm({
4503
4807
  // src/lib/cloud-sync.ts
4504
4808
  var cloud_sync_exports = {};
4505
4809
  __export(cloud_sync_exports, {
4810
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE: () => CLOUD_REUPLOAD_REQUIRED_MESSAGE,
4506
4811
  assertSecureEndpoint: () => assertSecureEndpoint,
4507
4812
  buildRosterBlob: () => buildRosterBlob,
4813
+ clearCloudReuploadRequired: () => clearCloudReuploadRequired,
4508
4814
  cloudPull: () => cloudPull,
4509
4815
  cloudPullBehaviors: () => cloudPullBehaviors,
4510
4816
  cloudPullBlob: () => cloudPullBlob,
@@ -4523,13 +4829,16 @@ __export(cloud_sync_exports, {
4523
4829
  cloudPushGraphRAG: () => cloudPushGraphRAG,
4524
4830
  cloudPushRoster: () => cloudPushRoster,
4525
4831
  cloudPushTasks: () => cloudPushTasks,
4832
+ cloudResetMemoryBlobs: () => cloudResetMemoryBlobs,
4526
4833
  cloudSync: () => cloudSync,
4834
+ getCloudReuploadRequired: () => getCloudReuploadRequired,
4835
+ markCloudReuploadRequired: () => markCloudReuploadRequired,
4527
4836
  mergeConfig: () => mergeConfig,
4528
4837
  mergeRosterFromRemote: () => mergeRosterFromRemote,
4529
4838
  pushToPostgres: () => pushToPostgres,
4530
4839
  recordRosterDeletion: () => recordRosterDeletion
4531
4840
  });
4532
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as statSync3 } from "fs";
4841
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as statSync4 } from "fs";
4533
4842
  import crypto3 from "crypto";
4534
4843
  import path12 from "path";
4535
4844
  import { homedir as homedir2 } from "os";
@@ -4576,18 +4885,36 @@ function loadPgClient() {
4576
4885
  const { pathToFileURL: pathToFileURL3 } = await import("url");
4577
4886
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4578
4887
  if (explicitPath) {
4579
- const mod2 = await import(pathToFileURL3(explicitPath).href);
4580
- const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4581
- if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4582
- return new Ctor2();
4888
+ const mod = await import(pathToFileURL3(explicitPath).href);
4889
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4890
+ if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
4891
+ return new Ctor();
4583
4892
  }
4584
4893
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path12.join(homedir2(), "exe-db");
4585
- const req = createRequire3(path12.join(exeDbRoot, "package.json"));
4586
- const entry = req.resolve("@prisma/client");
4587
- const mod = await import(pathToFileURL3(entry).href);
4588
- const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4589
- if (!Ctor) throw new Error("No PrismaClient");
4590
- return new Ctor();
4894
+ const packagePath = path12.join(exeDbRoot, "package.json");
4895
+ if (existsSync12(packagePath)) {
4896
+ const req = createRequire3(packagePath);
4897
+ const entry = req.resolve("@prisma/client");
4898
+ const mod = await import(pathToFileURL3(entry).href);
4899
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4900
+ if (!Ctor) throw new Error("No PrismaClient");
4901
+ return new Ctor();
4902
+ }
4903
+ const { Pool } = await import("pg");
4904
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
4905
+ return {
4906
+ async $queryRawUnsafe(query, ...values) {
4907
+ const result = await pool.query(query, values);
4908
+ return result.rows;
4909
+ },
4910
+ async $executeRawUnsafe(query, ...values) {
4911
+ const result = await pool.query(query, values);
4912
+ return result.rowCount ?? 0;
4913
+ },
4914
+ async $disconnect() {
4915
+ await pool.end();
4916
+ }
4917
+ };
4591
4918
  })().catch(() => {
4592
4919
  _pgFailed = true;
4593
4920
  _pgPromise = null;
@@ -4608,7 +4935,7 @@ async function pushToPostgres(records) {
4608
4935
  let inserted = 0;
4609
4936
  for (const rec of records) {
4610
4937
  try {
4611
- await prisma.$executeRawUnsafe(
4938
+ const changed = await prisma.$executeRawUnsafe(
4612
4939
  `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
4613
4940
  VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
4614
4941
  ON CONFLICT (source, source_id, event_type) DO NOTHING`,
@@ -4617,7 +4944,7 @@ async function pushToPostgres(records) {
4617
4944
  JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
4618
4945
  rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
4619
4946
  );
4620
- inserted++;
4947
+ inserted += Number(changed ?? 0);
4621
4948
  } catch {
4622
4949
  }
4623
4950
  }
@@ -4722,6 +5049,23 @@ async function cloudPush(records, maxVersion, config) {
4722
5049
  return false;
4723
5050
  }
4724
5051
  }
5052
+ async function cloudResetMemoryBlobs(config) {
5053
+ assertSecureEndpoint(config.endpoint);
5054
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
5055
+ method: "POST",
5056
+ headers: {
5057
+ Authorization: `Bearer ${config.apiKey}`,
5058
+ "Content-Type": "application/json",
5059
+ "X-Device-Id": loadDeviceId()
5060
+ },
5061
+ body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
5062
+ });
5063
+ if (!resp.ok) {
5064
+ throw new Error(`cloud reset failed: HTTP ${resp.status}`);
5065
+ }
5066
+ const data = await resp.json();
5067
+ return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
5068
+ }
4725
5069
  async function cloudPull(sinceVersion, config) {
4726
5070
  assertSecureEndpoint(config.endpoint);
4727
5071
  try {
@@ -4741,22 +5085,61 @@ async function cloudPull(sinceVersion, config) {
4741
5085
  if (!response.ok) return { records: [], maxVersion: sinceVersion };
4742
5086
  const data = await response.json();
4743
5087
  const allRecords = [];
4744
- for (const { blob } of data.blobs ?? []) {
5088
+ let maxReadableVersion = sinceVersion;
5089
+ let skippedBlobs = 0;
5090
+ for (const { version, blob } of data.blobs ?? []) {
4745
5091
  try {
4746
5092
  const compressed = decryptSyncBlob(blob);
4747
5093
  const json = decompress(compressed).toString("utf8");
4748
5094
  const records = JSON.parse(json);
4749
5095
  allRecords.push(...records);
5096
+ const recordMax = records.reduce((max, rec) => {
5097
+ const v = Number(rec.version ?? 0);
5098
+ return Number.isFinite(v) ? Math.max(max, v) : max;
5099
+ }, 0);
5100
+ const blobVersion = Number(version ?? 0);
5101
+ maxReadableVersion = Math.max(
5102
+ maxReadableVersion,
5103
+ Number.isFinite(blobVersion) ? blobVersion : 0,
5104
+ recordMax
5105
+ );
4750
5106
  } catch {
5107
+ skippedBlobs++;
4751
5108
  continue;
4752
5109
  }
4753
5110
  }
4754
- return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
5111
+ if (skippedBlobs > 0) {
5112
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
5113
+ }
5114
+ return { records: allRecords, maxVersion: maxReadableVersion };
4755
5115
  } catch (err) {
4756
5116
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
4757
5117
  return { records: [], maxVersion: sinceVersion };
4758
5118
  }
4759
5119
  }
5120
+ async function getCloudReuploadRequired(client = getClient()) {
5121
+ try {
5122
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5123
+ const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
5124
+ return result.rows.some((row) => String(row.value ?? "") === "1");
5125
+ } catch {
5126
+ return false;
5127
+ }
5128
+ }
5129
+ async function clearCloudReuploadRequired(client = getClient()) {
5130
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5131
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
5132
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
5133
+ await client.execute({
5134
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
5135
+ args: [(/* @__PURE__ */ new Date()).toISOString()]
5136
+ });
5137
+ await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
5138
+ }
5139
+ async function markCloudReuploadRequired(client = getClient()) {
5140
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5141
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
5142
+ }
4760
5143
  async function cloudSync(config) {
4761
5144
  if (!isSyncCryptoInitialized()) {
4762
5145
  try {
@@ -4778,13 +5161,10 @@ async function cloudSync(config) {
4778
5161
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
4779
5162
  }
4780
5163
  try {
4781
- const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
4782
- if (String(relink.rows[0]?.value ?? "") === "1") {
4783
- throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
4784
- }
5164
+ if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
4785
5165
  } catch (err) {
4786
5166
  const msg = err instanceof Error ? err.message : String(err);
4787
- if (msg.includes("Paused after key rotation")) throw err;
5167
+ if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
4788
5168
  }
4789
5169
  try {
4790
5170
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -5042,7 +5422,7 @@ async function cloudSync(config) {
5042
5422
  const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
5043
5423
  const latestBackup = getLatestBackup2();
5044
5424
  if (latestBackup) {
5045
- const backupSize = statSync3(latestBackup).size;
5425
+ const backupSize = statSync4(latestBackup).size;
5046
5426
  const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
5047
5427
  if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
5048
5428
  const backupData = readFileSync8(latestBackup);
@@ -5713,7 +6093,7 @@ async function cloudPullDocuments(config) {
5713
6093
  }
5714
6094
  return { pulled };
5715
6095
  }
5716
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
6096
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
5717
6097
  var init_cloud_sync = __esm({
5718
6098
  "src/lib/cloud-sync.ts"() {
5719
6099
  "use strict";
@@ -5732,43 +6112,194 @@ var init_cloud_sync = __esm({
5732
6112
  LOCK_STALE_MS = 3e4;
5733
6113
  _pgPromise = null;
5734
6114
  _pgFailed = false;
6115
+ 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.";
5735
6116
  ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
5736
6117
  }
5737
6118
  });
5738
6119
 
6120
+ // src/lib/key-backup-status.ts
6121
+ var key_backup_status_exports = {};
6122
+ __export(key_backup_status_exports, {
6123
+ getKeyBackupStatus: () => getKeyBackupStatus,
6124
+ keyBackupMarkerPath: () => keyBackupMarkerPath,
6125
+ markKeyBackupConfirmed: () => markKeyBackupConfirmed
6126
+ });
6127
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
6128
+ import path13 from "path";
6129
+ function keyBackupMarkerPath() {
6130
+ return path13.join(EXE_AI_DIR, "key-backup-confirmed.json");
6131
+ }
6132
+ function getKeyBackupStatus() {
6133
+ const marker = keyBackupMarkerPath();
6134
+ if (!existsSync13(marker)) return { exists: false };
6135
+ try {
6136
+ const parsed = JSON.parse(readFileSync9(marker, "utf8"));
6137
+ return {
6138
+ exists: true,
6139
+ confirmedAt: parsed.confirmedAt,
6140
+ source: parsed.source
6141
+ };
6142
+ } catch {
6143
+ return { exists: true };
6144
+ }
6145
+ }
6146
+ function markKeyBackupConfirmed(source) {
6147
+ mkdirSync6(EXE_AI_DIR, { recursive: true, mode: 448 });
6148
+ writeFileSync7(
6149
+ keyBackupMarkerPath(),
6150
+ JSON.stringify({ confirmedAt: (/* @__PURE__ */ new Date()).toISOString(), source }, null, 2) + "\n",
6151
+ { mode: 384 }
6152
+ );
6153
+ }
6154
+ var init_key_backup_status = __esm({
6155
+ "src/lib/key-backup-status.ts"() {
6156
+ "use strict";
6157
+ init_config();
6158
+ }
6159
+ });
6160
+
6161
+ // src/lib/orchestration-phase.ts
6162
+ var orchestration_phase_exports = {};
6163
+ __export(orchestration_phase_exports, {
6164
+ ORCHESTRATION_PHASES: () => ORCHESTRATION_PHASES,
6165
+ applyDefaultOrchestrationPhase: () => applyDefaultOrchestrationPhase,
6166
+ getConfigOrchestrationPhase: () => getConfigOrchestrationPhase,
6167
+ getOrchestrationPhaseInfo: () => getOrchestrationPhaseInfo,
6168
+ loadOrchestrationPhase: () => loadOrchestrationPhase,
6169
+ normalizeOrchestrationPhase: () => normalizeOrchestrationPhase,
6170
+ phaseReminderLine: () => phaseReminderLine,
6171
+ setOrchestrationPhase: () => setOrchestrationPhase
6172
+ });
6173
+ function normalizeOrchestrationPhase(input) {
6174
+ if (typeof input !== "string") return "phase_1_coo";
6175
+ const normalized = input.trim().toLowerCase().replace(/\s+/g, "_");
6176
+ if (["1", "phase1", "phase_1", "coo", "coo_mode", "chief_of_staff", "phase_1_coo"].includes(normalized)) {
6177
+ return "phase_1_coo";
6178
+ }
6179
+ if (["2", "phase2", "phase_2", "executives", "executive", "executive_bench", "phase_2_executives"].includes(normalized)) {
6180
+ return "phase_2_executives";
6181
+ }
6182
+ if (["3", "phase3", "phase_3", "parallel", "parallel_org", "parallel_execution", "phase_3_parallel_org"].includes(normalized)) {
6183
+ return "phase_3_parallel_org";
6184
+ }
6185
+ if (ORCHESTRATION_PHASES.includes(normalized)) return normalized;
6186
+ return "phase_1_coo";
6187
+ }
6188
+ function getOrchestrationPhaseInfo(phase) {
6189
+ return PHASE_INFO[normalizeOrchestrationPhase(phase)];
6190
+ }
6191
+ function getConfigOrchestrationPhase(config) {
6192
+ const orchestration = config.orchestration;
6193
+ return normalizeOrchestrationPhase(orchestration?.phase);
6194
+ }
6195
+ function applyDefaultOrchestrationPhase(config) {
6196
+ if (!config.orchestration) {
6197
+ config.orchestration = {
6198
+ phase: "phase_1_coo",
6199
+ phaseSetAt: (/* @__PURE__ */ new Date()).toISOString(),
6200
+ phaseSetBy: "setup-default"
6201
+ };
6202
+ return config;
6203
+ }
6204
+ if (!config.orchestration.phase) {
6205
+ config.orchestration = {
6206
+ ...config.orchestration,
6207
+ phase: "phase_1_coo",
6208
+ phaseSetAt: config.orchestration.phaseSetAt ?? (/* @__PURE__ */ new Date()).toISOString(),
6209
+ phaseSetBy: config.orchestration.phaseSetBy ?? "setup-default"
6210
+ };
6211
+ }
6212
+ return config;
6213
+ }
6214
+ async function loadOrchestrationPhase() {
6215
+ const config = await loadConfig();
6216
+ return getOrchestrationPhaseInfo(config.orchestration?.phase);
6217
+ }
6218
+ async function setOrchestrationPhase(phaseInput, setBy = "user") {
6219
+ const config = await loadConfig();
6220
+ const phase = normalizeOrchestrationPhase(phaseInput);
6221
+ config.orchestration = {
6222
+ ...config.orchestration ?? {},
6223
+ phase,
6224
+ phaseSetAt: (/* @__PURE__ */ new Date()).toISOString(),
6225
+ phaseSetBy: setBy
6226
+ };
6227
+ await saveConfig(config);
6228
+ return getOrchestrationPhaseInfo(phase);
6229
+ }
6230
+ function phaseReminderLine(phase) {
6231
+ const info = getOrchestrationPhaseInfo(phase);
6232
+ return `${info.shortLabel}: ${info.focus}. ${info.nextSuggestion}`;
6233
+ }
6234
+ var ORCHESTRATION_PHASES, PHASE_INFO;
6235
+ var init_orchestration_phase = __esm({
6236
+ "src/lib/orchestration-phase.ts"() {
6237
+ "use strict";
6238
+ init_config();
6239
+ ORCHESTRATION_PHASES = [
6240
+ "phase_1_coo",
6241
+ "phase_2_executives",
6242
+ "phase_3_parallel_org"
6243
+ ];
6244
+ PHASE_INFO = {
6245
+ phase_1_coo: {
6246
+ phase: "phase_1_coo",
6247
+ shortLabel: "Phase 1 \u2014 COO mode",
6248
+ label: "Phase 1 \u2014 COO / Chief of Staff mode",
6249
+ focus: "building company context before delegation",
6250
+ nextSuggestion: "Unlock executives when technical, marketing, ops, legal, or finance work repeats."
6251
+ },
6252
+ phase_2_executives: {
6253
+ phase: "phase_2_executives",
6254
+ shortLabel: "Phase 2 \u2014 Executive bench",
6255
+ label: "Phase 2 \u2014 Executive bench",
6256
+ focus: "COO works with domain executives like CTO/CMO before specialist fan-out",
6257
+ nextSuggestion: "Unlock parallel execution when review gates, permissions, and workflows are ready."
6258
+ },
6259
+ phase_3_parallel_org: {
6260
+ phase: "phase_3_parallel_org",
6261
+ shortLabel: "Phase 3 \u2014 Parallel org",
6262
+ label: "Phase 3 \u2014 Parallel execution org",
6263
+ focus: "executives can delegate to specialists in parallel with review gates",
6264
+ nextSuggestion: "Keep review/CI/permission gates healthy; downgrade anytime if you want simpler COO-only mode."
6265
+ }
6266
+ };
6267
+ }
6268
+ });
6269
+
5739
6270
  // src/lib/preferences.ts
5740
6271
  var preferences_exports = {};
5741
6272
  __export(preferences_exports, {
5742
6273
  loadPreferences: () => loadPreferences,
5743
6274
  savePreferences: () => savePreferences
5744
6275
  });
5745
- import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
5746
- import path13 from "path";
6276
+ import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
6277
+ import path14 from "path";
5747
6278
  import os7 from "os";
5748
6279
  function loadPreferences(homeDir = os7.homedir()) {
5749
- const configPath = path13.join(homeDir, ".exe-os", "config.json");
5750
- if (!existsSync13(configPath)) return {};
6280
+ const configPath = path14.join(homeDir, ".exe-os", "config.json");
6281
+ if (!existsSync14(configPath)) return {};
5751
6282
  try {
5752
- const config = JSON.parse(readFileSync9(configPath, "utf-8"));
6283
+ const config = JSON.parse(readFileSync10(configPath, "utf-8"));
5753
6284
  return config.preferences ?? {};
5754
6285
  } catch {
5755
6286
  return {};
5756
6287
  }
5757
6288
  }
5758
6289
  function savePreferences(prefs, homeDir = os7.homedir()) {
5759
- const configDir = path13.join(homeDir, ".exe-os");
5760
- const configPath = path13.join(configDir, "config.json");
6290
+ const configDir = path14.join(homeDir, ".exe-os");
6291
+ const configPath = path14.join(configDir, "config.json");
5761
6292
  ensurePrivateDirSync(configDir);
5762
6293
  let config = {};
5763
- if (existsSync13(configPath)) {
6294
+ if (existsSync14(configPath)) {
5764
6295
  try {
5765
- config = JSON.parse(readFileSync9(configPath, "utf-8"));
6296
+ config = JSON.parse(readFileSync10(configPath, "utf-8"));
5766
6297
  } catch {
5767
6298
  config = {};
5768
6299
  }
5769
6300
  }
5770
6301
  config.preferences = prefs;
5771
- writeFileSync7(configPath, JSON.stringify(config, null, 2) + "\n");
6302
+ writeFileSync8(configPath, JSON.stringify(config, null, 2) + "\n");
5772
6303
  enforcePrivateFileSync(configPath);
5773
6304
  }
5774
6305
  var init_preferences = __esm({
@@ -5816,6 +6347,12 @@ var init_platform_procedures = __esm({
5816
6347
  priority: "p0",
5817
6348
  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."
5818
6349
  },
6350
+ {
6351
+ title: "Customer orchestration maturity \u2014 recommend, never trap",
6352
+ domain: "workflow",
6353
+ priority: "p1",
6354
+ 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."
6355
+ },
5819
6356
  {
5820
6357
  title: "Single dispatch path \u2014 create_task only",
5821
6358
  domain: "workflow",
@@ -5874,6 +6411,12 @@ var init_platform_procedures = __esm({
5874
6411
  priority: "p0",
5875
6412
  content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
5876
6413
  },
6414
+ {
6415
+ title: "Commit discipline \u2014 never leave verified work floating",
6416
+ domain: "workflow",
6417
+ priority: "p1",
6418
+ content: "After any code-change batch passes typecheck/tests/build, run git status, summarize changed files, and commit with a clear message before ending the session. If work must remain uncommitted for review/dogfood, explicitly say so, list the files, and state the blocker. Never imply work is complete while verified changes are still floating locally."
6419
+ },
5877
6420
  {
5878
6421
  title: "Desktop and TUI are the same product",
5879
6422
  domain: "architecture",
@@ -6641,17 +7184,17 @@ __export(identity_exports, {
6641
7184
  listIdentities: () => listIdentities,
6642
7185
  updateIdentity: () => updateIdentity
6643
7186
  });
6644
- import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
7187
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
6645
7188
  import { readdirSync as readdirSync3 } from "fs";
6646
- import path14 from "path";
7189
+ import path15 from "path";
6647
7190
  import { createHash as createHash2 } from "crypto";
6648
7191
  function ensureDir() {
6649
- if (!existsSync14(IDENTITY_DIR2)) {
6650
- mkdirSync6(IDENTITY_DIR2, { recursive: true });
7192
+ if (!existsSync15(IDENTITY_DIR2)) {
7193
+ mkdirSync7(IDENTITY_DIR2, { recursive: true });
6651
7194
  }
6652
7195
  }
6653
7196
  function identityPath(agentId) {
6654
- return path14.join(IDENTITY_DIR2, `${agentId}.md`);
7197
+ return path15.join(IDENTITY_DIR2, `${agentId}.md`);
6655
7198
  }
6656
7199
  function sanitizeIdentityBody(body) {
6657
7200
  return body.replace(/<!--[\s\S]*?-->/g, "").trim();
@@ -6695,8 +7238,8 @@ function contentHash(content) {
6695
7238
  }
6696
7239
  function getIdentity(agentId) {
6697
7240
  const filePath = identityPath(agentId);
6698
- if (!existsSync14(filePath)) return null;
6699
- const raw = readFileSync10(filePath, "utf-8");
7241
+ if (!existsSync15(filePath)) return null;
7242
+ const raw = readFileSync11(filePath, "utf-8");
6700
7243
  const { frontmatter, body } = parseFrontmatter(raw);
6701
7244
  return {
6702
7245
  agentId,
@@ -6710,7 +7253,7 @@ async function updateIdentity(agentId, content, updatedBy) {
6710
7253
  ensureDir();
6711
7254
  const filePath = identityPath(agentId);
6712
7255
  const hash = contentHash(content);
6713
- writeFileSync8(filePath, content, "utf-8");
7256
+ writeFileSync9(filePath, content, "utf-8");
6714
7257
  try {
6715
7258
  const client = getClient();
6716
7259
  await client.execute({
@@ -6768,7 +7311,7 @@ var init_identity = __esm({
6768
7311
  "use strict";
6769
7312
  init_config();
6770
7313
  init_database();
6771
- IDENTITY_DIR2 = path14.join(EXE_AI_DIR, "identity");
7314
+ IDENTITY_DIR2 = path15.join(EXE_AI_DIR, "identity");
6772
7315
  }
6773
7316
  });
6774
7317
 
@@ -7319,36 +7862,36 @@ __export(session_wrappers_exports, {
7319
7862
  generateSessionWrappers: () => generateSessionWrappers
7320
7863
  });
7321
7864
  import {
7322
- existsSync as existsSync15,
7323
- readFileSync as readFileSync11,
7324
- writeFileSync as writeFileSync9,
7325
- mkdirSync as mkdirSync7,
7865
+ existsSync as existsSync16,
7866
+ readFileSync as readFileSync12,
7867
+ writeFileSync as writeFileSync10,
7868
+ mkdirSync as mkdirSync8,
7326
7869
  chmodSync as chmodSync2,
7327
7870
  readdirSync as readdirSync4,
7328
7871
  unlinkSync as unlinkSync7
7329
7872
  } from "fs";
7330
- import path15 from "path";
7873
+ import path16 from "path";
7331
7874
  import { homedir as homedir3 } from "os";
7332
7875
  function generateSessionWrappers(packageRoot, homeDir) {
7333
7876
  const home = homeDir ?? homedir3();
7334
- const binDir = path15.join(home, ".exe-os", "bin");
7335
- const rosterPath = path15.join(home, ".exe-os", "exe-employees.json");
7336
- mkdirSync7(binDir, { recursive: true });
7337
- const exeStartDst = path15.join(binDir, "exe-start");
7877
+ const binDir = path16.join(home, ".exe-os", "bin");
7878
+ const rosterPath = path16.join(home, ".exe-os", "exe-employees.json");
7879
+ mkdirSync8(binDir, { recursive: true });
7880
+ const exeStartDst = path16.join(binDir, "exe-start");
7338
7881
  const candidates = [
7339
- path15.join(packageRoot, "dist", "bin", "exe-start.sh"),
7340
- path15.join(packageRoot, "src", "bin", "exe-start.sh")
7882
+ path16.join(packageRoot, "dist", "bin", "exe-start.sh"),
7883
+ path16.join(packageRoot, "src", "bin", "exe-start.sh")
7341
7884
  ];
7342
7885
  for (const src of candidates) {
7343
- if (existsSync15(src)) {
7344
- writeFileSync9(exeStartDst, readFileSync11(src));
7886
+ if (existsSync16(src)) {
7887
+ writeFileSync10(exeStartDst, readFileSync12(src));
7345
7888
  chmodSync2(exeStartDst, 493);
7346
7889
  break;
7347
7890
  }
7348
7891
  }
7349
7892
  let employees = [];
7350
7893
  try {
7351
- employees = JSON.parse(readFileSync11(rosterPath, "utf8"));
7894
+ employees = JSON.parse(readFileSync12(rosterPath, "utf8"));
7352
7895
  } catch {
7353
7896
  return { created: 0, pathConfigured: false };
7354
7897
  }
@@ -7358,9 +7901,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
7358
7901
  try {
7359
7902
  for (const f of readdirSync4(binDir)) {
7360
7903
  if (f === "exe-start") continue;
7361
- const fPath = path15.join(binDir, f);
7904
+ const fPath = path16.join(binDir, f);
7362
7905
  try {
7363
- const content = readFileSync11(fPath, "utf8");
7906
+ const content = readFileSync12(fPath, "utf8");
7364
7907
  if (content.includes("exe-start")) {
7365
7908
  unlinkSync7(fPath);
7366
7909
  }
@@ -7375,34 +7918,34 @@ exec "${exeStartDst}" "$0" "$@"
7375
7918
  `;
7376
7919
  for (const emp of employees) {
7377
7920
  for (let n = 1; n <= MAX_N; n++) {
7378
- const wrapperPath = path15.join(binDir, `${emp.name}${n}`);
7379
- writeFileSync9(wrapperPath, wrapperContent);
7921
+ const wrapperPath = path16.join(binDir, `${emp.name}${n}`);
7922
+ writeFileSync10(wrapperPath, wrapperContent);
7380
7923
  chmodSync2(wrapperPath, 493);
7381
7924
  created++;
7382
- const codexPath = path15.join(binDir, `${emp.name}${n}-codex`);
7383
- writeFileSync9(codexPath, wrapperContent);
7925
+ const codexPath = path16.join(binDir, `${emp.name}${n}-codex`);
7926
+ writeFileSync10(codexPath, wrapperContent);
7384
7927
  chmodSync2(codexPath, 493);
7385
7928
  created++;
7386
7929
  }
7387
7930
  }
7388
7931
  const codexLauncherCandidates = [
7389
- path15.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
7390
- path15.join(packageRoot, "src", "bin", "exe-start-codex.ts")
7932
+ path16.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
7933
+ path16.join(packageRoot, "src", "bin", "exe-start-codex.ts")
7391
7934
  ];
7392
7935
  let codexLauncher = null;
7393
7936
  for (const c of codexLauncherCandidates) {
7394
- if (existsSync15(c)) {
7937
+ if (existsSync16(c)) {
7395
7938
  codexLauncher = c;
7396
7939
  break;
7397
7940
  }
7398
7941
  }
7399
7942
  if (codexLauncher) {
7400
7943
  for (const emp of employees) {
7401
- const wrapperPath = path15.join(binDir, `${emp.name}-codex`);
7944
+ const wrapperPath = path16.join(binDir, `${emp.name}-codex`);
7402
7945
  const content = `#!/bin/bash
7403
7946
  exec node "${codexLauncher}" --agent ${emp.name} "$@"
7404
7947
  `;
7405
- writeFileSync9(wrapperPath, content);
7948
+ writeFileSync10(wrapperPath, content);
7406
7949
  chmodSync2(wrapperPath, 493);
7407
7950
  created++;
7408
7951
  }
@@ -7421,24 +7964,24 @@ export PATH="${binDir}:$PATH"
7421
7964
  const shell = process.env.SHELL ?? "/bin/bash";
7422
7965
  const profilePaths = [];
7423
7966
  if (shell.includes("zsh")) {
7424
- profilePaths.push(path15.join(home, ".zshrc"));
7967
+ profilePaths.push(path16.join(home, ".zshrc"));
7425
7968
  } else if (shell.includes("bash")) {
7426
- profilePaths.push(path15.join(home, ".bashrc"));
7427
- profilePaths.push(path15.join(home, ".bash_profile"));
7969
+ profilePaths.push(path16.join(home, ".bashrc"));
7970
+ profilePaths.push(path16.join(home, ".bash_profile"));
7428
7971
  } else {
7429
- profilePaths.push(path15.join(home, ".profile"));
7972
+ profilePaths.push(path16.join(home, ".profile"));
7430
7973
  }
7431
7974
  for (const profilePath of profilePaths) {
7432
7975
  try {
7433
7976
  let content = "";
7434
7977
  try {
7435
- content = readFileSync11(profilePath, "utf8");
7978
+ content = readFileSync12(profilePath, "utf8");
7436
7979
  } catch {
7437
7980
  }
7438
7981
  if (content.includes(".exe-os/bin")) {
7439
7982
  return false;
7440
7983
  }
7441
- writeFileSync9(profilePath, content + exportLine);
7984
+ writeFileSync10(profilePath, content + exportLine);
7442
7985
  return true;
7443
7986
  } catch {
7444
7987
  continue;
@@ -7458,9 +8001,9 @@ var init_session_wrappers = __esm({
7458
8001
  init_config();
7459
8002
  init_keychain();
7460
8003
  import crypto4 from "crypto";
7461
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync8 } from "fs";
8004
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync11, unlinkSync as unlinkSync8 } from "fs";
7462
8005
  import os8 from "os";
7463
- import path16 from "path";
8006
+ import path17 from "path";
7464
8007
  import { createInterface } from "readline";
7465
8008
 
7466
8009
  // src/lib/model-downloader.ts
@@ -7552,32 +8095,32 @@ async function fileHash(filePath) {
7552
8095
 
7553
8096
  // src/lib/setup-wizard.ts
7554
8097
  function findPackageRoot2() {
7555
- let dir = path16.dirname(new URL(import.meta.url).pathname);
7556
- const root = path16.parse(dir).root;
8098
+ let dir = path17.dirname(new URL(import.meta.url).pathname);
8099
+ const root = path17.parse(dir).root;
7557
8100
  while (dir !== root) {
7558
- const pkgPath = path16.join(dir, "package.json");
7559
- if (existsSync16(pkgPath)) {
8101
+ const pkgPath = path17.join(dir, "package.json");
8102
+ if (existsSync17(pkgPath)) {
7560
8103
  try {
7561
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
8104
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7562
8105
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
7563
8106
  } catch {
7564
8107
  }
7565
8108
  }
7566
- dir = path16.dirname(dir);
8109
+ dir = path17.dirname(dir);
7567
8110
  }
7568
8111
  return null;
7569
8112
  }
7570
- var SETUP_STATE_PATH = path16.join(os8.homedir(), ".exe-os", "setup-state.json");
8113
+ var SETUP_STATE_PATH = path17.join(os8.homedir(), ".exe-os", "setup-state.json");
7571
8114
  function loadSetupState() {
7572
8115
  try {
7573
- return JSON.parse(readFileSync12(SETUP_STATE_PATH, "utf8"));
8116
+ return JSON.parse(readFileSync13(SETUP_STATE_PATH, "utf8"));
7574
8117
  } catch {
7575
8118
  return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
7576
8119
  }
7577
8120
  }
7578
8121
  function saveSetupState(state) {
7579
- mkdirSync8(path16.dirname(SETUP_STATE_PATH), { recursive: true });
7580
- writeFileSync10(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
8122
+ mkdirSync9(path17.dirname(SETUP_STATE_PATH), { recursive: true });
8123
+ writeFileSync11(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
7581
8124
  }
7582
8125
  function clearSetupState() {
7583
8126
  try {
@@ -7627,10 +8170,10 @@ async function validateModel(log) {
7627
8170
  if (totalGB <= 8 || isLowMemory()) {
7628
8171
  log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
7629
8172
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
7630
- const modelPath = path16.join(MODELS_DIR, LOCAL_FILENAME);
7631
- if (existsSync16(modelPath)) {
7632
- const { statSync: statSync4 } = await import("fs");
7633
- const size = statSync4(modelPath).size;
8173
+ const modelPath = path17.join(MODELS_DIR, LOCAL_FILENAME);
8174
+ if (existsSync17(modelPath)) {
8175
+ const { statSync: statSync5 } = await import("fs");
8176
+ const size = statSync5(modelPath).size;
7634
8177
  if (size > 300 * 1e6) {
7635
8178
  log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
7636
8179
  return;
@@ -7659,10 +8202,10 @@ async function runSetupWizard(opts = {}) {
7659
8202
  log("");
7660
8203
  log("=== exe-os Setup ===");
7661
8204
  log("");
7662
- log("Is this a new installation or pairing with an existing device?");
8205
+ log("What are you setting up?");
7663
8206
  log("");
7664
- log(" [1] New installation (first device)");
7665
- log(" [2] Pair with existing device (I have a 24-word phrase)");
8207
+ log(" [1] First device / brand new Exe OS memory");
8208
+ log(" [2] Another device / I already have a 24-word recovery phrase");
7666
8209
  log("");
7667
8210
  const installType = await ask(rl, "Choice (1/2): ");
7668
8211
  let isPairing = false;
@@ -7671,16 +8214,20 @@ async function runSetupWizard(opts = {}) {
7671
8214
  isPairing = true;
7672
8215
  log("");
7673
8216
  const { importMnemonic: importMnemonic2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
7674
- const mnemonic = await ask(rl, "Paste your 24-word recovery phrase: ");
8217
+ log("On your existing device, open Terminal and run:");
8218
+ log(" exe-os cloud link --show-full");
8219
+ log("Then copy the 24-word phrase here.");
8220
+ log("");
8221
+ const mnemonic = await ask(rl, "Paste the 24-word recovery phrase: ");
7675
8222
  try {
7676
8223
  const key = await importMnemonic2(mnemonic);
7677
8224
  await setMasterKey(key);
7678
8225
  log("Master key imported and stored securely.");
7679
8226
  log("");
7680
- log("Enter the API key from your existing device.");
7681
- log("(Find it in ~/.exe-os/config.json under cloud.apiKey, or ask your team lead.)");
8227
+ log("Now paste the Cloud API key from that same `exe-os cloud link --show-full` output.");
8228
+ log("It starts with: exe_sk_");
7682
8229
  log("");
7683
- const apiKey = await ask(rl, "API key (exe_sk_...): ");
8230
+ const apiKey = await ask(rl, "Cloud API key (starts with exe_sk_): ");
7684
8231
  if (apiKey && apiKey.startsWith("exe_sk_")) {
7685
8232
  const cloudEndpoint = "https://askexe.com/cloud";
7686
8233
  const cloudCfg = { apiKey, endpoint: cloudEndpoint };
@@ -7719,7 +8266,7 @@ async function runSetupWizard(opts = {}) {
7719
8266
  if (state.completedSteps.length > 0) {
7720
8267
  log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
7721
8268
  }
7722
- if (existsSync16(LEGACY_LANCE_PATH)) {
8269
+ if (existsSync17(LEGACY_LANCE_PATH)) {
7723
8270
  log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
7724
8271
  log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
7725
8272
  log(" The old directory will not be modified or deleted.");
@@ -7743,14 +8290,22 @@ async function runSetupWizard(opts = {}) {
7743
8290
  log("");
7744
8291
  log(" " + mnemonic);
7745
8292
  log("");
7746
- log(" SAVE THIS NOW. Write it down or store it in your password");
7747
- log(" manager. This phrase is the ONLY way to decrypt your memories");
7748
- log(" if you lose this computer. We cannot recover it for you.");
8293
+ log(" SAVE THIS NOW before continuing.");
8294
+ log(" Best: put it in your password manager, or write it down and keep it safe.");
8295
+ log(" This phrase is the ONLY way to decrypt your memories if you lose this computer.");
8296
+ log(" We cannot recover it for you.");
7749
8297
  log("");
7750
8298
  log(" Like a Bitcoin wallet \u2014 lose the phrase, lose everything.");
7751
8299
  log("=============================================================");
7752
8300
  log("");
7753
- await ask(rl, "Press Enter after you've saved your recovery phrase...");
8301
+ const confirmation = await ask(rl, 'Type "I SAVED MY RECOVERY PHRASE" after you have saved it: ');
8302
+ if (confirmation !== "I SAVED MY RECOVERY PHRASE") {
8303
+ throw new Error(
8304
+ "Setup cancelled: recovery phrase was not confirmed. Save the 24-word phrase before continuing \u2014 it is required to recover encrypted memories."
8305
+ );
8306
+ }
8307
+ const { markKeyBackupConfirmed: markKeyBackupConfirmed2 } = await Promise.resolve().then(() => (init_key_backup_status(), key_backup_status_exports));
8308
+ markKeyBackupConfirmed2("setup-wizard");
7754
8309
  }
7755
8310
  state.completedSteps.push(1);
7756
8311
  saveSetupState(state);
@@ -7897,13 +8452,15 @@ async function runSetupWizard(opts = {}) {
7897
8452
  if (cloudConfig) {
7898
8453
  config.cloud = cloudConfig;
7899
8454
  }
8455
+ const { applyDefaultOrchestrationPhase: applyDefaultOrchestrationPhase2 } = await Promise.resolve().then(() => (init_orchestration_phase(), orchestration_phase_exports));
8456
+ applyDefaultOrchestrationPhase2(config);
7900
8457
  await saveConfig(config);
7901
8458
  log("");
7902
8459
  try {
7903
- const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
8460
+ const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
7904
8461
  let claudeJson = {};
7905
8462
  try {
7906
- claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
8463
+ claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
7907
8464
  } catch {
7908
8465
  }
7909
8466
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -7912,7 +8469,7 @@ async function runSetupWizard(opts = {}) {
7912
8469
  if (!projects[dir]) projects[dir] = {};
7913
8470
  projects[dir].hasTrustDialogAccepted = true;
7914
8471
  }
7915
- writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
8472
+ writeFileSync11(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7916
8473
  } catch {
7917
8474
  }
7918
8475
  state.completedSteps.push(5);
@@ -7926,7 +8483,7 @@ async function runSetupWizard(opts = {}) {
7926
8483
  const prefs = { ...existingPrefs };
7927
8484
  log("=== Config Defaults ===");
7928
8485
  log("");
7929
- const ghosttyDetected = existsSync16(path16.join(os8.homedir(), ".config", "ghostty")) || existsSync16(path16.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
8486
+ const ghosttyDetected = existsSync17(path17.join(os8.homedir(), ".config", "ghostty")) || existsSync17(path17.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
7930
8487
  if (ghosttyDetected) {
7931
8488
  const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
7932
8489
  prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
@@ -7973,7 +8530,7 @@ async function runSetupWizard(opts = {}) {
7973
8530
  let missingIdentities = [];
7974
8531
  for (const emp of roster) {
7975
8532
  const idPath = identityPath2(emp.name);
7976
- if (!existsSync16(idPath)) {
8533
+ if (!existsSync17(idPath)) {
7977
8534
  missingIdentities.push(emp.name);
7978
8535
  }
7979
8536
  }
@@ -8005,7 +8562,7 @@ async function runSetupWizard(opts = {}) {
8005
8562
  }
8006
8563
  missingIdentities = [];
8007
8564
  for (const emp of roster) {
8008
- if (!existsSync16(identityPath2(emp.name))) {
8565
+ if (!existsSync17(identityPath2(emp.name))) {
8009
8566
  missingIdentities.push(emp.name);
8010
8567
  }
8011
8568
  }
@@ -8064,9 +8621,9 @@ async function runSetupWizard(opts = {}) {
8064
8621
  const cooIdentityContent = getIdentityTemplate("coo");
8065
8622
  if (cooIdentityContent) {
8066
8623
  const cooIdPath = identityPath2(cooName);
8067
- mkdirSync8(path16.dirname(cooIdPath), { recursive: true });
8624
+ mkdirSync9(path17.dirname(cooIdPath), { recursive: true });
8068
8625
  const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
8069
- writeFileSync10(cooIdPath, replaced, "utf-8");
8626
+ writeFileSync11(cooIdPath, replaced, "utf-8");
8070
8627
  }
8071
8628
  registerBinSymlinks2(cooName);
8072
8629
  createdEmployees.push({ name: cooName, role: "COO" });
@@ -8117,7 +8674,8 @@ async function runSetupWizard(opts = {}) {
8117
8674
  }
8118
8675
  if (!isLicensed) {
8119
8676
  log("");
8120
- log("You're all set. Your COO handles everything from here.");
8677
+ log("You're all set. You're starting in Phase 1: COO / Chief of Staff mode.");
8678
+ log("Your COO will learn your company context first. You can unlock executives later anytime.");
8121
8679
  log("When you're ready for specialists, add a license key at askexe.com.");
8122
8680
  log("");
8123
8681
  log(`Type your COO's name to start: /${cooName}`);
@@ -8132,67 +8690,89 @@ async function runSetupWizard(opts = {}) {
8132
8690
  }
8133
8691
  if (!state.completedSteps.includes(8)) {
8134
8692
  let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
8135
- log("=== Hire Your Team ===");
8693
+ log("=== Orchestration Phase ===");
8136
8694
  log("");
8137
- log("Your COO coordinates specialists. Each one stays focused on");
8138
- log("their domain \u2014 no context switching, no competing priorities.");
8695
+ log("Recommended default: Phase 1 \u2014 COO / Chief of Staff mode.");
8696
+ log("Start with one-on-one conversations so your COO learns your company,");
8697
+ log("vision, priorities, constraints, and operating style before delegating.");
8139
8698
  log("");
8140
- const ctoTemplate = getTemplateByRole2("CTO");
8141
- const ctoDefault = ctoTemplate?.name ?? "cto";
8142
- log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
8143
- const cmoTemplate = getTemplateByRole2("CMO");
8144
- const cmoDefault = cmoTemplate?.name ?? "cmo";
8145
- log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
8699
+ log("You can jump ahead now if you want. This is a suggestion, not a blocker.");
8146
8700
  log("");
8147
- const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
8148
- const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
8149
- if (!employees.some((e) => e.name === ctoName)) {
8150
- const ctoEmployee = {
8151
- name: ctoName,
8152
- role: "CTO",
8153
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8154
- templateName: ctoDefault,
8155
- templateVersion: 1
8156
- };
8157
- employees = addEmployee2(employees, ctoEmployee);
8158
- await saveEmployees2(employees, EMPLOYEES_PATH2);
8159
- }
8160
- const ctoIdentityContent = getIdentityTemplate("cto");
8161
- if (ctoIdentityContent) {
8162
- const ctoIdPath = identityPath2(ctoName);
8163
- mkdirSync8(path16.dirname(ctoIdPath), { recursive: true });
8164
- const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
8165
- writeFileSync10(ctoIdPath, replaced, "utf-8");
8166
- }
8167
- registerBinSymlinks2(ctoName);
8168
- createdEmployees.push({ name: ctoName, role: "CTO" });
8169
- log(`Created ${ctoName} (CTO)`);
8170
- const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
8171
- const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
8172
- if (!employees.some((e) => e.name === cmoName)) {
8173
- const cmoEmployee = {
8174
- name: cmoName,
8175
- role: "CMO",
8176
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8177
- templateName: cmoDefault,
8178
- templateVersion: 1
8179
- };
8180
- employees = addEmployee2(employees, cmoEmployee);
8181
- await saveEmployees2(employees, EMPLOYEES_PATH2);
8182
- }
8183
- const cmoIdentityContent = getIdentityTemplate("cmo");
8184
- if (cmoIdentityContent) {
8185
- const cmoIdPath = identityPath2(cmoName);
8186
- mkdirSync8(path16.dirname(cmoIdPath), { recursive: true });
8187
- const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
8188
- writeFileSync10(cmoIdPath, replaced, "utf-8");
8189
- }
8190
- registerBinSymlinks2(cmoName);
8191
- createdEmployees.push({ name: cmoName, role: "CMO" });
8192
- log(`Created ${cmoName} (CMO)`);
8701
+ log(" Phase 1: COO only \u2014 build company context first");
8702
+ log(" Phase 2: Executive bench \u2014 add CTO/CMO for repeated domain work");
8703
+ log(" Phase 3: Parallel org \u2014 specialists execute in parallel with reviews");
8193
8704
  log("");
8194
- state.completedSteps.push(8);
8195
- saveSetupState(state);
8705
+ const unlockExecutives = (await ask(rl, "Unlock Phase 2 executives now? (y/N): ")).toLowerCase() === "y";
8706
+ const { setOrchestrationPhase: setOrchestrationPhase2 } = await Promise.resolve().then(() => (init_orchestration_phase(), orchestration_phase_exports));
8707
+ if (!unlockExecutives) {
8708
+ await setOrchestrationPhase2("phase_1_coo", "setup-wizard");
8709
+ log("");
8710
+ log("Starting in Phase 1. Your executive templates are available when you're ready.");
8711
+ log("Later: run `exe-os org unlock executives` to move to Phase 2.");
8712
+ state.completedSteps.push(8);
8713
+ saveSetupState(state);
8714
+ } else {
8715
+ await setOrchestrationPhase2("phase_2_executives", "setup-wizard");
8716
+ log("");
8717
+ log("Phase 2 enabled. Let's name your executive bench.");
8718
+ log("");
8719
+ const ctoTemplate = getTemplateByRole2("CTO");
8720
+ const ctoDefault = ctoTemplate?.name ?? "cto";
8721
+ log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
8722
+ const cmoTemplate = getTemplateByRole2("CMO");
8723
+ const cmoDefault = cmoTemplate?.name ?? "cmo";
8724
+ log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
8725
+ log("");
8726
+ const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
8727
+ const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
8728
+ if (!employees.some((e) => e.name === ctoName)) {
8729
+ const ctoEmployee = {
8730
+ name: ctoName,
8731
+ role: "CTO",
8732
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8733
+ templateName: ctoDefault,
8734
+ templateVersion: 1
8735
+ };
8736
+ employees = addEmployee2(employees, ctoEmployee);
8737
+ await saveEmployees2(employees, EMPLOYEES_PATH2);
8738
+ }
8739
+ const ctoIdentityContent = getIdentityTemplate("cto");
8740
+ if (ctoIdentityContent) {
8741
+ const ctoIdPath = identityPath2(ctoName);
8742
+ mkdirSync9(path17.dirname(ctoIdPath), { recursive: true });
8743
+ const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
8744
+ writeFileSync11(ctoIdPath, replaced, "utf-8");
8745
+ }
8746
+ registerBinSymlinks2(ctoName);
8747
+ createdEmployees.push({ name: ctoName, role: "CTO" });
8748
+ log(`Created ${ctoName} (CTO)`);
8749
+ const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
8750
+ const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
8751
+ if (!employees.some((e) => e.name === cmoName)) {
8752
+ const cmoEmployee = {
8753
+ name: cmoName,
8754
+ role: "CMO",
8755
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8756
+ templateName: cmoDefault,
8757
+ templateVersion: 1
8758
+ };
8759
+ employees = addEmployee2(employees, cmoEmployee);
8760
+ await saveEmployees2(employees, EMPLOYEES_PATH2);
8761
+ }
8762
+ const cmoIdentityContent = getIdentityTemplate("cmo");
8763
+ if (cmoIdentityContent) {
8764
+ const cmoIdPath = identityPath2(cmoName);
8765
+ mkdirSync9(path17.dirname(cmoIdPath), { recursive: true });
8766
+ const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
8767
+ writeFileSync11(cmoIdPath, replaced, "utf-8");
8768
+ }
8769
+ registerBinSymlinks2(cmoName);
8770
+ createdEmployees.push({ name: cmoName, role: "CMO" });
8771
+ log(`Created ${cmoName} (CMO)`);
8772
+ log("");
8773
+ state.completedSteps.push(8);
8774
+ saveSetupState(state);
8775
+ }
8196
8776
  } else {
8197
8777
  log("Step 8 already complete \u2014 skipping.");
8198
8778
  }
@@ -8207,7 +8787,7 @@ async function runSetupWizard(opts = {}) {
8207
8787
  log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
8208
8788
  }
8209
8789
  if (wrapResult.pathConfigured) {
8210
- const binDir = path16.join(os8.homedir(), ".exe-os", "bin");
8790
+ const binDir = path17.join(os8.homedir(), ".exe-os", "bin");
8211
8791
  process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
8212
8792
  pathJustConfigured = true;
8213
8793
  }
@@ -8250,7 +8830,7 @@ async function runSetupWizard(opts = {}) {
8250
8830
  const pkgRoot2 = findPackageRoot2();
8251
8831
  if (pkgRoot2) {
8252
8832
  try {
8253
- version = JSON.parse(readFileSync12(path16.join(pkgRoot2, "package.json"), "utf-8")).version;
8833
+ version = JSON.parse(readFileSync13(path17.join(pkgRoot2, "package.json"), "utf-8")).version;
8254
8834
  } catch {
8255
8835
  }
8256
8836
  }
@@ -8264,6 +8844,10 @@ async function runSetupWizard(opts = {}) {
8264
8844
  log("");
8265
8845
  log("=== Next Steps ===");
8266
8846
  log("");
8847
+ log(" Recommended start: Phase 1 \u2014 talk to your COO first");
8848
+ log(" Check/change phase: exe-os org phase");
8849
+ log(" Unlock executives later: exe-os org unlock executives");
8850
+ log("");
8267
8851
  log(` cd into a project folder: cd ~/my-project`);
8268
8852
  log(` Launch your COO: ${cooName}1`);
8269
8853
  if (pathJustConfigured) {