@askexenow/exe-os 0.9.53 → 0.9.55
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 +103 -0
- package/dist/bin/backfill-responses.js +103 -0
- package/dist/bin/backfill-vectors.js +103 -0
- package/dist/bin/cleanup-stale-review-tasks.js +103 -0
- package/dist/bin/cli.js +121 -10
- package/dist/bin/exe-assign.js +103 -0
- package/dist/bin/exe-boot.js +96 -10
- package/dist/bin/exe-call.js +25 -0
- package/dist/bin/exe-cloud.js +31 -10
- package/dist/bin/exe-dispatch.js +103 -0
- package/dist/bin/exe-doctor.js +152 -3
- package/dist/bin/exe-export-behaviors.js +103 -0
- package/dist/bin/exe-forget.js +103 -0
- package/dist/bin/exe-gateway.js +103 -0
- package/dist/bin/exe-heartbeat.js +103 -0
- package/dist/bin/exe-kill.js +103 -0
- package/dist/bin/exe-launch-agent.js +103 -0
- package/dist/bin/exe-link.js +31 -10
- package/dist/bin/exe-new-employee.js +25 -0
- package/dist/bin/exe-pending-messages.js +103 -0
- package/dist/bin/exe-pending-notifications.js +103 -0
- package/dist/bin/exe-pending-reviews.js +103 -0
- package/dist/bin/exe-rename.js +103 -0
- package/dist/bin/exe-review.js +103 -0
- package/dist/bin/exe-search.js +103 -0
- package/dist/bin/exe-session-cleanup.js +103 -0
- package/dist/bin/exe-start-codex.js +103 -0
- package/dist/bin/exe-start-opencode.js +103 -0
- package/dist/bin/exe-status.js +103 -0
- package/dist/bin/exe-team.js +103 -0
- package/dist/bin/git-sweep.js +103 -0
- package/dist/bin/graph-backfill.js +103 -0
- package/dist/bin/graph-export.js +103 -0
- package/dist/bin/intercom-check.js +103 -0
- package/dist/bin/scan-tasks.js +103 -0
- package/dist/bin/setup.js +56 -10
- package/dist/bin/shard-migrate.js +103 -0
- package/dist/gateway/index.js +103 -0
- package/dist/hooks/bug-report-worker.js +103 -0
- package/dist/hooks/codex-stop-task-finalizer.js +103 -0
- package/dist/hooks/commit-complete.js +103 -0
- package/dist/hooks/error-recall.js +103 -0
- package/dist/hooks/ingest.js +103 -0
- package/dist/hooks/instructions-loaded.js +103 -0
- package/dist/hooks/notification.js +103 -0
- package/dist/hooks/post-compact.js +103 -0
- package/dist/hooks/post-tool-combined.js +103 -0
- package/dist/hooks/pre-compact.js +103 -0
- package/dist/hooks/pre-tool-use.js +103 -0
- package/dist/hooks/prompt-submit.js +103 -0
- package/dist/hooks/session-end.js +103 -0
- package/dist/hooks/session-start.js +103 -0
- package/dist/hooks/stop.js +103 -0
- package/dist/hooks/subagent-stop.js +103 -0
- package/dist/hooks/summary-worker.js +96 -10
- package/dist/index.js +103 -0
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/employee-templates.js +25 -0
- package/dist/lib/exe-daemon.js +165 -14
- package/dist/lib/hybrid-search.js +103 -0
- package/dist/lib/keychain.js +31 -10
- package/dist/lib/schedules.js +103 -0
- package/dist/lib/store.js +103 -0
- package/dist/mcp/server.js +164 -13
- package/dist/runtime/index.js +103 -0
- package/dist/tui/App.js +96 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6926,6 +6926,15 @@ function readMachineId() {
|
|
|
6926
6926
|
return "";
|
|
6927
6927
|
}
|
|
6928
6928
|
}
|
|
6929
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
6930
|
+
const crypto11 = __require("crypto");
|
|
6931
|
+
const iv = crypto11.randomBytes(12);
|
|
6932
|
+
const cipher = crypto11.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
6933
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
6934
|
+
encrypted += cipher.final("base64");
|
|
6935
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
6936
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
6937
|
+
}
|
|
6929
6938
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
6930
6939
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
6931
6940
|
try {
|
|
@@ -6944,6 +6953,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
6944
6953
|
return null;
|
|
6945
6954
|
}
|
|
6946
6955
|
}
|
|
6956
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
6957
|
+
const dir = getKeyDir();
|
|
6958
|
+
await mkdir4(dir, { recursive: true });
|
|
6959
|
+
const keyPath = getKeyPath();
|
|
6960
|
+
const machineKey = deriveMachineKey();
|
|
6961
|
+
if (machineKey) {
|
|
6962
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
6963
|
+
await writeFile5(keyPath, encrypted + "\n", "utf-8");
|
|
6964
|
+
await chmod2(keyPath, 384);
|
|
6965
|
+
return "encrypted";
|
|
6966
|
+
}
|
|
6967
|
+
await writeFile5(keyPath, b64 + "\n", "utf-8");
|
|
6968
|
+
await chmod2(keyPath, 384);
|
|
6969
|
+
return "plaintext";
|
|
6970
|
+
}
|
|
6947
6971
|
async function getMasterKey() {
|
|
6948
6972
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
6949
6973
|
if (nativeValue) {
|
|
@@ -6995,6 +7019,20 @@ async function getMasterKey() {
|
|
|
6995
7019
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
6996
7020
|
if (migrated) {
|
|
6997
7021
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
7022
|
+
try {
|
|
7023
|
+
await unlink(keyPath);
|
|
7024
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
7025
|
+
} catch {
|
|
7026
|
+
}
|
|
7027
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
7028
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
7029
|
+
if (fallback === "encrypted") {
|
|
7030
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
7031
|
+
} else {
|
|
7032
|
+
process.stderr.write(
|
|
7033
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
7034
|
+
);
|
|
7035
|
+
}
|
|
6998
7036
|
}
|
|
6999
7037
|
return key;
|
|
7000
7038
|
} catch (err) {
|
|
@@ -7212,6 +7250,7 @@ var init_memory_write_governor = __esm({
|
|
|
7212
7250
|
// src/lib/shard-manager.ts
|
|
7213
7251
|
var shard_manager_exports = {};
|
|
7214
7252
|
__export(shard_manager_exports, {
|
|
7253
|
+
auditShardHealth: () => auditShardHealth,
|
|
7215
7254
|
disposeShards: () => disposeShards,
|
|
7216
7255
|
ensureShardSchema: () => ensureShardSchema,
|
|
7217
7256
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -7278,6 +7317,70 @@ function listShards() {
|
|
|
7278
7317
|
if (!existsSync16(SHARDS_DIR)) return [];
|
|
7279
7318
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
7280
7319
|
}
|
|
7320
|
+
async function auditShardHealth(options = {}) {
|
|
7321
|
+
if (!_encryptionKey) {
|
|
7322
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7323
|
+
}
|
|
7324
|
+
const repair = options.repair === true;
|
|
7325
|
+
const dryRun = options.dryRun === true;
|
|
7326
|
+
const names = listShards();
|
|
7327
|
+
const shards = [];
|
|
7328
|
+
for (const name of names) {
|
|
7329
|
+
const dbPath = path20.join(SHARDS_DIR, `${name}.db`);
|
|
7330
|
+
const stat = statSync2(dbPath);
|
|
7331
|
+
const item = {
|
|
7332
|
+
name,
|
|
7333
|
+
path: dbPath,
|
|
7334
|
+
ok: false,
|
|
7335
|
+
unreadable: false,
|
|
7336
|
+
error: null,
|
|
7337
|
+
size: stat.size,
|
|
7338
|
+
mtime: stat.mtime.toISOString(),
|
|
7339
|
+
memoryCount: null
|
|
7340
|
+
};
|
|
7341
|
+
const client = createClient2({
|
|
7342
|
+
url: `file:${dbPath}`,
|
|
7343
|
+
encryptionKey: _encryptionKey
|
|
7344
|
+
});
|
|
7345
|
+
try {
|
|
7346
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
7347
|
+
const hasMemories = await client.execute(
|
|
7348
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
7349
|
+
);
|
|
7350
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
7351
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
7352
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
7353
|
+
}
|
|
7354
|
+
item.ok = true;
|
|
7355
|
+
} catch (err) {
|
|
7356
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7357
|
+
item.error = message;
|
|
7358
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
7359
|
+
if (item.unreadable && repair && !dryRun) {
|
|
7360
|
+
client.close();
|
|
7361
|
+
_shards.delete(name);
|
|
7362
|
+
_shardLastAccess.delete(name);
|
|
7363
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7364
|
+
const archivedPath = path20.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
7365
|
+
renameSync4(dbPath, archivedPath);
|
|
7366
|
+
item.archivedPath = archivedPath;
|
|
7367
|
+
}
|
|
7368
|
+
} finally {
|
|
7369
|
+
try {
|
|
7370
|
+
client.close();
|
|
7371
|
+
} catch {
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
7374
|
+
shards.push(item);
|
|
7375
|
+
}
|
|
7376
|
+
return {
|
|
7377
|
+
total: shards.length,
|
|
7378
|
+
ok: shards.filter((s) => s.ok).length,
|
|
7379
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
7380
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
7381
|
+
shards
|
|
7382
|
+
};
|
|
7383
|
+
}
|
|
7281
7384
|
async function ensureShardSchema(client) {
|
|
7282
7385
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
7283
7386
|
await client.execute("PRAGMA busy_timeout = 30000");
|
package/dist/lib/cloud-sync.js
CHANGED
|
@@ -2915,6 +2915,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2915
2915
|
return null;
|
|
2916
2916
|
}
|
|
2917
2917
|
}
|
|
2918
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2919
|
+
const dir = getKeyDir();
|
|
2920
|
+
await mkdir3(dir, { recursive: true });
|
|
2921
|
+
const keyPath = getKeyPath();
|
|
2922
|
+
const machineKey = deriveMachineKey();
|
|
2923
|
+
if (machineKey) {
|
|
2924
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2925
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2926
|
+
await chmod2(keyPath, 384);
|
|
2927
|
+
return "encrypted";
|
|
2928
|
+
}
|
|
2929
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2930
|
+
await chmod2(keyPath, 384);
|
|
2931
|
+
return "plaintext";
|
|
2932
|
+
}
|
|
2918
2933
|
async function getMasterKey() {
|
|
2919
2934
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2920
2935
|
if (nativeValue) {
|
|
@@ -2966,6 +2981,20 @@ async function getMasterKey() {
|
|
|
2966
2981
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2967
2982
|
if (migrated) {
|
|
2968
2983
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2984
|
+
try {
|
|
2985
|
+
await unlink(keyPath);
|
|
2986
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2987
|
+
} catch {
|
|
2988
|
+
}
|
|
2989
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2990
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
2991
|
+
if (fallback === "encrypted") {
|
|
2992
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
2993
|
+
} else {
|
|
2994
|
+
process.stderr.write(
|
|
2995
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2969
2998
|
}
|
|
2970
2999
|
return key;
|
|
2971
3000
|
} catch (err) {
|
|
@@ -2989,18 +3018,10 @@ async function setMasterKey(key) {
|
|
|
2989
3018
|
} catch {
|
|
2990
3019
|
}
|
|
2991
3020
|
}
|
|
2992
|
-
const
|
|
2993
|
-
|
|
2994
|
-
const keyPath = getKeyPath();
|
|
2995
|
-
const machineKey = deriveMachineKey();
|
|
2996
|
-
if (machineKey) {
|
|
2997
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2998
|
-
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2999
|
-
await chmod2(keyPath, 384);
|
|
3021
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
3022
|
+
if (fallback === "encrypted") {
|
|
3000
3023
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
3001
3024
|
} else {
|
|
3002
|
-
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3003
|
-
await chmod2(keyPath, 384);
|
|
3004
3025
|
process.stderr.write(
|
|
3005
3026
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
3006
3027
|
);
|
|
@@ -792,6 +792,31 @@ Audit method:
|
|
|
792
792
|
6. Write structured report with PASS/FAIL per item
|
|
793
793
|
|
|
794
794
|
After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
|
|
795
|
+
},
|
|
796
|
+
teddy: {
|
|
797
|
+
name: "teddy",
|
|
798
|
+
role: "Chief of Staff",
|
|
799
|
+
systemPrompt: `You are teddy, the Chief of Staff and executive assistant. You help the founder recall context, understand what happened, prepare concise briefs, and triage inbound conversations. You report to the COO.
|
|
800
|
+
|
|
801
|
+
Your job is read-first, not action-first:
|
|
802
|
+
- Retrieve memories, decisions, runbooks, and session context on demand
|
|
803
|
+
- Summarize what matters without changing source data
|
|
804
|
+
- Triage inbound conversations and surface likely bugs, requests, and follow-ups
|
|
805
|
+
- Prepare daily briefs and "what changed?" summaries
|
|
806
|
+
- Route recommended actions to the COO instead of taking them yourself
|
|
807
|
+
|
|
808
|
+
Permissions boundary:
|
|
809
|
+
- You are read-only by default.
|
|
810
|
+
- You may use recall_my_memory, ask_team_memory, get_memory_by_id, get_session_context, search_everything, query_conversations, list_tasks, and get_task.
|
|
811
|
+
- You must not create tasks, update tasks, store memories, send WhatsApp messages, mutate CRM/wiki/documents, deploy, or change configuration unless the founder explicitly promotes your permissions.
|
|
812
|
+
- If a requested action requires write access, explain the action and recommend that the COO dispatch it.
|
|
813
|
+
|
|
814
|
+
Operating style:
|
|
815
|
+
- Be concise and precise.
|
|
816
|
+
- Cite memory IDs, task IDs, timestamps, and sender names when available.
|
|
817
|
+
- Distinguish fact from inference.
|
|
818
|
+
- Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
|
|
819
|
+
- Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
|
|
795
820
|
}
|
|
796
821
|
};
|
|
797
822
|
function buildCustomEmployeePrompt(name, role) {
|
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -3635,6 +3635,7 @@ var init_projection_worker = __esm({
|
|
|
3635
3635
|
// src/lib/shard-manager.ts
|
|
3636
3636
|
var shard_manager_exports = {};
|
|
3637
3637
|
__export(shard_manager_exports, {
|
|
3638
|
+
auditShardHealth: () => auditShardHealth,
|
|
3638
3639
|
disposeShards: () => disposeShards,
|
|
3639
3640
|
ensureShardSchema: () => ensureShardSchema,
|
|
3640
3641
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3701,6 +3702,70 @@ function listShards() {
|
|
|
3701
3702
|
if (!existsSync9(SHARDS_DIR)) return [];
|
|
3702
3703
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3703
3704
|
}
|
|
3705
|
+
async function auditShardHealth(options = {}) {
|
|
3706
|
+
if (!_encryptionKey) {
|
|
3707
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3708
|
+
}
|
|
3709
|
+
const repair = options.repair === true;
|
|
3710
|
+
const dryRun = options.dryRun === true;
|
|
3711
|
+
const names = listShards();
|
|
3712
|
+
const shards = [];
|
|
3713
|
+
for (const name of names) {
|
|
3714
|
+
const dbPath = path9.join(SHARDS_DIR, `${name}.db`);
|
|
3715
|
+
const stat = statSync2(dbPath);
|
|
3716
|
+
const item = {
|
|
3717
|
+
name,
|
|
3718
|
+
path: dbPath,
|
|
3719
|
+
ok: false,
|
|
3720
|
+
unreadable: false,
|
|
3721
|
+
error: null,
|
|
3722
|
+
size: stat.size,
|
|
3723
|
+
mtime: stat.mtime.toISOString(),
|
|
3724
|
+
memoryCount: null
|
|
3725
|
+
};
|
|
3726
|
+
const client = createClient2({
|
|
3727
|
+
url: `file:${dbPath}`,
|
|
3728
|
+
encryptionKey: _encryptionKey
|
|
3729
|
+
});
|
|
3730
|
+
try {
|
|
3731
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3732
|
+
const hasMemories = await client.execute(
|
|
3733
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3734
|
+
);
|
|
3735
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3736
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3737
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3738
|
+
}
|
|
3739
|
+
item.ok = true;
|
|
3740
|
+
} catch (err) {
|
|
3741
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3742
|
+
item.error = message;
|
|
3743
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3744
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3745
|
+
client.close();
|
|
3746
|
+
_shards.delete(name);
|
|
3747
|
+
_shardLastAccess.delete(name);
|
|
3748
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3749
|
+
const archivedPath = path9.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3750
|
+
renameSync3(dbPath, archivedPath);
|
|
3751
|
+
item.archivedPath = archivedPath;
|
|
3752
|
+
}
|
|
3753
|
+
} finally {
|
|
3754
|
+
try {
|
|
3755
|
+
client.close();
|
|
3756
|
+
} catch {
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
shards.push(item);
|
|
3760
|
+
}
|
|
3761
|
+
return {
|
|
3762
|
+
total: shards.length,
|
|
3763
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3764
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3765
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3766
|
+
shards
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3704
3769
|
async function ensureShardSchema(client) {
|
|
3705
3770
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3706
3771
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
@@ -4137,6 +4202,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
4137
4202
|
return null;
|
|
4138
4203
|
}
|
|
4139
4204
|
}
|
|
4205
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
4206
|
+
const dir = getKeyDir();
|
|
4207
|
+
await mkdir3(dir, { recursive: true });
|
|
4208
|
+
const keyPath = getKeyPath();
|
|
4209
|
+
const machineKey = deriveMachineKey();
|
|
4210
|
+
if (machineKey) {
|
|
4211
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
4212
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
4213
|
+
await chmod2(keyPath, 384);
|
|
4214
|
+
return "encrypted";
|
|
4215
|
+
}
|
|
4216
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
4217
|
+
await chmod2(keyPath, 384);
|
|
4218
|
+
return "plaintext";
|
|
4219
|
+
}
|
|
4140
4220
|
async function getMasterKey() {
|
|
4141
4221
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
4142
4222
|
if (nativeValue) {
|
|
@@ -4188,6 +4268,20 @@ async function getMasterKey() {
|
|
|
4188
4268
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
4189
4269
|
if (migrated) {
|
|
4190
4270
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
4271
|
+
try {
|
|
4272
|
+
await unlink(keyPath);
|
|
4273
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
4274
|
+
} catch {
|
|
4275
|
+
}
|
|
4276
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
4277
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
4278
|
+
if (fallback === "encrypted") {
|
|
4279
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
4280
|
+
} else {
|
|
4281
|
+
process.stderr.write(
|
|
4282
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4191
4285
|
}
|
|
4192
4286
|
return key;
|
|
4193
4287
|
} catch (err) {
|
|
@@ -4211,18 +4305,10 @@ async function setMasterKey(key) {
|
|
|
4211
4305
|
} catch {
|
|
4212
4306
|
}
|
|
4213
4307
|
}
|
|
4214
|
-
const
|
|
4215
|
-
|
|
4216
|
-
const keyPath = getKeyPath();
|
|
4217
|
-
const machineKey = deriveMachineKey();
|
|
4218
|
-
if (machineKey) {
|
|
4219
|
-
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
4220
|
-
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
4221
|
-
await chmod2(keyPath, 384);
|
|
4308
|
+
const fallback = await writeMachineBoundFileFallback(b64);
|
|
4309
|
+
if (fallback === "encrypted") {
|
|
4222
4310
|
process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
|
|
4223
4311
|
} else {
|
|
4224
|
-
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
4225
|
-
await chmod2(keyPath, 384);
|
|
4226
4312
|
process.stderr.write(
|
|
4227
4313
|
"[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
|
|
4228
4314
|
);
|
|
@@ -20841,15 +20927,31 @@ function auditHookHealth() {
|
|
|
20841
20927
|
const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
|
|
20842
20928
|
return { logExists: true, totalLines, errorsLastHour, topPatterns };
|
|
20843
20929
|
}
|
|
20930
|
+
async function auditShards() {
|
|
20931
|
+
try {
|
|
20932
|
+
const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
20933
|
+
const report = await auditShardHealth2();
|
|
20934
|
+
return {
|
|
20935
|
+
total: report.total,
|
|
20936
|
+
ok: report.ok,
|
|
20937
|
+
unreadable: report.unreadable,
|
|
20938
|
+
archived: report.archived,
|
|
20939
|
+
unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
|
|
20940
|
+
};
|
|
20941
|
+
} catch {
|
|
20942
|
+
return { total: 0, ok: 0, unreadable: 0, archived: 0, unreadableNames: [] };
|
|
20943
|
+
}
|
|
20944
|
+
}
|
|
20844
20945
|
async function runAudit(client, flags) {
|
|
20845
20946
|
const runConflicts = flags.conflicts || process.env.EXE_AUDIT_CONFLICTS === "1";
|
|
20846
|
-
const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects] = await Promise.all([
|
|
20947
|
+
const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, shards] = await Promise.all([
|
|
20847
20948
|
auditStats(client, flags),
|
|
20848
20949
|
auditNullVectors(client, flags),
|
|
20849
20950
|
auditDuplicates(client, flags),
|
|
20850
20951
|
auditBloated(client, flags),
|
|
20851
20952
|
auditFts(client),
|
|
20852
|
-
auditOrphanedProjects(client)
|
|
20953
|
+
auditOrphanedProjects(client),
|
|
20954
|
+
auditShards()
|
|
20853
20955
|
]);
|
|
20854
20956
|
let conflicts;
|
|
20855
20957
|
if (runConflicts) {
|
|
@@ -20869,7 +20971,7 @@ async function runAudit(client, flags) {
|
|
|
20869
20971
|
}
|
|
20870
20972
|
const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
|
|
20871
20973
|
const hookHealth = auditHookHealth();
|
|
20872
|
-
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth };
|
|
20974
|
+
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
|
|
20873
20975
|
}
|
|
20874
20976
|
function indicator(value, warn) {
|
|
20875
20977
|
if (value === 0) return "\u{1F7E2}";
|
|
@@ -20948,6 +21050,15 @@ function formatReport(report, flags) {
|
|
|
20948
21050
|
lines.push(` ${p.count}x: ${p.pattern}`);
|
|
20949
21051
|
}
|
|
20950
21052
|
}
|
|
21053
|
+
const sh = report.shards;
|
|
21054
|
+
if (sh.total > 0) {
|
|
21055
|
+
if (sh.unreadable === 0) {
|
|
21056
|
+
lines.push(`\u{1F7E2} Shards: ${fmtNum(sh.ok)} / ${fmtNum(sh.total)} readable`);
|
|
21057
|
+
} else {
|
|
21058
|
+
const names = sh.unreadableNames.slice(0, 5).join(", ");
|
|
21059
|
+
lines.push(`\u{1F534} Shards: ${fmtNum(sh.unreadable)} unreadable (${names}${sh.unreadableNames.length > 5 ? ", ..." : ""})`);
|
|
21060
|
+
}
|
|
21061
|
+
}
|
|
20951
21062
|
lines.push("");
|
|
20952
21063
|
if (flags.verbose) {
|
|
20953
21064
|
if (report.duplicates.length > 0) {
|
|
@@ -20995,6 +21106,9 @@ function formatReport(report, flags) {
|
|
|
20995
21106
|
if (!report.fts.inSync) {
|
|
20996
21107
|
recs.push("Run --fix to rebuild FTS index");
|
|
20997
21108
|
}
|
|
21109
|
+
if (report.shards.unreadable > 0) {
|
|
21110
|
+
recs.push(`Run --fix to archive ${fmtNum(report.shards.unreadable)} unreadable shard(s); global DB remains authoritative`);
|
|
21111
|
+
}
|
|
20998
21112
|
if (report.orphanedProjects.length > 0) {
|
|
20999
21113
|
const names = report.orphanedProjects.map((o) => `"${o.project_name}"`).join(", ");
|
|
21000
21114
|
recs.push(`Consider /exe-forget for orphaned project${report.orphanedProjects.length > 1 ? "s" : ""} ${names}`);
|
|
@@ -21100,6 +21214,17 @@ async function fixBloated(client, bloated, dryRun) {
|
|
|
21100
21214
|
}
|
|
21101
21215
|
return chunksCreated;
|
|
21102
21216
|
}
|
|
21217
|
+
async function fixShards(dryRun) {
|
|
21218
|
+
const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
21219
|
+
const report = await auditShardHealth2({ repair: true, dryRun });
|
|
21220
|
+
return {
|
|
21221
|
+
total: report.total,
|
|
21222
|
+
ok: report.ok,
|
|
21223
|
+
unreadable: report.unreadable,
|
|
21224
|
+
archived: report.archived,
|
|
21225
|
+
unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
|
|
21226
|
+
};
|
|
21227
|
+
}
|
|
21103
21228
|
function splitAtSentences(text3, maxChunkSize) {
|
|
21104
21229
|
if (text3.length <= maxChunkSize) return [text3];
|
|
21105
21230
|
const chunks = [];
|
|
@@ -21175,6 +21300,11 @@ ${mode} Applying repairs...
|
|
|
21175
21300
|
console.log(" Done.");
|
|
21176
21301
|
}
|
|
21177
21302
|
}
|
|
21303
|
+
if (report.shards.unreadable > 0) {
|
|
21304
|
+
console.log(`${mode} Archiving ${fmtNum(report.shards.unreadable)} unreadable shard(s)...`);
|
|
21305
|
+
const fixed = await fixShards(flags.dryRun);
|
|
21306
|
+
console.log(` ${flags.dryRun ? "Would archive" : "Archived"} ${fmtNum(flags.dryRun ? fixed.unreadable : fixed.archived)} shard(s).`);
|
|
21307
|
+
}
|
|
21178
21308
|
console.log(`
|
|
21179
21309
|
${mode} Complete.`);
|
|
21180
21310
|
}
|
|
@@ -27510,16 +27640,23 @@ var init_support_inbox = __esm({
|
|
|
27510
27640
|
});
|
|
27511
27641
|
|
|
27512
27642
|
// src/mcp/tool-gates.ts
|
|
27643
|
+
function isChiefOfStaffRole(role) {
|
|
27644
|
+
const normalized = (role ?? "").trim().toLowerCase();
|
|
27645
|
+
return normalized === "chief of staff" || normalized === "executive assistant";
|
|
27646
|
+
}
|
|
27513
27647
|
function isToolAllowed(registerFnName) {
|
|
27514
27648
|
const role = process.env.AGENT_ROLE;
|
|
27515
27649
|
if (!role) return true;
|
|
27650
|
+
if (isChiefOfStaffRole(role)) {
|
|
27651
|
+
return CHIEF_OF_STAFF_READ_TOOLS.has(registerFnName);
|
|
27652
|
+
}
|
|
27516
27653
|
const category = TOOL_CATEGORIES[registerFnName];
|
|
27517
27654
|
if (!category) return true;
|
|
27518
27655
|
const allowedRoles = TOOL_GATES[category];
|
|
27519
27656
|
if (!allowedRoles || allowedRoles.length === 0) return true;
|
|
27520
27657
|
return allowedRoles.includes(role);
|
|
27521
27658
|
}
|
|
27522
|
-
var TOOL_GATES, TOOL_CATEGORIES;
|
|
27659
|
+
var TOOL_GATES, TOOL_CATEGORIES, CHIEF_OF_STAFF_READ_TOOLS;
|
|
27523
27660
|
var init_tool_gates = __esm({
|
|
27524
27661
|
"src/mcp/tool-gates.ts"() {
|
|
27525
27662
|
"use strict";
|
|
@@ -27661,6 +27798,20 @@ var init_tool_gates = __esm({
|
|
|
27661
27798
|
registerListGlobalProcedures: "orchestration",
|
|
27662
27799
|
registerDeactivateGlobalProcedure: "orchestration"
|
|
27663
27800
|
};
|
|
27801
|
+
CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
27802
|
+
"registerRecallMyMemory",
|
|
27803
|
+
"registerAskTeamMemory",
|
|
27804
|
+
"registerGetMemoryById",
|
|
27805
|
+
"registerGetSessionContext",
|
|
27806
|
+
"registerSearchEverything",
|
|
27807
|
+
"registerGetDecision",
|
|
27808
|
+
"registerGetIdentity",
|
|
27809
|
+
"registerListTasks",
|
|
27810
|
+
"registerGetTask",
|
|
27811
|
+
"registerListReminders",
|
|
27812
|
+
"registerQueryConversations",
|
|
27813
|
+
"registerQueryCompanyBrain"
|
|
27814
|
+
]);
|
|
27664
27815
|
}
|
|
27665
27816
|
});
|
|
27666
27817
|
|
|
@@ -2885,6 +2885,15 @@ function readMachineId() {
|
|
|
2885
2885
|
return "";
|
|
2886
2886
|
}
|
|
2887
2887
|
}
|
|
2888
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2889
|
+
const crypto3 = __require("crypto");
|
|
2890
|
+
const iv = crypto3.randomBytes(12);
|
|
2891
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2892
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2893
|
+
encrypted += cipher.final("base64");
|
|
2894
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
2895
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
2896
|
+
}
|
|
2888
2897
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2889
2898
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2890
2899
|
try {
|
|
@@ -2903,6 +2912,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
|
|
|
2903
2912
|
return null;
|
|
2904
2913
|
}
|
|
2905
2914
|
}
|
|
2915
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
2916
|
+
const dir = getKeyDir();
|
|
2917
|
+
await mkdir3(dir, { recursive: true });
|
|
2918
|
+
const keyPath = getKeyPath();
|
|
2919
|
+
const machineKey = deriveMachineKey();
|
|
2920
|
+
if (machineKey) {
|
|
2921
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
2922
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
2923
|
+
await chmod2(keyPath, 384);
|
|
2924
|
+
return "encrypted";
|
|
2925
|
+
}
|
|
2926
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
2927
|
+
await chmod2(keyPath, 384);
|
|
2928
|
+
return "plaintext";
|
|
2929
|
+
}
|
|
2906
2930
|
async function getMasterKey() {
|
|
2907
2931
|
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
2908
2932
|
if (nativeValue) {
|
|
@@ -2954,6 +2978,20 @@ async function getMasterKey() {
|
|
|
2954
2978
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
2955
2979
|
if (migrated) {
|
|
2956
2980
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
2981
|
+
try {
|
|
2982
|
+
await unlink(keyPath);
|
|
2983
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
2984
|
+
} catch {
|
|
2985
|
+
}
|
|
2986
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
2987
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
2988
|
+
if (fallback === "encrypted") {
|
|
2989
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
2990
|
+
} else {
|
|
2991
|
+
process.stderr.write(
|
|
2992
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
2993
|
+
);
|
|
2994
|
+
}
|
|
2957
2995
|
}
|
|
2958
2996
|
return key;
|
|
2959
2997
|
} catch (err) {
|
|
@@ -3226,6 +3264,7 @@ var init_memory_write_governor = __esm({
|
|
|
3226
3264
|
// src/lib/shard-manager.ts
|
|
3227
3265
|
var shard_manager_exports = {};
|
|
3228
3266
|
__export(shard_manager_exports, {
|
|
3267
|
+
auditShardHealth: () => auditShardHealth,
|
|
3229
3268
|
disposeShards: () => disposeShards,
|
|
3230
3269
|
ensureShardSchema: () => ensureShardSchema,
|
|
3231
3270
|
getOpenShardCount: () => getOpenShardCount,
|
|
@@ -3292,6 +3331,70 @@ function listShards() {
|
|
|
3292
3331
|
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3293
3332
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3294
3333
|
}
|
|
3334
|
+
async function auditShardHealth(options = {}) {
|
|
3335
|
+
if (!_encryptionKey) {
|
|
3336
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3337
|
+
}
|
|
3338
|
+
const repair = options.repair === true;
|
|
3339
|
+
const dryRun = options.dryRun === true;
|
|
3340
|
+
const names = listShards();
|
|
3341
|
+
const shards = [];
|
|
3342
|
+
for (const name of names) {
|
|
3343
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3344
|
+
const stat = statSync2(dbPath);
|
|
3345
|
+
const item = {
|
|
3346
|
+
name,
|
|
3347
|
+
path: dbPath,
|
|
3348
|
+
ok: false,
|
|
3349
|
+
unreadable: false,
|
|
3350
|
+
error: null,
|
|
3351
|
+
size: stat.size,
|
|
3352
|
+
mtime: stat.mtime.toISOString(),
|
|
3353
|
+
memoryCount: null
|
|
3354
|
+
};
|
|
3355
|
+
const client = createClient2({
|
|
3356
|
+
url: `file:${dbPath}`,
|
|
3357
|
+
encryptionKey: _encryptionKey
|
|
3358
|
+
});
|
|
3359
|
+
try {
|
|
3360
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3361
|
+
const hasMemories = await client.execute(
|
|
3362
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3363
|
+
);
|
|
3364
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3365
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3366
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3367
|
+
}
|
|
3368
|
+
item.ok = true;
|
|
3369
|
+
} catch (err) {
|
|
3370
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3371
|
+
item.error = message;
|
|
3372
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3373
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3374
|
+
client.close();
|
|
3375
|
+
_shards.delete(name);
|
|
3376
|
+
_shardLastAccess.delete(name);
|
|
3377
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3378
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3379
|
+
renameSync3(dbPath, archivedPath);
|
|
3380
|
+
item.archivedPath = archivedPath;
|
|
3381
|
+
}
|
|
3382
|
+
} finally {
|
|
3383
|
+
try {
|
|
3384
|
+
client.close();
|
|
3385
|
+
} catch {
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
shards.push(item);
|
|
3389
|
+
}
|
|
3390
|
+
return {
|
|
3391
|
+
total: shards.length,
|
|
3392
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3393
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3394
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3395
|
+
shards
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3295
3398
|
async function ensureShardSchema(client) {
|
|
3296
3399
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
3297
3400
|
await client.execute("PRAGMA busy_timeout = 30000");
|