@askexenow/exe-os 0.8.83 → 0.8.85

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 (95) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +97 -2
  5. package/dist/bin/cli.js +14350 -12518
  6. package/dist/bin/exe-agent.js +97 -88
  7. package/dist/bin/exe-assign.js +1003 -854
  8. package/dist/bin/exe-boot.js +1257 -320
  9. package/dist/bin/exe-call.js +10 -0
  10. package/dist/bin/exe-cloud.js +29 -6
  11. package/dist/bin/exe-dispatch.js +210 -34
  12. package/dist/bin/exe-doctor.js +403 -6
  13. package/dist/bin/exe-export-behaviors.js +175 -72
  14. package/dist/bin/exe-forget.js +97 -2
  15. package/dist/bin/exe-gateway.js +550 -171
  16. package/dist/bin/exe-healthcheck.js +1 -0
  17. package/dist/bin/exe-heartbeat.js +100 -5
  18. package/dist/bin/exe-kill.js +175 -72
  19. package/dist/bin/exe-launch-agent.js +189 -76
  20. package/dist/bin/exe-link.js +902 -80
  21. package/dist/bin/exe-new-employee.js +38 -8
  22. package/dist/bin/exe-pending-messages.js +96 -2
  23. package/dist/bin/exe-pending-notifications.js +97 -2
  24. package/dist/bin/exe-pending-reviews.js +98 -3
  25. package/dist/bin/exe-rename.js +564 -23
  26. package/dist/bin/exe-review.js +231 -73
  27. package/dist/bin/exe-search.js +989 -226
  28. package/dist/bin/exe-session-cleanup.js +4806 -1665
  29. package/dist/bin/exe-settings.js +20 -5
  30. package/dist/bin/exe-status.js +97 -2
  31. package/dist/bin/exe-team.js +97 -2
  32. package/dist/bin/git-sweep.js +899 -207
  33. package/dist/bin/graph-backfill.js +175 -72
  34. package/dist/bin/graph-export.js +175 -72
  35. package/dist/bin/install.js +38 -7
  36. package/dist/bin/list-providers.js +1 -0
  37. package/dist/bin/scan-tasks.js +904 -211
  38. package/dist/bin/setup.js +867 -268
  39. package/dist/bin/shard-migrate.js +175 -72
  40. package/dist/bin/update.js +1 -0
  41. package/dist/bin/wiki-sync.js +175 -72
  42. package/dist/gateway/index.js +548 -166
  43. package/dist/hooks/bug-report-worker.js +208 -23
  44. package/dist/hooks/commit-complete.js +897 -205
  45. package/dist/hooks/error-recall.js +988 -226
  46. package/dist/hooks/ingest-worker.js +1638 -1194
  47. package/dist/hooks/ingest.js +3 -0
  48. package/dist/hooks/instructions-loaded.js +707 -97
  49. package/dist/hooks/notification.js +699 -89
  50. package/dist/hooks/post-compact.js +714 -104
  51. package/dist/hooks/pre-compact.js +897 -205
  52. package/dist/hooks/pre-tool-use.js +742 -123
  53. package/dist/hooks/prompt-ingest-worker.js +242 -101
  54. package/dist/hooks/prompt-submit.js +995 -233
  55. package/dist/hooks/response-ingest-worker.js +242 -101
  56. package/dist/hooks/session-end.js +3941 -400
  57. package/dist/hooks/session-start.js +1001 -226
  58. package/dist/hooks/stop.js +725 -115
  59. package/dist/hooks/subagent-stop.js +714 -104
  60. package/dist/hooks/summary-worker.js +1964 -1330
  61. package/dist/index.js +1651 -1053
  62. package/dist/lib/cloud-sync.js +907 -86
  63. package/dist/lib/consolidation.js +2 -1
  64. package/dist/lib/database.js +642 -87
  65. package/dist/lib/db-daemon-client.js +503 -0
  66. package/dist/lib/device-registry.js +547 -7
  67. package/dist/lib/embedder.js +14 -28
  68. package/dist/lib/employee-templates.js +84 -74
  69. package/dist/lib/employees.js +9 -0
  70. package/dist/lib/exe-daemon-client.js +16 -29
  71. package/dist/lib/exe-daemon.js +1955 -922
  72. package/dist/lib/hybrid-search.js +988 -226
  73. package/dist/lib/identity.js +87 -67
  74. package/dist/lib/keychain.js +9 -1
  75. package/dist/lib/messaging.js +8 -1
  76. package/dist/lib/reminders.js +91 -74
  77. package/dist/lib/schedules.js +96 -2
  78. package/dist/lib/skill-learning.js +103 -85
  79. package/dist/lib/store.js +234 -73
  80. package/dist/lib/tasks.js +111 -22
  81. package/dist/lib/tmux-routing.js +120 -31
  82. package/dist/lib/token-spend.js +273 -0
  83. package/dist/lib/ws-client.js +11 -0
  84. package/dist/mcp/server.js +5222 -475
  85. package/dist/mcp/tools/complete-reminder.js +94 -77
  86. package/dist/mcp/tools/create-reminder.js +94 -77
  87. package/dist/mcp/tools/create-task.js +120 -22
  88. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  89. package/dist/mcp/tools/list-reminders.js +94 -77
  90. package/dist/mcp/tools/list-tasks.js +31 -1
  91. package/dist/mcp/tools/send-message.js +8 -1
  92. package/dist/mcp/tools/update-task.js +39 -10
  93. package/dist/runtime/index.js +911 -219
  94. package/dist/tui/App.js +997 -295
  95. package/package.json +6 -1
