@askexenow/exe-os 0.9.60 → 0.9.62

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 (106) hide show
  1. package/deploy/stack-manifests/v0.9.json +62 -13
  2. package/dist/bin/backfill-conversations.js +282 -7
  3. package/dist/bin/backfill-responses.js +282 -7
  4. package/dist/bin/backfill-vectors.js +119 -7
  5. package/dist/bin/cc-doctor.js +376 -0
  6. package/dist/bin/cleanup-stale-review-tasks.js +282 -7
  7. package/dist/bin/cli.js +455 -77
  8. package/dist/bin/customer-readiness.js +33 -0
  9. package/dist/bin/exe-agent-config.js +2 -2
  10. package/dist/bin/exe-agent.js +3 -3
  11. package/dist/bin/exe-assign.js +282 -7
  12. package/dist/bin/exe-boot.js +125 -13
  13. package/dist/bin/exe-call.js +3 -3
  14. package/dist/bin/exe-cloud.js +2 -2
  15. package/dist/bin/exe-dispatch.js +282 -7
  16. package/dist/bin/exe-doctor.js +119 -7
  17. package/dist/bin/exe-export-behaviors.js +282 -7
  18. package/dist/bin/exe-forget.js +336 -7
  19. package/dist/bin/exe-gateway.js +282 -7
  20. package/dist/bin/exe-heartbeat.js +284 -9
  21. package/dist/bin/exe-kill.js +282 -7
  22. package/dist/bin/exe-launch-agent.js +282 -7
  23. package/dist/bin/exe-link.js +121 -9
  24. package/dist/bin/exe-new-employee.js +50 -21
  25. package/dist/bin/exe-pending-messages.js +282 -7
  26. package/dist/bin/exe-pending-notifications.js +282 -7
  27. package/dist/bin/exe-pending-reviews.js +282 -7
  28. package/dist/bin/exe-rename.js +282 -7
  29. package/dist/bin/exe-review.js +282 -7
  30. package/dist/bin/exe-search.js +306 -8
  31. package/dist/bin/exe-session-cleanup.js +282 -7
  32. package/dist/bin/exe-settings.js +2 -2
  33. package/dist/bin/exe-start-codex.js +326 -21
  34. package/dist/bin/exe-start-opencode.js +304 -10
  35. package/dist/bin/exe-status.js +282 -7
  36. package/dist/bin/exe-team.js +282 -7
  37. package/dist/bin/git-sweep.js +282 -7
  38. package/dist/bin/graph-backfill.js +282 -7
  39. package/dist/bin/graph-export.js +282 -7
  40. package/dist/bin/install.js +58 -33
  41. package/dist/bin/intercom-check.js +282 -7
  42. package/dist/bin/pre-build-guard.js +98 -0
  43. package/dist/bin/scan-tasks.js +282 -7
  44. package/dist/bin/setup.js +122 -10
  45. package/dist/bin/shard-migrate.js +282 -7
  46. package/dist/bin/stack-update.js +79 -11
  47. package/dist/bin/update.js +2 -2
  48. package/dist/gateway/index.js +288 -13
  49. package/dist/hooks/bug-report-worker.js +282 -7
  50. package/dist/hooks/codex-stop-task-finalizer.js +282 -7
  51. package/dist/hooks/commit-complete.js +282 -7
  52. package/dist/hooks/error-recall.js +306 -8
  53. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  54. package/dist/hooks/ingest-worker.js +2 -2
  55. package/dist/hooks/ingest.js +282 -7
  56. package/dist/hooks/instructions-loaded.js +282 -7
  57. package/dist/hooks/notification.js +282 -7
  58. package/dist/hooks/post-compact.js +282 -7
  59. package/dist/hooks/post-tool-combined.js +306 -8
  60. package/dist/hooks/pre-compact.js +282 -7
  61. package/dist/hooks/pre-tool-use.js +282 -7
  62. package/dist/hooks/prompt-submit.js +306 -8
  63. package/dist/hooks/session-end.js +282 -7
  64. package/dist/hooks/session-start.js +308 -10
  65. package/dist/hooks/stop.js +282 -7
  66. package/dist/hooks/subagent-stop.js +282 -7
  67. package/dist/hooks/summary-worker.js +125 -13
  68. package/dist/index.js +288 -13
  69. package/dist/lib/agent-config.js +2 -2
  70. package/dist/lib/cloud-sync.js +121 -9
  71. package/dist/lib/config.js +2 -2
  72. package/dist/lib/consolidation.js +2 -2
  73. package/dist/lib/database.js +115 -3
  74. package/dist/lib/db-daemon-client.js +2 -2
  75. package/dist/lib/db.js +115 -3
  76. package/dist/lib/device-registry.js +115 -3
  77. package/dist/lib/embedder.js +2 -2
  78. package/dist/lib/employee-templates.js +3 -3
  79. package/dist/lib/employees.js +2 -2
  80. package/dist/lib/exe-daemon-client.js +2 -2
  81. package/dist/lib/exe-daemon.js +339 -31
  82. package/dist/lib/hybrid-search.js +306 -8
  83. package/dist/lib/identity.js +2 -2
  84. package/dist/lib/license.js +2 -2
  85. package/dist/lib/messaging.js +2 -2
  86. package/dist/lib/reminders.js +2 -2
  87. package/dist/lib/schedules.js +119 -7
  88. package/dist/lib/skill-learning.js +2 -2
  89. package/dist/lib/store.js +282 -7
  90. package/dist/lib/task-router.js +2 -2
  91. package/dist/lib/tasks.js +2 -2
  92. package/dist/lib/tmux-routing.js +2 -2
  93. package/dist/lib/token-spend.js +2 -2
  94. package/dist/mcp/server.js +339 -31
  95. package/dist/mcp/tools/complete-reminder.js +2 -2
  96. package/dist/mcp/tools/create-reminder.js +2 -2
  97. package/dist/mcp/tools/create-task.js +2 -2
  98. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  99. package/dist/mcp/tools/list-reminders.js +2 -2
  100. package/dist/mcp/tools/list-tasks.js +2 -2
  101. package/dist/mcp/tools/send-message.js +2 -2
  102. package/dist/mcp/tools/update-task.js +2 -2
  103. package/dist/runtime/index.js +282 -7
  104. package/dist/tui/App.js +290 -15
  105. package/package.json +3 -2
  106. package/stack.release.json +23 -7
