@askexenow/exe-os 0.8.33 → 0.8.37
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 +341 -349
- package/dist/bin/backfill-responses.js +81 -13
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1737 -1117
- package/dist/bin/exe-assign.js +89 -19
- package/dist/bin/exe-boot.js +951 -101
- package/dist/bin/exe-call.js +61 -2
- package/dist/bin/exe-dispatch.js +61 -13
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +71 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +178 -45
- package/dist/bin/exe-heartbeat.js +79 -14
- package/dist/bin/exe-kill.js +71 -3
- package/dist/bin/exe-launch-agent.js +148 -14
- package/dist/bin/exe-link.js +1437 -0
- package/dist/bin/exe-new-employee.js +98 -13
- package/dist/bin/exe-pending-messages.js +74 -8
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +77 -11
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +73 -5
- package/dist/bin/exe-search.js +88 -14
- package/dist/bin/exe-session-cleanup.js +102 -28
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +80 -5
- package/dist/bin/graph-backfill.js +71 -3
- package/dist/bin/graph-export.js +71 -3
- package/dist/bin/install.js +38 -8
- package/dist/bin/scan-tasks.js +80 -5
- package/dist/bin/setup.js +128 -10
- package/dist/bin/shard-migrate.js +71 -3
- package/dist/bin/wiki-sync.js +71 -3
- package/dist/gateway/index.js +179 -46
- package/dist/hooks/bug-report-worker.js +254 -28
- package/dist/hooks/commit-complete.js +80 -5
- package/dist/hooks/error-recall.js +89 -15
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +185 -51
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +81 -6
- package/dist/hooks/notification.js +81 -6
- package/dist/hooks/post-compact.js +81 -6
- package/dist/hooks/pre-compact.js +81 -6
- package/dist/hooks/pre-tool-use.js +423 -196
- package/dist/hooks/prompt-ingest-worker.js +91 -23
- package/dist/hooks/prompt-submit.js +159 -45
- package/dist/hooks/response-ingest-worker.js +96 -23
- package/dist/hooks/session-end.js +81 -6
- package/dist/hooks/session-start.js +89 -15
- package/dist/hooks/stop.js +81 -6
- package/dist/hooks/subagent-stop.js +81 -6
- package/dist/hooks/summary-worker.js +807 -55
- package/dist/index.js +198 -60
- package/dist/lib/cloud-sync.js +703 -18
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +26 -0
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +207 -74
- package/dist/lib/hybrid-search.js +88 -14
- package/dist/lib/identity-templates.js +51 -0
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +65 -17
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +73 -4
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +95 -28
- package/dist/lib/tmux-routing.js +92 -23
- package/dist/mcp/server.js +800 -74
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +198 -31
- package/dist/mcp/tools/deactivate-behavior.js +4 -4
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +19 -9
- package/dist/mcp/tools/send-message.js +69 -21
- package/dist/mcp/tools/update-task.js +28 -18
- package/dist/runtime/index.js +166 -28
- package/dist/tui/App.js +193 -40
- package/package.json +7 -3
- package/src/commands/exe/afk.md +116 -0
- package/src/commands/exe/rename.md +12 -0
package/dist/hooks/stop.js
CHANGED
|
@@ -231,6 +231,61 @@ var init_memory = __esm({
|
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
// src/lib/db-retry.ts
|
|
235
|
+
function isBusyError(err) {
|
|
236
|
+
if (err instanceof Error) {
|
|
237
|
+
const msg = err.message.toLowerCase();
|
|
238
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
function delay(ms) {
|
|
243
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
244
|
+
}
|
|
245
|
+
async function retryOnBusy(fn, label) {
|
|
246
|
+
let lastError;
|
|
247
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
248
|
+
try {
|
|
249
|
+
return await fn();
|
|
250
|
+
} catch (err) {
|
|
251
|
+
lastError = err;
|
|
252
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
256
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
257
|
+
process.stderr.write(
|
|
258
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
259
|
+
`
|
|
260
|
+
);
|
|
261
|
+
await delay(backoff + jitter);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
throw lastError;
|
|
265
|
+
}
|
|
266
|
+
function wrapWithRetry(client) {
|
|
267
|
+
return new Proxy(client, {
|
|
268
|
+
get(target, prop, receiver) {
|
|
269
|
+
if (prop === "execute") {
|
|
270
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
271
|
+
}
|
|
272
|
+
if (prop === "batch") {
|
|
273
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
274
|
+
}
|
|
275
|
+
return Reflect.get(target, prop, receiver);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
280
|
+
var init_db_retry = __esm({
|
|
281
|
+
"src/lib/db-retry.ts"() {
|
|
282
|
+
"use strict";
|
|
283
|
+
MAX_RETRIES = 3;
|
|
284
|
+
BASE_DELAY_MS = 200;
|
|
285
|
+
MAX_JITTER_MS = 300;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
234
289
|
// src/lib/database.ts
|
|
235
290
|
var database_exports = {};
|
|
236
291
|
__export(database_exports, {
|
|
@@ -238,6 +293,7 @@ __export(database_exports, {
|
|
|
238
293
|
disposeTurso: () => disposeTurso,
|
|
239
294
|
ensureSchema: () => ensureSchema,
|
|
240
295
|
getClient: () => getClient,
|
|
296
|
+
getRawClient: () => getRawClient,
|
|
241
297
|
initDatabase: () => initDatabase,
|
|
242
298
|
initTurso: () => initTurso,
|
|
243
299
|
isInitialized: () => isInitialized
|
|
@@ -247,6 +303,7 @@ async function initDatabase(config) {
|
|
|
247
303
|
if (_client) {
|
|
248
304
|
_client.close();
|
|
249
305
|
_client = null;
|
|
306
|
+
_resilientClient = null;
|
|
250
307
|
}
|
|
251
308
|
const opts = {
|
|
252
309
|
url: `file:${config.dbPath}`
|
|
@@ -255,20 +312,27 @@ async function initDatabase(config) {
|
|
|
255
312
|
opts.encryptionKey = config.encryptionKey;
|
|
256
313
|
}
|
|
257
314
|
_client = createClient(opts);
|
|
315
|
+
_resilientClient = wrapWithRetry(_client);
|
|
258
316
|
}
|
|
259
317
|
function isInitialized() {
|
|
260
318
|
return _client !== null;
|
|
261
319
|
}
|
|
262
320
|
function getClient() {
|
|
321
|
+
if (!_resilientClient) {
|
|
322
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
323
|
+
}
|
|
324
|
+
return _resilientClient;
|
|
325
|
+
}
|
|
326
|
+
function getRawClient() {
|
|
263
327
|
if (!_client) {
|
|
264
328
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
265
329
|
}
|
|
266
330
|
return _client;
|
|
267
331
|
}
|
|
268
332
|
async function ensureSchema() {
|
|
269
|
-
const client =
|
|
333
|
+
const client = getRawClient();
|
|
270
334
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
271
|
-
await client.execute("PRAGMA busy_timeout =
|
|
335
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
272
336
|
try {
|
|
273
337
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
274
338
|
} catch {
|
|
@@ -1061,13 +1125,16 @@ async function disposeDatabase() {
|
|
|
1061
1125
|
if (_client) {
|
|
1062
1126
|
_client.close();
|
|
1063
1127
|
_client = null;
|
|
1128
|
+
_resilientClient = null;
|
|
1064
1129
|
}
|
|
1065
1130
|
}
|
|
1066
|
-
var _client, initTurso, disposeTurso;
|
|
1131
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1067
1132
|
var init_database = __esm({
|
|
1068
1133
|
"src/lib/database.ts"() {
|
|
1069
1134
|
"use strict";
|
|
1135
|
+
init_db_retry();
|
|
1070
1136
|
_client = null;
|
|
1137
|
+
_resilientClient = null;
|
|
1071
1138
|
initTurso = initDatabase;
|
|
1072
1139
|
disposeTurso = disposeDatabase;
|
|
1073
1140
|
}
|
|
@@ -1180,7 +1247,7 @@ function listShards() {
|
|
|
1180
1247
|
}
|
|
1181
1248
|
async function ensureShardSchema(client) {
|
|
1182
1249
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1183
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1250
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1184
1251
|
try {
|
|
1185
1252
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1186
1253
|
} catch {
|
|
@@ -1441,7 +1508,8 @@ async function writeMemory(record) {
|
|
|
1441
1508
|
has_error: record.has_error ? 1 : 0,
|
|
1442
1509
|
raw_text: record.raw_text,
|
|
1443
1510
|
vector: record.vector,
|
|
1444
|
-
version:
|
|
1511
|
+
version: 0,
|
|
1512
|
+
// Placeholder — assigned atomically at flush time
|
|
1445
1513
|
task_id: record.task_id ?? null,
|
|
1446
1514
|
importance: record.importance ?? 5,
|
|
1447
1515
|
status: record.status ?? "active",
|
|
@@ -1475,6 +1543,13 @@ async function flushBatch() {
|
|
|
1475
1543
|
_flushing = true;
|
|
1476
1544
|
try {
|
|
1477
1545
|
const batch = _pendingRecords.slice(0);
|
|
1546
|
+
const client = getClient();
|
|
1547
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1548
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1549
|
+
for (const row of batch) {
|
|
1550
|
+
row.version = baseVersion++;
|
|
1551
|
+
}
|
|
1552
|
+
_nextVersion = baseVersion;
|
|
1478
1553
|
const buildStmt = (row) => {
|
|
1479
1554
|
const hasVector = row.vector !== null;
|
|
1480
1555
|
const taskId = row.task_id ?? null;
|
|
@@ -1881,7 +1956,7 @@ function getActiveAgent() {
|
|
|
1881
1956
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
1882
1957
|
{ encoding: "utf8", timeout: 2e3 }
|
|
1883
1958
|
).trim();
|
|
1884
|
-
const empMatch = sessionName.match(/^(
|
|
1959
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
1885
1960
|
if (empMatch && empMatch[1] !== "exe") {
|
|
1886
1961
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
1887
1962
|
}
|
|
@@ -212,6 +212,61 @@ var init_memory = __esm({
|
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
// src/lib/db-retry.ts
|
|
216
|
+
function isBusyError(err) {
|
|
217
|
+
if (err instanceof Error) {
|
|
218
|
+
const msg = err.message.toLowerCase();
|
|
219
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
function delay(ms) {
|
|
224
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
225
|
+
}
|
|
226
|
+
async function retryOnBusy(fn, label) {
|
|
227
|
+
let lastError;
|
|
228
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
229
|
+
try {
|
|
230
|
+
return await fn();
|
|
231
|
+
} catch (err) {
|
|
232
|
+
lastError = err;
|
|
233
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
237
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
238
|
+
process.stderr.write(
|
|
239
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
240
|
+
`
|
|
241
|
+
);
|
|
242
|
+
await delay(backoff + jitter);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
throw lastError;
|
|
246
|
+
}
|
|
247
|
+
function wrapWithRetry(client) {
|
|
248
|
+
return new Proxy(client, {
|
|
249
|
+
get(target, prop, receiver) {
|
|
250
|
+
if (prop === "execute") {
|
|
251
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
252
|
+
}
|
|
253
|
+
if (prop === "batch") {
|
|
254
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
255
|
+
}
|
|
256
|
+
return Reflect.get(target, prop, receiver);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
261
|
+
var init_db_retry = __esm({
|
|
262
|
+
"src/lib/db-retry.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
MAX_RETRIES = 3;
|
|
265
|
+
BASE_DELAY_MS = 200;
|
|
266
|
+
MAX_JITTER_MS = 300;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
215
270
|
// src/lib/database.ts
|
|
216
271
|
var database_exports = {};
|
|
217
272
|
__export(database_exports, {
|
|
@@ -219,6 +274,7 @@ __export(database_exports, {
|
|
|
219
274
|
disposeTurso: () => disposeTurso,
|
|
220
275
|
ensureSchema: () => ensureSchema,
|
|
221
276
|
getClient: () => getClient,
|
|
277
|
+
getRawClient: () => getRawClient,
|
|
222
278
|
initDatabase: () => initDatabase,
|
|
223
279
|
initTurso: () => initTurso,
|
|
224
280
|
isInitialized: () => isInitialized
|
|
@@ -228,6 +284,7 @@ async function initDatabase(config) {
|
|
|
228
284
|
if (_client) {
|
|
229
285
|
_client.close();
|
|
230
286
|
_client = null;
|
|
287
|
+
_resilientClient = null;
|
|
231
288
|
}
|
|
232
289
|
const opts = {
|
|
233
290
|
url: `file:${config.dbPath}`
|
|
@@ -236,20 +293,27 @@ async function initDatabase(config) {
|
|
|
236
293
|
opts.encryptionKey = config.encryptionKey;
|
|
237
294
|
}
|
|
238
295
|
_client = createClient(opts);
|
|
296
|
+
_resilientClient = wrapWithRetry(_client);
|
|
239
297
|
}
|
|
240
298
|
function isInitialized() {
|
|
241
299
|
return _client !== null;
|
|
242
300
|
}
|
|
243
301
|
function getClient() {
|
|
302
|
+
if (!_resilientClient) {
|
|
303
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
304
|
+
}
|
|
305
|
+
return _resilientClient;
|
|
306
|
+
}
|
|
307
|
+
function getRawClient() {
|
|
244
308
|
if (!_client) {
|
|
245
309
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
246
310
|
}
|
|
247
311
|
return _client;
|
|
248
312
|
}
|
|
249
313
|
async function ensureSchema() {
|
|
250
|
-
const client =
|
|
314
|
+
const client = getRawClient();
|
|
251
315
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
252
|
-
await client.execute("PRAGMA busy_timeout =
|
|
316
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
253
317
|
try {
|
|
254
318
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
255
319
|
} catch {
|
|
@@ -1042,13 +1106,16 @@ async function disposeDatabase() {
|
|
|
1042
1106
|
if (_client) {
|
|
1043
1107
|
_client.close();
|
|
1044
1108
|
_client = null;
|
|
1109
|
+
_resilientClient = null;
|
|
1045
1110
|
}
|
|
1046
1111
|
}
|
|
1047
|
-
var _client, initTurso, disposeTurso;
|
|
1112
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1048
1113
|
var init_database = __esm({
|
|
1049
1114
|
"src/lib/database.ts"() {
|
|
1050
1115
|
"use strict";
|
|
1116
|
+
init_db_retry();
|
|
1051
1117
|
_client = null;
|
|
1118
|
+
_resilientClient = null;
|
|
1052
1119
|
initTurso = initDatabase;
|
|
1053
1120
|
disposeTurso = disposeDatabase;
|
|
1054
1121
|
}
|
|
@@ -1161,7 +1228,7 @@ function listShards() {
|
|
|
1161
1228
|
}
|
|
1162
1229
|
async function ensureShardSchema(client) {
|
|
1163
1230
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1164
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1231
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1165
1232
|
try {
|
|
1166
1233
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1167
1234
|
} catch {
|
|
@@ -1422,7 +1489,8 @@ async function writeMemory(record) {
|
|
|
1422
1489
|
has_error: record.has_error ? 1 : 0,
|
|
1423
1490
|
raw_text: record.raw_text,
|
|
1424
1491
|
vector: record.vector,
|
|
1425
|
-
version:
|
|
1492
|
+
version: 0,
|
|
1493
|
+
// Placeholder — assigned atomically at flush time
|
|
1426
1494
|
task_id: record.task_id ?? null,
|
|
1427
1495
|
importance: record.importance ?? 5,
|
|
1428
1496
|
status: record.status ?? "active",
|
|
@@ -1456,6 +1524,13 @@ async function flushBatch() {
|
|
|
1456
1524
|
_flushing = true;
|
|
1457
1525
|
try {
|
|
1458
1526
|
const batch = _pendingRecords.slice(0);
|
|
1527
|
+
const client = getClient();
|
|
1528
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1529
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1530
|
+
for (const row of batch) {
|
|
1531
|
+
row.version = baseVersion++;
|
|
1532
|
+
}
|
|
1533
|
+
_nextVersion = baseVersion;
|
|
1459
1534
|
const buildStmt = (row) => {
|
|
1460
1535
|
const hasVector = row.vector !== null;
|
|
1461
1536
|
const taskId = row.task_id ?? null;
|
|
@@ -1854,7 +1929,7 @@ function getActiveAgent() {
|
|
|
1854
1929
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
1855
1930
|
{ encoding: "utf8", timeout: 2e3 }
|
|
1856
1931
|
).trim();
|
|
1857
|
-
const empMatch = sessionName.match(/^(
|
|
1932
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
1858
1933
|
if (empMatch && empMatch[1] !== "exe") {
|
|
1859
1934
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
1860
1935
|
}
|