@askexenow/exe-os 0.9.37 → 0.9.39

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 (73) hide show
  1. package/deploy/stack-manifests/v0.9.json +55 -0
  2. package/dist/bin/backfill-conversations.js +36 -9
  3. package/dist/bin/backfill-responses.js +36 -9
  4. package/dist/bin/backfill-vectors.js +36 -9
  5. package/dist/bin/cleanup-stale-review-tasks.js +37 -10
  6. package/dist/bin/cli.js +624 -204
  7. package/dist/bin/exe-agent.js +13 -5
  8. package/dist/bin/exe-assign.js +36 -9
  9. package/dist/bin/exe-boot.js +50 -20
  10. package/dist/bin/exe-call.js +134 -342
  11. package/dist/bin/exe-dispatch.js +36 -9
  12. package/dist/bin/exe-doctor.js +39 -12
  13. package/dist/bin/exe-export-behaviors.js +38 -11
  14. package/dist/bin/exe-forget.js +36 -9
  15. package/dist/bin/exe-gateway.js +64 -15
  16. package/dist/bin/exe-heartbeat.js +37 -10
  17. package/dist/bin/exe-kill.js +36 -9
  18. package/dist/bin/exe-launch-agent.js +287 -1081
  19. package/dist/bin/exe-new-employee.js +100 -14
  20. package/dist/bin/exe-pending-messages.js +36 -9
  21. package/dist/bin/exe-pending-notifications.js +36 -9
  22. package/dist/bin/exe-pending-reviews.js +36 -9
  23. package/dist/bin/exe-rename.js +1780 -204
  24. package/dist/bin/exe-review.js +36 -9
  25. package/dist/bin/exe-search.js +38 -11
  26. package/dist/bin/exe-session-cleanup.js +38 -11
  27. package/dist/bin/exe-start-codex.js +38 -11
  28. package/dist/bin/exe-start-opencode.js +38 -11
  29. package/dist/bin/exe-status.js +37 -10
  30. package/dist/bin/exe-team.js +36 -9
  31. package/dist/bin/git-sweep.js +36 -9
  32. package/dist/bin/graph-backfill.js +36 -9
  33. package/dist/bin/graph-export.js +36 -9
  34. package/dist/bin/install.js +70 -3
  35. package/dist/bin/intercom-check.js +38 -11
  36. package/dist/bin/scan-tasks.js +36 -9
  37. package/dist/bin/setup.js +20 -19
  38. package/dist/bin/shard-migrate.js +36 -9
  39. package/dist/bin/stack-update.js +308 -0
  40. package/dist/gateway/index.js +62 -13
  41. package/dist/hooks/bug-report-worker.js +40 -12
  42. package/dist/hooks/codex-stop-task-finalizer.js +38 -11
  43. package/dist/hooks/commit-complete.js +36 -9
  44. package/dist/hooks/error-recall.js +38 -11
  45. package/dist/hooks/ingest.js +38 -10
  46. package/dist/hooks/instructions-loaded.js +44 -12
  47. package/dist/hooks/notification.js +36 -9
  48. package/dist/hooks/post-compact.js +36 -9
  49. package/dist/hooks/post-tool-combined.js +39 -12
  50. package/dist/hooks/pre-compact.js +37 -10
  51. package/dist/hooks/pre-tool-use.js +38 -10
  52. package/dist/hooks/prompt-submit.js +43 -15
  53. package/dist/hooks/session-end.js +37 -10
  54. package/dist/hooks/session-start.js +49 -16
  55. package/dist/hooks/stop.js +37 -10
  56. package/dist/hooks/subagent-stop.js +36 -9
  57. package/dist/hooks/summary-worker.js +45 -18
  58. package/dist/index.js +60 -11
  59. package/dist/lib/consolidation.js +2 -1
  60. package/dist/lib/employee-templates.js +4 -3
  61. package/dist/lib/employees.js +2 -1
  62. package/dist/lib/exe-daemon.js +11229 -10537
  63. package/dist/lib/hybrid-search.js +38 -11
  64. package/dist/lib/identity.js +8 -3
  65. package/dist/lib/schedules.js +36 -9
  66. package/dist/lib/store.js +36 -9
  67. package/dist/mcp/server.js +6873 -6249
  68. package/dist/mcp/tools/create-task.js +10 -4
  69. package/dist/runtime/index.js +36 -9
  70. package/dist/tui/App.js +42 -13
  71. package/package.json +4 -1
  72. package/stack.release.json +31 -0
  73. package/stack.release.schema.json +31 -0
@@ -546,7 +546,8 @@ function isMultiInstance(agentName, employees) {
546
546
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
547
547
  }