package/dist/bin/cli.js CHANGED
@@ -258,8 +258,8 @@ var init_config = __esm({
258
258
  rerankerAutoTrigger: {
259
259
  enabled: true,
260
260
  broadQueryMinCardinality: 5e4,
261
- fetchTopK: 150,
262
- returnTopK: 5
261
+ fetchTopK: 200,
262
+ returnTopK: 20
263
263
  }
264
264
  },
265
265
  graphRagEnabled: true,
@@ -810,6 +810,48 @@ var init_preferences = __esm({
810
810
  }
811
811
  });
812
812
 
813
+ // src/adapters/runtime-hook-manifest.ts
814
+ function commandHasAnyMarker(command, markers) {
815
+ return markers.some((marker) => command.includes(marker));
816
+ }
817
+ function isLegacySplitPostToolCommand(command) {
818
+ return commandHasAnyMarker(command, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS);
819
+ }
820
+ function textHasLegacySplitPostToolHook(text) {
821
+ return [EXE_HOOK_FILES.ingest, EXE_HOOK_FILES.errorRecall, EXE_HOOK_FILES.ingestWorker].some((file) => text.includes(file));
822
+ }
823
+ var EXE_HOOK_FILES, EXE_HOOKS, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
824
+ var init_runtime_hook_manifest = __esm({
825
+ "src/adapters/runtime-hook-manifest.ts"() {
826
+ "use strict";
827
+ EXE_HOOK_FILES = {
828
+ postToolCombined: "post-tool-combined.js",
829
+ ingest: "ingest.js",
830
+ errorRecall: "error-recall.js",
831
+ ingestWorker: "ingest-worker.js"
832
+ };
833
+ EXE_HOOKS = {
834
+ postToolCombined: "dist/hooks/post-tool-combined.js",
835
+ sessionStart: "dist/hooks/session-start.js",
836
+ promptSubmit: "dist/hooks/prompt-submit.js",
837
+ heartbeat: "dist/hooks/exe-heartbeat-hook.js",
838
+ stop: "dist/hooks/stop.js",
839
+ preToolUse: "dist/hooks/pre-tool-use.js",
840
+ subagentStop: "dist/hooks/subagent-stop.js",
841
+ preCompact: "dist/hooks/pre-compact.js",
842
+ postCompact: "dist/hooks/post-compact.js",
843
+ sessionEnd: "dist/hooks/session-end.js",
844
+ notification: "dist/hooks/notification.js",
845
+ instructionsLoaded: "dist/hooks/instructions-loaded.js"
846
+ };
847
+ LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
848
+ "dist/hooks/ingest.js",
849
+ "dist/hooks/error-recall.js",
850
+ "dist/hooks/ingest-worker.js"
851
+ ];
852
+ }
853
+ });
854
+
813
855
  // src/adapters/claude/installer.ts
814
856
  var installer_exports = {};
815
857
  __export(installer_exports, {
@@ -1132,7 +1174,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1132
1174
  }
1133
1175
  ]
1134
1176
  },
1135
- marker: "dist/hooks/post-tool-combined.js"
1177
+ marker: EXE_HOOKS.postToolCombined
1136
1178
  },
1137
1179
  {
1138
1180
  event: "SessionStart",
@@ -1145,7 +1187,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1145
1187
  }
1146
1188
  ]
1147
1189
  },
1148
- marker: "dist/hooks/session-start.js"
1190
+ marker: EXE_HOOKS.sessionStart
1149
1191
  },
1150
1192
  {
1151
1193
  event: "UserPromptSubmit",
@@ -1157,7 +1199,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1157
1199
  }
1158
1200
  ]
1159
1201
  },
1160
- marker: "dist/hooks/prompt-submit.js"
1202
+ marker: EXE_HOOKS.promptSubmit
1161
1203
  },
1162
1204
  {
1163
1205
  event: "UserPromptSubmit",
@@ -1170,7 +1212,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1170
1212
  }
1171
1213
  ]
1172
1214
  },
1173
- marker: "dist/hooks/exe-heartbeat-hook.js"
1215
+ marker: EXE_HOOKS.heartbeat
1174
1216
  },
1175
1217
  {
1176
1218
  event: "Stop",
@@ -1182,7 +1224,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1182
1224
  }
1183
1225
  ]
1184
1226
  },
1185
- marker: "dist/hooks/stop.js"
1227
+ marker: EXE_HOOKS.stop
1186
1228
  },
1187
1229
  {
1188
1230
  event: "PreToolUse",
@@ -1195,7 +1237,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1195
1237
  }
1196
1238
  ]
1197
1239
  },
1198
- marker: "dist/hooks/pre-tool-use.js"
1240
+ marker: EXE_HOOKS.preToolUse
1199
1241
  },
1200
1242
  {
1201
1243
  event: "SubagentStop",
@@ -1207,7 +1249,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1207
1249
  }
1208
1250
  ]
1209
1251
  },
1210
- marker: "dist/hooks/subagent-stop.js"
1252
+ marker: EXE_HOOKS.subagentStop
1211
1253
  },
1212
1254
  {
1213
1255
  event: "PreCompact",
@@ -1220,7 +1262,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1220
1262
  }
1221
1263
  ]
1222
1264
  },
1223
- marker: "dist/hooks/pre-compact.js"
1265
+ marker: EXE_HOOKS.preCompact
1224
1266
  },
1225
1267
  {
1226
1268
  event: "SessionEnd",
@@ -1232,7 +1274,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1232
1274
  }
1233
1275
  ]
1234
1276
  },
1235
- marker: "dist/hooks/session-end.js"
1277
+ marker: EXE_HOOKS.sessionEnd
1236
1278
  },
1237
1279
  {
1238
1280
  event: "Notification",
@@ -1244,7 +1286,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1244
1286
  }
1245
1287
  ]
1246
1288
  },
1247
- marker: "dist/hooks/notification.js"
1289
+ marker: EXE_HOOKS.notification
1248
1290
  },
1249
1291
  {
1250
1292
  event: "PostCompact",
@@ -1257,7 +1299,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1257
1299
  }
1258
1300
  ]
1259
1301
  },
1260
- marker: "dist/hooks/post-compact.js"
1302
+ marker: EXE_HOOKS.postCompact
1261
1303
  },
1262
1304
  {
1263
1305
  event: "InstructionsLoaded",
@@ -1269,21 +1311,16 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1269
1311
  }
1270
1312
  ]
1271
1313
  },
1272
- marker: "dist/hooks/instructions-loaded.js"
1314
+ marker: EXE_HOOKS.instructionsLoaded
1273
1315
  }
1274
1316
  ];
1275
1317
  let added = 0;
1276
1318
  let skipped = 0;
1277
- const legacyPostToolMarkers = [
1278
- "dist/hooks/ingest.js",
1279
- "dist/hooks/error-recall.js",
1280
- "dist/hooks/ingest-worker.js"
1281
- ];
1282
1319
  const postToolGroups = settings.hooks["PostToolUse"];