@@ -1,18 +1,7 @@
1
- // src/mcp/tools/list-reminders.ts
2
- import { z } from "zod";
3
-
4
- // src/lib/reminders.ts
5
- import crypto from "crypto";
6
-
7
- // src/lib/database.ts
8
- import { createClient } from "@libsql/client";
9
-
10
- // src/lib/employees.ts
11
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
12
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
13
- import { execSync } from "child_process";
14
- import path2 from "path";
15
- import os2 from "os";
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res) => function __init() {
3
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ };
16
5
 
17
6
  // src/lib/config.ts
18
7
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
@@ -35,79 +24,107 @@ function resolveDataDir() {
35
24
  }
36
25
  return newDir;
37
26
  }
38
- var EXE_AI_DIR = resolveDataDir();
39
- var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
40
- var MODELS_DIR = path.join(EXE_AI_DIR, "models");
41
- var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
42
- var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
43
- var CURRENT_CONFIG_VERSION = 1;
44
- var DEFAULT_CONFIG = {
45
- config_version: CURRENT_CONFIG_VERSION,
46
- dbPath: DB_PATH,
47
- modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
48
- embeddingDim: 1024,
49
- batchSize: 20,
50
- flushIntervalMs: 1e4,
51
- autoIngestion: true,
52
- autoRetrieval: true,
53
- searchMode: "hybrid",
54
- hookSearchMode: "hybrid",
55
- fileGrepEnabled: true,
56
- splashEffect: true,
57
- consolidationEnabled: true,
58
- consolidationIntervalMs: 6 * 60 * 60 * 1e3,
59
- consolidationModel: "claude-haiku-4-5-20251001",
60
- consolidationMaxCallsPerRun: 20,
61
- selfQueryRouter: true,
62
- selfQueryModel: "claude-haiku-4-5-20251001",
63
- rerankerEnabled: true,
64
- scalingRoadmap: {
65
- rerankerAutoTrigger: {
66
- enabled: true,
67
- broadQueryMinCardinality: 5e4,
68
- fetchTopK: 150,
69
- returnTopK: 5
70
- }
71
- },
72
- graphRagEnabled: true,
73
- wikiEnabled: false,
74
- wikiUrl: "",
75
- wikiApiKey: "",
76
- wikiSyncIntervalMs: 30 * 60 * 1e3,
77
- wikiWorkspaceMapping: {},
78
- wikiAutoUpdate: true,
79
- wikiAutoUpdateThreshold: 0.5,
80
- wikiAutoUpdateCreateNew: true,
81
- skillLearning: true,
82
- skillThreshold: 3,
83
- skillModel: "claude-haiku-4-5-20251001",
84
- exeHeartbeat: {
85
- enabled: true,
86
- intervalSeconds: 60,
87
- staleInProgressThresholdHours: 2
88
- },
89
- sessionLifecycle: {
90
- idleKillEnabled: true,
91
- idleKillTicksRequired: 3,
92
- idleKillIntercomAckWindowMs: 1e4,
93
- maxAutoInstances: 10
94
- },
95
- autoUpdate: {
96
- checkOnBoot: true,
97
- autoInstall: false,
98
- checkIntervalMs: 24 * 60 * 60 * 1e3
27
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG;
28
+ var init_config = __esm({
29
+ "src/lib/config.ts"() {
30
+ "use strict";
31
+ EXE_AI_DIR = resolveDataDir();
32
+ DB_PATH = path.join(EXE_AI_DIR, "memories.db");
33
+ MODELS_DIR = path.join(EXE_AI_DIR, "models");
34
+ CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
35
+ LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
36
+ CURRENT_CONFIG_VERSION = 1;
37
+ DEFAULT_CONFIG = {
38
+ config_version: CURRENT_CONFIG_VERSION,
39
+ dbPath: DB_PATH,
40
+ modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
41
+ embeddingDim: 1024,
42
+ batchSize: 20,
43
+ flushIntervalMs: 1e4,
44
+ autoIngestion: true,
45
+ autoRetrieval: true,
46
+ searchMode: "hybrid",
47
+ hookSearchMode: "hybrid",
48
+ fileGrepEnabled: true,
49
+ splashEffect: true,
50
+ consolidationEnabled: true,
51
+ consolidationIntervalMs: 6 * 60 * 60 * 1e3,
52
+ consolidationModel: "claude-haiku-4-5-20251001",
53
+ consolidationMaxCallsPerRun: 20,
54
+ selfQueryRouter: true,
55
+ selfQueryModel: "claude-haiku-4-5-20251001",
56
+ rerankerEnabled: true,
57
+ scalingRoadmap: {
58
+ rerankerAutoTrigger: {
59
+ enabled: true,
60
+ broadQueryMinCardinality: 5e4,
61
+ fetchTopK: 150,
62
+ returnTopK: 5
63
+ }
64
+ },
65
+ graphRagEnabled: true,
66
+ wikiEnabled: false,
67
+ wikiUrl: "",
68
+ wikiApiKey: "",
69
+ wikiSyncIntervalMs: 30 * 60 * 1e3,
70
+ wikiWorkspaceMapping: {},
71
+ wikiAutoUpdate: true,
72
+ wikiAutoUpdateThreshold: 0.5,
73
+ wikiAutoUpdateCreateNew: true,
74
+ skillLearning: true,
75
+ skillThreshold: 3,
76
+ skillModel: "claude-haiku-4-5-20251001",
77
+ exeHeartbeat: {
78
+ enabled: true,
79
+ intervalSeconds: 60,
80
+ staleInProgressThresholdHours: 2
81
+ },
82
+ sessionLifecycle: {
83
+ idleKillEnabled: true,
84
+ idleKillTicksRequired: 3,
85
+ idleKillIntercomAckWindowMs: 1e4,
86
+ maxAutoInstances: 10
87
+ },
88
+ autoUpdate: {
89
+ checkOnBoot: true,
90
+ autoInstall: false,
91
+ checkIntervalMs: 24 * 60 * 60 * 1e3
92
+ }
93
+ };
99
94
  }
100
- };
95
+ });
96
+
97
+ // src/mcp/tools/list-reminders.ts
98
+ import { z } from "zod";
99
+
100
+ // src/lib/reminders.ts
101
+ import crypto from "crypto";
102
+
103
+ // src/lib/database.ts
104
+ import { createClient } from "@libsql/client";
101
105
 
102
106
  // src/lib/employees.ts
107
+ init_config();
108
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
109
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
110
+ import { execSync } from "child_process";
111
+ import path2 from "path";
112
+ import os2 from "os";
103
113
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
104
114
 
105
115
  // src/lib/database.ts
106
116
  var _resilientClient = null;
117
+ var _daemonClient = null;
107
118
  function getClient() {
108
119
  if (!_resilientClient) {
109
120
  throw new Error("Database client not initialized. Call initDatabase() first.");
110
121
  }
122
+ if (process.env.EXE_IS_DAEMON === "1") {
123
+ return _resilientClient;
124
+ }
125
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
126
+ return _daemonClient;
127
+ }
111
128
  return _resilientClient;
112
129
  }
