@askexenow/exe-os 0.9.35 → 0.9.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 +2 -1
- package/dist/bin/backfill-responses.js +2 -1
- package/dist/bin/backfill-vectors.js +1 -1
- package/dist/bin/cleanup-stale-review-tasks.js +7 -2
- package/dist/bin/cli.js +45 -14
- package/dist/bin/exe-agent.js +1 -1
- package/dist/bin/exe-assign.js +7 -2
- package/dist/bin/exe-boot.js +1 -1
- package/dist/bin/exe-call.js +7 -5
- package/dist/bin/exe-dispatch.js +7 -2
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-export-behaviors.js +84 -4
- package/dist/bin/exe-forget.js +28 -11
- package/dist/bin/exe-gateway.js +7 -2
- package/dist/bin/exe-heartbeat.js +7 -2
- package/dist/bin/exe-kill.js +7 -2
- package/dist/bin/exe-launch-agent.js +85 -7
- package/dist/bin/exe-new-employee.js +26 -2
- package/dist/bin/exe-pending-messages.js +7 -2
- package/dist/bin/exe-pending-notifications.js +7 -2
- package/dist/bin/exe-pending-reviews.js +7 -2
- package/dist/bin/exe-rename.js +1 -1
- package/dist/bin/exe-review.js +7 -2
- package/dist/bin/exe-search.js +29 -11
- package/dist/bin/exe-session-cleanup.js +7 -2
- package/dist/bin/exe-start-codex.js +69 -3
- package/dist/bin/exe-start-opencode.js +80 -3
- package/dist/bin/exe-status.js +7 -2
- package/dist/bin/exe-team.js +7 -2
- package/dist/bin/git-sweep.js +7 -2
- package/dist/bin/graph-backfill.js +2 -1
- package/dist/bin/graph-export.js +7 -2
- package/dist/bin/install.js +25 -1
- package/dist/bin/intercom-check.js +7 -2
- package/dist/bin/scan-tasks.js +7 -2
- package/dist/bin/setup.js +7 -5
- package/dist/bin/shard-migrate.js +2 -1
- package/dist/gateway/index.js +7 -2
- package/dist/hooks/bug-report-worker.js +7 -2
- package/dist/hooks/codex-stop-task-finalizer.js +7 -2
- package/dist/hooks/commit-complete.js +7 -2
- package/dist/hooks/error-recall.js +29 -11
- package/dist/hooks/ingest.js +7 -2
- package/dist/hooks/instructions-loaded.js +7 -2
- package/dist/hooks/notification.js +7 -2
- package/dist/hooks/post-compact.js +7 -2
- package/dist/hooks/post-tool-combined.js +29 -11
- package/dist/hooks/pre-compact.js +7 -2
- package/dist/hooks/pre-tool-use.js +17 -8
- package/dist/hooks/prompt-submit.js +124 -12
- package/dist/hooks/session-end.js +7 -2
- package/dist/hooks/session-start.js +207 -38
- package/dist/hooks/stop.js +7 -2
- package/dist/hooks/subagent-stop.js +7 -2
- package/dist/hooks/summary-worker.js +1 -1
- package/dist/index.js +7 -2
- package/dist/lib/employee-templates.js +7 -5
- package/dist/lib/exe-daemon.js +115 -26
- package/dist/lib/hybrid-search.js +29 -11
- package/dist/lib/schedules.js +1 -1
- package/dist/lib/store.js +7 -2
- package/dist/mcp/server.js +109 -26
- package/dist/runtime/index.js +7 -2
- package/dist/tui/App.js +7 -2
- package/package.json +1 -1
- package/src/commands/exe/save.md +48 -0
|
@@ -3529,6 +3529,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
3529
3529
|
}
|
|
3530
3530
|
}
|
|
3531
3531
|
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3532
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3532
3533
|
if (memoryIds.length === 0) return;
|
|
3533
3534
|
const run = () => {
|
|
3534
3535
|
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
@@ -3923,7 +3924,7 @@ var init_platform_procedures = __esm({
|
|
|
3923
3924
|
title: "Chain of command \u2014 who talks to whom",
|
|
3924
3925
|
domain: "workflow",
|
|
3925
3926
|
priority: "p0",
|
|
3926
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3927
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3927
3928
|
},
|
|
3928
3929
|
{
|
|
3929
3930
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4595,7 +4596,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4595
4596
|
sql += ` AND timestamp >= ?`;
|
|
4596
4597
|
args.push(options.since);
|
|
4597
4598
|
}
|
|
4598
|
-
if (options?.
|
|
4599
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4600
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4601
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4602
|
+
args.push(...uniqueTypes);
|
|
4603
|
+
} else if (options?.memoryType) {
|
|
4599
4604
|
sql += ` AND memory_type = ?`;
|
|
4600
4605
|
args.push(options.memoryType);
|
|
4601
4606
|
}
|
|
@@ -7723,6 +7728,13 @@ var init_tasks_notify = __esm({
|
|
|
7723
7728
|
});
|
|
7724
7729
|
|
|
7725
7730
|
// src/lib/behaviors.ts
|
|
7731
|
+
var behaviors_exports = {};
|
|
7732
|
+
__export(behaviors_exports, {
|
|
7733
|
+
deactivateBehavior: () => deactivateBehavior,
|
|
7734
|
+
listBehaviors: () => listBehaviors,
|
|
7735
|
+
listBehaviorsByDomain: () => listBehaviorsByDomain,
|
|
7736
|
+
storeBehavior: () => storeBehavior
|
|
7737
|
+
});
|
|
7726
7738
|
import crypto6 from "crypto";
|
|
7727
7739
|
async function storeBehavior(opts) {
|
|
7728
7740
|
const client = getClient();
|
|
@@ -7742,6 +7754,63 @@ async function storeBehavior(opts) {
|
|
|
7742
7754
|
});
|
|
7743
7755
|
return id;
|
|
7744
7756
|
}
|
|
7757
|
+
async function listBehaviors(agentId, projectName, limit = 30) {
|
|
7758
|
+
const client = getClient();
|
|
7759
|
+
const result = await client.execute({
|
|
7760
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
7761
|
+
FROM behaviors
|
|
7762
|
+
WHERE agent_id = ? AND active = 1
|
|
7763
|
+
AND (project_name IS NULL OR project_name = ?)
|
|
7764
|
+
ORDER BY
|
|
7765
|
+
CASE WHEN priority = 'p0' THEN 0
|
|
7766
|
+
WHEN priority = 'p1' THEN 1
|
|
7767
|
+
ELSE 2 END,
|
|
7768
|
+
updated_at DESC
|
|
7769
|
+
LIMIT ?`,
|
|
7770
|
+
args: [agentId, projectName ?? "", limit]
|
|
7771
|
+
});
|
|
7772
|
+
return result.rows.map((r) => ({
|
|
7773
|
+
id: String(r.id),
|
|
7774
|
+
agent_id: String(r.agent_id),
|
|
7775
|
+
project_name: r.project_name ? String(r.project_name) : null,
|
|
7776
|
+
domain: r.domain ? String(r.domain) : null,
|
|
7777
|
+
priority: String(r.priority || "p1"),
|
|
7778
|
+
content: String(r.content),
|
|
7779
|
+
active: Number(r.active),
|
|
7780
|
+
created_at: String(r.created_at),
|
|
7781
|
+
updated_at: String(r.updated_at),
|
|
7782
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
|
|
7783
|
+
}));
|
|
7784
|
+
}
|
|
7785
|
+
async function listBehaviorsByDomain(agentId, domain) {
|
|
7786
|
+
const client = getClient();
|
|
7787
|
+
const result = await client.execute({
|
|
7788
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
7789
|
+
FROM behaviors
|
|
7790
|
+
WHERE agent_id = ? AND domain = ? AND active = 1`,
|
|
7791
|
+
args: [agentId, domain]
|
|
7792
|
+
});
|
|
7793
|
+
return result.rows.map((r) => ({
|
|
7794
|
+
id: String(r.id),
|
|
7795
|
+
agent_id: String(r.agent_id),
|
|
7796
|
+
project_name: r.project_name ? String(r.project_name) : null,
|
|
7797
|
+
domain: r.domain ? String(r.domain) : null,
|
|
7798
|
+
priority: String(r.priority || "p1"),
|
|
7799
|
+
content: String(r.content),
|
|
7800
|
+
active: Number(r.active),
|
|
7801
|
+
created_at: String(r.created_at),
|
|
7802
|
+
updated_at: String(r.updated_at),
|
|
7803
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
|
|
7804
|
+
}));
|
|
7805
|
+
}
|
|
7806
|
+
async function deactivateBehavior(id) {
|
|
7807
|
+
const client = getClient();
|
|
7808
|
+
const result = await client.execute({
|
|
7809
|
+
sql: `UPDATE behaviors SET active = 0, updated_at = ? WHERE id = ? AND active = 1`,
|
|
7810
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), id]
|
|
7811
|
+
});
|
|
7812
|
+
return (result.rowsAffected ?? 0) > 0;
|
|
7813
|
+
}
|
|
7745
7814
|
var init_behaviors = __esm({
|
|
7746
7815
|
"src/lib/behaviors.ts"() {
|
|
7747
7816
|
"use strict";
|
|
@@ -9697,6 +9766,17 @@ import path25 from "path";
|
|
|
9697
9766
|
init_store();
|
|
9698
9767
|
init_database();
|
|
9699
9768
|
var RRF_K = 60;
|
|
9769
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
9770
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
9771
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
9772
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
9773
|
+
args.push(...uniqueTypes);
|
|
9774
|
+
} else if (options?.memoryType) {
|
|
9775
|
+
sql += ` AND ${column} = ?`;
|
|
9776
|
+
args.push(options.memoryType);
|
|
9777
|
+
}
|
|
9778
|
+
return sql;
|
|
9779
|
+
}
|
|
9700
9780
|
async function hybridSearch(queryText, agentId, options) {
|
|
9701
9781
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
9702
9782
|
const config = await loadConfig2();
|
|
@@ -9925,6 +10005,7 @@ async function estimateCardinality(agentId, options) {
|
|
|
9925
10005
|
sql += ` AND timestamp >= ?`;
|
|
9926
10006
|
args.push(options.since);
|
|
9927
10007
|
}
|
|
10008
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
9928
10009
|
try {
|
|
9929
10010
|
const result = await client.execute({ sql, args });
|
|
9930
10011
|
return Number(result.rows[0]?.cnt) || 0;
|
|
@@ -10043,10 +10124,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
10043
10124
|
sql += ` AND m.timestamp >= ?`;
|
|
10044
10125
|
args.push(options.since);
|
|
10045
10126
|
}
|
|
10046
|
-
|
|
10047
|
-
sql += ` AND m.memory_type = ?`;
|
|
10048
|
-
args.push(options.memoryType);
|
|
10049
|
-
}
|
|
10127
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
10050
10128
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
10051
10129
|
args.push(limit);
|
|
10052
10130
|
const result = await client.execute({ sql, args });
|
|
@@ -10103,9 +10181,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
10103
10181
|
AND timestamp >= ? AND timestamp <= ?
|
|
10104
10182
|
AND COALESCE(status, 'active') = 'active'
|
|
10105
10183
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
10184
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
10106
10185
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
10107
10186
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
10108
|
-
args: [
|
|
10187
|
+
args: [
|
|
10188
|
+
agentId,
|
|
10189
|
+
windowStart,
|
|
10190
|
+
killedAt,
|
|
10191
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
10192
|
+
boundarySlots
|
|
10193
|
+
]
|
|
10109
10194
|
});
|
|
10110
10195
|
for (const row of boundaryResult.rows) {
|
|
10111
10196
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -10151,10 +10236,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
10151
10236
|
sql += ` AND timestamp >= ?`;
|
|
10152
10237
|
args.push(options.since);
|
|
10153
10238
|
}
|
|
10154
|
-
|
|
10155
|
-
sql += ` AND memory_type = ?`;
|
|
10156
|
-
args.push(options.memoryType);
|
|
10157
|
-
}
|
|
10239
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
10158
10240
|
if (textFilter) {
|
|
10159
10241
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
10160
10242
|
args.push(textFilter);
|
|
@@ -10600,6 +10682,36 @@ ${fresh.map(
|
|
|
10600
10682
|
}
|
|
10601
10683
|
}
|
|
10602
10684
|
}
|
|
10685
|
+
let behaviorContext = "";
|
|
10686
|
+
if (!IS_CODEX_RUNTIME && agent.agentId !== "default") {
|
|
10687
|
+
try {
|
|
10688
|
+
const counterPath = path25.join(CACHE_DIR3, `prompt-count-${getSessionKey()}`);
|
|
10689
|
+
let count = 1;
|
|
10690
|
+
try {
|
|
10691
|
+
count = parseInt(readFileSync17(counterPath, "utf8").trim(), 10) + 1;
|
|
10692
|
+
} catch {
|
|
10693
|
+
}
|
|
10694
|
+
writeFileSync11(counterPath, String(count), "utf8");
|
|
10695
|
+
const BEHAVIOR_REINJECT_INTERVAL = 50;
|
|
10696
|
+
if (count === 1 || count % BEHAVIOR_REINJECT_INTERVAL === 0) {
|
|
10697
|
+
const { listBehaviors: listBehaviors2 } = await Promise.resolve().then(() => (init_behaviors(), behaviors_exports));
|
|
10698
|
+
const behaviors = await listBehaviors2(agent.agentId);
|
|
10699
|
+
const active = behaviors.filter((b) => b.active !== 0).sort((a, b) => (a.priority === "p0" ? -1 : 1) - (b.priority === "p0" ? -1 : 1)).slice(0, 15);
|
|
10700
|
+
if (active.length > 0) {
|
|
10701
|
+
const lines = active.map(
|
|
10702
|
+
(b) => `- [${b.domain ?? "workflow"}] ${b.content.slice(0, 200)}`
|
|
10703
|
+
);
|
|
10704
|
+
behaviorContext = `
|
|
10705
|
+
## Your Behavioral Memory
|
|
10706
|
+
Validated patterns and corrections for this session. Follow them.
|
|
10707
|
+
Sourced from exe-os Layer 2 (Expertise). Changes apply on next spawn.
|
|
10708
|
+
|
|
10709
|
+
${lines.join("\n")}`;
|
|
10710
|
+
}
|
|
10711
|
+
}
|
|
10712
|
+
} catch {
|
|
10713
|
+
}
|
|
10714
|
+
}
|
|
10603
10715
|
let reviewContext = "";
|
|
10604
10716
|
if (canCoordinate(agent.agentId, agent.agentRole)) {
|
|
10605
10717
|
try {
|
|
@@ -10655,7 +10767,7 @@ IMPORTANT: After completing your current task, you MUST address the pending revi
|
|
|
10655
10767
|
} catch {
|
|
10656
10768
|
}
|
|
10657
10769
|
}
|
|
10658
|
-
const combined = [cacheContext, memoryContext, reviewContext].filter(Boolean).join("\n\n");
|
|
10770
|
+
const combined = [cacheContext, memoryContext, behaviorContext, reviewContext].filter(Boolean).join("\n\n");
|
|
10659
10771
|
if (combined.length > 0) {
|
|
10660
10772
|
const output = JSON.stringify({
|
|
10661
10773
|
hookSpecificOutput: {
|
|
@@ -7088,6 +7088,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
7088
7088
|
}
|
|
7089
7089
|
}
|
|
7090
7090
|
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
7091
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
7091
7092
|
if (memoryIds.length === 0) return;
|
|
7092
7093
|
const run = () => {
|
|
7093
7094
|
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
@@ -7482,7 +7483,7 @@ var init_platform_procedures = __esm({
|
|
|
7482
7483
|
title: "Chain of command \u2014 who talks to whom",
|
|
7483
7484
|
domain: "workflow",
|
|
7484
7485
|
priority: "p0",
|
|
7485
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
7486
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
7486
7487
|
},
|
|
7487
7488
|
{
|
|
7488
7489
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -8154,7 +8155,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
8154
8155
|
sql += ` AND timestamp >= ?`;
|
|
8155
8156
|
args.push(options.since);
|
|
8156
8157
|
}
|
|
8157
|
-
if (options?.
|
|
8158
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
8159
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
8160
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
8161
|
+
args.push(...uniqueTypes);
|
|
8162
|
+
} else if (options?.memoryType) {
|
|
8158
8163
|
sql += ` AND memory_type = ?`;
|
|
8159
8164
|
args.push(options.memoryType);
|
|
8160
8165
|
}
|
|
@@ -3290,8 +3290,8 @@ function deriveMachineKey() {
|
|
|
3290
3290
|
}
|
|
3291
3291
|
function readMachineId() {
|
|
3292
3292
|
try {
|
|
3293
|
-
const { readFileSync:
|
|
3294
|
-
return
|
|
3293
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
3294
|
+
return readFileSync14("/etc/machine-id", "utf-8").trim();
|
|
3295
3295
|
} catch {
|
|
3296
3296
|
return "";
|
|
3297
3297
|
}
|
|
@@ -3545,10 +3545,10 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
3545
3545
|
const row = current.rows[0];
|
|
3546
3546
|
if (!row) return;
|
|
3547
3547
|
const memoryType = String(row.memory_type ?? "raw");
|
|
3548
|
-
const
|
|
3548
|
+
const contentHash2 = row.content_hash ? String(row.content_hash) : null;
|
|
3549
3549
|
const agentId = String(row.agent_id);
|
|
3550
3550
|
const projectName = String(row.project_name);
|
|
3551
|
-
if (
|
|
3551
|
+
if (contentHash2) {
|
|
3552
3552
|
await client.execute({
|
|
3553
3553
|
sql: `UPDATE memories
|
|
3554
3554
|
SET status = 'deleted',
|
|
@@ -3559,7 +3559,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
3559
3559
|
AND project_name = ?
|
|
3560
3560
|
AND COALESCE(memory_type, 'raw') = ?
|
|
3561
3561
|
AND COALESCE(status, 'active') = 'active'`,
|
|
3562
|
-
args: [memoryId,
|
|
3562
|
+
args: [memoryId, contentHash2, agentId, projectName, memoryType]
|
|
3563
3563
|
});
|
|
3564
3564
|
}
|
|
3565
3565
|
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
@@ -3595,6 +3595,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
3595
3595
|
}
|
|
3596
3596
|
}
|
|
3597
3597
|
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3598
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3598
3599
|
if (memoryIds.length === 0) return;
|
|
3599
3600
|
const run = () => {
|
|
3600
3601
|
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
@@ -3989,7 +3990,7 @@ var init_platform_procedures = __esm({
|
|
|
3989
3990
|
title: "Chain of command \u2014 who talks to whom",
|
|
3990
3991
|
domain: "workflow",
|
|
3991
3992
|
priority: "p0",
|
|
3992
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3993
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3993
3994
|
},
|
|
3994
3995
|
{
|
|
3995
3996
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4336,16 +4337,16 @@ async function writeMemory(record) {
|
|
|
4336
4337
|
const governed = governMemoryRecord(record);
|
|
4337
4338
|
if (governed.shouldDrop) return;
|
|
4338
4339
|
record = governed.record;
|
|
4339
|
-
const
|
|
4340
|
+
const contentHash2 = governed.contentHash;
|
|
4340
4341
|
const memoryType = record.memory_type ?? "raw";
|
|
4341
4342
|
if (_pendingRecords.some(
|
|
4342
|
-
(r) => r.content_hash ===
|
|
4343
|
+
(r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4343
4344
|
)) {
|
|
4344
4345
|
return;
|
|
4345
4346
|
}
|
|
4346
4347
|
try {
|
|
4347
4348
|
const existing = await findScopedDuplicate({
|
|
4348
|
-
contentHash,
|
|
4349
|
+
contentHash: contentHash2,
|
|
4349
4350
|
agentId: record.agent_id,
|
|
4350
4351
|
projectName: record.project_name,
|
|
4351
4352
|
memoryType
|
|
@@ -4383,7 +4384,7 @@ async function writeMemory(record) {
|
|
|
4383
4384
|
draft: record.draft ? 1 : 0,
|
|
4384
4385
|
memory_type: memoryType,
|
|
4385
4386
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4386
|
-
content_hash:
|
|
4387
|
+
content_hash: contentHash2,
|
|
4387
4388
|
intent: record.intent ?? null,
|
|
4388
4389
|
outcome: record.outcome ?? null,
|
|
4389
4390
|
domain: record.domain ?? inferDomain(record),
|
|
@@ -4456,7 +4457,7 @@ async function flushBatch() {
|
|
|
4456
4457
|
const draft = row.draft ? 1 : 0;
|
|
4457
4458
|
const memoryType = row.memory_type ?? "raw";
|
|
4458
4459
|
const trajectory = row.trajectory ?? null;
|
|
4459
|
-
const
|
|
4460
|
+
const contentHash2 = row.content_hash ?? null;
|
|
4460
4461
|
const intent = row.intent ?? null;
|
|
4461
4462
|
const outcome = row.outcome ?? null;
|
|
4462
4463
|
const domain = row.domain ?? null;
|
|
@@ -4528,7 +4529,7 @@ async function flushBatch() {
|
|
|
4528
4529
|
draft,
|
|
4529
4530
|
memoryType,
|
|
4530
4531
|
trajectory,
|
|
4531
|
-
|
|
4532
|
+
contentHash2
|
|
4532
4533
|
];
|
|
4533
4534
|
return {
|
|
4534
4535
|
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
@@ -4661,7 +4662,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4661
4662
|
sql += ` AND timestamp >= ?`;
|
|
4662
4663
|
args.push(options.since);
|
|
4663
4664
|
}
|
|
4664
|
-
if (options?.
|
|
4665
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4666
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4667
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4668
|
+
args.push(...uniqueTypes);
|
|
4669
|
+
} else if (options?.memoryType) {
|
|
4665
4670
|
sql += ` AND memory_type = ?`;
|
|
4666
4671
|
args.push(options.memoryType);
|
|
4667
4672
|
}
|
|
@@ -5066,10 +5071,10 @@ async function disposeEmbedder() {
|
|
|
5066
5071
|
async function embedDirect(text) {
|
|
5067
5072
|
const llamaCpp = await import("node-llama-cpp");
|
|
5068
5073
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5069
|
-
const { existsSync:
|
|
5070
|
-
const
|
|
5071
|
-
const modelPath =
|
|
5072
|
-
if (!
|
|
5074
|
+
const { existsSync: existsSync17 } = await import("fs");
|
|
5075
|
+
const path21 = await import("path");
|
|
5076
|
+
const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
5077
|
+
if (!existsSync17(modelPath)) {
|
|
5073
5078
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
5074
5079
|
}
|
|
5075
5080
|
const llama = await llamaCpp.getLlama();
|
|
@@ -5959,6 +5964,17 @@ __export(hybrid_search_exports, {
|
|
|
5959
5964
|
rrfMerge: () => rrfMerge,
|
|
5960
5965
|
rrfMergeMulti: () => rrfMergeMulti
|
|
5961
5966
|
});
|
|
5967
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
5968
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5969
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5970
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5971
|
+
args.push(...uniqueTypes);
|
|
5972
|
+
} else if (options?.memoryType) {
|
|
5973
|
+
sql += ` AND ${column} = ?`;
|
|
5974
|
+
args.push(options.memoryType);
|
|
5975
|
+
}
|
|
5976
|
+
return sql;
|
|
5977
|
+
}
|
|
5962
5978
|
async function hybridSearch(queryText, agentId, options) {
|
|
5963
5979
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5964
5980
|
const config = await loadConfig2();
|
|
@@ -6137,10 +6153,10 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
6137
6153
|
};
|
|
6138
6154
|
try {
|
|
6139
6155
|
const fs = await import("fs");
|
|
6140
|
-
const
|
|
6156
|
+
const path21 = await import("path");
|
|
6141
6157
|
const os10 = await import("os");
|
|
6142
|
-
const logPath =
|
|
6143
|
-
fs.mkdirSync(
|
|
6158
|
+
const logPath = path21.join(os10.homedir(), ".exe-os", "search-quality.jsonl");
|
|
6159
|
+
fs.mkdirSync(path21.dirname(logPath), { recursive: true });
|
|
6144
6160
|
fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
|
|
6145
6161
|
} catch {
|
|
6146
6162
|
}
|
|
@@ -6187,6 +6203,7 @@ async function estimateCardinality(agentId, options) {
|
|
|
6187
6203
|
sql += ` AND timestamp >= ?`;
|
|
6188
6204
|
args.push(options.since);
|
|
6189
6205
|
}
|
|
6206
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
6190
6207
|
try {
|
|
6191
6208
|
const result = await client.execute({ sql, args });
|
|
6192
6209
|
return Number(result.rows[0]?.cnt) || 0;
|
|
@@ -6308,10 +6325,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
6308
6325
|
sql += ` AND m.timestamp >= ?`;
|
|
6309
6326
|
args.push(options.since);
|
|
6310
6327
|
}
|
|
6311
|
-
|
|
6312
|
-
sql += ` AND m.memory_type = ?`;
|
|
6313
|
-
args.push(options.memoryType);
|
|
6314
|
-
}
|
|
6328
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
6315
6329
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
6316
6330
|
args.push(limit);
|
|
6317
6331
|
const result = await client.execute({ sql, args });
|
|
@@ -6368,9 +6382,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
6368
6382
|
AND timestamp >= ? AND timestamp <= ?
|
|
6369
6383
|
AND COALESCE(status, 'active') = 'active'
|
|
6370
6384
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
6385
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
6371
6386
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
6372
6387
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
6373
|
-
args: [
|
|
6388
|
+
args: [
|
|
6389
|
+
agentId,
|
|
6390
|
+
windowStart,
|
|
6391
|
+
killedAt,
|
|
6392
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
6393
|
+
boundarySlots
|
|
6394
|
+
]
|
|
6374
6395
|
});
|
|
6375
6396
|
for (const row of boundaryResult.rows) {
|
|
6376
6397
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -6416,10 +6437,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
6416
6437
|
sql += ` AND timestamp >= ?`;
|
|
6417
6438
|
args.push(options.since);
|
|
6418
6439
|
}
|
|
6419
|
-
|
|
6420
|
-
sql += ` AND memory_type = ?`;
|
|
6421
|
-
args.push(options.memoryType);
|
|
6422
|
-
}
|
|
6440
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
6423
6441
|
if (textFilter) {
|
|
6424
6442
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
6425
6443
|
args.push(textFilter);
|
|
@@ -7443,10 +7461,145 @@ var init_git_staleness = __esm({
|
|
|
7443
7461
|
}
|
|
7444
7462
|
});
|
|
7445
7463
|
|
|
7464
|
+
// src/lib/identity.ts
|
|
7465
|
+
var identity_exports = {};
|
|
7466
|
+
__export(identity_exports, {
|
|
7467
|
+
getIdentity: () => getIdentity,
|
|
7468
|
+
getIdentityInjection: () => getIdentityInjection,
|
|
7469
|
+
identityPath: () => identityPath,
|
|
7470
|
+
listIdentities: () => listIdentities,
|
|
7471
|
+
updateIdentity: () => updateIdentity
|
|
7472
|
+
});
|
|
7473
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
7474
|
+
import { readdirSync as readdirSync5 } from "fs";
|
|
7475
|
+
import path19 from "path";
|
|
7476
|
+
import { createHash as createHash2 } from "crypto";
|
|
7477
|
+
function ensureDir() {
|
|
7478
|
+
if (!existsSync15(IDENTITY_DIR2)) {
|
|
7479
|
+
mkdirSync7(IDENTITY_DIR2, { recursive: true });
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
function identityPath(agentId) {
|
|
7483
|
+
return path19.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
7484
|
+
}
|
|
7485
|
+
function parseFrontmatter(raw) {
|
|
7486
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
7487
|
+
if (!match) {
|
|
7488
|
+
return {
|
|
7489
|
+
frontmatter: {
|
|
7490
|
+
role: "unknown",
|
|
7491
|
+
title: "Unknown",
|
|
7492
|
+
agent_id: "unknown",
|
|
7493
|
+
org_level: "specialist",
|
|
7494
|
+
created_by: "system",
|
|
7495
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7496
|
+
},
|
|
7497
|
+
body: raw
|
|
7498
|
+
};
|
|
7499
|
+
}
|
|
7500
|
+
const yamlStr = match[1];
|
|
7501
|
+
const body = match[2].trim();
|
|
7502
|
+
const fm = {};
|
|
7503
|
+
for (const line of yamlStr.split("\n")) {
|
|
7504
|
+
const kv = line.match(/^(\w+):\s*(.+)$/);
|
|
7505
|
+
if (kv) fm[kv[1]] = kv[2].trim();
|
|
7506
|
+
}
|
|
7507
|
+
return {
|
|
7508
|
+
frontmatter: {
|
|
7509
|
+
role: fm.role ?? "unknown",
|
|
7510
|
+
title: fm.title ?? "Unknown",
|
|
7511
|
+
agent_id: fm.agent_id ?? "unknown",
|
|
7512
|
+
org_level: fm.org_level ?? "specialist",
|
|
7513
|
+
created_by: fm.created_by ?? "system",
|
|
7514
|
+
updated_at: fm.updated_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
7515
|
+
},
|
|
7516
|
+
body
|
|
7517
|
+
};
|
|
7518
|
+
}
|
|
7519
|
+
function contentHash(content) {
|
|
7520
|
+
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
7521
|
+
}
|
|
7522
|
+
function getIdentity(agentId) {
|
|
7523
|
+
const filePath = identityPath(agentId);
|
|
7524
|
+
if (!existsSync15(filePath)) return null;
|
|
7525
|
+
const raw = readFileSync12(filePath, "utf-8");
|
|
7526
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
7527
|
+
return {
|
|
7528
|
+
agentId,
|
|
7529
|
+
frontmatter,
|
|
7530
|
+
body,
|
|
7531
|
+
raw,
|
|
7532
|
+
contentHash: contentHash(raw)
|
|
7533
|
+
};
|
|
7534
|
+
}
|
|
7535
|
+
async function updateIdentity(agentId, content, updatedBy) {
|
|
7536
|
+
ensureDir();
|
|
7537
|
+
const filePath = identityPath(agentId);
|
|
7538
|
+
const hash = contentHash(content);
|
|
7539
|
+
writeFileSync8(filePath, content, "utf-8");
|
|
7540
|
+
try {
|
|
7541
|
+
const client = getClient();
|
|
7542
|
+
await client.execute({
|
|
7543
|
+
sql: `INSERT INTO identity (agent_id, content_hash, updated_at, updated_by)
|
|
7544
|
+
VALUES (?, ?, ?, ?)
|
|
7545
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
7546
|
+
content_hash = excluded.content_hash,
|
|
7547
|
+
updated_at = excluded.updated_at,
|
|
7548
|
+
updated_by = excluded.updated_by`,
|
|
7549
|
+
args: [agentId, hash, (/* @__PURE__ */ new Date()).toISOString(), updatedBy]
|
|
7550
|
+
});
|
|
7551
|
+
} catch {
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
function listIdentities() {
|
|
7555
|
+
ensureDir();
|
|
7556
|
+
const files = readdirSync5(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
7557
|
+
const results = [];
|
|
7558
|
+
for (const file of files) {
|
|
7559
|
+
const agentId = file.replace(".md", "");
|
|
7560
|
+
const identity = getIdentity(agentId);
|
|
7561
|
+
if (!identity) continue;
|
|
7562
|
+
const lines = identity.body.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
7563
|
+
const summary = lines[0]?.trim().slice(0, 120) ?? identity.frontmatter.title;
|
|
7564
|
+
results.push({
|
|
7565
|
+
agentId,
|
|
7566
|
+
title: `${identity.frontmatter.title} (${identity.frontmatter.role.toUpperCase()})`,
|
|
7567
|
+
summary
|
|
7568
|
+
});
|
|
7569
|
+
}
|
|
7570
|
+
return results;
|
|
7571
|
+
}
|
|
7572
|
+
function getIdentityInjection(agentId) {
|
|
7573
|
+
const own = getIdentity(agentId);
|
|
7574
|
+
const all = listIdentities();
|
|
7575
|
+
const parts = [];
|
|
7576
|
+
if (own) {
|
|
7577
|
+
parts.push(`## Your Identity (exe.md)
|
|
7578
|
+
These define WHO YOU ARE. Non-negotiable. Permanent.
|
|
7579
|
+
|
|
7580
|
+
${own.body}`);
|
|
7581
|
+
}
|
|
7582
|
+
const teamLines = all.filter((a) => a.agentId !== agentId).map((a) => `- ${a.agentId} (${a.title}): ${a.summary}`);
|
|
7583
|
+
if (teamLines.length > 0) {
|
|
7584
|
+
parts.push(`## Team Identities
|
|
7585
|
+
${teamLines.join("\n")}`);
|
|
7586
|
+
}
|
|
7587
|
+
return parts.join("\n\n");
|
|
7588
|
+
}
|
|
7589
|
+
var IDENTITY_DIR2;
|
|
7590
|
+
var init_identity = __esm({
|
|
7591
|
+
"src/lib/identity.ts"() {
|
|
7592
|
+
"use strict";
|
|
7593
|
+
init_config();
|
|
7594
|
+
init_database();
|
|
7595
|
+
IDENTITY_DIR2 = path19.join(EXE_AI_DIR, "identity");
|
|
7596
|
+
}
|
|
7597
|
+
});
|
|
7598
|
+
|
|
7446
7599
|
// src/adapters/claude/hooks/session-start.ts
|
|
7447
7600
|
init_config();
|
|
7448
|
-
import
|
|
7449
|
-
import { unlinkSync as unlinkSync4, existsSync as
|
|
7601
|
+
import path20 from "path";
|
|
7602
|
+
import { unlinkSync as unlinkSync4, existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
7450
7603
|
if (!process.env.AGENT_ID) {
|
|
7451
7604
|
process.env.AGENT_ID = "default";
|
|
7452
7605
|
process.env.AGENT_ROLE = "employee";
|
|
@@ -7479,8 +7632,8 @@ process.stdin.on("end", async () => {
|
|
|
7479
7632
|
const sessionScope = getCurrentSessionScope2();
|
|
7480
7633
|
if (source === "startup") {
|
|
7481
7634
|
try {
|
|
7482
|
-
const undefinedPath =
|
|
7483
|
-
process.env.EXE_OS_DIR ??
|
|
7635
|
+
const undefinedPath = path20.join(
|
|
7636
|
+
process.env.EXE_OS_DIR ?? path20.join(process.env.HOME ?? "", ".exe-os"),
|
|
7484
7637
|
"session-cache",
|
|
7485
7638
|
"active-agent-undefined.json"
|
|
7486
7639
|
);
|
|
@@ -7494,13 +7647,13 @@ process.stdin.on("end", async () => {
|
|
|
7494
7647
|
const agentId = agent.agentId;
|
|
7495
7648
|
if (agentId !== "default") {
|
|
7496
7649
|
try {
|
|
7497
|
-
const cacheDir =
|
|
7498
|
-
process.env.EXE_OS_DIR ??
|
|
7650
|
+
const cacheDir = path20.join(
|
|
7651
|
+
process.env.EXE_OS_DIR ?? path20.join(process.env.HOME ?? "", ".exe-os"),
|
|
7499
7652
|
"session-cache"
|
|
7500
7653
|
);
|
|
7501
|
-
const markerPath =
|
|
7502
|
-
if (
|
|
7503
|
-
const marker = JSON.parse(
|
|
7654
|
+
const markerPath = path20.join(cacheDir, `current-task-${agentId}.json`);
|
|
7655
|
+
if (existsSync16(markerPath)) {
|
|
7656
|
+
const marker = JSON.parse(readFileSync13(markerPath, "utf-8"));
|
|
7504
7657
|
if (marker.taskId) {
|
|
7505
7658
|
const client = getClient2();
|
|
7506
7659
|
const markerScope = sessionScopeFilter2(sessionScope);
|
|
@@ -7608,6 +7761,22 @@ flow, so bug fixes are fast.`;
|
|
|
7608
7761
|
} catch {
|
|
7609
7762
|
}
|
|
7610
7763
|
let additionalContext = "";
|
|
7764
|
+
if (agentId !== "default") {
|
|
7765
|
+
try {
|
|
7766
|
+
const { getIdentity: getIdentity2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
7767
|
+
const identity = getIdentity2(agentId);
|
|
7768
|
+
if (identity) {
|
|
7769
|
+
const title = identity.frontmatter.title ?? identity.frontmatter.role ?? agentId;
|
|
7770
|
+
const role = identity.frontmatter.role ?? "";
|
|
7771
|
+
const firstLine = identity.body.split("\n").find((l) => l.trim().length > 0) ?? "";
|
|
7772
|
+
additionalContext += `## Your Identity
|
|
7773
|
+
You are **${title}**${role ? ` (${role})` : ""}. ${firstLine}
|
|
7774
|
+
|
|
7775
|
+
`;
|
|
7776
|
+
}
|
|
7777
|
+
} catch {
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7611
7780
|
if (memories.length > 0) {
|
|
7612
7781
|
const brief = memories.map(
|
|
7613
7782
|
(m) => `[${m.timestamp}] ${m.tool_name}: ${m.raw_text.slice(0, 200)}`
|
package/dist/hooks/stop.js
CHANGED
|
@@ -3644,6 +3644,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
|
|
|
3644
3644
|
}
|
|
3645
3645
|
}
|
|
3646
3646
|
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3647
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3647
3648
|
if (memoryIds.length === 0) return;
|
|
3648
3649
|
const run = () => {
|
|
3649
3650
|
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
@@ -4038,7 +4039,7 @@ var init_platform_procedures = __esm({
|
|
|
4038
4039
|
title: "Chain of command \u2014 who talks to whom",
|
|
4039
4040
|
domain: "workflow",
|
|
4040
4041
|
priority: "p0",
|
|
4041
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4042
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
4042
4043
|
},
|
|
4043
4044
|
{
|
|
4044
4045
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4710,7 +4711,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4710
4711
|
sql += ` AND timestamp >= ?`;
|
|
4711
4712
|
args.push(options.since);
|
|
4712
4713
|
}
|
|
4713
|
-
if (options?.
|
|
4714
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4715
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4716
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4717
|
+
args.push(...uniqueTypes);
|
|
4718
|
+
} else if (options?.memoryType) {
|
|
4714
4719
|
sql += ` AND memory_type = ?`;
|
|
4715
4720
|
args.push(options.memoryType);
|
|
4716
4721
|
}
|