@askexenow/exe-os 0.9.7 → 0.9.9

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +953 -105
  2. package/dist/bin/backfill-responses.js +952 -104
  3. package/dist/bin/backfill-vectors.js +956 -108
  4. package/dist/bin/cleanup-stale-review-tasks.js +802 -58
  5. package/dist/bin/cli.js +2292 -1070
  6. package/dist/bin/exe-agent-config.js +157 -101
  7. package/dist/bin/exe-agent.js +55 -29
  8. package/dist/bin/exe-assign.js +940 -92
  9. package/dist/bin/exe-boot.js +1424 -442
  10. package/dist/bin/exe-call.js +240 -141
  11. package/dist/bin/exe-cloud.js +198 -70
  12. package/dist/bin/exe-dispatch.js +951 -192
  13. package/dist/bin/exe-doctor.js +791 -51
  14. package/dist/bin/exe-export-behaviors.js +790 -42
  15. package/dist/bin/exe-forget.js +771 -31
  16. package/dist/bin/exe-gateway.js +1592 -521
  17. package/dist/bin/exe-heartbeat.js +850 -109
  18. package/dist/bin/exe-kill.js +783 -35
  19. package/dist/bin/exe-launch-agent.js +1030 -107
  20. package/dist/bin/exe-link.js +916 -110
  21. package/dist/bin/exe-new-employee.js +526 -217
  22. package/dist/bin/exe-pending-messages.js +1046 -62
  23. package/dist/bin/exe-pending-notifications.js +1318 -111
  24. package/dist/bin/exe-pending-reviews.js +1040 -72
  25. package/dist/bin/exe-rename.js +772 -59
  26. package/dist/bin/exe-review.js +772 -32
  27. package/dist/bin/exe-search.js +982 -128
  28. package/dist/bin/exe-session-cleanup.js +1180 -306
  29. package/dist/bin/exe-settings.js +185 -105
  30. package/dist/bin/exe-start-codex.js +886 -132
  31. package/dist/bin/exe-start-opencode.js +873 -119
  32. package/dist/bin/exe-status.js +803 -59
  33. package/dist/bin/exe-team.js +772 -32
  34. package/dist/bin/git-sweep.js +1046 -223
  35. package/dist/bin/graph-backfill.js +779 -31
  36. package/dist/bin/graph-export.js +785 -37
  37. package/dist/bin/install.js +632 -200
  38. package/dist/bin/scan-tasks.js +1055 -232
  39. package/dist/bin/setup.js +1419 -320
  40. package/dist/bin/shard-migrate.js +783 -35
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +782 -34
  43. package/dist/gateway/index.js +1444 -449
  44. package/dist/hooks/bug-report-worker.js +1141 -269
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +1044 -221
  47. package/dist/hooks/error-recall.js +989 -135
  48. package/dist/hooks/exe-heartbeat-hook.js +99 -75
  49. package/dist/hooks/ingest-worker.js +4176 -3226
  50. package/dist/hooks/ingest.js +920 -168
  51. package/dist/hooks/instructions-loaded.js +874 -70
  52. package/dist/hooks/notification.js +860 -56
  53. package/dist/hooks/post-compact.js +881 -73
  54. package/dist/hooks/pre-compact.js +1050 -227
  55. package/dist/hooks/pre-tool-use.js +1084 -159
  56. package/dist/hooks/prompt-ingest-worker.js +1089 -164
  57. package/dist/hooks/prompt-submit.js +1469 -515
  58. package/dist/hooks/response-ingest-worker.js +1104 -179
  59. package/dist/hooks/session-end.js +1085 -251
  60. package/dist/hooks/session-start.js +1241 -231
  61. package/dist/hooks/stop.js +935 -109
  62. package/dist/hooks/subagent-stop.js +881 -73
  63. package/dist/hooks/summary-worker.js +1323 -307
  64. package/dist/index.js +1449 -452
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +909 -115
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +42 -9
  69. package/dist/lib/database.js +739 -33
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +2359 -0
  72. package/dist/lib/device-registry.js +760 -47
  73. package/dist/lib/embedder.js +201 -73
  74. package/dist/lib/employee-templates.js +30 -4
  75. package/dist/lib/employees.js +290 -86
  76. package/dist/lib/exe-daemon-client.js +187 -83
  77. package/dist/lib/exe-daemon.js +1696 -616
  78. package/dist/lib/hybrid-search.js +982 -128
  79. package/dist/lib/identity.js +43 -13
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +167 -80
  82. package/dist/lib/reminders.js +35 -5
  83. package/dist/lib/schedules.js +772 -32
  84. package/dist/lib/skill-learning.js +54 -7
  85. package/dist/lib/store.js +779 -31
  86. package/dist/lib/task-router.js +94 -73
  87. package/dist/lib/tasks.js +298 -225
  88. package/dist/lib/tmux-routing.js +246 -172
  89. package/dist/lib/token-spend.js +52 -14
  90. package/dist/mcp/server.js +2893 -850
  91. package/dist/mcp/tools/complete-reminder.js +35 -5
  92. package/dist/mcp/tools/create-reminder.js +35 -5
  93. package/dist/mcp/tools/create-task.js +507 -323
  94. package/dist/mcp/tools/deactivate-behavior.js +40 -10
  95. package/dist/mcp/tools/list-reminders.js +35 -5
  96. package/dist/mcp/tools/list-tasks.js +277 -104
  97. package/dist/mcp/tools/send-message.js +129 -56
  98. package/dist/mcp/tools/update-task.js +1864 -188
  99. package/dist/runtime/index.js +1083 -259
  100. package/dist/tui/App.js +1501 -434
  101. package/package.json +3 -2
@@ -1,6 +1,47 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // src/lib/secure-files.ts
18
+ import { chmodSync, existsSync, mkdirSync } from "fs";
19
+ import { chmod, mkdir } from "fs/promises";
20
+ function ensurePrivateDirSync(dirPath) {
21
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
22
+ try {
23
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
24
+ } catch {
25
+ }
26
+ }
27
+ function enforcePrivateFileSync(filePath) {
28
+ try {
29
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
30
+ } catch {
31
+ }
32
+ }
33
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
34
+ var init_secure_files = __esm({
35
+ "src/lib/secure-files.ts"() {
36
+ "use strict";
37
+ PRIVATE_DIR_MODE = 448;
38
+ PRIVATE_FILE_MODE = 384;
39
+ }
40
+ });
41
+
1
42
  // src/lib/config.ts
2
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
3
- import { readFileSync, existsSync, renameSync } from "fs";
43
+ import { readFile, writeFile } from "fs/promises";
44
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
4
45
  import path from "path";
5
46
  import os from "os";
