@askexenow/exe-os 0.8.83 → 0.8.85
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 +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +97 -2
- package/dist/bin/cli.js +14350 -12518
- package/dist/bin/exe-agent.js +97 -88
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1257 -320
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +210 -34
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +97 -2
- package/dist/bin/exe-gateway.js +550 -171
- package/dist/bin/exe-healthcheck.js +1 -0
- package/dist/bin/exe-heartbeat.js +100 -5
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +902 -80
- package/dist/bin/exe-new-employee.js +38 -8
- package/dist/bin/exe-pending-messages.js +96 -2
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +98 -3
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +989 -226
- package/dist/bin/exe-session-cleanup.js +4806 -1665
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-status.js +97 -2
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +899 -207
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +38 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +904 -211
- package/dist/bin/setup.js +867 -268
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +548 -166
- package/dist/hooks/bug-report-worker.js +208 -23
- package/dist/hooks/commit-complete.js +897 -205
- package/dist/hooks/error-recall.js +988 -226
- package/dist/hooks/ingest-worker.js +1638 -1194
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +714 -104
- package/dist/hooks/pre-compact.js +897 -205
- package/dist/hooks/pre-tool-use.js +742 -123
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +995 -233
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +3941 -400
- package/dist/hooks/session-start.js +1001 -226
- package/dist/hooks/stop.js +725 -115
- package/dist/hooks/subagent-stop.js +714 -104
- package/dist/hooks/summary-worker.js +1964 -1330
- package/dist/index.js +1651 -1053
- package/dist/lib/cloud-sync.js +907 -86
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +1955 -922
- package/dist/lib/hybrid-search.js +988 -226
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +8 -1
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +111 -22
- package/dist/lib/tmux-routing.js +120 -31
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5222 -475
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +120 -22
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +31 -1
- package/dist/mcp/tools/send-message.js +8 -1
- package/dist/mcp/tools/update-task.js +39 -10
- package/dist/runtime/index.js +911 -219
- package/dist/tui/App.js +997 -295
- package/package.json +6 -1
package/dist/gateway/index.js
CHANGED
|
@@ -632,6 +632,7 @@ __export(employees_exports, {
|
|
|
632
632
|
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
633
633
|
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
634
634
|
addEmployee: () => addEmployee,
|
|
635
|
+
baseAgentName: () => baseAgentName,
|
|
635
636
|
canCoordinate: () => canCoordinate,
|
|
636
637
|
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
637
638
|
getCoordinatorName: () => getCoordinatorName,
|
|
@@ -728,6 +729,14 @@ function hasRole(agentName, role) {
|
|
|
728
729
|
const emp = getEmployee(employees, agentName);
|
|
729
730
|
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
730
731
|
}
|
|
732
|
+
function baseAgentName(name, employees) {
|
|
733
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
734
|
+
if (!match) return name;
|
|
735
|
+
const base = match[1];
|
|
736
|
+
const roster = employees ?? loadEmployeesSync();
|
|
737
|
+
if (getEmployee(roster, base)) return base;
|
|
738
|
+
return name;
|
|
739
|
+
}
|
|
731
740
|
function isMultiInstance(agentName, employees) {
|
|
732
741
|
const roster = employees ?? loadEmployeesSync();
|
|
733
742
|
const emp = getEmployee(roster, agentName);
|
|
@@ -844,6 +853,12 @@ function getClient() {
|
|
|
844
853
|
if (!_resilientClient) {
|
|
845
854
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
846
855
|
}
|
|
856
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
857
|
+
return _resilientClient;
|
|
858
|
+
}
|
|
859
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
860
|
+
return _daemonClient;
|
|
861
|
+
}
|
|
847
862
|
return _resilientClient;
|
|
848
863
|
}
|
|
849
864
|
function getRawClient() {
|
|
@@ -1332,6 +1347,12 @@ async function ensureSchema() {
|
|
|
1332
1347
|
} catch {
|
|
1333
1348
|
}
|
|
1334
1349
|
}
|
|
1350
|
+
try {
|
|
1351
|
+
await client.execute(
|
|
1352
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1353
|
+
);
|
|
1354
|
+
} catch {
|
|
1355
|
+
}
|
|
1335
1356
|
await client.executeMultiple(`
|
|
1336
1357
|
CREATE TABLE IF NOT EXISTS entities (
|
|
1337
1358
|
id TEXT PRIMARY KEY,
|
|
@@ -1384,7 +1405,30 @@ async function ensureSchema() {
|
|
|
1384
1405
|
entity_id TEXT NOT NULL,
|
|
1385
1406
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1386
1407
|
);
|
|
1408
|
+
|
|
1409
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1410
|
+
name,
|
|
1411
|
+
content=entities,
|
|
1412
|
+
content_rowid=rowid
|
|
1413
|
+
);
|
|
1414
|
+
|
|
1415
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1416
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1417
|
+
END;
|
|
1418
|
+
|
|
1419
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1420
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1421
|
+
END;
|
|
1422
|
+
|
|
1423
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1424
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1425
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1426
|
+
END;
|
|
1387
1427
|
`);
|
|
1428
|
+
try {
|
|
1429
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1430
|
+
} catch {
|
|
1431
|
+
}
|
|
1388
1432
|
await client.executeMultiple(`
|
|
1389
1433
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1390
1434
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1565,6 +1609,33 @@ async function ensureSchema() {
|
|
|
1565
1609
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1566
1610
|
ON conversations(channel_id);
|
|
1567
1611
|
`);
|
|
1612
|
+
await client.executeMultiple(`
|
|
1613
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1614
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1615
|
+
agent_id TEXT NOT NULL,
|
|
1616
|
+
session_name TEXT,
|
|
1617
|
+
task_id TEXT,
|
|
1618
|
+
project_name TEXT,
|
|
1619
|
+
started_at TEXT NOT NULL
|
|
1620
|
+
);
|
|
1621
|
+
|
|
1622
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1623
|
+
ON session_agent_map(agent_id);
|
|
1624
|
+
`);
|
|
1625
|
+
try {
|
|
1626
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1627
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1628
|
+
await client.execute({
|
|
1629
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1630
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1631
|
+
FROM memories
|
|
1632
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1633
|
+
GROUP BY session_id, agent_id`,
|
|
1634
|
+
args: []
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1568
1639
|
try {
|
|
1569
1640
|
await client.execute({
|
|
1570
1641
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1698,15 +1769,41 @@ async function ensureSchema() {
|
|
|
1698
1769
|
});
|
|
1699
1770
|
} catch {
|
|
1700
1771
|
}
|
|
1772
|
+
for (const col of [
|
|
1773
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1774
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1775
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1776
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1777
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1778
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1779
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1780
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1781
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1782
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1783
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1784
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1785
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1786
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1787
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1788
|
+
]) {
|
|
1789
|
+
try {
|
|
1790
|
+
await client.execute(col);
|
|
1791
|
+
} catch {
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1701
1794
|
}
|
|
1702
1795
|
async function disposeDatabase() {
|
|
1796
|
+
if (_daemonClient) {
|
|
1797
|
+
_daemonClient.close();
|
|
1798
|
+
_daemonClient = null;
|
|
1799
|
+
}
|
|
1703
1800
|
if (_client) {
|
|
1704
1801
|
_client.close();
|
|
1705
1802
|
_client = null;
|
|
1706
1803
|
_resilientClient = null;
|
|
1707
1804
|
}
|
|
1708
1805
|
}
|
|
1709
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1806
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1710
1807
|
var init_database = __esm({
|
|
1711
1808
|
"src/lib/database.ts"() {
|
|
1712
1809
|
"use strict";
|
|
@@ -1714,6 +1811,7 @@ var init_database = __esm({
|
|
|
1714
1811
|
init_employees();
|
|
1715
1812
|
_client = null;
|
|
1716
1813
|
_resilientClient = null;
|
|
1814
|
+
_daemonClient = null;
|
|
1717
1815
|
initTurso = initDatabase;
|
|
1718
1816
|
disposeTurso = disposeDatabase;
|
|
1719
1817
|
}
|
|
@@ -1748,10 +1846,12 @@ function handleData(chunk) {
|
|
|
1748
1846
|
if (!line) continue;
|
|
1749
1847
|
try {
|
|
1750
1848
|
const response = JSON.parse(line);
|
|
1751
|
-
const
|
|
1849
|
+
const id = response.id;
|
|
1850
|
+
if (!id) continue;
|
|
1851
|
+
const entry = _pending.get(id);
|
|
1752
1852
|
if (entry) {
|
|
1753
1853
|
clearTimeout(entry.timer);
|
|
1754
|
-
_pending.delete(
|
|
1854
|
+
_pending.delete(id);
|
|
1755
1855
|
entry.resolve(response);
|
|
1756
1856
|
}
|
|
1757
1857
|
} catch {
|
|
@@ -1922,6 +2022,9 @@ async function connectEmbedDaemon() {
|
|
|
1922
2022
|
return false;
|
|
1923
2023
|
}
|
|
1924
2024
|
function sendRequest(texts, priority) {
|
|
2025
|
+
return sendDaemonRequest({ texts, priority });
|
|
2026
|
+
}
|
|
2027
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1925
2028
|
return new Promise((resolve) => {
|
|
1926
2029
|
if (!_socket || !_connected) {
|
|
1927
2030
|
resolve({ error: "Not connected" });
|
|
@@ -1931,10 +2034,10 @@ function sendRequest(texts, priority) {
|
|
|
1931
2034
|
const timer = setTimeout(() => {
|
|
1932
2035
|
_pending.delete(id);
|
|
1933
2036
|
resolve({ error: "Request timeout" });
|
|
1934
|
-
},
|
|
2037
|
+
}, timeoutMs);
|
|
1935
2038
|
_pending.set(id, { resolve, timer });
|
|
1936
2039
|
try {
|
|
1937
|
-
_socket.write(JSON.stringify({ id,
|
|
2040
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1938
2041
|
} catch {
|
|
1939
2042
|
clearTimeout(timer);
|
|
1940
2043
|
_pending.delete(id);
|
|
@@ -1944,30 +2047,11 @@ function sendRequest(texts, priority) {
|
|
|
1944
2047
|
}
|
|
1945
2048
|
async function pingDaemon() {
|
|
1946
2049
|
if (!_socket || !_connected) return null;
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
}, 5e3);
|
|
1953
|
-
_pending.set(id, {
|
|
1954
|
-
resolve: (data) => {
|
|
1955
|
-
if (data.health) {
|
|
1956
|
-
resolve(data.health);
|
|
1957
|
-
} else {
|
|
1958
|
-
resolve(null);
|
|
1959
|
-
}
|
|
1960
|
-
},
|
|
1961
|
-
timer
|
|
1962
|
-
});
|
|
1963
|
-
try {
|
|
1964
|
-
_socket.write(JSON.stringify({ id, type: "health" }) + "\n");
|
|
1965
|
-
} catch {
|
|
1966
|
-
clearTimeout(timer);
|
|
1967
|
-
_pending.delete(id);
|
|
1968
|
-
resolve(null);
|
|
1969
|
-
}
|
|
1970
|
-
});
|
|
2050
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
2051
|
+
if (response.health) {
|
|
2052
|
+
return response.health;
|
|
2053
|
+
}
|
|
2054
|
+
return null;
|
|
1971
2055
|
}
|
|
1972
2056
|
function killAndRespawnDaemon() {
|
|
1973
2057
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
@@ -2172,12 +2256,20 @@ async function getMasterKey() {
|
|
|
2172
2256
|
}
|
|
2173
2257
|
const keyPath = getKeyPath();
|
|
2174
2258
|
if (!existsSync4(keyPath)) {
|
|
2259
|
+
process.stderr.write(
|
|
2260
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2261
|
+
`
|
|
2262
|
+
);
|
|
2175
2263
|
return null;
|
|
2176
2264
|
}
|
|
2177
2265
|
try {
|
|
2178
2266
|
const content = await readFile3(keyPath, "utf-8");
|
|
2179
2267
|
return Buffer.from(content.trim(), "base64");
|
|
2180
|
-
} catch {
|
|
2268
|
+
} catch (err) {
|
|
2269
|
+
process.stderr.write(
|
|
2270
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
2271
|
+
`
|
|
2272
|
+
);
|
|
2181
2273
|
return null;
|
|
2182
2274
|
}
|
|
2183
2275
|
}
|
|
@@ -2633,6 +2725,7 @@ __export(store_exports, {
|
|
|
2633
2725
|
vectorToBlob: () => vectorToBlob,
|
|
2634
2726
|
writeMemory: () => writeMemory
|
|
2635
2727
|
});
|
|
2728
|
+
import { createHash } from "crypto";
|
|
2636
2729
|
function isBusyError2(err) {
|
|
2637
2730
|
if (err instanceof Error) {
|
|
2638
2731
|
const msg = err.message.toLowerCase();
|
|
@@ -2706,12 +2799,52 @@ function classifyTier(record) {
|
|
|
2706
2799
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
2707
2800
|
return 3;
|
|
2708
2801
|
}
|
|
2802
|
+
function inferFilePaths(record) {
|
|
2803
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
2804
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
2805
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
2806
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
2807
|
+
}
|
|
2808
|
+
function inferCommitHash(record) {
|
|
2809
|
+
if (record.tool_name !== "Bash") return null;
|
|
2810
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
2811
|
+
return match ? match[1] : null;
|
|
2812
|
+
}
|
|
2813
|
+
function inferLanguageType(record) {
|
|
2814
|
+
const text = record.raw_text;
|
|
2815
|
+
if (!text || text.length < 10) return null;
|
|
2816
|
+
const trimmed = text.trimStart();
|
|
2817
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
2818
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
2819
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
2820
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
2821
|
+
return "mixed";
|
|
2822
|
+
}
|
|
2823
|
+
function inferDomain(record) {
|
|
2824
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
2825
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
2826
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
2827
|
+
return null;
|
|
2828
|
+
}
|
|
2709
2829
|
async function writeMemory(record) {
|
|
2710
2830
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
2711
2831
|
throw new Error(
|
|
2712
2832
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
2713
2833
|
);
|
|
2714
2834
|
}
|
|
2835
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
2836
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
try {
|
|
2840
|
+
const client = getClient();
|
|
2841
|
+
const existing = await client.execute({
|
|
2842
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
2843
|
+
args: [contentHash, record.agent_id]
|
|
2844
|
+
});
|
|
2845
|
+
if (existing.rows.length > 0) return;
|
|
2846
|
+
} catch {
|
|
2847
|
+
}
|
|
2715
2848
|
const dbRow = {
|
|
2716
2849
|
id: record.id,
|
|
2717
2850
|
agent_id: record.agent_id,
|
|
@@ -2741,7 +2874,23 @@ async function writeMemory(record) {
|
|
|
2741
2874
|
supersedes_id: record.supersedes_id ?? null,
|
|
2742
2875
|
draft: record.draft ? 1 : 0,
|
|
2743
2876
|
memory_type: record.memory_type ?? "raw",
|
|
2744
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
2877
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
2878
|
+
content_hash: contentHash,
|
|
2879
|
+
intent: record.intent ?? null,
|
|
2880
|
+
outcome: record.outcome ?? null,
|
|
2881
|
+
domain: record.domain ?? inferDomain(record),
|
|
2882
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
2883
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
2884
|
+
chain_position: record.chain_position ?? null,
|
|
2885
|
+
review_status: record.review_status ?? null,
|
|
2886
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
2887
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
2888
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
2889
|
+
duration_ms: record.duration_ms ?? null,
|
|
2890
|
+
token_cost: record.token_cost ?? null,
|
|
2891
|
+
audience: record.audience ?? null,
|
|
2892
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
2893
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
2745
2894
|
};
|
|
2746
2895
|
_pendingRecords.push(dbRow);
|
|
2747
2896
|
orgBus.emit({
|
|
@@ -2799,80 +2948,85 @@ async function flushBatch() {
|
|
|
2799
2948
|
const draft = row.draft ? 1 : 0;
|
|
2800
2949
|
const memoryType = row.memory_type ?? "raw";
|
|
2801
2950
|
const trajectory = row.trajectory ?? null;
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2951
|
+
const contentHash = row.content_hash ?? null;
|
|
2952
|
+
const intent = row.intent ?? null;
|
|
2953
|
+
const outcome = row.outcome ?? null;
|
|
2954
|
+
const domain = row.domain ?? null;
|
|
2955
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
2956
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
2957
|
+
const chainPosition = row.chain_position ?? null;
|
|
2958
|
+
const reviewStatus = row.review_status ?? null;
|
|
2959
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
2960
|
+
const filePaths = row.file_paths ?? null;
|
|
2961
|
+
const commitHash = row.commit_hash ?? null;
|
|
2962
|
+
const durationMs = row.duration_ms ?? null;
|
|
2963
|
+
const tokenCost = row.token_cost ?? null;
|
|
2964
|
+
const audience = row.audience ?? null;
|
|
2965
|
+
const languageType = row.language_type ?? null;
|
|
2966
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
2967
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
2805
2968
|
tool_name, project_name,
|
|
2806
2969
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2807
2970
|
confidence, last_accessed,
|
|
2808
2971
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2809
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
pageNumber,
|
|
2868
|
-
sourcePath,
|
|
2869
|
-
sourceType,
|
|
2870
|
-
tier,
|
|
2871
|
-
supersedesId,
|
|
2872
|
-
draft,
|
|
2873
|
-
memoryType,
|
|
2874
|
-
trajectory
|
|
2875
|
-
]
|
|
2972
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
2973
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
2974
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
2975
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
2976
|
+
const metaArgs = [
|
|
2977
|
+
intent,
|
|
2978
|
+
outcome,
|
|
2979
|
+
domain,
|
|
2980
|
+
referencedEntities,
|
|
2981
|
+
retrievalCount,
|
|
2982
|
+
chainPosition,
|
|
2983
|
+
reviewStatus,
|
|
2984
|
+
contextWindowPct,
|
|
2985
|
+
filePaths,
|
|
2986
|
+
commitHash,
|
|
2987
|
+
durationMs,
|
|
2988
|
+
tokenCost,
|
|
2989
|
+
audience,
|
|
2990
|
+
languageType,
|
|
2991
|
+
parentMemoryId
|
|
2992
|
+
];
|
|
2993
|
+
const baseArgs = [
|
|
2994
|
+
row.id,
|
|
2995
|
+
row.agent_id,
|
|
2996
|
+
row.agent_role,
|
|
2997
|
+
row.session_id,
|
|
2998
|
+
row.timestamp,
|
|
2999
|
+
row.tool_name,
|
|
3000
|
+
row.project_name,
|
|
3001
|
+
row.has_error,
|
|
3002
|
+
row.raw_text
|
|
3003
|
+
];
|
|
3004
|
+
const sharedArgs = [
|
|
3005
|
+
row.version,
|
|
3006
|
+
taskId,
|
|
3007
|
+
importance,
|
|
3008
|
+
status,
|
|
3009
|
+
confidence,
|
|
3010
|
+
lastAccessed,
|
|
3011
|
+
workspaceId,
|
|
3012
|
+
documentId,
|
|
3013
|
+
userId,
|
|
3014
|
+
charOffset,
|
|
3015
|
+
pageNumber,
|
|
3016
|
+
sourcePath,
|
|
3017
|
+
sourceType,
|
|
3018
|
+
tier,
|
|
3019
|
+
supersedesId,
|
|
3020
|
+
draft,
|
|
3021
|
+
memoryType,
|
|
3022
|
+
trajectory,
|
|
3023
|
+
contentHash
|
|
3024
|
+
];
|
|
3025
|
+
return {
|
|
3026
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
3027
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
3028
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3029
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
2876
3030
|
};
|
|
2877
3031
|
};
|
|
2878
3032
|
const globalClient = getClient();
|
|
@@ -3259,7 +3413,7 @@ var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS2;
|
|
|
3259
3413
|
var init_wiki_client = __esm({
|
|
3260
3414
|
"src/lib/wiki-client.ts"() {
|
|
3261
3415
|
"use strict";
|
|
3262
|
-
LOCAL_WIKI_URL = "http://localhost:3001";
|
|
3416
|
+
LOCAL_WIKI_URL = process.env.EXE_WIKI_URL || "http://localhost:3001";
|
|
3263
3417
|
REQUEST_TIMEOUT_MS2 = 8e3;
|
|
3264
3418
|
}
|
|
3265
3419
|
});
|
|
@@ -4743,6 +4897,7 @@ var init_task_scope = __esm({
|
|
|
4743
4897
|
// src/lib/tasks-crud.ts
|
|
4744
4898
|
import crypto5 from "crypto";
|
|
4745
4899
|
import path12 from "path";
|
|
4900
|
+
import os8 from "os";
|
|
4746
4901
|
import { execSync as execSync4 } from "child_process";
|
|
4747
4902
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4748
4903
|
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
@@ -4786,6 +4941,35 @@ function extractParentFromContext(contextBody) {
|
|
|
4786
4941
|
function slugify(title) {
|
|
4787
4942
|
return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
4788
4943
|
}
|
|
4944
|
+
function buildKeywordIndex() {
|
|
4945
|
+
const idx = /* @__PURE__ */ new Map();
|
|
4946
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
4947
|
+
for (const kw of keywords) {
|
|
4948
|
+
const existing = idx.get(kw) ?? [];
|
|
4949
|
+
existing.push(role);
|
|
4950
|
+
idx.set(kw, existing);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
return idx;
|
|
4954
|
+
}
|
|
4955
|
+
function checkLaneAffinity(title, context, assigneeName) {
|
|
4956
|
+
const employees = loadEmployeesSync();
|
|
4957
|
+
const employee = employees.find((e) => e.name === assigneeName);
|
|
4958
|
+
if (!employee) return void 0;
|
|
4959
|
+
const assigneeRole = employee.role;
|
|
4960
|
+
const text = `${title} ${context}`.toLowerCase();
|
|
4961
|
+
const matchedRoles = /* @__PURE__ */ new Set();
|
|
4962
|
+
for (const [keyword, roles] of KEYWORD_INDEX) {
|
|
4963
|
+
if (text.includes(keyword)) {
|
|
4964
|
+
for (const role of roles) matchedRoles.add(role);
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4967
|
+
if (matchedRoles.size === 0) return void 0;
|
|
4968
|
+
if (matchedRoles.has(assigneeRole)) return void 0;
|
|
4969
|
+
if (assigneeRole === "COO") return void 0;
|
|
4970
|
+
const expectedRoles = Array.from(matchedRoles).join(" or ");
|
|
4971
|
+
return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
|
|
4972
|
+
}
|
|
4789
4973
|
async function resolveTask(client, identifier, scopeSession) {
|
|
4790
4974
|
const scope = sessionScopeFilter(scopeSession);
|
|
4791
4975
|
let result = await client.execute({
|
|
@@ -4835,7 +5019,14 @@ async function createTaskCore(input) {
|
|
|
4835
5019
|
const id = crypto5.randomUUID();
|
|
4836
5020
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4837
5021
|
const slug = slugify(input.title);
|
|
4838
|
-
|
|
5022
|
+
let earlySessionScope = null;
|
|
5023
|
+
try {
|
|
5024
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
5025
|
+
earlySessionScope = resolveExeSession2();
|
|
5026
|
+
} catch {
|
|
5027
|
+
}
|
|
5028
|
+
const scope = earlySessionScope ?? "default";
|
|
5029
|
+
const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
|
|
4839
5030
|
let blockedById = null;
|
|
4840
5031
|
const initialStatus = input.blockedBy ? "blocked" : "open";
|
|
4841
5032
|
if (input.blockedBy) {
|
|
@@ -4875,6 +5066,13 @@ async function createTaskCore(input) {
|
|
|
4875
5066
|
if (dupCheck.rows.length > 0) {
|
|
4876
5067
|
warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
|
|
4877
5068
|
}
|
|
5069
|
+
if (!process.env.DISABLE_LANE_AFFINITY) {
|
|
5070
|
+
const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
|
|
5071
|
+
if (laneWarning) {
|
|
5072
|
+
warning = warning ? `${warning}
|
|
5073
|
+
${laneWarning}` : laneWarning;
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
4878
5076
|
if (input.baseDir) {
|
|
4879
5077
|
try {
|
|
4880
5078
|
await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
@@ -4885,12 +5083,7 @@ async function createTaskCore(input) {
|
|
|
4885
5083
|
}
|
|
4886
5084
|
}
|
|
4887
5085
|
const complexity = input.complexity ?? "standard";
|
|
4888
|
-
|
|
4889
|
-
try {
|
|
4890
|
-
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
4891
|
-
sessionScope = resolveExeSession2();
|
|
4892
|
-
} catch {
|
|
4893
|
-
}
|
|
5086
|
+
const sessionScope = earlySessionScope;
|
|
4894
5087
|
await client.execute({
|
|
4895
5088
|
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, session_scope, created_at, updated_at)
|
|
4896
5089
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -4917,6 +5110,39 @@ async function createTaskCore(input) {
|
|
|
4917
5110
|
now
|
|
4918
5111
|
]
|
|
4919
5112
|
});
|
|
5113
|
+
if (input.baseDir) {
|
|
5114
|
+
try {
|
|
5115
|
+
const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
|
|
5116
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
5117
|
+
const mdDir = path12.dirname(mdPath);
|
|
5118
|
+
if (!existsSync11(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
5119
|
+
const reviewer = input.reviewer ?? input.assignedBy;
|
|
5120
|
+
const mdContent = `# ${input.title}
|
|
5121
|
+
|
|
5122
|
+
**ID:** ${id}
|
|
5123
|
+
**Status:** ${initialStatus}
|
|
5124
|
+
**Priority:** ${input.priority}
|
|
5125
|
+
**Assigned by:** ${input.assignedBy}
|
|
5126
|
+
**Assigned to:** ${input.assignedTo}
|
|
5127
|
+
**Project:** ${input.projectName}
|
|
5128
|
+
**Created:** ${now.split("T")[0]}${parentTaskId ? `
|
|
5129
|
+
**Parent task:** ${parentTaskId}` : ""}
|
|
5130
|
+
**Reviewer:** ${reviewer}
|
|
5131
|
+
|
|
5132
|
+
## Context
|
|
5133
|
+
|
|
5134
|
+
${input.context}
|
|
5135
|
+
|
|
5136
|
+
## MANDATORY: When done
|
|
5137
|
+
|
|
5138
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
5139
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
5140
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
5141
|
+
`;
|
|
5142
|
+
await writeFile4(mdPath, mdContent, "utf-8");
|
|
5143
|
+
} catch {
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
4920
5146
|
return {
|
|
4921
5147
|
id,
|
|
4922
5148
|
title: input.title,
|
|
@@ -5109,7 +5335,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
5109
5335
|
return { row, taskFile, now, taskId };
|
|
5110
5336
|
}
|
|
5111
5337
|
}
|
|
5112
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
5338
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
5113
5339
|
process.stderr.write(
|
|
5114
5340
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
5115
5341
|
`
|
|
@@ -5221,12 +5447,22 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
5221
5447
|
} catch {
|
|
5222
5448
|
}
|
|
5223
5449
|
}
|
|
5224
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
5450
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
5225
5451
|
var init_tasks_crud = __esm({
|
|
5226
5452
|
"src/lib/tasks-crud.ts"() {
|
|
5227
5453
|
"use strict";
|
|
5228
5454
|
init_database();
|
|
5229
5455
|
init_task_scope();
|
|
5456
|
+
init_employees();
|
|
5457
|
+
LANE_KEYWORDS = {
|
|
5458
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
5459
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
5460
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
5461
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
5462
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
5463
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
5464
|
+
};
|
|
5465
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
5230
5466
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
5231
5467
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
5232
5468
|
}
|
|
@@ -5256,7 +5492,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
|
5256
5492
|
const result2 = await client.execute({
|
|
5257
5493
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5258
5494
|
WHERE status = 'needs_review' AND updated_at > ?
|
|
5259
|
-
AND
|
|
5495
|
+
AND session_scope = ?`,
|
|
5260
5496
|
args: [sinceIso, sessionScope]
|
|
5261
5497
|
});
|
|
5262
5498
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -5274,7 +5510,7 @@ async function listPendingReviews(limit, sessionScope) {
|
|
|
5274
5510
|
const result2 = await client.execute({
|
|
5275
5511
|
sql: `SELECT title, assigned_to, project_name FROM tasks
|
|
5276
5512
|
WHERE status = 'needs_review'
|
|
5277
|
-
AND
|
|
5513
|
+
AND session_scope = ?
|
|
5278
5514
|
ORDER BY priority ASC, created_at DESC LIMIT ?`,
|
|
5279
5515
|
args: [sessionScope, limit]
|
|
5280
5516
|
});
|
|
@@ -5395,14 +5631,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5395
5631
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
5396
5632
|
const agent = parts[1];
|
|
5397
5633
|
const slug = parts.slice(2).join("-");
|
|
5398
|
-
const
|
|
5634
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
5399
5635
|
const result = await client.execute({
|
|
5400
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
5401
|
-
args: [now,
|
|
5636
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
5637
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
5402
5638
|
});
|
|
5403
5639
|
if (result.rowsAffected > 0) {
|
|
5404
5640
|
process.stderr.write(
|
|
5405
|
-
`[review-cleanup] Cascaded original task to done
|
|
5641
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
5406
5642
|
`
|
|
5407
5643
|
);
|
|
5408
5644
|
}
|
|
@@ -5584,7 +5820,7 @@ function findSessionForProject(projectName) {
|
|
|
5584
5820
|
const sessions = listSessions();
|
|
5585
5821
|
for (const s of sessions) {
|
|
5586
5822
|
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
5587
|
-
if (proj === projectName &&
|
|
5823
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
5588
5824
|
}
|
|
5589
5825
|
return null;
|
|
5590
5826
|
}
|
|
@@ -5630,7 +5866,7 @@ var init_session_scope = __esm({
|
|
|
5630
5866
|
|
|
5631
5867
|
// src/lib/tasks-notify.ts
|
|
5632
5868
|
async function dispatchTaskToEmployee(input) {
|
|
5633
|
-
if (
|
|
5869
|
+
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
5634
5870
|
let crossProject = false;
|
|
5635
5871
|
if (input.projectName) {
|
|
5636
5872
|
try {
|
|
@@ -6109,7 +6345,7 @@ async function updateTask(input) {
|
|
|
6109
6345
|
}
|
|
6110
6346
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
6111
6347
|
if (isTerminal) {
|
|
6112
|
-
const isCoordinator =
|
|
6348
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
6113
6349
|
if (!isCoordinator) {
|
|
6114
6350
|
notifyTaskDone();
|
|
6115
6351
|
}
|
|
@@ -6134,7 +6370,7 @@ async function updateTask(input) {
|
|
|
6134
6370
|
}
|
|
6135
6371
|
}
|
|
6136
6372
|
}
|
|
6137
|
-
if (input.status === "done" &&
|
|
6373
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6138
6374
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6139
6375
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
6140
6376
|
taskId,
|
|
@@ -6150,7 +6386,7 @@ async function updateTask(input) {
|
|
|
6150
6386
|
});
|
|
6151
6387
|
}
|
|
6152
6388
|
let nextTask;
|
|
6153
|
-
if (isTerminal &&
|
|
6389
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
6154
6390
|
try {
|
|
6155
6391
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
6156
6392
|
} catch {
|
|
@@ -6518,7 +6754,7 @@ __export(tmux_routing_exports, {
|
|
|
6518
6754
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6519
6755
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync13, appendFileSync } from "fs";
|
|
6520
6756
|
import path17 from "path";
|
|
6521
|
-
import
|
|
6757
|
+
import os9 from "os";
|
|
6522
6758
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6523
6759
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6524
6760
|
function spawnLockPath(sessionName) {
|
|
@@ -6842,7 +7078,7 @@ function notifyParentExe(sessionKey) {
|
|
|
6842
7078
|
return true;
|
|
6843
7079
|
}
|
|
6844
7080
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
6845
|
-
if (
|
|
7081
|
+
if (isCoordinatorName(employeeName)) {
|
|
6846
7082
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
6847
7083
|
}
|
|
6848
7084
|
try {
|
|
@@ -6914,7 +7150,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6914
7150
|
const transport = getTransport();
|
|
6915
7151
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6916
7152
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6917
|
-
const logDir = path17.join(
|
|
7153
|
+
const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
|
|
6918
7154
|
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6919
7155
|
if (!existsSync13(logDir)) {
|
|
6920
7156
|
mkdirSync7(logDir, { recursive: true });
|
|
@@ -6930,7 +7166,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6930
7166
|
} catch {
|
|
6931
7167
|
}
|
|
6932
7168
|
try {
|
|
6933
|
-
const claudeJsonPath = path17.join(
|
|
7169
|
+
const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
|
|
6934
7170
|
let claudeJson = {};
|
|
6935
7171
|
try {
|
|
6936
7172
|
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
@@ -6945,7 +7181,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6945
7181
|
} catch {
|
|
6946
7182
|
}
|
|
6947
7183
|
try {
|
|
6948
|
-
const settingsDir = path17.join(
|
|
7184
|
+
const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
|
|
6949
7185
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6950
7186
|
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6951
7187
|
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
@@ -6993,7 +7229,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6993
7229
|
let legacyFallbackWarned = false;
|
|
6994
7230
|
if (!useExeAgent && !useBinSymlink) {
|
|
6995
7231
|
const identityPath = path17.join(
|
|
6996
|
-
|
|
7232
|
+
os9.homedir(),
|
|
6997
7233
|
".exe-os",
|
|
6998
7234
|
"identity",
|
|
6999
7235
|
`${employeeName}.md`
|
|
@@ -7023,7 +7259,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7023
7259
|
}
|
|
7024
7260
|
let sessionContextFlag = "";
|
|
7025
7261
|
try {
|
|
7026
|
-
const ctxDir = path17.join(
|
|
7262
|
+
const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7027
7263
|
mkdirSync7(ctxDir, { recursive: true });
|
|
7028
7264
|
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7029
7265
|
const ctxContent = [
|
|
@@ -7135,13 +7371,13 @@ var init_tmux_routing = __esm({
|
|
|
7135
7371
|
init_intercom_queue();
|
|
7136
7372
|
init_plan_limits();
|
|
7137
7373
|
init_employees();
|
|
7138
|
-
SPAWN_LOCK_DIR = path17.join(
|
|
7139
|
-
SESSION_CACHE = path17.join(
|
|
7374
|
+
SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
7375
|
+
SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7140
7376
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7141
7377
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7142
7378
|
VERIFY_PANE_LINES = 200;
|
|
7143
7379
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7144
|
-
INTERCOM_LOG2 = path17.join(
|
|
7380
|
+
INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
7145
7381
|
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7146
7382
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7147
7383
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
@@ -8051,10 +8287,49 @@ function buildPermissionContext(platform, permissions) {
|
|
|
8051
8287
|
return `[${platform.toUpperCase()} \u2014 allowed: ${parts.join(", ")}]`;
|
|
8052
8288
|
}
|
|
8053
8289
|
|
|
8290
|
+
// src/gateway/bot-errors.ts
|
|
8291
|
+
var FatalBotError = class extends Error {
|
|
8292
|
+
constructor(message, cause) {
|
|
8293
|
+
super(message);
|
|
8294
|
+
this.cause = cause;
|
|
8295
|
+
this.name = "FatalBotError";
|
|
8296
|
+
}
|
|
8297
|
+
fatal = true;
|
|
8298
|
+
};
|
|
8299
|
+
var RecoverableBotError = class extends Error {
|
|
8300
|
+
constructor(message, toolName, cause) {
|
|
8301
|
+
super(message);
|
|
8302
|
+
this.toolName = toolName;
|
|
8303
|
+
this.cause = cause;
|
|
8304
|
+
this.name = "RecoverableBotError";
|
|
8305
|
+
}
|
|
8306
|
+
recoverable = true;
|
|
8307
|
+
};
|
|
8308
|
+
var MaxStepsError = class extends Error {
|
|
8309
|
+
constructor(stepsTaken, maxSteps) {
|
|
8310
|
+
super(
|
|
8311
|
+
`Reached maximum steps (${stepsTaken}/${maxSteps}). Returning partial result.`
|
|
8312
|
+
);
|
|
8313
|
+
this.stepsTaken = stepsTaken;
|
|
8314
|
+
this.maxSteps = maxSteps;
|
|
8315
|
+
this.name = "MaxStepsError";
|
|
8316
|
+
}
|
|
8317
|
+
};
|
|
8318
|
+
function classifyError(err, toolName) {
|
|
8319
|
+
if (err instanceof FatalBotError) return err;
|
|
8320
|
+
if (err instanceof RecoverableBotError) return err;
|
|
8321
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8322
|
+
if (message.includes("401") || message.includes("403") || message.includes("authentication") || message.includes("rate_limit")) {
|
|
8323
|
+
return new FatalBotError(message, err);
|
|
8324
|
+
}
|
|
8325
|
+
return new RecoverableBotError(message, toolName, err);
|
|
8326
|
+
}
|
|
8327
|
+
|
|
8054
8328
|
// src/gateway/bot-runtime.ts
|
|
8055
8329
|
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
8056
8330
|
var MAX_TURNS = 10;
|
|
8057
8331
|
var MAX_HISTORY = 50;
|
|
8332
|
+
var DEFAULT_PLANNING_INTERVAL = 3;
|
|
8058
8333
|
function buildExecAssistantSystemPrompt(platform, permissions) {
|
|
8059
8334
|
const permContext = buildPermissionContext(platform, permissions);
|
|
8060
8335
|
return `You are the founder's executive assistant (agent_id: "ea").
|
|
@@ -8106,7 +8381,7 @@ var BotRuntime = class {
|
|
|
8106
8381
|
async processMessage(msg, permissions) {
|
|
8107
8382
|
const sessionKey = msg.chatType === "group" ? msg.channelId : msg.senderId;
|
|
8108
8383
|
const history = this.getHistory(sessionKey);
|
|
8109
|
-
history.push({ role: "user", content: msg.text });
|
|
8384
|
+
history.push({ role: "user", content: msg.text, frameType: "task" });
|
|
8110
8385
|
const systemPrompt = this.config.systemPrompt + "\n\n" + buildPermissionContext(msg.platform, permissions);
|
|
8111
8386
|
const allowedTools = filterToolsForPermissions(
|
|
8112
8387
|
this.config.tools,
|
|
@@ -8114,29 +8389,54 @@ var BotRuntime = class {
|
|
|
8114
8389
|
);
|
|
8115
8390
|
const model = this.config.model ?? DEFAULT_MODEL;
|
|
8116
8391
|
const maxTurns = this.config.maxTurns ?? MAX_TURNS;
|
|
8392
|
+
const planningInterval = this.config.planningInterval ?? DEFAULT_PLANNING_INTERVAL;
|
|
8117
8393
|
let turns = 0;
|
|
8118
8394
|
while (turns < maxTurns) {
|
|
8119
8395
|
turns++;
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8396
|
+
if (planningInterval > 0 && turns > 1 && turns % planningInterval === 1 && maxTurns - turns >= 2) {
|
|
8397
|
+
history.push({
|
|
8398
|
+
role: "user",
|
|
8399
|
+
content: "[Planning checkpoint] Review what you know so far. What facts have you gathered? What is your plan for the remaining steps? Be concise.",
|
|
8400
|
+
frameType: "planning"
|
|
8401
|
+
});
|
|
8402
|
+
}
|
|
8403
|
+
let response;
|
|
8404
|
+
try {
|
|
8405
|
+
response = await this.client.messages.create({
|
|
8406
|
+
model,
|
|
8407
|
+
max_tokens: 4096,
|
|
8408
|
+
system: systemPrompt,
|
|
8409
|
+
messages: history.map((m) => ({
|
|
8410
|
+
role: m.role,
|
|
8411
|
+
content: m.content
|
|
8412
|
+
})),
|
|
8413
|
+
tools: allowedTools.map((t) => ({
|
|
8414
|
+
name: t.name,
|
|
8415
|
+
description: t.description,
|
|
8416
|
+
input_schema: t.input_schema
|
|
8417
|
+
}))
|
|
8418
|
+
});
|
|
8419
|
+
} catch (err) {
|
|
8420
|
+
const classified = classifyError(err);
|
|
8421
|
+
if (classified instanceof FatalBotError) {
|
|
8422
|
+
const errorMsg = `Bot error: ${classified.message}`;
|
|
8423
|
+
history.push({ role: "assistant", content: errorMsg, frameType: "error" });
|
|
8424
|
+
this.trimHistory(sessionKey);
|
|
8425
|
+
return errorMsg;
|
|
8426
|
+
}
|
|
8427
|
+
history.push({
|
|
8428
|
+
role: "assistant",
|
|
8429
|
+
content: `[API error \u2014 retrying] ${classified.message}`,
|
|
8430
|
+
frameType: "error"
|
|
8431
|
+
});
|
|
8432
|
+
continue;
|
|
8433
|
+
}
|
|
8134
8434
|
const toolUseBlocks = response.content.filter(
|
|
8135
8435
|
(b) => b.type === "tool_use"
|
|
8136
8436
|
);
|
|
8137
8437
|
if (toolUseBlocks.length === 0) {
|
|
8138
8438
|
const textContent = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
8139
|
-
history.push({ role: "assistant", content: textContent });
|
|
8439
|
+
history.push({ role: "assistant", content: textContent, frameType: "assistant" });
|
|
8140
8440
|
this.trimHistory(sessionKey);
|
|
8141
8441
|
return textContent;
|
|
8142
8442
|
}
|
|
@@ -8146,9 +8446,11 @@ var BotRuntime = class {
|
|
|
8146
8446
|
);
|
|
8147
8447
|
history.push({
|
|
8148
8448
|
role: "assistant",
|
|
8149
|
-
content: response.content
|
|
8449
|
+
content: response.content,
|
|
8450
|
+
frameType: "assistant"
|
|
8150
8451
|
});
|
|
8151
8452
|
const toolResults = [];
|
|
8453
|
+
let hadFatalError = false;
|
|
8152
8454
|
for (const block of allowed) {
|
|
8153
8455
|
try {
|
|
8154
8456
|
const result = await this.config.toolExecutor(
|
|
@@ -8161,12 +8463,23 @@ var BotRuntime = class {
|
|
|
8161
8463
|
content: result
|
|
8162
8464
|
});
|
|
8163
8465
|
} catch (err) {
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8466
|
+
const classified = classifyError(err, block.name);
|
|
8467
|
+
if (classified instanceof FatalBotError) {
|
|
8468
|
+
toolResults.push({
|
|
8469
|
+
type: "tool_result",
|
|
8470
|
+
tool_use_id: block.id,
|
|
8471
|
+
content: `Fatal error: ${classified.message}`,
|
|
8472
|
+
is_error: true
|
|
8473
|
+
});
|
|
8474
|
+
hadFatalError = true;
|
|
8475
|
+
} else {
|
|
8476
|
+
toolResults.push({
|
|
8477
|
+
type: "tool_result",
|
|
8478
|
+
tool_use_id: block.id,
|
|
8479
|
+
content: `Error (recoverable): ${classified.message}`,
|
|
8480
|
+
is_error: true
|
|
8481
|
+
});
|
|
8482
|
+
}
|
|
8170
8483
|
}
|
|
8171
8484
|
}
|
|
8172
8485
|
for (const { block, check } of blocked) {
|
|
@@ -8179,10 +8492,22 @@ var BotRuntime = class {
|
|
|
8179
8492
|
}
|
|
8180
8493
|
history.push({
|
|
8181
8494
|
role: "user",
|
|
8182
|
-
content: toolResults
|
|
8495
|
+
content: toolResults,
|
|
8496
|
+
frameType: hadFatalError ? "error" : "tool_result"
|
|
8183
8497
|
});
|
|
8498
|
+
if (hadFatalError) {
|
|
8499
|
+
this.trimHistory(sessionKey);
|
|
8500
|
+
return `A fatal error occurred during tool execution. The bot loop has been stopped.`;
|
|
8501
|
+
}
|
|
8184
8502
|
}
|
|
8185
|
-
|
|
8503
|
+
const maxErr = new MaxStepsError(turns, maxTurns);
|
|
8504
|
+
history.push({
|
|
8505
|
+
role: "assistant",
|
|
8506
|
+
content: maxErr.message,
|
|
8507
|
+
frameType: "error"
|
|
8508
|
+
});
|
|
8509
|
+
this.trimHistory(sessionKey);
|
|
8510
|
+
return maxErr.message;
|
|
8186
8511
|
}
|
|
8187
8512
|
getHistory(sessionKey) {
|
|
8188
8513
|
if (!this.conversations.has(sessionKey)) {
|
|
@@ -8192,9 +8517,19 @@ var BotRuntime = class {
|
|
|
8192
8517
|
}
|
|
8193
8518
|
trimHistory(sessionKey) {
|
|
8194
8519
|
const history = this.conversations.get(sessionKey);
|
|
8195
|
-
if (history
|
|
8196
|
-
|
|
8520
|
+
if (!history || history.length <= MAX_HISTORY) return;
|
|
8521
|
+
const firstTaskIdx = history.findIndex((m) => m.frameType === "task");
|
|
8522
|
+
let trimmed = history.filter(
|
|
8523
|
+
(m, i) => m.frameType !== "planning" || i >= history.length - MAX_HISTORY
|
|
8524
|
+
);
|
|
8525
|
+
if (trimmed.length > MAX_HISTORY) {
|
|
8526
|
+
const tail = trimmed.slice(-MAX_HISTORY);
|
|
8527
|
+
if (firstTaskIdx >= 0 && !tail.includes(history[firstTaskIdx])) {
|
|
8528
|
+
tail[0] = history[firstTaskIdx];
|
|
8529
|
+
}
|
|
8530
|
+
trimmed = tail;
|
|
8197
8531
|
}
|
|
8532
|
+
this.conversations.set(sessionKey, trimmed);
|
|
8198
8533
|
}
|
|
8199
8534
|
/** Clear conversation history for a session */
|
|
8200
8535
|
clearHistory(sessionKey) {
|
|
@@ -8900,8 +9235,19 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
8900
9235
|
import { homedir } from "os";
|
|
8901
9236
|
import { join } from "path";
|
|
8902
9237
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
8903
|
-
var
|
|
9238
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
9239
|
+
var MAX_BACKOFF_MS = 3e5;
|
|
9240
|
+
var BACKOFF_MULTIPLIER = 2;
|
|
9241
|
+
var JITTER_FACTOR = 0.25;
|
|
8904
9242
|
var AUTH_DIR = join(homedir(), ".exe-os", "whatsapp-auth");
|
|
9243
|
+
function calculateBackoff(retryCount) {
|
|
9244
|
+
const base = Math.min(
|
|
9245
|
+
INITIAL_BACKOFF_MS * BACKOFF_MULTIPLIER ** retryCount,
|
|
9246
|
+
MAX_BACKOFF_MS
|
|
9247
|
+
);
|
|
9248
|
+
const jitter = base * JITTER_FACTOR * (2 * Math.random() - 1);
|
|
9249
|
+
return Math.max(INITIAL_BACKOFF_MS, Math.round(base + jitter));
|
|
9250
|
+
}
|
|
8905
9251
|
var WhatsAppAdapter = class {
|
|
8906
9252
|
platform = "whatsapp";
|
|
8907
9253
|
sock = null;
|
|
@@ -8909,6 +9255,9 @@ var WhatsAppAdapter = class {
|
|
|
8909
9255
|
connected = false;
|
|
8910
9256
|
abortController = null;
|
|
8911
9257
|
authDir = AUTH_DIR;
|
|
9258
|
+
// Resilience state
|
|
9259
|
+
retryCount = 0;
|
|
9260
|
+
disconnectedAt = 0;
|
|
8912
9261
|
async connect(config2) {
|
|
8913
9262
|
this.authDir = config2.credentials.authDir ?? AUTH_DIR;
|
|
8914
9263
|
mkdirSync2(this.authDir, { recursive: true });
|
|
@@ -8917,6 +9266,20 @@ var WhatsAppAdapter = class {
|
|
|
8917
9266
|
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
8918
9267
|
const { version } = await fetchLatestBaileysVersion();
|
|
8919
9268
|
this.abortController = new AbortController();
|
|
9269
|
+
let agent;
|
|
9270
|
+
const socksProxy = config2.credentials.socksProxy;
|
|
9271
|
+
if (socksProxy) {
|
|
9272
|
+
try {
|
|
9273
|
+
const modName = "socks-proxy-agent";
|
|
9274
|
+
const mod = await import(modName);
|
|
9275
|
+
const SocksProxyAgent = mod.SocksProxyAgent ?? mod.default;
|
|
9276
|
+
agent = new SocksProxyAgent(socksProxy);
|
|
9277
|
+
console.log(`[whatsapp] Using SOCKS proxy: ${socksProxy.replace(/\/\/.*@/, "//***@")}`);
|
|
9278
|
+
} catch {
|
|
9279
|
+
console.error("[whatsapp] socks-proxy-agent not installed \u2014 run: npm i socks-proxy-agent");
|
|
9280
|
+
throw new Error("SOCKS proxy configured but socks-proxy-agent package not installed");
|
|
9281
|
+
}
|
|
9282
|
+
}
|
|
8920
9283
|
const sock = makeWASocket({
|
|
8921
9284
|
auth: {
|
|
8922
9285
|
creds: state.creds,
|
|
@@ -8926,7 +9289,8 @@ var WhatsAppAdapter = class {
|
|
|
8926
9289
|
printQRInTerminal: true,
|
|
8927
9290
|
browser: ["exe-os", "cli", "1.0"],
|
|
8928
9291
|
syncFullHistory: false,
|
|
8929
|
-
markOnlineOnConnect: false
|
|
9292
|
+
markOnlineOnConnect: false,
|
|
9293
|
+
...agent ? { agent } : {}
|
|
8930
9294
|
});
|
|
8931
9295
|
this.sock = sock;
|
|
8932
9296
|
sock.ev.on("creds.update", saveCreds);
|
|
@@ -8934,18 +9298,32 @@ var WhatsAppAdapter = class {
|
|
|
8934
9298
|
const { connection, lastDisconnect } = update;
|
|
8935
9299
|
if (connection === "close") {
|
|
8936
9300
|
this.connected = false;
|
|
9301
|
+
if (this.disconnectedAt === 0) this.disconnectedAt = Date.now();
|
|
8937
9302
|
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
8938
9303
|
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
|
|
8939
9304
|
if (shouldReconnect && !this.abortController?.signal.aborted) {
|
|
8940
|
-
|
|
8941
|
-
|
|
9305
|
+
const delay2 = calculateBackoff(this.retryCount);
|
|
9306
|
+
this.retryCount++;
|
|
9307
|
+
console.log(
|
|
9308
|
+
`[whatsapp] Connection closed (code=${statusCode}), retry #${this.retryCount} in ${(delay2 / 1e3).toFixed(1)}s` + (socksProxy ? ` (proxy: ${socksProxy.replace(/\/\/.*@/, "//***@")})` : "")
|
|
9309
|
+
);
|
|
9310
|
+
setTimeout(() => void this.connect(config2), delay2);
|
|
8942
9311
|
} else {
|
|
8943
9312
|
console.log("[whatsapp] Logged out \u2014 clear auth and re-scan QR");
|
|
8944
9313
|
}
|
|
8945
9314
|
}
|
|
8946
9315
|
if (connection === "open") {
|
|
9316
|
+
if (this.retryCount > 0) {
|
|
9317
|
+
const downtimeSec = this.disconnectedAt > 0 ? ((Date.now() - this.disconnectedAt) / 1e3).toFixed(1) : "?";
|
|
9318
|
+
console.log(
|
|
9319
|
+
`[whatsapp] Reconnected after ${this.retryCount} retries (${downtimeSec}s downtime)`
|
|
9320
|
+
);
|
|
9321
|
+
} else {
|
|
9322
|
+
console.log("[whatsapp] Connected via Baileys (linked device)");
|
|
9323
|
+
}
|
|
8947
9324
|
this.connected = true;
|
|
8948
|
-
|
|
9325
|
+
this.retryCount = 0;
|
|
9326
|
+
this.disconnectedAt = 0;
|
|
8949
9327
|
}
|
|
8950
9328
|
});
|
|
8951
9329
|
sock.ev.on("messages.upsert", (upsert) => {
|
|
@@ -11035,8 +11413,8 @@ async function ensureCRMContact(info) {
|
|
|
11035
11413
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
|
|
11036
11414
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
11037
11415
|
import path18 from "path";
|
|
11038
|
-
import
|
|
11039
|
-
var TRIGGERS_PATH = path18.join(
|
|
11416
|
+
import os10 from "os";
|
|
11417
|
+
var TRIGGERS_PATH = path18.join(os10.homedir(), ".exe-os", "triggers.json");
|
|
11040
11418
|
var GRAPH_API_VERSION = "v21.0";
|
|
11041
11419
|
function substituteTemplate(template, record) {
|
|
11042
11420
|
return template.replace(
|
|
@@ -11390,13 +11768,16 @@ export {
|
|
|
11390
11768
|
FULL_ACCESS,
|
|
11391
11769
|
FailoverCascade,
|
|
11392
11770
|
FailoverExhaustedError,
|
|
11771
|
+
FatalBotError,
|
|
11393
11772
|
Gateway,
|
|
11394
11773
|
IMessageAdapter,
|
|
11774
|
+
MaxStepsError,
|
|
11395
11775
|
OllamaProvider,
|
|
11396
11776
|
OpenAICompatProvider,
|
|
11397
11777
|
READ_ONLY,
|
|
11398
11778
|
READ_TOOLS,
|
|
11399
11779
|
RateLimiter,
|
|
11780
|
+
RecoverableBotError,
|
|
11400
11781
|
SessionStore,
|
|
11401
11782
|
SignalAdapter,
|
|
11402
11783
|
SlackAdapter,
|
|
@@ -11408,6 +11789,7 @@ export {
|
|
|
11408
11789
|
buildExecAssistantTools,
|
|
11409
11790
|
buildPermissionContext,
|
|
11410
11791
|
checkToolPermission,
|
|
11792
|
+
classifyError,
|
|
11411
11793
|
createCRMWebhookHandler,
|
|
11412
11794
|
createPerson,
|
|
11413
11795
|
createReceptionist,
|