@askexenow/exe-os 0.8.36 → 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 +9 -1
- package/dist/bin/backfill-responses.js +9 -1
- package/dist/bin/cli.js +255 -31
- package/dist/bin/exe-assign.js +9 -1
- package/dist/bin/exe-boot.js +554 -23
- package/dist/bin/exe-dispatch.js +43 -3
- package/dist/bin/exe-export-behaviors.js +7 -0
- package/dist/bin/exe-gateway.js +57 -9
- package/dist/bin/exe-heartbeat.js +2 -1
- package/dist/bin/exe-kill.js +7 -0
- package/dist/bin/exe-launch-agent.js +8 -1
- package/dist/bin/exe-link.js +503 -12
- package/dist/bin/exe-pending-messages.js +2 -1
- package/dist/bin/exe-pending-reviews.js +2 -1
- package/dist/bin/exe-review.js +9 -1
- package/dist/bin/exe-search.js +9 -1
- package/dist/bin/exe-session-cleanup.js +11 -2
- package/dist/bin/git-sweep.js +9 -1
- package/dist/bin/graph-backfill.js +7 -0
- package/dist/bin/graph-export.js +7 -0
- package/dist/bin/install.js +35 -5
- package/dist/bin/scan-tasks.js +9 -1
- package/dist/bin/shard-migrate.js +7 -0
- package/dist/bin/wiki-sync.js +7 -0
- package/dist/gateway/index.js +57 -9
- package/dist/hooks/bug-report-worker.js +45 -5
- package/dist/hooks/commit-complete.js +9 -1
- package/dist/hooks/error-recall.js +10 -2
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +56 -8
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +10 -2
- package/dist/hooks/notification.js +10 -2
- package/dist/hooks/post-compact.js +10 -2
- package/dist/hooks/pre-compact.js +10 -2
- package/dist/hooks/pre-tool-use.js +10 -2
- package/dist/hooks/prompt-ingest-worker.js +9 -1
- package/dist/hooks/prompt-submit.js +56 -8
- package/dist/hooks/response-ingest-worker.js +9 -1
- package/dist/hooks/session-end.js +10 -2
- package/dist/hooks/session-start.js +10 -2
- package/dist/hooks/stop.js +10 -2
- package/dist/hooks/subagent-stop.js +10 -2
- package/dist/hooks/summary-worker.js +512 -13
- package/dist/index.js +65 -15
- package/dist/lib/cloud-sync.js +502 -11
- package/dist/lib/exe-daemon.js +73 -23
- package/dist/lib/hybrid-search.js +9 -1
- package/dist/lib/messaging.js +43 -3
- package/dist/lib/store.js +9 -1
- package/dist/lib/tasks.js +47 -7
- package/dist/lib/tmux-routing.js +45 -3
- package/dist/mcp/server.js +73 -16
- package/dist/mcp/tools/create-task.js +48 -8
- package/dist/mcp/tools/deactivate-behavior.js +1 -1
- package/dist/mcp/tools/list-tasks.js +2 -1
- package/dist/mcp/tools/send-message.js +46 -6
- package/dist/mcp/tools/update-task.js +3 -2
- package/dist/runtime/index.js +54 -4
- package/dist/tui/App.js +54 -4
- package/package.json +2 -2
- package/src/commands/exe/afk.md +116 -0
|
@@ -2510,7 +2510,7 @@ function sendIntercom(targetSession) {
|
|
|
2510
2510
|
return "failed";
|
|
2511
2511
|
}
|
|
2512
2512
|
}
|
|
2513
|
-
var SESSION_CACHE, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2513
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2514
2514
|
var init_tmux_routing = __esm({
|
|
2515
2515
|
"src/lib/tmux-routing.ts"() {
|
|
2516
2516
|
"use strict";
|
|
@@ -2522,6 +2522,7 @@ var init_tmux_routing = __esm({
|
|
|
2522
2522
|
init_provider_table();
|
|
2523
2523
|
init_intercom_queue();
|
|
2524
2524
|
init_plan_limits();
|
|
2525
|
+
SPAWN_LOCK_DIR = path13.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
2525
2526
|
SESSION_CACHE = path13.join(os5.homedir(), ".exe-os", "session-cache");
|
|
2526
2527
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
2527
2528
|
INTERCOM_LOG2 = path13.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
@@ -3106,7 +3107,8 @@ async function writeMemory(record) {
|
|
|
3106
3107
|
has_error: record.has_error ? 1 : 0,
|
|
3107
3108
|
raw_text: record.raw_text,
|
|
3108
3109
|
vector: record.vector,
|
|
3109
|
-
version:
|
|
3110
|
+
version: 0,
|
|
3111
|
+
// Placeholder — assigned atomically at flush time
|
|
3110
3112
|
task_id: record.task_id ?? null,
|
|
3111
3113
|
importance: record.importance ?? 5,
|
|
3112
3114
|
status: record.status ?? "active",
|
|
@@ -3140,6 +3142,13 @@ async function flushBatch() {
|
|
|
3140
3142
|
_flushing = true;
|
|
3141
3143
|
try {
|
|
3142
3144
|
const batch = _pendingRecords.slice(0);
|
|
3145
|
+
const client = getClient();
|
|
3146
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
3147
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
3148
|
+
for (const row of batch) {
|
|
3149
|
+
row.version = baseVersion++;
|
|
3150
|
+
}
|
|
3151
|
+
_nextVersion = baseVersion;
|
|
3143
3152
|
const buildStmt = (row) => {
|
|
3144
3153
|
const hasVector = row.vector !== null;
|
|
3145
3154
|
const taskId = row.task_id ?? null;
|
package/dist/bin/git-sweep.js
CHANGED
|
@@ -1490,7 +1490,8 @@ async function writeMemory(record) {
|
|
|
1490
1490
|
has_error: record.has_error ? 1 : 0,
|
|
1491
1491
|
raw_text: record.raw_text,
|
|
1492
1492
|
vector: record.vector,
|
|
1493
|
-
version:
|
|
1493
|
+
version: 0,
|
|
1494
|
+
// Placeholder — assigned atomically at flush time
|
|
1494
1495
|
task_id: record.task_id ?? null,
|
|
1495
1496
|
importance: record.importance ?? 5,
|
|
1496
1497
|
status: record.status ?? "active",
|
|
@@ -1524,6 +1525,13 @@ async function flushBatch() {
|
|
|
1524
1525
|
_flushing = true;
|
|
1525
1526
|
try {
|
|
1526
1527
|
const batch = _pendingRecords.slice(0);
|
|
1528
|
+
const client = getClient();
|
|
1529
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1530
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1531
|
+
for (const row of batch) {
|
|
1532
|
+
row.version = baseVersion++;
|
|
1533
|
+
}
|
|
1534
|
+
_nextVersion = baseVersion;
|
|
1527
1535
|
const buildStmt = (row) => {
|
|
1528
1536
|
const hasVector = row.vector !== null;
|
|
1529
1537
|
const taskId = row.task_id ?? null;
|
|
@@ -1424,6 +1424,13 @@ async function flushBatch() {
|
|
|
1424
1424
|
_flushing = true;
|
|
1425
1425
|
try {
|
|
1426
1426
|
const batch = _pendingRecords.slice(0);
|
|
1427
|
+
const client = getClient();
|
|
1428
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1429
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1430
|
+
for (const row of batch) {
|
|
1431
|
+
row.version = baseVersion++;
|
|
1432
|
+
}
|
|
1433
|
+
_nextVersion = baseVersion;
|
|
1427
1434
|
const buildStmt = (row) => {
|
|
1428
1435
|
const hasVector = row.vector !== null;
|
|
1429
1436
|
const taskId = row.task_id ?? null;
|
package/dist/bin/graph-export.js
CHANGED
|
@@ -1428,6 +1428,13 @@ async function flushBatch() {
|
|
|
1428
1428
|
_flushing = true;
|
|
1429
1429
|
try {
|
|
1430
1430
|
const batch = _pendingRecords.slice(0);
|
|
1431
|
+
const client = getClient();
|
|
1432
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1433
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1434
|
+
for (const row of batch) {
|
|
1435
|
+
row.version = baseVersion++;
|
|
1436
|
+
}
|
|
1437
|
+
_nextVersion = baseVersion;
|
|
1431
1438
|
const buildStmt = (row) => {
|
|
1432
1439
|
const hasVector = row.vector !== null;
|
|
1433
1440
|
const taskId = row.task_id ?? null;
|
package/dist/bin/install.js
CHANGED
|
@@ -501,26 +501,56 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
501
501
|
permissions.allow = [];
|
|
502
502
|
}
|
|
503
503
|
const toolNames = [
|
|
504
|
+
// Core memory
|
|
504
505
|
"store_memory",
|
|
505
506
|
"recall_my_memory",
|
|
507
|
+
"commit_to_long_term_memory",
|
|
508
|
+
"consolidate_memories",
|
|
509
|
+
"ask_team_memory",
|
|
510
|
+
"get_session_context",
|
|
511
|
+
// Tasks
|
|
506
512
|
"create_task",
|
|
507
513
|
"list_tasks",
|
|
508
514
|
"get_task",
|
|
509
515
|
"update_task",
|
|
510
516
|
"close_task",
|
|
517
|
+
"checkpoint_task",
|
|
518
|
+
// Behaviors
|
|
511
519
|
"store_behavior",
|
|
512
520
|
"deactivate_behavior",
|
|
513
|
-
"
|
|
521
|
+
"list_behaviors",
|
|
522
|
+
// Identity
|
|
514
523
|
"get_identity",
|
|
515
524
|
"update_identity",
|
|
516
|
-
|
|
517
|
-
"
|
|
525
|
+
// Messaging
|
|
526
|
+
"send_message",
|
|
527
|
+
"acknowledge_messages",
|
|
528
|
+
"send_whatsapp",
|
|
529
|
+
"query_conversations",
|
|
530
|
+
// Reminders + triggers
|
|
518
531
|
"create_reminder",
|
|
519
532
|
"complete_reminder",
|
|
520
533
|
"list_reminders",
|
|
521
|
-
"
|
|
534
|
+
"create_trigger",
|
|
535
|
+
"list_triggers",
|
|
536
|
+
// GraphRAG
|
|
522
537
|
"query_relationships",
|
|
523
|
-
"
|
|
538
|
+
"merge_entities",
|
|
539
|
+
// Documents + wiki
|
|
540
|
+
"ingest_document",
|
|
541
|
+
"list_documents",
|
|
542
|
+
"purge_document",
|
|
543
|
+
"rerank_documents",
|
|
544
|
+
"set_document_importance",
|
|
545
|
+
"create_wiki_page",
|
|
546
|
+
"update_wiki_page",
|
|
547
|
+
"get_wiki_page",
|
|
548
|
+
"list_wiki_pages",
|
|
549
|
+
// System
|
|
550
|
+
"load_skill",
|
|
551
|
+
"apply_starter_pack",
|
|
552
|
+
"resume_employee",
|
|
553
|
+
"deploy_client"
|
|
524
554
|
];
|
|
525
555
|
const allowList = permissions.allow;
|
|
526
556
|
for (const tool of expandDualPrefixTools(toolNames)) {
|
package/dist/bin/scan-tasks.js
CHANGED
|
@@ -1490,7 +1490,8 @@ async function writeMemory(record) {
|
|
|
1490
1490
|
has_error: record.has_error ? 1 : 0,
|
|
1491
1491
|
raw_text: record.raw_text,
|
|
1492
1492
|
vector: record.vector,
|
|
1493
|
-
version:
|
|
1493
|
+
version: 0,
|
|
1494
|
+
// Placeholder — assigned atomically at flush time
|
|
1494
1495
|
task_id: record.task_id ?? null,
|
|
1495
1496
|
importance: record.importance ?? 5,
|
|
1496
1497
|
status: record.status ?? "active",
|
|
@@ -1524,6 +1525,13 @@ async function flushBatch() {
|
|
|
1524
1525
|
_flushing = true;
|
|
1525
1526
|
try {
|
|
1526
1527
|
const batch = _pendingRecords.slice(0);
|
|
1528
|
+
const client = getClient();
|
|
1529
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1530
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1531
|
+
for (const row of batch) {
|
|
1532
|
+
row.version = baseVersion++;
|
|
1533
|
+
}
|
|
1534
|
+
_nextVersion = baseVersion;
|
|
1527
1535
|
const buildStmt = (row) => {
|
|
1528
1536
|
const hasVector = row.vector !== null;
|
|
1529
1537
|
const taskId = row.task_id ?? null;
|
|
@@ -1424,6 +1424,13 @@ async function flushBatch() {
|
|
|
1424
1424
|
_flushing = true;
|
|
1425
1425
|
try {
|
|
1426
1426
|
const batch = _pendingRecords.slice(0);
|
|
1427
|
+
const client = getClient();
|
|
1428
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1429
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1430
|
+
for (const row of batch) {
|
|
1431
|
+
row.version = baseVersion++;
|
|
1432
|
+
}
|
|
1433
|
+
_nextVersion = baseVersion;
|
|
1427
1434
|
const buildStmt = (row) => {
|
|
1428
1435
|
const hasVector = row.vector !== null;
|
|
1429
1436
|
const taskId = row.task_id ?? null;
|
package/dist/bin/wiki-sync.js
CHANGED
|
@@ -1424,6 +1424,13 @@ async function flushBatch() {
|
|
|
1424
1424
|
_flushing = true;
|
|
1425
1425
|
try {
|
|
1426
1426
|
const batch = _pendingRecords.slice(0);
|
|
1427
|
+
const client = getClient();
|
|
1428
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1429
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1430
|
+
for (const row of batch) {
|
|
1431
|
+
row.version = baseVersion++;
|
|
1432
|
+
}
|
|
1433
|
+
_nextVersion = baseVersion;
|
|
1427
1434
|
const buildStmt = (row) => {
|
|
1428
1435
|
const hasVector = row.vector !== null;
|
|
1429
1436
|
const taskId = row.task_id ?? null;
|
package/dist/gateway/index.js
CHANGED
|
@@ -2199,7 +2199,8 @@ async function writeMemory(record) {
|
|
|
2199
2199
|
has_error: record.has_error ? 1 : 0,
|
|
2200
2200
|
raw_text: record.raw_text,
|
|
2201
2201
|
vector: record.vector,
|
|
2202
|
-
version:
|
|
2202
|
+
version: 0,
|
|
2203
|
+
// Placeholder — assigned atomically at flush time
|
|
2203
2204
|
task_id: record.task_id ?? null,
|
|
2204
2205
|
importance: record.importance ?? 5,
|
|
2205
2206
|
status: record.status ?? "active",
|
|
@@ -2233,6 +2234,13 @@ async function flushBatch() {
|
|
|
2233
2234
|
_flushing = true;
|
|
2234
2235
|
try {
|
|
2235
2236
|
const batch = _pendingRecords.slice(0);
|
|
2237
|
+
const client = getClient();
|
|
2238
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
2239
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2240
|
+
for (const row of batch) {
|
|
2241
|
+
row.version = baseVersion++;
|
|
2242
|
+
}
|
|
2243
|
+
_nextVersion = baseVersion;
|
|
2236
2244
|
const buildStmt = (row) => {
|
|
2237
2245
|
const hasVector = row.vector !== null;
|
|
2238
2246
|
const taskId = row.task_id ?? null;
|
|
@@ -3172,6 +3180,42 @@ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSy
|
|
|
3172
3180
|
import path11 from "path";
|
|
3173
3181
|
import os4 from "os";
|
|
3174
3182
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3183
|
+
import { unlinkSync as unlinkSync2 } from "fs";
|
|
3184
|
+
function spawnLockPath(sessionName) {
|
|
3185
|
+
return path11.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3186
|
+
}
|
|
3187
|
+
function isProcessAlive(pid) {
|
|
3188
|
+
try {
|
|
3189
|
+
process.kill(pid, 0);
|
|
3190
|
+
return true;
|
|
3191
|
+
} catch {
|
|
3192
|
+
return false;
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
function acquireSpawnLock2(sessionName) {
|
|
3196
|
+
if (!existsSync10(SPAWN_LOCK_DIR)) {
|
|
3197
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
3198
|
+
}
|
|
3199
|
+
const lockFile = spawnLockPath(sessionName);
|
|
3200
|
+
if (existsSync10(lockFile)) {
|
|
3201
|
+
try {
|
|
3202
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
3203
|
+
const age = Date.now() - lock.timestamp;
|
|
3204
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
3205
|
+
return false;
|
|
3206
|
+
}
|
|
3207
|
+
} catch {
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
writeFileSync4(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
3211
|
+
return true;
|
|
3212
|
+
}
|
|
3213
|
+
function releaseSpawnLock2(sessionName) {
|
|
3214
|
+
try {
|
|
3215
|
+
unlinkSync2(spawnLockPath(sessionName));
|
|
3216
|
+
} catch {
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3175
3219
|
function resolveBehaviorsExporterScript() {
|
|
3176
3220
|
try {
|
|
3177
3221
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -3252,10 +3296,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
3252
3296
|
}
|
|
3253
3297
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
3254
3298
|
const base = employeeSessionName(employeeName, exeSession);
|
|
3255
|
-
if (!isAlive(base)) return 0;
|
|
3299
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
3256
3300
|
for (let i = 2; i <= maxInstances; i++) {
|
|
3257
3301
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
3258
|
-
if (!isAlive(candidate)) return i;
|
|
3302
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
3259
3303
|
}
|
|
3260
3304
|
return null;
|
|
3261
3305
|
}
|
|
@@ -3589,6 +3633,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3589
3633
|
command: spawnCommand
|
|
3590
3634
|
});
|
|
3591
3635
|
if (spawnResult.error) {
|
|
3636
|
+
releaseSpawnLock2(sessionName);
|
|
3592
3637
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
3593
3638
|
}
|
|
3594
3639
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -3626,6 +3671,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3626
3671
|
}
|
|
3627
3672
|
}
|
|
3628
3673
|
if (!booted) {
|
|
3674
|
+
releaseSpawnLock2(sessionName);
|
|
3629
3675
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
3630
3676
|
}
|
|
3631
3677
|
if (!useExeAgent) {
|
|
@@ -3642,9 +3688,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3642
3688
|
pid: 0,
|
|
3643
3689
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3644
3690
|
});
|
|
3691
|
+
releaseSpawnLock2(sessionName);
|
|
3645
3692
|
return { sessionName };
|
|
3646
3693
|
}
|
|
3647
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
3694
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
3648
3695
|
var init_tmux_routing = __esm({
|
|
3649
3696
|
"src/lib/tmux-routing.ts"() {
|
|
3650
3697
|
"use strict";
|
|
@@ -3656,6 +3703,7 @@ var init_tmux_routing = __esm({
|
|
|
3656
3703
|
init_provider_table();
|
|
3657
3704
|
init_intercom_queue();
|
|
3658
3705
|
init_plan_limits();
|
|
3706
|
+
SPAWN_LOCK_DIR = path11.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
3659
3707
|
SESSION_CACHE = path11.join(os4.homedir(), ".exe-os", "session-cache");
|
|
3660
3708
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
3661
3709
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
@@ -3923,7 +3971,7 @@ import os5 from "os";
|
|
|
3923
3971
|
import {
|
|
3924
3972
|
readFileSync as readFileSync10,
|
|
3925
3973
|
readdirSync,
|
|
3926
|
-
unlinkSync as
|
|
3974
|
+
unlinkSync as unlinkSync3,
|
|
3927
3975
|
existsSync as existsSync11,
|
|
3928
3976
|
rmdirSync
|
|
3929
3977
|
} from "fs";
|
|
@@ -4371,7 +4419,7 @@ var init_tasks_crud = __esm({
|
|
|
4371
4419
|
|
|
4372
4420
|
// src/lib/tasks-review.ts
|
|
4373
4421
|
import path14 from "path";
|
|
4374
|
-
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as
|
|
4422
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
4375
4423
|
async function countPendingReviews() {
|
|
4376
4424
|
const client = getClient();
|
|
4377
4425
|
const result = await client.execute({
|
|
@@ -4495,7 +4543,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4495
4543
|
if (existsSync13(cacheDir)) {
|
|
4496
4544
|
for (const f of readdirSync2(cacheDir)) {
|
|
4497
4545
|
if (f.startsWith("review-notified-")) {
|
|
4498
|
-
|
|
4546
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4499
4547
|
}
|
|
4500
4548
|
}
|
|
4501
4549
|
}
|
|
@@ -5096,7 +5144,7 @@ __export(tasks_exports, {
|
|
|
5096
5144
|
writeCheckpoint: () => writeCheckpoint
|
|
5097
5145
|
});
|
|
5098
5146
|
import path17 from "path";
|
|
5099
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, unlinkSync as
|
|
5147
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
5100
5148
|
async function createTask(input) {
|
|
5101
5149
|
const result = await createTaskCore(input);
|
|
5102
5150
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5122,7 +5170,7 @@ async function updateTask(input) {
|
|
|
5122
5170
|
writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5123
5171
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5124
5172
|
try {
|
|
5125
|
-
|
|
5173
|
+
unlinkSync5(cachePath);
|
|
5126
5174
|
} catch {
|
|
5127
5175
|
}
|
|
5128
5176
|
}
|
|
@@ -2109,6 +2109,42 @@ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSy
|
|
|
2109
2109
|
import path11 from "path";
|
|
2110
2110
|
import os5 from "os";
|
|
2111
2111
|
import { fileURLToPath } from "url";
|
|
2112
|
+
import { unlinkSync as unlinkSync2 } from "fs";
|
|
2113
|
+
function spawnLockPath(sessionName) {
|
|
2114
|
+
return path11.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
2115
|
+
}
|
|
2116
|
+
function isProcessAlive(pid) {
|
|
2117
|
+
try {
|
|
2118
|
+
process.kill(pid, 0);
|
|
2119
|
+
return true;
|
|
2120
|
+
} catch {
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
function acquireSpawnLock(sessionName) {
|
|
2125
|
+
if (!existsSync11(SPAWN_LOCK_DIR)) {
|
|
2126
|
+
mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
|
|
2127
|
+
}
|
|
2128
|
+
const lockFile = spawnLockPath(sessionName);
|
|
2129
|
+
if (existsSync11(lockFile)) {
|
|
2130
|
+
try {
|
|
2131
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
2132
|
+
const age = Date.now() - lock.timestamp;
|
|
2133
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
2134
|
+
return false;
|
|
2135
|
+
}
|
|
2136
|
+
} catch {
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
writeFileSync4(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
2140
|
+
return true;
|
|
2141
|
+
}
|
|
2142
|
+
function releaseSpawnLock(sessionName) {
|
|
2143
|
+
try {
|
|
2144
|
+
unlinkSync2(spawnLockPath(sessionName));
|
|
2145
|
+
} catch {
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2112
2148
|
function resolveBehaviorsExporterScript() {
|
|
2113
2149
|
try {
|
|
2114
2150
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -2178,10 +2214,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
2178
2214
|
}
|
|
2179
2215
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
2180
2216
|
const base = employeeSessionName(employeeName, exeSession);
|
|
2181
|
-
if (!isAlive(base)) return 0;
|
|
2217
|
+
if (!isAlive(base) && acquireSpawnLock(base)) return 0;
|
|
2182
2218
|
for (let i = 2; i <= maxInstances; i++) {
|
|
2183
2219
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
2184
|
-
if (!isAlive(candidate)) return i;
|
|
2220
|
+
if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
|
|
2185
2221
|
}
|
|
2186
2222
|
return null;
|
|
2187
2223
|
}
|
|
@@ -2493,6 +2529,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2493
2529
|
command: spawnCommand
|
|
2494
2530
|
});
|
|
2495
2531
|
if (spawnResult.error) {
|
|
2532
|
+
releaseSpawnLock(sessionName);
|
|
2496
2533
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
2497
2534
|
}
|
|
2498
2535
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -2530,6 +2567,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2530
2567
|
}
|
|
2531
2568
|
}
|
|
2532
2569
|
if (!booted) {
|
|
2570
|
+
releaseSpawnLock(sessionName);
|
|
2533
2571
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
2534
2572
|
}
|
|
2535
2573
|
if (!useExeAgent) {
|
|
@@ -2546,9 +2584,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2546
2584
|
pid: 0,
|
|
2547
2585
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2548
2586
|
});
|
|
2587
|
+
releaseSpawnLock(sessionName);
|
|
2549
2588
|
return { sessionName };
|
|
2550
2589
|
}
|
|
2551
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2590
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2552
2591
|
var init_tmux_routing = __esm({
|
|
2553
2592
|
"src/lib/tmux-routing.ts"() {
|
|
2554
2593
|
"use strict";
|
|
@@ -2560,6 +2599,7 @@ var init_tmux_routing = __esm({
|
|
|
2560
2599
|
init_provider_table();
|
|
2561
2600
|
init_intercom_queue();
|
|
2562
2601
|
init_plan_limits();
|
|
2602
|
+
SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
2563
2603
|
SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
|
|
2564
2604
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
2565
2605
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
@@ -2572,7 +2612,7 @@ var init_tmux_routing = __esm({
|
|
|
2572
2612
|
|
|
2573
2613
|
// src/lib/tasks-review.ts
|
|
2574
2614
|
import path12 from "path";
|
|
2575
|
-
import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as
|
|
2615
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2576
2616
|
var init_tasks_review = __esm({
|
|
2577
2617
|
"src/lib/tasks-review.ts"() {
|
|
2578
2618
|
"use strict";
|
|
@@ -2755,7 +2795,7 @@ var init_tasks_notify = __esm({
|
|
|
2755
2795
|
|
|
2756
2796
|
// src/lib/tasks.ts
|
|
2757
2797
|
import path15 from "path";
|
|
2758
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, unlinkSync as
|
|
2798
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4 } from "fs";
|
|
2759
2799
|
async function createTask(input) {
|
|
2760
2800
|
const result = await createTaskCore(input);
|
|
2761
2801
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -1489,7 +1489,8 @@ async function writeMemory(record) {
|
|
|
1489
1489
|
has_error: record.has_error ? 1 : 0,
|
|
1490
1490
|
raw_text: record.raw_text,
|
|
1491
1491
|
vector: record.vector,
|
|
1492
|
-
version:
|
|
1492
|
+
version: 0,
|
|
1493
|
+
// Placeholder — assigned atomically at flush time
|
|
1493
1494
|
task_id: record.task_id ?? null,
|
|
1494
1495
|
importance: record.importance ?? 5,
|
|
1495
1496
|
status: record.status ?? "active",
|
|
@@ -1523,6 +1524,13 @@ async function flushBatch() {
|
|
|
1523
1524
|
_flushing = true;
|
|
1524
1525
|
try {
|
|
1525
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;
|
|
1526
1534
|
const buildStmt = (row) => {
|
|
1527
1535
|
const hasVector = row.vector !== null;
|
|
1528
1536
|
const taskId = row.task_id ?? null;
|
|
@@ -1529,7 +1529,8 @@ async function writeMemory(record) {
|
|
|
1529
1529
|
has_error: record.has_error ? 1 : 0,
|
|
1530
1530
|
raw_text: record.raw_text,
|
|
1531
1531
|
vector: record.vector,
|
|
1532
|
-
version:
|
|
1532
|
+
version: 0,
|
|
1533
|
+
// Placeholder — assigned atomically at flush time
|
|
1533
1534
|
task_id: record.task_id ?? null,
|
|
1534
1535
|
importance: record.importance ?? 5,
|
|
1535
1536
|
status: record.status ?? "active",
|
|
@@ -1563,6 +1564,13 @@ async function flushBatch() {
|
|
|
1563
1564
|
_flushing = true;
|
|
1564
1565
|
try {
|
|
1565
1566
|
const batch = _pendingRecords.slice(0);
|
|
1567
|
+
const client = getClient();
|
|
1568
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1569
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1570
|
+
for (const row of batch) {
|
|
1571
|
+
row.version = baseVersion++;
|
|
1572
|
+
}
|
|
1573
|
+
_nextVersion = baseVersion;
|
|
1566
1574
|
const buildStmt = (row) => {
|
|
1567
1575
|
const hasVector = row.vector !== null;
|
|
1568
1576
|
const taskId = row.task_id ?? null;
|
|
@@ -3306,7 +3314,7 @@ function getActiveAgent() {
|
|
|
3306
3314
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
3307
3315
|
{ encoding: "utf8", timeout: 2e3 }
|
|
3308
3316
|
).trim();
|
|
3309
|
-
const empMatch = sessionName.match(/^(
|
|
3317
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
3310
3318
|
if (empMatch && empMatch[1] !== "exe") {
|
|
3311
3319
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
3312
3320
|
}
|
|
@@ -167,7 +167,7 @@ function getActiveAgent() {
|
|
|
167
167
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
168
168
|
{ encoding: "utf8", timeout: 2e3 }
|
|
169
169
|
).trim();
|
|
170
|
-
const empMatch = sessionName.match(/^(
|
|
170
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
171
171
|
if (empMatch && empMatch[1] !== "exe") {
|
|
172
172
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
173
173
|
}
|