@askexenow/exe-os 0.8.0 → 0.8.1
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/README.md +178 -79
- package/dist/bin/backfill-responses.js +160 -8
- package/dist/bin/backfill-vectors.js +130 -1
- package/dist/bin/cleanup-stale-review-tasks.js +130 -1
- package/dist/bin/cli.js +10111 -7540
- package/dist/bin/exe-agent.js +159 -1
- package/dist/bin/exe-assign.js +235 -16
- package/dist/bin/exe-boot.js +344 -472
- package/dist/bin/exe-call.js +145 -1
- package/dist/bin/exe-cloud.js +11 -0
- package/dist/bin/exe-dispatch.js +37 -24
- package/dist/bin/exe-doctor.js +130 -1
- package/dist/bin/exe-export-behaviors.js +150 -7
- package/dist/bin/exe-forget.js +822 -665
- package/dist/bin/exe-gateway.js +470 -62
- package/dist/bin/exe-heartbeat.js +133 -2
- package/dist/bin/exe-kill.js +150 -7
- package/dist/bin/exe-launch-agent.js +150 -7
- package/dist/bin/exe-new-employee.js +756 -224
- package/dist/bin/exe-pending-messages.js +132 -2
- package/dist/bin/exe-pending-notifications.js +130 -1
- package/dist/bin/exe-pending-reviews.js +132 -2
- package/dist/bin/exe-review.js +160 -8
- package/dist/bin/exe-search.js +2473 -2008
- package/dist/bin/exe-session-cleanup.js +238 -51
- package/dist/bin/exe-settings.js +11 -0
- package/dist/bin/exe-status.js +130 -1
- package/dist/bin/exe-team.js +130 -1
- package/dist/bin/git-sweep.js +272 -16
- package/dist/bin/graph-backfill.js +150 -7
- package/dist/bin/graph-export.js +150 -7
- package/dist/bin/install.js +5 -0
- package/dist/bin/scan-tasks.js +238 -19
- package/dist/bin/setup.js +1776 -10
- package/dist/bin/shard-migrate.js +150 -7
- package/dist/bin/update.js +9 -6
- package/dist/bin/wiki-sync.js +150 -7
- package/dist/gateway/index.js +470 -62
- package/dist/hooks/bug-report-worker.js +195 -35
- package/dist/hooks/commit-complete.js +272 -16
- package/dist/hooks/error-recall.js +2313 -1847
- package/dist/hooks/exe-heartbeat-hook.js +5 -0
- package/dist/hooks/ingest-worker.js +330 -58
- package/dist/hooks/ingest.js +11 -0
- package/dist/hooks/instructions-loaded.js +199 -10
- package/dist/hooks/notification.js +199 -10
- package/dist/hooks/post-compact.js +199 -10
- package/dist/hooks/pre-compact.js +199 -10
- package/dist/hooks/pre-tool-use.js +199 -10
- package/dist/hooks/prompt-ingest-worker.js +179 -14
- package/dist/hooks/prompt-submit.js +781 -285
- package/dist/hooks/response-ingest-worker.js +1900 -1405
- package/dist/hooks/session-end.js +456 -12
- package/dist/hooks/session-start.js +2188 -1724
- package/dist/hooks/stop.js +200 -10
- package/dist/hooks/subagent-stop.js +199 -10
- package/dist/hooks/summary-worker.js +604 -334
- package/dist/index.js +554 -61
- package/dist/lib/cloud-sync.js +5 -0
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +5 -0
- package/dist/lib/database.js +104 -0
- package/dist/lib/device-registry.js +109 -0
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employee-templates.js +53 -26
- package/dist/lib/employees.js +5 -0
- package/dist/lib/exe-daemon-client.js +5 -0
- package/dist/lib/exe-daemon.js +493 -79
- package/dist/lib/file-grep.js +20 -4
- package/dist/lib/hybrid-search.js +1435 -190
- package/dist/lib/identity-templates.js +126 -5
- package/dist/lib/identity.js +5 -0
- package/dist/lib/license.js +5 -0
- package/dist/lib/messaging.js +37 -24
- package/dist/lib/schedules.js +130 -1
- package/dist/lib/skill-learning.js +11 -0
- package/dist/lib/status-brief.js +5 -0
- package/dist/lib/store.js +199 -10
- package/dist/lib/task-router.js +72 -6
- package/dist/lib/tasks.js +179 -50
- package/dist/lib/tmux-routing.js +179 -46
- package/dist/mcp/server.js +2129 -1855
- package/dist/mcp/tools/create-task.js +86 -36
- package/dist/mcp/tools/deactivate-behavior.js +5 -0
- package/dist/mcp/tools/list-tasks.js +39 -11
- package/dist/mcp/tools/send-message.js +37 -24
- package/dist/mcp/tools/update-task.js +153 -38
- package/dist/runtime/index.js +451 -59
- package/dist/tui/App.js +454 -59
- package/package.json +1 -1
|
@@ -243,6 +243,27 @@ async function ensureSchema() {
|
|
|
243
243
|
});
|
|
244
244
|
} catch {
|
|
245
245
|
}
|
|
246
|
+
try {
|
|
247
|
+
await client.execute({
|
|
248
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
|
|
249
|
+
args: []
|
|
250
|
+
});
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
await client.execute({
|
|
255
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
|
|
256
|
+
args: []
|
|
257
|
+
});
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
await client.execute({
|
|
262
|
+
sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
|
|
263
|
+
args: []
|
|
264
|
+
});
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
246
267
|
try {
|
|
247
268
|
await client.execute({
|
|
248
269
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -653,6 +674,15 @@ async function ensureSchema() {
|
|
|
653
674
|
} catch {
|
|
654
675
|
}
|
|
655
676
|
}
|
|
677
|
+
for (const col of [
|
|
678
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
679
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
|
|
680
|
+
]) {
|
|
681
|
+
try {
|
|
682
|
+
await client.execute(col);
|
|
683
|
+
} catch {
|
|
684
|
+
}
|
|
685
|
+
}
|
|
656
686
|
await client.executeMultiple(`
|
|
657
687
|
CREATE INDEX IF NOT EXISTS idx_memories_workspace
|
|
658
688
|
ON memories(workspace_id);
|
|
@@ -717,6 +747,34 @@ async function ensureSchema() {
|
|
|
717
747
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
718
748
|
ON conversations(channel_id);
|
|
719
749
|
`);
|
|
750
|
+
try {
|
|
751
|
+
await client.execute({
|
|
752
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
753
|
+
args: []
|
|
754
|
+
});
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
await client.execute({
|
|
759
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
|
|
760
|
+
args: []
|
|
761
|
+
});
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
try {
|
|
765
|
+
await client.execute({
|
|
766
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
|
|
767
|
+
args: []
|
|
768
|
+
});
|
|
769
|
+
} catch {
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
await client.execute({
|
|
773
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
|
|
774
|
+
args: []
|
|
775
|
+
});
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
720
778
|
await client.executeMultiple(`
|
|
721
779
|
CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
|
|
722
780
|
content_text,
|
|
@@ -743,6 +801,52 @@ async function ensureSchema() {
|
|
|
743
801
|
VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
|
|
744
802
|
END;
|
|
745
803
|
`);
|
|
804
|
+
try {
|
|
805
|
+
await client.execute({
|
|
806
|
+
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
807
|
+
args: []
|
|
808
|
+
});
|
|
809
|
+
} catch {
|
|
810
|
+
}
|
|
811
|
+
try {
|
|
812
|
+
await client.execute(
|
|
813
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
|
|
814
|
+
);
|
|
815
|
+
} catch {
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
await client.execute({
|
|
819
|
+
sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
|
|
820
|
+
args: []
|
|
821
|
+
});
|
|
822
|
+
await client.execute({
|
|
823
|
+
sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
|
|
824
|
+
args: []
|
|
825
|
+
});
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
try {
|
|
829
|
+
await client.execute({
|
|
830
|
+
sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
|
|
831
|
+
args: []
|
|
832
|
+
});
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
await client.execute(
|
|
837
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
|
|
838
|
+
);
|
|
839
|
+
} catch {
|
|
840
|
+
}
|
|
841
|
+
for (const col of [
|
|
842
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
|
|
843
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
|
|
844
|
+
]) {
|
|
845
|
+
try {
|
|
846
|
+
await client.execute(col);
|
|
847
|
+
} catch {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
746
850
|
}
|
|
747
851
|
var _client, initTurso;
|
|
748
852
|
var init_database = __esm({
|
|
@@ -834,6 +938,11 @@ function normalizeSessionLifecycle(raw) {
|
|
|
834
938
|
const userSL = raw.sessionLifecycle ?? {};
|
|
835
939
|
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
836
940
|
}
|
|
941
|
+
function normalizeAutoUpdate(raw) {
|
|
942
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
943
|
+
const userAU = raw.autoUpdate ?? {};
|
|
944
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
945
|
+
}
|
|
837
946
|
async function loadConfig() {
|
|
838
947
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
839
948
|
await mkdir2(dir, { recursive: true });
|
|
@@ -856,6 +965,7 @@ async function loadConfig() {
|
|
|
856
965
|
}
|
|
857
966
|
normalizeScalingRoadmap(migratedCfg);
|
|
858
967
|
normalizeSessionLifecycle(migratedCfg);
|
|
968
|
+
normalizeAutoUpdate(migratedCfg);
|
|
859
969
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
860
970
|
if (config.dbPath.startsWith("~")) {
|
|
861
971
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -878,6 +988,7 @@ function loadConfigSync() {
|
|
|
878
988
|
const { config: migratedCfg } = migrateConfig(parsed);
|
|
879
989
|
normalizeScalingRoadmap(migratedCfg);
|
|
880
990
|
normalizeSessionLifecycle(migratedCfg);
|
|
991
|
+
normalizeAutoUpdate(migratedCfg);
|
|
881
992
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
882
993
|
} catch {
|
|
883
994
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
@@ -897,6 +1008,7 @@ async function loadConfigFrom(configPath) {
|
|
|
897
1008
|
const { config: migratedCfg } = migrateConfig(parsed);
|
|
898
1009
|
normalizeScalingRoadmap(migratedCfg);
|
|
899
1010
|
normalizeSessionLifecycle(migratedCfg);
|
|
1011
|
+
normalizeAutoUpdate(migratedCfg);
|
|
900
1012
|
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
901
1013
|
} catch {
|
|
902
1014
|
return { ...DEFAULT_CONFIG };
|
|
@@ -968,6 +1080,11 @@ var init_config = __esm({
|
|
|
968
1080
|
idleKillTicksRequired: 3,
|
|
969
1081
|
idleKillIntercomAckWindowMs: 1e4,
|
|
970
1082
|
maxAutoInstances: 10
|
|
1083
|
+
},
|
|
1084
|
+
autoUpdate: {
|
|
1085
|
+
checkOnBoot: true,
|
|
1086
|
+
autoInstall: false,
|
|
1087
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
971
1088
|
}
|
|
972
1089
|
};
|
|
973
1090
|
CONFIG_MIGRATIONS = [
|
|
@@ -1101,13 +1218,27 @@ async function ensureShardSchema(client) {
|
|
|
1101
1218
|
"ALTER TABLE memories ADD COLUMN document_id TEXT",
|
|
1102
1219
|
"ALTER TABLE memories ADD COLUMN user_id TEXT",
|
|
1103
1220
|
"ALTER TABLE memories ADD COLUMN char_offset INTEGER",
|
|
1104
|
-
"ALTER TABLE memories ADD COLUMN page_number INTEGER"
|
|
1221
|
+
"ALTER TABLE memories ADD COLUMN page_number INTEGER",
|
|
1222
|
+
// Source provenance columns (must match database.ts)
|
|
1223
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1224
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1225
|
+
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1226
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1105
1227
|
]) {
|
|
1106
1228
|
try {
|
|
1107
1229
|
await client.execute(col);
|
|
1108
1230
|
} catch {
|
|
1109
1231
|
}
|
|
1110
1232
|
}
|
|
1233
|
+
for (const idx of [
|
|
1234
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
1235
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
1236
|
+
]) {
|
|
1237
|
+
try {
|
|
1238
|
+
await client.execute(idx);
|
|
1239
|
+
} catch {
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1111
1242
|
try {
|
|
1112
1243
|
await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
|
|
1113
1244
|
} catch {
|
|
@@ -1218,12 +1349,23 @@ function getProjectName(cwd) {
|
|
|
1218
1349
|
const dir = cwd ?? process.cwd();
|
|
1219
1350
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
1220
1351
|
try {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1352
|
+
let repoRoot;
|
|
1353
|
+
try {
|
|
1354
|
+
const gitCommonDir = execSync("git rev-parse --path-format=absolute --git-common-dir", {
|
|
1355
|
+
cwd: dir,
|
|
1356
|
+
encoding: "utf8",
|
|
1357
|
+
timeout: 2e3,
|
|
1358
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1359
|
+
}).trim();
|
|
1360
|
+
repoRoot = path4.dirname(gitCommonDir);
|
|
1361
|
+
} catch {
|
|
1362
|
+
repoRoot = execSync("git rev-parse --show-toplevel", {
|
|
1363
|
+
cwd: dir,
|
|
1364
|
+
encoding: "utf8",
|
|
1365
|
+
timeout: 2e3,
|
|
1366
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1367
|
+
}).trim();
|
|
1368
|
+
}
|
|
1227
1369
|
_cached = path4.basename(repoRoot);
|
|
1228
1370
|
_cachedCwd = dir;
|
|
1229
1371
|
return _cached;
|
|
@@ -1828,9 +1970,10 @@ async function createTaskCore(input) {
|
|
|
1828
1970
|
} catch {
|
|
1829
1971
|
}
|
|
1830
1972
|
}
|
|
1973
|
+
const complexity = input.complexity ?? "standard";
|
|
1831
1974
|
await client.execute({
|
|
1832
|
-
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, created_at, updated_at)
|
|
1833
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1975
|
+
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
|
|
1976
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1834
1977
|
args: [
|
|
1835
1978
|
id,
|
|
1836
1979
|
input.title,
|
|
@@ -1844,6 +1987,11 @@ async function createTaskCore(input) {
|
|
|
1844
1987
|
parentTaskId,
|
|
1845
1988
|
input.reviewer ?? null,
|
|
1846
1989
|
input.context,
|
|
1990
|
+
input.budgetTokens ?? null,
|
|
1991
|
+
input.budgetFallbackModel ?? null,
|
|
1992
|
+
0,
|
|
1993
|
+
null,
|
|
1994
|
+
complexity,
|
|
1847
1995
|
now,
|
|
1848
1996
|
now
|
|
1849
1997
|
]
|
|
@@ -1859,7 +2007,11 @@ async function createTaskCore(input) {
|
|
|
1859
2007
|
taskFile,
|
|
1860
2008
|
createdAt: now,
|
|
1861
2009
|
updatedAt: now,
|
|
1862
|
-
warning
|
|
2010
|
+
warning,
|
|
2011
|
+
budgetTokens: input.budgetTokens ?? null,
|
|
2012
|
+
budgetFallbackModel: input.budgetFallbackModel ?? null,
|
|
2013
|
+
tokensUsed: 0,
|
|
2014
|
+
tokensWarnedAt: null
|
|
1863
2015
|
};
|
|
1864
2016
|
}
|
|
1865
2017
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
@@ -2138,11 +2290,12 @@ function queueIntercom(targetSession, reason) {
|
|
|
2138
2290
|
}
|
|
2139
2291
|
writeQueue(queue);
|
|
2140
2292
|
}
|
|
2141
|
-
var QUEUE_PATH, INTERCOM_LOG;
|
|
2293
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
2142
2294
|
var init_intercom_queue = __esm({
|
|
2143
2295
|
"src/lib/intercom-queue.ts"() {
|
|
2144
2296
|
"use strict";
|
|
2145
2297
|
QUEUE_PATH = path10.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
2298
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
2146
2299
|
INTERCOM_LOG = path10.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
2147
2300
|
}
|
|
2148
2301
|
});
|
|
@@ -2390,7 +2543,6 @@ var init_worktree = __esm({
|
|
|
2390
2543
|
});
|
|
2391
2544
|
|
|
2392
2545
|
// src/lib/tmux-routing.ts
|
|
2393
|
-
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
2394
2546
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, existsSync as existsSync12, appendFileSync as appendFileSync2 } from "fs";
|
|
2395
2547
|
import path14 from "path";
|
|
2396
2548
|
import os5 from "os";
|
|
@@ -2447,6 +2599,11 @@ function getSessionState(sessionName) {
|
|
|
2447
2599
|
if (!transport.isAlive(sessionName)) return "offline";
|
|
2448
2600
|
try {
|
|
2449
2601
|
const pane = transport.capturePane(sessionName, 5);
|
|
2602
|
+
if (!pane.includes("\u276F") && !pane.includes("Claude Code") && !BUSY_PATTERN.test(pane) && !/Running…/.test(pane)) {
|
|
2603
|
+
if (/\$\s*$/.test(pane) || /% $/.test(pane.trimEnd())) {
|
|
2604
|
+
return "no_claude";
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2450
2607
|
if (/Running…/.test(pane)) return "tool";
|
|
2451
2608
|
if (BUSY_PATTERN.test(pane)) return "thinking";
|
|
2452
2609
|
return "idle";
|
|
@@ -2454,10 +2611,6 @@ function getSessionState(sessionName) {
|
|
|
2454
2611
|
return "offline";
|
|
2455
2612
|
}
|
|
2456
2613
|
}
|
|
2457
|
-
function isSessionBusy(sessionName) {
|
|
2458
|
-
const state = getSessionState(sessionName);
|
|
2459
|
-
return state === "thinking" || state === "tool";
|
|
2460
|
-
}
|
|
2461
2614
|
function isExeSession(sessionName) {
|
|
2462
2615
|
return /^exe\d*$/.test(sessionName);
|
|
2463
2616
|
}
|
|
@@ -2477,7 +2630,14 @@ function sendIntercom(targetSession) {
|
|
|
2477
2630
|
logIntercom(`SKIP \u2192 ${targetSession} (session not found)`);
|
|
2478
2631
|
return "failed";
|
|
2479
2632
|
}
|
|
2480
|
-
|
|
2633
|
+
const sessionState = getSessionState(targetSession);
|
|
2634
|
+
if (sessionState === "no_claude") {
|
|
2635
|
+
queueIntercom(targetSession, "claude not running in session");
|
|
2636
|
+
recordDebounce(targetSession);
|
|
2637
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
|
|
2638
|
+
return "queued";
|
|
2639
|
+
}
|
|
2640
|
+
if (sessionState === "thinking" || sessionState === "tool") {
|
|
2481
2641
|
queueIntercom(targetSession, "session busy at send time");
|
|
2482
2642
|
recordDebounce(targetSession);
|
|
2483
2643
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
|
|
@@ -2489,25 +2649,14 @@ function sendIntercom(targetSession) {
|
|
|
2489
2649
|
}
|
|
2490
2650
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
2491
2651
|
recordDebounce(targetSession);
|
|
2492
|
-
|
|
2493
|
-
try {
|
|
2494
|
-
execSync7(`sleep ${INTERCOM_POLL_INTERVAL_S}`);
|
|
2495
|
-
} catch {
|
|
2496
|
-
}
|
|
2497
|
-
const state = getSessionState(targetSession);
|
|
2498
|
-
if (state === "thinking" || state === "tool") {
|
|
2499
|
-
logIntercom(`ACKNOWLEDGED \u2192 ${targetSession} (state=${state}, poll=${i + 1})`);
|
|
2500
|
-
return "acknowledged";
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (no state transition after ${INTERCOM_POLL_MAX_ATTEMPTS}s)`);
|
|
2652
|
+
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
2504
2653
|
return "delivered";
|
|
2505
2654
|
} catch {
|
|
2506
2655
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
2507
2656
|
return "failed";
|
|
2508
2657
|
}
|
|
2509
2658
|
}
|
|
2510
|
-
var SESSION_CACHE, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN
|
|
2659
|
+
var SESSION_CACHE, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2511
2660
|
var init_tmux_routing = __esm({
|
|
2512
2661
|
"src/lib/tmux-routing.ts"() {
|
|
2513
2662
|
"use strict";
|
|
@@ -2526,8 +2675,6 @@ var init_tmux_routing = __esm({
|
|
|
2526
2675
|
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2527
2676
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2528
2677
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
2529
|
-
INTERCOM_POLL_INTERVAL_S = 1;
|
|
2530
|
-
INTERCOM_POLL_MAX_ATTEMPTS = 8;
|
|
2531
2678
|
}
|
|
2532
2679
|
});
|
|
2533
2680
|
|
|
@@ -2664,6 +2811,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
2664
2811
|
"- **Approved:** mark this review task as done",
|
|
2665
2812
|
"- **Needs work:** re-open the original task with notes, mark this review as done"
|
|
2666
2813
|
].join("\n");
|
|
2814
|
+
const originalTaskId = String(row.id);
|
|
2667
2815
|
const reviewTask = await createTaskCore({
|
|
2668
2816
|
title: `Review: ${agent} completed "${taskTitle}"`,
|
|
2669
2817
|
assignedTo: reviewer,
|
|
@@ -2672,6 +2820,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
2672
2820
|
priority: "p1",
|
|
2673
2821
|
context: reviewContext,
|
|
2674
2822
|
taskFile: reviewFile,
|
|
2823
|
+
parentTaskId: originalTaskId,
|
|
2675
2824
|
skipDispatch: true
|
|
2676
2825
|
});
|
|
2677
2826
|
const reviewId = reviewTask.id;
|
|
@@ -2710,23 +2859,38 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2710
2859
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
2711
2860
|
try {
|
|
2712
2861
|
const client = getClient();
|
|
2713
|
-
const
|
|
2714
|
-
const
|
|
2715
|
-
|
|
2716
|
-
if (parts.length >= 3 && parts[0] === "review") {
|
|
2717
|
-
const agent = parts[1];
|
|
2718
|
-
const slug = parts.slice(2).join("-");
|
|
2719
|
-
const originalTaskFile = `exe/${agent}/${slug}.md`;
|
|
2862
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2863
|
+
const parentId = row.parent_task_id ? String(row.parent_task_id) : null;
|
|
2864
|
+
if (parentId) {
|
|
2720
2865
|
const result = await client.execute({
|
|
2721
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE
|
|
2722
|
-
args: [
|
|
2866
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ? AND status = 'needs_review'",
|
|
2867
|
+
args: [now, parentId]
|
|
2723
2868
|
});
|
|
2724
2869
|
if (result.rowsAffected > 0) {
|
|
2725
2870
|
process.stderr.write(
|
|
2726
|
-
`[review-cleanup] Cascaded original task to done: ${
|
|
2871
|
+
`[review-cleanup] Cascaded original task to done via parent_task_id: ${parentId}
|
|
2727
2872
|
`
|
|
2728
2873
|
);
|
|
2729
2874
|
}
|
|
2875
|
+
} else {
|
|
2876
|
+
const fileName = taskFile.split("/").pop() ?? "";
|
|
2877
|
+
const reviewPrefix = fileName.replace(".md", "");
|
|
2878
|
+
const parts = reviewPrefix.split("-");
|
|
2879
|
+
if (parts.length >= 3 && parts[0] === "review") {
|
|
2880
|
+
const agent = parts[1];
|
|
2881
|
+
const slug = parts.slice(2).join("-");
|
|
2882
|
+
const originalTaskFile = `exe/${agent}/${slug}.md`;
|
|
2883
|
+
const result = await client.execute({
|
|
2884
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
2885
|
+
args: [now, originalTaskFile]
|
|
2886
|
+
});
|
|
2887
|
+
if (result.rowsAffected > 0) {
|
|
2888
|
+
process.stderr.write(
|
|
2889
|
+
`[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
|
|
2890
|
+
`
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2730
2894
|
}
|
|
2731
2895
|
} catch (err) {
|
|
2732
2896
|
process.stderr.write(
|
|
@@ -2761,7 +2925,7 @@ var init_tasks_review = __esm({
|
|
|
2761
2925
|
|
|
2762
2926
|
// src/bin/exe-session-cleanup.ts
|
|
2763
2927
|
import crypto4 from "crypto";
|
|
2764
|
-
import { execSync as
|
|
2928
|
+
import { execSync as execSync7 } from "child_process";
|
|
2765
2929
|
|
|
2766
2930
|
// src/lib/store.ts
|
|
2767
2931
|
init_memory();
|
|
@@ -2856,6 +3020,11 @@ async function initStore(options) {
|
|
|
2856
3020
|
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
2857
3021
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2858
3022
|
}
|
|
3023
|
+
function classifyTier(record) {
|
|
3024
|
+
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
3025
|
+
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
3026
|
+
return 3;
|
|
3027
|
+
}
|
|
2859
3028
|
async function writeMemory(record) {
|
|
2860
3029
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
2861
3030
|
throw new Error(
|
|
@@ -2883,7 +3052,11 @@ async function writeMemory(record) {
|
|
|
2883
3052
|
document_id: record.document_id ?? null,
|
|
2884
3053
|
user_id: record.user_id ?? null,
|
|
2885
3054
|
char_offset: record.char_offset ?? null,
|
|
2886
|
-
page_number: record.page_number ?? null
|
|
3055
|
+
page_number: record.page_number ?? null,
|
|
3056
|
+
source_path: record.source_path ?? null,
|
|
3057
|
+
source_type: record.source_type ?? null,
|
|
3058
|
+
tier: record.tier ?? classifyTier(record),
|
|
3059
|
+
supersedes_id: record.supersedes_id ?? null
|
|
2887
3060
|
};
|
|
2888
3061
|
_pendingRecords.push(dbRow);
|
|
2889
3062
|
if (_flushTimer === null) {
|
|
@@ -2915,20 +3088,26 @@ async function flushBatch() {
|
|
|
2915
3088
|
const userId = row.user_id ?? null;
|
|
2916
3089
|
const charOffset = row.char_offset ?? null;
|
|
2917
3090
|
const pageNumber = row.page_number ?? null;
|
|
3091
|
+
const sourcePath = row.source_path ?? null;
|
|
3092
|
+
const sourceType = row.source_type ?? null;
|
|
3093
|
+
const tier = row.tier ?? 3;
|
|
3094
|
+
const supersedesId = row.supersedes_id ?? null;
|
|
2918
3095
|
return {
|
|
2919
3096
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
2920
3097
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
2921
3098
|
tool_name, project_name,
|
|
2922
3099
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2923
3100
|
confidence, last_accessed,
|
|
2924
|
-
workspace_id, document_id, user_id, char_offset, page_number
|
|
2925
|
-
|
|
3101
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
3102
|
+
source_path, source_type, tier, supersedes_id)
|
|
3103
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2926
3104
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
2927
3105
|
tool_name, project_name,
|
|
2928
3106
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2929
3107
|
confidence, last_accessed,
|
|
2930
|
-
workspace_id, document_id, user_id, char_offset, page_number
|
|
2931
|
-
|
|
3108
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
3109
|
+
source_path, source_type, tier, supersedes_id)
|
|
3110
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2932
3111
|
args: hasVector ? [
|
|
2933
3112
|
row.id,
|
|
2934
3113
|
row.agent_id,
|
|
@@ -2950,7 +3129,11 @@ async function flushBatch() {
|
|
|
2950
3129
|
documentId,
|
|
2951
3130
|
userId,
|
|
2952
3131
|
charOffset,
|
|
2953
|
-
pageNumber
|
|
3132
|
+
pageNumber,
|
|
3133
|
+
sourcePath,
|
|
3134
|
+
sourceType,
|
|
3135
|
+
tier,
|
|
3136
|
+
supersedesId
|
|
2954
3137
|
] : [
|
|
2955
3138
|
row.id,
|
|
2956
3139
|
row.agent_id,
|
|
@@ -2971,7 +3154,11 @@ async function flushBatch() {
|
|
|
2971
3154
|
documentId,
|
|
2972
3155
|
userId,
|
|
2973
3156
|
charOffset,
|
|
2974
|
-
pageNumber
|
|
3157
|
+
pageNumber,
|
|
3158
|
+
sourcePath,
|
|
3159
|
+
sourceType,
|
|
3160
|
+
tier,
|
|
3161
|
+
supersedesId
|
|
2975
3162
|
]
|
|
2976
3163
|
};
|
|
2977
3164
|
};
|
|
@@ -3150,7 +3337,7 @@ try {
|
|
|
3150
3337
|
const taskList = result.rows.map((r) => `"${String(r.title)}"`).join(", ");
|
|
3151
3338
|
const msg = `session-ended: ${agentName} session terminated unexpectedly. ${count} task(s) marked blocked: ${taskList}`;
|
|
3152
3339
|
try {
|
|
3153
|
-
|
|
3340
|
+
execSync7(`tmux send-keys -t ${JSON.stringify(exeSession)} ${JSON.stringify(msg)} Enter`, {
|
|
3154
3341
|
timeout: 3e3
|
|
3155
3342
|
});
|
|
3156
3343
|
} catch {
|
package/dist/bin/exe-settings.js
CHANGED
|
@@ -84,6 +84,11 @@ var DEFAULT_CONFIG = {
|
|
|
84
84
|
idleKillTicksRequired: 3,
|
|
85
85
|
idleKillIntercomAckWindowMs: 1e4,
|
|
86
86
|
maxAutoInstances: 10
|
|
87
|
+
},
|
|
88
|
+
autoUpdate: {
|
|
89
|
+
checkOnBoot: true,
|
|
90
|
+
autoInstall: false,
|
|
91
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
87
92
|
}
|
|
88
93
|
};
|
|
89
94
|
function migrateLegacyConfig(raw) {
|
|
@@ -141,6 +146,11 @@ function normalizeSessionLifecycle(raw) {
|
|
|
141
146
|
const userSL = raw.sessionLifecycle ?? {};
|
|
142
147
|
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
143
148
|
}
|
|
149
|
+
function normalizeAutoUpdate(raw) {
|
|
150
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
151
|
+
const userAU = raw.autoUpdate ?? {};
|
|
152
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
153
|
+
}
|
|
144
154
|
async function loadConfig() {
|
|
145
155
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
146
156
|
await mkdir(dir, { recursive: true });
|
|
@@ -163,6 +173,7 @@ async function loadConfig() {
|
|
|
163
173
|
}
|
|
164
174
|
normalizeScalingRoadmap(migratedCfg);
|
|
165
175
|
normalizeSessionLifecycle(migratedCfg);
|
|
176
|
+
normalizeAutoUpdate(migratedCfg);
|
|
166
177
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
167
178
|
if (config.dbPath.startsWith("~")) {
|
|
168
179
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|