@askexenow/exe-os 0.8.40 → 0.8.41

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.
@@ -2109,7 +2109,7 @@ async function listTasks(input) {
2109
2109
  }
2110
2110
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2111
2111
  const result = await client.execute({
2112
- sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
2112
+ sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
2113
2113
  args: args2
2114
2114
  });
2115
2115
  return result.rows.map((r) => ({
package/dist/bin/setup.js CHANGED
@@ -20,6 +20,7 @@ var config_exports = {};
20
20
  __export(config_exports, {
21
21
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
22
22
  CONFIG_PATH: () => CONFIG_PATH,
23
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
23
24
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
24
25
  DB_PATH: () => DB_PATH,
25
26
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -175,7 +176,7 @@ async function loadConfigFrom(configPath) {
175
176
  return { ...DEFAULT_CONFIG };
176
177
  }
177
178
  }
178
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
179
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
179
180
  var init_config = __esm({
180
181
  "src/lib/config.ts"() {
181
182
  "use strict";
@@ -183,6 +184,7 @@ var init_config = __esm({
183
184
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
184
185
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
185
186
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
187
+ COO_AGENT_NAME = "exe";
186
188
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
187
189
  CURRENT_CONFIG_VERSION = 1;
188
190
  DEFAULT_CONFIG = {
@@ -1185,6 +1185,7 @@ var config_exports = {};
1185
1185
  __export(config_exports, {
1186
1186
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
1187
1187
  CONFIG_PATH: () => CONFIG_PATH,
1188
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
1188
1189
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
1189
1190
  DB_PATH: () => DB_PATH,
1190
1191
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -1340,7 +1341,7 @@ async function loadConfigFrom(configPath) {
1340
1341
  return { ...DEFAULT_CONFIG };
1341
1342
  }
1342
1343
  }
1343
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1344
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1344
1345
  var init_config = __esm({
1345
1346
  "src/lib/config.ts"() {
1346
1347
  "use strict";
@@ -1348,6 +1349,7 @@ var init_config = __esm({
1348
1349
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
1349
1350
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
1350
1351
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
1352
+ COO_AGENT_NAME = "exe";
1351
1353
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
1352
1354
  CURRENT_CONFIG_VERSION = 1;
1353
1355
  DEFAULT_CONFIG = {
@@ -4281,7 +4283,7 @@ async function listTasks(input) {
4281
4283
  }
4282
4284
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4283
4285
  const result = await client.execute({
4284
- sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
4286
+ sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
4285
4287
  args
4286
4288
  });
4287
4289
  return result.rows.map((r) => ({
@@ -4502,6 +4504,34 @@ async function listPendingReviews(limit) {
4502
4504
  });
4503
4505
  return result.rows;
4504
4506
  }
4507
+ async function cleanupOrphanedReviews() {
4508
+ const client = getClient();
4509
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4510
+ const r1 = await client.execute({
4511
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
4512
+ WHERE status = 'needs_review'
4513
+ AND assigned_by = 'system'
4514
+ AND title LIKE 'Review:%'
4515
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
4516
+ args: [now]
4517
+ });
4518
+ const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
4519
+ const r2 = await client.execute({
4520
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
4521
+ WHERE status = 'needs_review'
4522
+ AND result IS NOT NULL
4523
+ AND updated_at < ?`,
4524
+ args: [now, staleThreshold]
4525
+ });
4526
+ const total = r1.rowsAffected + r2.rowsAffected;
4527
+ if (total > 0) {
4528
+ process.stderr.write(
4529
+ `[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
4530
+ `
4531
+ );
4532
+ }
4533
+ return total;
4534
+ }
4505
4535
  function getReviewChecklist(role, agent, taskSlug) {
4506
4536
  const roleLower = role.toLowerCase();
4507
4537
  if (roleLower.includes("engineer") || roleLower === "principal engineer") {
@@ -5181,6 +5211,7 @@ var init_skill_learning = __esm({
5181
5211
  // src/lib/tasks.ts
5182
5212
  var tasks_exports = {};
5183
5213
  __export(tasks_exports, {
5214
+ cleanupOrphanedReviews: () => cleanupOrphanedReviews,
5184
5215
  countNewPendingReviewsSince: () => countNewPendingReviewsSince,
5185
5216
  countPendingReviews: () => countPendingReviews,
5186
5217
  createTask: () => createTask,
@@ -5246,6 +5277,21 @@ async function updateTask(input) {
5246
5277
  });
5247
5278
  } catch {
5248
5279
  }
5280
+ try {
5281
+ const client = getClient();
5282
+ const cascaded = await client.execute({
5283
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
5284
+ WHERE parent_task_id = ? AND status = 'needs_review'`,
5285
+ args: [now, taskId]
5286
+ });
5287
+ if (cascaded.rowsAffected > 0) {
5288
+ process.stderr.write(
5289
+ `[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
5290
+ `
5291
+ );
5292
+ }
5293
+ } catch {
5294
+ }
5249
5295
  }
5250
5296
  const isTerminal = input.status === "done" || input.status === "needs_review";
5251
5297
  if (isTerminal) {
@@ -2108,7 +2108,7 @@ async function listTasks(input) {
2108
2108
  }
2109
2109
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2110
2110
  const result = await client.execute({
2111
- sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
2111
+ sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
2112
2112
  args
2113
2113
  });
2114
2114
  return result.rows.map((r) => ({
@@ -13,6 +13,7 @@ var config_exports = {};
13
13
  __export(config_exports, {
14
14
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
15
15
  CONFIG_PATH: () => CONFIG_PATH,
16
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
16
17
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
17
18
  DB_PATH: () => DB_PATH,
18
19
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -168,7 +169,7 @@ async function loadConfigFrom(configPath) {
168
169
  return { ...DEFAULT_CONFIG };
169
170
  }
170
171
  }
171
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
172
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
172
173
  var init_config = __esm({
173
174
  "src/lib/config.ts"() {
174
175
  "use strict";
@@ -176,6 +177,7 @@ var init_config = __esm({
176
177
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
177
178
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
178
179
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
180
+ COO_AGENT_NAME = "exe";
179
181
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
180
182
  CURRENT_CONFIG_VERSION = 1;
181
183
  DEFAULT_CONFIG = {
@@ -1024,6 +1024,7 @@ var config_exports = {};
1024
1024
  __export(config_exports, {
1025
1025
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
1026
1026
  CONFIG_PATH: () => CONFIG_PATH,
1027
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
1027
1028
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
1028
1029
  DB_PATH: () => DB_PATH,
1029
1030
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -1179,7 +1180,7 @@ async function loadConfigFrom(configPath) {
1179
1180
  return { ...DEFAULT_CONFIG };
1180
1181
  }
1181
1182
  }
1182
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1183
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1183
1184
  var init_config = __esm({
1184
1185
  "src/lib/config.ts"() {
1185
1186
  "use strict";
@@ -1187,6 +1188,7 @@ var init_config = __esm({
1187
1188
  DB_PATH = path4.join(EXE_AI_DIR, "memories.db");
1188
1189
  MODELS_DIR = path4.join(EXE_AI_DIR, "models");
1189
1190
  CONFIG_PATH = path4.join(EXE_AI_DIR, "config.json");
1191
+ COO_AGENT_NAME = "exe";
1190
1192
  LEGACY_LANCE_PATH = path4.join(EXE_AI_DIR, "local.lance");
1191
1193
  CURRENT_CONFIG_VERSION = 1;
1192
1194
  DEFAULT_CONFIG = {
@@ -2479,7 +2481,7 @@ async function listTasks(input2) {
2479
2481
  }
2480
2482
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2481
2483
  const result = await client.execute({
2482
- sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
2484
+ sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
2483
2485
  args
2484
2486
  });
2485
2487
  return result.rows.map((r) => ({
@@ -3515,6 +3517,34 @@ async function listPendingReviews(limit) {
3515
3517
  });
3516
3518
  return result.rows;
3517
3519
  }
3520
+ async function cleanupOrphanedReviews() {
3521
+ const client = getClient();
3522
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3523
+ const r1 = await client.execute({
3524
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
3525
+ WHERE status = 'needs_review'
3526
+ AND assigned_by = 'system'
3527
+ AND title LIKE 'Review:%'
3528
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
3529
+ args: [now]
3530
+ });
3531
+ const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
3532
+ const r2 = await client.execute({
3533
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
3534
+ WHERE status = 'needs_review'
3535
+ AND result IS NOT NULL
3536
+ AND updated_at < ?`,
3537
+ args: [now, staleThreshold]
3538
+ });
3539
+ const total = r1.rowsAffected + r2.rowsAffected;
3540
+ if (total > 0) {
3541
+ process.stderr.write(
3542
+ `[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
3543
+ `
3544
+ );
3545
+ }
3546
+ return total;
3547
+ }
3518
3548
  function getReviewChecklist(role, agent, taskSlug) {
3519
3549
  const roleLower = role.toLowerCase();
3520
3550
  if (roleLower.includes("engineer") || roleLower === "principal engineer") {
@@ -4152,6 +4182,7 @@ var init_skill_learning = __esm({
4152
4182
  // src/lib/tasks.ts
4153
4183
  var tasks_exports = {};
4154
4184
  __export(tasks_exports, {
4185
+ cleanupOrphanedReviews: () => cleanupOrphanedReviews,
4155
4186
  countNewPendingReviewsSince: () => countNewPendingReviewsSince,
4156
4187
  countPendingReviews: () => countPendingReviews,
4157
4188
  createTask: () => createTask,
@@ -4217,6 +4248,21 @@ async function updateTask(input2) {
4217
4248
  });
4218
4249
  } catch {
4219
4250
  }
4251
+ try {
4252
+ const client = getClient();
4253
+ const cascaded = await client.execute({
4254
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
4255
+ WHERE parent_task_id = ? AND status = 'needs_review'`,
4256
+ args: [now, taskId]
4257
+ });
4258
+ if (cascaded.rowsAffected > 0) {
4259
+ process.stderr.write(
4260
+ `[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
4261
+ `
4262
+ );
4263
+ }
4264
+ } catch {
4265
+ }
4220
4266
  }
4221
4267
  const isTerminal = input2.status === "done" || input2.status === "needs_review";
4222
4268
  if (isTerminal) {
@@ -28,6 +28,7 @@ var config_exports = {};
28
28
  __export(config_exports, {
29
29
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
30
30
  CONFIG_PATH: () => CONFIG_PATH,
31
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
31
32
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
32
33
  DB_PATH: () => DB_PATH,
33
34
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -183,7 +184,7 @@ async function loadConfigFrom(configPath) {
183
184
  return { ...DEFAULT_CONFIG };
184
185
  }
185
186
  }
186
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
187
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
187
188
  var init_config = __esm({
188
189
  "src/lib/config.ts"() {
189
190
  "use strict";
@@ -191,6 +192,7 @@ var init_config = __esm({
191
192
  DB_PATH = path3.join(EXE_AI_DIR, "memories.db");
192
193
  MODELS_DIR = path3.join(EXE_AI_DIR, "models");
193
194
  CONFIG_PATH = path3.join(EXE_AI_DIR, "config.json");
195
+ COO_AGENT_NAME = "exe";
194
196
  LEGACY_LANCE_PATH = path3.join(EXE_AI_DIR, "local.lance");
195
197
  CURRENT_CONFIG_VERSION = 1;
196
198
  DEFAULT_CONFIG = {
@@ -30,6 +30,7 @@ var config_exports = {};
30
30
  __export(config_exports, {
31
31
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
32
32
  CONFIG_PATH: () => CONFIG_PATH,
33
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
33
34
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
34
35
  DB_PATH: () => DB_PATH,
35
36
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -185,7 +186,7 @@ async function loadConfigFrom(configPath) {
185
186
  return { ...DEFAULT_CONFIG };
186
187
  }
187
188
  }
188
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
189
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
189
190
  var init_config = __esm({
190
191
  "src/lib/config.ts"() {
191
192
  "use strict";
@@ -193,6 +194,7 @@ var init_config = __esm({
193
194
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
194
195
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
195
196
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
197
+ COO_AGENT_NAME = "exe";
196
198
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
197
199
  CURRENT_CONFIG_VERSION = 1;
198
200
  DEFAULT_CONFIG = {
@@ -4338,6 +4340,7 @@ var init_tasks_crud = __esm({
4338
4340
  // src/lib/tasks-review.ts
4339
4341
  var tasks_review_exports = {};
4340
4342
  __export(tasks_review_exports, {
4343
+ cleanupOrphanedReviews: () => cleanupOrphanedReviews,
4341
4344
  cleanupReviewFile: () => cleanupReviewFile,
4342
4345
  countNewPendingReviewsSince: () => countNewPendingReviewsSince,
4343
4346
  countPendingReviews: () => countPendingReviews,
@@ -4374,6 +4377,34 @@ async function listPendingReviews(limit) {
4374
4377
  });
4375
4378
  return result.rows;
4376
4379
  }
4380
+ async function cleanupOrphanedReviews() {
4381
+ const client = getClient();
4382
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4383
+ const r1 = await client.execute({
4384
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
4385
+ WHERE status = 'needs_review'
4386
+ AND assigned_by = 'system'
4387
+ AND title LIKE 'Review:%'
4388
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
4389
+ args: [now]
4390
+ });
4391
+ const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
4392
+ const r2 = await client.execute({
4393
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
4394
+ WHERE status = 'needs_review'
4395
+ AND result IS NOT NULL
4396
+ AND updated_at < ?`,
4397
+ args: [now, staleThreshold]
4398
+ });
4399
+ const total = r1.rowsAffected + r2.rowsAffected;
4400
+ if (total > 0) {
4401
+ process.stderr.write(
4402
+ `[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
4403
+ `
4404
+ );
4405
+ }
4406
+ return total;
4407
+ }
4377
4408
  function getReviewChecklist(role, agent, taskSlug) {
4378
4409
  const roleLower = role.toLowerCase();
4379
4410
  if (roleLower.includes("engineer") || roleLower === "principal engineer") {
@@ -5117,7 +5148,7 @@ ${fresh.map(
5117
5148
  }
5118
5149
  }
5119
5150
  let reviewContext = "";
5120
- if (agent.agentId === "exe" || agent.agentId === "default") {
5151
+ if (agent.agentId === COO_AGENT_NAME || agent.agentId === "default") {
5121
5152
  try {
5122
5153
  const { countPendingReviews: countPendingReviews2, countNewPendingReviewsSince: countNewPendingReviewsSince2 } = await Promise.resolve().then(() => (init_tasks_review(), tasks_review_exports));
5123
5154
  const sessionKey = getSessionKey();
@@ -919,6 +919,7 @@ var config_exports = {};
919
919
  __export(config_exports, {
920
920
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
921
921
  CONFIG_PATH: () => CONFIG_PATH,
922
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
922
923
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
923
924
  DB_PATH: () => DB_PATH,
924
925
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -1074,7 +1075,7 @@ async function loadConfigFrom(configPath) {
1074
1075
  return { ...DEFAULT_CONFIG };
1075
1076
  }
1076
1077
  }
1077
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1078
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1078
1079
  var init_config = __esm({
1079
1080
  "src/lib/config.ts"() {
1080
1081
  "use strict";
@@ -1082,6 +1083,7 @@ var init_config = __esm({
1082
1083
  DB_PATH = path3.join(EXE_AI_DIR, "memories.db");
1083
1084
  MODELS_DIR = path3.join(EXE_AI_DIR, "models");
1084
1085
  CONFIG_PATH = path3.join(EXE_AI_DIR, "config.json");
1086
+ COO_AGENT_NAME = "exe";
1085
1087
  LEGACY_LANCE_PATH = path3.join(EXE_AI_DIR, "local.lance");
1086
1088
  CURRENT_CONFIG_VERSION = 1;
1087
1089
  DEFAULT_CONFIG = {
@@ -13,6 +13,7 @@ var config_exports = {};
13
13
  __export(config_exports, {
14
14
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
15
15
  CONFIG_PATH: () => CONFIG_PATH,
16
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
16
17
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
17
18
  DB_PATH: () => DB_PATH,
18
19
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -168,7 +169,7 @@ async function loadConfigFrom(configPath) {
168
169
  return { ...DEFAULT_CONFIG };
169
170
  }
170
171
  }
171
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
172
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
172
173
  var init_config = __esm({
173
174
  "src/lib/config.ts"() {
174
175
  "use strict";
@@ -176,6 +177,7 @@ var init_config = __esm({
176
177
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
177
178
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
178
179
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
180
+ COO_AGENT_NAME = "exe";
179
181
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
180
182
  CURRENT_CONFIG_VERSION = 1;
181
183
  DEFAULT_CONFIG = {
@@ -111,7 +111,7 @@ async function loadConfig() {
111
111
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
112
112
  }
113
113
  }
114
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
114
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
115
115
  var init_config = __esm({
116
116
  "src/lib/config.ts"() {
117
117
  "use strict";
@@ -119,6 +119,7 @@ var init_config = __esm({
119
119
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
120
120
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
121
121
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
122
+ COO_AGENT_NAME = "exe";
122
123
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
123
124
  CURRENT_CONFIG_VERSION = 1;
124
125
  DEFAULT_CONFIG = {
@@ -1973,6 +1974,7 @@ function getActiveAgent() {
1973
1974
  }
1974
1975
 
1975
1976
  // src/adapters/claude/hooks/subagent-stop.ts
1977
+ init_config();
1976
1978
  if (!process.env.AGENT_ID) {
1977
1979
  process.env.AGENT_ID = "default";
1978
1980
  process.env.AGENT_ROLE = "employee";
@@ -1991,7 +1993,7 @@ process.stdin.on("end", async () => {
1991
1993
  try {
1992
1994
  JSON.parse(input);
1993
1995
  const agent = getActiveAgent();
1994
- if (agent.agentId === "exe" || agent.agentId === "default") {
1996
+ if (agent.agentId === COO_AGENT_NAME || agent.agentId === "default") {
1995
1997
  process.exit(0);
1996
1998
  }
1997
1999
  const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
@@ -1085,6 +1085,7 @@ var config_exports = {};
1085
1085
  __export(config_exports, {
1086
1086
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
1087
1087
  CONFIG_PATH: () => CONFIG_PATH,
1088
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
1088
1089
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
1089
1090
  DB_PATH: () => DB_PATH,
1090
1091
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -1240,7 +1241,7 @@ async function loadConfigFrom(configPath) {
1240
1241
  return { ...DEFAULT_CONFIG };
1241
1242
  }
1242
1243
  }
1243
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1244
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1244
1245
  var init_config = __esm({
1245
1246
  "src/lib/config.ts"() {
1246
1247
  "use strict";
@@ -1248,6 +1249,7 @@ var init_config = __esm({
1248
1249
  DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
1249
1250
  MODELS_DIR = path2.join(EXE_AI_DIR, "models");
1250
1251
  CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
1252
+ COO_AGENT_NAME = "exe";
1251
1253
  LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
1252
1254
  CURRENT_CONFIG_VERSION = 1;
1253
1255
  DEFAULT_CONFIG = {
@@ -2458,7 +2460,8 @@ __export(cloud_sync_exports, {
2458
2460
  mergeRosterFromRemote: () => mergeRosterFromRemote,
2459
2461
  recordRosterDeletion: () => recordRosterDeletion
2460
2462
  });
2461
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync3, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync3 } from "fs";
2463
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync3, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2 } from "fs";
2464
+ import crypto4 from "crypto";
2462
2465
  import path9 from "path";
2463
2466
  import { homedir } from "os";
2464
2467
  function logError(msg) {
@@ -2470,17 +2473,29 @@ function logError(msg) {
2470
2473
  }
2471
2474
  }
2472
2475
  async function withRosterLock(fn) {
2473
- if (existsSync9(ROSTER_LOCK_PATH)) {
2474
- try {
2475
- const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
2476
- if (Date.now() - ts < LOCK_STALE_MS) {
2476
+ try {
2477
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2478
+ closeSync2(fd);
2479
+ writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
2480
+ } catch (err) {
2481
+ if (err.code === "EEXIST") {
2482
+ try {
2483
+ const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
2484
+ if (Date.now() - ts < LOCK_STALE_MS) {
2485
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
2486
+ }
2487
+ unlinkSync3(ROSTER_LOCK_PATH);
2488
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2489
+ closeSync2(fd);
2490
+ writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
2491
+ } catch (retryErr) {
2492
+ if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
2477
2493
  throw new Error("Roster merge already in progress \u2014 another sync is running");
2478
2494
  }
2479
- } catch (err) {
2480
- if (err instanceof Error && err.message.includes("already in progress")) throw err;
2495
+ } else {
2496
+ throw err;
2481
2497
  }
2482
2498
  }
2483
- writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
2484
2499
  try {
2485
2500
  return await fn();
2486
2501
  } finally {
@@ -2802,7 +2817,7 @@ function buildRosterBlob(paths) {
2802
2817
  }
2803
2818
  const deletedNames = consumeRosterDeletions();
2804
2819
  const content = JSON.stringify({ roster, identities, config, deletedNames });
2805
- const hash = Buffer.from(content).length;
2820
+ const hash = crypto4.createHash("sha256").update(content).digest("hex").slice(0, 16);
2806
2821
  return { roster, identities, config, deletedNames, version: hash };
2807
2822
  }
2808
2823
  async function cloudPushRoster(config) {
@@ -3653,7 +3668,7 @@ function vectorToBlob(vector) {
3653
3668
 
3654
3669
  // src/adapters/claude/hooks/summary-worker.ts
3655
3670
  init_database();
3656
- import crypto4 from "crypto";
3671
+ import crypto5 from "crypto";
3657
3672
 
3658
3673
  // src/lib/notifications.ts
3659
3674
  init_database();
@@ -3694,7 +3709,7 @@ async function writeNotification(notification) {
3694
3709
 
3695
3710
  // src/adapters/claude/hooks/summary-worker.ts
3696
3711
  import { execSync as execSync2 } from "child_process";
3697
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
3712
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, openSync as openSync3, closeSync as closeSync3 } from "fs";
3698
3713
  import path11 from "path";
3699
3714
  async function main() {
3700
3715
  const agentId = process.env.AGENT_ID ?? "default";
@@ -3764,7 +3779,7 @@ async function main() {
3764
3779
  process.exit(0);
3765
3780
  }
3766
3781
  await writeMemory({
3767
- id: crypto4.randomUUID(),
3782
+ id: crypto5.randomUUID(),
3768
3783
  agent_id: agentId,
3769
3784
  agent_role: agentRole,
3770
3785
  session_id: `auto-summary-${Date.now()}`,
@@ -3829,14 +3844,14 @@ async function main() {
3829
3844
  const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3830
3845
  const bLogPath = path11.join(exeDir2, "workers.log");
3831
3846
  mkdirSync5(path11.dirname(bLogPath), { recursive: true });
3832
- const bLogFd = openSync2(bLogPath, "a");
3847
+ const bLogFd = openSync3(bLogPath, "a");
3833
3848
  const child = spawn2(process.execPath, [backfillPath], {
3834
3849
  detached: true,
3835
3850
  stdio: ["ignore", "ignore", bLogFd]
3836
3851
  });
3837
3852
  child.unref();
3838
3853
  try {
3839
- closeSync2(bLogFd);
3854
+ closeSync3(bLogFd);
3840
3855
  } catch {
3841
3856
  }
3842
3857
  process.stderr.write("[summary-worker] Spawned backfill job\n");
package/dist/index.js CHANGED
@@ -1274,6 +1274,7 @@ var config_exports = {};
1274
1274
  __export(config_exports, {
1275
1275
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
1276
1276
  CONFIG_PATH: () => CONFIG_PATH,
1277
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
1277
1278
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
1278
1279
  DB_PATH: () => DB_PATH,
1279
1280
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -1429,7 +1430,7 @@ async function loadConfigFrom(configPath) {
1429
1430
  return { ...DEFAULT_CONFIG };
1430
1431
  }
1431
1432
  }
1432
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1433
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
1433
1434
  var init_config = __esm({
1434
1435
  "src/lib/config.ts"() {
1435
1436
  "use strict";
@@ -1437,6 +1438,7 @@ var init_config = __esm({
1437
1438
  DB_PATH = path4.join(EXE_AI_DIR, "memories.db");
1438
1439
  MODELS_DIR = path4.join(EXE_AI_DIR, "models");
1439
1440
  CONFIG_PATH = path4.join(EXE_AI_DIR, "config.json");
1441
+ COO_AGENT_NAME = "exe";
1440
1442
  LEGACY_LANCE_PATH = path4.join(EXE_AI_DIR, "local.lance");
1441
1443
  CURRENT_CONFIG_VERSION = 1;
1442
1444
  DEFAULT_CONFIG = {
@@ -1944,7 +1946,7 @@ async function listTasks(input) {
1944
1946
  }
1945
1947
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1946
1948
  const result = await client.execute({
1947
- sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
1949
+ sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
1948
1950
  args
1949
1951
  });
1950
1952
  return result.rows.map((r) => ({
@@ -2165,6 +2167,34 @@ async function listPendingReviews(limit) {
2165
2167
  });
2166
2168
  return result.rows;
2167
2169
  }
2170
+ async function cleanupOrphanedReviews() {
2171
+ const client = getClient();
2172
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2173
+ const r1 = await client.execute({
2174
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
2175
+ WHERE status = 'needs_review'
2176
+ AND assigned_by = 'system'
2177
+ AND title LIKE 'Review:%'
2178
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
2179
+ args: [now]
2180
+ });
2181
+ const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
2182
+ const r2 = await client.execute({
2183
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
2184
+ WHERE status = 'needs_review'
2185
+ AND result IS NOT NULL
2186
+ AND updated_at < ?`,
2187
+ args: [now, staleThreshold]
2188
+ });
2189
+ const total = r1.rowsAffected + r2.rowsAffected;
2190
+ if (total > 0) {
2191
+ process.stderr.write(
2192
+ `[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
2193
+ `
2194
+ );
2195
+ }
2196
+ return total;
2197
+ }
2168
2198
  function getReviewChecklist(role, agent, taskSlug) {
2169
2199
  const roleLower = role.toLowerCase();
2170
2200
  if (roleLower.includes("engineer") || roleLower === "principal engineer") {
@@ -2906,6 +2936,7 @@ var init_skill_learning = __esm({
2906
2936
  // src/lib/tasks.ts
2907
2937
  var tasks_exports = {};
2908
2938
  __export(tasks_exports, {
2939
+ cleanupOrphanedReviews: () => cleanupOrphanedReviews,
2909
2940
  countNewPendingReviewsSince: () => countNewPendingReviewsSince,
2910
2941
  countPendingReviews: () => countPendingReviews,
2911
2942
  createTask: () => createTask,
@@ -2971,6 +3002,21 @@ async function updateTask(input) {
2971
3002
  });
2972
3003
  } catch {
2973
3004
  }
3005
+ try {
3006
+ const client = getClient();
3007
+ const cascaded = await client.execute({
3008
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
3009
+ WHERE parent_task_id = ? AND status = 'needs_review'`,
3010
+ args: [now, taskId]
3011
+ });
3012
+ if (cascaded.rowsAffected > 0) {
3013
+ process.stderr.write(
3014
+ `[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
3015
+ `
3016
+ );
3017
+ }
3018
+ } catch {
3019
+ }
2974
3020
  }
2975
3021
  const isTerminal = input.status === "done" || input.status === "needs_review";
2976
3022
  if (isTerminal) {