113
130
 
@@ -138,15 +138,22 @@ function getClient() {
138
138
  if (!_resilientClient) {
139
139
  throw new Error("Database client not initialized. Call initDatabase() first.");
140
140
  }
141
+ if (process.env.EXE_IS_DAEMON === "1") {
142
+ return _resilientClient;
143
+ }
144
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
145
+ return _daemonClient;
146
+ }
141
147
  return _resilientClient;
142
148
  }
143
- var _resilientClient;
149
+ var _resilientClient, _daemonClient;
144
150
  var init_database = __esm({
145
151
  "src/lib/database.ts"() {
146
152
  "use strict";
147
153
  init_db_retry();
148
154
  init_employees();
149
155
  _resilientClient = null;
156
+ _daemonClient = null;
150
157
  }
151
158
  });
152
159
 
@@ -533,9 +540,21 @@ var init_task_scope = __esm({
533
540
  // src/lib/tasks-crud.ts
534
541
  import crypto2 from "crypto";
535
542
  import path9 from "path";
543
+ import os7 from "os";
536
544
  import { execSync as execSync4 } from "child_process";
537
545
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
538
546
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
547
+ function buildKeywordIndex() {
548
+ const idx = /* @__PURE__ */ new Map();
549
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
550
+ for (const kw of keywords) {
551
+ const existing = idx.get(kw) ?? [];
552
+ existing.push(role);
553
+ idx.set(kw, existing);
554
+ }
555
+ }
556
+ return idx;
557
+ }
539
558
  async function listTasks(input) {
540
559
  const client = getClient();
541
560
  const conditions = [];
@@ -586,11 +605,22 @@ async function listTasks(input) {
586
605
  tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
587
606
  }));
