@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.
- package/dist/bin/backfill-conversations.js +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
package/dist/hooks/ingest.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
168
|
-
import
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
514
|
-
import
|
|
515
|
-
|
|
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
|
-
|
|
1210
|
+
mkdirSync3(WORKER_PID_DIR, { recursive: true });
|
|
520
1211
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
521
|
-
const reservationPath =
|
|
522
|
-
|
|
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(
|
|
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
|
-
|
|
563
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1284
|
+
var COUNTER_DIR = path8.join(EXE_AI_DIR, "session-cache");
|
|
593
1285
|
function getCounterPath(sessionId) {
|
|
594
|
-
return
|
|
1286
|
+
return path8.join(COUNTER_DIR, `counter-${sessionId}.json`);
|
|
595
1287
|
}
|
|
596
1288
|
function loadCounter(sessionId) {
|
|
597
1289
|
try {
|
|
598
|
-
const raw =
|
|
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
|
-
|
|
607
|
-
|
|
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 =
|
|
624
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
720
|
-
|
|
1411
|
+
const bugWorkerPath = path8.resolve(
|
|
1412
|
+
path8.dirname(fileURLToPath(import.meta.url)),
|
|
721
1413
|
"bug-report-worker.js"
|
|
722
1414
|
);
|
|
723
|
-
if (
|
|
1415
|
+
if (existsSync7(bugWorkerPath) && tryAcquireWorkerSlot()) {
|
|
724
1416
|
const stderrFd2 = openWorkerLog();
|
|
725
|
-
const projectName = process.cwd().split(
|
|
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
|
-
|
|
754
|
-
|
|
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 =
|
|
772
|
-
|
|
1463
|
+
const summaryWorkerPath = path8.resolve(
|
|
1464
|
+
path8.dirname(fileURLToPath(import.meta.url)),
|
|
773
1465
|
"summary-worker.js"
|
|
774
1466
|
);
|
|
775
|
-
if (
|
|
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 =
|
|
822
|
-
|
|
1513
|
+
const commitWorkerPath = path8.resolve(
|
|
1514
|
+
path8.dirname(fileURLToPath(import.meta.url)),
|
|
823
1515
|
"commit-complete.js"
|
|
824
1516
|
);
|
|
825
|
-
if (
|
|
1517
|
+
if (existsSync7(commitWorkerPath) && tryAcquireWorkerSlot()) {
|
|
826
1518
|
const stderrFd2 = openWorkerLog();
|
|
827
|
-
const projectName = process.cwd().split(
|
|
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
|
-
|
|
852
|
-
|
|
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()) {
|