6
47
  function resolveDataDir() {
@@ -8,7 +49,7 @@ function resolveDataDir() {
8
49
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
9
50
  const newDir = path.join(os.homedir(), ".exe-os");
10
51
  const legacyDir = path.join(os.homedir(), ".exe-mem");
11
- if (!existsSync(newDir) && existsSync(legacyDir)) {
52
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
12
53
  try {
13
54
  renameSync(legacyDir, newDir);
14
55
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -19,69 +60,6 @@ function resolveDataDir() {
19
60
  }
20
61
  return newDir;
21
62
  }
22
- var EXE_AI_DIR = resolveDataDir();
23
- var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
24
- var MODELS_DIR = path.join(EXE_AI_DIR, "models");
25
- var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
26
- var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
27
- var CURRENT_CONFIG_VERSION = 1;
28
- var DEFAULT_CONFIG = {
29
- config_version: CURRENT_CONFIG_VERSION,
30
- dbPath: DB_PATH,
31
- modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
32
- embeddingDim: 1024,
33
- batchSize: 20,
34
- flushIntervalMs: 1e4,
35
- autoIngestion: true,
36
- autoRetrieval: true,
37
- searchMode: "hybrid",
38
- hookSearchMode: "hybrid",
39
- fileGrepEnabled: true,
40
- splashEffect: true,
41
- consolidationEnabled: true,
42
- consolidationIntervalMs: 6 * 60 * 60 * 1e3,
43
- consolidationModel: "claude-haiku-4-5-20251001",
44
- consolidationMaxCallsPerRun: 20,
45
- selfQueryRouter: true,
46
- selfQueryModel: "claude-haiku-4-5-20251001",
47
- rerankerEnabled: true,
48
- scalingRoadmap: {
49
- rerankerAutoTrigger: {
50
- enabled: true,
51
- broadQueryMinCardinality: 5e4,
52
- fetchTopK: 150,
53
- returnTopK: 5
54
- }
55
- },
56
- graphRagEnabled: true,
57
- wikiEnabled: false,
58
- wikiUrl: "",
59
- wikiApiKey: "",
60
- wikiSyncIntervalMs: 30 * 60 * 1e3,
61
- wikiWorkspaceMapping: {},
62
- wikiAutoUpdate: true,
63
- wikiAutoUpdateThreshold: 0.5,
64
- wikiAutoUpdateCreateNew: true,
65
- skillLearning: true,
66
- skillThreshold: 3,
67
- skillModel: "claude-haiku-4-5-20251001",
68
- exeHeartbeat: {
69
- enabled: true,
70
- intervalSeconds: 60,
71
- staleInProgressThresholdHours: 2
72
- },
73
- sessionLifecycle: {
74
- idleKillEnabled: true,
75
- idleKillTicksRequired: 3,
76
- idleKillIntercomAckWindowMs: 1e4,
77
- maxAutoInstances: 10
78
- },
79
- autoUpdate: {
80
- checkOnBoot: true,
81
- autoInstall: false,
82
- checkIntervalMs: 24 * 60 * 60 * 1e3
83
- }
84
- };
85
63
  function migrateLegacyConfig(raw) {
86
64
  if ("r2" in raw) {
87
65
  process.stderr.write(
@@ -94,16 +72,6 @@ function migrateLegacyConfig(raw) {
94
72
  }
95
73
  return raw;
96
74
  }
97
- var CONFIG_MIGRATIONS = [
98
- {
99
- from: 0,
100
- to: 1,
101
- migrate: (cfg) => {
102
- cfg.config_version = 1;
103
- return cfg;
104
- }
105
- }
106
- ];
107
75
  function migrateConfig(raw) {
108
76
  const fromVersion = typeof raw.config_version === "number" ? raw.config_version : 0;
109
77
  let currentVersion = fromVersion;
@@ -145,7 +113,7 @@ function normalizeAutoUpdate(raw) {
145
113
  function loadConfigSync() {
146
114
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
147
115
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
116
+ if (!existsSync2(configPath)) {
149
117
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
118
  }
151
119
  try {
@@ -161,17 +129,784 @@ function loadConfigSync() {
161
129
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
162
130
  }
163
131
  }
132
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
133
+ var init_config = __esm({
134
+ "src/lib/config.ts"() {
135
+ "use strict";
136
+ init_secure_files();
137
+ EXE_AI_DIR = resolveDataDir();
138
+ DB_PATH = path.join(EXE_AI_DIR, "memories.db");
139
+ MODELS_DIR = path.join(EXE_AI_DIR, "models");
140
+ CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
141
+ LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
142
+ CURRENT_CONFIG_VERSION = 1;
143
+ DEFAULT_CONFIG = {
144
+ config_version: CURRENT_CONFIG_VERSION,
145
+ dbPath: DB_PATH,
146
+ modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
147
+ embeddingDim: 1024,
148
+ batchSize: 20,
149
+ flushIntervalMs: 1e4,
150
+ autoIngestion: true,
151
+ autoRetrieval: true,
152
+ searchMode: "hybrid",
153
+ hookSearchMode: "hybrid",
154
+ fileGrepEnabled: true,
155
+ splashEffect: true,
156
+ consolidationEnabled: true,
157
+ consolidationIntervalMs: 6 * 60 * 60 * 1e3,
158
+ consolidationModel: "claude-haiku-4-5-20251001",
159
+ consolidationMaxCallsPerRun: 20,
160
+ selfQueryRouter: true,
161
+ selfQueryModel: "claude-haiku-4-5-20251001",
162
+ rerankerEnabled: true,
163
+ scalingRoadmap: {
164
+ rerankerAutoTrigger: {
165
+ enabled: true,
166
+ broadQueryMinCardinality: 5e4,
167
+ fetchTopK: 150,
168
+ returnTopK: 5
169
+ }
170
+ },
171
+ graphRagEnabled: true,
172
+ wikiEnabled: false,
173
+ wikiUrl: "",
174
+ wikiApiKey: "",
175
+ wikiSyncIntervalMs: 30 * 60 * 1e3,
176
+ wikiWorkspaceMapping: {},
177
+ wikiAutoUpdate: true,
178
+ wikiAutoUpdateThreshold: 0.5,
179
+ wikiAutoUpdateCreateNew: true,
180
+ skillLearning: true,
181
+ skillThreshold: 3,
182
+ skillModel: "claude-haiku-4-5-20251001",
183
+ exeHeartbeat: {
184
+ enabled: true,
185
+ intervalSeconds: 60,
186
+ staleInProgressThresholdHours: 2
187
+ },
188
+ sessionLifecycle: {
189
+ idleKillEnabled: true,
190
+ idleKillTicksRequired: 3,
191
+ idleKillIntercomAckWindowMs: 1e4,
192
+ maxAutoInstances: 10
193
+ },
194
+ autoUpdate: {
195
+ checkOnBoot: true,
196
+ autoInstall: false,
197
+ checkIntervalMs: 24 * 60 * 60 * 1e3
198
+ }
199
+ };
200
+ CONFIG_MIGRATIONS = [
201
+ {
202
+ from: 0,
203
+ to: 1,
204
+ migrate: (cfg) => {
205
+ cfg.config_version = 1;
206
+ return cfg;
207
+ }
208
+ }
209
+ ];
210
+ }
211
+ });
212
+
213
+ // src/lib/runtime-table.ts
214
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
215
+ var init_runtime_table = __esm({
216
+ "src/lib/runtime-table.ts"() {
217
+ "use strict";
218
+ RUNTIME_TABLE = {
219
+ codex: {
220
+ binary: "codex",
221
+ launchMode: "interactive",
222
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
223
+ inlineFlag: "--no-alt-screen",
224
+ apiKeyEnv: "OPENAI_API_KEY",
225
+ defaultModel: "gpt-5.4"
226
+ },
227
+ opencode: {
228
+ binary: "opencode",
229
+ launchMode: "exec",
230
+ autoApproveFlag: "--dangerously-skip-permissions",
231
+ inlineFlag: "",
232
+ apiKeyEnv: "ANTHROPIC_API_KEY",
233
+ defaultModel: "anthropic/claude-sonnet-4-6"
234
+ }
235
+ };
236
+ DEFAULT_RUNTIME = "claude";
237
+ }
238
+ });
239
+
240
+ // src/lib/agent-config.ts
241
+ var agent_config_exports = {};
242
+ __export(agent_config_exports, {
243
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
244
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
245
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
246
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
247
+ clearAgentRuntime: () => clearAgentRuntime,
248
+ getAgentRuntime: () => getAgentRuntime,
249
+ loadAgentConfig: () => loadAgentConfig,
250
+ saveAgentConfig: () => saveAgentConfig,
251
+ setAgentRuntime: () => setAgentRuntime
252
+ });
253
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
254
+ import path2 from "path";
255
+ function loadAgentConfig() {
256
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
257
+ try {
258
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
259
+ } catch {
260
+ return {};
261
+ }
262
+ }
263
+ function saveAgentConfig(config) {
264
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
265
+ ensurePrivateDirSync(dir);
266
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
267
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
268
+ }
269
+ function getAgentRuntime(agentId) {
270
+ const config = loadAgentConfig();
271
+ const entry = config[agentId];
272
+ if (entry) return entry;
273
+ const orgDefault = config["default"];
274
+ if (orgDefault) return orgDefault;
275
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
276
+ }
277
+ function setAgentRuntime(agentId, runtime, model) {
278
+ const knownModels = KNOWN_RUNTIMES[runtime];
279
+ if (!knownModels) {
280
+ return {
281
+ ok: false,
282
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
283
+ };
284
+ }
285
+ if (!knownModels.includes(model)) {
286
+ return {
287
+ ok: false,
288
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
289
+ };
290
+ }
291
+ const config = loadAgentConfig();
292
+ config[agentId] = { runtime, model };
293
+ saveAgentConfig(config);
294
+ return { ok: true };
295
+ }
296
+ function clearAgentRuntime(agentId) {
297
+ const config = loadAgentConfig();
298
+ delete config[agentId];
299
+ saveAgentConfig(config);
300
+ }
301
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
302
+ var init_agent_config = __esm({
303
+ "src/lib/agent-config.ts"() {
304
+ "use strict";
305
+ init_config();
306
+ init_runtime_table();
307
+ init_secure_files();
308
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
309
+ KNOWN_RUNTIMES = {
310
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
311
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
312
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
313
+ };
314
+ RUNTIME_LABELS = {
315
+ claude: "Claude Code (Anthropic)",
316
+ codex: "Codex (OpenAI)",
317
+ opencode: "OpenCode (open source)"
318
+ };
319
+ DEFAULT_MODELS = {
320
+ claude: "claude-opus-4",
321
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
322
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
323
+ };
324
+ }
325
+ });
326
+
327
+ // src/lib/employees.ts
328
+ var employees_exports = {};
329
+ __export(employees_exports, {
330
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
331
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
332
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
333
+ addEmployee: () => addEmployee,
334
+ baseAgentName: () => baseAgentName,
335
+ canCoordinate: () => canCoordinate,
336
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
337
+ getCoordinatorName: () => getCoordinatorName,
338
+ getEmployee: () => getEmployee,
339
+ getEmployeeByRole: () => getEmployeeByRole,
340
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
341
+ hasRole: () => hasRole,
342
+ hireEmployee: () => hireEmployee,
343
+ isCoordinatorName: () => isCoordinatorName,
344
+ isCoordinatorRole: () => isCoordinatorRole,
345
+ isMultiInstance: () => isMultiInstance,
346
+ loadEmployees: () => loadEmployees,
347
+ loadEmployeesSync: () => loadEmployeesSync,
348
+ normalizeRole: () => normalizeRole,
349
+ normalizeRosterCase: () => normalizeRosterCase,
350
+ registerBinSymlinks: () => registerBinSymlinks,
351
+ saveEmployees: () => saveEmployees,
352
+ validateEmployeeName: () => validateEmployeeName
353
+ });
354
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
355
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
356
+ import { execSync as execSync2 } from "child_process";
357
+ import path3 from "path";
358
+ import os2 from "os";
359
+ function normalizeRole(role) {
360
+ return (role ?? "").trim().toLowerCase();
361
+ }
362
+ function isCoordinatorRole(role) {
363
+ return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
364
+ }
365
+ function getCoordinatorEmployee(employees) {
366
+ return employees.find((e) => isCoordinatorRole(e.role));
367
+ }
368
+ function getCoordinatorName(employees = loadEmployeesSync()) {
369
+ return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
370
+ }
371
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
372
+ if (!agentName) return false;
373
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
374
+ }
375
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
376
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
377
+ }
378
+ function validateEmployeeName(name) {
379
+ if (!name) {
380
+ return { valid: false, error: "Name is required" };
381
+ }
382
+ if (name.length > 32) {
383
+ return { valid: false, error: "Name must be 32 characters or fewer" };
384
+ }
385
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
386
+ return {
387
+ valid: false,
388
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
389
+ };
390
+ }
391
+ return { valid: true };
392
+ }
393
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
394
+ if (!existsSync4(employeesPath)) {
395
+ return [];
396
+ }
397
+ const raw = await readFile2(employeesPath, "utf-8");
398
+ try {
399
+ return JSON.parse(raw);
400
+ } catch {
401
+ return [];
402
+ }
403
+ }
404
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
405
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
406
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
407
+ }
408
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
409
+ if (!existsSync4(employeesPath)) return [];
410
+ try {
411
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
412
+ } catch {
413
+ return [];
414
+ }
415
+ }
416
+ function getEmployee(employees, name) {
417
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
418
+ }
419
+ function getEmployeeByRole(employees, role) {
420
+ const lower = role.toLowerCase();
421
+ return employees.find((e) => e.role.toLowerCase() === lower);
422
+ }
423
+ function getEmployeeNamesByRole(employees, role) {
424
+ const lower = role.toLowerCase();
425
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
426
+ }
427
+ function hasRole(agentName, role) {
428
+ const employees = loadEmployeesSync();
429
+ const emp = getEmployee(employees, agentName);
430
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
431
+ }
432
+ function baseAgentName(name, employees) {
433
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
434
+ if (!match) return name;
435
+ const base = match[1];
436
+ const roster = employees ?? loadEmployeesSync();
437
+ if (getEmployee(roster, base)) return base;
438
+ return name;
439
+ }
440
+ function isMultiInstance(agentName, employees) {
441
+ const roster = employees ?? loadEmployeesSync();
442
+ const emp = getEmployee(roster, agentName);
443
+ if (!emp) return false;
444
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
445
+ }
446
+ function addEmployee(employees, employee) {
447
+ const normalized = { ...employee, name: employee.name.toLowerCase() };
448
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
449
+ throw new Error(`Employee '${normalized.name}' already exists`);
450
+ }
451
+ return [...employees, normalized];
452
+ }
453
+ function appendToCoordinatorTeam(employee) {
454
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
455
+ if (!coordinator) return;
456
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
457
+ if (!existsSync4(idPath)) return;
458
+ const content = readFileSync3(idPath, "utf-8");
459
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
460
+ const teamMatch = content.match(TEAM_SECTION_RE);
461
+ if (!teamMatch || teamMatch.index === void 0) return;
462
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
463
+ const nextHeading = afterTeam.match(/\n## /);
464
+ const entry = `
465
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
466
+ `;
467
+ let updated;
468
+ if (nextHeading && nextHeading.index !== void 0) {
469
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
470
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
471
+ } else {
472
+ updated = content.trimEnd() + "\n" + entry;
473
+ }
474
+ writeFileSync2(idPath, updated, "utf-8");
475
+ }
476
+ function capitalize(s) {
477
+ return s.charAt(0).toUpperCase() + s.slice(1);
478
+ }
479
+ async function hireEmployee(employee) {
480
+ const employees = await loadEmployees();
481
+ const updated = addEmployee(employees, employee);
482
+ await saveEmployees(updated);
483
+ try {
484
+ appendToCoordinatorTeam(employee);
485
+ } catch {
486
+ }
487
+ try {
488
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
489
+ const config = loadAgentConfig2();
490
+ const name = employee.name.toLowerCase();
491
+ if (!config[name] && config["default"]) {
492
+ config[name] = { ...config["default"] };
493
+ saveAgentConfig2(config);
494
+ }
495
+ } catch {
496
+ }
497
+ return updated;
498
+ }
499
+ async function normalizeRosterCase(rosterPath) {
500
+ const employees = await loadEmployees(rosterPath);
501
+ let changed = false;
502
+ for (const emp of employees) {
503
+ if (emp.name !== emp.name.toLowerCase()) {
504
+ const oldName = emp.name;
505
+ emp.name = emp.name.toLowerCase();
506
+ changed = true;
507
+ try {
508
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
509
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
510
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
511
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
512
+ renameSync2(oldPath, newPath);
513
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
514
+ const content = readFileSync3(oldPath, "utf-8");
515
+ writeFileSync2(newPath, content, "utf-8");
516
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
517
+ unlinkSync(oldPath);
518
+ }
519
+ }
520
+ } catch {
521
+ }
522
+ }
523
+ }
524
+ if (changed) {
525
+ await saveEmployees(employees, rosterPath);
526
+ }
527
+ return changed;
528
+ }
529
+ function findExeBin() {
530
+ try {
531
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ function registerBinSymlinks(name) {
537
+ const created = [];
538
+ const skipped = [];
539
+ const errors = [];
540
+ const exeBinPath = findExeBin();
541
+ if (!exeBinPath) {
542
+ errors.push("Could not find 'exe-os' in PATH");
543
+ return { created, skipped, errors };
544
+ }
545
+ const binDir = path3.dirname(exeBinPath);
546
+ let target;
547
+ try {
548
+ target = readlinkSync(exeBinPath);
549
+ } catch {
550
+ errors.push("Could not read 'exe' symlink");
551
+ return { created, skipped, errors };
552
+ }
553
+ for (const suffix of ["", "-opencode"]) {
554
+ const linkName = `${name}${suffix}`;
555
+ const linkPath = path3.join(binDir, linkName);
556
+ if (existsSync4(linkPath)) {
557
+ skipped.push(linkName);
558
+ continue;
559
+ }
560
+ try {
561
+ symlinkSync(target, linkPath);
562
+ created.push(linkName);
563
+ } catch (err) {
564
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
565
+ }
566
+ }
567
+ return { created, skipped, errors };
568
+ }
569
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
570
+ var init_employees = __esm({
571
+ "src/lib/employees.ts"() {
572
+ "use strict";
573
+ init_config();
574
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
575
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
576
+ COORDINATOR_ROLE = "COO";
577
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
578
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
579
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
580
+ }
581
+ });
582
+
583
+ // src/lib/mcp-prefix.ts
584
+ function isExeMcpTool(toolName) {
585
+ if (!toolName) return false;
586
+ return MCP_TOOL_PREFIXES.some((p) => toolName.startsWith(p));
587
+ }
588
+ function stripExeMcpPrefix(toolName) {
589
+ for (const p of MCP_TOOL_PREFIXES) {
590
+ if (toolName.startsWith(p)) return toolName.slice(p.length);
591
+ }
592
+ return toolName;
593
+ }
594
+ var MCP_PRIMARY_KEY, MCP_LEGACY_KEY, MCP_TOOL_PREFIXES;
595
+ var init_mcp_prefix = __esm({
596
+ "src/lib/mcp-prefix.ts"() {
597
+ "use strict";
598
+ MCP_PRIMARY_KEY = "exe-os";
599
+ MCP_LEGACY_KEY = "exe-mem";
600
+ MCP_TOOL_PREFIXES = [
601
+ `mcp__${MCP_PRIMARY_KEY}__`,
602
+ `mcp__${MCP_LEGACY_KEY}__`
603
+ ];
604
+ }
605
+ });
606
+
607
+ // src/lib/content-extractor.ts
608
+ var content_extractor_exports = {};
609
+ __export(content_extractor_exports, {
610
+ extractSemanticText: () => extractSemanticText
611
+ });
612
+ function extractSemanticText(toolName, toolInput, toolResponse) {
613
+ switch (toolName) {
614
+ case "Write":
615
+ return extractWrite(toolInput);
616
+ case "Edit":
617
+ return extractEdit(toolInput);
618
+ case "Read":
619
+ return extractRead(toolInput, toolResponse);
620
+ case "Bash":
621
+ return extractBash(toolInput, toolResponse);
622
+ case "Grep":
623
+ return extractGrep(toolInput, toolResponse);
624
+ case "Glob":
625
+ return extractGlob(toolInput, toolResponse);
626
+ default:
627
+ if (isExeMcpTool(toolName)) {
628
+ return extractExeMemMcp(toolName, toolInput, toolResponse);
629
+ }
630
+ if (toolName.startsWith("mcp__")) {
631
+ return extractGenericMcp(toolName, toolInput, toolResponse);
632
+ }
633
+ return extractDefault(toolName, toolInput, toolResponse);
634
+ }
635
+ }
636
+ function findContainingChunk(filePath, snippet) {
637
+ try {
638
+ const ext = filePath.split(".").pop()?.toLowerCase();
639
+ if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
640
+ const { readFileSync: readFileSync7 } = __require("fs");
641
+ const source = readFileSync7(filePath, "utf8");
642
+ const lines = source.split("\n");
643
+ const lowerSnippet = snippet.toLowerCase().slice(0, 80);
644
+ let matchLine = -1;
645
+ for (let i = 0; i < lines.length; i++) {
646
+ if (lines[i].toLowerCase().includes(lowerSnippet)) {
647
+ matchLine = i;
648
+ break;
649
+ }
650
+ }
651
+ if (matchLine === -1) return "";
652
+ for (let i = matchLine; i >= 0; i--) {
653
+ const line = lines[i];
654
+ const fnMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)/);
655
+ if (fnMatch) return `Function ${fnMatch[1]} in ${filePath}:${i + 1}`;
656
+ const classMatch = line.match(/(?:export\s+)?class\s+(\w+)/);
657
+ if (classMatch) return `Class ${classMatch[1]} in ${filePath}:${i + 1}`;
658
+ const arrowMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(/);
659
+ if (arrowMatch) return `Function ${arrowMatch[1]} in ${filePath}:${i + 1}`;
660
+ }
661
+ } catch {
662
+ }
663
+ return "";
664
+ }
665
+ function extractWrite(input2) {
666
+ const filePath = String(input2.file_path ?? "");
667
+ const content = String(input2.content ?? "");
668
+ const chunkContext = findContainingChunk(filePath, content.slice(0, 200));
669
+ const prefix = chunkContext ? `Wrote ${filePath} (${chunkContext})` : `Wrote ${filePath}`;
670
+ return `${prefix}
671
+ ${content.slice(0, MAX_CONTENT)}`;
672
+ }
673
+ function extractEdit(input2) {
674
+ const filePath = String(input2.file_path ?? "");
675
+ const oldStr = String(input2.old_string ?? "");
676
+ const newStr = String(input2.new_string ?? "");
677
+ const chunkContext = findContainingChunk(filePath, oldStr.slice(0, 200));
678
+ const prefix = chunkContext ? `Edited ${filePath} (${chunkContext})` : `Edited ${filePath}`;
679
+ return `${prefix}
680
+ Removed: ${oldStr.slice(0, MAX_CONTENT / 2)}
681
+ Added: ${newStr.slice(0, MAX_CONTENT / 2)}`;
682
+ }
683
+ function extractRead(input2, response) {
684
+ const filePath = String(input2.file_path ?? "");
685
+ const file = response.file;
686
+ const content = file ? String(file.content ?? "") : "";
687
+ if (!content) {
688
+ const text = String(response.text ?? response.content ?? "");
689
+ return `Read ${filePath}
690
+ ${text.slice(0, MAX_CONTENT)}`;
691
+ }
692
+ return `Read ${filePath}
693
+ ${content.slice(0, MAX_CONTENT)}`;
694
+ }
695
+ function extractBash(input2, response) {
696
+ const command = String(input2.command ?? "");
697
+ const description = input2.description ? String(input2.description) : "";
698
+ const stdout = String(response.stdout ?? response.text ?? "");
699
+ const stderr = String(response.stderr ?? "");
700
+ const parts = [description ? `${description}: ${command}` : `Ran: ${command}`];
701
+ if (stdout) parts.push(`Output: ${stdout.slice(0, MAX_OUTPUT)}`);
702
+ if (stderr && !stdout) parts.push(`Error: ${stderr.slice(0, MAX_OUTPUT)}`);
703
+ return parts.join("\n");
704
+ }
705
+ function extractGrep(input2, response) {
706
+ const pattern = String(input2.pattern ?? "");
707
+ const path9 = input2.path ? String(input2.path) : "";
708
+ const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
709
+ return `Searched for "${pattern}"${path9 ? ` in ${path9}` : ""}
710
+ ${output.slice(0, MAX_OUTPUT)}`;
711
+ }
712
+ function extractGlob(input2, response) {
713
+ const pattern = String(input2.pattern ?? "");
714
+ const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
715
+ return `Found files matching "${pattern}"
716
+ ${output.slice(0, MAX_OUTPUT)}`;
717
+ }
718
+ function extractExeMemMcp(toolName, input2, response) {
719
+ const shortName = stripExeMcpPrefix(toolName);
720
+ switch (shortName) {
721
+ case "store_memory": {
722
+ const text = String(input2.text ?? input2.query ?? "");
723
+ return `Stored memory: ${text.slice(0, MAX_CONTENT)}`;
724
+ }
725
+ case "recall_my_memory":
726
+ case "ask_team_memory": {
727
+ const query = String(input2.query ?? "");
728
+ const member = input2.team_member ? ` (from ${input2.team_member})` : "";
729
+ const resultText = extractResponseText(response);
730
+ return `Memory search${member}: "${query}"
731
+ ${resultText.slice(0, MAX_OUTPUT)}`;
732
+ }
733
+ case "create_task": {
734
+ const title = String(input2.title ?? "");
735
+ const assignedTo = String(input2.assigned_to ?? "");
736
+ const priority = String(input2.priority ?? "p1");
737
+ const context = String(input2.context ?? "");
738
+ return `Task created: "${title}" assigned to ${assignedTo} [${priority}]
739
+ ${context.slice(0, MAX_CONTENT)}`;
740
+ }
741
+ case "update_task": {
742
+ const taskId = String(input2.task_id ?? "");
743
+ const status = String(input2.status ?? "");
744
+ const result = input2.result ? String(input2.result) : "";
745
+ return `Task updated: ${taskId} \u2192 ${status}${result ? `
746
+ Result: ${result.slice(0, MAX_CONTENT)}` : ""}`;
747
+ }
748
+ case "list_tasks": {
749
+ const resultText = extractResponseText(response);
750
+ return `Listed tasks
751
+ ${resultText.slice(0, MAX_OUTPUT)}`;
752
+ }
753
+ default: {
754
+ return extractGenericMcp(toolName, input2, response);
755
+ }
756
+ }
757
+ }
758
+ function extractGenericMcp(toolName, input2, response) {
759
+ const shortName = toolName.replace(/^mcp__[^_]+__/, "");
760
+ const inputParts = Object.entries(input2).filter(([, v]) => v != null && String(v).length > 0).map(([k, v]) => `${k}: ${String(v).slice(0, 200)}`).join(", ");
761
+ const resultText = extractResponseText(response);
762
+ return `${shortName}(${inputParts})
763
+ ${resultText.slice(0, MAX_OUTPUT)}`;
764
+ }
765
+ function extractDefault(toolName, input2, response) {
766
+ const inputStr = JSON.stringify(input2);
767
+ const resultText = extractResponseText(response);
768
+ return `Tool: ${toolName}
769
+ ${inputStr.slice(0, MAX_CONTENT / 2)}
770
+ ${resultText.slice(0, MAX_OUTPUT)}`;
771
+ }
772
+ function extractResponseText(response) {
773
+ if (typeof response.text === "string") return response.text;
774
+ if (typeof response.content === "string") return response.content;
775
+ if (Array.isArray(response.content)) {
776
+ return response.content.map((block) => {
777
+ if (typeof block === "object" && block !== null && "text" in block) {
778
+ return String(block.text);
779
+ }
780
+ return "";
781
+ }).filter(Boolean).join("\n");
782
+ }
783
+ if (Array.isArray(response)) {
784
+ return response.map((item) => {
785
+ if (typeof item === "object" && item !== null && "text" in item) {
786
+ return String(item.text);
787
+ }
788
+ return "";
789
+ }).filter(Boolean).join("\n");
790
+ }
791
+ return JSON.stringify(response).slice(0, MAX_OUTPUT);
792
+ }
793
+ var MAX_CONTENT, MAX_OUTPUT;
794
+ var init_content_extractor = __esm({
795
+ "src/lib/content-extractor.ts"() {
796
+ "use strict";
797
+ init_mcp_prefix();
798
+ MAX_CONTENT = 2e3;
799
+ MAX_OUTPUT = 1e3;
800
+ }
801
+ });
802
+
803
+ // src/lib/project-name.ts
804
+ var project_name_exports = {};
805
+ __export(project_name_exports, {
806
+ _resetCache: () => _resetCache,
807
+ getProjectName: () => getProjectName
808
+ });
809
+ import { execSync as execSync4 } from "child_process";
810
+ import path6 from "path";
811
+ function getProjectName(cwd) {
812
+ const dir = cwd ?? process.cwd();
813
+ if (_cached2 && _cachedCwd === dir) return _cached2;
814
+ try {
815
+ let repoRoot;
816
+ try {
817
+ const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
818
+ cwd: dir,
819
+ encoding: "utf8",
820
+ timeout: 2e3,
821
+ stdio: ["pipe", "pipe", "pipe"]
822
+ }).trim();
823
+ repoRoot = path6.dirname(gitCommonDir);
824
+ } catch {
825
+ repoRoot = execSync4("git rev-parse --show-toplevel", {
826
+ cwd: dir,
827
+ encoding: "utf8",
828
+ timeout: 2e3,
829
+ stdio: ["pipe", "pipe", "pipe"]
830
+ }).trim();
831
+ }
832
+ _cached2 = path6.basename(repoRoot);
833
+ _cachedCwd = dir;
834
+ return _cached2;
835
+ } catch {
836
+ _cached2 = path6.basename(dir);
837
+ _cachedCwd = dir;
838
+ return _cached2;
839
+ }
840
+ }
841
+ function _resetCache() {
842
+ _cached2 = null;
843
+ _cachedCwd = null;
844
+ }
845
+ var _cached2, _cachedCwd;
846
+ var init_project_name = __esm({
847
+ "src/lib/project-name.ts"() {
848
+ "use strict";
849
+ _cached2 = null;
850
+ _cachedCwd = null;
851
+ }
852
+ });
853
+
854
+ // src/lib/daemon-auth.ts
855
+ var daemon_auth_exports = {};
856
+ __export(daemon_auth_exports, {
857
+ DAEMON_TOKEN_PATH: () => DAEMON_TOKEN_PATH,
858
+ ensureDaemonToken: () => ensureDaemonToken,
859
+ readDaemonToken: () => readDaemonToken
860
+ });
861
+ import crypto2 from "crypto";
862
+ import path7 from "path";
863
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
864
+ function normalizeToken(token) {
865
+ if (!token) return null;
866
+ const trimmed = token.trim();
867
+ return trimmed.length > 0 ? trimmed : null;
868
+ }
869
+ function readDaemonToken() {
870
+ try {
871
+ if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
872
+ return normalizeToken(readFileSync5(DAEMON_TOKEN_PATH, "utf8"));
873
+ } catch {
874
+ return null;
875
+ }
876
+ }
877
+ function ensureDaemonToken(seed) {
878
+ const existing = readDaemonToken();
879
+ if (existing) return existing;
880
+ const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
881
+ ensurePrivateDirSync(EXE_AI_DIR);
882
+ writeFileSync5(DAEMON_TOKEN_PATH, `${token}
883
+ `, "utf8");
884
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
885
+ return token;
886
+ }
887
+ var DAEMON_TOKEN_PATH;
888
+ var init_daemon_auth = __esm({
889
+ "src/lib/daemon-auth.ts"() {
890
+ "use strict";
891
+ init_config();
892
+ init_secure_files();
893
+ DAEMON_TOKEN_PATH = path7.join(EXE_AI_DIR, "exed.token");
894
+ }
895
+ });
164
896
 