588
607
  }
608
+ var LANE_KEYWORDS, KEYWORD_INDEX;
589
609
  var init_tasks_crud = __esm({
590
610
  "src/lib/tasks-crud.ts"() {
591
611
  "use strict";
592
612
  init_database();
593
613
  init_task_scope();
614
+ init_employees();
615
+ LANE_KEYWORDS = {
616
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
617
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
618
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
619
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
620
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
621
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
622
+ };
623
+ KEYWORD_INDEX = buildKeywordIndex();
594
624
  }
595
625
  });
596
626
 
@@ -163,15 +163,22 @@ function getClient() {
163
163
  if (!_resilientClient) {
164
164
  throw new Error("Database client not initialized. Call initDatabase() first.");
165
165
  }
166
+ if (process.env.EXE_IS_DAEMON === "1") {
167
+ return _resilientClient;
168
+ }
169
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
170
+ return _daemonClient;
171
+ }
166
172
  return _resilientClient;
167
173
  }
168
- var _resilientClient;
174
+ var _resilientClient, _daemonClient;
169
175
  var init_database = __esm({
170
176
  "src/lib/database.ts"() {
171
177
  "use strict";
172
178
  init_db_retry();
173
179
  init_employees();
174
180
  _resilientClient = null;
181
+ _daemonClient = null;
175
182
  }
176
183
  });
177
184
 
@@ -262,15 +262,22 @@ function getClient() {
262
262
  if (!_resilientClient) {
263
263
  throw new Error("Database client not initialized. Call initDatabase() first.");
264
264
  }
265
+ if (process.env.EXE_IS_DAEMON === "1") {
266
+ return _resilientClient;
267
+ }
268
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
269
+ return _daemonClient;
270
+ }
265
271
  return _resilientClient;