548
548
  function addEmployee(employees, employee) {
549
- const normalized = { ...employee, name: employee.name.toLowerCase() };
549
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
550
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
550
551
  if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
551
552
  throw new Error(`Employee '${normalized.name}' already exists`);
552
553
  }
@@ -4808,6 +4809,9 @@ function ensureDir2() {
4808
4809
  function identityPath(agentId) {
4809
4810
  return path19.join(IDENTITY_DIR2, `${agentId}.md`);
4810
4811
  }
4812
+ function sanitizeIdentityBody(body) {
4813
+ return body.replace(/<!--[\s\S]*?-->/g, "").trim();
4814
+ }
4811
4815
  function parseFrontmatter(raw) {
4812
4816
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
4813
4817
  if (!match) {
@@ -4820,11 +4824,11 @@ function parseFrontmatter(raw) {
4820
4824
  created_by: "system",
4821
4825
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
4822
4826
  },
4823
- body: raw
4827
+ body: sanitizeIdentityBody(raw)
4824
4828
  };
4825
4829
  }
4826
4830
  const yamlStr = match[1];
4827
- const body = match[2].trim();
4831
+ const body = sanitizeIdentityBody(match[2]);
4828
4832
  const fm = {};
4829
4833
  for (const line of yamlStr.split("\n")) {
4830
4834
  const kv = line.match(/^(\w+):\s*(.+)$/);
@@ -4889,7 +4893,9 @@ function listIdentities() {
4889
4893
  const summary = lines[0]?.trim().slice(0, 120) ?? identity.frontmatter.title;
4890
4894
  results.push({
4891
4895
  agentId,
4892
- title: `${identity.frontmatter.title} (${identity.frontmatter.role.toUpperCase()})`,
4896
+ // User-facing/team-facing title only. `frontmatter.role` is internal
4897
+ // routing metadata and must not leak as an external title.
4898
+ title: identity.frontmatter.title,
4893
4899
  summary
4894
4900
  });
4895
4901
  }
@@ -6984,7 +6984,7 @@ __export(shard_manager_exports, {
6984
6984
  shardExists: () => shardExists
6985
6985
  });
6986
6986
  import path20 from "path";
6987
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
6987
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4, renameSync as renameSync4, statSync as statSync2 } from "fs";
6988
6988
  import { createClient as createClient2 } from "@libsql/client";
6989
6989
  function initShardManager(encryptionKey) {
6990
6990
  _encryptionKey = encryptionKey;
@@ -7006,7 +7006,7 @@ function getShardClient(projectName) {
7006
7006
  if (!_encryptionKey) {
7007
7007
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
7008
7008
  }
7009
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
7009
+ const safeName = safeShardName(projectName);
7010
7010
  if (!safeName || safeName === "unknown") {
7011
7011
  throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
7012
7012
  }
@@ -7028,9 +7028,12 @@ function getShardClient(projectName) {
7028
7028
  return client;
7029
7029
  }
7030
7030
  function shardExists(projectName) {
7031
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
7031
+ const safeName = safeShardName(projectName);
7032
7032
  return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
7033
7033
  }
7034
+ function safeShardName(projectName) {
7035
+ return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
7036
+ }
7034
7037
  function listShards() {
7035
7038
  if (!existsSync16(SHARDS_DIR)) return [];
7036
7039
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
@@ -7124,7 +7127,8 @@ async function ensureShardSchema(client) {
7124
7127
  "ALTER TABLE memories ADD COLUMN token_cost REAL",
7125
7128
  "ALTER TABLE memories ADD COLUMN audience TEXT",
7126
7129
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
7127
- "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
7130
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
7131
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
7128
7132
  ]) {
7129
7133
  try {
7130
7134
  await client.execute(col);
@@ -7220,9 +7224,32 @@ async function ensureShardSchema(client) {
7220
7224
  }
7221
7225
  }
7222
7226
  async function getReadyShardClient(projectName) {
7223
- const client = getShardClient(projectName);
7224
- await ensureShardSchema(client);
7225
- return client;
7227
+ const safeName = safeShardName(projectName);
7228
+ let client = getShardClient(projectName);
7229
+ try {
7230
+ await ensureShardSchema(client);
7231
+ return client;
7232
+ } catch (err) {
7233
+ const message = err instanceof Error ? err.message : String(err);
7234
+ if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
7235
+ client.close();
7236
+ _shards.delete(safeName);
7237
+ _shardLastAccess.delete(safeName);
7238
+ const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
7239
+ if (existsSync16(dbPath)) {
7240
+ const stat = statSync2(dbPath);
7241
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7242
+ const archivedPath = path20.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
7243
+ renameSync4(dbPath, archivedPath);
7244
+ process.stderr.write(
7245
+ `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
7246
+ `
7247
+ );
7248
+ }
7249
+ client = getShardClient(projectName);
7250
+ await ensureShardSchema(client);
7251
+ return client;
7252
+ }
7226
7253
  }
7227
7254
  function evictLRU() {
7228
7255
  let oldest = null;
@@ -7443,7 +7470,7 @@ var init_platform_procedures = __esm({
7443
7470
  title: "MCP tools \u2014 wiki, documents, and content",
7444
7471
  domain: "tool-use",
7445
7472
  priority: "p1",
7446
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
7473
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
7447
7474
  },
7448
7475
  {
7449
7476
  title: "MCP tools \u2014 system, operations, and admin",
@@ -7461,7 +7488,7 @@ var init_platform_procedures = __esm({
7461
7488
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
7462
7489
  domain: "tool-use",
7463
7490
  priority: "p1",
7464
- 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 Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
7491
+ 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."
7465
7492
  }
7466
7493
  ];
7467
7494
  PLATFORM_PROCEDURE_TITLES = new Set(
package/dist/tui/App.js CHANGED
@@ -1071,7 +1071,8 @@ function isMultiInstance(agentName, employees) {
1071
1071
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
1072
1072
  }
1073
1073
  function addEmployee(employees, employee) {
1074
- const normalized = { ...employee, name: employee.name.toLowerCase() };
1074
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
1075
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
1075
1076
  if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
1076
1077
  throw new Error(`Employee '${normalized.name}' already exists`);
1077
1078
  }
@@ -3872,8 +3873,8 @@ async function validateLicense(apiKey, deviceId) {
3872
3873
  }
3873
3874
  function getCacheAgeMs() {
3874
3875
  try {
3875
- const { statSync: statSync2 } = __require("fs");
3876
- const s = statSync2(CACHE_PATH);
3876
+ const { statSync: statSync3 } = __require("fs");
3877
+ const s = statSync3(CACHE_PATH);
3877
3878
  return Date.now() - s.mtimeMs;
3878
3879
  } catch {
3879
3880
  return Infinity;
@@ -8635,7 +8636,7 @@ var init_platform_procedures = __esm({
8635
8636
  title: "MCP tools \u2014 wiki, documents, and content",
8636
8637
  domain: "tool-use",
8637
8638
  priority: "p1",
8638
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
8639
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
8639
8640
  },
8640
8641
  {
8641
8642
  title: "MCP tools \u2014 system, operations, and admin",
@@ -8653,7 +8654,7 @@ var init_platform_procedures = __esm({
8653
8654
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
8654
8655
  domain: "tool-use",
8655
8656
  priority: "p1",
8656
- 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 Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
8657
+ 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."
8657
8658
  }
8658
8659
  ];
8659
8660
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -11293,7 +11294,7 @@ __export(shard_manager_exports, {
11293
11294
  shardExists: () => shardExists
11294
11295
  });
11295
11296
  import path27 from "path";
11296
- import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
11297
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync4, renameSync as renameSync4, statSync as statSync2 } from "fs";
11297
11298
  import { createClient as createClient2 } from "@libsql/client";
11298
11299
  function initShardManager(encryptionKey) {
11299
11300
  _encryptionKey = encryptionKey;
@@ -11315,7 +11316,7 @@ function getShardClient(projectName) {
11315
11316
  if (!_encryptionKey) {
11316
11317
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
11317
11318
  }
11318
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
11319
+ const safeName = safeShardName(projectName);
11319
11320
  if (!safeName || safeName === "unknown") {
11320
11321
  throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
11321
11322
  }
@@ -11337,9 +11338,12 @@ function getShardClient(projectName) {
11337
11338
  return client;
11338
11339
  }
11339
11340
  function shardExists(projectName) {
11340
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
11341
+ const safeName = safeShardName(projectName);
11341
11342
  return existsSync17(path27.join(SHARDS_DIR, `${safeName}.db`));
11342
11343
  }
11344
+ function safeShardName(projectName) {
11345
+ return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
11346
+ }
11343
11347
  function listShards() {
11344
11348
  if (!existsSync17(SHARDS_DIR)) return [];
11345
11349
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
@@ -11433,7 +11437,8 @@ async function ensureShardSchema(client) {
11433
11437
  "ALTER TABLE memories ADD COLUMN token_cost REAL",
11434
11438
  "ALTER TABLE memories ADD COLUMN audience TEXT",
11435
11439
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
11436
- "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
11440
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
11441
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
11437
11442
  ]) {
11438
11443
  try {
11439
11444
  await client.execute(col);
@@ -11529,9 +11534,32 @@ async function ensureShardSchema(client) {
11529
11534
  }
11530
11535
  }
11531
11536
  async function getReadyShardClient(projectName) {
11532
- const client = getShardClient(projectName);
11533
- await ensureShardSchema(client);
11534
- return client;
11537
+ const safeName = safeShardName(projectName);
11538
+ let client = getShardClient(projectName);
11539
+ try {
11540
+ await ensureShardSchema(client);
11541
+ return client;
11542
+ } catch (err) {
11543
+ const message = err instanceof Error ? err.message : String(err);
11544
+ if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
11545
+ client.close();
11546
+ _shards.delete(safeName);
11547
+ _shardLastAccess.delete(safeName);
11548
+ const dbPath = path27.join(SHARDS_DIR, `${safeName}.db`);
11549
+ if (existsSync17(dbPath)) {
11550
+ const stat = statSync2(dbPath);
11551
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
11552
+ const archivedPath = path27.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
11553
+ renameSync4(dbPath, archivedPath);
11554
+ process.stderr.write(
11555
+ `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
11556
+ `
11557
+ );
11558
+ }
11559
+ client = getShardClient(projectName);
11560
+ await ensureShardSchema(client);
11561
+ return client;
11562
+ }
11535
11563
  }
11536
11564
  function evictLRU() {
11537
11565
  let oldest = null;
@@ -18974,7 +19002,8 @@ When you need to assign work to another employee (e.g., CTO assigns to an engine
18974
19002
  var PROCEDURES_MARKER = "EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES";
18975
19003
  function getSessionPrompt(storedPrompt) {
18976
19004
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
18977
- const rolePrompt = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
19005
+ const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
19006
+ const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
18978
19007
  const globalBlock = getGlobalProceduresBlock();
18979
19008
  return `${globalBlock}${rolePrompt}
18980
19009
  ${BASE_OPERATING_PROCEDURES}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -41,6 +41,9 @@
41
41
  "files": [
42
42
  "dist",
43
43
  "src/commands",
44
+ "deploy/stack-manifests",
45
+ "stack.release.json",
46
+ "stack.release.schema.json",
44
47
  "package.json",
45
48
  "LICENSE"
46
49
  ],
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://updates.askexe.com/schemas/stack.release.schema.json",
3
+ "schemaVersion": 1,
4
+ "repo": "AskExe/exe-os",
5
+ "service": "exed",
6
+ "packageName": "@askexenow/exe-os",
7
+ "version": "0.9.37",
8
+ "image": "ghcr.io/askexe/exed:v0.9.37",
9
+ "imageEnv": "EXED_IMAGE_TAG",
10
+ "stackParticipation": {
11
+ "required": true,
12
+ "compatibleStack": ">=0.9.0 <1.0.0",
13
+ "health": [
14
+ { "type": "http", "url": "http://127.0.0.1:8765/health" }
15
+ ],
16
+ "smokeTests": [
17
+ { "name": "exe healthcheck", "command": "exe-os healthcheck" },
18
+ { "name": "mcp startup", "command": "node dist/mcp/server.js --help", "optional": true }
19
+ ],
20
+ "migrations": {
21
+ "command": "npm run build",
22
+ "rollback": "Restore previous EXED_IMAGE_TAG and restart exed; local SQLCipher memory data is not overwritten by updates."
23
+ },
24
+ "config": {
25
+ "envExample": "deploy/compose/.env.example",
26
+ "requiredEnv": ["EXED_IMAGE_TAG", "EXED_MCP_TOKEN", "EXED_DEVICE_ID"]
27
+ },
28
+ "breakingChanges": [],
29
+ "dataSovereignty": "Customer-local memory/tasks/behaviors stay in SQLCipher/local storage. Updates must not overwrite roster, identity, behavior, or local memory files."
30
+ }
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://updates.askexe.com/schemas/stack.release.schema.json",
4
+ "title": "Exe OS stack release descriptor",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "repo", "service", "version", "stackParticipation"],
7
+ "properties": {
8
+ "schemaVersion": { "const": 1 },
9
+ "repo": { "type": "string" },
10
+ "service": { "type": "string" },
11
+ "packageName": { "type": "string" },
12
+ "version": { "type": "string" },
13
+ "image": { "type": "string" },
14
+ "imageEnv": { "type": "string" },
15
+ "components": { "type": "object" },
16
+ "stackParticipation": {
17
+ "type": "object",
18
+ "required": ["required", "compatibleStack", "health", "smokeTests", "migrations", "config", "breakingChanges", "dataSovereignty"],
19
+ "properties": {
20
+ "required": { "type": "boolean" },
21
+ "compatibleStack": { "type": "string" },
22
+ "health": { "type": "array", "items": { "type": "object" } },
23
+ "smokeTests": { "type": "array", "items": { "type": "object" } },
24
+ "migrations": { "type": "object" },
25
+ "config": { "type": "object" },
26
+ "breakingChanges": { "type": "array", "items": { "type": "object" } },
27
+ "dataSovereignty": { "type": "string" }
28
+ }
29
+ }
30
+ }
31
+ }