@askexenow/exe-os 0.9.113 → 0.9.115
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/agentic-ontology-backfill.js +36 -12
- package/dist/bin/agentic-reflection-backfill.js +36 -12
- package/dist/bin/agentic-semantic-label.js +36 -12
- package/dist/bin/backfill-conversations.js +36 -12
- package/dist/bin/backfill-responses.js +36 -12
- package/dist/bin/backfill-vectors.js +36 -12
- package/dist/bin/bulk-sync-postgres.js +36 -12
- package/dist/bin/cleanup-stale-review-tasks.js +470 -113
- package/dist/bin/cli.js +413 -62
- package/dist/bin/exe-agent.js +27 -0
- package/dist/bin/exe-assign.js +36 -12
- package/dist/bin/exe-boot.js +246 -54
- package/dist/bin/exe-call.js +8 -0
- package/dist/bin/exe-cloud.js +47 -12
- package/dist/bin/exe-dispatch.js +348 -53
- package/dist/bin/exe-doctor.js +51 -13
- package/dist/bin/exe-export-behaviors.js +37 -12
- package/dist/bin/exe-forget.js +36 -12
- package/dist/bin/exe-gateway.js +348 -53
- package/dist/bin/exe-heartbeat.js +471 -113
- package/dist/bin/exe-kill.js +36 -12
- package/dist/bin/exe-launch-agent.js +117 -18
- package/dist/bin/exe-new-employee.js +9 -1
- package/dist/bin/exe-pending-messages.js +452 -95
- package/dist/bin/exe-pending-notifications.js +452 -95
- package/dist/bin/exe-pending-reviews.js +452 -95
- package/dist/bin/exe-rename.js +36 -12
- package/dist/bin/exe-review.js +36 -12
- package/dist/bin/exe-search.js +37 -12
- package/dist/bin/exe-session-cleanup.js +348 -53
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +46 -13
- package/dist/bin/exe-start-opencode.js +46 -13
- package/dist/bin/exe-status.js +460 -114
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +36 -12
- package/dist/bin/git-sweep.js +348 -53
- package/dist/bin/graph-backfill.js +36 -12
- package/dist/bin/graph-export.js +36 -12
- package/dist/bin/install.js +9 -1
- package/dist/bin/intercom-check.js +255 -53
- package/dist/bin/scan-tasks.js +348 -53
- package/dist/bin/setup.js +74 -12
- package/dist/bin/shard-migrate.js +36 -12
- package/dist/gateway/index.js +348 -53
- package/dist/hooks/bug-report-worker.js +348 -53
- package/dist/hooks/codex-stop-task-finalizer.js +308 -37
- package/dist/hooks/commit-complete.js +348 -53
- package/dist/hooks/error-recall.js +37 -12
- package/dist/hooks/ingest.js +363 -54
- package/dist/hooks/instructions-loaded.js +36 -12
- package/dist/hooks/notification.js +36 -12
- package/dist/hooks/post-compact.js +426 -72
- package/dist/hooks/post-tool-combined.js +501 -146
- package/dist/hooks/pre-compact.js +348 -53
- package/dist/hooks/pre-tool-use.js +92 -13
- package/dist/hooks/prompt-submit.js +348 -53
- package/dist/hooks/session-end.js +158 -53
- package/dist/hooks/session-start.js +66 -13
- package/dist/hooks/stop.js +420 -72
- package/dist/hooks/subagent-stop.js +419 -72
- package/dist/hooks/summary-worker.js +442 -121
- package/dist/index.js +375 -53
- package/dist/lib/agent-config.js +8 -0
- package/dist/lib/cloud-sync.js +35 -12
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +9 -1
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employees.js +8 -0
- package/dist/lib/exe-daemon.js +524 -60
- package/dist/lib/hybrid-search.js +37 -12
- package/dist/lib/keychain.js +25 -13
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +36 -12
- package/dist/lib/skill-learning.js +21 -0
- package/dist/lib/store.js +36 -12
- package/dist/lib/tasks.js +324 -41
- package/dist/lib/tmux-routing.js +324 -41
- package/dist/mcp/server.js +374 -54
- package/dist/mcp/tools/create-task.js +324 -41
- package/dist/mcp/tools/list-tasks.js +406 -57
- package/dist/mcp/tools/send-message.js +395 -74
- package/dist/mcp/tools/update-task.js +324 -41
- package/dist/runtime/index.js +375 -53
- package/dist/tui/App.js +377 -55
- package/package.json +1 -1
|
@@ -210,6 +210,17 @@ function normalizeOrchestration(raw) {
|
|
|
210
210
|
const userOrg = raw.orchestration ?? {};
|
|
211
211
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
212
212
|
}
|
|
213
|
+
function normalizeCloudEndpoint(raw) {
|
|
214
|
+
const cloud = raw.cloud;
|
|
215
|
+
if (!cloud?.endpoint) return;
|
|
216
|
+
const ep = String(cloud.endpoint);
|
|
217
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
218
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
219
|
+
process.stderr.write(
|
|
220
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
213
224
|
async function loadConfig() {
|
|
214
225
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
215
226
|
await ensurePrivateDir(dir);
|
|
@@ -235,6 +246,7 @@ async function loadConfig() {
|
|
|
235
246
|
normalizeSessionLifecycle(migratedCfg);
|
|
236
247
|
normalizeAutoUpdate(migratedCfg);
|
|
237
248
|
normalizeOrchestration(migratedCfg);
|
|
249
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
238
250
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
239
251
|
if (config.dbPath.startsWith("~")) {
|
|
240
252
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -263,6 +275,7 @@ function loadConfigSync() {
|
|
|
263
275
|
normalizeSessionLifecycle(migratedCfg);
|
|
264
276
|
normalizeAutoUpdate(migratedCfg);
|
|
265
277
|
normalizeOrchestration(migratedCfg);
|
|
278
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
266
279
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
267
280
|
if (config.dbPath.startsWith("~")) {
|
|
268
281
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -423,6 +436,7 @@ __export(agent_config_exports, {
|
|
|
423
436
|
clearAgentRuntime: () => clearAgentRuntime,
|
|
424
437
|
getAgentRuntime: () => getAgentRuntime,
|
|
425
438
|
loadAgentConfig: () => loadAgentConfig,
|
|
439
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
426
440
|
saveAgentConfig: () => saveAgentConfig,
|
|
427
441
|
setAgentMcps: () => setAgentMcps,
|
|
428
442
|
setAgentRuntime: () => setAgentRuntime
|
|
@@ -451,6 +465,13 @@ function getAgentRuntime(agentId) {
|
|
|
451
465
|
if (orgDefault) return orgDefault;
|
|
452
466
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
453
467
|
}
|
|
468
|
+
function normalizeCcModelName(model) {
|
|
469
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
470
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
471
|
+
ccModel += "[1m]";
|
|
472
|
+
}
|
|
473
|
+
return ccModel;
|
|
474
|
+
}
|
|
454
475
|
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
455
476
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
456
477
|
if (!knownModels) {
|
|
@@ -3742,7 +3763,7 @@ var init_database = __esm({
|
|
|
3742
3763
|
});
|
|
3743
3764
|
|
|
3744
3765
|
// src/lib/keychain.ts
|
|
3745
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3766
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3746
3767
|
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
3747
3768
|
import { execSync as execSync3 } from "child_process";
|
|
3748
3769
|
import path7 from "path";
|
|
@@ -3777,12 +3798,14 @@ function linuxSecretAvailable() {
|
|
|
3777
3798
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3778
3799
|
if (process.platform !== "linux") return false;
|
|
3779
3800
|
try {
|
|
3780
|
-
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3781
3801
|
const st = statSync3(keyPath);
|
|
3782
3802
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3803
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3783
3804
|
if (uid === 0) return true;
|
|
3784
3805
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3785
|
-
|
|
3806
|
+
if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
|
|
3807
|
+
if (!linuxSecretAvailable()) return true;
|
|
3808
|
+
return false;
|
|
3786
3809
|
} catch {
|
|
3787
3810
|
return false;
|
|
3788
3811
|
}
|
|
@@ -3932,15 +3955,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3932
3955
|
await mkdir3(dir, { recursive: true });
|
|
3933
3956
|
const keyPath = getKeyPath();
|
|
3934
3957
|
const machineKey = deriveMachineKey();
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3958
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
3959
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
3960
|
+
const tmpPath = keyPath + ".tmp";
|
|
3961
|
+
try {
|
|
3962
|
+
if (existsSync8(keyPath)) {
|
|
3963
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
3967
|
+
await chmod2(tmpPath, 384);
|
|
3968
|
+
await rename(tmpPath, keyPath);
|
|
3969
|
+
} catch (err) {
|
|
3970
|
+
try {
|
|
3971
|
+
await unlink(tmpPath);
|
|
3972
|
+
} catch {
|
|
3973
|
+
}
|
|
3974
|
+
throw err;
|
|
3940
3975
|
}
|
|
3941
|
-
|
|
3942
|
-
await chmod2(keyPath, 384);
|
|
3943
|
-
return "plaintext";
|
|
3976
|
+
return result;
|
|
3944
3977
|
}
|
|
3945
3978
|
async function getMasterKey() {
|
|
3946
3979
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4007,7 +4040,7 @@ async function getMasterKey() {
|
|
|
4007
4040
|
b64Value = content;
|
|
4008
4041
|
}
|
|
4009
4042
|
const key = Buffer.from(b64Value, "base64");
|
|
4010
|
-
if (
|
|
4043
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
4011
4044
|
return key;
|
|
4012
4045
|
}
|
|
4013
4046
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -6620,6 +6653,7 @@ var init_provider_table = __esm({
|
|
|
6620
6653
|
// src/lib/intercom-queue.ts
|
|
6621
6654
|
var intercom_queue_exports = {};
|
|
6622
6655
|
__export(intercom_queue_exports, {
|
|
6656
|
+
_resetDrainGuard: () => _resetDrainGuard,
|
|
6623
6657
|
clearQueueForAgent: () => clearQueueForAgent,
|
|
6624
6658
|
drainForSession: () => drainForSession,
|
|
6625
6659
|
drainQueue: () => drainQueue,
|
|
@@ -6665,38 +6699,47 @@ function queueIntercom(targetSession, reason) {
|
|
|
6665
6699
|
writeQueue(queue);
|
|
6666
6700
|
}
|
|
6667
6701
|
function drainQueue(isSessionBusy2, sendKeys) {
|
|
6702
|
+
if (_draining) {
|
|
6703
|
+
logQueue("SKIP_DRAIN \u2014 previous drain still running (possible tmux hang)");
|
|
6704
|
+
return { drained: 0, failed: 0 };
|
|
6705
|
+
}
|
|
6668
6706
|
const queue = readQueue();
|
|
6669
6707
|
if (queue.length === 0) return { drained: 0, failed: 0 };
|
|
6708
|
+
_draining = true;
|
|
6670
6709
|
const remaining = [];
|
|
6671
6710
|
let drained = 0;
|
|
6672
6711
|
let failed = 0;
|
|
6673
|
-
|
|
6674
|
-
const
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6712
|
+
try {
|
|
6713
|
+
for (const item of queue) {
|
|
6714
|
+
const age = Date.now() - new Date(item.queuedAt).getTime();
|
|
6715
|
+
if (age > TTL_MS) {
|
|
6716
|
+
logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
|
|
6717
|
+
failed++;
|
|
6718
|
+
continue;
|
|
6719
|
+
}
|
|
6720
|
+
try {
|
|
6721
|
+
if (!isSessionBusy2(item.targetSession)) {
|
|
6722
|
+
const success = sendKeys(item.targetSession);
|
|
6723
|
+
if (success) {
|
|
6724
|
+
logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
|
|
6725
|
+
drained++;
|
|
6726
|
+
continue;
|
|
6727
|
+
}
|
|
6687
6728
|
}
|
|
6729
|
+
} catch {
|
|
6688
6730
|
}
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6731
|
+
item.attempts++;
|
|
6732
|
+
if (item.attempts >= MAX_RETRIES2) {
|
|
6733
|
+
logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
|
|
6734
|
+
failed++;
|
|
6735
|
+
continue;
|
|
6736
|
+
}
|
|
6737
|
+
remaining.push(item);
|
|
6696
6738
|
}
|
|
6697
|
-
remaining
|
|
6739
|
+
writeQueue(remaining);
|
|
6740
|
+
} finally {
|
|
6741
|
+
_draining = false;
|
|
6698
6742
|
}
|
|
6699
|
-
writeQueue(remaining);
|
|
6700
6743
|
return { drained, failed };
|
|
6701
6744
|
}
|
|
6702
6745
|
function drainForSession(targetSession, sendKeys) {
|
|
@@ -6721,6 +6764,9 @@ function clearQueueForAgent(agentName) {
|
|
|
6721
6764
|
logQueue(`CLEARED ${before - filtered.length} stale item(s) for ${agentName}`);
|
|
6722
6765
|
}
|
|
6723
6766
|
}
|
|
6767
|
+
function _resetDrainGuard() {
|
|
6768
|
+
_draining = false;
|
|
6769
|
+
}
|
|
6724
6770
|
function logQueue(msg) {
|
|
6725
6771
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
|
|
6726
6772
|
`;
|
|
@@ -6732,13 +6778,14 @@ function logQueue(msg) {
|
|
|
6732
6778
|
} catch {
|
|
6733
6779
|
}
|
|
6734
6780
|
}
|
|
6735
|
-
var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
6781
|
+
var QUEUE_PATH, MAX_RETRIES2, TTL_MS, _draining, INTERCOM_LOG;
|
|
6736
6782
|
var init_intercom_queue = __esm({
|
|
6737
6783
|
"src/lib/intercom-queue.ts"() {
|
|
6738
6784
|
"use strict";
|
|
6739
6785
|
QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
6740
6786
|
MAX_RETRIES2 = 5;
|
|
6741
6787
|
TTL_MS = 60 * 60 * 1e3;
|
|
6788
|
+
_draining = false;
|
|
6742
6789
|
INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
6743
6790
|
}
|
|
6744
6791
|
});
|
|
@@ -8379,6 +8426,13 @@ var init_embedder = __esm({
|
|
|
8379
8426
|
});
|
|
8380
8427
|
|
|
8381
8428
|
// src/lib/behaviors.ts
|
|
8429
|
+
var behaviors_exports = {};
|
|
8430
|
+
__export(behaviors_exports, {
|
|
8431
|
+
deactivateBehavior: () => deactivateBehavior,
|
|
8432
|
+
listBehaviors: () => listBehaviors,
|
|
8433
|
+
listBehaviorsByDomain: () => listBehaviorsByDomain,
|
|
8434
|
+
storeBehavior: () => storeBehavior
|
|
8435
|
+
});
|
|
8382
8436
|
import crypto4 from "crypto";
|
|
8383
8437
|
async function storeBehavior(opts) {
|
|
8384
8438
|
try {
|
|
@@ -8415,6 +8469,76 @@ async function storeBehavior(opts) {
|
|
|
8415
8469
|
});
|
|
8416
8470
|
return id;
|
|
8417
8471
|
}
|
|
8472
|
+
async function listBehaviors(agentId, projectName, limit = 30) {
|
|
8473
|
+
const client = getClient();
|
|
8474
|
+
const result = await client.execute({
|
|
8475
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id
|
|
8476
|
+
FROM behaviors
|
|
8477
|
+
WHERE agent_id = ? AND active = 1
|
|
8478
|
+
AND (project_name IS NULL OR project_name = ?)
|
|
8479
|
+
ORDER BY
|
|
8480
|
+
CASE WHEN priority = 'p0' THEN 0
|
|
8481
|
+
WHEN priority = 'p1' THEN 1
|
|
8482
|
+
ELSE 2 END,
|
|
8483
|
+
CASE
|
|
8484
|
+
WHEN lower(coalesce(domain, '')) IN ('identity', 'title', 'role') THEN 0
|
|
8485
|
+
WHEN lower(content) LIKE '%identity%' THEN 0
|
|
8486
|
+
WHEN lower(content) LIKE '%title%' THEN 0
|
|
8487
|
+
WHEN lower(content) LIKE '%who are you%' THEN 0
|
|
8488
|
+
ELSE 1
|
|
8489
|
+
END,
|
|
8490
|
+
updated_at DESC
|
|
8491
|
+
LIMIT ?`,
|
|
8492
|
+
args: [agentId, projectName ?? "", limit]
|
|
8493
|
+
});
|
|
8494
|
+
return result.rows.map((r) => ({
|
|
8495
|
+
id: String(r.id),
|
|
8496
|
+
agent_id: String(r.agent_id),
|
|
8497
|
+
project_name: r.project_name ? String(r.project_name) : null,
|
|
8498
|
+
domain: r.domain ? String(r.domain) : null,
|
|
8499
|
+
priority: String(r.priority || "p1"),
|
|
8500
|
+
content: String(r.content),
|
|
8501
|
+
active: Number(r.active),
|
|
8502
|
+
created_at: String(r.created_at),
|
|
8503
|
+
updated_at: String(r.updated_at),
|
|
8504
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
|
|
8505
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
8506
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
8507
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
8508
|
+
}));
|
|
8509
|
+
}
|
|
8510
|
+
async function listBehaviorsByDomain(agentId, domain) {
|
|
8511
|
+
const client = getClient();
|
|
8512
|
+
const result = await client.execute({
|
|
8513
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
8514
|
+
FROM behaviors
|
|
8515
|
+
WHERE agent_id = ? AND domain = ? AND active = 1`,
|
|
8516
|
+
args: [agentId, domain]
|
|
8517
|
+
});
|
|
8518
|
+
return result.rows.map((r) => ({
|
|
8519
|
+
id: String(r.id),
|
|
8520
|
+
agent_id: String(r.agent_id),
|
|
8521
|
+
project_name: r.project_name ? String(r.project_name) : null,
|
|
8522
|
+
domain: r.domain ? String(r.domain) : null,
|
|
8523
|
+
priority: String(r.priority || "p1"),
|
|
8524
|
+
content: String(r.content),
|
|
8525
|
+
active: Number(r.active),
|
|
8526
|
+
created_at: String(r.created_at),
|
|
8527
|
+
updated_at: String(r.updated_at),
|
|
8528
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
|
|
8529
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
8530
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
8531
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
8532
|
+
}));
|
|
8533
|
+
}
|
|
8534
|
+
async function deactivateBehavior(id) {
|
|
8535
|
+
const client = getClient();
|
|
8536
|
+
const result = await client.execute({
|
|
8537
|
+
sql: `UPDATE behaviors SET active = 0, updated_at = ? WHERE id = ? AND active = 1`,
|
|
8538
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), id]
|
|
8539
|
+
});
|
|
8540
|
+
return (result.rowsAffected ?? 0) > 0;
|
|
8541
|
+
}
|
|
8418
8542
|
var init_behaviors = __esm({
|
|
8419
8543
|
"src/lib/behaviors.ts"() {
|
|
8420
8544
|
"use strict";
|
|
@@ -8823,6 +8947,20 @@ async function updateTask(input) {
|
|
|
8823
8947
|
notifyTaskDone();
|
|
8824
8948
|
}
|
|
8825
8949
|
await markTaskNotificationsRead(taskFile);
|
|
8950
|
+
if (input.status === "needs_review" && !isCoordinator) {
|
|
8951
|
+
try {
|
|
8952
|
+
const { writeNotification: writeNotification2 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
|
|
8953
|
+
await writeNotification2({
|
|
8954
|
+
agentId: String(row.assigned_to),
|
|
8955
|
+
agentRole: String(row.assigned_to),
|
|
8956
|
+
event: "task_complete",
|
|
8957
|
+
project: String(row.project_name),
|
|
8958
|
+
summary: `"${String(row.title)}" is ready for review`,
|
|
8959
|
+
taskFile
|
|
8960
|
+
});
|
|
8961
|
+
} catch {
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8826
8964
|
if (input.status === "done" || input.status === "closed") {
|
|
8827
8965
|
try {
|
|
8828
8966
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
@@ -9255,18 +9393,31 @@ function acquireSpawnLock2(sessionName) {
|
|
|
9255
9393
|
mkdirSync9(SPAWN_LOCK_DIR, { recursive: true });
|
|
9256
9394
|
}
|
|
9257
9395
|
const lockFile = spawnLockPath(sessionName);
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9396
|
+
const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
|
|
9397
|
+
const { openSync: openSync3, closeSync: closeSync3, writeSync } = __require("fs");
|
|
9398
|
+
const { constants } = __require("fs");
|
|
9399
|
+
try {
|
|
9400
|
+
const fd = openSync3(lockFile, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 420);
|
|
9401
|
+
writeSync(fd, lockData);
|
|
9402
|
+
closeSync3(fd);
|
|
9403
|
+
return true;
|
|
9404
|
+
} catch (err) {
|
|
9405
|
+
if (err?.code !== "EEXIST") {
|
|
9406
|
+
return true;
|
|
9266
9407
|
}
|
|
9267
9408
|
}
|
|
9268
|
-
|
|
9269
|
-
|
|
9409
|
+
try {
|
|
9410
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
9411
|
+
const age = Date.now() - lock.timestamp;
|
|
9412
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
9413
|
+
return false;
|
|
9414
|
+
}
|
|
9415
|
+
writeFileSync8(lockFile, lockData);
|
|
9416
|
+
return true;
|
|
9417
|
+
} catch {
|
|
9418
|
+
writeFileSync8(lockFile, lockData);
|
|
9419
|
+
return true;
|
|
9420
|
+
}
|
|
9270
9421
|
}
|
|
9271
9422
|
function releaseSpawnLock2(sessionName) {
|
|
9272
9423
|
try {
|
|
@@ -9345,6 +9496,21 @@ function parseParentExe(sessionName, agentId) {
|
|
|
9345
9496
|
function extractRootExe(name) {
|
|
9346
9497
|
if (!name) return null;
|
|
9347
9498
|
if (!name.includes("-")) return name;
|
|
9499
|
+
try {
|
|
9500
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
9501
|
+
if (roster.length > 0) {
|
|
9502
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
9503
|
+
for (const agentName of sortedNames) {
|
|
9504
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9505
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
9506
|
+
const match = name.match(regex);
|
|
9507
|
+
if (match) {
|
|
9508
|
+
return extractRootExe(match[1]);
|
|
9509
|
+
}
|
|
9510
|
+
}
|
|
9511
|
+
}
|
|
9512
|
+
} catch {
|
|
9513
|
+
}
|
|
9348
9514
|
const parts = name.split("-").filter(Boolean);
|
|
9349
9515
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
9350
9516
|
}
|
|
@@ -9363,6 +9529,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
9363
9529
|
function getParentExe(sessionKey) {
|
|
9364
9530
|
try {
|
|
9365
9531
|
const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
9532
|
+
if (data.registeredAt) {
|
|
9533
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
9534
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
9535
|
+
}
|
|
9366
9536
|
return data.parentExe || null;
|
|
9367
9537
|
} catch {
|
|
9368
9538
|
return null;
|
|
@@ -9911,7 +10081,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9911
10081
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
9912
10082
|
} catch {
|
|
9913
10083
|
}
|
|
9914
|
-
let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName}`;
|
|
10084
|
+
let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName} EXE_SESSION_START_ISO=${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
9915
10085
|
if (ccProvider !== DEFAULT_PROVIDER) {
|
|
9916
10086
|
const cfg = PROVIDER_TABLE[ccProvider];
|
|
9917
10087
|
if (cfg?.apiKeyEnv) {
|
|
@@ -9946,10 +10116,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9946
10116
|
}
|
|
9947
10117
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
9948
10118
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
ccModel += "[1m]";
|
|
9952
|
-
}
|
|
10119
|
+
const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
|
|
10120
|
+
const ccModel = normalizeCcModelName2(agentRtConfig.model);
|
|
9953
10121
|
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
9954
10122
|
}
|
|
9955
10123
|
}
|
|
@@ -10050,7 +10218,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
10050
10218
|
releaseSpawnLock2(sessionName);
|
|
10051
10219
|
return { sessionName };
|
|
10052
10220
|
}
|
|
10053
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
10221
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, PARENT_EXE_CACHE_TTL_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
10054
10222
|
var init_tmux_routing = __esm({
|
|
10055
10223
|
"src/lib/tmux-routing.ts"() {
|
|
10056
10224
|
"use strict";
|
|
@@ -10070,6 +10238,7 @@ var init_tmux_routing = __esm({
|
|
|
10070
10238
|
SESSION_CACHE = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
10071
10239
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
10072
10240
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
10241
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
10073
10242
|
VERIFY_PANE_LINES = 200;
|
|
10074
10243
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
10075
10244
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
@@ -10577,7 +10746,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
10577
10746
|
taskFile
|
|
10578
10747
|
});
|
|
10579
10748
|
const originalPriority = String(row.priority).toLowerCase();
|
|
10580
|
-
const
|
|
10749
|
+
const resultLower = result?.toLowerCase() ?? "";
|
|
10750
|
+
const hasTestEvidence = (
|
|
10751
|
+
// Vitest/Jest output patterns (hard to fake without actually running tests)
|
|
10752
|
+
/\d+\s+pass(ed|ing)/.test(resultLower) || /test files?\s+\d+\s+passed/.test(resultLower) || /tests?\s+\d+\s+passed/.test(resultLower)
|
|
10753
|
+
);
|
|
10754
|
+
const hasNoFailures = !/fail(ed|ure|ing)|error/i.test(resultLower);
|
|
10755
|
+
const autoApprove = originalPriority === "p2" && hasTestEvidence && hasNoFailures;
|
|
10581
10756
|
if (!autoApprove) {
|
|
10582
10757
|
try {
|
|
10583
10758
|
const key = getSessionKey();
|
|
@@ -10585,6 +10760,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
10585
10760
|
if (exeSession) {
|
|
10586
10761
|
sendIntercom(exeSession);
|
|
10587
10762
|
}
|
|
10763
|
+
if (reviewer && reviewer !== coordinatorName && reviewer !== exeSession) {
|
|
10764
|
+
const { employeeSessionName: employeeSessionName2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
10765
|
+
if (exeSession) {
|
|
10766
|
+
const reviewerSession = employeeSessionName2(reviewer, exeSession);
|
|
10767
|
+
sendIntercom(reviewerSession);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10588
10770
|
} catch {
|
|
10589
10771
|
}
|
|
10590
10772
|
}
|
|
@@ -11273,6 +11455,26 @@ async function main2() {
|
|
|
11273
11455
|
} catch {
|
|
11274
11456
|
}
|
|
11275
11457
|
}
|
|
11458
|
+
if (!isCoordinator) {
|
|
11459
|
+
try {
|
|
11460
|
+
const { listBehaviors: listBehaviors2 } = await Promise.resolve().then(() => (init_behaviors(), behaviors_exports));
|
|
11461
|
+
const sessionStart = process.env.EXE_SESSION_START_ISO ?? "";
|
|
11462
|
+
if (sessionStart) {
|
|
11463
|
+
const behaviors = await listBehaviors2(agent);
|
|
11464
|
+
const newBehaviors = behaviors.filter(
|
|
11465
|
+
(b) => b.created_at > sessionStart
|
|
11466
|
+
);
|
|
11467
|
+
if (newBehaviors.length > 0) {
|
|
11468
|
+
process.stdout.write("\n[NEW BEHAVIORS \u2014 apply these immediately]\n");
|
|
11469
|
+
for (const b of newBehaviors) {
|
|
11470
|
+
process.stdout.write(` [${b.priority}] ${b.content.slice(0, 300)}
|
|
11471
|
+
`);
|
|
11472
|
+
}
|
|
11473
|
+
}
|
|
11474
|
+
}
|
|
11475
|
+
} catch {
|
|
11476
|
+
}
|
|
11477
|
+
}
|
|
11276
11478
|
try {
|
|
11277
11479
|
const { readUnreadNotifications: readUnreadNotifications2, markAsRead: markAsRead2 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
|
|
11278
11480
|
const notifications = await readUnreadNotifications2(agent);
|