1283
1320
  if (Array.isArray(postToolGroups)) {
1284
1321
  settings.hooks["PostToolUse"] = postToolGroups.map((g) => ({
1285
1322
  ...g,
1286
- hooks: g.hooks.filter((h) => !legacyPostToolMarkers.some((marker) => h.command.includes(marker)))
1323
+ hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
1287
1324
  })).filter((g) => g.hooks.length > 0);
1288
1325
  }
1289
1326
  for (const { event, group, marker } of hooksToRegister) {
@@ -1760,6 +1797,7 @@ var init_installer = __esm({
1760
1797
  init_agent_symlinks();
1761
1798
  init_mcp_prefix();
1762
1799
  init_preferences();
1800
+ init_runtime_hook_manifest();
1763
1801
  EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
1764
1802
  EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
1765
1803
  ORCHESTRATION_RULES = `${EXE_SECTION_START}
@@ -4306,7 +4344,7 @@ async function ensureSchema() {
4306
4344
  ON session_kills(agent_id);
4307
4345
  `);
4308
4346
  await client.execute(`
4309
- CREATE TABLE IF NOT EXISTS global_procedures (
4347
+ CREATE TABLE IF NOT EXISTS company_procedures (
4310
4348
  id TEXT PRIMARY KEY,
4311
4349
  title TEXT NOT NULL,
4312
4350
  content TEXT NOT NULL,
@@ -4317,6 +4355,73 @@ async function ensureSchema() {
4317
4355
  updated_at TEXT NOT NULL
4318
4356
  )
4319
4357
  `);
4358
+ const legacyProcedureObject = await client.execute({
4359
+ sql: "SELECT type FROM sqlite_master WHERE name = 'global_procedures'",
4360
+ args: []
4361
+ });
4362
+ const legacyProcedureType = legacyProcedureObject.rows[0]?.type == null ? null : String(legacyProcedureObject.rows[0].type);
4363
+ if (legacyProcedureType === "table") {
4364
+ await client.execute(`
4365
+ INSERT OR IGNORE INTO company_procedures
4366
+ (id, title, content, priority, domain, active, created_at, updated_at)
4367
+ SELECT id, title, content, priority, domain, active, created_at, updated_at
4368
+ FROM global_procedures
4369
+ `);
4370
+ await client.executeMultiple(`
4371
+ CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_insert
4372
+ AFTER INSERT ON global_procedures
4373
+ BEGIN
4374
+ INSERT OR IGNORE INTO company_procedures
4375
+ (id, title, content, priority, domain, active, created_at, updated_at)
4376
+ VALUES
4377
+ (NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
4378
+ END;
4379
+
4380
+ CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_update
4381
+ AFTER UPDATE ON global_procedures
4382
+ BEGIN
4383
+ UPDATE company_procedures
4384
+ SET title = NEW.title,
4385
+ content = NEW.content,
4386
+ priority = NEW.priority,
4387
+ domain = NEW.domain,
4388
+ active = NEW.active,
4389
+ created_at = NEW.created_at,
4390
+ updated_at = NEW.updated_at
4391
+ WHERE id = OLD.id;
4392
+ END;
4393
+ `);
4394
+ } else {
4395
+ await client.execute(`
4396
+ CREATE VIEW IF NOT EXISTS global_procedures AS
4397
+ SELECT id, title, content, priority, domain, active, created_at, updated_at
4398
+ FROM company_procedures
4399
+ `);
4400
+ await client.executeMultiple(`
4401
+ CREATE TRIGGER IF NOT EXISTS global_procedures_insert
4402
+ INSTEAD OF INSERT ON global_procedures
4403
+ BEGIN
4404
+ INSERT INTO company_procedures
4405
+ (id, title, content, priority, domain, active, created_at, updated_at)
4406
+ VALUES
4407
+ (NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
4408
+ END;
4409
+
4410
+ CREATE TRIGGER IF NOT EXISTS global_procedures_update
4411
+ INSTEAD OF UPDATE ON global_procedures
4412
+ BEGIN
4413
+ UPDATE company_procedures
4414
+ SET title = NEW.title,
4415
+ content = NEW.content,
4416
+ priority = NEW.priority,
4417
+ domain = NEW.domain,
4418
+ active = NEW.active,
4419
+ created_at = NEW.created_at,
4420
+ updated_at = NEW.updated_at
4421
+ WHERE id = OLD.id;
4422
+ END;
4423
+ `);
4424
+ }
4320
4425
  await client.executeMultiple(`
4321
4426
  CREATE TABLE IF NOT EXISTS conversations (
4322
4427
  id TEXT PRIMARY KEY,
@@ -4456,6 +4561,51 @@ async function ensureSchema() {
4456
4561
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
4457
4562
  END;
4458
4563
  `);
4564
+ await client.executeMultiple(`
4565
+ CREATE TABLE IF NOT EXISTS memory_cards (
4566
+ id TEXT PRIMARY KEY,
4567
+ memory_id TEXT NOT NULL,
4568
+ agent_id TEXT NOT NULL,
4569
+ session_id TEXT NOT NULL,
4570
+ project_name TEXT,
4571
+ timestamp TEXT NOT NULL,
4572
+ card_type TEXT NOT NULL,
4573
+ subject TEXT,
4574
+ predicate TEXT,
4575
+ object TEXT,
4576
+ content TEXT NOT NULL,
4577
+ source_ref TEXT,
4578
+ confidence REAL DEFAULT 0.6,
4579
+ active INTEGER DEFAULT 1,
4580
+ created_at TEXT NOT NULL
4581
+ );
4582
+
4583
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
4584
+ ON memory_cards(agent_id, active, timestamp);
4585
+
4586
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
4587
+ ON memory_cards(memory_id);
4588
+
4589
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
4590
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
4591
+
4592
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
4593
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
4594
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
4595
+ END;
4596
+
4597
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
4598
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
4599
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
4600
+ END;
4601
+
4602
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
4603
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
4604
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
4605
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
4606
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
4607
+ END;
4608
+ `);
4459
4609
  try {
4460
4610
  await client.execute({
4461
4611
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -5904,12 +6054,12 @@ async function cloudSync(config) {
5904
6054
  try {
5905
6055
  await cloudPushGlobalProcedures(config);
5906
6056
  } catch (err) {
5907
- logError(`[cloud-sync] Global procedures push: ${err instanceof Error ? err.message : String(err)}`);
6057
+ logError(`[cloud-sync] Company procedures push: ${err instanceof Error ? err.message : String(err)}`);
5908
6058
  }
5909
6059
  try {
5910
6060
  await cloudPullGlobalProcedures(config);
5911
6061
  } catch (err) {
5912
- logError(`[cloud-sync] Global procedures pull: ${err instanceof Error ? err.message : String(err)}`);
6062
+ logError(`[cloud-sync] Company procedures pull: ${err instanceof Error ? err.message : String(err)}`);
5913
6063
  }
5914
6064
  const countRows = async (sql) => {
5915
6065
  try {
@@ -6318,12 +6468,12 @@ async function cloudPullBlob(route, config) {
6318
6468
  }
6319
6469
  async function cloudPushGlobalProcedures(config) {
6320
6470
  const client = getClient();
6321
- const result = await client.execute("SELECT * FROM global_procedures LIMIT 1000");
6471
+ const result = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
6322
6472
  const rows = result.rows;
6323
6473
  const { ok } = await cloudPushBlob(
6324
6474
  "/sync/push-global-procedures",
6325
6475
  rows,
6326
- "last_global_procedures_push_version",
6476
+ "last_company_procedures_push_version",
6327
6477
  config
6328
6478
  );
6329
6479
  return ok;
@@ -6336,7 +6486,7 @@ async function cloudPullGlobalProcedures(config) {
6336
6486
  if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
6337
6487
  const client = getClient();
6338
6488
  const stmts = remoteProcs.map((p) => ({
6339
- sql: `INSERT INTO global_procedures
6489
+ sql: `INSERT INTO company_procedures
6340
6490
  (id, title, content, priority, domain, active, created_at, updated_at)
6341
6491
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
6342
6492
  ON CONFLICT(id) DO UPDATE SET
@@ -6346,7 +6496,7 @@ async function cloudPullGlobalProcedures(config) {
6346
6496
  domain = excluded.domain,
6347
6497
  active = excluded.active,
6348
6498
  updated_at = excluded.updated_at
6349
- WHERE excluded.updated_at > global_procedures.updated_at`,
6499
+ WHERE excluded.updated_at > company_procedures.updated_at`,
6350
6500
  args: [
6351
6501
  sqlSafe(p.id),
6352
6502
  sqlSafe(p.title),
@@ -7756,7 +7906,7 @@ var init_platform_procedures = __esm({
7756
7906
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
7757
7907
  domain: "tool-use",
7758
7908
  priority: "p1",
7759
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
7909
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. company_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: global_procedure, store_global_procedure, list_global_procedures, deactivate_global_procedure."
7760
7910
  }
7761
7911
  ];
7762
7912
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -7777,7 +7927,7 @@ import { randomUUID as randomUUID3 } from "crypto";
7777
7927
  async function loadGlobalProcedures() {
7778
7928
  const client = getClient();
7779
7929
  const result = await client.execute({
7780
- sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
7930
+ sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
7781
7931
  args: []
7782
7932
  });
7783
7933
  const allRows = result.rows;
@@ -7806,7 +7956,7 @@ async function storeGlobalProcedure(input) {
7806
7956
  const now = (/* @__PURE__ */ new Date()).toISOString();
7807
7957
  const client = getClient();
7808
7958
  await client.execute({
7809
- sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
7959
+ sql: `INSERT INTO company_procedures (id, title, content, priority, domain, active, created_at, updated_at)
7810
7960
  VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
7811
7961
  args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
7812
7962
  });
@@ -7817,7 +7967,7 @@ async function deactivateGlobalProcedure(id) {
7817
7967
  const now = (/* @__PURE__ */ new Date()).toISOString();
7818
7968
  const client = getClient();
7819
7969
  const result = await client.execute({
7820
- sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
7970
+ sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
7821
7971
  args: [now, id]
7822
7972
  });
7823
7973
  await loadGlobalProcedures();
@@ -7836,6 +7986,164 @@ ${p.content}`).join("\n\n");
7836
7986
  }
7837
7987
  });
7838
7988
 
7989
+ // src/lib/memory-cards.ts
7990
+ var memory_cards_exports = {};
7991
+ __export(memory_cards_exports, {
7992
+ extractMemoryCards: () => extractMemoryCards,
7993
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
7994
+ searchMemoryCards: () => searchMemoryCards
7995
+ });
7996
+ import { createHash as createHash3 } from "crypto";
7997
+ function stableId(memoryId, type, content) {
7998
+ return createHash3("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
7999
+ }
8000
+ function cleanText(text) {
8001
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8002
+ }
8003
+ function splitSentences(text) {
8004
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
8005
+ }
8006
+ function inferCardType(sentence, toolName) {
8007
+ const lower = sentence.toLowerCase();
8008
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
8009
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
8010
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
8011
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
8012
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
8013
+ return "fact";
8014
+ }
8015
+ function extractSubject(sentence, agentId) {
8016
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
8017
+ return explicit?.[1] ?? agentId;
8018
+ }
8019
+ function predicateFor(type) {
8020
+ switch (type) {
8021
+ case "preference":
8022
+ return "prefers";
8023
+ case "belief_update":
8024
+ return "updated";
8025
+ case "decision":
8026
+ return "decided";
8027
+ case "event":
8028
+ return "happened";
8029
+ case "code":
8030
+ return "implemented";
8031
+ default:
8032
+ return "states";
8033
+ }
8034
+ }
8035
+ function extractMemoryCards(row) {
8036
+ const sentences = splitSentences(row.raw_text);
8037
+ const cards = [];
8038
+ for (const sentence of sentences) {
8039
+ const type = inferCardType(sentence, row.tool_name);
8040
+ const subject = extractSubject(sentence, row.agent_id);
8041
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
8042
+ cards.push({
8043
+ id: stableId(row.id, type, content),
8044
+ memory_id: row.id,
8045
+ agent_id: row.agent_id,
8046
+ session_id: row.session_id,
8047
+ project_name: row.project_name ?? null,
8048
+ timestamp: row.timestamp,
8049
+ card_type: type,
8050
+ subject,
8051
+ predicate: predicateFor(type),
8052
+ object: content,
8053
+ content,
8054
+ source_ref: row.id,
8055
+ confidence: type === "fact" ? 0.55 : 0.65
8056
+ });
8057
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
8058
+ }
8059
+ return cards;
8060
+ }
8061
+ async function insertMemoryCardsForBatch(rows) {
8062
+ const cards = rows.flatMap(extractMemoryCards);
8063
+ if (cards.length === 0) return 0;
8064
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8065
+ const client = getClient();
8066
+ const stmts = cards.map((card) => ({
8067
+ sql: `INSERT OR IGNORE INTO memory_cards
8068
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
8069
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
8070
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
8071
+ args: [
8072
+ card.id,
8073
+ card.memory_id,
8074
+ card.agent_id,
8075
+ card.session_id,
8076
+ card.project_name,
8077
+ card.timestamp,
8078
+ card.card_type,
8079
+ card.subject,
8080
+ card.predicate,
8081
+ card.object,
8082
+ card.content,
8083
+ card.source_ref,
8084
+ card.confidence,
8085
+ now
8086
+ ]
8087
+ }));
8088
+ await client.batch(stmts, "write");
8089
+ return cards.length;
8090
+ }
8091
+ function buildMatchExpr(queryText) {
8092
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
8093
+ if (terms.length === 0) return null;
8094
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
8095
+ }
8096
+ async function searchMemoryCards(queryText, agentId, options) {
8097
+ const limit = options?.limit ?? 10;
8098
+ const matchExpr = buildMatchExpr(queryText);
8099
+ if (!matchExpr) return [];
8100
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
8101
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
8102
+ FROM memory_cards c
8103
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
8104
+ WHERE memory_cards_fts MATCH ?
8105
+ AND c.agent_id = ?
8106
+ AND COALESCE(c.active, 1) = 1`;
8107
+ const args2 = [matchExpr, agentId];
8108
+ if (options?.projectName) {
8109
+ sql += ` AND c.project_name = ?`;
8110
+ args2.push(options.projectName);
8111
+ }
8112
+ if (options?.since) {
8113
+ sql += ` AND c.timestamp >= ?`;
8114
+ args2.push(options.since);
8115
+ }
8116
+ sql += ` ORDER BY rank LIMIT ?`;
8117
+ args2.push(limit);
8118
+ const result = await getClient().execute({ sql, args: args2 });
8119
+ return result.rows.map((row) => ({
8120
+ id: `card:${String(row.id)}`,
8121
+ agent_id: String(row.agent_id),
8122
+ agent_role: "memory_card",
8123
+ session_id: String(row.session_id),
8124
+ timestamp: String(row.timestamp),
8125
+ tool_name: `memory_card:${String(row.card_type)}`,
8126
+ project_name: row.project_name == null ? "" : String(row.project_name),
8127
+ has_error: false,
8128
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
8129
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
8130
+ vector: [],
8131
+ importance: 6,
8132
+ status: "active",
8133
+ confidence: Number(row.confidence ?? 0.6),
8134
+ last_accessed: String(row.timestamp)
8135
+ }));
8136
+ }
8137
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
8138
+ var init_memory_cards = __esm({
8139
+ "src/lib/memory-cards.ts"() {
8140
+ "use strict";
8141
+ init_database();
8142
+ MAX_CARDS_PER_MEMORY = 6;
8143
+ MAX_SENTENCE_CHARS = 360;
8144
+ }
8145
+ });
8146
+
7839
8147
  // src/lib/store.ts
7840
8148
  var store_exports = {};
7841
8149
  __export(store_exports, {
@@ -8174,6 +8482,11 @@ async function flushBatch() {
8174
8482
  const globalClient = getClient();
8175
8483
  const globalStmts = batch.map(buildStmt);
8176
8484
  await globalClient.batch(globalStmts, "write");
8485
+ try {
8486
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
8487
+ await insertMemoryCardsForBatch2(batch);
8488
+ } catch {
8489
+ }
8177
8490
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
8178
8491
  _pendingRecords.splice(0, batch.length);
8179
8492
  try {
@@ -9432,7 +9745,7 @@ __export(identity_exports, {
9432
9745
  import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
9433
9746
  import { readdirSync as readdirSync4 } from "fs";
9434
9747
  import path17 from "path";
9435
- import { createHash as createHash3 } from "crypto";
9748
+ import { createHash as createHash4 } from "crypto";
9436
9749
  function ensureDir() {
9437
9750
  if (!existsSync16(IDENTITY_DIR2)) {
9438
9751
  mkdirSync9(IDENTITY_DIR2, { recursive: true });
@@ -9479,7 +9792,7 @@ function parseFrontmatter(raw) {
9479
9792
  };
9480
9793
  }
9481
9794
  function contentHash(content) {
9482
- return createHash3("sha256").update(content).digest("hex").slice(0, 16);
9795
+ return createHash4("sha256").update(content).digest("hex").slice(0, 16);
9483
9796
  }
9484
9797
  function getIdentity(agentId) {
9485
9798
  const filePath = identityPath(agentId);
@@ -9695,7 +10008,7 @@ async function insertProcedures(procedures, timestamp, options) {
9695
10008
  continue;
9696
10009
  }
9697
10010
  await client.execute({
9698
- sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
10011
+ sql: `INSERT INTO company_procedures (id, title, content, priority, domain, active, created_at, updated_at)
9699
10012
  VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
9700
10013
  args: [
9701
10014
  randomUUID4(),
@@ -9777,7 +10090,7 @@ async function importIdentities(identities, updatedBy) {
9777
10090
  async function getActiveProcedureTitles() {
9778
10091
  const client = getClient();
9779
10092
  const result = await client.execute({
9780
- sql: "SELECT title FROM global_procedures WHERE active = 1",
10093
+ sql: "SELECT title FROM company_procedures WHERE active = 1",
9781
10094
  args: []
9782
10095
  });
9783
10096
  return new Set(result.rows.map((row) => String(row.title)));
@@ -9800,7 +10113,7 @@ async function exportOrchestration(createdBy) {
9800
10113
  args: []
9801
10114
  });
9802
10115
  const procedureResult = await client.execute({
9803
- sql: "SELECT title, content, priority, domain FROM global_procedures WHERE active = 1",
10116
+ sql: "SELECT title, content, priority, domain FROM company_procedures WHERE active = 1",
9804
10117
  args: []
9805
10118
  });
9806
10119
  const behaviors = behaviorResult.rows.map((row) => ({
@@ -14899,7 +15212,7 @@ var init_exe_rename = __esm({
14899
15212
  // src/lib/model-downloader.ts
14900
15213
  import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync26, unlinkSync as unlinkSync12, renameSync as renameSync6 } from "fs";
14901
15214
  import { mkdir as mkdir6 } from "fs/promises";
14902
- import { createHash as createHash4 } from "crypto";
15215
+ import { createHash as createHash5 } from "crypto";
14903
15216
  import path32 from "path";
14904
15217
  async function downloadModel(opts) {
14905
15218
  const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
@@ -14927,7 +15240,7 @@ async function downloadModel(opts) {
14927
15240
  throw new Error(`Download failed: HTTP ${response.status}`);
14928
15241
  }
14929
15242
  const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
14930
- const hash = createHash4("sha256");
15243
+ const hash = createHash5("sha256");
14931
15244
  const fileStream = createWriteStream(tmpPath);
14932
15245
  const reader = response.body.getReader();
14933
15246
  try {
@@ -14971,7 +15284,7 @@ Download attempt ${attempt} failed, retrying...
14971
15284
  }
14972
15285
  async function fileHash(filePath) {
14973
15286
  return new Promise((resolve, reject) => {
14974
- const hash = createHash4("sha256");
15287
+ const hash = createHash5("sha256");
14975
15288
  const stream = createReadStream2(filePath);
14976
15289
  stream.on("data", (chunk) => hash.update(chunk));
14977
15290
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -16793,12 +17106,64 @@ var init_update = __esm({
16793
17106
 
16794
17107
  // src/lib/stack-update.ts
16795
17108
  import { execFileSync as execFileSync3 } from "child_process";
16796
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "fs";
17109
+ import { createVerify, verify as verifySignature } from "crypto";
17110
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readdirSync as readdirSync10, readFileSync as readFileSync25, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "fs";
16797
17111
  import http from "http";
16798
17112
  import https from "https";
16799
17113
  import path37 from "path";
16800
- function parseStackManifest(raw) {
16801
- const parsed = JSON.parse(raw);
17114
+ function isSignedEnvelope(value) {
17115
+ return !!value && typeof value === "object" && "manifest" in value && "signature" in value;
17116
+ }
17117
+ function canonicalizeStackManifest(manifest) {
17118
+ const clone = JSON.parse(JSON.stringify(manifest));
17119
+ delete clone.signature;
17120
+ return stableJson(clone);
17121
+ }
17122
+ function verifyStackManifestSignature(manifest, publicKeyPem) {
17123
+ const signature = manifest.signature;
17124
+ if (!signature) throw new Error("Stack manifest signature required but missing");
17125
+ const payload = Buffer.from(canonicalizeStackManifest(manifest));
17126
+ const sig = Buffer.from(signature.signature, "base64");
17127
+ let ok = false;
17128
+ if (signature.alg === "ed25519") {
17129
+ ok = verifySignature(null, payload, publicKeyPem, sig);
17130
+ } else if (signature.alg === "rsa-sha256") {
17131
+ const verifier = createVerify("RSA-SHA256");
17132
+ verifier.update(payload);
17133
+ verifier.end();
17134
+ ok = verifier.verify(publicKeyPem, sig);
17135
+ } else {
17136
+ throw new Error(`Unsupported stack manifest signature alg: ${signature.alg}`);
17137
+ }
17138
+ if (!ok) throw new Error("Stack manifest signature verification failed");
17139
+ }
17140
+ function stableJson(value) {
17141
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
17142
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
17143
+ const obj = value;
17144
+ return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableJson(obj[key])}`).join(",")}}`;
17145
+ }
17146
+ function findLatestBackupEnvFile(envFile) {
17147
+ const backupDir = path37.join(path37.dirname(envFile), ".exe-stack-backups");
17148
+ if (!existsSync30(backupDir)) return null;
17149
+ const backups = readdirSync10(backupDir).filter((name) => name.startsWith("env-") && name.endsWith(".bak")).sort();
17150
+ const latest = backups.at(-1);
17151
+ return latest ? path37.join(backupDir, latest) : null;
17152
+ }
17153
+ async function rollbackStackUpdate(options) {
17154
+ const exec2 = options.exec ?? defaultExec;
17155
+ const backupEnvFile = options.lockFile && existsSync30(options.lockFile) ? JSON.parse(readFileSync25(options.lockFile, "utf8")).backupEnvFile : void 0;
17156
+ const rollbackEnv = backupEnvFile && existsSync30(backupEnvFile) ? backupEnvFile : findLatestBackupEnvFile(options.envFile);
17157
+ if (!rollbackEnv) throw new Error(`No stack backup env found beside ${options.envFile}`);
17158
+ writeFileSync20(options.envFile, readFileSync25(rollbackEnv), { mode: 384 });
17159
+ const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
17160
+ exec2("docker", [...composeArgs, "up", "-d"]);
17161
+ return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json") };
17162
+ }
17163
+ function parseStackManifest(raw, publicKey) {
17164
+ const parsedRaw = JSON.parse(raw);
17165
+ const parsed = isSignedEnvelope(parsedRaw) ? { ...parsedRaw.manifest, signature: parsedRaw.signature } : parsedRaw;
17166
+ if (publicKey) verifyStackManifestSignature(parsed, publicKey);
16802
17167
  if (parsed.schemaVersion !== 1) throw new Error("Unsupported stack manifest schemaVersion");
16803
17168
  if (!parsed.latest || !parsed.stacks || typeof parsed.stacks !== "object") {
16804
17169
  throw new Error("Invalid stack manifest: latest and stacks are required");
@@ -16816,9 +17181,9 @@ function parseStackManifest(raw) {
16816
17181
  }
16817
17182
  return parsed;
16818
17183
  }
16819
- async function loadStackManifest(ref, fetchText = defaultFetchText) {
16820
- if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref));
16821
- return parseStackManifest(readFileSync25(ref, "utf8"));
17184
+ async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey) {
17185
+ if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref), publicKey);
17186
+ return parseStackManifest(readFileSync25(ref, "utf8"), publicKey);
16822
17187
  }
16823
17188
  function parseEnv(raw) {
16824
17189
  const env = /* @__PURE__ */ new Map();
@@ -16884,7 +17249,8 @@ Re-run with --allow-breaking ${missing.map((c) => c.id).join(",")}`
16884
17249
  async function runStackUpdate(options) {
16885
17250
  const exec2 = options.exec ?? defaultExec;
16886
17251
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
16887
- const manifest = await loadStackManifest(options.manifestRef, options.fetchText);
17252
+ if (options.rollback) return rollbackStackUpdate(options);
17253
+ const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey);
16888
17254
  const envRaw = readFileSync25(options.envFile, "utf8");
16889
17255
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
16890
17256
  assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
@@ -16907,7 +17273,7 @@ async function runStackUpdate(options) {
16907
17273
  exec2("docker", [...composeArgs, "pull"]);
16908
17274
  exec2("docker", [...composeArgs, "up", "-d"]);
16909
17275
  await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
16910
- writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), services: plan.release.services }, null, 2) + "\n");
17276
+ writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
16911
17277
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
16912
17278
  } catch (err) {
16913
17279
  writeFileSync20(options.envFile, envRaw, { mode: 384 });
@@ -16966,7 +17332,7 @@ function defaultStackPaths() {
16966
17332
  return {
16967
17333
  composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
16968
17334
  envFile: process.env.EXE_STACK_ENV_FILE || (existsSync30(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
16969
- manifestRef: process.env.EXE_STACK_MANIFEST || "https://updates.askexe.com/stack-manifest.json"
17335
+ manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json"
16970
17336
  };
16971
17337
  }
16972
17338
  var init_stack_update = __esm({
@@ -16989,6 +17355,7 @@ function parseArgs4(args2) {
16989
17355
  envFile: defaults.envFile,
16990
17356
  dryRun: false,
16991
17357
  check: false,
17358
+ rollback: false,
16992
17359
  yes: false,
16993
17360
  allowedBreakingChangeIds: []
16994
17361
  };
@@ -17004,6 +17371,9 @@ function parseArgs4(args2) {
17004
17371
  else if (arg === "--env-file") opts.envFile = next();
17005
17372
  else if (arg.startsWith("--env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
17006
17373
  else if (arg === "--lock-file") opts.lockFile = next();
17374
+ else if (arg === "--public-key") opts.manifestPublicKey = readFileSync26(next(), "utf8");
17375
+ else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync26(arg.split("=").slice(1).join("="), "utf8");
17376
+ else if (arg === "--rollback") opts.rollback = true;
17007
17377
  else if (arg === "--dry-run") opts.dryRun = true;
17008
17378
  else if (arg === "--check") opts.check = true;
17009
17379
  else if (arg === "--yes" || arg === "-y") opts.yes = true;
@@ -17025,13 +17395,15 @@ Usage:
17025
17395
  exe-os stack-update [--manifest <path-or-url>] [--target <version>] [--yes]
17026
17396
 
17027
17397
  Options:
17028
- --manifest <ref> Stack manifest JSON path or URL (default: updates.askexe.com)
17398
+ --manifest <ref> Stack manifest JSON path or URL (default: update.askexe.com)
17029
17399
  --target <version> Stack version to install (default: manifest.latest)
17030
17400
  --compose-file <path> docker-compose.yml path (default: ./docker-compose.yml or /opt/exe-stack/docker-compose.yml)
17031
17401
  --env-file <path> .env path (default: ./.env or /opt/exe-stack/.env)
17032
17402
  --lock-file <path> Lock file path (default: beside .env)
17033
17403
  --check Print available changes only
17034
17404
  --dry-run Plan only; do not run Docker
17405
+ --public-key <path> PEM public key for signed manifest verification
17406
+ --rollback Restore latest backed-up .env and restart stack
17035
17407
  --allow-breaking <ids> Confirm breaking changes, comma-separated
17036
17408
  -y, --yes Non-interactive confirmation
17037
17409
  `);
@@ -17059,7 +17431,16 @@ function printBreaking(changes) {
17059
17431
  }
17060
17432
  async function main3() {
17061
17433
  const opts = parseArgs4(process.argv.slice(2));
17062
- const manifest = await loadStackManifest(opts.manifestRef);
17434
+ if (opts.rollback) {
17435
+ if (!opts.yes) {
17436
+ console.error("Refusing to rollback without --yes.");
17437
+ process.exit(2);
17438
+ }
17439
+ const result2 = await runStackUpdate(opts);
17440
+ console.log(`\u2705 Stack rollback attempted using backup: ${result2.backupEnvFile ?? "latest backup"}`);
17441
+ return;
17442
+ }
17443
+ const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey);
17063
17444
  const envRaw = readFileSync26(opts.envFile, "utf8");
17064
17445
  const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
17065
17446
  console.log(`Exe OS stack target: ${plan.targetVersion}`);
@@ -25494,16 +25875,16 @@ var init_useAgentLoop = __esm({
25494
25875
  }
25495
25876
  });
25496
25877
 
25497
- // src/gateway/providers/anthropic.ts
25878
+ // src/lib/providers/anthropic.ts
25498
25879
  import Anthropic from "@anthropic-ai/sdk";
25499
25880
  var AnthropicProvider;
25500
25881
  var init_anthropic = __esm({
25501
- "src/gateway/providers/anthropic.ts"() {
25882
+ "src/lib/providers/anthropic.ts"() {
25502
25883
  "use strict";
25503
25884
  AnthropicProvider = class {
25504
25885
  name;
25505
- client;
25506
25886
  defaultModel;
25887
+ client;
25507
25888
  constructor(name, config) {
25508
25889
  this.name = name;
25509
25890
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
@@ -25596,17 +25977,17 @@ var init_anthropic = __esm({
25596
25977
  }
25597
25978
  });
25598
25979
 
25599
- // src/gateway/providers/openai-compat.ts
25980
+ // src/lib/providers/openai-compat.ts
25600
25981
  import OpenAI from "openai";
25601
25982
  import { randomUUID as randomUUID5 } from "crypto";
25602
25983
  var OpenAICompatProvider;
25603
25984
  var init_openai_compat = __esm({
25604
- "src/gateway/providers/openai-compat.ts"() {
25985
+ "src/lib/providers/openai-compat.ts"() {
25605
25986
  "use strict";
25606
25987
  OpenAICompatProvider = class {
25607
25988
  name;
25608
- client;
25609
25989
  defaultModel;
25990
+ client;
25610
25991
  constructor(name, config) {
25611
25992
  this.name = name;
25612
25993
  this.defaultModel = config.defaultModel ?? "gpt-4o";
@@ -25738,7 +26119,7 @@ var init_openai_compat = __esm({
25738
26119
  }
25739
26120
  });
25740
26121
 
25741
- // src/gateway/providers/factory.ts
26122
+ // src/lib/providers/factory.ts
25742
26123
  var factory_exports = {};
25743
26124
  __export(factory_exports, {
25744
26125
  OPENCODE_ANTHROPIC_MODELS: () => OPENCODE_ANTHROPIC_MODELS,
@@ -25789,7 +26170,7 @@ function createProvider(opts) {
25789
26170
  }
25790
26171
  var OPENCODE_BASE_URL, OPENCODE_ANTHROPIC_MODELS, OPENCODE_OPENAI_MODELS, OPENAI_BASE_URL;
25791
26172
  var init_factory = __esm({
25792
- "src/gateway/providers/factory.ts"() {
26173
+ "src/lib/providers/factory.ts"() {
25793
26174
  "use strict";
25794
26175
  init_anthropic();
25795
26176
  init_openai_compat();
@@ -31434,8 +31815,8 @@ function verifyOpenCodeHooks(homeDir = os19.homedir()) {
31434
31815
  if (!existsSync32(pluginPath)) return false;
31435
31816
  try {
31436
31817
  const plugin = readFileSync28(pluginPath, "utf-8");
31437
- if (!plugin.includes("post-tool-combined.js")) return false;
31438
- if (plugin.includes('fireHook("ingest.js"') || plugin.includes('runHook("error-recall.js"')) return false;
31818
+ if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
31819
+ if (textHasLegacySplitPostToolHook(plugin)) return false;
31439
31820
  } catch {
31440
31821
  return false;
31441
31822
  }
@@ -31459,6 +31840,7 @@ var init_installer2 = __esm({
31459
31840
  "use strict";
31460
31841
  init_installer();
31461
31842
  init_plugin_template();
31843
+ init_runtime_hook_manifest();
31462
31844
  }
31463
31845
  });
31464
31846
 
@@ -31507,7 +31889,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
31507
31889
  }
31508
31890
  ]
31509
31891
  },
31510
- marker: "dist/hooks/session-start.js"
31892
+ marker: EXE_HOOKS.sessionStart
31511
31893
  },
31512
31894
  {
31513
31895
  event: "PostToolUse",
@@ -31522,7 +31904,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
31522
31904
  }
31523
31905
  ]
31524
31906
  },
31525
- marker: "dist/hooks/post-tool-combined.js"
31907
+ marker: EXE_HOOKS.postToolCombined
31526
31908
  },
31527
31909
  {
31528
31910
  event: "UserPromptSubmit",
@@ -31536,7 +31918,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
31536
31918
  }
31537
31919
  ]
31538
31920
  },
31539
- marker: "dist/hooks/prompt-submit.js"
31921
+ marker: EXE_HOOKS.promptSubmit
31540
31922
  },
31541
31923
  {
31542
31924
  event: "Stop",
@@ -31548,7 +31930,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
31548
31930
  }
31549
31931
  ]
31550
31932
  },
31551
- marker: "dist/hooks/stop.js"
31933
+ marker: EXE_HOOKS.stop
31552
31934
  },
31553
31935
  {
31554
31936
  event: "PreToolUse",
@@ -31561,21 +31943,16 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
31561
31943
  }
31562
31944
  ]
31563
31945
  },
31564
- marker: "dist/hooks/pre-tool-use.js"
31946
+ marker: EXE_HOOKS.preToolUse
31565
31947
  }
31566
31948
  ];
31567
31949
  let added = 0;
31568
31950
  let skipped = 0;
31569
- const legacyPostToolMarkers = [
31570
- "dist/hooks/ingest.js",
31571
- "dist/hooks/error-recall.js",
31572
- "dist/hooks/ingest-worker.js"
31573
- ];
31574
31951
  const postToolGroups = hooksJson.hooks["PostToolUse"];
31575
31952
  if (Array.isArray(postToolGroups)) {
31576
31953
  hooksJson.hooks["PostToolUse"] = postToolGroups.map((g) => ({
31577
31954
  ...g,
31578
- hooks: g.hooks.filter((h) => !legacyPostToolMarkers.some((marker) => h.command.includes(marker)))
31955
+ hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
31579
31956
  })).filter((g) => g.hooks.length > 0);
31580
31957
  }
31581
31958
  for (const { event, group, marker } of hooksToRegister) {
@@ -31618,11 +31995,11 @@ function verifyCodexHooks(homeDir = os20.homedir()) {
31618
31995
  }
31619
31996
  }
31620
31997
  const postToolCommands = (hooksJson.hooks.PostToolUse ?? []).flatMap((g) => g.hooks.map((h) => h.command));
31621
- if (!postToolCommands.some((cmd) => cmd.includes("dist/hooks/post-tool-combined.js"))) {
31998
+ if (!postToolCommands.some((cmd) => cmd.includes(EXE_HOOKS.postToolCombined))) {
31622
31999
  return false;
31623
32000
  }
31624
32001
  if (postToolCommands.some(
31625
- (cmd) => cmd.includes("dist/hooks/ingest.js") || cmd.includes("dist/hooks/error-recall.js") || cmd.includes("dist/hooks/ingest-worker.js")
32002
+ (cmd) => isLegacySplitPostToolCommand(cmd)
31626
32003
  )) {
31627
32004
  return false;
31628
32005
  }
@@ -31783,6 +32160,7 @@ var init_installer3 = __esm({
31783
32160
  "use strict";
31784
32161
  init_installer();
31785
32162
  init_preferences();
32163
+ init_runtime_hook_manifest();
31786
32164
  DEFAULT_CODEX_STATUS_LINE = [
31787
32165
  "model-with-reasoning",
31788
32166
  "current-dir",
@@ -32002,7 +32380,7 @@ var init_mcp_diagnostics = __esm({
32002
32380
  });
32003
32381
 
32004
32382
  // src/bin/cli.ts
32005
- import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
32383
+ import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as readdirSync11, rmSync } from "fs";
32006
32384
  import path49 from "path";
32007
32385
  import os22 from "os";
32008
32386
  var args = process.argv.slice(2);
@@ -32451,7 +32829,7 @@ async function runClaudeUninstall(flags = []) {
32451
32829
  if (existsSync35(skillsDir)) {
32452
32830
  let skillCount = 0;
32453
32831
  try {
32454
- const entries = readdirSync10(skillsDir);
32832
+ const entries = readdirSync11(skillsDir);
32455
32833
  for (const entry of entries) {
32456
32834
  if (entry.startsWith("exe")) {
32457
32835
  const fullPath = path49.join(skillsDir, entry);
@@ -32484,7 +32862,7 @@ async function runClaudeUninstall(flags = []) {
32484
32862
  if (existsSync35(agentsDir)) {
32485
32863
  let agentCount = 0;
32486
32864
  try {
32487
- const entries = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
32865
+ const entries = readdirSync11(agentsDir).filter((f) => f.endsWith(".md"));
32488
32866
  let knownNames = /* @__PURE__ */ new Set();
32489
32867
  const rosterPath = path49.join(exeOsDir, "exe-employees.json");
32490
32868
  if (existsSync35(rosterPath)) {
@@ -32512,7 +32890,7 @@ async function runClaudeUninstall(flags = []) {
32512
32890
  if (existsSync35(projectsDir)) {
32513
32891
  let projectCount = 0;
32514
32892
  try {
32515
- const projects = readdirSync10(projectsDir);
32893
+ const projects = readdirSync11(projectsDir);
32516
32894
  for (const proj of projects) {
32517
32895
  const projSettings = path49.join(projectsDir, proj, "settings.json");
32518
32896
  if (!existsSync35(projSettings)) continue;