@askexenow/exe-os 0.9.34 → 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 +212 -11
- package/dist/bin/backfill-responses.js +212 -11
- package/dist/bin/backfill-vectors.js +14 -3
- package/dist/bin/cleanup-stale-review-tasks.js +224 -12
- package/dist/bin/cli.js +265 -23
- package/dist/bin/exe-agent.js +1 -1
- package/dist/bin/exe-assign.js +217 -12
- package/dist/bin/exe-boot.js +25 -4
- package/dist/bin/exe-call.js +7 -5
- package/dist/bin/exe-dispatch.js +229 -13
- package/dist/bin/exe-doctor.js +14 -3
- package/dist/bin/exe-export-behaviors.js +301 -14
- package/dist/bin/exe-forget.js +245 -21
- package/dist/bin/exe-gateway.js +229 -13
- package/dist/bin/exe-heartbeat.js +224 -12
- package/dist/bin/exe-kill.js +224 -12
- package/dist/bin/exe-launch-agent.js +177 -9
- package/dist/bin/exe-link.js +8 -0
- package/dist/bin/exe-new-employee.js +26 -2
- package/dist/bin/exe-pending-messages.js +224 -12
- package/dist/bin/exe-pending-notifications.js +224 -12
- package/dist/bin/exe-pending-reviews.js +224 -12
- package/dist/bin/exe-rename.js +9 -1
- package/dist/bin/exe-review.js +224 -12
- package/dist/bin/exe-search.js +246 -21
- package/dist/bin/exe-session-cleanup.js +229 -13
- package/dist/bin/exe-start-codex.js +161 -5
- package/dist/bin/exe-start-opencode.js +172 -5
- package/dist/bin/exe-status.js +224 -12
- package/dist/bin/exe-team.js +224 -12
- package/dist/bin/git-sweep.js +229 -13
- package/dist/bin/graph-backfill.js +94 -3
- package/dist/bin/graph-export.js +224 -12
- package/dist/bin/install.js +25 -1
- package/dist/bin/intercom-check.js +229 -13
- package/dist/bin/scan-tasks.js +229 -13
- package/dist/bin/setup.js +15 -5
- package/dist/bin/shard-migrate.js +94 -3
- package/dist/gateway/index.js +229 -13
- package/dist/hooks/bug-report-worker.js +229 -13
- package/dist/hooks/codex-stop-task-finalizer.js +224 -12
- package/dist/hooks/commit-complete.js +229 -13
- package/dist/hooks/error-recall.js +246 -21
- package/dist/hooks/ingest.js +224 -12
- package/dist/hooks/instructions-loaded.js +224 -12
- package/dist/hooks/notification.js +224 -12
- package/dist/hooks/post-compact.js +224 -12
- package/dist/hooks/post-tool-combined.js +246 -21
- package/dist/hooks/pre-compact.js +229 -13
- package/dist/hooks/pre-tool-use.js +234 -18
- package/dist/hooks/prompt-submit.js +346 -23
- package/dist/hooks/session-end.js +229 -13
- package/dist/hooks/session-start.js +418 -42
- package/dist/hooks/stop.js +224 -12
- package/dist/hooks/subagent-stop.js +224 -12
- package/dist/hooks/summary-worker.js +15 -2
- package/dist/index.js +229 -13
- package/dist/lib/cloud-sync.js +8 -0
- package/dist/lib/consolidation.js +3 -1
- package/dist/lib/database.js +8 -0
- package/dist/lib/db.js +8 -0
- package/dist/lib/device-registry.js +8 -0
- package/dist/lib/employee-templates.js +7 -5
- package/dist/lib/exe-daemon.js +1776 -1433
- package/dist/lib/hybrid-search.js +246 -21
- package/dist/lib/schedules.js +14 -3
- package/dist/lib/store.js +217 -12
- package/dist/lib/tasks.js +5 -1
- package/dist/lib/tmux-routing.js +5 -1
- package/dist/mcp/server.js +331 -37
- package/dist/mcp/tools/create-task.js +5 -1
- package/dist/mcp/tools/update-task.js +5 -1
- package/dist/runtime/index.js +229 -13
- package/dist/tui/App.js +229 -13
- package/package.json +1 -1
- package/src/commands/exe/save.md +48 -0
package/dist/bin/exe-forget.js
CHANGED
|
@@ -2288,6 +2288,14 @@ async function ensureSchema() {
|
|
|
2288
2288
|
);
|
|
2289
2289
|
} catch {
|
|
2290
2290
|
}
|
|
2291
|
+
try {
|
|
2292
|
+
await client.execute(
|
|
2293
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2294
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2295
|
+
WHERE content_hash IS NOT NULL`
|
|
2296
|
+
);
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2291
2299
|
await client.executeMultiple(`
|
|
2292
2300
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2293
2301
|
id TEXT PRIMARY KEY,
|
|
@@ -3033,6 +3041,197 @@ var init_state_bus = __esm({
|
|
|
3033
3041
|
}
|
|
3034
3042
|
});
|
|
3035
3043
|
|
|
3044
|
+
// src/lib/memory-write-governor.ts
|
|
3045
|
+
import { createHash } from "crypto";
|
|
3046
|
+
function normalizeMemoryText(text) {
|
|
3047
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3048
|
+
}
|
|
3049
|
+
function classifyMemoryType(input) {
|
|
3050
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3051
|
+
const tool = input.tool_name.toLowerCase();
|
|
3052
|
+
const text = input.raw_text.toLowerCase();
|
|
3053
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3054
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3055
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3056
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3057
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3058
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3059
|
+
return "raw";
|
|
3060
|
+
}
|
|
3061
|
+
function shouldDropMemory(text) {
|
|
3062
|
+
const normalized = normalizeMemoryText(text);
|
|
3063
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3064
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3065
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3066
|
+
}
|
|
3067
|
+
return { drop: false };
|
|
3068
|
+
}
|
|
3069
|
+
function shouldSkipEmbedding(input) {
|
|
3070
|
+
const type = classifyMemoryType(input);
|
|
3071
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3072
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3073
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
function hashMemoryContent(text) {
|
|
3077
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3078
|
+
}
|
|
3079
|
+
function scopedDedupArgs(input) {
|
|
3080
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3081
|
+
}
|
|
3082
|
+
function governMemoryRecord(record) {
|
|
3083
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3084
|
+
const memoryType = classifyMemoryType({
|
|
3085
|
+
raw_text: normalized,
|
|
3086
|
+
agent_id: record.agent_id,
|
|
3087
|
+
project_name: record.project_name,
|
|
3088
|
+
tool_name: record.tool_name,
|
|
3089
|
+
memory_type: record.memory_type
|
|
3090
|
+
});
|
|
3091
|
+
const drop = shouldDropMemory(normalized);
|
|
3092
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3093
|
+
raw_text: normalized,
|
|
3094
|
+
agent_id: record.agent_id,
|
|
3095
|
+
project_name: record.project_name,
|
|
3096
|
+
tool_name: record.tool_name,
|
|
3097
|
+
memory_type: memoryType
|
|
3098
|
+
});
|
|
3099
|
+
return {
|
|
3100
|
+
record: {
|
|
3101
|
+
...record,
|
|
3102
|
+
raw_text: normalized,
|
|
3103
|
+
memory_type: memoryType,
|
|
3104
|
+
vector: skipEmbedding ? null : record.vector
|
|
3105
|
+
},
|
|
3106
|
+
contentHash: hashMemoryContent(normalized),
|
|
3107
|
+
shouldDrop: drop.drop,
|
|
3108
|
+
dropReason: drop.reason,
|
|
3109
|
+
skipEmbedding,
|
|
3110
|
+
hygiene: {
|
|
3111
|
+
dedup: true,
|
|
3112
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
async function findScopedDuplicate(input) {
|
|
3117
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3118
|
+
const client = getClient2();
|
|
3119
|
+
const args = scopedDedupArgs(input);
|
|
3120
|
+
let sql = `SELECT id FROM memories
|
|
3121
|
+
WHERE content_hash = ?
|
|
3122
|
+
AND agent_id = ?
|
|
3123
|
+
AND project_name = ?
|
|
3124
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3125
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3126
|
+
if (input.excludeId) {
|
|
3127
|
+
sql += " AND id != ?";
|
|
3128
|
+
args.push(input.excludeId);
|
|
3129
|
+
}
|
|
3130
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3131
|
+
const result = await client.execute({ sql, args });
|
|
3132
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3133
|
+
}
|
|
3134
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3135
|
+
try {
|
|
3136
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3137
|
+
const client = getClient2();
|
|
3138
|
+
const current = await client.execute({
|
|
3139
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3140
|
+
importance, timestamp
|
|
3141
|
+
FROM memories
|
|
3142
|
+
WHERE id = ?
|
|
3143
|
+
LIMIT 1`,
|
|
3144
|
+
args: [memoryId]
|
|
3145
|
+
});
|
|
3146
|
+
const row = current.rows[0];
|
|
3147
|
+
if (!row) return;
|
|
3148
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3149
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3150
|
+
const agentId = String(row.agent_id);
|
|
3151
|
+
const projectName = String(row.project_name);
|
|
3152
|
+
if (contentHash) {
|
|
3153
|
+
await client.execute({
|
|
3154
|
+
sql: `UPDATE memories
|
|
3155
|
+
SET status = 'deleted',
|
|
3156
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3157
|
+
WHERE id != ?
|
|
3158
|
+
AND content_hash = ?
|
|
3159
|
+
AND agent_id = ?
|
|
3160
|
+
AND project_name = ?
|
|
3161
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3162
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3163
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3167
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3168
|
+
const old = await client.execute({
|
|
3169
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3170
|
+
args: [supersedesId]
|
|
3171
|
+
});
|
|
3172
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3173
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3174
|
+
await client.batch([
|
|
3175
|
+
{
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'archived',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id = ?`,
|
|
3180
|
+
args: [supersedesId]
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
sql: `UPDATE memories
|
|
3184
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3185
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3186
|
+
WHERE id = ?`,
|
|
3187
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3188
|
+
}
|
|
3189
|
+
], "write");
|
|
3190
|
+
}
|
|
3191
|
+
} catch (err) {
|
|
3192
|
+
process.stderr.write(
|
|
3193
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3194
|
+
`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3199
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3200
|
+
if (memoryIds.length === 0) return;
|
|
3201
|
+
const run = () => {
|
|
3202
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3203
|
+
};
|
|
3204
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3205
|
+
else setTimeout(run, 0);
|
|
3206
|
+
}
|
|
3207
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3208
|
+
var init_memory_write_governor = __esm({
|
|
3209
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3210
|
+
"use strict";
|
|
3211
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3212
|
+
"decision",
|
|
3213
|
+
"adr",
|
|
3214
|
+
"behavior",
|
|
3215
|
+
"procedure"
|
|
3216
|
+
]);
|
|
3217
|
+
NOISE_DROP_PATTERNS = [
|
|
3218
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3219
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3220
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3221
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3222
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3223
|
+
];
|
|
3224
|
+
SKIP_EMBED_PATTERNS = [
|
|
3225
|
+
/tmux capture-pane\b/i,
|
|
3226
|
+
/docker ps\b/i,
|
|
3227
|
+
/docker images\b/i,
|
|
3228
|
+
/git status\b/i,
|
|
3229
|
+
/grep .*node_modules/i,
|
|
3230
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3231
|
+
];
|
|
3232
|
+
}
|
|
3233
|
+
});
|
|
3234
|
+
|
|
3036
3235
|
// src/lib/shard-manager.ts
|
|
3037
3236
|
var shard_manager_exports = {};
|
|
3038
3237
|
__export(shard_manager_exports, {
|
|
@@ -3197,7 +3396,8 @@ async function ensureShardSchema(client) {
|
|
|
3197
3396
|
}
|
|
3198
3397
|
for (const idx of [
|
|
3199
3398
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3200
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3399
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3400
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3201
3401
|
]) {
|
|
3202
3402
|
try {
|
|
3203
3403
|
await client.execute(idx);
|
|
@@ -3391,7 +3591,7 @@ var init_platform_procedures = __esm({
|
|
|
3391
3591
|
title: "Chain of command \u2014 who talks to whom",
|
|
3392
3592
|
domain: "workflow",
|
|
3393
3593
|
priority: "p0",
|
|
3394
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3594
|
+
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."
|
|
3395
3595
|
},
|
|
3396
3596
|
{
|
|
3397
3597
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3622,7 +3822,6 @@ __export(store_exports, {
|
|
|
3622
3822
|
vectorToBlob: () => vectorToBlob,
|
|
3623
3823
|
writeMemory: () => writeMemory
|
|
3624
3824
|
});
|
|
3625
|
-
import { createHash } from "crypto";
|
|
3626
3825
|
function isBusyError2(err) {
|
|
3627
3826
|
if (err instanceof Error) {
|
|
3628
3827
|
const msg = err.message.toLowerCase();
|
|
@@ -3736,17 +3935,24 @@ async function writeMemory(record) {
|
|
|
3736
3935
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3737
3936
|
);
|
|
3738
3937
|
}
|
|
3739
|
-
const
|
|
3740
|
-
if (
|
|
3938
|
+
const governed = governMemoryRecord(record);
|
|
3939
|
+
if (governed.shouldDrop) return;
|
|
3940
|
+
record = governed.record;
|
|
3941
|
+
const contentHash = governed.contentHash;
|
|
3942
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3943
|
+
if (_pendingRecords.some(
|
|
3944
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3945
|
+
)) {
|
|
3741
3946
|
return;
|
|
3742
3947
|
}
|
|
3743
3948
|
try {
|
|
3744
|
-
const
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3949
|
+
const existing = await findScopedDuplicate({
|
|
3950
|
+
contentHash,
|
|
3951
|
+
agentId: record.agent_id,
|
|
3952
|
+
projectName: record.project_name,
|
|
3953
|
+
memoryType
|
|
3748
3954
|
});
|
|
3749
|
-
if (existing
|
|
3955
|
+
if (existing) return;
|
|
3750
3956
|
} catch {
|
|
3751
3957
|
}
|
|
3752
3958
|
const dbRow = {
|
|
@@ -3777,7 +3983,7 @@ async function writeMemory(record) {
|
|
|
3777
3983
|
tier: record.tier ?? classifyTier(record),
|
|
3778
3984
|
supersedes_id: record.supersedes_id ?? null,
|
|
3779
3985
|
draft: record.draft ? 1 : 0,
|
|
3780
|
-
memory_type:
|
|
3986
|
+
memory_type: memoryType,
|
|
3781
3987
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3782
3988
|
content_hash: contentHash,
|
|
3783
3989
|
intent: record.intent ?? null,
|
|
@@ -3936,6 +4142,7 @@ async function flushBatch() {
|
|
|
3936
4142
|
const globalClient = getClient();
|
|
3937
4143
|
const globalStmts = batch.map(buildStmt);
|
|
3938
4144
|
await globalClient.batch(globalStmts, "write");
|
|
4145
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3939
4146
|
_pendingRecords.splice(0, batch.length);
|
|
3940
4147
|
try {
|
|
3941
4148
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4056,7 +4263,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4056
4263
|
sql += ` AND timestamp >= ?`;
|
|
4057
4264
|
args.push(options.since);
|
|
4058
4265
|
}
|
|
4059
|
-
if (options?.
|
|
4266
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4267
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4268
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4269
|
+
args.push(...uniqueTypes);
|
|
4270
|
+
} else if (options?.memoryType) {
|
|
4060
4271
|
sql += ` AND memory_type = ?`;
|
|
4061
4272
|
args.push(options.memoryType);
|
|
4062
4273
|
}
|
|
@@ -4194,6 +4405,7 @@ var init_store = __esm({
|
|
|
4194
4405
|
init_keychain();
|
|
4195
4406
|
init_config();
|
|
4196
4407
|
init_state_bus();
|
|
4408
|
+
init_memory_write_governor();
|
|
4197
4409
|
INIT_MAX_RETRIES = 3;
|
|
4198
4410
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4199
4411
|
_pendingRecords = [];
|
|
@@ -4277,6 +4489,17 @@ init_database();
|
|
|
4277
4489
|
// src/lib/hybrid-search.ts
|
|
4278
4490
|
init_store();
|
|
4279
4491
|
init_database();
|
|
4492
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
4493
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4494
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4495
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4496
|
+
args.push(...uniqueTypes);
|
|
4497
|
+
} else if (options?.memoryType) {
|
|
4498
|
+
sql += ` AND ${column} = ?`;
|
|
4499
|
+
args.push(options.memoryType);
|
|
4500
|
+
}
|
|
4501
|
+
return sql;
|
|
4502
|
+
}
|
|
4280
4503
|
async function lightweightSearch(queryText, agentId, options) {
|
|
4281
4504
|
const client = getClient();
|
|
4282
4505
|
const limit = options?.limit ?? 5;
|
|
@@ -4344,10 +4567,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
4344
4567
|
sql += ` AND m.timestamp >= ?`;
|
|
4345
4568
|
args.push(options.since);
|
|
4346
4569
|
}
|
|
4347
|
-
|
|
4348
|
-
sql += ` AND m.memory_type = ?`;
|
|
4349
|
-
args.push(options.memoryType);
|
|
4350
|
-
}
|
|
4570
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
4351
4571
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
4352
4572
|
args.push(limit);
|
|
4353
4573
|
const result = await client.execute({ sql, args });
|
|
@@ -4404,9 +4624,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
4404
4624
|
AND timestamp >= ? AND timestamp <= ?
|
|
4405
4625
|
AND COALESCE(status, 'active') = 'active'
|
|
4406
4626
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
4627
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
4407
4628
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
4408
4629
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
4409
|
-
args: [
|
|
4630
|
+
args: [
|
|
4631
|
+
agentId,
|
|
4632
|
+
windowStart,
|
|
4633
|
+
killedAt,
|
|
4634
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
4635
|
+
boundarySlots
|
|
4636
|
+
]
|
|
4410
4637
|
});
|
|
4411
4638
|
for (const row of boundaryResult.rows) {
|
|
4412
4639
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -4452,10 +4679,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
4452
4679
|
sql += ` AND timestamp >= ?`;
|
|
4453
4680
|
args.push(options.since);
|
|
4454
4681
|
}
|
|
4455
|
-
|
|
4456
|
-
sql += ` AND memory_type = ?`;
|
|
4457
|
-
args.push(options.memoryType);
|
|
4458
|
-
}
|
|
4682
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
4459
4683
|
if (textFilter) {
|
|
4460
4684
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
4461
4685
|
args.push(textFilter);
|
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -2933,6 +2933,14 @@ async function ensureSchema() {
|
|
|
2933
2933
|
);
|
|
2934
2934
|
} catch {
|
|
2935
2935
|
}
|
|
2936
|
+
try {
|
|
2937
|
+
await client.execute(
|
|
2938
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2939
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2940
|
+
WHERE content_hash IS NOT NULL`
|
|
2941
|
+
);
|
|
2942
|
+
} catch {
|
|
2943
|
+
}
|
|
2936
2944
|
await client.executeMultiple(`
|
|
2937
2945
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2938
2946
|
id TEXT PRIMARY KEY,
|
|
@@ -3691,6 +3699,197 @@ var init_keychain = __esm({
|
|
|
3691
3699
|
}
|
|
3692
3700
|
});
|
|
3693
3701
|
|
|
3702
|
+
// src/lib/memory-write-governor.ts
|
|
3703
|
+
import { createHash } from "crypto";
|
|
3704
|
+
function normalizeMemoryText(text) {
|
|
3705
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3706
|
+
}
|
|
3707
|
+
function classifyMemoryType(input) {
|
|
3708
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3709
|
+
const tool = input.tool_name.toLowerCase();
|
|
3710
|
+
const text = input.raw_text.toLowerCase();
|
|
3711
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3712
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3713
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3714
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3715
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3716
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3717
|
+
return "raw";
|
|
3718
|
+
}
|
|
3719
|
+
function shouldDropMemory(text) {
|
|
3720
|
+
const normalized = normalizeMemoryText(text);
|
|
3721
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3722
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3723
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3724
|
+
}
|
|
3725
|
+
return { drop: false };
|
|
3726
|
+
}
|
|
3727
|
+
function shouldSkipEmbedding(input) {
|
|
3728
|
+
const type = classifyMemoryType(input);
|
|
3729
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3730
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3731
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3732
|
+
return false;
|
|
3733
|
+
}
|
|
3734
|
+
function hashMemoryContent(text) {
|
|
3735
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3736
|
+
}
|
|
3737
|
+
function scopedDedupArgs(input) {
|
|
3738
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3739
|
+
}
|
|
3740
|
+
function governMemoryRecord(record) {
|
|
3741
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3742
|
+
const memoryType = classifyMemoryType({
|
|
3743
|
+
raw_text: normalized,
|
|
3744
|
+
agent_id: record.agent_id,
|
|
3745
|
+
project_name: record.project_name,
|
|
3746
|
+
tool_name: record.tool_name,
|
|
3747
|
+
memory_type: record.memory_type
|
|
3748
|
+
});
|
|
3749
|
+
const drop = shouldDropMemory(normalized);
|
|
3750
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3751
|
+
raw_text: normalized,
|
|
3752
|
+
agent_id: record.agent_id,
|
|
3753
|
+
project_name: record.project_name,
|
|
3754
|
+
tool_name: record.tool_name,
|
|
3755
|
+
memory_type: memoryType
|
|
3756
|
+
});
|
|
3757
|
+
return {
|
|
3758
|
+
record: {
|
|
3759
|
+
...record,
|
|
3760
|
+
raw_text: normalized,
|
|
3761
|
+
memory_type: memoryType,
|
|
3762
|
+
vector: skipEmbedding ? null : record.vector
|
|
3763
|
+
},
|
|
3764
|
+
contentHash: hashMemoryContent(normalized),
|
|
3765
|
+
shouldDrop: drop.drop,
|
|
3766
|
+
dropReason: drop.reason,
|
|
3767
|
+
skipEmbedding,
|
|
3768
|
+
hygiene: {
|
|
3769
|
+
dedup: true,
|
|
3770
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
async function findScopedDuplicate(input) {
|
|
3775
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3776
|
+
const client = getClient2();
|
|
3777
|
+
const args = scopedDedupArgs(input);
|
|
3778
|
+
let sql = `SELECT id FROM memories
|
|
3779
|
+
WHERE content_hash = ?
|
|
3780
|
+
AND agent_id = ?
|
|
3781
|
+
AND project_name = ?
|
|
3782
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3783
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3784
|
+
if (input.excludeId) {
|
|
3785
|
+
sql += " AND id != ?";
|
|
3786
|
+
args.push(input.excludeId);
|
|
3787
|
+
}
|
|
3788
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3789
|
+
const result = await client.execute({ sql, args });
|
|
3790
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3791
|
+
}
|
|
3792
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3793
|
+
try {
|
|
3794
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3795
|
+
const client = getClient2();
|
|
3796
|
+
const current = await client.execute({
|
|
3797
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3798
|
+
importance, timestamp
|
|
3799
|
+
FROM memories
|
|
3800
|
+
WHERE id = ?
|
|
3801
|
+
LIMIT 1`,
|
|
3802
|
+
args: [memoryId]
|
|
3803
|
+
});
|
|
3804
|
+
const row = current.rows[0];
|
|
3805
|
+
if (!row) return;
|
|
3806
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3807
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3808
|
+
const agentId = String(row.agent_id);
|
|
3809
|
+
const projectName = String(row.project_name);
|
|
3810
|
+
if (contentHash) {
|
|
3811
|
+
await client.execute({
|
|
3812
|
+
sql: `UPDATE memories
|
|
3813
|
+
SET status = 'deleted',
|
|
3814
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3815
|
+
WHERE id != ?
|
|
3816
|
+
AND content_hash = ?
|
|
3817
|
+
AND agent_id = ?
|
|
3818
|
+
AND project_name = ?
|
|
3819
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3820
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3821
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3824
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3825
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3826
|
+
const old = await client.execute({
|
|
3827
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3828
|
+
args: [supersedesId]
|
|
3829
|
+
});
|
|
3830
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3831
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3832
|
+
await client.batch([
|
|
3833
|
+
{
|
|
3834
|
+
sql: `UPDATE memories
|
|
3835
|
+
SET status = 'archived',
|
|
3836
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3837
|
+
WHERE id = ?`,
|
|
3838
|
+
args: [supersedesId]
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
sql: `UPDATE memories
|
|
3842
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3843
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3844
|
+
WHERE id = ?`,
|
|
3845
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3846
|
+
}
|
|
3847
|
+
], "write");
|
|
3848
|
+
}
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
process.stderr.write(
|
|
3851
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3852
|
+
`
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3857
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3858
|
+
if (memoryIds.length === 0) return;
|
|
3859
|
+
const run = () => {
|
|
3860
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3861
|
+
};
|
|
3862
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3863
|
+
else setTimeout(run, 0);
|
|
3864
|
+
}
|
|
3865
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3866
|
+
var init_memory_write_governor = __esm({
|
|
3867
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3868
|
+
"use strict";
|
|
3869
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3870
|
+
"decision",
|
|
3871
|
+
"adr",
|
|
3872
|
+
"behavior",
|
|
3873
|
+
"procedure"
|
|
3874
|
+
]);
|
|
3875
|
+
NOISE_DROP_PATTERNS = [
|
|
3876
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3877
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3878
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3879
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3880
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3881
|
+
];
|
|
3882
|
+
SKIP_EMBED_PATTERNS = [
|
|
3883
|
+
/tmux capture-pane\b/i,
|
|
3884
|
+
/docker ps\b/i,
|
|
3885
|
+
/docker images\b/i,
|
|
3886
|
+
/git status\b/i,
|
|
3887
|
+
/grep .*node_modules/i,
|
|
3888
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3889
|
+
];
|
|
3890
|
+
}
|
|
3891
|
+
});
|
|
3892
|
+
|
|
3694
3893
|
// src/lib/shard-manager.ts
|
|
3695
3894
|
var shard_manager_exports = {};
|
|
3696
3895
|
__export(shard_manager_exports, {
|
|
@@ -3855,7 +4054,8 @@ async function ensureShardSchema(client) {
|
|
|
3855
4054
|
}
|
|
3856
4055
|
for (const idx of [
|
|
3857
4056
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3858
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
4057
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
4058
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3859
4059
|
]) {
|
|
3860
4060
|
try {
|
|
3861
4061
|
await client.execute(idx);
|
|
@@ -4049,7 +4249,7 @@ var init_platform_procedures = __esm({
|
|
|
4049
4249
|
title: "Chain of command \u2014 who talks to whom",
|
|
4050
4250
|
domain: "workflow",
|
|
4051
4251
|
priority: "p0",
|
|
4052
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4252
|
+
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."
|
|
4053
4253
|
},
|
|
4054
4254
|
{
|
|
4055
4255
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4280,7 +4480,6 @@ __export(store_exports, {
|
|
|
4280
4480
|
vectorToBlob: () => vectorToBlob,
|
|
4281
4481
|
writeMemory: () => writeMemory
|
|
4282
4482
|
});
|
|
4283
|
-
import { createHash } from "crypto";
|
|
4284
4483
|
function isBusyError2(err) {
|
|
4285
4484
|
if (err instanceof Error) {
|
|
4286
4485
|
const msg = err.message.toLowerCase();
|
|
@@ -4394,17 +4593,24 @@ async function writeMemory(record) {
|
|
|
4394
4593
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4395
4594
|
);
|
|
4396
4595
|
}
|
|
4397
|
-
const
|
|
4398
|
-
if (
|
|
4596
|
+
const governed = governMemoryRecord(record);
|
|
4597
|
+
if (governed.shouldDrop) return;
|
|
4598
|
+
record = governed.record;
|
|
4599
|
+
const contentHash = governed.contentHash;
|
|
4600
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4601
|
+
if (_pendingRecords.some(
|
|
4602
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4603
|
+
)) {
|
|
4399
4604
|
return;
|
|
4400
4605
|
}
|
|
4401
4606
|
try {
|
|
4402
|
-
const
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4607
|
+
const existing = await findScopedDuplicate({
|
|
4608
|
+
contentHash,
|
|
4609
|
+
agentId: record.agent_id,
|
|
4610
|
+
projectName: record.project_name,
|
|
4611
|
+
memoryType
|
|
4406
4612
|
});
|
|
4407
|
-
if (existing
|
|
4613
|
+
if (existing) return;
|
|
4408
4614
|
} catch {
|
|
4409
4615
|
}
|
|
4410
4616
|
const dbRow = {
|
|
@@ -4435,7 +4641,7 @@ async function writeMemory(record) {
|
|
|
4435
4641
|
tier: record.tier ?? classifyTier(record),
|
|
4436
4642
|
supersedes_id: record.supersedes_id ?? null,
|
|
4437
4643
|
draft: record.draft ? 1 : 0,
|
|
4438
|
-
memory_type:
|
|
4644
|
+
memory_type: memoryType,
|
|
4439
4645
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4440
4646
|
content_hash: contentHash,
|
|
4441
4647
|
intent: record.intent ?? null,
|
|
@@ -4594,6 +4800,7 @@ async function flushBatch() {
|
|
|
4594
4800
|
const globalClient = getClient();
|
|
4595
4801
|
const globalStmts = batch.map(buildStmt);
|
|
4596
4802
|
await globalClient.batch(globalStmts, "write");
|
|
4803
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4597
4804
|
_pendingRecords.splice(0, batch.length);
|
|
4598
4805
|
try {
|
|
4599
4806
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4714,7 +4921,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4714
4921
|
sql += ` AND timestamp >= ?`;
|
|
4715
4922
|
args.push(options.since);
|
|
4716
4923
|
}
|
|
4717
|
-
if (options?.
|
|
4924
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4925
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4926
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4927
|
+
args.push(...uniqueTypes);
|
|
4928
|
+
} else if (options?.memoryType) {
|
|
4718
4929
|
sql += ` AND memory_type = ?`;
|
|
4719
4930
|
args.push(options.memoryType);
|
|
4720
4931
|
}
|
|
@@ -4852,6 +5063,7 @@ var init_store = __esm({
|
|
|
4852
5063
|
init_keychain();
|
|
4853
5064
|
init_config();
|
|
4854
5065
|
init_state_bus();
|
|
5066
|
+
init_memory_write_governor();
|
|
4855
5067
|
INIT_MAX_RETRIES = 3;
|
|
4856
5068
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4857
5069
|
_pendingRecords = [];
|
|
@@ -11377,7 +11589,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11377
11589
|
}
|
|
11378
11590
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
11379
11591
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
11380
|
-
|
|
11592
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
11593
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
11594
|
+
ccModel += "[1m]";
|
|
11595
|
+
}
|
|
11596
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
11381
11597
|
}
|
|
11382
11598
|
}
|
|
11383
11599
|
let spawnCommand;
|