@askexenow/exe-os 0.8.32 → 0.8.36
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 +332 -348
- package/dist/bin/backfill-responses.js +72 -12
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1518 -1122
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +80 -18
- package/dist/bin/exe-boot.js +408 -89
- package/dist/bin/exe-call.js +83 -24
- package/dist/bin/exe-dispatch.js +18 -10
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +64 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +121 -36
- package/dist/bin/exe-heartbeat.js +77 -13
- package/dist/bin/exe-kill.js +64 -3
- package/dist/bin/exe-launch-agent.js +162 -35
- package/dist/bin/exe-link.js +946 -0
- package/dist/bin/exe-new-employee.js +121 -36
- package/dist/bin/exe-pending-messages.js +72 -7
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +75 -10
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +64 -4
- package/dist/bin/exe-search.js +79 -13
- package/dist/bin/exe-session-cleanup.js +91 -26
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +71 -4
- package/dist/bin/graph-backfill.js +64 -3
- package/dist/bin/graph-export.js +64 -3
- package/dist/bin/install.js +3 -3
- package/dist/bin/scan-tasks.js +71 -4
- package/dist/bin/setup.js +156 -38
- package/dist/bin/shard-migrate.js +64 -3
- package/dist/bin/wiki-sync.js +64 -3
- package/dist/gateway/index.js +122 -37
- package/dist/hooks/bug-report-worker.js +209 -23
- package/dist/hooks/commit-complete.js +71 -4
- package/dist/hooks/error-recall.js +79 -13
- package/dist/hooks/ingest-worker.js +129 -43
- package/dist/hooks/instructions-loaded.js +71 -4
- package/dist/hooks/notification.js +71 -4
- package/dist/hooks/post-compact.js +71 -4
- package/dist/hooks/pre-compact.js +71 -4
- package/dist/hooks/pre-tool-use.js +413 -194
- package/dist/hooks/prompt-ingest-worker.js +82 -22
- package/dist/hooks/prompt-submit.js +103 -37
- package/dist/hooks/response-ingest-worker.js +87 -22
- package/dist/hooks/session-end.js +71 -4
- package/dist/hooks/session-start.js +79 -13
- package/dist/hooks/stop.js +71 -4
- package/dist/hooks/subagent-stop.js +71 -4
- package/dist/hooks/summary-worker.js +303 -50
- package/dist/index.js +134 -46
- package/dist/lib/cloud-sync.js +209 -15
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +48 -22
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +136 -53
- package/dist/lib/hybrid-search.js +79 -13
- package/dist/lib/identity-templates.js +57 -6
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +22 -14
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +64 -3
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +48 -21
- package/dist/lib/tmux-routing.js +47 -20
- package/dist/mcp/server.js +727 -58
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +151 -24
- package/dist/mcp/tools/deactivate-behavior.js +3 -3
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +17 -8
- package/dist/mcp/tools/send-message.js +24 -16
- package/dist/mcp/tools/update-task.js +25 -16
- package/dist/runtime/index.js +112 -24
- package/dist/tui/App.js +139 -36
- package/package.json +6 -2
- package/src/commands/exe/rename.md +12 -0
package/dist/mcp/server.js
CHANGED
|
@@ -454,11 +454,11 @@ async function connectEmbedDaemon() {
|
|
|
454
454
|
}
|
|
455
455
|
}
|
|
456
456
|
const start = Date.now();
|
|
457
|
-
let
|
|
457
|
+
let delay2 = 100;
|
|
458
458
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
459
|
-
await new Promise((r) => setTimeout(r,
|
|
459
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
460
460
|
if (await connectToSocket()) return true;
|
|
461
|
-
|
|
461
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
462
462
|
}
|
|
463
463
|
return false;
|
|
464
464
|
}
|
|
@@ -550,11 +550,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
550
550
|
`);
|
|
551
551
|
killAndRespawnDaemon();
|
|
552
552
|
const start = Date.now();
|
|
553
|
-
let
|
|
553
|
+
let delay2 = 200;
|
|
554
554
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
555
|
-
await new Promise((r) => setTimeout(r,
|
|
555
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
556
556
|
if (await connectToSocket()) break;
|
|
557
|
-
|
|
557
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
558
558
|
}
|
|
559
559
|
if (!_connected) return null;
|
|
560
560
|
}
|
|
@@ -566,11 +566,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
566
566
|
`);
|
|
567
567
|
killAndRespawnDaemon();
|
|
568
568
|
const start = Date.now();
|
|
569
|
-
let
|
|
569
|
+
let delay2 = 200;
|
|
570
570
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
571
|
-
await new Promise((r) => setTimeout(r,
|
|
571
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
572
572
|
if (await connectToSocket()) break;
|
|
573
|
-
|
|
573
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
574
574
|
}
|
|
575
575
|
if (!_connected) return null;
|
|
576
576
|
const retry = await sendRequest([text], priority);
|
|
@@ -681,12 +681,68 @@ var init_embedder = __esm({
|
|
|
681
681
|
}
|
|
682
682
|
});
|
|
683
683
|
|
|
684
|
+
// src/lib/db-retry.ts
|
|
685
|
+
function isBusyError(err) {
|
|
686
|
+
if (err instanceof Error) {
|
|
687
|
+
const msg = err.message.toLowerCase();
|
|
688
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
689
|
+
}
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
function delay(ms) {
|
|
693
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
694
|
+
}
|
|
695
|
+
async function retryOnBusy(fn, label) {
|
|
696
|
+
let lastError;
|
|
697
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
698
|
+
try {
|
|
699
|
+
return await fn();
|
|
700
|
+
} catch (err) {
|
|
701
|
+
lastError = err;
|
|
702
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
703
|
+
throw err;
|
|
704
|
+
}
|
|
705
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
706
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
707
|
+
process.stderr.write(
|
|
708
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
709
|
+
`
|
|
710
|
+
);
|
|
711
|
+
await delay(backoff + jitter);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
throw lastError;
|
|
715
|
+
}
|
|
716
|
+
function wrapWithRetry(client) {
|
|
717
|
+
return new Proxy(client, {
|
|
718
|
+
get(target, prop, receiver) {
|
|
719
|
+
if (prop === "execute") {
|
|
720
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
721
|
+
}
|
|
722
|
+
if (prop === "batch") {
|
|
723
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
724
|
+
}
|
|
725
|
+
return Reflect.get(target, prop, receiver);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
730
|
+
var init_db_retry = __esm({
|
|
731
|
+
"src/lib/db-retry.ts"() {
|
|
732
|
+
"use strict";
|
|
733
|
+
MAX_RETRIES = 3;
|
|
734
|
+
BASE_DELAY_MS = 200;
|
|
735
|
+
MAX_JITTER_MS = 300;
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
684
739
|
// src/lib/database.ts
|
|
685
740
|
import { createClient } from "@libsql/client";
|
|
686
741
|
async function initDatabase(config2) {
|
|
687
742
|
if (_client) {
|
|
688
743
|
_client.close();
|
|
689
744
|
_client = null;
|
|
745
|
+
_resilientClient = null;
|
|
690
746
|
}
|
|
691
747
|
const opts = {
|
|
692
748
|
url: `file:${config2.dbPath}`
|
|
@@ -695,20 +751,27 @@ async function initDatabase(config2) {
|
|
|
695
751
|
opts.encryptionKey = config2.encryptionKey;
|
|
696
752
|
}
|
|
697
753
|
_client = createClient(opts);
|
|
754
|
+
_resilientClient = wrapWithRetry(_client);
|
|
698
755
|
}
|
|
699
756
|
function isInitialized() {
|
|
700
757
|
return _client !== null;
|
|
701
758
|
}
|
|
702
759
|
function getClient() {
|
|
760
|
+
if (!_resilientClient) {
|
|
761
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
762
|
+
}
|
|
763
|
+
return _resilientClient;
|
|
764
|
+
}
|
|
765
|
+
function getRawClient() {
|
|
703
766
|
if (!_client) {
|
|
704
767
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
705
768
|
}
|
|
706
769
|
return _client;
|
|
707
770
|
}
|
|
708
771
|
async function ensureSchema() {
|
|
709
|
-
const client =
|
|
772
|
+
const client = getRawClient();
|
|
710
773
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
711
|
-
await client.execute("PRAGMA busy_timeout =
|
|
774
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
712
775
|
try {
|
|
713
776
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
714
777
|
} catch {
|
|
@@ -1501,13 +1564,16 @@ async function disposeDatabase() {
|
|
|
1501
1564
|
if (_client) {
|
|
1502
1565
|
_client.close();
|
|
1503
1566
|
_client = null;
|
|
1567
|
+
_resilientClient = null;
|
|
1504
1568
|
}
|
|
1505
1569
|
}
|
|
1506
|
-
var _client, initTurso, disposeTurso;
|
|
1570
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1507
1571
|
var init_database = __esm({
|
|
1508
1572
|
"src/lib/database.ts"() {
|
|
1509
1573
|
"use strict";
|
|
1574
|
+
init_db_retry();
|
|
1510
1575
|
_client = null;
|
|
1576
|
+
_resilientClient = null;
|
|
1511
1577
|
initTurso = initDatabase;
|
|
1512
1578
|
disposeTurso = disposeDatabase;
|
|
1513
1579
|
}
|
|
@@ -1620,7 +1686,7 @@ function listShards() {
|
|
|
1620
1686
|
}
|
|
1621
1687
|
async function ensureShardSchema(client) {
|
|
1622
1688
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1623
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1689
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1624
1690
|
try {
|
|
1625
1691
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1626
1692
|
} catch {
|
|
@@ -2890,10 +2956,40 @@ var init_active_agent = __esm({
|
|
|
2890
2956
|
});
|
|
2891
2957
|
|
|
2892
2958
|
// src/lib/employees.ts
|
|
2959
|
+
var employees_exports = {};
|
|
2960
|
+
__export(employees_exports, {
|
|
2961
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
2962
|
+
addEmployee: () => addEmployee,
|
|
2963
|
+
getEmployee: () => getEmployee,
|
|
2964
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
2965
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
2966
|
+
hasRole: () => hasRole,
|
|
2967
|
+
isMultiInstance: () => isMultiInstance,
|
|
2968
|
+
loadEmployees: () => loadEmployees,
|
|
2969
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
2970
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
2971
|
+
saveEmployees: () => saveEmployees,
|
|
2972
|
+
validateEmployeeName: () => validateEmployeeName
|
|
2973
|
+
});
|
|
2893
2974
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2894
|
-
import { existsSync as existsSync7, symlinkSync, readlinkSync } from "fs";
|
|
2975
|
+
import { existsSync as existsSync7, symlinkSync, readlinkSync, readFileSync as readFileSync5 } from "fs";
|
|
2895
2976
|
import { execSync as execSync5 } from "child_process";
|
|
2896
2977
|
import path9 from "path";
|
|
2978
|
+
function validateEmployeeName(name) {
|
|
2979
|
+
if (!name) {
|
|
2980
|
+
return { valid: false, error: "Name is required" };
|
|
2981
|
+
}
|
|
2982
|
+
if (name.length > 32) {
|
|
2983
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
2984
|
+
}
|
|
2985
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
2986
|
+
return {
|
|
2987
|
+
valid: false,
|
|
2988
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
return { valid: true };
|
|
2992
|
+
}
|
|
2897
2993
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
2898
2994
|
if (!existsSync7(employeesPath)) {
|
|
2899
2995
|
return [];
|
|
@@ -2909,6 +3005,36 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
2909
3005
|
await mkdir3(path9.dirname(employeesPath), { recursive: true });
|
|
2910
3006
|
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
2911
3007
|
}
|
|
3008
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
3009
|
+
if (!existsSync7(employeesPath)) return [];
|
|
3010
|
+
try {
|
|
3011
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
3012
|
+
} catch {
|
|
3013
|
+
return [];
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
function getEmployee(employees, name) {
|
|
3017
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
3018
|
+
}
|
|
3019
|
+
function getEmployeeByRole(employees, role) {
|
|
3020
|
+
const lower = role.toLowerCase();
|
|
3021
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
3022
|
+
}
|
|
3023
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
3024
|
+
const lower = role.toLowerCase();
|
|
3025
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
3026
|
+
}
|
|
3027
|
+
function hasRole(agentName, role) {
|
|
3028
|
+
const employees = loadEmployeesSync();
|
|
3029
|
+
const emp = getEmployee(employees, agentName);
|
|
3030
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
3031
|
+
}
|
|
3032
|
+
function isMultiInstance(agentName, employees) {
|
|
3033
|
+
const roster = employees ?? loadEmployeesSync();
|
|
3034
|
+
const emp = getEmployee(roster, agentName);
|
|
3035
|
+
if (!emp) return false;
|
|
3036
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
3037
|
+
}
|
|
2912
3038
|
function addEmployee(employees, employee) {
|
|
2913
3039
|
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
2914
3040
|
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
@@ -2916,12 +3042,48 @@ function addEmployee(employees, employee) {
|
|
|
2916
3042
|
}
|
|
2917
3043
|
return [...employees, normalized];
|
|
2918
3044
|
}
|
|
2919
|
-
|
|
3045
|
+
function registerBinSymlinks(name) {
|
|
3046
|
+
const created = [];
|
|
3047
|
+
const skipped = [];
|
|
3048
|
+
const errors = [];
|
|
3049
|
+
let exeBinPath;
|
|
3050
|
+
try {
|
|
3051
|
+
exeBinPath = execSync5("which exe", { encoding: "utf-8" }).trim();
|
|
3052
|
+
} catch {
|
|
3053
|
+
errors.push("Could not find 'exe' in PATH");
|
|
3054
|
+
return { created, skipped, errors };
|
|
3055
|
+
}
|
|
3056
|
+
const binDir = path9.dirname(exeBinPath);
|
|
3057
|
+
let target;
|
|
3058
|
+
try {
|
|
3059
|
+
target = readlinkSync(exeBinPath);
|
|
3060
|
+
} catch {
|
|
3061
|
+
errors.push("Could not read 'exe' symlink");
|
|
3062
|
+
return { created, skipped, errors };
|
|
3063
|
+
}
|
|
3064
|
+
for (const suffix of ["", "-opencode"]) {
|
|
3065
|
+
const linkName = `${name}${suffix}`;
|
|
3066
|
+
const linkPath = path9.join(binDir, linkName);
|
|
3067
|
+
if (existsSync7(linkPath)) {
|
|
3068
|
+
skipped.push(linkName);
|
|
3069
|
+
continue;
|
|
3070
|
+
}
|
|
3071
|
+
try {
|
|
3072
|
+
symlinkSync(target, linkPath);
|
|
3073
|
+
created.push(linkName);
|
|
3074
|
+
} catch (err) {
|
|
3075
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
return { created, skipped, errors };
|
|
3079
|
+
}
|
|
3080
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
2920
3081
|
var init_employees = __esm({
|
|
2921
3082
|
"src/lib/employees.ts"() {
|
|
2922
3083
|
"use strict";
|
|
2923
3084
|
init_config();
|
|
2924
3085
|
EMPLOYEES_PATH = path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
3086
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
2925
3087
|
}
|
|
2926
3088
|
});
|
|
2927
3089
|
|
|
@@ -2940,7 +3102,7 @@ __export(license_exports, {
|
|
|
2940
3102
|
saveLicense: () => saveLicense,
|
|
2941
3103
|
validateLicense: () => validateLicense
|
|
2942
3104
|
});
|
|
2943
|
-
import { readFileSync as
|
|
3105
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
2944
3106
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2945
3107
|
import path10 from "path";
|
|
2946
3108
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -2948,14 +3110,14 @@ function loadDeviceId() {
|
|
|
2948
3110
|
const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
|
|
2949
3111
|
try {
|
|
2950
3112
|
if (existsSync8(deviceJsonPath)) {
|
|
2951
|
-
const data = JSON.parse(
|
|
3113
|
+
const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
|
|
2952
3114
|
if (data.deviceId) return data.deviceId;
|
|
2953
3115
|
}
|
|
2954
3116
|
} catch {
|
|
2955
3117
|
}
|
|
2956
3118
|
try {
|
|
2957
3119
|
if (existsSync8(DEVICE_ID_PATH)) {
|
|
2958
|
-
const id2 =
|
|
3120
|
+
const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
|
|
2959
3121
|
if (id2) return id2;
|
|
2960
3122
|
}
|
|
2961
3123
|
} catch {
|
|
@@ -2968,7 +3130,7 @@ function loadDeviceId() {
|
|
|
2968
3130
|
function loadLicense() {
|
|
2969
3131
|
try {
|
|
2970
3132
|
if (!existsSync8(LICENSE_PATH)) return null;
|
|
2971
|
-
return
|
|
3133
|
+
return readFileSync6(LICENSE_PATH, "utf8").trim();
|
|
2972
3134
|
} catch {
|
|
2973
3135
|
return null;
|
|
2974
3136
|
}
|
|
@@ -3002,7 +3164,7 @@ async function verifyLicenseJwt(token) {
|
|
|
3002
3164
|
async function getCachedLicense() {
|
|
3003
3165
|
try {
|
|
3004
3166
|
if (!existsSync8(CACHE_PATH)) return null;
|
|
3005
|
-
const raw = JSON.parse(
|
|
3167
|
+
const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
|
|
3006
3168
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3007
3169
|
return await verifyLicenseJwt(raw.token);
|
|
3008
3170
|
} catch {
|
|
@@ -3012,7 +3174,7 @@ async function getCachedLicense() {
|
|
|
3012
3174
|
function readCachedToken() {
|
|
3013
3175
|
try {
|
|
3014
3176
|
if (!existsSync8(CACHE_PATH)) return null;
|
|
3015
|
-
const raw = JSON.parse(
|
|
3177
|
+
const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
|
|
3016
3178
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3017
3179
|
} catch {
|
|
3018
3180
|
return null;
|
|
@@ -3234,12 +3396,12 @@ __export(plan_limits_exports, {
|
|
|
3234
3396
|
countActiveMemories: () => countActiveMemories,
|
|
3235
3397
|
getLicenseSync: () => getLicenseSync
|
|
3236
3398
|
});
|
|
3237
|
-
import { readFileSync as
|
|
3399
|
+
import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
|
|
3238
3400
|
import path11 from "path";
|
|
3239
3401
|
function getLicenseSync() {
|
|
3240
3402
|
try {
|
|
3241
3403
|
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
3242
|
-
const raw = JSON.parse(
|
|
3404
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
|
|
3243
3405
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3244
3406
|
const parts = raw.token.split(".");
|
|
3245
3407
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3307,7 +3469,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3307
3469
|
let count = 0;
|
|
3308
3470
|
try {
|
|
3309
3471
|
if (existsSync9(filePath)) {
|
|
3310
|
-
const raw =
|
|
3472
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
3311
3473
|
const employees = JSON.parse(raw);
|
|
3312
3474
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3313
3475
|
}
|
|
@@ -3353,7 +3515,7 @@ import crypto5 from "crypto";
|
|
|
3353
3515
|
import path14 from "path";
|
|
3354
3516
|
import os2 from "os";
|
|
3355
3517
|
import {
|
|
3356
|
-
readFileSync as
|
|
3518
|
+
readFileSync as readFileSync8,
|
|
3357
3519
|
readdirSync as readdirSync3,
|
|
3358
3520
|
unlinkSync as unlinkSync3,
|
|
3359
3521
|
existsSync as existsSync10,
|
|
@@ -3405,7 +3567,7 @@ import crypto6 from "crypto";
|
|
|
3405
3567
|
import path15 from "path";
|
|
3406
3568
|
import { execSync as execSync6 } from "child_process";
|
|
3407
3569
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3408
|
-
import { existsSync as existsSync11, readFileSync as
|
|
3570
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
3409
3571
|
async function writeCheckpoint(input) {
|
|
3410
3572
|
const client = getClient();
|
|
3411
3573
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3782,7 +3944,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
3782
3944
|
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
3783
3945
|
try {
|
|
3784
3946
|
if (existsSync11(gitignorePath)) {
|
|
3785
|
-
const content =
|
|
3947
|
+
const content = readFileSync9(gitignorePath, "utf-8");
|
|
3786
3948
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3787
3949
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3788
3950
|
} else {
|
|
@@ -3802,7 +3964,7 @@ var init_tasks_crud = __esm({
|
|
|
3802
3964
|
});
|
|
3803
3965
|
|
|
3804
3966
|
// src/lib/session-registry.ts
|
|
3805
|
-
import { readFileSync as
|
|
3967
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync12 } from "fs";
|
|
3806
3968
|
import path16 from "path";
|
|
3807
3969
|
import os3 from "os";
|
|
3808
3970
|
function registerSession(entry) {
|
|
@@ -3821,7 +3983,7 @@ function registerSession(entry) {
|
|
|
3821
3983
|
}
|
|
3822
3984
|
function listSessions() {
|
|
3823
3985
|
try {
|
|
3824
|
-
const raw =
|
|
3986
|
+
const raw = readFileSync10(REGISTRY_PATH, "utf8");
|
|
3825
3987
|
return JSON.parse(raw);
|
|
3826
3988
|
} catch {
|
|
3827
3989
|
return [];
|
|
@@ -4016,7 +4178,7 @@ var init_provider_table = __esm({
|
|
|
4016
4178
|
});
|
|
4017
4179
|
|
|
4018
4180
|
// src/lib/intercom-queue.ts
|
|
4019
|
-
import { readFileSync as
|
|
4181
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
|
|
4020
4182
|
import path17 from "path";
|
|
4021
4183
|
import os4 from "os";
|
|
4022
4184
|
function ensureDir() {
|
|
@@ -4026,7 +4188,7 @@ function ensureDir() {
|
|
|
4026
4188
|
function readQueue() {
|
|
4027
4189
|
try {
|
|
4028
4190
|
if (!existsSync13(QUEUE_PATH)) return [];
|
|
4029
|
-
return JSON.parse(
|
|
4191
|
+
return JSON.parse(readFileSync11(QUEUE_PATH, "utf8"));
|
|
4030
4192
|
} catch {
|
|
4031
4193
|
return [];
|
|
4032
4194
|
}
|
|
@@ -4066,7 +4228,7 @@ var init_intercom_queue = __esm({
|
|
|
4066
4228
|
|
|
4067
4229
|
// src/lib/tmux-routing.ts
|
|
4068
4230
|
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
4069
|
-
import { readFileSync as
|
|
4231
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync } from "fs";
|
|
4070
4232
|
import path18 from "path";
|
|
4071
4233
|
import os5 from "os";
|
|
4072
4234
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -4115,7 +4277,7 @@ function extractRootExe(name) {
|
|
|
4115
4277
|
}
|
|
4116
4278
|
function getParentExe(sessionKey) {
|
|
4117
4279
|
try {
|
|
4118
|
-
const data = JSON.parse(
|
|
4280
|
+
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4119
4281
|
return data.parentExe || null;
|
|
4120
4282
|
} catch {
|
|
4121
4283
|
return null;
|
|
@@ -4123,7 +4285,7 @@ function getParentExe(sessionKey) {
|
|
|
4123
4285
|
}
|
|
4124
4286
|
function getDispatchedBy(sessionKey) {
|
|
4125
4287
|
try {
|
|
4126
|
-
const data = JSON.parse(
|
|
4288
|
+
const data = JSON.parse(readFileSync12(
|
|
4127
4289
|
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4128
4290
|
"utf8"
|
|
4129
4291
|
));
|
|
@@ -4160,7 +4322,7 @@ function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive =
|
|
|
4160
4322
|
function readDebounceState() {
|
|
4161
4323
|
try {
|
|
4162
4324
|
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
4163
|
-
return JSON.parse(
|
|
4325
|
+
return JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
4164
4326
|
} catch {
|
|
4165
4327
|
return {};
|
|
4166
4328
|
}
|
|
@@ -4356,7 +4518,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4356
4518
|
const claudeJsonPath = path18.join(os5.homedir(), ".claude.json");
|
|
4357
4519
|
let claudeJson = {};
|
|
4358
4520
|
try {
|
|
4359
|
-
claudeJson = JSON.parse(
|
|
4521
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
4360
4522
|
} catch {
|
|
4361
4523
|
}
|
|
4362
4524
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4374,7 +4536,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4374
4536
|
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
4375
4537
|
let settings = {};
|
|
4376
4538
|
try {
|
|
4377
|
-
settings = JSON.parse(
|
|
4539
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
4378
4540
|
} catch {
|
|
4379
4541
|
}
|
|
4380
4542
|
const perms = settings.permissions ?? {};
|
|
@@ -4879,7 +5041,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
4879
5041
|
} else {
|
|
4880
5042
|
const projectDir = input.projectDir ?? process.cwd();
|
|
4881
5043
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
4882
|
-
autoInstance: input.assignedTo
|
|
5044
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
4883
5045
|
});
|
|
4884
5046
|
if (result.status === "failed") {
|
|
4885
5047
|
process.stderr.write(
|
|
@@ -4914,6 +5076,7 @@ var init_tasks_notify = __esm({
|
|
|
4914
5076
|
init_session_key();
|
|
4915
5077
|
init_notifications();
|
|
4916
5078
|
init_transport();
|
|
5079
|
+
init_employees();
|
|
4917
5080
|
}
|
|
4918
5081
|
});
|
|
4919
5082
|
|
|
@@ -5611,7 +5774,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
5611
5774
|
return true;
|
|
5612
5775
|
} catch {
|
|
5613
5776
|
const newRetryCount = msg.retryCount + 1;
|
|
5614
|
-
if (newRetryCount >=
|
|
5777
|
+
if (newRetryCount >= MAX_RETRIES2) {
|
|
5615
5778
|
await markFailed(messageId, "session unavailable after 10 retries");
|
|
5616
5779
|
} else {
|
|
5617
5780
|
await client.execute({
|
|
@@ -5629,25 +5792,25 @@ async function markFailed(messageId, reason) {
|
|
|
5629
5792
|
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId]
|
|
5630
5793
|
});
|
|
5631
5794
|
}
|
|
5632
|
-
var
|
|
5795
|
+
var MAX_RETRIES2, _wsClientSend;
|
|
5633
5796
|
var init_messaging = __esm({
|
|
5634
5797
|
"src/lib/messaging.ts"() {
|
|
5635
5798
|
"use strict";
|
|
5636
5799
|
init_database();
|
|
5637
5800
|
init_tmux_routing();
|
|
5638
|
-
|
|
5801
|
+
MAX_RETRIES2 = 10;
|
|
5639
5802
|
_wsClientSend = null;
|
|
5640
5803
|
}
|
|
5641
5804
|
});
|
|
5642
5805
|
|
|
5643
5806
|
// src/gateway/whatsapp-accounts.ts
|
|
5644
|
-
import { readFileSync as
|
|
5807
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
5645
5808
|
import { join } from "path";
|
|
5646
5809
|
import { homedir } from "os";
|
|
5647
5810
|
function loadAccounts() {
|
|
5648
5811
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
5649
5812
|
try {
|
|
5650
|
-
const raw =
|
|
5813
|
+
const raw = readFileSync14(CONFIG_PATH2, "utf8");
|
|
5651
5814
|
const parsed = JSON.parse(raw);
|
|
5652
5815
|
if (!Array.isArray(parsed)) {
|
|
5653
5816
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -5680,6 +5843,376 @@ var init_whatsapp_accounts = __esm({
|
|
|
5680
5843
|
}
|
|
5681
5844
|
});
|
|
5682
5845
|
|
|
5846
|
+
// src/lib/consolidation.ts
|
|
5847
|
+
var consolidation_exports = {};
|
|
5848
|
+
__export(consolidation_exports, {
|
|
5849
|
+
buildConsolidationPrompt: () => buildConsolidationPrompt,
|
|
5850
|
+
consolidateCluster: () => consolidateCluster,
|
|
5851
|
+
countUnconsolidated: () => countUnconsolidated,
|
|
5852
|
+
groupMemories: () => groupMemories,
|
|
5853
|
+
isUserIdle: () => isUserIdle,
|
|
5854
|
+
pushToWiki: () => pushToWiki,
|
|
5855
|
+
runConsolidation: () => runConsolidation,
|
|
5856
|
+
selectUnconsolidated: () => selectUnconsolidated,
|
|
5857
|
+
storeConsolidation: () => storeConsolidation
|
|
5858
|
+
});
|
|
5859
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5860
|
+
async function selectUnconsolidated(client, limit = 200) {
|
|
5861
|
+
const result = await client.execute({
|
|
5862
|
+
sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
|
|
5863
|
+
FROM memories
|
|
5864
|
+
WHERE consolidated = 0
|
|
5865
|
+
AND timestamp >= datetime('now', '-7 days')
|
|
5866
|
+
ORDER BY timestamp DESC
|
|
5867
|
+
LIMIT ?`,
|
|
5868
|
+
args: [limit]
|
|
5869
|
+
});
|
|
5870
|
+
return result.rows.map((row) => ({
|
|
5871
|
+
id: row.id,
|
|
5872
|
+
agent_id: row.agent_id,
|
|
5873
|
+
project_name: row.project_name,
|
|
5874
|
+
tool_name: row.tool_name,
|
|
5875
|
+
raw_text: row.raw_text,
|
|
5876
|
+
timestamp: row.timestamp
|
|
5877
|
+
}));
|
|
5878
|
+
}
|
|
5879
|
+
function groupMemories(memories) {
|
|
5880
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
5881
|
+
for (const mem of memories) {
|
|
5882
|
+
const key = `${mem.agent_id}::${mem.project_name}`;
|
|
5883
|
+
const list = byProject.get(key) ?? [];
|
|
5884
|
+
list.push(mem);
|
|
5885
|
+
byProject.set(key, list);
|
|
5886
|
+
}
|
|
5887
|
+
const clusters = [];
|
|
5888
|
+
for (const [key, projectMemories] of byProject) {
|
|
5889
|
+
const [agentId, projectName] = key.split("::");
|
|
5890
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
5891
|
+
for (const mem of projectMemories) {
|
|
5892
|
+
const day = mem.timestamp.slice(0, 10);
|
|
5893
|
+
const list = byDay.get(day) ?? [];
|
|
5894
|
+
list.push(mem);
|
|
5895
|
+
byDay.set(day, list);
|
|
5896
|
+
}
|
|
5897
|
+
for (const [day, dayMemories] of byDay) {
|
|
5898
|
+
if (dayMemories.length <= 15) {
|
|
5899
|
+
clusters.push({
|
|
5900
|
+
agentId,
|
|
5901
|
+
projectName,
|
|
5902
|
+
dateRange: day,
|
|
5903
|
+
memories: dayMemories
|
|
5904
|
+
});
|
|
5905
|
+
} else {
|
|
5906
|
+
for (let i = 0; i < dayMemories.length; i += 12) {
|
|
5907
|
+
const chunk = dayMemories.slice(i, i + 12);
|
|
5908
|
+
clusters.push({
|
|
5909
|
+
agentId,
|
|
5910
|
+
projectName,
|
|
5911
|
+
dateRange: day,
|
|
5912
|
+
memories: chunk
|
|
5913
|
+
});
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
return clusters;
|
|
5919
|
+
}
|
|
5920
|
+
function buildConsolidationPrompt(cluster) {
|
|
5921
|
+
const snippets = cluster.memories.map((m, i) => {
|
|
5922
|
+
const text = m.raw_text.length > 200 ? m.raw_text.slice(0, 200) + "..." : m.raw_text;
|
|
5923
|
+
return `${i + 1}. [${m.tool_name}] ${text}`;
|
|
5924
|
+
}).join("\n");
|
|
5925
|
+
return `You are reviewing a set of work memories from an AI coding agent.
|
|
5926
|
+
These are raw tool call records from ${cluster.dateRange}.
|
|
5927
|
+
|
|
5928
|
+
Agent: ${cluster.agentId} | Project: ${cluster.projectName} | Date: ${cluster.dateRange}
|
|
5929
|
+
|
|
5930
|
+
MEMORIES:
|
|
5931
|
+
${snippets}
|
|
5932
|
+
|
|
5933
|
+
Extract EXACTLY THREE types of insights:
|
|
5934
|
+
|
|
5935
|
+
1. KEY DECISIONS \u2014 choices that were made and why (at most 3)
|
|
5936
|
+
2. RECURRING PATTERNS \u2014 actions or approaches that repeated (at most 3)
|
|
5937
|
+
3. OPEN QUESTIONS \u2014 things that seem unresolved or risky (at most 2)
|
|
5938
|
+
|
|
5939
|
+
Format each as a single sentence. Be specific \u2014 include file names,
|
|
5940
|
+
function names, and concrete details. Skip if no insight for a category.`;
|
|
5941
|
+
}
|
|
5942
|
+
async function consolidateCluster(cluster, model) {
|
|
5943
|
+
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
5944
|
+
const client = new Anthropic();
|
|
5945
|
+
const prompt = buildConsolidationPrompt(cluster);
|
|
5946
|
+
const response = await client.messages.create({
|
|
5947
|
+
model,
|
|
5948
|
+
max_tokens: 300,
|
|
5949
|
+
messages: [{ role: "user", content: prompt }]
|
|
5950
|
+
});
|
|
5951
|
+
const textBlock = response.content.find((b) => b.type === "text");
|
|
5952
|
+
return textBlock?.text ?? "";
|
|
5953
|
+
}
|
|
5954
|
+
async function storeConsolidation(client, cluster, synthesisText, embedFn) {
|
|
5955
|
+
const consolidatedId = randomUUID4();
|
|
5956
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5957
|
+
const rawText = `CONSOLIDATION [${cluster.dateRange}, ${cluster.projectName}]:
|
|
5958
|
+
|
|
5959
|
+
${synthesisText}`;
|
|
5960
|
+
let vector = null;
|
|
5961
|
+
if (embedFn) {
|
|
5962
|
+
try {
|
|
5963
|
+
vector = await embedFn(rawText);
|
|
5964
|
+
} catch {
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
const insertSql = vector ? `INSERT INTO memories
|
|
5968
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
5969
|
+
tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
|
|
5970
|
+
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, vector32(?), 0, 1, 9)` : `INSERT INTO memories
|
|
5971
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
5972
|
+
tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
|
|
5973
|
+
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, NULL, 0, 1, 9)`;
|
|
5974
|
+
const insertArgs = vector ? [consolidatedId, cluster.agentId, now, cluster.projectName, rawText, vectorToBlob(vector)] : [consolidatedId, cluster.agentId, now, cluster.projectName, rawText];
|
|
5975
|
+
await client.execute({ sql: insertSql, args: insertArgs });
|
|
5976
|
+
const sourceIds = cluster.memories.map((m) => m.id);
|
|
5977
|
+
const linkStmts = sourceIds.map((sourceId) => ({
|
|
5978
|
+
sql: `INSERT INTO consolidations (id, consolidated_memory_id, source_memory_id, created_at)
|
|
5979
|
+
VALUES (?, ?, ?, ?)`,
|
|
5980
|
+
args: [randomUUID4(), consolidatedId, sourceId, now]
|
|
5981
|
+
}));
|
|
5982
|
+
const placeholders = sourceIds.map(() => "?").join(",");
|
|
5983
|
+
const markStmt = {
|
|
5984
|
+
sql: `UPDATE memories SET consolidated = 1 WHERE id IN (${placeholders})`,
|
|
5985
|
+
args: sourceIds
|
|
5986
|
+
};
|
|
5987
|
+
await client.batch([...linkStmts, markStmt], "write");
|
|
5988
|
+
return { consolidatedMemoryId: consolidatedId, sourceIds, rawText };
|
|
5989
|
+
}
|
|
5990
|
+
async function pushToWiki(consolidation, config2) {
|
|
5991
|
+
if (!config2.wikiEnabled || !config2.wikiAutoUpdate) {
|
|
5992
|
+
return { updated: false };
|
|
5993
|
+
}
|
|
5994
|
+
const apiUrl = config2.wikiUrl || process.env.EXE_WIKI_API_URL;
|
|
5995
|
+
const apiKey = config2.wikiApiKey || process.env.EXE_WIKI_API_KEY;
|
|
5996
|
+
if (!apiUrl || !apiKey) {
|
|
5997
|
+
return { updated: false };
|
|
5998
|
+
}
|
|
5999
|
+
const workspace = config2.wikiWorkspaceMapping[consolidation.projectName] ?? consolidation.projectName;
|
|
6000
|
+
try {
|
|
6001
|
+
const listRes = await fetch(
|
|
6002
|
+
`${apiUrl}/v1/workspace/${encodeURIComponent(workspace)}/documents`,
|
|
6003
|
+
{
|
|
6004
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
6005
|
+
signal: AbortSignal.timeout(WIKI_FETCH_TIMEOUT_MS)
|
|
6006
|
+
}
|
|
6007
|
+
);
|
|
6008
|
+
if (!listRes.ok) {
|
|
6009
|
+
return { updated: false, error: `Workspace "${workspace}" not accessible (${listRes.status})` };
|
|
6010
|
+
}
|
|
6011
|
+
const listJson = await listRes.json();
|
|
6012
|
+
const docs = listJson.documents ?? listJson.localFiles?.items ?? [];
|
|
6013
|
+
const contentLines = consolidation.rawText.split("\n").filter((l) => l.trim() && !l.startsWith("CONSOLIDATION") && !l.match(/^[A-Z\s]+:$/)).join(" ");
|
|
6014
|
+
const keywords = contentLines.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
|
|
6015
|
+
let bestMatch = null;
|
|
6016
|
+
for (const doc of docs) {
|
|
6017
|
+
if (!doc.id || !doc.title) continue;
|
|
6018
|
+
const titleWords = doc.title.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
|
|
6019
|
+
if (titleWords.length === 0) continue;
|
|
6020
|
+
const matchCount = titleWords.filter(
|
|
6021
|
+
(tw) => keywords.some((k) => k.includes(tw) || tw.includes(k))
|
|
6022
|
+
).length;
|
|
6023
|
+
const score = matchCount / titleWords.length;
|
|
6024
|
+
if (score > (bestMatch?.score ?? 0)) {
|
|
6025
|
+
bestMatch = { id: doc.id, title: doc.title, score };
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
if (bestMatch && bestMatch.score >= config2.wikiAutoUpdateThreshold) {
|
|
6029
|
+
const updateRes = await fetch(`${apiUrl}/v1/document/raw-text`, {
|
|
6030
|
+
method: "POST",
|
|
6031
|
+
headers: {
|
|
6032
|
+
"Content-Type": "application/json",
|
|
6033
|
+
Authorization: `Bearer ${apiKey}`
|
|
6034
|
+
},
|
|
6035
|
+
body: JSON.stringify({
|
|
6036
|
+
textContent: consolidation.rawText,
|
|
6037
|
+
metadata: { title: bestMatch.title, appendTo: bestMatch.id },
|
|
6038
|
+
workspaceSlugs: [workspace]
|
|
6039
|
+
}),
|
|
6040
|
+
signal: AbortSignal.timeout(WIKI_FETCH_TIMEOUT_MS)
|
|
6041
|
+
});
|
|
6042
|
+
if (updateRes.ok) {
|
|
6043
|
+
process.stderr.write(
|
|
6044
|
+
`[consolidation] Wiki auto-updated: ${workspace}/${bestMatch.title}
|
|
6045
|
+
`
|
|
6046
|
+
);
|
|
6047
|
+
return { updated: true, action: "updated", page: bestMatch.title };
|
|
6048
|
+
}
|
|
6049
|
+
return { updated: false, error: `Update failed (${updateRes.status})` };
|
|
6050
|
+
}
|
|
6051
|
+
if (config2.wikiAutoUpdateCreateNew) {
|
|
6052
|
+
const title = `Consolidated Insights \u2014 ${consolidation.projectName} (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)})`;
|
|
6053
|
+
const createRes = await fetch(`${apiUrl}/v1/document/raw-text`, {
|
|
6054
|
+
method: "POST",
|
|
6055
|
+
headers: {
|
|
6056
|
+
"Content-Type": "application/json",
|
|
6057
|
+
Authorization: `Bearer ${apiKey}`
|
|
6058
|
+
},
|
|
6059
|
+
body: JSON.stringify({
|
|
6060
|
+
textContent: consolidation.rawText,
|
|
6061
|
+
metadata: { title },
|
|
6062
|
+
workspaceSlugs: [workspace]
|
|
6063
|
+
}),
|
|
6064
|
+
signal: AbortSignal.timeout(WIKI_FETCH_TIMEOUT_MS)
|
|
6065
|
+
});
|
|
6066
|
+
if (createRes.ok) {
|
|
6067
|
+
process.stderr.write(
|
|
6068
|
+
`[consolidation] Wiki page created: ${workspace}/${title}
|
|
6069
|
+
`
|
|
6070
|
+
);
|
|
6071
|
+
return { updated: true, action: "created", page: title };
|
|
6072
|
+
}
|
|
6073
|
+
return { updated: false, error: `Create failed (${createRes.status})` };
|
|
6074
|
+
}
|
|
6075
|
+
return { updated: false };
|
|
6076
|
+
} catch (err) {
|
|
6077
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6078
|
+
process.stderr.write(`[consolidation] Wiki push failed: ${msg}
|
|
6079
|
+
`);
|
|
6080
|
+
return { updated: false, error: msg };
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
async function runConsolidation(client, options) {
|
|
6084
|
+
const memories = await selectUnconsolidated(client);
|
|
6085
|
+
if (memories.length < 20) {
|
|
6086
|
+
return { clustersProcessed: 0, memoriesConsolidated: 0 };
|
|
6087
|
+
}
|
|
6088
|
+
const clusters = groupMemories(memories);
|
|
6089
|
+
let clustersProcessed = 0;
|
|
6090
|
+
let memoriesConsolidated = 0;
|
|
6091
|
+
for (const cluster of clusters) {
|
|
6092
|
+
if (clustersProcessed >= options.maxCalls) break;
|
|
6093
|
+
if (cluster.memories.length < 3) continue;
|
|
6094
|
+
try {
|
|
6095
|
+
const isExe = cluster.agentId === "exe";
|
|
6096
|
+
if (isExe) {
|
|
6097
|
+
const synthesis = await consolidateCluster(cluster, options.model);
|
|
6098
|
+
if (!synthesis.trim()) continue;
|
|
6099
|
+
const result = await storeConsolidation(client, cluster, synthesis, options.embedFn);
|
|
6100
|
+
if (options.wikiConfig) {
|
|
6101
|
+
await pushToWiki(
|
|
6102
|
+
{ ...result, projectName: cluster.projectName },
|
|
6103
|
+
options.wikiConfig
|
|
6104
|
+
).catch((err) => {
|
|
6105
|
+
process.stderr.write(
|
|
6106
|
+
`[consolidation] Wiki push error (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
6107
|
+
`
|
|
6108
|
+
);
|
|
6109
|
+
});
|
|
6110
|
+
}
|
|
6111
|
+
const sourceIds = result.sourceIds;
|
|
6112
|
+
if (sourceIds.length > 0) {
|
|
6113
|
+
const placeholders = sourceIds.map(() => "?").join(",");
|
|
6114
|
+
await client.execute({
|
|
6115
|
+
sql: `UPDATE memories SET status = 'archived' WHERE id IN (${placeholders})`,
|
|
6116
|
+
args: sourceIds
|
|
6117
|
+
});
|
|
6118
|
+
}
|
|
6119
|
+
} else {
|
|
6120
|
+
const dedupCount = await dedupCluster(client, cluster, options.embedFn);
|
|
6121
|
+
memoriesConsolidated += dedupCount;
|
|
6122
|
+
if (dedupCount === 0) continue;
|
|
6123
|
+
}
|
|
6124
|
+
clustersProcessed++;
|
|
6125
|
+
memoriesConsolidated += isExe ? cluster.memories.length : 0;
|
|
6126
|
+
} catch (err) {
|
|
6127
|
+
process.stderr.write(
|
|
6128
|
+
`[consolidation] Cluster failed (${cluster.projectName}/${cluster.dateRange}): ${err instanceof Error ? err.message : String(err)}
|
|
6129
|
+
`
|
|
6130
|
+
);
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
return { clustersProcessed, memoriesConsolidated };
|
|
6134
|
+
}
|
|
6135
|
+
async function dedupCluster(client, cluster, embedFn) {
|
|
6136
|
+
if (!embedFn || cluster.memories.length < 2) return 0;
|
|
6137
|
+
const vectors = [];
|
|
6138
|
+
for (const mem of cluster.memories) {
|
|
6139
|
+
try {
|
|
6140
|
+
const v = await embedFn(mem.raw_text.slice(0, 500));
|
|
6141
|
+
vectors.push({ id: mem.id, vector: v });
|
|
6142
|
+
} catch {
|
|
6143
|
+
}
|
|
6144
|
+
}
|
|
6145
|
+
if (vectors.length < 2) return 0;
|
|
6146
|
+
const toArchive = /* @__PURE__ */ new Set();
|
|
6147
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
6148
|
+
if (toArchive.has(vectors[i].id)) continue;
|
|
6149
|
+
for (let j = i + 1; j < vectors.length; j++) {
|
|
6150
|
+
if (toArchive.has(vectors[j].id)) continue;
|
|
6151
|
+
const sim = cosineSimilarity(vectors[i].vector, vectors[j].vector);
|
|
6152
|
+
if (sim > 0.95) {
|
|
6153
|
+
toArchive.add(vectors[j].id);
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
6157
|
+
if (toArchive.size === 0) return 0;
|
|
6158
|
+
const ids = [...toArchive];
|
|
6159
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
6160
|
+
await client.execute({
|
|
6161
|
+
sql: `UPDATE memories SET status = 'archived', consolidated = 1 WHERE id IN (${placeholders})`,
|
|
6162
|
+
args: ids
|
|
6163
|
+
});
|
|
6164
|
+
const survivors = vectors.filter((v) => !toArchive.has(v.id)).map((v) => v.id);
|
|
6165
|
+
if (survivors.length > 0) {
|
|
6166
|
+
const survivorPlaceholders = survivors.map(() => "?").join(",");
|
|
6167
|
+
await client.execute({
|
|
6168
|
+
sql: `UPDATE memories SET confidence = MIN(1.0, COALESCE(confidence, 0.7) + 0.1) WHERE id IN (${survivorPlaceholders})`,
|
|
6169
|
+
args: survivors
|
|
6170
|
+
});
|
|
6171
|
+
}
|
|
6172
|
+
return ids.length;
|
|
6173
|
+
}
|
|
6174
|
+
function cosineSimilarity(a, b) {
|
|
6175
|
+
let dot = 0, normA = 0, normB = 0;
|
|
6176
|
+
for (let i = 0; i < a.length; i++) {
|
|
6177
|
+
dot += a[i] * b[i];
|
|
6178
|
+
normA += a[i] * a[i];
|
|
6179
|
+
normB += b[i] * b[i];
|
|
6180
|
+
}
|
|
6181
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
6182
|
+
return denom === 0 ? 0 : dot / denom;
|
|
6183
|
+
}
|
|
6184
|
+
async function isUserIdle(client, idleMinutes = 30) {
|
|
6185
|
+
const result = await client.execute({
|
|
6186
|
+
sql: `SELECT MAX(timestamp) as last_activity
|
|
6187
|
+
FROM memories
|
|
6188
|
+
WHERE tool_name != 'consolidation'
|
|
6189
|
+
AND timestamp >= datetime('now', '-1 day')`,
|
|
6190
|
+
args: []
|
|
6191
|
+
});
|
|
6192
|
+
const lastActivity = result.rows[0]?.last_activity;
|
|
6193
|
+
if (!lastActivity) return true;
|
|
6194
|
+
const lastMs = new Date(lastActivity).getTime();
|
|
6195
|
+
const now = Date.now();
|
|
6196
|
+
return now - lastMs >= idleMinutes * 60 * 1e3;
|
|
6197
|
+
}
|
|
6198
|
+
async function countUnconsolidated(client) {
|
|
6199
|
+
const result = await client.execute({
|
|
6200
|
+
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
6201
|
+
WHERE consolidated = 0
|
|
6202
|
+
AND timestamp >= datetime('now', '-7 days')`,
|
|
6203
|
+
args: []
|
|
6204
|
+
});
|
|
6205
|
+
return Number(result.rows[0]?.cnt ?? 0);
|
|
6206
|
+
}
|
|
6207
|
+
var WIKI_FETCH_TIMEOUT_MS;
|
|
6208
|
+
var init_consolidation = __esm({
|
|
6209
|
+
"src/lib/consolidation.ts"() {
|
|
6210
|
+
"use strict";
|
|
6211
|
+
init_store();
|
|
6212
|
+
WIKI_FETCH_TIMEOUT_MS = 1e4;
|
|
6213
|
+
}
|
|
6214
|
+
});
|
|
6215
|
+
|
|
5683
6216
|
// src/mcp/server.ts
|
|
5684
6217
|
init_embedder();
|
|
5685
6218
|
init_store();
|
|
@@ -6779,8 +7312,8 @@ function registerCreateTask(server2) {
|
|
|
6779
7312
|
try {
|
|
6780
7313
|
const exeSession = resolveExeSession();
|
|
6781
7314
|
if (exeSession) {
|
|
6782
|
-
const
|
|
6783
|
-
const useAutoInstance =
|
|
7315
|
+
const { isMultiInstance: isMultiInstance2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
7316
|
+
const useAutoInstance = isMultiInstance2(assigned_to);
|
|
6784
7317
|
const { loadConfigSync: loadConfigSync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6785
7318
|
const cfg = loadConfigSync2();
|
|
6786
7319
|
const result = ensureEmployee(assigned_to, exeSession, process.cwd(), {
|
|
@@ -7588,7 +8121,7 @@ import { z as z20 } from "zod";
|
|
|
7588
8121
|
// src/lib/identity.ts
|
|
7589
8122
|
init_config();
|
|
7590
8123
|
init_database();
|
|
7591
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as
|
|
8124
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
7592
8125
|
import { readdirSync as readdirSync5 } from "fs";
|
|
7593
8126
|
import path22 from "path";
|
|
7594
8127
|
import { createHash } from "crypto";
|
|
@@ -7641,7 +8174,7 @@ function contentHash(content) {
|
|
|
7641
8174
|
function getIdentity(agentId) {
|
|
7642
8175
|
const filePath = identityPath(agentId);
|
|
7643
8176
|
if (!existsSync16(filePath)) return null;
|
|
7644
|
-
const raw =
|
|
8177
|
+
const raw = readFileSync13(filePath, "utf-8");
|
|
7645
8178
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
7646
8179
|
return {
|
|
7647
8180
|
agentId,
|
|
@@ -8297,7 +8830,6 @@ function registerAcknowledgeMessages(server2) {
|
|
|
8297
8830
|
const agent = getActiveAgent();
|
|
8298
8831
|
const agentId = agent.agentId || "exe";
|
|
8299
8832
|
const client = getClient();
|
|
8300
|
-
await client.execute({ sql: "PRAGMA busy_timeout = 5000", args: [] });
|
|
8301
8833
|
const result = await client.execute({
|
|
8302
8834
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
8303
8835
|
WHERE target_agent = ? AND status IN ('pending', 'delivered')`,
|
|
@@ -8627,7 +9159,7 @@ function registerSendWhatsapp(server2) {
|
|
|
8627
9159
|
import { z as z29 } from "zod";
|
|
8628
9160
|
|
|
8629
9161
|
// src/automation/trigger-engine.ts
|
|
8630
|
-
import { readFileSync as
|
|
9162
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
8631
9163
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
8632
9164
|
import path23 from "path";
|
|
8633
9165
|
import os6 from "os";
|
|
@@ -8635,7 +9167,7 @@ var TRIGGERS_PATH = path23.join(os6.homedir(), ".exe-os", "triggers.json");
|
|
|
8635
9167
|
function loadTriggers(project) {
|
|
8636
9168
|
if (!existsSync17(TRIGGERS_PATH)) return [];
|
|
8637
9169
|
try {
|
|
8638
|
-
const raw =
|
|
9170
|
+
const raw = readFileSync15(TRIGGERS_PATH, "utf-8");
|
|
8639
9171
|
const all = JSON.parse(raw);
|
|
8640
9172
|
if (!Array.isArray(all)) return [];
|
|
8641
9173
|
if (project) {
|
|
@@ -8933,7 +9465,7 @@ function registerListTriggers(server2) {
|
|
|
8933
9465
|
import { z as z31 } from "zod";
|
|
8934
9466
|
|
|
8935
9467
|
// src/automation/starter-packs/index.ts
|
|
8936
|
-
import { readFileSync as
|
|
9468
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync6, existsSync as existsSync18 } from "fs";
|
|
8937
9469
|
import path24 from "path";
|
|
8938
9470
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8939
9471
|
var __dirname = path24.dirname(fileURLToPath3(import.meta.url));
|
|
@@ -8955,7 +9487,7 @@ function loadPack(industry) {
|
|
|
8955
9487
|
let customObjects = [];
|
|
8956
9488
|
try {
|
|
8957
9489
|
customObjects = JSON.parse(
|
|
8958
|
-
|
|
9490
|
+
readFileSync16(objectsPath, "utf-8")
|
|
8959
9491
|
);
|
|
8960
9492
|
} catch {
|
|
8961
9493
|
customObjects = [];
|
|
@@ -8964,7 +9496,7 @@ function loadPack(industry) {
|
|
|
8964
9496
|
if (existsSync18(triggersPath)) {
|
|
8965
9497
|
try {
|
|
8966
9498
|
triggers = JSON.parse(
|
|
8967
|
-
|
|
9499
|
+
readFileSync16(triggersPath, "utf-8")
|
|
8968
9500
|
);
|
|
8969
9501
|
} catch {
|
|
8970
9502
|
triggers = [];
|
|
@@ -8974,7 +9506,7 @@ function loadPack(industry) {
|
|
|
8974
9506
|
if (existsSync18(wikiDir)) {
|
|
8975
9507
|
const files = readdirSync6(wikiDir).filter((f) => f.endsWith(".md"));
|
|
8976
9508
|
for (const file of files) {
|
|
8977
|
-
const content =
|
|
9509
|
+
const content = readFileSync16(path24.join(wikiDir, file), "utf-8");
|
|
8978
9510
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
8979
9511
|
wikiSeeds.push({
|
|
8980
9512
|
filename: file,
|
|
@@ -8986,7 +9518,7 @@ function loadPack(industry) {
|
|
|
8986
9518
|
let manifest = {};
|
|
8987
9519
|
if (existsSync18(manifestPath)) {
|
|
8988
9520
|
try {
|
|
8989
|
-
manifest = JSON.parse(
|
|
9521
|
+
manifest = JSON.parse(readFileSync16(manifestPath, "utf-8"));
|
|
8990
9522
|
} catch {
|
|
8991
9523
|
manifest = {};
|
|
8992
9524
|
}
|
|
@@ -8994,7 +9526,7 @@ function loadPack(industry) {
|
|
|
8994
9526
|
let identityContext = null;
|
|
8995
9527
|
if (existsSync18(identityContextPath)) {
|
|
8996
9528
|
try {
|
|
8997
|
-
identityContext =
|
|
9529
|
+
identityContext = readFileSync16(identityContextPath, "utf-8");
|
|
8998
9530
|
} catch {
|
|
8999
9531
|
identityContext = null;
|
|
9000
9532
|
}
|
|
@@ -10474,7 +11006,7 @@ function registerQueryConversations(server2) {
|
|
|
10474
11006
|
|
|
10475
11007
|
// src/mcp/tools/load-skill.ts
|
|
10476
11008
|
import { z as z39 } from "zod";
|
|
10477
|
-
import { readFileSync as
|
|
11009
|
+
import { readFileSync as readFileSync17, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
|
|
10478
11010
|
import path27 from "path";
|
|
10479
11011
|
import { homedir as homedir2 } from "os";
|
|
10480
11012
|
var SKILLS_DIR = path27.join(homedir2(), ".claude", "skills");
|
|
@@ -10531,7 +11063,7 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
10531
11063
|
const sanitized = path27.basename(skill_name);
|
|
10532
11064
|
const skillFile = path27.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
10533
11065
|
try {
|
|
10534
|
-
const content =
|
|
11066
|
+
const content = readFileSync17(skillFile, "utf-8");
|
|
10535
11067
|
return {
|
|
10536
11068
|
content: [{
|
|
10537
11069
|
type: "text",
|
|
@@ -10557,12 +11089,133 @@ ${available.map((s) => `- ${s}`).join("\n")}` : "\n\nNo skills found in ~/.claud
|
|
|
10557
11089
|
);
|
|
10558
11090
|
}
|
|
10559
11091
|
|
|
11092
|
+
// src/mcp/tools/consolidate-memories.ts
|
|
11093
|
+
init_database();
|
|
11094
|
+
init_active_agent();
|
|
11095
|
+
import { z as z40 } from "zod";
|
|
11096
|
+
function registerConsolidateMemories(server2) {
|
|
11097
|
+
server2.registerTool(
|
|
11098
|
+
"consolidate_memories",
|
|
11099
|
+
{
|
|
11100
|
+
title: "Consolidate Memories",
|
|
11101
|
+
description: "Trigger content-aware memory consolidation. Groups recent memories by project, uses LLM to extract key facts, decisions, and patterns, then stores a high-importance summary and archives the originals. Runs automatically during idle time, but can be triggered manually when you want to compress your working memory.",
|
|
11102
|
+
inputSchema: {
|
|
11103
|
+
max_clusters: z40.coerce.number().optional().default(5).describe("Maximum number of clusters to consolidate (default 5)"),
|
|
11104
|
+
model: z40.string().optional().describe("LLM model for summarization (defaults to config value)")
|
|
11105
|
+
}
|
|
11106
|
+
},
|
|
11107
|
+
async ({ max_clusters, model }) => {
|
|
11108
|
+
const { agentId } = getActiveAgent();
|
|
11109
|
+
try {
|
|
11110
|
+
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11111
|
+
const config2 = await loadConfig2();
|
|
11112
|
+
const consolidationModel = model ?? config2.consolidationModel ?? "claude-haiku-4-5-20251001";
|
|
11113
|
+
const { runConsolidation: runConsolidation2 } = await Promise.resolve().then(() => (init_consolidation(), consolidation_exports));
|
|
11114
|
+
const client = getClient();
|
|
11115
|
+
let embedFn;
|
|
11116
|
+
try {
|
|
11117
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
11118
|
+
embedFn = embed2;
|
|
11119
|
+
} catch {
|
|
11120
|
+
}
|
|
11121
|
+
const result = await runConsolidation2(client, {
|
|
11122
|
+
model: consolidationModel,
|
|
11123
|
+
maxCalls: max_clusters,
|
|
11124
|
+
embedFn
|
|
11125
|
+
});
|
|
11126
|
+
if (result.clustersProcessed === 0) {
|
|
11127
|
+
return {
|
|
11128
|
+
content: [{
|
|
11129
|
+
type: "text",
|
|
11130
|
+
text: `No consolidation needed \u2014 fewer than 20 unconsolidated memories for ${agentId}.`
|
|
11131
|
+
}]
|
|
11132
|
+
};
|
|
11133
|
+
}
|
|
11134
|
+
return {
|
|
11135
|
+
content: [{
|
|
11136
|
+
type: "text",
|
|
11137
|
+
text: `Consolidation complete:
|
|
11138
|
+
- Clusters processed: ${result.clustersProcessed}
|
|
11139
|
+
- Memories consolidated: ${result.memoriesConsolidated}
|
|
11140
|
+
|
|
11141
|
+
Consolidated summaries stored as tier-1 (importance=9) memories.`
|
|
11142
|
+
}]
|
|
11143
|
+
};
|
|
11144
|
+
} catch (err) {
|
|
11145
|
+
return {
|
|
11146
|
+
content: [{
|
|
11147
|
+
type: "text",
|
|
11148
|
+
text: `Consolidation failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11149
|
+
}]
|
|
11150
|
+
};
|
|
11151
|
+
}
|
|
11152
|
+
}
|
|
11153
|
+
);
|
|
11154
|
+
}
|
|
11155
|
+
|
|
11156
|
+
// src/lib/telemetry.ts
|
|
11157
|
+
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
11158
|
+
var initialized = false;
|
|
11159
|
+
async function ensureInit() {
|
|
11160
|
+
if (initialized || !ENABLED) return;
|
|
11161
|
+
initialized = true;
|
|
11162
|
+
try {
|
|
11163
|
+
const { NodeSDK } = await import("@opentelemetry/sdk-node");
|
|
11164
|
+
const { ConsoleSpanExporter } = await import("@opentelemetry/sdk-trace-base");
|
|
11165
|
+
const sdk = new NodeSDK({
|
|
11166
|
+
serviceName: "exe-os",
|
|
11167
|
+
traceExporter: new ConsoleSpanExporter()
|
|
11168
|
+
});
|
|
11169
|
+
sdk.start();
|
|
11170
|
+
process.stderr.write("[exe-os] OpenTelemetry tracing enabled\n");
|
|
11171
|
+
} catch (err) {
|
|
11172
|
+
process.stderr.write(
|
|
11173
|
+
`[exe-os] OpenTelemetry init failed: ${err instanceof Error ? err.message : String(err)}
|
|
11174
|
+
`
|
|
11175
|
+
);
|
|
11176
|
+
}
|
|
11177
|
+
}
|
|
11178
|
+
async function withTrace(toolName, fn) {
|
|
11179
|
+
if (!ENABLED) return fn();
|
|
11180
|
+
await ensureInit();
|
|
11181
|
+
const { trace, SpanStatusCode } = await import("@opentelemetry/api");
|
|
11182
|
+
const tracer = trace.getTracer("exe-os", "1.0.0");
|
|
11183
|
+
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
11184
|
+
span.setAttribute("mcp.tool.name", toolName);
|
|
11185
|
+
try {
|
|
11186
|
+
const result = await fn();
|
|
11187
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
11188
|
+
return result;
|
|
11189
|
+
} catch (err) {
|
|
11190
|
+
span.setStatus({
|
|
11191
|
+
code: SpanStatusCode.ERROR,
|
|
11192
|
+
message: err instanceof Error ? err.message : String(err)
|
|
11193
|
+
});
|
|
11194
|
+
span.recordException(
|
|
11195
|
+
err instanceof Error ? err : new Error(String(err))
|
|
11196
|
+
);
|
|
11197
|
+
throw err;
|
|
11198
|
+
} finally {
|
|
11199
|
+
span.end();
|
|
11200
|
+
}
|
|
11201
|
+
});
|
|
11202
|
+
}
|
|
11203
|
+
function instrumentServer(server2) {
|
|
11204
|
+
if (!ENABLED) return;
|
|
11205
|
+
const original = server2.registerTool.bind(server2);
|
|
11206
|
+
server2.registerTool = (name, config2, handler) => {
|
|
11207
|
+
const traced = async (...args) => withTrace(name, () => handler(...args));
|
|
11208
|
+
return original(name, config2, traced);
|
|
11209
|
+
};
|
|
11210
|
+
}
|
|
11211
|
+
|
|
10560
11212
|
// src/mcp/server.ts
|
|
10561
11213
|
var server = new McpServer({
|
|
10562
11214
|
name: "exe-os",
|
|
10563
11215
|
version: "1.3.0"
|
|
10564
11216
|
});
|
|
10565
11217
|
var _backfillTimer = null;
|
|
11218
|
+
instrumentServer(server);
|
|
10566
11219
|
registerRecallMyMemory(server);
|
|
10567
11220
|
registerAskTeamMemory(server);
|
|
10568
11221
|
registerGetSessionContext(server);
|
|
@@ -10603,6 +11256,7 @@ registerUpdateWikiPage(server);
|
|
|
10603
11256
|
registerDeployClient(server);
|
|
10604
11257
|
registerQueryConversations(server);
|
|
10605
11258
|
registerLoadSkill(server);
|
|
11259
|
+
registerConsolidateMemories(server);
|
|
10606
11260
|
try {
|
|
10607
11261
|
await initStore();
|
|
10608
11262
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
@@ -10678,3 +11332,18 @@ async function shutdown() {
|
|
|
10678
11332
|
}
|
|
10679
11333
|
process.on("SIGINT", () => void shutdown());
|
|
10680
11334
|
process.on("SIGTERM", () => void shutdown());
|
|
11335
|
+
process.on("uncaughtException", (err) => {
|
|
11336
|
+
process.stderr.write(
|
|
11337
|
+
`[exe-os] uncaughtException (non-fatal): ${err.message}
|
|
11338
|
+
`
|
|
11339
|
+
);
|
|
11340
|
+
if (err.stack) {
|
|
11341
|
+
process.stderr.write(`[exe-os] ${err.stack.split("\n").slice(1, 3).join("\n ")}
|
|
11342
|
+
`);
|
|
11343
|
+
}
|
|
11344
|
+
});
|
|
11345
|
+
process.on("unhandledRejection", (reason) => {
|
|
11346
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
11347
|
+
process.stderr.write(`[exe-os] unhandledRejection (non-fatal): ${msg}
|
|
11348
|
+
`);
|
|
11349
|
+
});
|