165
897
  // src/adapters/claude/hooks/ingest.ts
898
+ init_config();
899
+ init_config();
166
900
  import { spawn } from "child_process";
167
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync4, openSync, closeSync } from "fs";
168
- import path5 from "path";
901
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync7, openSync, closeSync } from "fs";
902
+ import path8 from "path";
169
903
  import { fileURLToPath } from "url";
170
904
 
171
905
  // src/lib/active-agent.ts
172
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
906
+ init_config();
907
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
173
908
  import { execSync as execSync3 } from "child_process";
174
- import path3 from "path";
909
+ import path4 from "path";
175
910
 
176
911
  // src/lib/session-key.ts
177
912
  import { execSync } from "child_process";
@@ -235,41 +970,9 @@ function getSessionKey() {
235
970
  return _cached;
236
971
  }
237
972
 
238
- // src/lib/employees.ts
239
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
240
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
241
- import { execSync as execSync2 } from "child_process";
242
- import path2 from "path";
243
- import os2 from "os";
244
- var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
245
- var DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
246
- var COORDINATOR_ROLE = "COO";
247
- function normalizeRole(role) {
248
- return (role ?? "").trim().toLowerCase();
249
- }
250
- function isCoordinatorRole(role) {
251
- return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
252
- }
253
- function getCoordinatorEmployee(employees) {
254
- return employees.find((e) => isCoordinatorRole(e.role));
255
- }
256
- function getCoordinatorName(employees = loadEmployeesSync()) {
257
- return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
258
- }
259
- function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
260
- if (!existsSync2(employeesPath)) return [];
261
- try {
262
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
263
- } catch {
264
- return [];
265
- }
266
- }
267
- function getEmployee(employees, name) {
268
- return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
269
- }
270
-
271
973
  // src/lib/active-agent.ts