266
272
  }
267
- var _resilientClient;
273
+ var _resilientClient, _daemonClient;
268
274
  var init_database = __esm({
269
275
  "src/lib/database.ts"() {
270
276
  "use strict";
271
277
  init_db_retry();
272
278
  init_employees();
273
279
  _resilientClient = null;
280
+ _daemonClient = null;
274
281
  }
275
282
  });
276
283
 
@@ -864,6 +871,7 @@ var init_task_scope = __esm({
864
871
  // src/lib/tasks-crud.ts
865
872
  import crypto2 from "crypto";
866
873
  import path9 from "path";
874
+ import os7 from "os";
867
875
  import { execSync as execSync4 } from "child_process";
868
876
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
869
877
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
@@ -897,6 +905,17 @@ async function writeCheckpoint(input) {
897
905
  const checkpointCount = Number(countResult.rows[0]?.checkpoint_count ?? 1);
898
906
  return { checkpointCount };
899
907
  }
908
+ function buildKeywordIndex() {
909
+ const idx = /* @__PURE__ */ new Map();
910
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
911
+ for (const kw of keywords) {
912
+ const existing = idx.get(kw) ?? [];
913
+ existing.push(role);
914
+ idx.set(kw, existing);
915
+ }
916
+ }
917
+ return idx;
918
+ }
900
919
  async function resolveTask(client, identifier, scopeSession) {
901
920
  const scope = sessionScopeFilter(scopeSession);
902
921
  let result = await client.execute({
@@ -1065,7 +1084,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
1065
1084
  return { row, taskFile, now, taskId };
1066
1085
  }
1067
1086
  }
1068
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
1087
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
1069
1088
  process.stderr.write(
1070
1089
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
1071
1090
  `
@@ -1118,12 +1137,22 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
1118
1137
  }
1119
1138
  return { row, taskFile, now, taskId };
1120
1139
  }
1121
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
1140
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
1122
1141
  var init_tasks_crud = __esm({
1123
1142
  "src/lib/tasks-crud.ts"() {
1124
1143
  "use strict";
1125
1144
  init_database();
1126
1145
  init_task_scope();
1146
+ init_employees();
1147
+ LANE_KEYWORDS = {
1148
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
1149
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
1150
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
1151
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
1152
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
1153
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
1154
+ };
1155
+ KEYWORD_INDEX = buildKeywordIndex();
1127
1156
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
1128
1157
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
1129
1158
  }
@@ -1156,14 +1185,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
1156
1185
  if (parts.length >= 3 && parts[0] === "review") {
1157
1186
  const agent = parts[1];
1158
1187
  const slug = parts.slice(2).join("-");
1159
- const originalTaskFile = `exe/${agent}/${slug}.md`;
1188
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
1160
1189
  const result = await client.execute({
1161
- sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
1162
- args: [now, originalTaskFile]
1190
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
1191
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
1163
1192
  });
1164
1193
  if (result.rowsAffected > 0) {
1165
1194
  process.stderr.write(
1166
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
1195
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
1167
1196
  `
1168
1197
  );
1169
1198
  }
@@ -1691,7 +1720,7 @@ async function updateTask(input) {
1691
1720
  }
1692
1721
  const isTerminal = input.status === "done" || input.status === "needs_review";
1693
1722
  if (isTerminal) {
1694
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
1723
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
1695
1724
  if (!isCoordinator) {
1696
1725
  notifyTaskDone();
1697
1726
  }
@@ -1716,7 +1745,7 @@ async function updateTask(input) {
1716
1745
  }
1717
1746
  }
1718
1747
  }
1719
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
1748
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
1720
1749
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
1721
1750
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
1722
1751
  taskId,
@@ -1732,7 +1761,7 @@ async function updateTask(input) {
1732
1761
  });
1733
1762
  }
1734
1763
  let nextTask;
1735
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
1764
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
1736
1765
  try {
1737
1766
  nextTask = await findNextTask(String(row.assigned_to));
1738
1767
  } catch {