272
- var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
974
+ init_employees();
975
+ var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
273
976
  var STALE_MS = 24 * 60 * 60 * 1e3;
274
977
  function isNameWithOptionalInstance(candidate, baseName) {
275
978
  if (candidate === baseName) return true;
@@ -314,12 +1017,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
314
1017
  return null;
315
1018
  }
316
1019
  function getMarkerPath() {
317
- return path3.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1020
+ return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
318
1021
  }
319
1022
  function getActiveAgent() {
320
1023
  try {
321
1024
  const markerPath = getMarkerPath();
322
- const raw = readFileSync3(markerPath, "utf8");
1025
+ const raw = readFileSync4(markerPath, "utf8");
323
1026
  const data = JSON.parse(raw);
324
1027
  if (data.agentId) {
325
1028
  if (data.startedAt) {
@@ -360,21 +1063,8 @@ function getActiveAgent() {
360
1063
  }
361
1064
 
362
1065
  // src/lib/error-detector.ts
1066
+ init_mcp_prefix();
363
1067
  import crypto from "crypto";
364
-
365
- // src/lib/mcp-prefix.ts
366
- var MCP_PRIMARY_KEY = "exe-os";
367
- var MCP_LEGACY_KEY = "exe-mem";
368
- var MCP_TOOL_PREFIXES = [
369
- `mcp__${MCP_PRIMARY_KEY}__`,
370
- `mcp__${MCP_LEGACY_KEY}__`
371
- ];
372
- function isExeMcpTool(toolName) {
373
- if (!toolName) return false;
374
- return MCP_TOOL_PREFIXES.some((p) => toolName.startsWith(p));
375
- }
376
-
377
- // src/lib/error-detector.ts
378
1068
  var ERROR_PATTERNS = [
379
1069
  /\bError\b/i,
380
1070
  /\bERR!\b/,
@@ -510,16 +1200,17 @@ function errorFingerprint(toolName, errorText) {
510
1200
  }
511
1201
 
512
1202
  // src/lib/worker-gate.ts
513
- import { readdirSync as readdirSync2, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
514
- import path4 from "path";
515
- var WORKER_PID_DIR = path4.join(EXE_AI_DIR, "worker-pids");
1203
+ init_config();
1204
+ import { readdirSync as readdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
1205
+ import path5 from "path";
1206
+ var WORKER_PID_DIR = path5.join(EXE_AI_DIR, "worker-pids");
516
1207
  var MAX_CONCURRENT_WORKERS = 3;
517
1208
  function tryAcquireWorkerSlot() {
518
1209
  try {
519
- mkdirSync2(WORKER_PID_DIR, { recursive: true });
1210
+ mkdirSync3(WORKER_PID_DIR, { recursive: true });
520
1211
  const reservationId = `res-${process.pid}-${Date.now()}`;
521
- const reservationPath = path4.join(WORKER_PID_DIR, `${reservationId}.pid`);
522
- writeFileSync3(reservationPath, String(process.pid));
1212
+ const reservationPath = path5.join(WORKER_PID_DIR, `${reservationId}.pid`);
1213
+ writeFileSync4(reservationPath, String(process.pid));
523
1214
  const files = readdirSync2(WORKER_PID_DIR);
524
1215
  let alive = 0;
525
1216
  for (const f of files) {
@@ -536,7 +1227,7 @@ function tryAcquireWorkerSlot() {
536
1227
  alive++;
537
1228
  } catch {
538
1229
  try {
539
- unlinkSync3(path4.join(WORKER_PID_DIR, f));
1230
+ unlinkSync3(path5.join(WORKER_PID_DIR, f));
540
1231
  } catch {
541
1232
  }
542
1233
  }
@@ -559,14 +1250,15 @@ function tryAcquireWorkerSlot() {
559
1250
  }
560
1251
  function registerWorkerPid(pid) {
561
1252
  try {
562
- mkdirSync2(WORKER_PID_DIR, { recursive: true });
563
- writeFileSync3(path4.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
1253
+ mkdirSync3(WORKER_PID_DIR, { recursive: true });
1254
+ writeFileSync4(path5.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
564
1255
  } catch {
565
1256
  }
566
1257
  }
567
- var BACKFILL_LOCK = path4.join(WORKER_PID_DIR, "backfill.lock");
1258
+ var BACKFILL_LOCK = path5.join(WORKER_PID_DIR, "backfill.lock");
568
1259
 
569
1260
  // src/adapters/claude/hooks/ingest.ts
1261
+ init_employees();
570
1262
  if (!process.env.AGENT_ID) {
571
1263
  process.env.AGENT_ID = "default";
572
1264
  process.env.AGENT_ROLE = "employee";
@@ -577,7 +1269,7 @@ if (!loadConfigSync().autoIngestion) {
577
1269
  if (!process.env.EXE_OS_DIR) {
578
1270
  process.env.EXE_OS_DIR = EXE_AI_DIR;
579
1271
  }
580
- var WORKER_LOG_PATH = path5.join(EXE_AI_DIR, "workers.log");
1272
+ var WORKER_LOG_PATH = path8.join(EXE_AI_DIR, "workers.log");
581
1273
  function openWorkerLog() {
582
1274
  try {
583
1275
  return openSync(WORKER_LOG_PATH, "a");
@@ -589,13 +1281,13 @@ var ALLOWED_TOOL_RE = /^(Bash|Edit|Write|Read|Glob|Grep|Agent|apply_patch|mcp__.
589
1281
  var WRITE_TOOL_RE = /^(Bash|Edit|Write|apply_patch)$/;
590
1282
  var SUMMARY_INTERVAL = 25;
591
1283
  var MIN_WRITES_FOR_SUMMARY = 3;
592
- var COUNTER_DIR = path5.join(EXE_AI_DIR, "session-cache");
1284
+ var COUNTER_DIR = path8.join(EXE_AI_DIR, "session-cache");
593
1285
  function getCounterPath(sessionId) {
594
- return path5.join(COUNTER_DIR, `counter-${sessionId}.json`);
1286
+ return path8.join(COUNTER_DIR, `counter-${sessionId}.json`);
595
1287
  }
596
1288
  function loadCounter(sessionId) {
597
1289
  try {
598
- const raw = readFileSync4(getCounterPath(sessionId), "utf8");
1290
+ const raw = readFileSync6(getCounterPath(sessionId), "utf8");
599
1291
  return JSON.parse(raw);
600
1292
  } catch {
601
1293
  return { total: 0, writes: 0, pipelineWrites: 0, lastSummaryAt: 0, pipelineDetected: false };
@@ -603,8 +1295,8 @@ function loadCounter(sessionId) {
603
1295
  }
604
1296
  function saveCounter(sessionId, counter) {
605
1297
  try {
606
- mkdirSync3(COUNTER_DIR, { recursive: true });
607
- writeFileSync4(getCounterPath(sessionId), JSON.stringify(counter));
1298
+ mkdirSync4(COUNTER_DIR, { recursive: true });
1299
+ writeFileSync6(getCounterPath(sessionId), JSON.stringify(counter));
608
1300
  } catch {
609
1301
  }
610
1302
  }
@@ -616,15 +1308,15 @@ process.stdin.on("data", (chunk) => {
616
1308
  input += chunk;
617
1309
  }
618
1310
  });
619
- process.stdin.on("end", () => {
1311
+ process.stdin.on("end", async () => {
620
1312
  try {
621
1313
  if (process.env.EXE_DEBUG_HOOKS) {
622
1314
  try {
623
- const debugPath = path5.join(EXE_AI_DIR, "logs", "hook-stdin-ingest.log");
624
- mkdirSync3(path5.dirname(debugPath), { recursive: true });
1315
+ const debugPath = path8.join(EXE_AI_DIR, "logs", "hook-stdin-ingest.log");
1316
+ mkdirSync4(path8.dirname(debugPath), { recursive: true });
625
1317
  const ts = (/* @__PURE__ */ new Date()).toISOString();
626
1318
  const snippet = input.length > 2e3 ? input.slice(0, 2e3) + "...[truncated]" : input;
627
- writeFileSync4(debugPath, `[${ts}] ${snippet}
1319
+ writeFileSync6(debugPath, `[${ts}] ${snippet}
628
1320
  `, { flag: "a" });
629
1321
  } catch {
630
1322
  }
@@ -691,14 +1383,14 @@ Your output files must start with: exe/output/${agent.agentId}-`
691
1383
  const classification = classifyError(errorText);
692
1384
  if (classification === "system" && data.session_id) {
693
1385
  const fp = errorFingerprint(data.tool_name, errorText);
694
- const fpFilePath = path5.join(COUNTER_DIR, `bug-fingerprints-${data.session_id}.json`);
1386
+ const fpFilePath = path8.join(COUNTER_DIR, `bug-fingerprints-${data.session_id}.json`);
695
1387
  let fpData = {
696
1388
  seen: {},
697
1389
  taskCount: 0,
698
1390
  lastTaskAt: ""
699
1391
  };
700
1392
  try {
701
- fpData = JSON.parse(readFileSync4(fpFilePath, "utf8"));
1393
+ fpData = JSON.parse(readFileSync6(fpFilePath, "utf8"));
702
1394
  } catch {
703
1395
  }
704
1396
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -716,13 +1408,13 @@ Your output files must start with: exe/output/${agent.agentId}-`
716
1408
  fpData.seen[fp] = { count: 1, firstAt: now, lastAt: now };
717
1409
  fpData.taskCount++;
718
1410
  fpData.lastTaskAt = now;
719
- const bugWorkerPath = path5.resolve(
720
- path5.dirname(fileURLToPath(import.meta.url)),
1411
+ const bugWorkerPath = path8.resolve(
1412
+ path8.dirname(fileURLToPath(import.meta.url)),
721
1413
  "bug-report-worker.js"
722
1414
  );
723
- if (existsSync4(bugWorkerPath) && tryAcquireWorkerSlot()) {
1415
+ if (existsSync7(bugWorkerPath) && tryAcquireWorkerSlot()) {
724
1416
  const stderrFd2 = openWorkerLog();
725
- const projectName = process.cwd().split(path5.sep).pop() ?? "unknown";
1417
+ const projectName = process.cwd().split(path8.sep).pop() ?? "unknown";
726
1418
  const bugToolInput = data.tool_input ?? {};
727
1419
  const bugWorker = spawn(process.execPath, [bugWorkerPath], {
728
1420
  detached: true,
@@ -750,8 +1442,8 @@ Your output files must start with: exe/output/${agent.agentId}-`
750
1442
  }
751
1443
  }
752
1444
  try {
753
- mkdirSync3(COUNTER_DIR, { recursive: true });
754
- writeFileSync4(fpFilePath, JSON.stringify(fpData));
1445
+ mkdirSync4(COUNTER_DIR, { recursive: true });
1446
+ writeFileSync6(fpFilePath, JSON.stringify(fpData));
755
1447
  } catch {
756
1448
  }
757
1449
  }
@@ -768,11 +1460,11 @@ Your output files must start with: exe/output/${agent.agentId}-`
768
1460
  }
769
1461
  const callsSinceLastSummary = counter.total - counter.lastSummaryAt;
770
1462
  if (callsSinceLastSummary >= SUMMARY_INTERVAL && counter.writes >= MIN_WRITES_FOR_SUMMARY) {
771
- const summaryWorkerPath = path5.resolve(
772
- path5.dirname(fileURLToPath(import.meta.url)),
1463
+ const summaryWorkerPath = path8.resolve(
1464
+ path8.dirname(fileURLToPath(import.meta.url)),
773
1465
  "summary-worker.js"
774
1466
  );
775
- if (existsSync4(summaryWorkerPath) && tryAcquireWorkerSlot()) {
1467
+ if (existsSync7(summaryWorkerPath) && tryAcquireWorkerSlot()) {
776
1468
  const stderrFd2 = openWorkerLog();
777
1469
  const summaryWorker = spawn(process.execPath, [summaryWorkerPath], {
778
1470
  detached: true,
@@ -818,13 +1510,13 @@ WARNING: You are writing code without running the build pipeline. If this task a
818
1510
  const bashOutput = typeof data.tool_response === "string" ? data.tool_response : JSON.stringify(data.tool_response ?? "");
819
1511
  const commitMatch = bashOutput.match(/\[(\S+)\s+([a-f0-9]{7,40})\]\s+(.+)/);
820
1512
  if (commitMatch) {
821
- const commitWorkerPath = path5.resolve(
822
- path5.dirname(fileURLToPath(import.meta.url)),
1513
+ const commitWorkerPath = path8.resolve(
1514
+ path8.dirname(fileURLToPath(import.meta.url)),
823
1515
  "commit-complete.js"
824
1516
  );
825
- if (existsSync4(commitWorkerPath) && tryAcquireWorkerSlot()) {
1517
+ if (existsSync7(commitWorkerPath) && tryAcquireWorkerSlot()) {
826
1518
  const stderrFd2 = openWorkerLog();
827
- const projectName = process.cwd().split(path5.sep).pop() ?? "unknown";
1519
+ const projectName = process.cwd().split(path8.sep).pop() ?? "unknown";
828
1520
  const commitWorker = spawn(process.execPath, [commitWorkerPath], {
829
1521
  detached: true,
830
1522
  stdio: ["ignore", "ignore", stderrFd2],
@@ -848,8 +1540,68 @@ WARNING: You are writing code without running the build pipeline. If this task a
848
1540
  if (!ALLOWED_TOOL_RE.test(data.tool_name)) {
849
1541
  process.exit(0);
850
1542
  }
851
- const workerPath = path5.resolve(
852
- path5.dirname(fileURLToPath(import.meta.url)),
1543
+ let sentViaDaemon = false;
1544
+ try {
1545
+ const socketPath = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
1546
+ if (existsSync7(socketPath)) {
1547
+ const { extractSemanticText: extractSemanticText2 } = await Promise.resolve().then(() => (init_content_extractor(), content_extractor_exports));
1548
+ const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
1549
+ const { canCoordinate: canCoordinate2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
1550
+ const { readDaemonToken: readDaemonToken2 } = await Promise.resolve().then(() => (init_daemon_auth(), daemon_auth_exports));
1551
+ const rawText = extractSemanticText2(data.tool_name, data.tool_input ?? {}, data.tool_response ?? {});
1552
+ if (rawText.length >= 50) {
1553
+ let taskId = null;
1554
+ try {
1555
+ const cachePath = path8.join(EXE_AI_DIR, "session-cache", `current-task-${agent.agentId}.json`);
1556
+ if (existsSync7(cachePath)) {
1557
+ const cached = JSON.parse(readFileSync6(cachePath, "utf8"));
1558
+ taskId = cached.taskId ?? null;
1559
+ }
1560
+ } catch {
1561
+ }
1562
+ const detectData2 = { tool_name: data.tool_name, tool_response: data.tool_response };
1563
+ const hasError = detectError(detectData2);
1564
+ const isDraft = taskId !== null && !canCoordinate2(agent.agentId, agent.agentRole);
1565
+ const toolInputStr = JSON.stringify(data.tool_input ?? "").slice(0, 200);
1566
+ const toolOutputStr = JSON.stringify(data.tool_response ?? "").slice(0, 200);
1567
+ const token = process.env.EXE_DAEMON_TOKEN ?? readDaemonToken2();
1568
+ const projectName = getProjectName2(data.cwd ?? process.cwd());
1569
+ const payload = JSON.stringify({
1570
+ id: `ingest-${Date.now()}`,
1571
+ token,
1572
+ type: "ingest",
1573
+ rawText,
1574
+ agentId: agent.agentId,
1575
+ agentRole: agent.agentRole,
1576
+ sessionId: data.session_id ?? "",
1577
+ toolName: data.tool_name,
1578
+ projectName,
1579
+ hasError,
1580
+ taskId,
1581
+ confidence: agent.agentId === "default" ? 0.9 : 0.7,
1582
+ draft: isDraft,
1583
+ trajectory: { input: toolInputStr, tool: data.tool_name, output: toolOutputStr, result_type: hasError ? "error" : "success" }
1584
+ }) + "\n";
1585
+ const net = await import("net");
1586
+ const sock = net.connect(socketPath);
1587
+ sock.on("error", () => {
1588
+ });
1589
+ sock.write(payload, () => {
1590
+ sock.destroy();
1591
+ });
1592
+ sentViaDaemon = true;
1593
+ } else {
1594
+ sentViaDaemon = true;
1595
+ }
1596
+ }
1597
+ } catch {
1598
+ }
1599
+ if (sentViaDaemon) {
1600
+ setTimeout(() => process.exit(0), 10);
1601
+ return;
1602
+ }
1603
+ const workerPath = path8.resolve(
1604
+ path8.dirname(fileURLToPath(import.meta.url)),
853
1605
  "ingest-worker.js"
854
1606
  );
855
1607
  if (!tryAcquireWorkerSlot()) {