@gethmy/mcp 2.1.3 → 2.2.1
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/cli.js +2279 -2017
- package/dist/index.js +24656 -24394
- package/dist/lib/__tests__/auto-session.test.js +33 -33
- package/dist/lib/api-client.js +116 -0
- package/dist/lib/auto-session.js +49 -8
- package/dist/lib/cli.js +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/http.js +1 -0
- package/dist/lib/prompt-builder.js +1 -0
- package/dist/lib/remote.js +2 -2
- package/dist/lib/server.js +200 -100
- package/dist/lib/skills.js +3 -3
- package/dist/lib/tui/setup.js +3 -3
- package/package.json +1 -1
- package/src/__tests__/auto-session.test.ts +33 -33
- package/src/api-client.ts +205 -0
- package/src/auto-session.ts +64 -7
- package/src/cli.ts +1 -1
- package/src/config.ts +1 -1
- package/src/http.ts +1 -0
- package/src/prompt-builder.ts +3 -0
- package/src/remote.ts +3 -2
- package/src/server.ts +267 -121
- package/src/skills.ts +3 -3
- package/src/tui/setup.ts +3 -3
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ var __export = (target, all) => {
|
|
|
26
26
|
set: (newValue) => all[name] = () => newValue
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
30
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
31
|
|
|
31
32
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -2121,204 +2122,781 @@ var require_commander = __commonJS((exports) => {
|
|
|
2121
2122
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2122
2123
|
});
|
|
2123
2124
|
|
|
2124
|
-
//
|
|
2125
|
-
var
|
|
2126
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2127
|
-
exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = undefined;
|
|
2125
|
+
// ../memory/src/schema.ts
|
|
2126
|
+
var init_schema = () => {};
|
|
2128
2127
|
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2128
|
+
// ../memory/src/constraints.ts
|
|
2129
|
+
var init_constraints = __esm(() => {
|
|
2130
|
+
init_schema();
|
|
2131
|
+
});
|
|
2132
|
+
// ../memory/src/client.ts
|
|
2133
|
+
var init_client = __esm(() => {
|
|
2134
|
+
init_constraints();
|
|
2135
|
+
});
|
|
2133
2136
|
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2137
|
+
// ../memory/src/graph-walk.ts
|
|
2138
|
+
async function discoverRelatedContext(client, startIds, maxDepth = 2, maxEntities = 20, minConfidence = 0.5) {
|
|
2139
|
+
const visited = new Set;
|
|
2140
|
+
const collectedEntities = [];
|
|
2141
|
+
const collectedRelations = [];
|
|
2142
|
+
let truncated = false;
|
|
2143
|
+
const queue = startIds.map((id) => [id, 0]);
|
|
2144
|
+
for (const id of startIds) {
|
|
2145
|
+
visited.add(id);
|
|
2146
|
+
}
|
|
2147
|
+
while (queue.length > 0) {
|
|
2148
|
+
const [entityId, depth] = queue.shift();
|
|
2149
|
+
if (collectedEntities.length >= maxEntities) {
|
|
2150
|
+
truncated = true;
|
|
2151
|
+
break;
|
|
2149
2152
|
}
|
|
2153
|
+
if (depth > maxDepth)
|
|
2154
|
+
continue;
|
|
2155
|
+
try {
|
|
2156
|
+
const entityResult = await client.getMemoryEntity(entityId);
|
|
2157
|
+
const entity = entityResult.entity;
|
|
2158
|
+
if (entity) {
|
|
2159
|
+
collectedEntities.push({
|
|
2160
|
+
id: entity.id,
|
|
2161
|
+
type: entity.type,
|
|
2162
|
+
title: entity.title,
|
|
2163
|
+
confidence: entity.confidence ?? 1,
|
|
2164
|
+
memory_tier: entity.memory_tier || "reference"
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
if (depth >= maxDepth)
|
|
2168
|
+
continue;
|
|
2169
|
+
const related = await client.getRelatedEntities(entityId);
|
|
2170
|
+
for (const raw of related.outgoing || []) {
|
|
2171
|
+
const rel = raw;
|
|
2172
|
+
const relConfidence = rel.confidence ?? 1;
|
|
2173
|
+
if (relConfidence < minConfidence)
|
|
2174
|
+
continue;
|
|
2175
|
+
const target = rel.target;
|
|
2176
|
+
const targetId = target?.id ?? rel.target_id;
|
|
2177
|
+
if (targetId && !visited.has(targetId)) {
|
|
2178
|
+
visited.add(targetId);
|
|
2179
|
+
queue.push([targetId, depth + 1]);
|
|
2180
|
+
collectedRelations.push({
|
|
2181
|
+
id: rel.id,
|
|
2182
|
+
source_id: entityId,
|
|
2183
|
+
target_id: targetId,
|
|
2184
|
+
relation_type: rel.relation_type,
|
|
2185
|
+
confidence: relConfidence
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
for (const raw of related.incoming || []) {
|
|
2190
|
+
const rel = raw;
|
|
2191
|
+
const relConfidence = rel.confidence ?? 1;
|
|
2192
|
+
if (relConfidence < minConfidence)
|
|
2193
|
+
continue;
|
|
2194
|
+
const source = rel.source;
|
|
2195
|
+
const sourceId = source?.id ?? rel.source_id;
|
|
2196
|
+
if (sourceId && !visited.has(sourceId)) {
|
|
2197
|
+
visited.add(sourceId);
|
|
2198
|
+
queue.push([sourceId, depth + 1]);
|
|
2199
|
+
collectedRelations.push({
|
|
2200
|
+
id: rel.id,
|
|
2201
|
+
source_id: sourceId,
|
|
2202
|
+
target_id: entityId,
|
|
2203
|
+
relation_type: rel.relation_type,
|
|
2204
|
+
confidence: relConfidence
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
} catch {}
|
|
2150
2209
|
}
|
|
2151
|
-
|
|
2210
|
+
return {
|
|
2211
|
+
entities: collectedEntities,
|
|
2212
|
+
relations: collectedRelations,
|
|
2213
|
+
depth: maxDepth,
|
|
2214
|
+
truncated
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2152
2217
|
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2218
|
+
// ../memory/src/lifecycle.ts
|
|
2219
|
+
function computeDecayScore(tier, lastAccessedAt, accessCount) {
|
|
2220
|
+
const halfLife = DECAY_HALF_LIVES[tier];
|
|
2221
|
+
const now = Date.now();
|
|
2222
|
+
let daysSinceAccess = 0;
|
|
2223
|
+
if (lastAccessedAt) {
|
|
2224
|
+
daysSinceAccess = (now - new Date(lastAccessedAt).getTime()) / (1000 * 60 * 60 * 24);
|
|
2225
|
+
}
|
|
2226
|
+
const timeDecay = 0.5 ** (daysSinceAccess / halfLife);
|
|
2227
|
+
const accessBonus = Math.log10(accessCount + 1) * 0.1;
|
|
2228
|
+
const score = Math.min(timeDecay + accessBonus, 1);
|
|
2229
|
+
return { score, daysSinceAccess, halfLife, accessBonus };
|
|
2230
|
+
}
|
|
2231
|
+
function checkPromotion(currentTier, accessCount, confidence, createdAt) {
|
|
2232
|
+
const ageDays = (Date.now() - new Date(createdAt).getTime()) / (1000 * 60 * 60 * 24);
|
|
2233
|
+
const base = {
|
|
2234
|
+
eligible: false,
|
|
2235
|
+
targetTier: null,
|
|
2236
|
+
reason: null,
|
|
2237
|
+
currentTier,
|
|
2238
|
+
accessCount,
|
|
2239
|
+
confidence,
|
|
2240
|
+
ageDays
|
|
2241
|
+
};
|
|
2242
|
+
if (currentTier === "draft") {
|
|
2243
|
+
const rules = PROMOTION_RULES.draftToEpisode;
|
|
2244
|
+
if (accessCount >= rules.minAccessCount && confidence >= rules.minConfidence && ageDays >= rules.minAgeDays) {
|
|
2245
|
+
return {
|
|
2246
|
+
...base,
|
|
2247
|
+
eligible: true,
|
|
2248
|
+
targetTier: "episode",
|
|
2249
|
+
reason: `Accessed ${accessCount} times (≥${rules.minAccessCount}), confidence ${confidence} (≥${rules.minConfidence}), age ${Math.round(ageDays)}d (≥${rules.minAgeDays}d)`
|
|
2250
|
+
};
|
|
2178
2251
|
}
|
|
2179
2252
|
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2253
|
+
if (currentTier === "episode") {
|
|
2254
|
+
const rules = PROMOTION_RULES.episodeToReference;
|
|
2255
|
+
if (accessCount >= rules.minAccessCount && confidence >= rules.minConfidence && ageDays >= rules.minAgeDays) {
|
|
2256
|
+
return {
|
|
2257
|
+
...base,
|
|
2258
|
+
eligible: true,
|
|
2259
|
+
targetTier: "reference",
|
|
2260
|
+
reason: `Accessed ${accessCount} times (≥${rules.minAccessCount}), confidence ${confidence} (≥${rules.minConfidence}), age ${Math.round(ageDays)}d (≥${rules.minAgeDays}d)`
|
|
2261
|
+
};
|
|
2188
2262
|
}
|
|
2189
|
-
return new _Code(code);
|
|
2190
2263
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2264
|
+
return base;
|
|
2265
|
+
}
|
|
2266
|
+
function evaluateLifecycle(entity) {
|
|
2267
|
+
const decay = computeDecayScore(entity.memory_tier, entity.last_accessed_at, entity.access_count);
|
|
2268
|
+
const promotion = checkPromotion(entity.memory_tier, entity.access_count, entity.confidence, entity.created_at);
|
|
2269
|
+
const shouldArchive = entity.confidence < ARCHIVE_THRESHOLD;
|
|
2270
|
+
const archiveReason = shouldArchive ? `Confidence ${entity.confidence} below threshold ${ARCHIVE_THRESHOLD}` : undefined;
|
|
2271
|
+
const shouldFlagForReview = decay.daysSinceAccess >= STALE_DAYS && entity.access_count < STALE_MIN_ACCESS;
|
|
2272
|
+
const reviewReason = shouldFlagForReview ? `Not accessed in ${Math.round(decay.daysSinceAccess)} days with only ${entity.access_count} accesses` : undefined;
|
|
2273
|
+
return {
|
|
2274
|
+
decay,
|
|
2275
|
+
promotion,
|
|
2276
|
+
shouldArchive,
|
|
2277
|
+
shouldFlagForReview,
|
|
2278
|
+
archiveReason,
|
|
2279
|
+
reviewReason
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
var DECAY_HALF_LIVES, PROMOTION_RULES, ARCHIVE_THRESHOLD = 0.3, STALE_DAYS = 90, STALE_MIN_ACCESS = 3;
|
|
2283
|
+
var init_lifecycle = __esm(() => {
|
|
2284
|
+
DECAY_HALF_LIVES = {
|
|
2285
|
+
draft: 7,
|
|
2286
|
+
episode: 30,
|
|
2287
|
+
reference: 180
|
|
2288
|
+
};
|
|
2289
|
+
PROMOTION_RULES = {
|
|
2290
|
+
draftToEpisode: {
|
|
2291
|
+
minAccessCount: 5,
|
|
2292
|
+
minConfidence: 0.8,
|
|
2293
|
+
minAgeDays: 1
|
|
2294
|
+
},
|
|
2295
|
+
episodeToReference: {
|
|
2296
|
+
minAccessCount: 10,
|
|
2297
|
+
minConfidence: 0.9,
|
|
2298
|
+
minAgeDays: 7
|
|
2200
2299
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2300
|
+
};
|
|
2301
|
+
});
|
|
2302
|
+
|
|
2303
|
+
// ../memory/src/sync-storage.ts
|
|
2304
|
+
function parseSyncMarkdown(markdown) {
|
|
2305
|
+
const trimmed = markdown.trim();
|
|
2306
|
+
let frontmatter = {
|
|
2307
|
+
type: "context",
|
|
2308
|
+
scope: "project",
|
|
2309
|
+
tier: "reference",
|
|
2310
|
+
confidence: 1,
|
|
2311
|
+
tags: []
|
|
2312
|
+
};
|
|
2313
|
+
let body = trimmed;
|
|
2314
|
+
const fmMatch = trimmed.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
2315
|
+
if (fmMatch) {
|
|
2316
|
+
frontmatter = parseSyncYamlFrontmatter(fmMatch[1]);
|
|
2317
|
+
body = fmMatch[2].trim();
|
|
2203
2318
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
code.push(arg);
|
|
2210
|
-
else
|
|
2211
|
-
code.push(interpolate(arg));
|
|
2319
|
+
let title = "";
|
|
2320
|
+
const titleMatch = body.match(/^#\s+(.+)/m);
|
|
2321
|
+
if (titleMatch) {
|
|
2322
|
+
title = titleMatch[1].trim();
|
|
2323
|
+
body = body.replace(/^#\s+.+\n?/, "").trim();
|
|
2212
2324
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
continue;
|
|
2222
|
-
}
|
|
2223
|
-
expr[i++] = "+";
|
|
2224
|
-
}
|
|
2225
|
-
i++;
|
|
2226
|
-
}
|
|
2325
|
+
return { frontmatter, title, content: body };
|
|
2326
|
+
}
|
|
2327
|
+
function serializeSyncMarkdown(entity) {
|
|
2328
|
+
const lines = ["---"];
|
|
2329
|
+
lines.push(`id: ${entity.id}`);
|
|
2330
|
+
lines.push(`workspace_id: ${entity.workspace_id}`);
|
|
2331
|
+
if (entity.project_id) {
|
|
2332
|
+
lines.push(`project_id: ${entity.project_id}`);
|
|
2227
2333
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
if (typeof b != "string")
|
|
2237
|
-
return `${a.slice(0, -1)}${b}"`;
|
|
2238
|
-
if (b[0] === '"')
|
|
2239
|
-
return a.slice(0, -1) + b.slice(1);
|
|
2240
|
-
return;
|
|
2241
|
-
}
|
|
2242
|
-
if (typeof b == "string" && b[0] === '"' && !(a instanceof Name))
|
|
2243
|
-
return `"${a}${b.slice(1)}`;
|
|
2244
|
-
return;
|
|
2334
|
+
lines.push(`type: ${entity.type}`);
|
|
2335
|
+
lines.push(`scope: ${entity.scope}`);
|
|
2336
|
+
lines.push(`tier: ${entity.memory_tier || "reference"}`);
|
|
2337
|
+
lines.push(`confidence: ${entity.confidence}`);
|
|
2338
|
+
if (entity.tags.length > 0) {
|
|
2339
|
+
lines.push(`tags: [${entity.tags.join(", ")}]`);
|
|
2340
|
+
} else {
|
|
2341
|
+
lines.push("tags: []");
|
|
2245
2342
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2343
|
+
if (entity.agent_identifier) {
|
|
2344
|
+
lines.push(`agent: ${entity.agent_identifier}`);
|
|
2248
2345
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2346
|
+
lines.push(`created_at: ${entity.created_at}`);
|
|
2347
|
+
lines.push(`updated_at: ${entity.updated_at}`);
|
|
2348
|
+
lines.push("---");
|
|
2349
|
+
lines.push("");
|
|
2350
|
+
lines.push(`# ${entity.title}`);
|
|
2351
|
+
lines.push("");
|
|
2352
|
+
lines.push(entity.content);
|
|
2353
|
+
return lines.join(`
|
|
2354
|
+
`);
|
|
2355
|
+
}
|
|
2356
|
+
function parseSyncYamlFrontmatter(yaml) {
|
|
2357
|
+
const result = {
|
|
2358
|
+
type: "context",
|
|
2359
|
+
scope: "project",
|
|
2360
|
+
tier: "reference",
|
|
2361
|
+
confidence: 1,
|
|
2362
|
+
tags: []
|
|
2363
|
+
};
|
|
2364
|
+
for (const line of yaml.split(`
|
|
2365
|
+
`)) {
|
|
2366
|
+
const colonIndex = line.indexOf(":");
|
|
2367
|
+
if (colonIndex === -1)
|
|
2368
|
+
continue;
|
|
2369
|
+
const key = line.slice(0, colonIndex).trim();
|
|
2370
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
2371
|
+
switch (key) {
|
|
2372
|
+
case "id":
|
|
2373
|
+
result.id = value;
|
|
2374
|
+
break;
|
|
2375
|
+
case "workspace_id":
|
|
2376
|
+
result.workspace_id = value;
|
|
2377
|
+
break;
|
|
2378
|
+
case "project_id":
|
|
2379
|
+
result.project_id = value;
|
|
2380
|
+
break;
|
|
2381
|
+
case "type":
|
|
2382
|
+
result.type = value;
|
|
2383
|
+
break;
|
|
2384
|
+
case "scope":
|
|
2385
|
+
result.scope = value;
|
|
2386
|
+
break;
|
|
2387
|
+
case "tier":
|
|
2388
|
+
result.tier = value;
|
|
2389
|
+
break;
|
|
2390
|
+
case "confidence":
|
|
2391
|
+
result.confidence = parseFloat(value) || 1;
|
|
2392
|
+
break;
|
|
2393
|
+
case "tags":
|
|
2394
|
+
result.tags = parseYamlArray(value);
|
|
2395
|
+
break;
|
|
2396
|
+
case "related":
|
|
2397
|
+
result.related = parseYamlArray(value);
|
|
2398
|
+
break;
|
|
2399
|
+
case "agent":
|
|
2400
|
+
result.agent = value;
|
|
2401
|
+
break;
|
|
2402
|
+
case "created_at":
|
|
2403
|
+
result.created_at = value;
|
|
2404
|
+
break;
|
|
2405
|
+
case "updated_at":
|
|
2406
|
+
result.updated_at = value;
|
|
2407
|
+
break;
|
|
2408
|
+
}
|
|
2252
2409
|
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2410
|
+
return result;
|
|
2411
|
+
}
|
|
2412
|
+
function parseYamlArray(value) {
|
|
2413
|
+
const match = value.match(/^\[(.*)]\s*$/);
|
|
2414
|
+
if (!match)
|
|
2415
|
+
return [];
|
|
2416
|
+
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// ../memory/src/sync.ts
|
|
2420
|
+
import { createHash } from "node:crypto";
|
|
2421
|
+
import {
|
|
2422
|
+
existsSync as existsSync2,
|
|
2423
|
+
mkdirSync as mkdirSync2,
|
|
2424
|
+
readdirSync,
|
|
2425
|
+
readFileSync as readFileSync2,
|
|
2426
|
+
rmSync,
|
|
2427
|
+
writeFileSync as writeFileSync2
|
|
2428
|
+
} from "node:fs";
|
|
2429
|
+
import { join as join2, relative, sep } from "node:path";
|
|
2430
|
+
function computeFileHash(content) {
|
|
2431
|
+
return `sha256:${createHash("sha256").update(content).digest("hex")}`;
|
|
2432
|
+
}
|
|
2433
|
+
function slugifyTitle(title) {
|
|
2434
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
2435
|
+
}
|
|
2436
|
+
function entityToFilename(entity) {
|
|
2437
|
+
const slug = slugifyTitle(entity.title);
|
|
2438
|
+
const shortId = entity.id.slice(0, 8);
|
|
2439
|
+
return `${entity.type}--${slug}--${shortId}.md`;
|
|
2440
|
+
}
|
|
2441
|
+
function entityToDirectoryPath(entity, memoryDir) {
|
|
2442
|
+
const wsDir = join2(memoryDir, entity.workspace_id);
|
|
2443
|
+
if (entity.scope === "private") {
|
|
2444
|
+
return join2(wsDir, "_private");
|
|
2255
2445
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
2446
|
+
if (entity.scope === "workspace" || !entity.project_id) {
|
|
2447
|
+
return join2(wsDir, "_workspace");
|
|
2259
2448
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2449
|
+
return join2(wsDir, entity.project_id);
|
|
2450
|
+
}
|
|
2451
|
+
function emptySyncState() {
|
|
2452
|
+
return { version: 1, lastPullAt: null, entities: {} };
|
|
2453
|
+
}
|
|
2454
|
+
function loadSyncState(memoryDir) {
|
|
2455
|
+
const statePath = join2(memoryDir, ".sync-state.json");
|
|
2456
|
+
if (!existsSync2(statePath))
|
|
2457
|
+
return emptySyncState();
|
|
2458
|
+
try {
|
|
2459
|
+
return JSON.parse(readFileSync2(statePath, "utf-8"));
|
|
2460
|
+
} catch {
|
|
2461
|
+
return emptySyncState();
|
|
2263
2462
|
}
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
}
|
|
2269
|
-
throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`);
|
|
2463
|
+
}
|
|
2464
|
+
function saveSyncState(memoryDir, state) {
|
|
2465
|
+
if (!existsSync2(memoryDir)) {
|
|
2466
|
+
mkdirSync2(memoryDir, { recursive: true });
|
|
2270
2467
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2468
|
+
writeFileSync2(join2(memoryDir, ".sync-state.json"), JSON.stringify(state, null, 2));
|
|
2469
|
+
}
|
|
2470
|
+
function writeEntityFile(entity, memoryDir) {
|
|
2471
|
+
const dir = entityToDirectoryPath(entity, memoryDir);
|
|
2472
|
+
if (!existsSync2(dir))
|
|
2473
|
+
mkdirSync2(dir, { recursive: true });
|
|
2474
|
+
const filename = entityToFilename(entity);
|
|
2475
|
+
const filePath = join2(dir, filename);
|
|
2476
|
+
const markdown = serializeSyncMarkdown(entity);
|
|
2477
|
+
writeFileSync2(filePath, markdown);
|
|
2478
|
+
return relative(memoryDir, filePath);
|
|
2479
|
+
}
|
|
2480
|
+
function deleteEntityFile(relPath, memoryDir) {
|
|
2481
|
+
const absPath = join2(memoryDir, relPath);
|
|
2482
|
+
if (existsSync2(absPath)) {
|
|
2483
|
+
rmSync(absPath);
|
|
2274
2484
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
super(`CodeGen: "code" for ${name} not defined`);
|
|
2287
|
-
this.value = name.value;
|
|
2485
|
+
}
|
|
2486
|
+
function findMarkdownFiles(dir) {
|
|
2487
|
+
const results = [];
|
|
2488
|
+
if (!existsSync2(dir))
|
|
2489
|
+
return results;
|
|
2490
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
2491
|
+
const fullPath = join2(dir, entry.name);
|
|
2492
|
+
if (entry.isDirectory()) {
|
|
2493
|
+
results.push(...findMarkdownFiles(fullPath));
|
|
2494
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2495
|
+
results.push(fullPath);
|
|
2288
2496
|
}
|
|
2289
2497
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2498
|
+
return results;
|
|
2499
|
+
}
|
|
2500
|
+
async function syncPull(client, config, workspaceId, projectId) {
|
|
2501
|
+
const { memoryDir } = config;
|
|
2502
|
+
const state = loadSyncState(memoryDir);
|
|
2503
|
+
const result = {
|
|
2504
|
+
pulled: 0,
|
|
2505
|
+
pushed: 0,
|
|
2506
|
+
deleted: 0,
|
|
2507
|
+
conflicts: 0,
|
|
2508
|
+
errors: []
|
|
2299
2509
|
};
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
}
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2510
|
+
const allEntities = [];
|
|
2511
|
+
let offset = 0;
|
|
2512
|
+
const batchSize = 100;
|
|
2513
|
+
while (true) {
|
|
2514
|
+
try {
|
|
2515
|
+
const resp = await client.listMemoryEntities({
|
|
2516
|
+
workspace_id: workspaceId,
|
|
2517
|
+
project_id: projectId,
|
|
2518
|
+
limit: batchSize,
|
|
2519
|
+
offset
|
|
2520
|
+
});
|
|
2521
|
+
const batch = resp.entities;
|
|
2522
|
+
allEntities.push(...batch);
|
|
2523
|
+
if (batch.length < batchSize)
|
|
2524
|
+
break;
|
|
2525
|
+
offset += batchSize;
|
|
2526
|
+
} catch (err) {
|
|
2527
|
+
result.errors.push(`Failed to fetch entities: ${err}`);
|
|
2528
|
+
return result;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
const remoteIds = new Set(allEntities.map((e) => e.id));
|
|
2532
|
+
for (const entity of allEntities) {
|
|
2533
|
+
const existing = state.entities[entity.id];
|
|
2534
|
+
const markdown = serializeSyncMarkdown(entity);
|
|
2535
|
+
const hash = computeFileHash(markdown);
|
|
2536
|
+
if (!existing) {
|
|
2537
|
+
const relPath = writeEntityFile(entity, memoryDir);
|
|
2538
|
+
state.entities[entity.id] = {
|
|
2539
|
+
filePath: relPath,
|
|
2540
|
+
remoteUpdatedAt: entity.updated_at,
|
|
2541
|
+
lastSyncedHash: hash
|
|
2542
|
+
};
|
|
2543
|
+
result.pulled++;
|
|
2544
|
+
} else {
|
|
2545
|
+
const remoteChanged = entity.updated_at > existing.remoteUpdatedAt;
|
|
2546
|
+
if (!remoteChanged)
|
|
2547
|
+
continue;
|
|
2548
|
+
const absPath = join2(memoryDir, existing.filePath);
|
|
2549
|
+
let localChanged = false;
|
|
2550
|
+
if (existsSync2(absPath)) {
|
|
2551
|
+
const localContent = readFileSync2(absPath, "utf-8");
|
|
2552
|
+
const localHash = computeFileHash(localContent);
|
|
2553
|
+
localChanged = localHash !== existing.lastSyncedHash;
|
|
2554
|
+
}
|
|
2555
|
+
if (localChanged) {
|
|
2556
|
+
result.conflicts++;
|
|
2557
|
+
}
|
|
2558
|
+
const newRelPath = writeEntityFile(entity, memoryDir);
|
|
2559
|
+
if (existing.filePath !== newRelPath) {
|
|
2560
|
+
deleteEntityFile(existing.filePath, memoryDir);
|
|
2561
|
+
}
|
|
2562
|
+
state.entities[entity.id] = {
|
|
2563
|
+
filePath: newRelPath,
|
|
2564
|
+
remoteUpdatedAt: entity.updated_at,
|
|
2565
|
+
lastSyncedHash: hash
|
|
2566
|
+
};
|
|
2567
|
+
result.pulled++;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
for (const [entityId, entry] of Object.entries(state.entities)) {
|
|
2571
|
+
if (!remoteIds.has(entityId)) {
|
|
2572
|
+
deleteEntityFile(entry.filePath, memoryDir);
|
|
2573
|
+
delete state.entities[entityId];
|
|
2574
|
+
result.deleted++;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
state.lastPullAt = new Date().toISOString();
|
|
2578
|
+
saveSyncState(memoryDir, state);
|
|
2579
|
+
return result;
|
|
2580
|
+
}
|
|
2581
|
+
async function syncPush(client, config, workspaceId) {
|
|
2582
|
+
const { memoryDir } = config;
|
|
2583
|
+
const state = loadSyncState(memoryDir);
|
|
2584
|
+
const result = {
|
|
2585
|
+
pulled: 0,
|
|
2586
|
+
pushed: 0,
|
|
2587
|
+
deleted: 0,
|
|
2588
|
+
conflicts: 0,
|
|
2589
|
+
errors: []
|
|
2590
|
+
};
|
|
2591
|
+
const mdFiles = findMarkdownFiles(memoryDir);
|
|
2592
|
+
for (const absPath of mdFiles) {
|
|
2593
|
+
const content = readFileSync2(absPath, "utf-8");
|
|
2594
|
+
const hash = computeFileHash(content);
|
|
2595
|
+
const parsed = parseSyncMarkdown(content);
|
|
2596
|
+
if (parsed.frontmatter.id) {
|
|
2597
|
+
const entityId = parsed.frontmatter.id;
|
|
2598
|
+
const existing = state.entities[entityId];
|
|
2599
|
+
if (existing && hash === existing.lastSyncedHash)
|
|
2600
|
+
continue;
|
|
2601
|
+
try {
|
|
2602
|
+
const resp = await client.updateMemoryEntity(entityId, {
|
|
2603
|
+
title: parsed.title || "Untitled",
|
|
2604
|
+
content: parsed.content,
|
|
2605
|
+
type: parsed.frontmatter.type,
|
|
2606
|
+
scope: parsed.frontmatter.scope,
|
|
2607
|
+
confidence: parsed.frontmatter.confidence,
|
|
2608
|
+
tags: parsed.frontmatter.tags
|
|
2609
|
+
});
|
|
2610
|
+
const updated = resp.entity;
|
|
2611
|
+
const newMarkdown = serializeSyncMarkdown(updated);
|
|
2612
|
+
writeFileSync2(absPath, newMarkdown);
|
|
2613
|
+
const relPath = relative(memoryDir, absPath);
|
|
2614
|
+
state.entities[entityId] = {
|
|
2615
|
+
filePath: relPath,
|
|
2616
|
+
remoteUpdatedAt: updated.updated_at,
|
|
2617
|
+
lastSyncedHash: computeFileHash(newMarkdown)
|
|
2618
|
+
};
|
|
2619
|
+
result.pushed++;
|
|
2620
|
+
} catch (err) {
|
|
2621
|
+
result.errors.push(`Failed to update ${entityId}: ${err}`);
|
|
2622
|
+
}
|
|
2623
|
+
} else {
|
|
2624
|
+
const relPath = relative(memoryDir, absPath);
|
|
2625
|
+
const parts = relPath.split(sep);
|
|
2626
|
+
const fileWorkspaceId = parts.length >= 2 ? parts[0] : workspaceId;
|
|
2627
|
+
let fileProjectId;
|
|
2628
|
+
if (parts.length >= 3) {
|
|
2629
|
+
const scopeDir = parts[1];
|
|
2630
|
+
if (scopeDir !== "_workspace" && scopeDir !== "_private") {
|
|
2631
|
+
fileProjectId = scopeDir;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
let scope = parsed.frontmatter.scope || "project";
|
|
2635
|
+
if (parts.length >= 3) {
|
|
2636
|
+
const scopeDir = parts[1];
|
|
2637
|
+
if (scopeDir === "_private")
|
|
2638
|
+
scope = "private";
|
|
2639
|
+
else if (scopeDir === "_workspace")
|
|
2640
|
+
scope = "workspace";
|
|
2641
|
+
}
|
|
2642
|
+
try {
|
|
2643
|
+
const resp = await client.createMemoryEntity({
|
|
2644
|
+
workspace_id: fileWorkspaceId,
|
|
2645
|
+
project_id: fileProjectId,
|
|
2646
|
+
type: parsed.frontmatter.type,
|
|
2647
|
+
scope,
|
|
2648
|
+
title: parsed.title || "Untitled",
|
|
2649
|
+
content: parsed.content,
|
|
2650
|
+
confidence: parsed.frontmatter.confidence,
|
|
2651
|
+
tags: parsed.frontmatter.tags,
|
|
2652
|
+
agent_identifier: parsed.frontmatter.agent
|
|
2653
|
+
});
|
|
2654
|
+
const created = resp.entity;
|
|
2655
|
+
const dir = entityToDirectoryPath(created, memoryDir);
|
|
2656
|
+
if (!existsSync2(dir))
|
|
2657
|
+
mkdirSync2(dir, { recursive: true });
|
|
2658
|
+
const newFilename = entityToFilename(created);
|
|
2659
|
+
const newAbsPath = join2(dir, newFilename);
|
|
2660
|
+
const newMarkdown = serializeSyncMarkdown(created);
|
|
2661
|
+
writeFileSync2(newAbsPath, newMarkdown);
|
|
2662
|
+
if (absPath !== newAbsPath && existsSync2(absPath)) {
|
|
2663
|
+
rmSync(absPath);
|
|
2664
|
+
}
|
|
2665
|
+
const newRelPath = relative(memoryDir, newAbsPath);
|
|
2666
|
+
state.entities[created.id] = {
|
|
2667
|
+
filePath: newRelPath,
|
|
2668
|
+
remoteUpdatedAt: created.updated_at,
|
|
2669
|
+
lastSyncedHash: computeFileHash(newMarkdown)
|
|
2670
|
+
};
|
|
2671
|
+
result.pushed++;
|
|
2672
|
+
} catch (err) {
|
|
2673
|
+
result.errors.push(`Failed to create entity from ${relPath}: ${err}`);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
saveSyncState(memoryDir, state);
|
|
2678
|
+
return result;
|
|
2679
|
+
}
|
|
2680
|
+
async function syncFull(client, config, workspaceId, projectId) {
|
|
2681
|
+
const pullResult = await syncPull(client, config, workspaceId, projectId);
|
|
2682
|
+
const pushResult = await syncPush(client, config, workspaceId);
|
|
2683
|
+
return {
|
|
2684
|
+
pulled: pullResult.pulled,
|
|
2685
|
+
pushed: pushResult.pushed,
|
|
2686
|
+
deleted: pullResult.deleted,
|
|
2687
|
+
conflicts: pullResult.conflicts,
|
|
2688
|
+
errors: [...pullResult.errors, ...pushResult.errors]
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
var init_sync = () => {};
|
|
2692
|
+
|
|
2693
|
+
// ../memory/src/index.ts
|
|
2694
|
+
var init_src = __esm(() => {
|
|
2695
|
+
init_client();
|
|
2696
|
+
init_constraints();
|
|
2697
|
+
init_lifecycle();
|
|
2698
|
+
init_schema();
|
|
2699
|
+
init_sync();
|
|
2700
|
+
});
|
|
2701
|
+
|
|
2702
|
+
// ../../node_modules/ajv/dist/compile/codegen/code.js
|
|
2703
|
+
var require_code = __commonJS((exports) => {
|
|
2704
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2705
|
+
exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = undefined;
|
|
2706
|
+
|
|
2707
|
+
class _CodeOrName {
|
|
2708
|
+
}
|
|
2709
|
+
exports._CodeOrName = _CodeOrName;
|
|
2710
|
+
exports.IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
|
|
2711
|
+
|
|
2712
|
+
class Name extends _CodeOrName {
|
|
2713
|
+
constructor(s) {
|
|
2714
|
+
super();
|
|
2715
|
+
if (!exports.IDENTIFIER.test(s))
|
|
2716
|
+
throw new Error("CodeGen: name must be a valid identifier");
|
|
2717
|
+
this.str = s;
|
|
2718
|
+
}
|
|
2719
|
+
toString() {
|
|
2720
|
+
return this.str;
|
|
2721
|
+
}
|
|
2722
|
+
emptyStr() {
|
|
2723
|
+
return false;
|
|
2724
|
+
}
|
|
2725
|
+
get names() {
|
|
2726
|
+
return { [this.str]: 1 };
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
exports.Name = Name;
|
|
2730
|
+
|
|
2731
|
+
class _Code extends _CodeOrName {
|
|
2732
|
+
constructor(code) {
|
|
2733
|
+
super();
|
|
2734
|
+
this._items = typeof code === "string" ? [code] : code;
|
|
2735
|
+
}
|
|
2736
|
+
toString() {
|
|
2737
|
+
return this.str;
|
|
2738
|
+
}
|
|
2739
|
+
emptyStr() {
|
|
2740
|
+
if (this._items.length > 1)
|
|
2741
|
+
return false;
|
|
2742
|
+
const item = this._items[0];
|
|
2743
|
+
return item === "" || item === '""';
|
|
2744
|
+
}
|
|
2745
|
+
get str() {
|
|
2746
|
+
var _a2;
|
|
2747
|
+
return (_a2 = this._str) !== null && _a2 !== undefined ? _a2 : this._str = this._items.reduce((s, c) => `${s}${c}`, "");
|
|
2748
|
+
}
|
|
2749
|
+
get names() {
|
|
2750
|
+
var _a2;
|
|
2751
|
+
return (_a2 = this._names) !== null && _a2 !== undefined ? _a2 : this._names = this._items.reduce((names, c) => {
|
|
2752
|
+
if (c instanceof Name)
|
|
2753
|
+
names[c.str] = (names[c.str] || 0) + 1;
|
|
2754
|
+
return names;
|
|
2755
|
+
}, {});
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
exports._Code = _Code;
|
|
2759
|
+
exports.nil = new _Code("");
|
|
2760
|
+
function _(strs, ...args) {
|
|
2761
|
+
const code = [strs[0]];
|
|
2762
|
+
let i = 0;
|
|
2763
|
+
while (i < args.length) {
|
|
2764
|
+
addCodeArg(code, args[i]);
|
|
2765
|
+
code.push(strs[++i]);
|
|
2766
|
+
}
|
|
2767
|
+
return new _Code(code);
|
|
2768
|
+
}
|
|
2769
|
+
exports._ = _;
|
|
2770
|
+
var plus = new _Code("+");
|
|
2771
|
+
function str(strs, ...args) {
|
|
2772
|
+
const expr = [safeStringify(strs[0])];
|
|
2773
|
+
let i = 0;
|
|
2774
|
+
while (i < args.length) {
|
|
2775
|
+
expr.push(plus);
|
|
2776
|
+
addCodeArg(expr, args[i]);
|
|
2777
|
+
expr.push(plus, safeStringify(strs[++i]));
|
|
2778
|
+
}
|
|
2779
|
+
optimize(expr);
|
|
2780
|
+
return new _Code(expr);
|
|
2781
|
+
}
|
|
2782
|
+
exports.str = str;
|
|
2783
|
+
function addCodeArg(code, arg) {
|
|
2784
|
+
if (arg instanceof _Code)
|
|
2785
|
+
code.push(...arg._items);
|
|
2786
|
+
else if (arg instanceof Name)
|
|
2787
|
+
code.push(arg);
|
|
2788
|
+
else
|
|
2789
|
+
code.push(interpolate(arg));
|
|
2790
|
+
}
|
|
2791
|
+
exports.addCodeArg = addCodeArg;
|
|
2792
|
+
function optimize(expr) {
|
|
2793
|
+
let i = 1;
|
|
2794
|
+
while (i < expr.length - 1) {
|
|
2795
|
+
if (expr[i] === plus) {
|
|
2796
|
+
const res = mergeExprItems(expr[i - 1], expr[i + 1]);
|
|
2797
|
+
if (res !== undefined) {
|
|
2798
|
+
expr.splice(i - 1, 3, res);
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
expr[i++] = "+";
|
|
2802
|
+
}
|
|
2803
|
+
i++;
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
function mergeExprItems(a, b) {
|
|
2807
|
+
if (b === '""')
|
|
2808
|
+
return a;
|
|
2809
|
+
if (a === '""')
|
|
2810
|
+
return b;
|
|
2811
|
+
if (typeof a == "string") {
|
|
2812
|
+
if (b instanceof Name || a[a.length - 1] !== '"')
|
|
2813
|
+
return;
|
|
2814
|
+
if (typeof b != "string")
|
|
2815
|
+
return `${a.slice(0, -1)}${b}"`;
|
|
2816
|
+
if (b[0] === '"')
|
|
2817
|
+
return a.slice(0, -1) + b.slice(1);
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
if (typeof b == "string" && b[0] === '"' && !(a instanceof Name))
|
|
2821
|
+
return `"${a}${b.slice(1)}`;
|
|
2822
|
+
return;
|
|
2823
|
+
}
|
|
2824
|
+
function strConcat(c1, c2) {
|
|
2825
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str`${c1}${c2}`;
|
|
2826
|
+
}
|
|
2827
|
+
exports.strConcat = strConcat;
|
|
2828
|
+
function interpolate(x) {
|
|
2829
|
+
return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
|
|
2830
|
+
}
|
|
2831
|
+
function stringify(x) {
|
|
2832
|
+
return new _Code(safeStringify(x));
|
|
2833
|
+
}
|
|
2834
|
+
exports.stringify = stringify;
|
|
2835
|
+
function safeStringify(x) {
|
|
2836
|
+
return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
2837
|
+
}
|
|
2838
|
+
exports.safeStringify = safeStringify;
|
|
2839
|
+
function getProperty(key) {
|
|
2840
|
+
return typeof key == "string" && exports.IDENTIFIER.test(key) ? new _Code(`.${key}`) : _`[${key}]`;
|
|
2841
|
+
}
|
|
2842
|
+
exports.getProperty = getProperty;
|
|
2843
|
+
function getEsmExportName(key) {
|
|
2844
|
+
if (typeof key == "string" && exports.IDENTIFIER.test(key)) {
|
|
2845
|
+
return new _Code(`${key}`);
|
|
2846
|
+
}
|
|
2847
|
+
throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`);
|
|
2848
|
+
}
|
|
2849
|
+
exports.getEsmExportName = getEsmExportName;
|
|
2850
|
+
function regexpCode(rx) {
|
|
2851
|
+
return new _Code(rx.toString());
|
|
2852
|
+
}
|
|
2853
|
+
exports.regexpCode = regexpCode;
|
|
2854
|
+
});
|
|
2855
|
+
|
|
2856
|
+
// ../../node_modules/ajv/dist/compile/codegen/scope.js
|
|
2857
|
+
var require_scope = __commonJS((exports) => {
|
|
2858
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2859
|
+
exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = undefined;
|
|
2860
|
+
var code_1 = require_code();
|
|
2861
|
+
|
|
2862
|
+
class ValueError extends Error {
|
|
2863
|
+
constructor(name) {
|
|
2864
|
+
super(`CodeGen: "code" for ${name} not defined`);
|
|
2865
|
+
this.value = name.value;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
var UsedValueState;
|
|
2869
|
+
(function(UsedValueState2) {
|
|
2870
|
+
UsedValueState2[UsedValueState2["Started"] = 0] = "Started";
|
|
2871
|
+
UsedValueState2[UsedValueState2["Completed"] = 1] = "Completed";
|
|
2872
|
+
})(UsedValueState || (exports.UsedValueState = UsedValueState = {}));
|
|
2873
|
+
exports.varKinds = {
|
|
2874
|
+
const: new code_1.Name("const"),
|
|
2875
|
+
let: new code_1.Name("let"),
|
|
2876
|
+
var: new code_1.Name("var")
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2879
|
+
class Scope {
|
|
2880
|
+
constructor({ prefixes, parent } = {}) {
|
|
2881
|
+
this._names = {};
|
|
2882
|
+
this._prefixes = prefixes;
|
|
2883
|
+
this._parent = parent;
|
|
2884
|
+
}
|
|
2885
|
+
toName(nameOrPrefix) {
|
|
2886
|
+
return nameOrPrefix instanceof code_1.Name ? nameOrPrefix : this.name(nameOrPrefix);
|
|
2887
|
+
}
|
|
2888
|
+
name(prefix) {
|
|
2889
|
+
return new code_1.Name(this._newName(prefix));
|
|
2890
|
+
}
|
|
2891
|
+
_newName(prefix) {
|
|
2892
|
+
const ng = this._names[prefix] || this._nameGroup(prefix);
|
|
2893
|
+
return `${prefix}${ng.index++}`;
|
|
2894
|
+
}
|
|
2895
|
+
_nameGroup(prefix) {
|
|
2896
|
+
var _a2, _b;
|
|
2897
|
+
if (((_b = (_a2 = this._parent) === null || _a2 === undefined ? undefined : _a2._prefixes) === null || _b === undefined ? undefined : _b.has(prefix)) || this._prefixes && !this._prefixes.has(prefix)) {
|
|
2898
|
+
throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`);
|
|
2899
|
+
}
|
|
2322
2900
|
return this._names[prefix] = { prefix, index: 0 };
|
|
2323
2901
|
}
|
|
2324
2902
|
}
|
|
@@ -8452,136 +9030,1083 @@ var require_formats = __commonJS((exports) => {
|
|
|
8452
9030
|
BYTE.lastIndex = 0;
|
|
8453
9031
|
return BYTE.test(str);
|
|
8454
9032
|
}
|
|
8455
|
-
var MIN_INT32 = -(2 ** 31);
|
|
8456
|
-
var MAX_INT32 = 2 ** 31 - 1;
|
|
8457
|
-
function validateInt32(value) {
|
|
8458
|
-
return Number.isInteger(value) && value <= MAX_INT32 && value >= MIN_INT32;
|
|
9033
|
+
var MIN_INT32 = -(2 ** 31);
|
|
9034
|
+
var MAX_INT32 = 2 ** 31 - 1;
|
|
9035
|
+
function validateInt32(value) {
|
|
9036
|
+
return Number.isInteger(value) && value <= MAX_INT32 && value >= MIN_INT32;
|
|
9037
|
+
}
|
|
9038
|
+
function validateInt64(value) {
|
|
9039
|
+
return Number.isInteger(value);
|
|
9040
|
+
}
|
|
9041
|
+
function validateNumber() {
|
|
9042
|
+
return true;
|
|
9043
|
+
}
|
|
9044
|
+
var Z_ANCHOR = /[^\\]\\Z/;
|
|
9045
|
+
function regex(str) {
|
|
9046
|
+
if (Z_ANCHOR.test(str))
|
|
9047
|
+
return false;
|
|
9048
|
+
try {
|
|
9049
|
+
new RegExp(str);
|
|
9050
|
+
return true;
|
|
9051
|
+
} catch (e) {
|
|
9052
|
+
return false;
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
});
|
|
9056
|
+
|
|
9057
|
+
// ../../node_modules/ajv-formats/dist/limit.js
|
|
9058
|
+
var require_limit = __commonJS((exports) => {
|
|
9059
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9060
|
+
exports.formatLimitDefinition = undefined;
|
|
9061
|
+
var ajv_1 = require_ajv();
|
|
9062
|
+
var codegen_1 = require_codegen();
|
|
9063
|
+
var ops = codegen_1.operators;
|
|
9064
|
+
var KWDs = {
|
|
9065
|
+
formatMaximum: { okStr: "<=", ok: ops.LTE, fail: ops.GT },
|
|
9066
|
+
formatMinimum: { okStr: ">=", ok: ops.GTE, fail: ops.LT },
|
|
9067
|
+
formatExclusiveMaximum: { okStr: "<", ok: ops.LT, fail: ops.GTE },
|
|
9068
|
+
formatExclusiveMinimum: { okStr: ">", ok: ops.GT, fail: ops.LTE }
|
|
9069
|
+
};
|
|
9070
|
+
var error48 = {
|
|
9071
|
+
message: ({ keyword, schemaCode }) => (0, codegen_1.str)`should be ${KWDs[keyword].okStr} ${schemaCode}`,
|
|
9072
|
+
params: ({ keyword, schemaCode }) => (0, codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}`
|
|
9073
|
+
};
|
|
9074
|
+
exports.formatLimitDefinition = {
|
|
9075
|
+
keyword: Object.keys(KWDs),
|
|
9076
|
+
type: "string",
|
|
9077
|
+
schemaType: "string",
|
|
9078
|
+
$data: true,
|
|
9079
|
+
error: error48,
|
|
9080
|
+
code(cxt) {
|
|
9081
|
+
const { gen, data, schemaCode, keyword, it } = cxt;
|
|
9082
|
+
const { opts, self } = it;
|
|
9083
|
+
if (!opts.validateFormats)
|
|
9084
|
+
return;
|
|
9085
|
+
const fCxt = new ajv_1.KeywordCxt(it, self.RULES.all.format.definition, "format");
|
|
9086
|
+
if (fCxt.$data)
|
|
9087
|
+
validate$DataFormat();
|
|
9088
|
+
else
|
|
9089
|
+
validateFormat();
|
|
9090
|
+
function validate$DataFormat() {
|
|
9091
|
+
const fmts = gen.scopeValue("formats", {
|
|
9092
|
+
ref: self.formats,
|
|
9093
|
+
code: opts.code.formats
|
|
9094
|
+
});
|
|
9095
|
+
const fmt = gen.const("fmt", (0, codegen_1._)`${fmts}[${fCxt.schemaCode}]`);
|
|
9096
|
+
cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt} != "object"`, (0, codegen_1._)`${fmt} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt}.compare != "function"`, compareCode(fmt)));
|
|
9097
|
+
}
|
|
9098
|
+
function validateFormat() {
|
|
9099
|
+
const format = fCxt.schema;
|
|
9100
|
+
const fmtDef = self.formats[format];
|
|
9101
|
+
if (!fmtDef || fmtDef === true)
|
|
9102
|
+
return;
|
|
9103
|
+
if (typeof fmtDef != "object" || fmtDef instanceof RegExp || typeof fmtDef.compare != "function") {
|
|
9104
|
+
throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
|
|
9105
|
+
}
|
|
9106
|
+
const fmt = gen.scopeValue("formats", {
|
|
9107
|
+
key: format,
|
|
9108
|
+
ref: fmtDef,
|
|
9109
|
+
code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format)}` : undefined
|
|
9110
|
+
});
|
|
9111
|
+
cxt.fail$data(compareCode(fmt));
|
|
9112
|
+
}
|
|
9113
|
+
function compareCode(fmt) {
|
|
9114
|
+
return (0, codegen_1._)`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`;
|
|
9115
|
+
}
|
|
9116
|
+
},
|
|
9117
|
+
dependencies: ["format"]
|
|
9118
|
+
};
|
|
9119
|
+
var formatLimitPlugin = (ajv) => {
|
|
9120
|
+
ajv.addKeyword(exports.formatLimitDefinition);
|
|
9121
|
+
return ajv;
|
|
9122
|
+
};
|
|
9123
|
+
exports.default = formatLimitPlugin;
|
|
9124
|
+
});
|
|
9125
|
+
|
|
9126
|
+
// ../../node_modules/ajv-formats/dist/index.js
|
|
9127
|
+
var require_dist = __commonJS((exports, module) => {
|
|
9128
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9129
|
+
var formats_1 = require_formats();
|
|
9130
|
+
var limit_1 = require_limit();
|
|
9131
|
+
var codegen_1 = require_codegen();
|
|
9132
|
+
var fullName = new codegen_1.Name("fullFormats");
|
|
9133
|
+
var fastName = new codegen_1.Name("fastFormats");
|
|
9134
|
+
var formatsPlugin = (ajv, opts = { keywords: true }) => {
|
|
9135
|
+
if (Array.isArray(opts)) {
|
|
9136
|
+
addFormats(ajv, opts, formats_1.fullFormats, fullName);
|
|
9137
|
+
return ajv;
|
|
9138
|
+
}
|
|
9139
|
+
const [formats, exportName] = opts.mode === "fast" ? [formats_1.fastFormats, fastName] : [formats_1.fullFormats, fullName];
|
|
9140
|
+
const list = opts.formats || formats_1.formatNames;
|
|
9141
|
+
addFormats(ajv, list, formats, exportName);
|
|
9142
|
+
if (opts.keywords)
|
|
9143
|
+
(0, limit_1.default)(ajv);
|
|
9144
|
+
return ajv;
|
|
9145
|
+
};
|
|
9146
|
+
formatsPlugin.get = (name, mode = "full") => {
|
|
9147
|
+
const formats = mode === "fast" ? formats_1.fastFormats : formats_1.fullFormats;
|
|
9148
|
+
const f = formats[name];
|
|
9149
|
+
if (!f)
|
|
9150
|
+
throw new Error(`Unknown format "${name}"`);
|
|
9151
|
+
return f;
|
|
9152
|
+
};
|
|
9153
|
+
function addFormats(ajv, list, fs, exportName) {
|
|
9154
|
+
var _a2;
|
|
9155
|
+
var _b;
|
|
9156
|
+
(_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== undefined || (_b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`);
|
|
9157
|
+
for (const f of list)
|
|
9158
|
+
ajv.addFormat(f, fs[f]);
|
|
9159
|
+
}
|
|
9160
|
+
module.exports = exports = formatsPlugin;
|
|
9161
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9162
|
+
exports.default = formatsPlugin;
|
|
9163
|
+
});
|
|
9164
|
+
|
|
9165
|
+
// src/context-assembly.ts
|
|
9166
|
+
var exports_context_assembly = {};
|
|
9167
|
+
__export(exports_context_assembly, {
|
|
9168
|
+
trackSessionAssembly: () => trackSessionAssembly,
|
|
9169
|
+
recordContextFeedback: () => recordContextFeedback,
|
|
9170
|
+
mapToContextEntity: () => mapToContextEntity,
|
|
9171
|
+
getSessionAssemblyId: () => getSessionAssemblyId,
|
|
9172
|
+
getCachedManifest: () => getCachedManifest,
|
|
9173
|
+
computeRelevanceScore: () => computeRelevanceScore,
|
|
9174
|
+
cacheManifest: () => cacheManifest,
|
|
9175
|
+
assembleContext: () => assembleContext
|
|
9176
|
+
});
|
|
9177
|
+
function estimateTokens(text) {
|
|
9178
|
+
return Math.ceil(text.length / 4);
|
|
9179
|
+
}
|
|
9180
|
+
function generateAssemblyId() {
|
|
9181
|
+
return `ctx_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
9182
|
+
}
|
|
9183
|
+
function truncateContent(content, maxTokens) {
|
|
9184
|
+
const currentTokens = estimateTokens(content);
|
|
9185
|
+
if (currentTokens <= maxTokens) {
|
|
9186
|
+
return { text: content, truncated: false };
|
|
9187
|
+
}
|
|
9188
|
+
const paragraphs = content.split(/\n\n+/);
|
|
9189
|
+
let result = paragraphs[0];
|
|
9190
|
+
for (let i = 1;i < paragraphs.length; i++) {
|
|
9191
|
+
const lines = paragraphs[i].split(`
|
|
9192
|
+
`).filter((l) => l.startsWith("- ") || l.startsWith("* "));
|
|
9193
|
+
if (lines.length > 0) {
|
|
9194
|
+
const bulletSection = lines.join(`
|
|
9195
|
+
`);
|
|
9196
|
+
if (estimateTokens(result + `
|
|
9197
|
+
|
|
9198
|
+
` + bulletSection) <= maxTokens) {
|
|
9199
|
+
result += `
|
|
9200
|
+
|
|
9201
|
+
` + bulletSection;
|
|
9202
|
+
}
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
if (estimateTokens(result) > maxTokens) {
|
|
9206
|
+
const maxChars = maxTokens * 4;
|
|
9207
|
+
result = result.slice(0, maxChars - 3) + "...";
|
|
9208
|
+
}
|
|
9209
|
+
return { text: result, truncated: true };
|
|
9210
|
+
}
|
|
9211
|
+
function computeRelevanceScore(entity, taskContext, cardLabels) {
|
|
9212
|
+
const reasons = [];
|
|
9213
|
+
let score = 0;
|
|
9214
|
+
const hasRrfScore = entity.rrf_score !== undefined && entity.rrf_score > 0;
|
|
9215
|
+
if (hasRrfScore) {
|
|
9216
|
+
const normalizedRrf = Math.min(entity.rrf_score / 0.04, 1);
|
|
9217
|
+
const rrfContribution = normalizedRrf * 0.3;
|
|
9218
|
+
score += rrfContribution;
|
|
9219
|
+
reasons.push(`hybrid_search(rrf=${entity.rrf_score.toFixed(4)})`);
|
|
9220
|
+
}
|
|
9221
|
+
const textMatchWeight = hasRrfScore ? 0.15 : 0.4;
|
|
9222
|
+
const taskWords = new Set(taskContext.toLowerCase().split(/\W+/).filter((w) => w.length > 2));
|
|
9223
|
+
const entityWords = new Set(`${entity.title} ${entity.content}`.toLowerCase().split(/\W+/).filter((w) => w.length > 2));
|
|
9224
|
+
const overlap = [...taskWords].filter((w) => entityWords.has(w));
|
|
9225
|
+
if (overlap.length > 0) {
|
|
9226
|
+
const textScore = Math.min(overlap.length / Math.max(taskWords.size, 1), 1) * textMatchWeight;
|
|
9227
|
+
score += textScore;
|
|
9228
|
+
reasons.push(`text_match(${overlap.length} words)`);
|
|
9229
|
+
}
|
|
9230
|
+
if (cardLabels.length > 0 && entity.tags.length > 0) {
|
|
9231
|
+
const labelSet = new Set(cardLabels.map((l) => l.toLowerCase()));
|
|
9232
|
+
const tagOverlap = entity.tags.filter((t) => labelSet.has(t.toLowerCase()));
|
|
9233
|
+
if (tagOverlap.length > 0) {
|
|
9234
|
+
const tagScore = tagOverlap.length / cardLabels.length * 0.3;
|
|
9235
|
+
score += tagScore;
|
|
9236
|
+
reasons.push(`tag_match(${tagOverlap.join(",")})`);
|
|
9237
|
+
}
|
|
9238
|
+
}
|
|
9239
|
+
score += entity.confidence * 0.15;
|
|
9240
|
+
if (entity.confidence >= 0.9) {
|
|
9241
|
+
reasons.push("high_confidence");
|
|
9242
|
+
}
|
|
9243
|
+
if (entity.last_accessed_at) {
|
|
9244
|
+
const daysSinceAccess = (Date.now() - new Date(entity.last_accessed_at).getTime()) / (1000 * 60 * 60 * 24);
|
|
9245
|
+
const halfLife = { draft: 7, episode: 30, reference: 180 }[entity.memory_tier];
|
|
9246
|
+
const recencyScore = 0.5 ** (daysSinceAccess / halfLife) * 0.1;
|
|
9247
|
+
score += recencyScore;
|
|
9248
|
+
if (daysSinceAccess < 7)
|
|
9249
|
+
reasons.push("recently_accessed");
|
|
9250
|
+
}
|
|
9251
|
+
if (entity.access_count > 0) {
|
|
9252
|
+
const freqScore = Math.log10(entity.access_count + 1) * 0.05;
|
|
9253
|
+
score += Math.min(freqScore, 0.1);
|
|
9254
|
+
if (entity.access_count >= 5)
|
|
9255
|
+
reasons.push(`frequently_used(${entity.access_count})`);
|
|
9256
|
+
}
|
|
9257
|
+
const usefulnessScore = entity.metadata?.usefulness_score ?? 0;
|
|
9258
|
+
if (usefulnessScore >= 3) {
|
|
9259
|
+
const usefulnessBoost = Math.min(usefulnessScore / 20, 0.15);
|
|
9260
|
+
score += usefulnessBoost;
|
|
9261
|
+
reasons.push(`useful(${usefulnessScore})`);
|
|
9262
|
+
} else if (usefulnessScore === 0 && entity.access_count >= 5) {
|
|
9263
|
+
score -= 0.02;
|
|
9264
|
+
reasons.push("low_usefulness");
|
|
9265
|
+
}
|
|
9266
|
+
if (entity.type === "procedure") {
|
|
9267
|
+
score += 0.1;
|
|
9268
|
+
reasons.push("procedure_boost");
|
|
9269
|
+
}
|
|
9270
|
+
score = Math.min(score, 1);
|
|
9271
|
+
const tierWeight = TIER_WEIGHTS[entity.memory_tier];
|
|
9272
|
+
score *= tierWeight;
|
|
9273
|
+
return { score, reasons };
|
|
9274
|
+
}
|
|
9275
|
+
async function assembleContext(options) {
|
|
9276
|
+
const {
|
|
9277
|
+
workspaceId,
|
|
9278
|
+
projectId,
|
|
9279
|
+
taskContext,
|
|
9280
|
+
cardLabels = [],
|
|
9281
|
+
tokenBudget = DEFAULT_TOKEN_BUDGET,
|
|
9282
|
+
client: client2
|
|
9283
|
+
} = options;
|
|
9284
|
+
const assemblyId = generateAssemblyId();
|
|
9285
|
+
const manifest = {
|
|
9286
|
+
assemblyId,
|
|
9287
|
+
timestamp: new Date().toISOString(),
|
|
9288
|
+
included: [],
|
|
9289
|
+
excluded: [],
|
|
9290
|
+
budgetUsed: 0,
|
|
9291
|
+
budgetTotal: tokenBudget,
|
|
9292
|
+
tierBreakdown: {
|
|
9293
|
+
draft: { count: 0, tokens: 0 },
|
|
9294
|
+
episode: { count: 0, tokens: 0 },
|
|
9295
|
+
reference: { count: 0, tokens: 0 }
|
|
9296
|
+
}
|
|
9297
|
+
};
|
|
9298
|
+
let candidates = [];
|
|
9299
|
+
try {
|
|
9300
|
+
const searchResult = await client2.searchMemoryEntities(workspaceId, taskContext, { project_id: projectId, limit: 30 });
|
|
9301
|
+
if (searchResult.entities?.length > 0) {
|
|
9302
|
+
candidates = searchResult.entities.map(mapToContextEntity);
|
|
9303
|
+
}
|
|
9304
|
+
} catch {}
|
|
9305
|
+
if (candidates.length < 10 && projectId) {
|
|
9306
|
+
try {
|
|
9307
|
+
const listResult = await client2.listMemoryEntities({
|
|
9308
|
+
workspace_id: workspaceId,
|
|
9309
|
+
project_id: projectId,
|
|
9310
|
+
limit: 30
|
|
9311
|
+
});
|
|
9312
|
+
if (listResult.entities?.length > 0) {
|
|
9313
|
+
const existingIds = new Set(candidates.map((c) => c.id));
|
|
9314
|
+
const additional = listResult.entities.map(mapToContextEntity).filter((e) => !existingIds.has(e.id));
|
|
9315
|
+
candidates.push(...additional);
|
|
9316
|
+
}
|
|
9317
|
+
} catch {}
|
|
9318
|
+
}
|
|
9319
|
+
if (candidates.length < 20) {
|
|
9320
|
+
try {
|
|
9321
|
+
const wsResult = await client2.listMemoryEntities({
|
|
9322
|
+
workspace_id: workspaceId,
|
|
9323
|
+
scope: "workspace",
|
|
9324
|
+
limit: 20
|
|
9325
|
+
});
|
|
9326
|
+
if (wsResult.entities?.length > 0) {
|
|
9327
|
+
const existingIds = new Set(candidates.map((c) => c.id));
|
|
9328
|
+
const additional = wsResult.entities.map(mapToContextEntity).filter((e) => !existingIds.has(e.id));
|
|
9329
|
+
candidates.push(...additional);
|
|
9330
|
+
}
|
|
9331
|
+
} catch {}
|
|
9332
|
+
}
|
|
9333
|
+
if (candidates.length === 0) {
|
|
9334
|
+
return {
|
|
9335
|
+
context: "",
|
|
9336
|
+
manifest,
|
|
9337
|
+
memories: []
|
|
9338
|
+
};
|
|
9339
|
+
}
|
|
9340
|
+
const scored = candidates.map((entity) => {
|
|
9341
|
+
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels);
|
|
9342
|
+
return { entity, score, reasons };
|
|
9343
|
+
});
|
|
9344
|
+
scored.sort((a, b) => b.score - a.score);
|
|
9345
|
+
const procedureBudget = Math.floor(tokenBudget * PROCEDURE_BUDGET_FRACTION);
|
|
9346
|
+
const remainingBudget = tokenBudget - procedureBudget;
|
|
9347
|
+
const tierBudgets = {
|
|
9348
|
+
reference: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.reference),
|
|
9349
|
+
episode: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.episode),
|
|
9350
|
+
draft: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.draft)
|
|
9351
|
+
};
|
|
9352
|
+
const tierUsed = {
|
|
9353
|
+
reference: 0,
|
|
9354
|
+
episode: 0,
|
|
9355
|
+
draft: 0
|
|
9356
|
+
};
|
|
9357
|
+
let procedureUsed = 0;
|
|
9358
|
+
const included = [];
|
|
9359
|
+
let totalUsed = 0;
|
|
9360
|
+
let referenceCount = 0;
|
|
9361
|
+
for (const item of scored) {
|
|
9362
|
+
if (item.entity.memory_tier === "reference" && item.entity.type !== "procedure" && referenceCount < MIN_REFERENCE_SLOTS) {
|
|
9363
|
+
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
9364
|
+
const tokens = estimateTokens(`### ${item.entity.title}
|
|
9365
|
+
${text}`);
|
|
9366
|
+
if (totalUsed + tokens <= tokenBudget) {
|
|
9367
|
+
included.push({ ...item, tokens, truncated });
|
|
9368
|
+
item.entity.content = text;
|
|
9369
|
+
totalUsed += tokens;
|
|
9370
|
+
tierUsed.reference += tokens;
|
|
9371
|
+
referenceCount++;
|
|
9372
|
+
}
|
|
9373
|
+
}
|
|
9374
|
+
}
|
|
9375
|
+
const includedIds = new Set(included.map((i) => i.entity.id));
|
|
9376
|
+
const procedureCandidates = scored.filter((item) => item.entity.type === "procedure" && !includedIds.has(item.entity.id));
|
|
9377
|
+
for (const item of procedureCandidates) {
|
|
9378
|
+
if (item.score < MIN_RELEVANCE_THRESHOLD) {
|
|
9379
|
+
manifest.excluded.push({
|
|
9380
|
+
entityId: item.entity.id,
|
|
9381
|
+
title: item.entity.title,
|
|
9382
|
+
type: item.entity.type,
|
|
9383
|
+
tier: item.entity.memory_tier,
|
|
9384
|
+
relevanceScore: item.score,
|
|
9385
|
+
reason: "below_relevance_threshold"
|
|
9386
|
+
});
|
|
9387
|
+
continue;
|
|
9388
|
+
}
|
|
9389
|
+
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
9390
|
+
const tokens = estimateTokens(`### ${item.entity.title}
|
|
9391
|
+
${text}`);
|
|
9392
|
+
if (procedureUsed + tokens > procedureBudget) {
|
|
9393
|
+
const totalRemaining = tokenBudget - totalUsed;
|
|
9394
|
+
if (tokens > totalRemaining) {
|
|
9395
|
+
manifest.excluded.push({
|
|
9396
|
+
entityId: item.entity.id,
|
|
9397
|
+
title: item.entity.title,
|
|
9398
|
+
type: item.entity.type,
|
|
9399
|
+
tier: item.entity.memory_tier,
|
|
9400
|
+
relevanceScore: item.score,
|
|
9401
|
+
reason: "procedure_budget_exceeded"
|
|
9402
|
+
});
|
|
9403
|
+
continue;
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
if (totalUsed + tokens > tokenBudget) {
|
|
9407
|
+
manifest.excluded.push({
|
|
9408
|
+
entityId: item.entity.id,
|
|
9409
|
+
title: item.entity.title,
|
|
9410
|
+
type: item.entity.type,
|
|
9411
|
+
tier: item.entity.memory_tier,
|
|
9412
|
+
relevanceScore: item.score,
|
|
9413
|
+
reason: "total_budget_exceeded"
|
|
9414
|
+
});
|
|
9415
|
+
continue;
|
|
9416
|
+
}
|
|
9417
|
+
included.push({ ...item, tokens, truncated });
|
|
9418
|
+
item.entity.content = text;
|
|
9419
|
+
totalUsed += tokens;
|
|
9420
|
+
procedureUsed += tokens;
|
|
9421
|
+
includedIds.add(item.entity.id);
|
|
9422
|
+
}
|
|
9423
|
+
for (const item of scored) {
|
|
9424
|
+
if (includedIds.has(item.entity.id))
|
|
9425
|
+
continue;
|
|
9426
|
+
if (item.entity.type === "procedure")
|
|
9427
|
+
continue;
|
|
9428
|
+
if (item.score < MIN_RELEVANCE_THRESHOLD) {
|
|
9429
|
+
manifest.excluded.push({
|
|
9430
|
+
entityId: item.entity.id,
|
|
9431
|
+
title: item.entity.title,
|
|
9432
|
+
type: item.entity.type,
|
|
9433
|
+
tier: item.entity.memory_tier,
|
|
9434
|
+
relevanceScore: item.score,
|
|
9435
|
+
reason: "below_relevance_threshold"
|
|
9436
|
+
});
|
|
9437
|
+
continue;
|
|
9438
|
+
}
|
|
9439
|
+
const tier = item.entity.memory_tier;
|
|
9440
|
+
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
9441
|
+
const tokens = estimateTokens(`### ${item.entity.title}
|
|
9442
|
+
${text}`);
|
|
9443
|
+
if (tierUsed[tier] + tokens > tierBudgets[tier]) {
|
|
9444
|
+
const totalRemaining = tokenBudget - totalUsed;
|
|
9445
|
+
if (tokens > totalRemaining) {
|
|
9446
|
+
manifest.excluded.push({
|
|
9447
|
+
entityId: item.entity.id,
|
|
9448
|
+
title: item.entity.title,
|
|
9449
|
+
type: item.entity.type,
|
|
9450
|
+
tier,
|
|
9451
|
+
relevanceScore: item.score,
|
|
9452
|
+
reason: "budget_exceeded"
|
|
9453
|
+
});
|
|
9454
|
+
continue;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
if (totalUsed + tokens > tokenBudget) {
|
|
9458
|
+
manifest.excluded.push({
|
|
9459
|
+
entityId: item.entity.id,
|
|
9460
|
+
title: item.entity.title,
|
|
9461
|
+
type: item.entity.type,
|
|
9462
|
+
tier,
|
|
9463
|
+
relevanceScore: item.score,
|
|
9464
|
+
reason: "total_budget_exceeded"
|
|
9465
|
+
});
|
|
9466
|
+
continue;
|
|
9467
|
+
}
|
|
9468
|
+
included.push({ ...item, tokens, truncated });
|
|
9469
|
+
item.entity.content = text;
|
|
9470
|
+
totalUsed += tokens;
|
|
9471
|
+
tierUsed[tier] += tokens;
|
|
9472
|
+
includedIds.add(item.entity.id);
|
|
9473
|
+
}
|
|
9474
|
+
manifest.budgetUsed = totalUsed;
|
|
9475
|
+
const procedureItems = included.filter((i) => i.entity.type === "procedure");
|
|
9476
|
+
manifest.tierBreakdown = {
|
|
9477
|
+
reference: {
|
|
9478
|
+
count: included.filter((i) => i.entity.memory_tier === "reference" && i.entity.type !== "procedure").length,
|
|
9479
|
+
tokens: tierUsed.reference
|
|
9480
|
+
},
|
|
9481
|
+
episode: {
|
|
9482
|
+
count: included.filter((i) => i.entity.memory_tier === "episode" && i.entity.type !== "procedure").length,
|
|
9483
|
+
tokens: tierUsed.episode
|
|
9484
|
+
},
|
|
9485
|
+
draft: {
|
|
9486
|
+
count: included.filter((i) => i.entity.memory_tier === "draft" && i.entity.type !== "procedure").length,
|
|
9487
|
+
tokens: tierUsed.draft
|
|
9488
|
+
}
|
|
9489
|
+
};
|
|
9490
|
+
manifest.procedureBreakdown = {
|
|
9491
|
+
count: procedureItems.length,
|
|
9492
|
+
tokens: procedureUsed,
|
|
9493
|
+
budget: procedureBudget
|
|
9494
|
+
};
|
|
9495
|
+
for (const item of included) {
|
|
9496
|
+
manifest.included.push({
|
|
9497
|
+
entityId: item.entity.id,
|
|
9498
|
+
title: item.entity.title,
|
|
9499
|
+
type: item.entity.type,
|
|
9500
|
+
tier: item.entity.memory_tier,
|
|
9501
|
+
relevanceScore: item.score,
|
|
9502
|
+
reasons: item.reasons,
|
|
9503
|
+
tokenCount: item.tokens,
|
|
9504
|
+
truncated: item.truncated
|
|
9505
|
+
});
|
|
9506
|
+
}
|
|
9507
|
+
const contextSections = [];
|
|
9508
|
+
const nonProcedureItems = included.filter((i) => i.entity.type !== "procedure");
|
|
9509
|
+
if (included.length > 0) {
|
|
9510
|
+
if (procedureItems.length > 0) {
|
|
9511
|
+
contextSections.push(`## Procedures (${procedureItems.length} loaded, ${procedureUsed}/${procedureBudget} tokens)`);
|
|
9512
|
+
for (const item of procedureItems) {
|
|
9513
|
+
const tags = item.entity.tags.length > 0 ? ` [${item.entity.tags.join(", ")}]` : "";
|
|
9514
|
+
const tierLabel = item.entity.memory_tier !== "reference" ? ` (${item.entity.memory_tier})` : "";
|
|
9515
|
+
contextSections.push(`
|
|
9516
|
+
### ${item.entity.title} (confidence: ${item.entity.confidence})${tierLabel}${tags}`);
|
|
9517
|
+
contextSections.push(item.entity.content);
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9520
|
+
if (nonProcedureItems.length > 0) {
|
|
9521
|
+
contextSections.push(`
|
|
9522
|
+
## Relevant Memories (${nonProcedureItems.length} loaded, ${manifest.excluded.length} excluded)`);
|
|
9523
|
+
contextSections.push(`*Assembly: ${assemblyId} | Budget: ${totalUsed}/${tokenBudget} tokens*`);
|
|
9524
|
+
for (const item of nonProcedureItems) {
|
|
9525
|
+
const tags = item.entity.tags.length > 0 ? ` [${item.entity.tags.join(", ")}]` : "";
|
|
9526
|
+
const tierLabel = item.entity.memory_tier !== "reference" ? ` (${item.entity.memory_tier})` : "";
|
|
9527
|
+
contextSections.push(`
|
|
9528
|
+
### ${item.entity.title} (${item.entity.type}, confidence: ${item.entity.confidence})${tierLabel}${tags}`);
|
|
9529
|
+
contextSections.push(item.entity.content);
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9533
|
+
incrementAccessCounts(client2, included.map((i) => i.entity.id)).catch(() => {});
|
|
9534
|
+
promoteEligibleEntities(client2, included.map((i) => i.entity)).catch(() => {});
|
|
9535
|
+
return {
|
|
9536
|
+
context: contextSections.join(`
|
|
9537
|
+
`),
|
|
9538
|
+
manifest,
|
|
9539
|
+
memories: included.map((i) => i.entity)
|
|
9540
|
+
};
|
|
9541
|
+
}
|
|
9542
|
+
function mapToContextEntity(raw) {
|
|
9543
|
+
const e = raw;
|
|
9544
|
+
return {
|
|
9545
|
+
id: e.id,
|
|
9546
|
+
type: e.type,
|
|
9547
|
+
title: e.title,
|
|
9548
|
+
content: e.content,
|
|
9549
|
+
confidence: e.confidence ?? 1,
|
|
9550
|
+
tags: e.tags || [],
|
|
9551
|
+
memory_tier: e.memory_tier || "reference",
|
|
9552
|
+
access_count: e.access_count || 0,
|
|
9553
|
+
last_accessed_at: e.last_accessed_at || null,
|
|
9554
|
+
created_at: e.created_at || "",
|
|
9555
|
+
updated_at: e.updated_at || "",
|
|
9556
|
+
metadata: e.metadata ?? undefined,
|
|
9557
|
+
rrf_score: e.rrf_score ?? undefined,
|
|
9558
|
+
fts_rank: e.fts_rank ?? undefined,
|
|
9559
|
+
semantic_rank: e.semantic_rank ?? undefined
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
9562
|
+
async function incrementAccessCounts(client2, entityIds) {
|
|
9563
|
+
if (entityIds.length === 0)
|
|
9564
|
+
return;
|
|
9565
|
+
try {
|
|
9566
|
+
await client2.batchTouchMemoryEntities(entityIds);
|
|
9567
|
+
} catch {
|
|
9568
|
+
await Promise.allSettled(entityIds.map((id) => client2.touchMemoryEntity(id)));
|
|
9569
|
+
}
|
|
9570
|
+
}
|
|
9571
|
+
async function promoteEligibleEntities(client2, entities) {
|
|
9572
|
+
for (const entity of entities) {
|
|
9573
|
+
if (entity.memory_tier === "reference")
|
|
9574
|
+
continue;
|
|
9575
|
+
if (!entity.created_at)
|
|
9576
|
+
continue;
|
|
9577
|
+
const promotion = checkPromotion(entity.memory_tier, entity.access_count + 1, entity.confidence, entity.created_at);
|
|
9578
|
+
if (promotion.eligible && promotion.targetTier) {
|
|
9579
|
+
try {
|
|
9580
|
+
await client2.updateMemoryEntity(entity.id, {
|
|
9581
|
+
memory_tier: promotion.targetTier,
|
|
9582
|
+
metadata: {
|
|
9583
|
+
...entity.metadata || {},
|
|
9584
|
+
promoted_at: new Date().toISOString(),
|
|
9585
|
+
promotion_reason: promotion.reason,
|
|
9586
|
+
promoted_from: entity.memory_tier
|
|
9587
|
+
}
|
|
9588
|
+
});
|
|
9589
|
+
} catch {}
|
|
9590
|
+
}
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
function cacheManifest(manifest) {
|
|
9594
|
+
if (manifestCache.size >= MAX_CACHE_SIZE) {
|
|
9595
|
+
const firstKey = manifestCache.keys().next().value;
|
|
9596
|
+
if (firstKey)
|
|
9597
|
+
manifestCache.delete(firstKey);
|
|
9598
|
+
}
|
|
9599
|
+
manifestCache.set(manifest.assemblyId, manifest);
|
|
9600
|
+
}
|
|
9601
|
+
function getCachedManifest(assemblyId) {
|
|
9602
|
+
return manifestCache.get(assemblyId);
|
|
9603
|
+
}
|
|
9604
|
+
function trackSessionAssembly(cardId, assemblyId) {
|
|
9605
|
+
if (sessionAssemblyMap.size >= MAX_SESSION_MAP_SIZE) {
|
|
9606
|
+
const firstKey = sessionAssemblyMap.keys().next().value;
|
|
9607
|
+
if (firstKey)
|
|
9608
|
+
sessionAssemblyMap.delete(firstKey);
|
|
9609
|
+
}
|
|
9610
|
+
sessionAssemblyMap.set(cardId, assemblyId);
|
|
9611
|
+
}
|
|
9612
|
+
function getSessionAssemblyId(cardId) {
|
|
9613
|
+
return sessionAssemblyMap.get(cardId);
|
|
9614
|
+
}
|
|
9615
|
+
async function recordContextFeedback(client2, cardId, sessionStatus, progressPercent, hadBlockers) {
|
|
9616
|
+
const assemblyId = sessionAssemblyMap.get(cardId);
|
|
9617
|
+
if (!assemblyId)
|
|
9618
|
+
return { adjusted: 0 };
|
|
9619
|
+
const manifest = manifestCache.get(assemblyId);
|
|
9620
|
+
if (!manifest || manifest.included.length === 0)
|
|
9621
|
+
return { adjusted: 0 };
|
|
9622
|
+
let adjusted = 0;
|
|
9623
|
+
const isSuccess = sessionStatus === "completed" && (progressPercent ?? 0) >= 100;
|
|
9624
|
+
for (const entry of manifest.included) {
|
|
9625
|
+
try {
|
|
9626
|
+
if (isSuccess) {
|
|
9627
|
+
const { entity } = await client2.getMemoryEntity(entry.entityId);
|
|
9628
|
+
const e = entity;
|
|
9629
|
+
const currentUsefulness = e.metadata?.usefulness_score ?? 0;
|
|
9630
|
+
const newConfidence = Math.min((e.confidence ?? 0.5) + 0.05, 1);
|
|
9631
|
+
await client2.updateMemoryEntity(entry.entityId, {
|
|
9632
|
+
confidence: newConfidence,
|
|
9633
|
+
metadata: {
|
|
9634
|
+
usefulness_score: currentUsefulness + 1,
|
|
9635
|
+
last_feedback_at: new Date().toISOString()
|
|
9636
|
+
}
|
|
9637
|
+
});
|
|
9638
|
+
adjusted++;
|
|
9639
|
+
} else if (hadBlockers) {
|
|
9640
|
+
const { entity } = await client2.getMemoryEntity(entry.entityId);
|
|
9641
|
+
const e = entity;
|
|
9642
|
+
const newConfidence = Math.max((e.confidence ?? 0.5) - 0.02, 0.1);
|
|
9643
|
+
await client2.updateMemoryEntity(entry.entityId, {
|
|
9644
|
+
confidence: newConfidence,
|
|
9645
|
+
metadata: {
|
|
9646
|
+
last_feedback_at: new Date().toISOString()
|
|
9647
|
+
}
|
|
9648
|
+
});
|
|
9649
|
+
adjusted++;
|
|
9650
|
+
}
|
|
9651
|
+
} catch {}
|
|
9652
|
+
}
|
|
9653
|
+
sessionAssemblyMap.delete(cardId);
|
|
9654
|
+
return { adjusted };
|
|
9655
|
+
}
|
|
9656
|
+
var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
|
|
9657
|
+
var init_context_assembly = __esm(() => {
|
|
9658
|
+
init_src();
|
|
9659
|
+
TIER_WEIGHTS = {
|
|
9660
|
+
reference: 1,
|
|
9661
|
+
episode: 0.7,
|
|
9662
|
+
draft: 0.4
|
|
9663
|
+
};
|
|
9664
|
+
TIER_BUDGET_ALLOCATION = {
|
|
9665
|
+
reference: 0.6,
|
|
9666
|
+
episode: 0.3,
|
|
9667
|
+
draft: 0.1
|
|
9668
|
+
};
|
|
9669
|
+
manifestCache = new Map;
|
|
9670
|
+
sessionAssemblyMap = new Map;
|
|
9671
|
+
});
|
|
9672
|
+
|
|
9673
|
+
// src/prompt-builder.ts
|
|
9674
|
+
var exports_prompt_builder = {};
|
|
9675
|
+
__export(exports_prompt_builder, {
|
|
9676
|
+
inferCategoryFromLabels: () => inferCategoryFromLabels,
|
|
9677
|
+
getRoleFraming: () => getRoleFraming,
|
|
9678
|
+
getAvailableVariants: () => getAvailableVariants,
|
|
9679
|
+
getAvailableCategories: () => getAvailableCategories,
|
|
9680
|
+
generatePrompt: () => generatePrompt
|
|
9681
|
+
});
|
|
9682
|
+
function inferCategoryFromLabels(labels) {
|
|
9683
|
+
for (const label of labels) {
|
|
9684
|
+
const normalizedName = label.name.toLowerCase().trim();
|
|
9685
|
+
if (LABEL_CATEGORY_MAP[normalizedName]) {
|
|
9686
|
+
return LABEL_CATEGORY_MAP[normalizedName];
|
|
9687
|
+
}
|
|
9688
|
+
for (const [key, category] of Object.entries(LABEL_CATEGORY_MAP)) {
|
|
9689
|
+
if (normalizedName.includes(key) || key.includes(normalizedName)) {
|
|
9690
|
+
return category;
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9693
|
+
}
|
|
9694
|
+
return "custom";
|
|
9695
|
+
}
|
|
9696
|
+
function getRoleFraming(category) {
|
|
9697
|
+
return DEFAULT_ROLE_FRAMINGS[category];
|
|
9698
|
+
}
|
|
9699
|
+
function estimateTokens2(text) {
|
|
9700
|
+
return Math.ceil(text.length / 4);
|
|
9701
|
+
}
|
|
9702
|
+
function formatSubtasks(subtasks) {
|
|
9703
|
+
if (subtasks.length === 0)
|
|
9704
|
+
return "";
|
|
9705
|
+
const completed = subtasks.filter((s) => s.completed).length;
|
|
9706
|
+
const lines = subtasks.map((s) => ` ${s.completed ? "[x]" : "[ ]"} ${s.title}`);
|
|
9707
|
+
return `
|
|
9708
|
+
## Subtasks (${completed}/${subtasks.length} completed)
|
|
9709
|
+
${lines.join(`
|
|
9710
|
+
`)}`;
|
|
9711
|
+
}
|
|
9712
|
+
function formatLabels(labels) {
|
|
9713
|
+
if (labels.length === 0)
|
|
9714
|
+
return "";
|
|
9715
|
+
return `
|
|
9716
|
+
**Labels:** ${labels.map((l) => l.name).join(", ")}`;
|
|
9717
|
+
}
|
|
9718
|
+
function formatLinkedCards(links) {
|
|
9719
|
+
if (!links || links.length === 0)
|
|
9720
|
+
return "";
|
|
9721
|
+
const lines = links.map((link) => {
|
|
9722
|
+
const prefix = link.direction === "outgoing" ? "->" : "<-";
|
|
9723
|
+
return ` ${prefix} #${link.target_card.short_id}: ${link.target_card.title} (${link.display_type})`;
|
|
9724
|
+
});
|
|
9725
|
+
return `
|
|
9726
|
+
## Related Cards
|
|
9727
|
+
${lines.join(`
|
|
9728
|
+
`)}`;
|
|
9729
|
+
}
|
|
9730
|
+
function generatePrompt(options) {
|
|
9731
|
+
const {
|
|
9732
|
+
card,
|
|
9733
|
+
column,
|
|
9734
|
+
variant,
|
|
9735
|
+
customConstraints,
|
|
9736
|
+
memories,
|
|
9737
|
+
assembledContext,
|
|
9738
|
+
assemblyId
|
|
9739
|
+
} = options;
|
|
9740
|
+
const contextOpts = {
|
|
9741
|
+
includeTitle: true,
|
|
9742
|
+
includeDescription: true,
|
|
9743
|
+
includeLabels: true,
|
|
9744
|
+
includeSubtasks: true,
|
|
9745
|
+
includeActivity: false,
|
|
9746
|
+
includeAssignee: true,
|
|
9747
|
+
includeDueDate: true,
|
|
9748
|
+
includePriority: true,
|
|
9749
|
+
includeLinks: true,
|
|
9750
|
+
includeColumn: true,
|
|
9751
|
+
...options.contextOptions
|
|
9752
|
+
};
|
|
9753
|
+
const labels = card.labels || [];
|
|
9754
|
+
const subtasks = card.subtasks || [];
|
|
9755
|
+
const links = card.links || [];
|
|
9756
|
+
const category = inferCategoryFromLabels(labels);
|
|
9757
|
+
const roleFraming = getRoleFraming(category);
|
|
9758
|
+
const sections = [];
|
|
9759
|
+
sections.push(`# Role: ${roleFraming.role}
|
|
9760
|
+
`);
|
|
9761
|
+
sections.push(roleFraming.perspective);
|
|
9762
|
+
sections.push("");
|
|
9763
|
+
sections.push(VARIANT_INSTRUCTIONS[variant]);
|
|
9764
|
+
sections.push("");
|
|
9765
|
+
sections.push(`# Task: ${card.title}`);
|
|
9766
|
+
if (contextOpts.includeColumn && column) {
|
|
9767
|
+
sections.push(`**Status:** ${column.name}`);
|
|
9768
|
+
}
|
|
9769
|
+
if (contextOpts.includePriority) {
|
|
9770
|
+
sections.push(`**Priority:** ${card.priority}`);
|
|
9771
|
+
}
|
|
9772
|
+
if (contextOpts.includeDueDate && card.due_date) {
|
|
9773
|
+
sections.push(`**Due:** ${card.due_date}`);
|
|
9774
|
+
}
|
|
9775
|
+
if (contextOpts.includeAssignee && card.assignee) {
|
|
9776
|
+
sections.push(`**Assignee:** ${card.assignee.full_name || card.assignee.email}`);
|
|
9777
|
+
}
|
|
9778
|
+
if (contextOpts.includeLabels && labels.length > 0) {
|
|
9779
|
+
sections.push(formatLabels(labels));
|
|
9780
|
+
}
|
|
9781
|
+
if (contextOpts.includeDescription && card.description) {
|
|
9782
|
+
sections.push(`
|
|
9783
|
+
## Description
|
|
9784
|
+
${card.description}`);
|
|
9785
|
+
}
|
|
9786
|
+
if (contextOpts.includeSubtasks && subtasks.length > 0) {
|
|
9787
|
+
sections.push(formatSubtasks(subtasks));
|
|
9788
|
+
}
|
|
9789
|
+
if (contextOpts.includeLinks && links.length > 0) {
|
|
9790
|
+
sections.push(formatLinkedCards(links));
|
|
9791
|
+
}
|
|
9792
|
+
sections.push(`
|
|
9793
|
+
## Focus Areas`);
|
|
9794
|
+
roleFraming.focus.forEach((f) => {
|
|
9795
|
+
sections.push(`- ${f}`);
|
|
9796
|
+
});
|
|
9797
|
+
sections.push(`- **Memory:** When you discover important domain knowledge, architectural decisions, or infrastructure details, store them via \`harmony_remember\`. Focus on durable knowledge that future agents would benefit from — not ephemeral task details (those are auto-extracted from your session).`);
|
|
9798
|
+
sections.push(`
|
|
9799
|
+
## Suggested Outputs`);
|
|
9800
|
+
roleFraming.outputSuggestions.forEach((s) => {
|
|
9801
|
+
sections.push(`- ${s}`);
|
|
9802
|
+
});
|
|
9803
|
+
if (assembledContext) {
|
|
9804
|
+
sections.push(`
|
|
9805
|
+
${assembledContext}`);
|
|
9806
|
+
} else if (memories && memories.length > 0) {
|
|
9807
|
+
sections.push(`
|
|
9808
|
+
## Relevant Memories`);
|
|
9809
|
+
sections.push(`*${memories.length} memories recalled from knowledge graph:*`);
|
|
9810
|
+
for (const memory of memories) {
|
|
9811
|
+
const tags = memory.tags.length > 0 ? ` [${memory.tags.join(", ")}]` : "";
|
|
9812
|
+
sections.push(`
|
|
9813
|
+
### ${memory.title} (${memory.type}, confidence: ${memory.confidence})${tags}`);
|
|
9814
|
+
sections.push(memory.content);
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
const oneThingLine = synthesizeOneThing(card, subtasks, links, assembledContext);
|
|
9818
|
+
if (oneThingLine) {
|
|
9819
|
+
sections.push(`
|
|
9820
|
+
## Recommended Next Step
|
|
9821
|
+
${oneThingLine}`);
|
|
9822
|
+
}
|
|
9823
|
+
if (variant === "execute") {
|
|
9824
|
+
sections.push(`
|
|
9825
|
+
## Progress Tracking
|
|
9826
|
+
Update your progress by calling \`harmony_update_agent_progress\` with \`currentTask\` describing what you're doing now:
|
|
9827
|
+
- After exploring the codebase and understanding requirements (~20%)
|
|
9828
|
+
- When you start implementing changes (~50%)
|
|
9829
|
+
- When you move to testing or verification (~80%)
|
|
9830
|
+
- When done, before ending the session (100%)
|
|
9831
|
+
|
|
9832
|
+
Keep \`currentTask\` specific (e.g., "Refactoring auth middleware" not "Working on card").`);
|
|
9833
|
+
}
|
|
9834
|
+
if (customConstraints) {
|
|
9835
|
+
sections.push(`
|
|
9836
|
+
## Additional Instructions
|
|
9837
|
+
${customConstraints}`);
|
|
9838
|
+
}
|
|
9839
|
+
sections.push(`
|
|
9840
|
+
---
|
|
9841
|
+
*Card #${card.short_id} | Generated for ${variant} mode*`);
|
|
9842
|
+
const prompt = sections.join(`
|
|
9843
|
+
`);
|
|
9844
|
+
const memoryCount = assembledContext ? (assembledContext.match(/^### /gm) || []).length : memories?.length || 0;
|
|
9845
|
+
return {
|
|
9846
|
+
prompt,
|
|
9847
|
+
variant,
|
|
9848
|
+
category,
|
|
9849
|
+
role: roleFraming.role,
|
|
9850
|
+
contextSummary: {
|
|
9851
|
+
hasDescription: !!card.description,
|
|
9852
|
+
labelCount: labels.length,
|
|
9853
|
+
subtaskCount: subtasks.length,
|
|
9854
|
+
completedSubtasks: subtasks.filter((s) => s.completed).length,
|
|
9855
|
+
linkedCardCount: links.length,
|
|
9856
|
+
memoryCount
|
|
9857
|
+
},
|
|
9858
|
+
tokenEstimate: estimateTokens2(prompt),
|
|
9859
|
+
...assemblyId && { assemblyId }
|
|
9860
|
+
};
|
|
9861
|
+
}
|
|
9862
|
+
function extractSessionInsights(assembledContext) {
|
|
9863
|
+
const result = {
|
|
9864
|
+
lastSessionStatus: null,
|
|
9865
|
+
lastSessionTask: null,
|
|
9866
|
+
lastSessionProgress: null,
|
|
9867
|
+
blockers: [],
|
|
9868
|
+
procedureNextStep: null
|
|
9869
|
+
};
|
|
9870
|
+
const sessionMatches = assembledContext.match(/### Session:.*?\n([\s\S]*?)(?=\n###|\n## |\n---|\n\*Assembly|$)/g);
|
|
9871
|
+
if (sessionMatches && sessionMatches.length > 0) {
|
|
9872
|
+
const latest = sessionMatches[0];
|
|
9873
|
+
if (/Completed work on/i.test(latest)) {
|
|
9874
|
+
result.lastSessionStatus = "completed";
|
|
9875
|
+
} else if (/Paused work on|status:\s*paused/i.test(latest)) {
|
|
9876
|
+
result.lastSessionStatus = "paused";
|
|
9877
|
+
}
|
|
9878
|
+
const taskMatch = latest.match(/Final task:\s*(.+)/);
|
|
9879
|
+
if (taskMatch)
|
|
9880
|
+
result.lastSessionTask = taskMatch[1].trim();
|
|
9881
|
+
const progressMatch = latest.match(/Progress:\s*(\d+)%/);
|
|
9882
|
+
if (progressMatch)
|
|
9883
|
+
result.lastSessionProgress = parseInt(progressMatch[1], 10);
|
|
9884
|
+
}
|
|
9885
|
+
const blockerMatches = assembledContext.match(/(?:blocker|blocked by|blocking):\s*(.+)/gi);
|
|
9886
|
+
if (blockerMatches) {
|
|
9887
|
+
result.blockers = blockerMatches.map((m) => m.replace(/(?:blocker|blocked by|blocking):\s*/i, "").trim());
|
|
9888
|
+
}
|
|
9889
|
+
const stepMatches = assembledContext.match(/^\d+\.\s+(?!.*\*\*\[key step\]\*\*.*✓)(.+?)(?:\s*\*\*\[key step\]\*\*)?$/gm);
|
|
9890
|
+
if (stepMatches && stepMatches.length > 0) {
|
|
9891
|
+
result.procedureNextStep = stepMatches[0].replace(/^\d+\.\s+/, "").replace(/\s*\*\*\[key step\]\*\*.*$/, "").trim();
|
|
9892
|
+
}
|
|
9893
|
+
return result;
|
|
9894
|
+
}
|
|
9895
|
+
function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
9896
|
+
if (card.done)
|
|
9897
|
+
return null;
|
|
9898
|
+
const blockers = links.filter((l) => l.display_type === "is_blocked_by" && l.direction === "incoming");
|
|
9899
|
+
if (blockers.length > 0) {
|
|
9900
|
+
const blocker = blockers[0];
|
|
9901
|
+
return `Unblock first: resolve #${blocker.target_card.short_id} "${blocker.target_card.title}" which is blocking this card.`;
|
|
9902
|
+
}
|
|
9903
|
+
const session = assembledContext ? extractSessionInsights(assembledContext) : null;
|
|
9904
|
+
if (session?.blockers && session.blockers.length > 0) {
|
|
9905
|
+
return `Resolve blocker: ${session.blockers[0]}`;
|
|
9906
|
+
}
|
|
9907
|
+
if (session?.lastSessionStatus === "paused" && session.lastSessionTask) {
|
|
9908
|
+
const progress = session.lastSessionProgress ? ` (was ${session.lastSessionProgress}% complete)` : "";
|
|
9909
|
+
return `Resume previous session${progress}: "${session.lastSessionTask}".`;
|
|
9910
|
+
}
|
|
9911
|
+
if (subtasks.length > 0) {
|
|
9912
|
+
const completed = subtasks.filter((s) => s.completed).length;
|
|
9913
|
+
if (completed === subtasks.length) {
|
|
9914
|
+
return "All subtasks completed. Review the work and mark the card as done.";
|
|
9915
|
+
}
|
|
9916
|
+
const nextSubtask = subtasks.find((s) => !s.completed);
|
|
9917
|
+
if (nextSubtask) {
|
|
9918
|
+
return `Work on next subtask: "${nextSubtask.title}" (${completed}/${subtasks.length} done).`;
|
|
9919
|
+
}
|
|
8459
9920
|
}
|
|
8460
|
-
|
|
8461
|
-
return
|
|
9921
|
+
if (session?.procedureNextStep) {
|
|
9922
|
+
return `Follow procedure: ${session.procedureNextStep}`;
|
|
8462
9923
|
}
|
|
8463
|
-
|
|
8464
|
-
return
|
|
9924
|
+
if (session?.lastSessionStatus === "completed" && session.lastSessionTask) {
|
|
9925
|
+
return `Previous session completed ("${session.lastSessionTask}"). Review results and continue with remaining work.`;
|
|
8465
9926
|
}
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
if (Z_ANCHOR.test(str))
|
|
8469
|
-
return false;
|
|
8470
|
-
try {
|
|
8471
|
-
new RegExp(str);
|
|
8472
|
-
return true;
|
|
8473
|
-
} catch (e) {
|
|
8474
|
-
return false;
|
|
8475
|
-
}
|
|
9927
|
+
if (card.due_date && (card.priority === "urgent" || card.priority === "high")) {
|
|
9928
|
+
return `High-priority task with deadline ${card.due_date}. Start implementation immediately.`;
|
|
8476
9929
|
}
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
9930
|
+
if (card.description) {
|
|
9931
|
+
return "Analyze the description, identify the approach, and begin implementation.";
|
|
9932
|
+
}
|
|
9933
|
+
return null;
|
|
9934
|
+
}
|
|
9935
|
+
function getAvailableCategories() {
|
|
9936
|
+
return ["bug", "feature", "design", "review", "onboarding", "epic", "custom"];
|
|
9937
|
+
}
|
|
9938
|
+
function getAvailableVariants() {
|
|
9939
|
+
return ["analysis", "draft", "execute"];
|
|
9940
|
+
}
|
|
9941
|
+
var LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS;
|
|
9942
|
+
var init_prompt_builder = __esm(() => {
|
|
9943
|
+
LABEL_CATEGORY_MAP = {
|
|
9944
|
+
bug: "bug",
|
|
9945
|
+
fix: "bug",
|
|
9946
|
+
hotfix: "bug",
|
|
9947
|
+
defect: "bug",
|
|
9948
|
+
issue: "bug",
|
|
9949
|
+
error: "bug",
|
|
9950
|
+
feature: "feature",
|
|
9951
|
+
enhancement: "feature",
|
|
9952
|
+
improvement: "feature",
|
|
9953
|
+
new: "feature",
|
|
9954
|
+
design: "design",
|
|
9955
|
+
ui: "design",
|
|
9956
|
+
ux: "design",
|
|
9957
|
+
frontend: "design",
|
|
9958
|
+
styling: "design",
|
|
9959
|
+
review: "review",
|
|
9960
|
+
"code review": "review",
|
|
9961
|
+
pr: "review",
|
|
9962
|
+
feedback: "review",
|
|
9963
|
+
onboarding: "onboarding",
|
|
9964
|
+
documentation: "onboarding",
|
|
9965
|
+
docs: "onboarding",
|
|
9966
|
+
guide: "onboarding",
|
|
9967
|
+
tutorial: "onboarding",
|
|
9968
|
+
epic: "epic",
|
|
9969
|
+
initiative: "epic",
|
|
9970
|
+
project: "epic",
|
|
9971
|
+
milestone: "epic"
|
|
9972
|
+
};
|
|
9973
|
+
DEFAULT_ROLE_FRAMINGS = {
|
|
9974
|
+
bug: {
|
|
9975
|
+
category: "bug",
|
|
9976
|
+
role: "Senior QA Engineer and Software Developer",
|
|
9977
|
+
perspective: "You are investigating and fixing a bug report.",
|
|
9978
|
+
focus: [
|
|
9979
|
+
"Root cause analysis",
|
|
9980
|
+
"Steps to reproduce",
|
|
9981
|
+
"Impact assessment",
|
|
9982
|
+
"Fix implementation",
|
|
9983
|
+
"Regression prevention",
|
|
9984
|
+
"Test cases to prevent recurrence"
|
|
9985
|
+
],
|
|
9986
|
+
outputSuggestions: [
|
|
9987
|
+
"Bug triage summary",
|
|
9988
|
+
"Root cause explanation",
|
|
9989
|
+
"Fix implementation plan",
|
|
9990
|
+
"Test cases"
|
|
9991
|
+
]
|
|
8538
9992
|
},
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
9993
|
+
feature: {
|
|
9994
|
+
category: "feature",
|
|
9995
|
+
role: "Product Engineer",
|
|
9996
|
+
perspective: "You are implementing a new feature or enhancement.",
|
|
9997
|
+
focus: [
|
|
9998
|
+
"User requirements",
|
|
9999
|
+
"Technical specification",
|
|
10000
|
+
"Implementation approach",
|
|
10001
|
+
"Edge cases",
|
|
10002
|
+
"Acceptance criteria",
|
|
10003
|
+
"Integration points"
|
|
10004
|
+
],
|
|
10005
|
+
outputSuggestions: [
|
|
10006
|
+
"Technical specification",
|
|
10007
|
+
"Implementation tasks",
|
|
10008
|
+
"Acceptance criteria checklist",
|
|
10009
|
+
"API design"
|
|
10010
|
+
]
|
|
10011
|
+
},
|
|
10012
|
+
design: {
|
|
10013
|
+
category: "design",
|
|
10014
|
+
role: "UX Designer and Frontend Developer",
|
|
10015
|
+
perspective: "You are designing and implementing a user interface.",
|
|
10016
|
+
focus: [
|
|
10017
|
+
"User experience flow",
|
|
10018
|
+
"Visual design consistency",
|
|
10019
|
+
"Accessibility (WCAG)",
|
|
10020
|
+
"Responsive behavior",
|
|
10021
|
+
"Component architecture",
|
|
10022
|
+
"Interaction patterns"
|
|
10023
|
+
],
|
|
10024
|
+
outputSuggestions: [
|
|
10025
|
+
"User flow diagram",
|
|
10026
|
+
"Component specifications",
|
|
10027
|
+
"Accessibility checklist",
|
|
10028
|
+
"Responsive breakpoints"
|
|
10029
|
+
]
|
|
10030
|
+
},
|
|
10031
|
+
review: {
|
|
10032
|
+
category: "review",
|
|
10033
|
+
role: "Code Reviewer and Technical Lead",
|
|
10034
|
+
perspective: "You are reviewing code for quality, correctness, and maintainability.",
|
|
10035
|
+
focus: [
|
|
10036
|
+
"Code correctness",
|
|
10037
|
+
"Performance implications",
|
|
10038
|
+
"Security considerations",
|
|
10039
|
+
"Testing coverage",
|
|
10040
|
+
"Documentation",
|
|
10041
|
+
"Best practices adherence"
|
|
10042
|
+
],
|
|
10043
|
+
outputSuggestions: [
|
|
10044
|
+
"Review checklist",
|
|
10045
|
+
"Suggested improvements",
|
|
10046
|
+
"Test scenarios",
|
|
10047
|
+
"Security audit"
|
|
10048
|
+
]
|
|
10049
|
+
},
|
|
10050
|
+
onboarding: {
|
|
10051
|
+
category: "onboarding",
|
|
10052
|
+
role: "Technical Writer and Developer Advocate",
|
|
10053
|
+
perspective: "You are creating documentation or onboarding materials.",
|
|
10054
|
+
focus: [
|
|
10055
|
+
"Clear step-by-step instructions",
|
|
10056
|
+
"Prerequisites and setup",
|
|
10057
|
+
"Common pitfalls",
|
|
10058
|
+
"Examples and use cases",
|
|
10059
|
+
"Troubleshooting guide",
|
|
10060
|
+
"Related resources"
|
|
10061
|
+
],
|
|
10062
|
+
outputSuggestions: [
|
|
10063
|
+
"Getting started guide",
|
|
10064
|
+
"Step-by-step tutorial",
|
|
10065
|
+
"FAQ section",
|
|
10066
|
+
"Troubleshooting guide"
|
|
10067
|
+
]
|
|
10068
|
+
},
|
|
10069
|
+
epic: {
|
|
10070
|
+
category: "epic",
|
|
10071
|
+
role: "Technical Project Manager and Architect",
|
|
10072
|
+
perspective: "You are planning and coordinating a large initiative.",
|
|
10073
|
+
focus: [
|
|
10074
|
+
"Scope definition",
|
|
10075
|
+
"Task breakdown",
|
|
10076
|
+
"Dependencies",
|
|
10077
|
+
"Risk assessment",
|
|
10078
|
+
"Timeline considerations",
|
|
10079
|
+
"Success metrics"
|
|
10080
|
+
],
|
|
10081
|
+
outputSuggestions: [
|
|
10082
|
+
"Epic breakdown into stories",
|
|
10083
|
+
"Dependency graph",
|
|
10084
|
+
"Risk mitigation plan",
|
|
10085
|
+
"Success criteria"
|
|
10086
|
+
]
|
|
10087
|
+
},
|
|
10088
|
+
custom: {
|
|
10089
|
+
category: "custom",
|
|
10090
|
+
role: "Software Engineer",
|
|
10091
|
+
perspective: "You are working on a software task.",
|
|
10092
|
+
focus: [
|
|
10093
|
+
"Understanding requirements",
|
|
10094
|
+
"Implementation approach",
|
|
10095
|
+
"Quality considerations",
|
|
10096
|
+
"Testing strategy"
|
|
10097
|
+
],
|
|
10098
|
+
outputSuggestions: [
|
|
10099
|
+
"Implementation plan",
|
|
10100
|
+
"Technical notes",
|
|
10101
|
+
"Task checklist"
|
|
10102
|
+
]
|
|
8560
10103
|
}
|
|
8561
|
-
const [formats, exportName] = opts.mode === "fast" ? [formats_1.fastFormats, fastName] : [formats_1.fullFormats, fullName];
|
|
8562
|
-
const list = opts.formats || formats_1.formatNames;
|
|
8563
|
-
addFormats(ajv, list, formats, exportName);
|
|
8564
|
-
if (opts.keywords)
|
|
8565
|
-
(0, limit_1.default)(ajv);
|
|
8566
|
-
return ajv;
|
|
8567
10104
|
};
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
throw new Error(`Unknown format "${name}"`);
|
|
8573
|
-
return f;
|
|
10105
|
+
VARIANT_INSTRUCTIONS = {
|
|
10106
|
+
analysis: `ANALYSIS MODE: Analyze this task thoroughly. Identify requirements, constraints, edge cases, and potential challenges. Do NOT implement anything yet - focus on understanding and planning.`,
|
|
10107
|
+
draft: `DRAFT MODE: Create a detailed implementation plan with code structure, key decisions, and approach. Include pseudocode or skeleton code where helpful. This is for review before full implementation.`,
|
|
10108
|
+
execute: `EXECUTE MODE: Implement this task completely. Write production-ready code following best practices. Include necessary tests and documentation.`
|
|
8574
10109
|
};
|
|
8575
|
-
function addFormats(ajv, list, fs, exportName) {
|
|
8576
|
-
var _a2;
|
|
8577
|
-
var _b;
|
|
8578
|
-
(_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== undefined || (_b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`);
|
|
8579
|
-
for (const f of list)
|
|
8580
|
-
ajv.addFormat(f, fs[f]);
|
|
8581
|
-
}
|
|
8582
|
-
module.exports = exports = formatsPlugin;
|
|
8583
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8584
|
-
exports.default = formatsPlugin;
|
|
8585
10110
|
});
|
|
8586
10111
|
|
|
8587
10112
|
// ../../node_modules/sisteransi/src/index.js
|
|
@@ -8724,751 +10249,201 @@ var {
|
|
|
8724
10249
|
createOption,
|
|
8725
10250
|
CommanderError,
|
|
8726
10251
|
InvalidArgumentError,
|
|
8727
|
-
InvalidOptionArgumentError,
|
|
8728
|
-
Command,
|
|
8729
|
-
Argument,
|
|
8730
|
-
Option,
|
|
8731
|
-
Help
|
|
8732
|
-
} = import__.default;
|
|
8733
|
-
|
|
8734
|
-
// src/config.ts
|
|
8735
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8736
|
-
import { homedir } from "node:os";
|
|
8737
|
-
import { join } from "node:path";
|
|
8738
|
-
var DEFAULT_API_URL = "https://gethmy.com/api";
|
|
8739
|
-
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
8740
|
-
function getConfigDir() {
|
|
8741
|
-
return join(homedir(), ".harmony-mcp");
|
|
8742
|
-
}
|
|
8743
|
-
function getConfigPath() {
|
|
8744
|
-
return join(getConfigDir(), "config.json");
|
|
8745
|
-
}
|
|
8746
|
-
function getLocalConfigPath(cwd) {
|
|
8747
|
-
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
8748
|
-
}
|
|
8749
|
-
function loadConfig() {
|
|
8750
|
-
const configPath = getConfigPath();
|
|
8751
|
-
if (!existsSync(configPath)) {
|
|
8752
|
-
return {
|
|
8753
|
-
apiKey: null,
|
|
8754
|
-
apiUrl: DEFAULT_API_URL,
|
|
8755
|
-
activeWorkspaceId: null,
|
|
8756
|
-
activeProjectId: null,
|
|
8757
|
-
userEmail: null,
|
|
8758
|
-
memoryDir: null
|
|
8759
|
-
};
|
|
8760
|
-
}
|
|
8761
|
-
try {
|
|
8762
|
-
const data = readFileSync(configPath, "utf-8");
|
|
8763
|
-
const config = JSON.parse(data);
|
|
8764
|
-
return {
|
|
8765
|
-
apiKey: config.apiKey || null,
|
|
8766
|
-
apiUrl: config.apiUrl || DEFAULT_API_URL,
|
|
8767
|
-
activeWorkspaceId: config.activeWorkspaceId || null,
|
|
8768
|
-
activeProjectId: config.activeProjectId || null,
|
|
8769
|
-
userEmail: config.userEmail || null,
|
|
8770
|
-
memoryDir: config.memoryDir || null
|
|
8771
|
-
};
|
|
8772
|
-
} catch {
|
|
8773
|
-
return {
|
|
8774
|
-
apiKey: null,
|
|
8775
|
-
apiUrl: DEFAULT_API_URL,
|
|
8776
|
-
activeWorkspaceId: null,
|
|
8777
|
-
activeProjectId: null,
|
|
8778
|
-
userEmail: null,
|
|
8779
|
-
memoryDir: null
|
|
8780
|
-
};
|
|
8781
|
-
}
|
|
8782
|
-
}
|
|
8783
|
-
function saveConfig(config) {
|
|
8784
|
-
const configDir = getConfigDir();
|
|
8785
|
-
const configPath = getConfigPath();
|
|
8786
|
-
if (!existsSync(configDir)) {
|
|
8787
|
-
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
8788
|
-
}
|
|
8789
|
-
const existingConfig = loadConfig();
|
|
8790
|
-
const newConfig = { ...existingConfig, ...config };
|
|
8791
|
-
writeFileSync(configPath, JSON.stringify(newConfig, null, 2), {
|
|
8792
|
-
mode: 384
|
|
8793
|
-
});
|
|
8794
|
-
}
|
|
8795
|
-
function loadLocalConfig(cwd) {
|
|
8796
|
-
const localConfigPath = getLocalConfigPath(cwd);
|
|
8797
|
-
if (!existsSync(localConfigPath)) {
|
|
8798
|
-
return null;
|
|
8799
|
-
}
|
|
8800
|
-
try {
|
|
8801
|
-
const data = readFileSync(localConfigPath, "utf-8");
|
|
8802
|
-
const config = JSON.parse(data);
|
|
8803
|
-
return {
|
|
8804
|
-
workspaceId: config.workspaceId || null,
|
|
8805
|
-
projectId: config.projectId || null
|
|
8806
|
-
};
|
|
8807
|
-
} catch {
|
|
8808
|
-
return null;
|
|
8809
|
-
}
|
|
8810
|
-
}
|
|
8811
|
-
function saveLocalConfig(config, cwd) {
|
|
8812
|
-
const localConfigPath = getLocalConfigPath(cwd);
|
|
8813
|
-
const existingConfig = loadLocalConfig(cwd) || {
|
|
8814
|
-
workspaceId: null,
|
|
8815
|
-
projectId: null
|
|
8816
|
-
};
|
|
8817
|
-
const newConfig = { ...existingConfig, ...config };
|
|
8818
|
-
const cleanConfig = {};
|
|
8819
|
-
if (newConfig.workspaceId)
|
|
8820
|
-
cleanConfig.workspaceId = newConfig.workspaceId;
|
|
8821
|
-
if (newConfig.projectId)
|
|
8822
|
-
cleanConfig.projectId = newConfig.projectId;
|
|
8823
|
-
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
8824
|
-
}
|
|
8825
|
-
function hasLocalConfig(cwd) {
|
|
8826
|
-
return existsSync(getLocalConfigPath(cwd));
|
|
8827
|
-
}
|
|
8828
|
-
function getApiKey() {
|
|
8829
|
-
const config = loadConfig();
|
|
8830
|
-
if (!config.apiKey) {
|
|
8831
|
-
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
8832
|
-
` + "You can generate an API key at https://gethmy.com → Settings → API Keys.");
|
|
8833
|
-
}
|
|
8834
|
-
return config.apiKey;
|
|
8835
|
-
}
|
|
8836
|
-
function getApiUrl() {
|
|
8837
|
-
const config = loadConfig();
|
|
8838
|
-
return config.apiUrl;
|
|
8839
|
-
}
|
|
8840
|
-
function getUserEmail() {
|
|
8841
|
-
const config = loadConfig();
|
|
8842
|
-
return config.userEmail;
|
|
8843
|
-
}
|
|
8844
|
-
function setActiveWorkspace(workspaceId, options) {
|
|
8845
|
-
if (options?.local) {
|
|
8846
|
-
saveLocalConfig({ workspaceId }, options.cwd);
|
|
8847
|
-
} else {
|
|
8848
|
-
saveConfig({ activeWorkspaceId: workspaceId });
|
|
8849
|
-
}
|
|
8850
|
-
}
|
|
8851
|
-
function setActiveProject(projectId, options) {
|
|
8852
|
-
if (options?.local) {
|
|
8853
|
-
saveLocalConfig({ projectId }, options.cwd);
|
|
8854
|
-
} else {
|
|
8855
|
-
saveConfig({ activeProjectId: projectId });
|
|
8856
|
-
}
|
|
8857
|
-
}
|
|
8858
|
-
function getActiveWorkspaceId(cwd) {
|
|
8859
|
-
const localConfig = loadLocalConfig(cwd);
|
|
8860
|
-
if (localConfig?.workspaceId) {
|
|
8861
|
-
return localConfig.workspaceId;
|
|
8862
|
-
}
|
|
8863
|
-
return loadConfig().activeWorkspaceId;
|
|
8864
|
-
}
|
|
8865
|
-
function getActiveProjectId(cwd) {
|
|
8866
|
-
const localConfig = loadLocalConfig(cwd);
|
|
8867
|
-
if (localConfig?.projectId) {
|
|
8868
|
-
return localConfig.projectId;
|
|
8869
|
-
}
|
|
8870
|
-
return loadConfig().activeProjectId;
|
|
8871
|
-
}
|
|
8872
|
-
function isConfigured() {
|
|
8873
|
-
const config = loadConfig();
|
|
8874
|
-
return !!config.apiKey;
|
|
8875
|
-
}
|
|
8876
|
-
function areSkillsInstalled(cwd) {
|
|
8877
|
-
const home = homedir();
|
|
8878
|
-
const workingDir = cwd || process.cwd();
|
|
8879
|
-
const foundPaths = [];
|
|
8880
|
-
const globalSkillsDir = join(home, ".agents", "skills");
|
|
8881
|
-
const globalSkillPath = join(globalSkillsDir, "hmy", "SKILL.md");
|
|
8882
|
-
if (existsSync(globalSkillPath)) {
|
|
8883
|
-
foundPaths.push(globalSkillPath);
|
|
8884
|
-
return { installed: true, location: "global", paths: foundPaths };
|
|
8885
|
-
}
|
|
8886
|
-
const claudeGlobalSkill = join(home, ".claude", "skills", "hmy.md");
|
|
8887
|
-
if (existsSync(claudeGlobalSkill)) {
|
|
8888
|
-
foundPaths.push(claudeGlobalSkill);
|
|
8889
|
-
return { installed: true, location: "global", paths: foundPaths };
|
|
8890
|
-
}
|
|
8891
|
-
const claudeGlobalSkillAlt = join(home, ".claude", "skills", "hmy", "SKILL.md");
|
|
8892
|
-
if (existsSync(claudeGlobalSkillAlt)) {
|
|
8893
|
-
foundPaths.push(claudeGlobalSkillAlt);
|
|
8894
|
-
return { installed: true, location: "global", paths: foundPaths };
|
|
8895
|
-
}
|
|
8896
|
-
const localSkillPath = join(workingDir, ".claude", "skills", "hmy.md");
|
|
8897
|
-
if (existsSync(localSkillPath)) {
|
|
8898
|
-
foundPaths.push(localSkillPath);
|
|
8899
|
-
return { installed: true, location: "local", paths: foundPaths };
|
|
8900
|
-
}
|
|
8901
|
-
const localSkillPathAlt = join(workingDir, ".claude", "skills", "hmy", "SKILL.md");
|
|
8902
|
-
if (existsSync(localSkillPathAlt)) {
|
|
8903
|
-
foundPaths.push(localSkillPathAlt);
|
|
8904
|
-
return { installed: true, location: "local", paths: foundPaths };
|
|
8905
|
-
}
|
|
8906
|
-
return { installed: false, location: null, paths: [] };
|
|
8907
|
-
}
|
|
8908
|
-
function hasProjectContext(cwd) {
|
|
8909
|
-
const localConfig = loadLocalConfig(cwd);
|
|
8910
|
-
return !!(localConfig?.workspaceId || localConfig?.projectId);
|
|
8911
|
-
}
|
|
8912
|
-
function getMemoryDir() {
|
|
8913
|
-
const config = loadConfig();
|
|
8914
|
-
if (config.memoryDir)
|
|
8915
|
-
return config.memoryDir;
|
|
8916
|
-
return join(homedir(), ".harmony", "memory");
|
|
8917
|
-
}
|
|
8918
|
-
// ../memory/src/graph-walk.ts
|
|
8919
|
-
async function discoverRelatedContext(client, startIds, maxDepth = 2, maxEntities = 20, minConfidence = 0.5) {
|
|
8920
|
-
const visited = new Set;
|
|
8921
|
-
const collectedEntities = [];
|
|
8922
|
-
const collectedRelations = [];
|
|
8923
|
-
let truncated = false;
|
|
8924
|
-
const queue = startIds.map((id) => [id, 0]);
|
|
8925
|
-
for (const id of startIds) {
|
|
8926
|
-
visited.add(id);
|
|
8927
|
-
}
|
|
8928
|
-
while (queue.length > 0) {
|
|
8929
|
-
const [entityId, depth] = queue.shift();
|
|
8930
|
-
if (collectedEntities.length >= maxEntities) {
|
|
8931
|
-
truncated = true;
|
|
8932
|
-
break;
|
|
8933
|
-
}
|
|
8934
|
-
if (depth > maxDepth)
|
|
8935
|
-
continue;
|
|
8936
|
-
try {
|
|
8937
|
-
const entityResult = await client.getMemoryEntity(entityId);
|
|
8938
|
-
const entity = entityResult.entity;
|
|
8939
|
-
if (entity) {
|
|
8940
|
-
collectedEntities.push({
|
|
8941
|
-
id: entity.id,
|
|
8942
|
-
type: entity.type,
|
|
8943
|
-
title: entity.title,
|
|
8944
|
-
confidence: entity.confidence ?? 1,
|
|
8945
|
-
memory_tier: entity.memory_tier || "reference"
|
|
8946
|
-
});
|
|
8947
|
-
}
|
|
8948
|
-
if (depth >= maxDepth)
|
|
8949
|
-
continue;
|
|
8950
|
-
const related = await client.getRelatedEntities(entityId);
|
|
8951
|
-
for (const raw of related.outgoing || []) {
|
|
8952
|
-
const rel = raw;
|
|
8953
|
-
const relConfidence = rel.confidence ?? 1;
|
|
8954
|
-
if (relConfidence < minConfidence)
|
|
8955
|
-
continue;
|
|
8956
|
-
const target = rel.target;
|
|
8957
|
-
const targetId = target?.id ?? rel.target_id;
|
|
8958
|
-
if (targetId && !visited.has(targetId)) {
|
|
8959
|
-
visited.add(targetId);
|
|
8960
|
-
queue.push([targetId, depth + 1]);
|
|
8961
|
-
collectedRelations.push({
|
|
8962
|
-
id: rel.id,
|
|
8963
|
-
source_id: entityId,
|
|
8964
|
-
target_id: targetId,
|
|
8965
|
-
relation_type: rel.relation_type,
|
|
8966
|
-
confidence: relConfidence
|
|
8967
|
-
});
|
|
8968
|
-
}
|
|
8969
|
-
}
|
|
8970
|
-
for (const raw of related.incoming || []) {
|
|
8971
|
-
const rel = raw;
|
|
8972
|
-
const relConfidence = rel.confidence ?? 1;
|
|
8973
|
-
if (relConfidence < minConfidence)
|
|
8974
|
-
continue;
|
|
8975
|
-
const source = rel.source;
|
|
8976
|
-
const sourceId = source?.id ?? rel.source_id;
|
|
8977
|
-
if (sourceId && !visited.has(sourceId)) {
|
|
8978
|
-
visited.add(sourceId);
|
|
8979
|
-
queue.push([sourceId, depth + 1]);
|
|
8980
|
-
collectedRelations.push({
|
|
8981
|
-
id: rel.id,
|
|
8982
|
-
source_id: sourceId,
|
|
8983
|
-
target_id: entityId,
|
|
8984
|
-
relation_type: rel.relation_type,
|
|
8985
|
-
confidence: relConfidence
|
|
8986
|
-
});
|
|
8987
|
-
}
|
|
8988
|
-
}
|
|
8989
|
-
} catch {}
|
|
8990
|
-
}
|
|
8991
|
-
return {
|
|
8992
|
-
entities: collectedEntities,
|
|
8993
|
-
relations: collectedRelations,
|
|
8994
|
-
depth: maxDepth,
|
|
8995
|
-
truncated
|
|
8996
|
-
};
|
|
8997
|
-
}
|
|
8998
|
-
// ../memory/src/lifecycle.ts
|
|
8999
|
-
var DECAY_HALF_LIVES = {
|
|
9000
|
-
draft: 7,
|
|
9001
|
-
episode: 30,
|
|
9002
|
-
reference: 180
|
|
9003
|
-
};
|
|
9004
|
-
var PROMOTION_RULES = {
|
|
9005
|
-
draftToEpisode: {
|
|
9006
|
-
minAccessCount: 5,
|
|
9007
|
-
minConfidence: 0.8,
|
|
9008
|
-
minAgeDays: 1
|
|
9009
|
-
},
|
|
9010
|
-
episodeToReference: {
|
|
9011
|
-
minAccessCount: 10,
|
|
9012
|
-
minConfidence: 0.9,
|
|
9013
|
-
minAgeDays: 7
|
|
9014
|
-
}
|
|
9015
|
-
};
|
|
9016
|
-
var ARCHIVE_THRESHOLD = 0.3;
|
|
9017
|
-
var STALE_DAYS = 90;
|
|
9018
|
-
var STALE_MIN_ACCESS = 3;
|
|
9019
|
-
function computeDecayScore(tier, lastAccessedAt, accessCount) {
|
|
9020
|
-
const halfLife = DECAY_HALF_LIVES[tier];
|
|
9021
|
-
const now = Date.now();
|
|
9022
|
-
let daysSinceAccess = 0;
|
|
9023
|
-
if (lastAccessedAt) {
|
|
9024
|
-
daysSinceAccess = (now - new Date(lastAccessedAt).getTime()) / (1000 * 60 * 60 * 24);
|
|
9025
|
-
}
|
|
9026
|
-
const timeDecay = 0.5 ** (daysSinceAccess / halfLife);
|
|
9027
|
-
const accessBonus = Math.log10(accessCount + 1) * 0.1;
|
|
9028
|
-
const score = Math.min(timeDecay + accessBonus, 1);
|
|
9029
|
-
return { score, daysSinceAccess, halfLife, accessBonus };
|
|
9030
|
-
}
|
|
9031
|
-
function checkPromotion(currentTier, accessCount, confidence, createdAt) {
|
|
9032
|
-
const ageDays = (Date.now() - new Date(createdAt).getTime()) / (1000 * 60 * 60 * 24);
|
|
9033
|
-
const base = {
|
|
9034
|
-
eligible: false,
|
|
9035
|
-
targetTier: null,
|
|
9036
|
-
reason: null,
|
|
9037
|
-
currentTier,
|
|
9038
|
-
accessCount,
|
|
9039
|
-
confidence,
|
|
9040
|
-
ageDays
|
|
9041
|
-
};
|
|
9042
|
-
if (currentTier === "draft") {
|
|
9043
|
-
const rules = PROMOTION_RULES.draftToEpisode;
|
|
9044
|
-
if (accessCount >= rules.minAccessCount && confidence >= rules.minConfidence && ageDays >= rules.minAgeDays) {
|
|
9045
|
-
return {
|
|
9046
|
-
...base,
|
|
9047
|
-
eligible: true,
|
|
9048
|
-
targetTier: "episode",
|
|
9049
|
-
reason: `Accessed ${accessCount} times (≥${rules.minAccessCount}), confidence ${confidence} (≥${rules.minConfidence}), age ${Math.round(ageDays)}d (≥${rules.minAgeDays}d)`
|
|
9050
|
-
};
|
|
9051
|
-
}
|
|
9052
|
-
}
|
|
9053
|
-
if (currentTier === "episode") {
|
|
9054
|
-
const rules = PROMOTION_RULES.episodeToReference;
|
|
9055
|
-
if (accessCount >= rules.minAccessCount && confidence >= rules.minConfidence && ageDays >= rules.minAgeDays) {
|
|
9056
|
-
return {
|
|
9057
|
-
...base,
|
|
9058
|
-
eligible: true,
|
|
9059
|
-
targetTier: "reference",
|
|
9060
|
-
reason: `Accessed ${accessCount} times (≥${rules.minAccessCount}), confidence ${confidence} (≥${rules.minConfidence}), age ${Math.round(ageDays)}d (≥${rules.minAgeDays}d)`
|
|
9061
|
-
};
|
|
9062
|
-
}
|
|
9063
|
-
}
|
|
9064
|
-
return base;
|
|
9065
|
-
}
|
|
9066
|
-
function evaluateLifecycle(entity) {
|
|
9067
|
-
const decay = computeDecayScore(entity.memory_tier, entity.last_accessed_at, entity.access_count);
|
|
9068
|
-
const promotion = checkPromotion(entity.memory_tier, entity.access_count, entity.confidence, entity.created_at);
|
|
9069
|
-
const shouldArchive = entity.confidence < ARCHIVE_THRESHOLD;
|
|
9070
|
-
const archiveReason = shouldArchive ? `Confidence ${entity.confidence} below threshold ${ARCHIVE_THRESHOLD}` : undefined;
|
|
9071
|
-
const shouldFlagForReview = decay.daysSinceAccess >= STALE_DAYS && entity.access_count < STALE_MIN_ACCESS;
|
|
9072
|
-
const reviewReason = shouldFlagForReview ? `Not accessed in ${Math.round(decay.daysSinceAccess)} days with only ${entity.access_count} accesses` : undefined;
|
|
9073
|
-
return {
|
|
9074
|
-
decay,
|
|
9075
|
-
promotion,
|
|
9076
|
-
shouldArchive,
|
|
9077
|
-
shouldFlagForReview,
|
|
9078
|
-
archiveReason,
|
|
9079
|
-
reviewReason
|
|
9080
|
-
};
|
|
9081
|
-
}
|
|
9082
|
-
// ../memory/src/sync.ts
|
|
9083
|
-
import { createHash } from "node:crypto";
|
|
9084
|
-
import {
|
|
9085
|
-
existsSync as existsSync2,
|
|
9086
|
-
mkdirSync as mkdirSync2,
|
|
9087
|
-
readdirSync,
|
|
9088
|
-
readFileSync as readFileSync2,
|
|
9089
|
-
rmSync,
|
|
9090
|
-
writeFileSync as writeFileSync2
|
|
9091
|
-
} from "node:fs";
|
|
9092
|
-
import { join as join2, relative, sep } from "node:path";
|
|
10252
|
+
InvalidOptionArgumentError,
|
|
10253
|
+
Command,
|
|
10254
|
+
Argument,
|
|
10255
|
+
Option,
|
|
10256
|
+
Help
|
|
10257
|
+
} = import__.default;
|
|
9093
10258
|
|
|
9094
|
-
//
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
10259
|
+
// src/config.ts
|
|
10260
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10261
|
+
import { homedir } from "node:os";
|
|
10262
|
+
import { join } from "node:path";
|
|
10263
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api";
|
|
10264
|
+
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
10265
|
+
function getConfigDir() {
|
|
10266
|
+
return join(homedir(), ".harmony-mcp");
|
|
10267
|
+
}
|
|
10268
|
+
function getConfigPath() {
|
|
10269
|
+
return join(getConfigDir(), "config.json");
|
|
10270
|
+
}
|
|
10271
|
+
function getLocalConfigPath(cwd) {
|
|
10272
|
+
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
10273
|
+
}
|
|
10274
|
+
function loadConfig() {
|
|
10275
|
+
const configPath = getConfigPath();
|
|
10276
|
+
if (!existsSync(configPath)) {
|
|
10277
|
+
return {
|
|
10278
|
+
apiKey: null,
|
|
10279
|
+
apiUrl: DEFAULT_API_URL,
|
|
10280
|
+
activeWorkspaceId: null,
|
|
10281
|
+
activeProjectId: null,
|
|
10282
|
+
userEmail: null,
|
|
10283
|
+
memoryDir: null
|
|
10284
|
+
};
|
|
9109
10285
|
}
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
10286
|
+
try {
|
|
10287
|
+
const data = readFileSync(configPath, "utf-8");
|
|
10288
|
+
const config = JSON.parse(data);
|
|
10289
|
+
return {
|
|
10290
|
+
apiKey: config.apiKey || null,
|
|
10291
|
+
apiUrl: config.apiUrl || DEFAULT_API_URL,
|
|
10292
|
+
activeWorkspaceId: config.activeWorkspaceId || null,
|
|
10293
|
+
activeProjectId: config.activeProjectId || null,
|
|
10294
|
+
userEmail: config.userEmail || null,
|
|
10295
|
+
memoryDir: config.memoryDir || null
|
|
10296
|
+
};
|
|
10297
|
+
} catch {
|
|
10298
|
+
return {
|
|
10299
|
+
apiKey: null,
|
|
10300
|
+
apiUrl: DEFAULT_API_URL,
|
|
10301
|
+
activeWorkspaceId: null,
|
|
10302
|
+
activeProjectId: null,
|
|
10303
|
+
userEmail: null,
|
|
10304
|
+
memoryDir: null
|
|
10305
|
+
};
|
|
9115
10306
|
}
|
|
9116
|
-
return { frontmatter, title, content: body };
|
|
9117
10307
|
}
|
|
9118
|
-
function
|
|
9119
|
-
const
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
lines.push(`project_id: ${entity.project_id}`);
|
|
10308
|
+
function saveConfig(config) {
|
|
10309
|
+
const configDir = getConfigDir();
|
|
10310
|
+
const configPath = getConfigPath();
|
|
10311
|
+
if (!existsSync(configDir)) {
|
|
10312
|
+
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
9124
10313
|
}
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
10314
|
+
const existingConfig = loadConfig();
|
|
10315
|
+
const newConfig = { ...existingConfig, ...config };
|
|
10316
|
+
writeFileSync(configPath, JSON.stringify(newConfig, null, 2), {
|
|
10317
|
+
mode: 384
|
|
10318
|
+
});
|
|
10319
|
+
}
|
|
10320
|
+
function loadLocalConfig(cwd) {
|
|
10321
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
10322
|
+
if (!existsSync(localConfigPath)) {
|
|
10323
|
+
return null;
|
|
9133
10324
|
}
|
|
9134
|
-
|
|
9135
|
-
|
|
10325
|
+
try {
|
|
10326
|
+
const data = readFileSync(localConfigPath, "utf-8");
|
|
10327
|
+
const config = JSON.parse(data);
|
|
10328
|
+
return {
|
|
10329
|
+
workspaceId: config.workspaceId || null,
|
|
10330
|
+
projectId: config.projectId || null
|
|
10331
|
+
};
|
|
10332
|
+
} catch {
|
|
10333
|
+
return null;
|
|
9136
10334
|
}
|
|
9137
|
-
lines.push(`created_at: ${entity.created_at}`);
|
|
9138
|
-
lines.push(`updated_at: ${entity.updated_at}`);
|
|
9139
|
-
lines.push("---");
|
|
9140
|
-
lines.push("");
|
|
9141
|
-
lines.push(`# ${entity.title}`);
|
|
9142
|
-
lines.push("");
|
|
9143
|
-
lines.push(entity.content);
|
|
9144
|
-
return lines.join(`
|
|
9145
|
-
`);
|
|
9146
10335
|
}
|
|
9147
|
-
function
|
|
9148
|
-
const
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
confidence: 1,
|
|
9153
|
-
tags: []
|
|
10336
|
+
function saveLocalConfig(config, cwd) {
|
|
10337
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
10338
|
+
const existingConfig = loadLocalConfig(cwd) || {
|
|
10339
|
+
workspaceId: null,
|
|
10340
|
+
projectId: null
|
|
9154
10341
|
};
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
switch (key) {
|
|
9163
|
-
case "id":
|
|
9164
|
-
result.id = value;
|
|
9165
|
-
break;
|
|
9166
|
-
case "workspace_id":
|
|
9167
|
-
result.workspace_id = value;
|
|
9168
|
-
break;
|
|
9169
|
-
case "project_id":
|
|
9170
|
-
result.project_id = value;
|
|
9171
|
-
break;
|
|
9172
|
-
case "type":
|
|
9173
|
-
result.type = value;
|
|
9174
|
-
break;
|
|
9175
|
-
case "scope":
|
|
9176
|
-
result.scope = value;
|
|
9177
|
-
break;
|
|
9178
|
-
case "tier":
|
|
9179
|
-
result.tier = value;
|
|
9180
|
-
break;
|
|
9181
|
-
case "confidence":
|
|
9182
|
-
result.confidence = parseFloat(value) || 1;
|
|
9183
|
-
break;
|
|
9184
|
-
case "tags":
|
|
9185
|
-
result.tags = parseYamlArray(value);
|
|
9186
|
-
break;
|
|
9187
|
-
case "related":
|
|
9188
|
-
result.related = parseYamlArray(value);
|
|
9189
|
-
break;
|
|
9190
|
-
case "agent":
|
|
9191
|
-
result.agent = value;
|
|
9192
|
-
break;
|
|
9193
|
-
case "created_at":
|
|
9194
|
-
result.created_at = value;
|
|
9195
|
-
break;
|
|
9196
|
-
case "updated_at":
|
|
9197
|
-
result.updated_at = value;
|
|
9198
|
-
break;
|
|
9199
|
-
}
|
|
9200
|
-
}
|
|
9201
|
-
return result;
|
|
10342
|
+
const newConfig = { ...existingConfig, ...config };
|
|
10343
|
+
const cleanConfig = {};
|
|
10344
|
+
if (newConfig.workspaceId)
|
|
10345
|
+
cleanConfig.workspaceId = newConfig.workspaceId;
|
|
10346
|
+
if (newConfig.projectId)
|
|
10347
|
+
cleanConfig.projectId = newConfig.projectId;
|
|
10348
|
+
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
9202
10349
|
}
|
|
9203
|
-
function
|
|
9204
|
-
|
|
9205
|
-
if (!match)
|
|
9206
|
-
return [];
|
|
9207
|
-
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
10350
|
+
function hasLocalConfig(cwd) {
|
|
10351
|
+
return existsSync(getLocalConfigPath(cwd));
|
|
9208
10352
|
}
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
10353
|
+
function getApiKey() {
|
|
10354
|
+
const config = loadConfig();
|
|
10355
|
+
if (!config.apiKey) {
|
|
10356
|
+
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
10357
|
+
` + "You can generate an API key at https://gethmy.com → Settings → API Keys.");
|
|
10358
|
+
}
|
|
10359
|
+
return config.apiKey;
|
|
9213
10360
|
}
|
|
9214
|
-
function
|
|
9215
|
-
|
|
10361
|
+
function getApiUrl() {
|
|
10362
|
+
const config = loadConfig();
|
|
10363
|
+
return config.apiUrl;
|
|
9216
10364
|
}
|
|
9217
|
-
function
|
|
9218
|
-
const
|
|
9219
|
-
|
|
9220
|
-
return `${entity.type}--${slug}--${shortId}.md`;
|
|
10365
|
+
function getUserEmail() {
|
|
10366
|
+
const config = loadConfig();
|
|
10367
|
+
return config.userEmail;
|
|
9221
10368
|
}
|
|
9222
|
-
function
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
if (entity.scope === "workspace" || !entity.project_id) {
|
|
9228
|
-
return join2(wsDir, "_workspace");
|
|
10369
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
10370
|
+
if (options?.local) {
|
|
10371
|
+
saveLocalConfig({ workspaceId }, options.cwd);
|
|
10372
|
+
} else {
|
|
10373
|
+
saveConfig({ activeWorkspaceId: workspaceId });
|
|
9229
10374
|
}
|
|
9230
|
-
return join2(wsDir, entity.project_id);
|
|
9231
10375
|
}
|
|
9232
|
-
function
|
|
9233
|
-
|
|
10376
|
+
function setActiveProject(projectId, options) {
|
|
10377
|
+
if (options?.local) {
|
|
10378
|
+
saveLocalConfig({ projectId }, options.cwd);
|
|
10379
|
+
} else {
|
|
10380
|
+
saveConfig({ activeProjectId: projectId });
|
|
10381
|
+
}
|
|
9234
10382
|
}
|
|
9235
|
-
function
|
|
9236
|
-
const
|
|
9237
|
-
if (
|
|
9238
|
-
return
|
|
9239
|
-
try {
|
|
9240
|
-
return JSON.parse(readFileSync2(statePath, "utf-8"));
|
|
9241
|
-
} catch {
|
|
9242
|
-
return emptySyncState();
|
|
10383
|
+
function getActiveWorkspaceId(cwd) {
|
|
10384
|
+
const localConfig = loadLocalConfig(cwd);
|
|
10385
|
+
if (localConfig?.workspaceId) {
|
|
10386
|
+
return localConfig.workspaceId;
|
|
9243
10387
|
}
|
|
10388
|
+
return loadConfig().activeWorkspaceId;
|
|
9244
10389
|
}
|
|
9245
|
-
function
|
|
9246
|
-
|
|
9247
|
-
|
|
10390
|
+
function getActiveProjectId(cwd) {
|
|
10391
|
+
const localConfig = loadLocalConfig(cwd);
|
|
10392
|
+
if (localConfig?.projectId) {
|
|
10393
|
+
return localConfig.projectId;
|
|
9248
10394
|
}
|
|
9249
|
-
|
|
10395
|
+
return loadConfig().activeProjectId;
|
|
9250
10396
|
}
|
|
9251
|
-
function
|
|
9252
|
-
const
|
|
9253
|
-
|
|
9254
|
-
mkdirSync2(dir, { recursive: true });
|
|
9255
|
-
const filename = entityToFilename(entity);
|
|
9256
|
-
const filePath = join2(dir, filename);
|
|
9257
|
-
const markdown = serializeSyncMarkdown(entity);
|
|
9258
|
-
writeFileSync2(filePath, markdown);
|
|
9259
|
-
return relative(memoryDir, filePath);
|
|
10397
|
+
function isConfigured() {
|
|
10398
|
+
const config = loadConfig();
|
|
10399
|
+
return !!config.apiKey;
|
|
9260
10400
|
}
|
|
9261
|
-
function
|
|
9262
|
-
const
|
|
9263
|
-
|
|
9264
|
-
|
|
10401
|
+
function areSkillsInstalled(cwd) {
|
|
10402
|
+
const home = homedir();
|
|
10403
|
+
const workingDir = cwd || process.cwd();
|
|
10404
|
+
const foundPaths = [];
|
|
10405
|
+
const globalSkillsDir = join(home, ".agents", "skills");
|
|
10406
|
+
const globalSkillPath = join(globalSkillsDir, "hmy", "SKILL.md");
|
|
10407
|
+
if (existsSync(globalSkillPath)) {
|
|
10408
|
+
foundPaths.push(globalSkillPath);
|
|
10409
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
9265
10410
|
}
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
return results;
|
|
9271
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
9272
|
-
const fullPath = join2(dir, entry.name);
|
|
9273
|
-
if (entry.isDirectory()) {
|
|
9274
|
-
results.push(...findMarkdownFiles(fullPath));
|
|
9275
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
9276
|
-
results.push(fullPath);
|
|
9277
|
-
}
|
|
10411
|
+
const claudeGlobalSkill = join(home, ".claude", "skills", "hmy.md");
|
|
10412
|
+
if (existsSync(claudeGlobalSkill)) {
|
|
10413
|
+
foundPaths.push(claudeGlobalSkill);
|
|
10414
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
9278
10415
|
}
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
const state = loadSyncState(memoryDir);
|
|
9284
|
-
const result = {
|
|
9285
|
-
pulled: 0,
|
|
9286
|
-
pushed: 0,
|
|
9287
|
-
deleted: 0,
|
|
9288
|
-
conflicts: 0,
|
|
9289
|
-
errors: []
|
|
9290
|
-
};
|
|
9291
|
-
const allEntities = [];
|
|
9292
|
-
let offset = 0;
|
|
9293
|
-
const batchSize = 100;
|
|
9294
|
-
while (true) {
|
|
9295
|
-
try {
|
|
9296
|
-
const resp = await client.listMemoryEntities({
|
|
9297
|
-
workspace_id: workspaceId,
|
|
9298
|
-
project_id: projectId,
|
|
9299
|
-
limit: batchSize,
|
|
9300
|
-
offset
|
|
9301
|
-
});
|
|
9302
|
-
const batch = resp.entities;
|
|
9303
|
-
allEntities.push(...batch);
|
|
9304
|
-
if (batch.length < batchSize)
|
|
9305
|
-
break;
|
|
9306
|
-
offset += batchSize;
|
|
9307
|
-
} catch (err) {
|
|
9308
|
-
result.errors.push(`Failed to fetch entities: ${err}`);
|
|
9309
|
-
return result;
|
|
9310
|
-
}
|
|
10416
|
+
const claudeGlobalSkillAlt = join(home, ".claude", "skills", "hmy", "SKILL.md");
|
|
10417
|
+
if (existsSync(claudeGlobalSkillAlt)) {
|
|
10418
|
+
foundPaths.push(claudeGlobalSkillAlt);
|
|
10419
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
9311
10420
|
}
|
|
9312
|
-
const
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
const hash = computeFileHash(markdown);
|
|
9317
|
-
if (!existing) {
|
|
9318
|
-
const relPath = writeEntityFile(entity, memoryDir);
|
|
9319
|
-
state.entities[entity.id] = {
|
|
9320
|
-
filePath: relPath,
|
|
9321
|
-
remoteUpdatedAt: entity.updated_at,
|
|
9322
|
-
lastSyncedHash: hash
|
|
9323
|
-
};
|
|
9324
|
-
result.pulled++;
|
|
9325
|
-
} else {
|
|
9326
|
-
const remoteChanged = entity.updated_at > existing.remoteUpdatedAt;
|
|
9327
|
-
if (!remoteChanged)
|
|
9328
|
-
continue;
|
|
9329
|
-
const absPath = join2(memoryDir, existing.filePath);
|
|
9330
|
-
let localChanged = false;
|
|
9331
|
-
if (existsSync2(absPath)) {
|
|
9332
|
-
const localContent = readFileSync2(absPath, "utf-8");
|
|
9333
|
-
const localHash = computeFileHash(localContent);
|
|
9334
|
-
localChanged = localHash !== existing.lastSyncedHash;
|
|
9335
|
-
}
|
|
9336
|
-
if (localChanged) {
|
|
9337
|
-
result.conflicts++;
|
|
9338
|
-
}
|
|
9339
|
-
const newRelPath = writeEntityFile(entity, memoryDir);
|
|
9340
|
-
if (existing.filePath !== newRelPath) {
|
|
9341
|
-
deleteEntityFile(existing.filePath, memoryDir);
|
|
9342
|
-
}
|
|
9343
|
-
state.entities[entity.id] = {
|
|
9344
|
-
filePath: newRelPath,
|
|
9345
|
-
remoteUpdatedAt: entity.updated_at,
|
|
9346
|
-
lastSyncedHash: hash
|
|
9347
|
-
};
|
|
9348
|
-
result.pulled++;
|
|
9349
|
-
}
|
|
10421
|
+
const localSkillPath = join(workingDir, ".claude", "skills", "hmy.md");
|
|
10422
|
+
if (existsSync(localSkillPath)) {
|
|
10423
|
+
foundPaths.push(localSkillPath);
|
|
10424
|
+
return { installed: true, location: "local", paths: foundPaths };
|
|
9350
10425
|
}
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
result.deleted++;
|
|
9356
|
-
}
|
|
10426
|
+
const localSkillPathAlt = join(workingDir, ".claude", "skills", "hmy", "SKILL.md");
|
|
10427
|
+
if (existsSync(localSkillPathAlt)) {
|
|
10428
|
+
foundPaths.push(localSkillPathAlt);
|
|
10429
|
+
return { installed: true, location: "local", paths: foundPaths };
|
|
9357
10430
|
}
|
|
9358
|
-
|
|
9359
|
-
saveSyncState(memoryDir, state);
|
|
9360
|
-
return result;
|
|
10431
|
+
return { installed: false, location: null, paths: [] };
|
|
9361
10432
|
}
|
|
9362
|
-
|
|
9363
|
-
const
|
|
9364
|
-
|
|
9365
|
-
const result = {
|
|
9366
|
-
pulled: 0,
|
|
9367
|
-
pushed: 0,
|
|
9368
|
-
deleted: 0,
|
|
9369
|
-
conflicts: 0,
|
|
9370
|
-
errors: []
|
|
9371
|
-
};
|
|
9372
|
-
const mdFiles = findMarkdownFiles(memoryDir);
|
|
9373
|
-
for (const absPath of mdFiles) {
|
|
9374
|
-
const content = readFileSync2(absPath, "utf-8");
|
|
9375
|
-
const hash = computeFileHash(content);
|
|
9376
|
-
const parsed = parseSyncMarkdown(content);
|
|
9377
|
-
if (parsed.frontmatter.id) {
|
|
9378
|
-
const entityId = parsed.frontmatter.id;
|
|
9379
|
-
const existing = state.entities[entityId];
|
|
9380
|
-
if (existing && hash === existing.lastSyncedHash)
|
|
9381
|
-
continue;
|
|
9382
|
-
try {
|
|
9383
|
-
const resp = await client.updateMemoryEntity(entityId, {
|
|
9384
|
-
title: parsed.title || "Untitled",
|
|
9385
|
-
content: parsed.content,
|
|
9386
|
-
type: parsed.frontmatter.type,
|
|
9387
|
-
scope: parsed.frontmatter.scope,
|
|
9388
|
-
confidence: parsed.frontmatter.confidence,
|
|
9389
|
-
tags: parsed.frontmatter.tags
|
|
9390
|
-
});
|
|
9391
|
-
const updated = resp.entity;
|
|
9392
|
-
const newMarkdown = serializeSyncMarkdown(updated);
|
|
9393
|
-
writeFileSync2(absPath, newMarkdown);
|
|
9394
|
-
const relPath = relative(memoryDir, absPath);
|
|
9395
|
-
state.entities[entityId] = {
|
|
9396
|
-
filePath: relPath,
|
|
9397
|
-
remoteUpdatedAt: updated.updated_at,
|
|
9398
|
-
lastSyncedHash: computeFileHash(newMarkdown)
|
|
9399
|
-
};
|
|
9400
|
-
result.pushed++;
|
|
9401
|
-
} catch (err) {
|
|
9402
|
-
result.errors.push(`Failed to update ${entityId}: ${err}`);
|
|
9403
|
-
}
|
|
9404
|
-
} else {
|
|
9405
|
-
const relPath = relative(memoryDir, absPath);
|
|
9406
|
-
const parts = relPath.split(sep);
|
|
9407
|
-
const fileWorkspaceId = parts.length >= 2 ? parts[0] : workspaceId;
|
|
9408
|
-
let fileProjectId;
|
|
9409
|
-
if (parts.length >= 3) {
|
|
9410
|
-
const scopeDir = parts[1];
|
|
9411
|
-
if (scopeDir !== "_workspace" && scopeDir !== "_private") {
|
|
9412
|
-
fileProjectId = scopeDir;
|
|
9413
|
-
}
|
|
9414
|
-
}
|
|
9415
|
-
let scope = parsed.frontmatter.scope || "project";
|
|
9416
|
-
if (parts.length >= 3) {
|
|
9417
|
-
const scopeDir = parts[1];
|
|
9418
|
-
if (scopeDir === "_private")
|
|
9419
|
-
scope = "private";
|
|
9420
|
-
else if (scopeDir === "_workspace")
|
|
9421
|
-
scope = "workspace";
|
|
9422
|
-
}
|
|
9423
|
-
try {
|
|
9424
|
-
const resp = await client.createMemoryEntity({
|
|
9425
|
-
workspace_id: fileWorkspaceId,
|
|
9426
|
-
project_id: fileProjectId,
|
|
9427
|
-
type: parsed.frontmatter.type,
|
|
9428
|
-
scope,
|
|
9429
|
-
title: parsed.title || "Untitled",
|
|
9430
|
-
content: parsed.content,
|
|
9431
|
-
confidence: parsed.frontmatter.confidence,
|
|
9432
|
-
tags: parsed.frontmatter.tags,
|
|
9433
|
-
agent_identifier: parsed.frontmatter.agent
|
|
9434
|
-
});
|
|
9435
|
-
const created = resp.entity;
|
|
9436
|
-
const dir = entityToDirectoryPath(created, memoryDir);
|
|
9437
|
-
if (!existsSync2(dir))
|
|
9438
|
-
mkdirSync2(dir, { recursive: true });
|
|
9439
|
-
const newFilename = entityToFilename(created);
|
|
9440
|
-
const newAbsPath = join2(dir, newFilename);
|
|
9441
|
-
const newMarkdown = serializeSyncMarkdown(created);
|
|
9442
|
-
writeFileSync2(newAbsPath, newMarkdown);
|
|
9443
|
-
if (absPath !== newAbsPath && existsSync2(absPath)) {
|
|
9444
|
-
rmSync(absPath);
|
|
9445
|
-
}
|
|
9446
|
-
const newRelPath = relative(memoryDir, newAbsPath);
|
|
9447
|
-
state.entities[created.id] = {
|
|
9448
|
-
filePath: newRelPath,
|
|
9449
|
-
remoteUpdatedAt: created.updated_at,
|
|
9450
|
-
lastSyncedHash: computeFileHash(newMarkdown)
|
|
9451
|
-
};
|
|
9452
|
-
result.pushed++;
|
|
9453
|
-
} catch (err) {
|
|
9454
|
-
result.errors.push(`Failed to create entity from ${relPath}: ${err}`);
|
|
9455
|
-
}
|
|
9456
|
-
}
|
|
9457
|
-
}
|
|
9458
|
-
saveSyncState(memoryDir, state);
|
|
9459
|
-
return result;
|
|
10433
|
+
function hasProjectContext(cwd) {
|
|
10434
|
+
const localConfig = loadLocalConfig(cwd);
|
|
10435
|
+
return !!(localConfig?.workspaceId || localConfig?.projectId);
|
|
9460
10436
|
}
|
|
9461
|
-
|
|
9462
|
-
const
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
pushed: pushResult.pushed,
|
|
9467
|
-
deleted: pullResult.deleted,
|
|
9468
|
-
conflicts: pullResult.conflicts,
|
|
9469
|
-
errors: [...pullResult.errors, ...pushResult.errors]
|
|
9470
|
-
};
|
|
10437
|
+
function getMemoryDir() {
|
|
10438
|
+
const config = loadConfig();
|
|
10439
|
+
if (config.memoryDir)
|
|
10440
|
+
return config.memoryDir;
|
|
10441
|
+
return join(homedir(), ".harmony", "memory");
|
|
9471
10442
|
}
|
|
10443
|
+
|
|
10444
|
+
// src/server.ts
|
|
10445
|
+
init_src();
|
|
10446
|
+
|
|
9472
10447
|
// ../../node_modules/zod/v4/core/index.js
|
|
9473
10448
|
var exports_core2 = {};
|
|
9474
10449
|
__export(exports_core2, {
|
|
@@ -26600,6 +27575,100 @@ class HarmonyApiClient {
|
|
|
26600
27575
|
async generateApiKey(name) {
|
|
26601
27576
|
return this.request("POST", "/api-keys", { name });
|
|
26602
27577
|
}
|
|
27578
|
+
async generateCardPrompt(options) {
|
|
27579
|
+
const { assembleContext: assembleContext2, cacheManifest: cacheManifest2, generatePrompt: generatePrompt2 } = await loadPromptModules();
|
|
27580
|
+
const cardResult = await this.getCard(options.cardId);
|
|
27581
|
+
const cardData = cardResult.card;
|
|
27582
|
+
let columnData = null;
|
|
27583
|
+
const projectIdForBoard = options.projectId || cardData.project_id;
|
|
27584
|
+
if (projectIdForBoard) {
|
|
27585
|
+
try {
|
|
27586
|
+
const board = await this.getBoard(projectIdForBoard, { summary: true });
|
|
27587
|
+
const column = board.columns.find((col) => col.id === cardData.column_id);
|
|
27588
|
+
if (column) {
|
|
27589
|
+
columnData = { name: column.name };
|
|
27590
|
+
}
|
|
27591
|
+
} catch {}
|
|
27592
|
+
}
|
|
27593
|
+
const variant = options.variant || "execute";
|
|
27594
|
+
let assembledContextStr;
|
|
27595
|
+
let assemblyId;
|
|
27596
|
+
let memories;
|
|
27597
|
+
try {
|
|
27598
|
+
if (options.workspaceId && cardData.title) {
|
|
27599
|
+
const cardLabels = (cardData.labels || []).map((l) => l.name);
|
|
27600
|
+
const taskContext = [cardData.title, cardData.description || ""].filter(Boolean).join(" ");
|
|
27601
|
+
const assembled = await assembleContext2({
|
|
27602
|
+
workspaceId: options.workspaceId,
|
|
27603
|
+
projectId: options.projectId,
|
|
27604
|
+
taskContext,
|
|
27605
|
+
cardLabels,
|
|
27606
|
+
cardId: cardData.id,
|
|
27607
|
+
client: this
|
|
27608
|
+
});
|
|
27609
|
+
if (assembled.context) {
|
|
27610
|
+
assembledContextStr = assembled.context;
|
|
27611
|
+
assemblyId = assembled.manifest.assemblyId;
|
|
27612
|
+
cacheManifest2(assembled.manifest);
|
|
27613
|
+
}
|
|
27614
|
+
}
|
|
27615
|
+
} catch (err) {
|
|
27616
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27617
|
+
console.debug(`[generateCardPrompt] Context assembly failed: ${msg}`);
|
|
27618
|
+
try {
|
|
27619
|
+
if (options.workspaceId && cardData.title) {
|
|
27620
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
27621
|
+
project_id: options.projectId,
|
|
27622
|
+
limit: 5
|
|
27623
|
+
});
|
|
27624
|
+
if (memoryResult.entities?.length > 0) {
|
|
27625
|
+
memories = memoryResult.entities.map((e) => ({
|
|
27626
|
+
id: e.id,
|
|
27627
|
+
type: e.type,
|
|
27628
|
+
title: e.title,
|
|
27629
|
+
content: e.content,
|
|
27630
|
+
confidence: e.confidence,
|
|
27631
|
+
tags: e.tags || []
|
|
27632
|
+
}));
|
|
27633
|
+
}
|
|
27634
|
+
}
|
|
27635
|
+
} catch (fallbackErr) {
|
|
27636
|
+
const fallbackMsg = fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr);
|
|
27637
|
+
console.debug(`[generateCardPrompt] Memory fallback also failed: ${fallbackMsg}`);
|
|
27638
|
+
}
|
|
27639
|
+
}
|
|
27640
|
+
const result = generatePrompt2({
|
|
27641
|
+
card: cardData,
|
|
27642
|
+
column: columnData,
|
|
27643
|
+
variant,
|
|
27644
|
+
contextOptions: options.contextOptions,
|
|
27645
|
+
customConstraints: options.customConstraints,
|
|
27646
|
+
memories,
|
|
27647
|
+
assembledContext: assembledContextStr,
|
|
27648
|
+
assemblyId
|
|
27649
|
+
});
|
|
27650
|
+
return {
|
|
27651
|
+
...result,
|
|
27652
|
+
cardId: cardData.id,
|
|
27653
|
+
shortId: cardData.short_id,
|
|
27654
|
+
title: cardData.title
|
|
27655
|
+
};
|
|
27656
|
+
}
|
|
27657
|
+
}
|
|
27658
|
+
var _promptModules = null;
|
|
27659
|
+
async function loadPromptModules() {
|
|
27660
|
+
if (!_promptModules) {
|
|
27661
|
+
const [ca, pb] = await Promise.all([
|
|
27662
|
+
Promise.resolve().then(() => (init_context_assembly(), exports_context_assembly)),
|
|
27663
|
+
Promise.resolve().then(() => (init_prompt_builder(), exports_prompt_builder))
|
|
27664
|
+
]);
|
|
27665
|
+
_promptModules = {
|
|
27666
|
+
assembleContext: ca.assembleContext,
|
|
27667
|
+
cacheManifest: ca.cacheManifest,
|
|
27668
|
+
generatePrompt: pb.generatePrompt
|
|
27669
|
+
};
|
|
27670
|
+
}
|
|
27671
|
+
return _promptModules;
|
|
26603
27672
|
}
|
|
26604
27673
|
var client2 = null;
|
|
26605
27674
|
function getClient() {
|
|
@@ -26613,6 +27682,28 @@ function resetClient() {
|
|
|
26613
27682
|
}
|
|
26614
27683
|
|
|
26615
27684
|
// src/auto-session.ts
|
|
27685
|
+
var CLIENT_DISPLAY_NAMES = {
|
|
27686
|
+
"claude-code": "Claude Code",
|
|
27687
|
+
"claude-desktop": "Claude Desktop",
|
|
27688
|
+
cursor: "Cursor",
|
|
27689
|
+
windsurf: "Windsurf",
|
|
27690
|
+
cline: "Cline",
|
|
27691
|
+
continue: "Continue",
|
|
27692
|
+
"codex-cli": "OpenAI Codex",
|
|
27693
|
+
zed: "Zed",
|
|
27694
|
+
"gemini-cli": "Gemini CLI"
|
|
27695
|
+
};
|
|
27696
|
+
function toIdentifier(name) {
|
|
27697
|
+
return name.toLowerCase().replace(/\s+/g, "-");
|
|
27698
|
+
}
|
|
27699
|
+
function resolveAgentIdentity(info) {
|
|
27700
|
+
if (!info?.name) {
|
|
27701
|
+
return { agentIdentifier: "unknown", agentName: "Unknown Agent" };
|
|
27702
|
+
}
|
|
27703
|
+
const key = toIdentifier(info.name);
|
|
27704
|
+
const displayName = CLIENT_DISPLAY_NAMES[key] ?? info.name;
|
|
27705
|
+
return { agentIdentifier: key, agentName: displayName };
|
|
27706
|
+
}
|
|
26616
27707
|
var AUTO_START_TRIGGERS = new Set([
|
|
26617
27708
|
"harmony_generate_prompt",
|
|
26618
27709
|
"harmony_update_card",
|
|
@@ -26628,9 +27719,11 @@ var activeSessions = new Map;
|
|
|
26628
27719
|
var inactivityTimer = null;
|
|
26629
27720
|
var endCallback = null;
|
|
26630
27721
|
var clientGetter = null;
|
|
26631
|
-
|
|
27722
|
+
var clientInfoGetter = null;
|
|
27723
|
+
function initAutoSession(callback, getClient2, getClientInfo) {
|
|
26632
27724
|
endCallback = callback;
|
|
26633
27725
|
clientGetter = getClient2;
|
|
27726
|
+
clientInfoGetter = getClientInfo ?? null;
|
|
26634
27727
|
if (inactivityTimer)
|
|
26635
27728
|
clearInterval(inactivityTimer);
|
|
26636
27729
|
inactivityTimer = setInterval(checkInactivity, CHECK_INTERVAL_MS);
|
|
@@ -26656,10 +27749,12 @@ async function trackActivity(cardId, options) {
|
|
|
26656
27749
|
for (const otherCardId of toEnd) {
|
|
26657
27750
|
await autoEndSession(client3, otherCardId, "completed");
|
|
26658
27751
|
}
|
|
27752
|
+
const info = clientInfoGetter?.() ?? null;
|
|
27753
|
+
const { agentIdentifier, agentName } = resolveAgentIdentity(info);
|
|
26659
27754
|
try {
|
|
26660
27755
|
await client3.startAgentSession(cardId, {
|
|
26661
|
-
agentIdentifier
|
|
26662
|
-
agentName
|
|
27756
|
+
agentIdentifier,
|
|
27757
|
+
agentName,
|
|
26663
27758
|
status: "working"
|
|
26664
27759
|
});
|
|
26665
27760
|
} catch {}
|
|
@@ -26668,22 +27763,26 @@ async function trackActivity(cardId, options) {
|
|
|
26668
27763
|
startedAt: now,
|
|
26669
27764
|
lastActivityAt: now,
|
|
26670
27765
|
isExplicit: false,
|
|
26671
|
-
agentIdentifier
|
|
26672
|
-
agentName
|
|
27766
|
+
agentIdentifier,
|
|
27767
|
+
agentName
|
|
26673
27768
|
});
|
|
26674
27769
|
}
|
|
26675
|
-
function markExplicit(cardId) {
|
|
27770
|
+
function markExplicit(cardId, options) {
|
|
26676
27771
|
const existing = activeSessions.get(cardId);
|
|
26677
27772
|
if (existing) {
|
|
26678
27773
|
existing.isExplicit = true;
|
|
27774
|
+
if (options?.agentIdentifier)
|
|
27775
|
+
existing.agentIdentifier = options.agentIdentifier;
|
|
27776
|
+
if (options?.agentName)
|
|
27777
|
+
existing.agentName = options.agentName;
|
|
26679
27778
|
} else {
|
|
26680
27779
|
activeSessions.set(cardId, {
|
|
26681
27780
|
cardId,
|
|
26682
27781
|
startedAt: Date.now(),
|
|
26683
27782
|
lastActivityAt: Date.now(),
|
|
26684
27783
|
isExplicit: true,
|
|
26685
|
-
agentIdentifier: "explicit",
|
|
26686
|
-
agentName: "Explicit Agent"
|
|
27784
|
+
agentIdentifier: options?.agentIdentifier ?? "explicit",
|
|
27785
|
+
agentName: options?.agentName ?? "Explicit Agent"
|
|
26687
27786
|
});
|
|
26688
27787
|
}
|
|
26689
27788
|
}
|
|
@@ -26706,6 +27805,7 @@ function destroyAutoSession() {
|
|
|
26706
27805
|
activeSessions.clear();
|
|
26707
27806
|
endCallback = null;
|
|
26708
27807
|
clientGetter = null;
|
|
27808
|
+
clientInfoGetter = null;
|
|
26709
27809
|
}
|
|
26710
27810
|
function checkInactivity() {
|
|
26711
27811
|
const now = Date.now();
|
|
@@ -26925,504 +28025,11 @@ function deriveClusterTitle(cluster, type) {
|
|
|
26925
28025
|
return `Consolidated ${type}: ${suffix}`;
|
|
26926
28026
|
}
|
|
26927
28027
|
|
|
26928
|
-
// src/
|
|
26929
|
-
|
|
26930
|
-
var MAX_TOKENS_PER_ENTITY = 500;
|
|
26931
|
-
var MIN_RELEVANCE_THRESHOLD = 0.1;
|
|
26932
|
-
var TIER_WEIGHTS = {
|
|
26933
|
-
reference: 1,
|
|
26934
|
-
episode: 0.7,
|
|
26935
|
-
draft: 0.4
|
|
26936
|
-
};
|
|
26937
|
-
var PROCEDURE_BUDGET_FRACTION = 0.15;
|
|
26938
|
-
var TIER_BUDGET_ALLOCATION = {
|
|
26939
|
-
reference: 0.6,
|
|
26940
|
-
episode: 0.3,
|
|
26941
|
-
draft: 0.1
|
|
26942
|
-
};
|
|
26943
|
-
var MIN_REFERENCE_SLOTS = 3;
|
|
26944
|
-
function estimateTokens(text) {
|
|
26945
|
-
return Math.ceil(text.length / 4);
|
|
26946
|
-
}
|
|
26947
|
-
function generateAssemblyId() {
|
|
26948
|
-
return `ctx_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
26949
|
-
}
|
|
26950
|
-
function truncateContent(content, maxTokens) {
|
|
26951
|
-
const currentTokens = estimateTokens(content);
|
|
26952
|
-
if (currentTokens <= maxTokens) {
|
|
26953
|
-
return { text: content, truncated: false };
|
|
26954
|
-
}
|
|
26955
|
-
const paragraphs = content.split(/\n\n+/);
|
|
26956
|
-
let result = paragraphs[0];
|
|
26957
|
-
for (let i = 1;i < paragraphs.length; i++) {
|
|
26958
|
-
const lines = paragraphs[i].split(`
|
|
26959
|
-
`).filter((l) => l.startsWith("- ") || l.startsWith("* "));
|
|
26960
|
-
if (lines.length > 0) {
|
|
26961
|
-
const bulletSection = lines.join(`
|
|
26962
|
-
`);
|
|
26963
|
-
if (estimateTokens(result + `
|
|
26964
|
-
|
|
26965
|
-
` + bulletSection) <= maxTokens) {
|
|
26966
|
-
result += `
|
|
26967
|
-
|
|
26968
|
-
` + bulletSection;
|
|
26969
|
-
}
|
|
26970
|
-
}
|
|
26971
|
-
}
|
|
26972
|
-
if (estimateTokens(result) > maxTokens) {
|
|
26973
|
-
const maxChars = maxTokens * 4;
|
|
26974
|
-
result = result.slice(0, maxChars - 3) + "...";
|
|
26975
|
-
}
|
|
26976
|
-
return { text: result, truncated: true };
|
|
26977
|
-
}
|
|
26978
|
-
function computeRelevanceScore(entity, taskContext, cardLabels) {
|
|
26979
|
-
const reasons = [];
|
|
26980
|
-
let score = 0;
|
|
26981
|
-
const hasRrfScore = entity.rrf_score !== undefined && entity.rrf_score > 0;
|
|
26982
|
-
if (hasRrfScore) {
|
|
26983
|
-
const normalizedRrf = Math.min(entity.rrf_score / 0.04, 1);
|
|
26984
|
-
const rrfContribution = normalizedRrf * 0.3;
|
|
26985
|
-
score += rrfContribution;
|
|
26986
|
-
reasons.push(`hybrid_search(rrf=${entity.rrf_score.toFixed(4)})`);
|
|
26987
|
-
}
|
|
26988
|
-
const textMatchWeight = hasRrfScore ? 0.15 : 0.4;
|
|
26989
|
-
const taskWords = new Set(taskContext.toLowerCase().split(/\W+/).filter((w) => w.length > 2));
|
|
26990
|
-
const entityWords = new Set(`${entity.title} ${entity.content}`.toLowerCase().split(/\W+/).filter((w) => w.length > 2));
|
|
26991
|
-
const overlap = [...taskWords].filter((w) => entityWords.has(w));
|
|
26992
|
-
if (overlap.length > 0) {
|
|
26993
|
-
const textScore = Math.min(overlap.length / Math.max(taskWords.size, 1), 1) * textMatchWeight;
|
|
26994
|
-
score += textScore;
|
|
26995
|
-
reasons.push(`text_match(${overlap.length} words)`);
|
|
26996
|
-
}
|
|
26997
|
-
if (cardLabels.length > 0 && entity.tags.length > 0) {
|
|
26998
|
-
const labelSet = new Set(cardLabels.map((l) => l.toLowerCase()));
|
|
26999
|
-
const tagOverlap = entity.tags.filter((t) => labelSet.has(t.toLowerCase()));
|
|
27000
|
-
if (tagOverlap.length > 0) {
|
|
27001
|
-
const tagScore = tagOverlap.length / cardLabels.length * 0.3;
|
|
27002
|
-
score += tagScore;
|
|
27003
|
-
reasons.push(`tag_match(${tagOverlap.join(",")})`);
|
|
27004
|
-
}
|
|
27005
|
-
}
|
|
27006
|
-
score += entity.confidence * 0.15;
|
|
27007
|
-
if (entity.confidence >= 0.9) {
|
|
27008
|
-
reasons.push("high_confidence");
|
|
27009
|
-
}
|
|
27010
|
-
if (entity.last_accessed_at) {
|
|
27011
|
-
const daysSinceAccess = (Date.now() - new Date(entity.last_accessed_at).getTime()) / (1000 * 60 * 60 * 24);
|
|
27012
|
-
const halfLife = { draft: 7, episode: 30, reference: 180 }[entity.memory_tier];
|
|
27013
|
-
const recencyScore = 0.5 ** (daysSinceAccess / halfLife) * 0.1;
|
|
27014
|
-
score += recencyScore;
|
|
27015
|
-
if (daysSinceAccess < 7)
|
|
27016
|
-
reasons.push("recently_accessed");
|
|
27017
|
-
}
|
|
27018
|
-
if (entity.access_count > 0) {
|
|
27019
|
-
const freqScore = Math.log10(entity.access_count + 1) * 0.05;
|
|
27020
|
-
score += Math.min(freqScore, 0.1);
|
|
27021
|
-
if (entity.access_count >= 5)
|
|
27022
|
-
reasons.push(`frequently_used(${entity.access_count})`);
|
|
27023
|
-
}
|
|
27024
|
-
const usefulnessScore = entity.metadata?.usefulness_score ?? 0;
|
|
27025
|
-
if (usefulnessScore >= 3) {
|
|
27026
|
-
const usefulnessBoost = Math.min(usefulnessScore / 20, 0.15);
|
|
27027
|
-
score += usefulnessBoost;
|
|
27028
|
-
reasons.push(`useful(${usefulnessScore})`);
|
|
27029
|
-
} else if (usefulnessScore === 0 && entity.access_count >= 5) {
|
|
27030
|
-
score -= 0.02;
|
|
27031
|
-
reasons.push("low_usefulness");
|
|
27032
|
-
}
|
|
27033
|
-
if (entity.type === "procedure") {
|
|
27034
|
-
score += 0.1;
|
|
27035
|
-
reasons.push("procedure_boost");
|
|
27036
|
-
}
|
|
27037
|
-
score = Math.min(score, 1);
|
|
27038
|
-
const tierWeight = TIER_WEIGHTS[entity.memory_tier];
|
|
27039
|
-
score *= tierWeight;
|
|
27040
|
-
return { score, reasons };
|
|
27041
|
-
}
|
|
27042
|
-
async function assembleContext(options) {
|
|
27043
|
-
const {
|
|
27044
|
-
workspaceId,
|
|
27045
|
-
projectId,
|
|
27046
|
-
taskContext,
|
|
27047
|
-
cardLabels = [],
|
|
27048
|
-
tokenBudget = DEFAULT_TOKEN_BUDGET,
|
|
27049
|
-
client: client3
|
|
27050
|
-
} = options;
|
|
27051
|
-
const assemblyId = generateAssemblyId();
|
|
27052
|
-
const manifest = {
|
|
27053
|
-
assemblyId,
|
|
27054
|
-
timestamp: new Date().toISOString(),
|
|
27055
|
-
included: [],
|
|
27056
|
-
excluded: [],
|
|
27057
|
-
budgetUsed: 0,
|
|
27058
|
-
budgetTotal: tokenBudget,
|
|
27059
|
-
tierBreakdown: {
|
|
27060
|
-
draft: { count: 0, tokens: 0 },
|
|
27061
|
-
episode: { count: 0, tokens: 0 },
|
|
27062
|
-
reference: { count: 0, tokens: 0 }
|
|
27063
|
-
}
|
|
27064
|
-
};
|
|
27065
|
-
let candidates = [];
|
|
27066
|
-
try {
|
|
27067
|
-
const searchResult = await client3.searchMemoryEntities(workspaceId, taskContext, { project_id: projectId, limit: 30 });
|
|
27068
|
-
if (searchResult.entities?.length > 0) {
|
|
27069
|
-
candidates = searchResult.entities.map(mapToContextEntity);
|
|
27070
|
-
}
|
|
27071
|
-
} catch {}
|
|
27072
|
-
if (candidates.length < 10 && projectId) {
|
|
27073
|
-
try {
|
|
27074
|
-
const listResult = await client3.listMemoryEntities({
|
|
27075
|
-
workspace_id: workspaceId,
|
|
27076
|
-
project_id: projectId,
|
|
27077
|
-
limit: 30
|
|
27078
|
-
});
|
|
27079
|
-
if (listResult.entities?.length > 0) {
|
|
27080
|
-
const existingIds = new Set(candidates.map((c) => c.id));
|
|
27081
|
-
const additional = listResult.entities.map(mapToContextEntity).filter((e) => !existingIds.has(e.id));
|
|
27082
|
-
candidates.push(...additional);
|
|
27083
|
-
}
|
|
27084
|
-
} catch {}
|
|
27085
|
-
}
|
|
27086
|
-
if (candidates.length < 20) {
|
|
27087
|
-
try {
|
|
27088
|
-
const wsResult = await client3.listMemoryEntities({
|
|
27089
|
-
workspace_id: workspaceId,
|
|
27090
|
-
scope: "workspace",
|
|
27091
|
-
limit: 20
|
|
27092
|
-
});
|
|
27093
|
-
if (wsResult.entities?.length > 0) {
|
|
27094
|
-
const existingIds = new Set(candidates.map((c) => c.id));
|
|
27095
|
-
const additional = wsResult.entities.map(mapToContextEntity).filter((e) => !existingIds.has(e.id));
|
|
27096
|
-
candidates.push(...additional);
|
|
27097
|
-
}
|
|
27098
|
-
} catch {}
|
|
27099
|
-
}
|
|
27100
|
-
if (candidates.length === 0) {
|
|
27101
|
-
return {
|
|
27102
|
-
context: "",
|
|
27103
|
-
manifest,
|
|
27104
|
-
memories: []
|
|
27105
|
-
};
|
|
27106
|
-
}
|
|
27107
|
-
const scored = candidates.map((entity) => {
|
|
27108
|
-
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels);
|
|
27109
|
-
return { entity, score, reasons };
|
|
27110
|
-
});
|
|
27111
|
-
scored.sort((a, b) => b.score - a.score);
|
|
27112
|
-
const procedureBudget = Math.floor(tokenBudget * PROCEDURE_BUDGET_FRACTION);
|
|
27113
|
-
const remainingBudget = tokenBudget - procedureBudget;
|
|
27114
|
-
const tierBudgets = {
|
|
27115
|
-
reference: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.reference),
|
|
27116
|
-
episode: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.episode),
|
|
27117
|
-
draft: Math.floor(remainingBudget * TIER_BUDGET_ALLOCATION.draft)
|
|
27118
|
-
};
|
|
27119
|
-
const tierUsed = {
|
|
27120
|
-
reference: 0,
|
|
27121
|
-
episode: 0,
|
|
27122
|
-
draft: 0
|
|
27123
|
-
};
|
|
27124
|
-
let procedureUsed = 0;
|
|
27125
|
-
const included = [];
|
|
27126
|
-
let totalUsed = 0;
|
|
27127
|
-
let referenceCount = 0;
|
|
27128
|
-
for (const item of scored) {
|
|
27129
|
-
if (item.entity.memory_tier === "reference" && item.entity.type !== "procedure" && referenceCount < MIN_REFERENCE_SLOTS) {
|
|
27130
|
-
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
27131
|
-
const tokens = estimateTokens(`### ${item.entity.title}
|
|
27132
|
-
${text}`);
|
|
27133
|
-
if (totalUsed + tokens <= tokenBudget) {
|
|
27134
|
-
included.push({ ...item, tokens, truncated });
|
|
27135
|
-
item.entity.content = text;
|
|
27136
|
-
totalUsed += tokens;
|
|
27137
|
-
tierUsed.reference += tokens;
|
|
27138
|
-
referenceCount++;
|
|
27139
|
-
}
|
|
27140
|
-
}
|
|
27141
|
-
}
|
|
27142
|
-
const includedIds = new Set(included.map((i) => i.entity.id));
|
|
27143
|
-
const procedureCandidates = scored.filter((item) => item.entity.type === "procedure" && !includedIds.has(item.entity.id));
|
|
27144
|
-
for (const item of procedureCandidates) {
|
|
27145
|
-
if (item.score < MIN_RELEVANCE_THRESHOLD) {
|
|
27146
|
-
manifest.excluded.push({
|
|
27147
|
-
entityId: item.entity.id,
|
|
27148
|
-
title: item.entity.title,
|
|
27149
|
-
type: item.entity.type,
|
|
27150
|
-
tier: item.entity.memory_tier,
|
|
27151
|
-
relevanceScore: item.score,
|
|
27152
|
-
reason: "below_relevance_threshold"
|
|
27153
|
-
});
|
|
27154
|
-
continue;
|
|
27155
|
-
}
|
|
27156
|
-
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
27157
|
-
const tokens = estimateTokens(`### ${item.entity.title}
|
|
27158
|
-
${text}`);
|
|
27159
|
-
if (procedureUsed + tokens > procedureBudget) {
|
|
27160
|
-
const totalRemaining = tokenBudget - totalUsed;
|
|
27161
|
-
if (tokens > totalRemaining) {
|
|
27162
|
-
manifest.excluded.push({
|
|
27163
|
-
entityId: item.entity.id,
|
|
27164
|
-
title: item.entity.title,
|
|
27165
|
-
type: item.entity.type,
|
|
27166
|
-
tier: item.entity.memory_tier,
|
|
27167
|
-
relevanceScore: item.score,
|
|
27168
|
-
reason: "procedure_budget_exceeded"
|
|
27169
|
-
});
|
|
27170
|
-
continue;
|
|
27171
|
-
}
|
|
27172
|
-
}
|
|
27173
|
-
if (totalUsed + tokens > tokenBudget) {
|
|
27174
|
-
manifest.excluded.push({
|
|
27175
|
-
entityId: item.entity.id,
|
|
27176
|
-
title: item.entity.title,
|
|
27177
|
-
type: item.entity.type,
|
|
27178
|
-
tier: item.entity.memory_tier,
|
|
27179
|
-
relevanceScore: item.score,
|
|
27180
|
-
reason: "total_budget_exceeded"
|
|
27181
|
-
});
|
|
27182
|
-
continue;
|
|
27183
|
-
}
|
|
27184
|
-
included.push({ ...item, tokens, truncated });
|
|
27185
|
-
item.entity.content = text;
|
|
27186
|
-
totalUsed += tokens;
|
|
27187
|
-
procedureUsed += tokens;
|
|
27188
|
-
includedIds.add(item.entity.id);
|
|
27189
|
-
}
|
|
27190
|
-
for (const item of scored) {
|
|
27191
|
-
if (includedIds.has(item.entity.id))
|
|
27192
|
-
continue;
|
|
27193
|
-
if (item.entity.type === "procedure")
|
|
27194
|
-
continue;
|
|
27195
|
-
if (item.score < MIN_RELEVANCE_THRESHOLD) {
|
|
27196
|
-
manifest.excluded.push({
|
|
27197
|
-
entityId: item.entity.id,
|
|
27198
|
-
title: item.entity.title,
|
|
27199
|
-
type: item.entity.type,
|
|
27200
|
-
tier: item.entity.memory_tier,
|
|
27201
|
-
relevanceScore: item.score,
|
|
27202
|
-
reason: "below_relevance_threshold"
|
|
27203
|
-
});
|
|
27204
|
-
continue;
|
|
27205
|
-
}
|
|
27206
|
-
const tier = item.entity.memory_tier;
|
|
27207
|
-
const { text, truncated } = truncateContent(item.entity.content, MAX_TOKENS_PER_ENTITY);
|
|
27208
|
-
const tokens = estimateTokens(`### ${item.entity.title}
|
|
27209
|
-
${text}`);
|
|
27210
|
-
if (tierUsed[tier] + tokens > tierBudgets[tier]) {
|
|
27211
|
-
const totalRemaining = tokenBudget - totalUsed;
|
|
27212
|
-
if (tokens > totalRemaining) {
|
|
27213
|
-
manifest.excluded.push({
|
|
27214
|
-
entityId: item.entity.id,
|
|
27215
|
-
title: item.entity.title,
|
|
27216
|
-
type: item.entity.type,
|
|
27217
|
-
tier,
|
|
27218
|
-
relevanceScore: item.score,
|
|
27219
|
-
reason: "budget_exceeded"
|
|
27220
|
-
});
|
|
27221
|
-
continue;
|
|
27222
|
-
}
|
|
27223
|
-
}
|
|
27224
|
-
if (totalUsed + tokens > tokenBudget) {
|
|
27225
|
-
manifest.excluded.push({
|
|
27226
|
-
entityId: item.entity.id,
|
|
27227
|
-
title: item.entity.title,
|
|
27228
|
-
type: item.entity.type,
|
|
27229
|
-
tier,
|
|
27230
|
-
relevanceScore: item.score,
|
|
27231
|
-
reason: "total_budget_exceeded"
|
|
27232
|
-
});
|
|
27233
|
-
continue;
|
|
27234
|
-
}
|
|
27235
|
-
included.push({ ...item, tokens, truncated });
|
|
27236
|
-
item.entity.content = text;
|
|
27237
|
-
totalUsed += tokens;
|
|
27238
|
-
tierUsed[tier] += tokens;
|
|
27239
|
-
includedIds.add(item.entity.id);
|
|
27240
|
-
}
|
|
27241
|
-
manifest.budgetUsed = totalUsed;
|
|
27242
|
-
const procedureItems = included.filter((i) => i.entity.type === "procedure");
|
|
27243
|
-
manifest.tierBreakdown = {
|
|
27244
|
-
reference: {
|
|
27245
|
-
count: included.filter((i) => i.entity.memory_tier === "reference" && i.entity.type !== "procedure").length,
|
|
27246
|
-
tokens: tierUsed.reference
|
|
27247
|
-
},
|
|
27248
|
-
episode: {
|
|
27249
|
-
count: included.filter((i) => i.entity.memory_tier === "episode" && i.entity.type !== "procedure").length,
|
|
27250
|
-
tokens: tierUsed.episode
|
|
27251
|
-
},
|
|
27252
|
-
draft: {
|
|
27253
|
-
count: included.filter((i) => i.entity.memory_tier === "draft" && i.entity.type !== "procedure").length,
|
|
27254
|
-
tokens: tierUsed.draft
|
|
27255
|
-
}
|
|
27256
|
-
};
|
|
27257
|
-
manifest.procedureBreakdown = {
|
|
27258
|
-
count: procedureItems.length,
|
|
27259
|
-
tokens: procedureUsed,
|
|
27260
|
-
budget: procedureBudget
|
|
27261
|
-
};
|
|
27262
|
-
for (const item of included) {
|
|
27263
|
-
manifest.included.push({
|
|
27264
|
-
entityId: item.entity.id,
|
|
27265
|
-
title: item.entity.title,
|
|
27266
|
-
type: item.entity.type,
|
|
27267
|
-
tier: item.entity.memory_tier,
|
|
27268
|
-
relevanceScore: item.score,
|
|
27269
|
-
reasons: item.reasons,
|
|
27270
|
-
tokenCount: item.tokens,
|
|
27271
|
-
truncated: item.truncated
|
|
27272
|
-
});
|
|
27273
|
-
}
|
|
27274
|
-
const contextSections = [];
|
|
27275
|
-
const nonProcedureItems = included.filter((i) => i.entity.type !== "procedure");
|
|
27276
|
-
if (included.length > 0) {
|
|
27277
|
-
if (procedureItems.length > 0) {
|
|
27278
|
-
contextSections.push(`## Procedures (${procedureItems.length} loaded, ${procedureUsed}/${procedureBudget} tokens)`);
|
|
27279
|
-
for (const item of procedureItems) {
|
|
27280
|
-
const tags = item.entity.tags.length > 0 ? ` [${item.entity.tags.join(", ")}]` : "";
|
|
27281
|
-
const tierLabel = item.entity.memory_tier !== "reference" ? ` (${item.entity.memory_tier})` : "";
|
|
27282
|
-
contextSections.push(`
|
|
27283
|
-
### ${item.entity.title} (confidence: ${item.entity.confidence})${tierLabel}${tags}`);
|
|
27284
|
-
contextSections.push(item.entity.content);
|
|
27285
|
-
}
|
|
27286
|
-
}
|
|
27287
|
-
if (nonProcedureItems.length > 0) {
|
|
27288
|
-
contextSections.push(`
|
|
27289
|
-
## Relevant Memories (${nonProcedureItems.length} loaded, ${manifest.excluded.length} excluded)`);
|
|
27290
|
-
contextSections.push(`*Assembly: ${assemblyId} | Budget: ${totalUsed}/${tokenBudget} tokens*`);
|
|
27291
|
-
for (const item of nonProcedureItems) {
|
|
27292
|
-
const tags = item.entity.tags.length > 0 ? ` [${item.entity.tags.join(", ")}]` : "";
|
|
27293
|
-
const tierLabel = item.entity.memory_tier !== "reference" ? ` (${item.entity.memory_tier})` : "";
|
|
27294
|
-
contextSections.push(`
|
|
27295
|
-
### ${item.entity.title} (${item.entity.type}, confidence: ${item.entity.confidence})${tierLabel}${tags}`);
|
|
27296
|
-
contextSections.push(item.entity.content);
|
|
27297
|
-
}
|
|
27298
|
-
}
|
|
27299
|
-
}
|
|
27300
|
-
incrementAccessCounts(client3, included.map((i) => i.entity.id)).catch(() => {});
|
|
27301
|
-
promoteEligibleEntities(client3, included.map((i) => i.entity)).catch(() => {});
|
|
27302
|
-
return {
|
|
27303
|
-
context: contextSections.join(`
|
|
27304
|
-
`),
|
|
27305
|
-
manifest,
|
|
27306
|
-
memories: included.map((i) => i.entity)
|
|
27307
|
-
};
|
|
27308
|
-
}
|
|
27309
|
-
function mapToContextEntity(raw) {
|
|
27310
|
-
const e = raw;
|
|
27311
|
-
return {
|
|
27312
|
-
id: e.id,
|
|
27313
|
-
type: e.type,
|
|
27314
|
-
title: e.title,
|
|
27315
|
-
content: e.content,
|
|
27316
|
-
confidence: e.confidence ?? 1,
|
|
27317
|
-
tags: e.tags || [],
|
|
27318
|
-
memory_tier: e.memory_tier || "reference",
|
|
27319
|
-
access_count: e.access_count || 0,
|
|
27320
|
-
last_accessed_at: e.last_accessed_at || null,
|
|
27321
|
-
created_at: e.created_at || "",
|
|
27322
|
-
updated_at: e.updated_at || "",
|
|
27323
|
-
metadata: e.metadata ?? undefined,
|
|
27324
|
-
rrf_score: e.rrf_score ?? undefined,
|
|
27325
|
-
fts_rank: e.fts_rank ?? undefined,
|
|
27326
|
-
semantic_rank: e.semantic_rank ?? undefined
|
|
27327
|
-
};
|
|
27328
|
-
}
|
|
27329
|
-
async function incrementAccessCounts(client3, entityIds) {
|
|
27330
|
-
if (entityIds.length === 0)
|
|
27331
|
-
return;
|
|
27332
|
-
try {
|
|
27333
|
-
await client3.batchTouchMemoryEntities(entityIds);
|
|
27334
|
-
} catch {
|
|
27335
|
-
await Promise.allSettled(entityIds.map((id) => client3.touchMemoryEntity(id)));
|
|
27336
|
-
}
|
|
27337
|
-
}
|
|
27338
|
-
async function promoteEligibleEntities(client3, entities) {
|
|
27339
|
-
for (const entity of entities) {
|
|
27340
|
-
if (entity.memory_tier === "reference")
|
|
27341
|
-
continue;
|
|
27342
|
-
if (!entity.created_at)
|
|
27343
|
-
continue;
|
|
27344
|
-
const promotion = checkPromotion(entity.memory_tier, entity.access_count + 1, entity.confidence, entity.created_at);
|
|
27345
|
-
if (promotion.eligible && promotion.targetTier) {
|
|
27346
|
-
try {
|
|
27347
|
-
await client3.updateMemoryEntity(entity.id, {
|
|
27348
|
-
memory_tier: promotion.targetTier,
|
|
27349
|
-
metadata: {
|
|
27350
|
-
...entity.metadata || {},
|
|
27351
|
-
promoted_at: new Date().toISOString(),
|
|
27352
|
-
promotion_reason: promotion.reason,
|
|
27353
|
-
promoted_from: entity.memory_tier
|
|
27354
|
-
}
|
|
27355
|
-
});
|
|
27356
|
-
} catch {}
|
|
27357
|
-
}
|
|
27358
|
-
}
|
|
27359
|
-
}
|
|
27360
|
-
var manifestCache = new Map;
|
|
27361
|
-
var MAX_CACHE_SIZE = 50;
|
|
27362
|
-
function cacheManifest(manifest) {
|
|
27363
|
-
if (manifestCache.size >= MAX_CACHE_SIZE) {
|
|
27364
|
-
const firstKey = manifestCache.keys().next().value;
|
|
27365
|
-
if (firstKey)
|
|
27366
|
-
manifestCache.delete(firstKey);
|
|
27367
|
-
}
|
|
27368
|
-
manifestCache.set(manifest.assemblyId, manifest);
|
|
27369
|
-
}
|
|
27370
|
-
function getCachedManifest(assemblyId) {
|
|
27371
|
-
return manifestCache.get(assemblyId);
|
|
27372
|
-
}
|
|
27373
|
-
var sessionAssemblyMap = new Map;
|
|
27374
|
-
var MAX_SESSION_MAP_SIZE = 100;
|
|
27375
|
-
function trackSessionAssembly(cardId, assemblyId) {
|
|
27376
|
-
if (sessionAssemblyMap.size >= MAX_SESSION_MAP_SIZE) {
|
|
27377
|
-
const firstKey = sessionAssemblyMap.keys().next().value;
|
|
27378
|
-
if (firstKey)
|
|
27379
|
-
sessionAssemblyMap.delete(firstKey);
|
|
27380
|
-
}
|
|
27381
|
-
sessionAssemblyMap.set(cardId, assemblyId);
|
|
27382
|
-
}
|
|
27383
|
-
async function recordContextFeedback(client3, cardId, sessionStatus, progressPercent, hadBlockers) {
|
|
27384
|
-
const assemblyId = sessionAssemblyMap.get(cardId);
|
|
27385
|
-
if (!assemblyId)
|
|
27386
|
-
return { adjusted: 0 };
|
|
27387
|
-
const manifest = manifestCache.get(assemblyId);
|
|
27388
|
-
if (!manifest || manifest.included.length === 0)
|
|
27389
|
-
return { adjusted: 0 };
|
|
27390
|
-
let adjusted = 0;
|
|
27391
|
-
const isSuccess = sessionStatus === "completed" && (progressPercent ?? 0) >= 100;
|
|
27392
|
-
for (const entry of manifest.included) {
|
|
27393
|
-
try {
|
|
27394
|
-
if (isSuccess) {
|
|
27395
|
-
const { entity } = await client3.getMemoryEntity(entry.entityId);
|
|
27396
|
-
const e = entity;
|
|
27397
|
-
const currentUsefulness = e.metadata?.usefulness_score ?? 0;
|
|
27398
|
-
const newConfidence = Math.min((e.confidence ?? 0.5) + 0.05, 1);
|
|
27399
|
-
await client3.updateMemoryEntity(entry.entityId, {
|
|
27400
|
-
confidence: newConfidence,
|
|
27401
|
-
metadata: {
|
|
27402
|
-
usefulness_score: currentUsefulness + 1,
|
|
27403
|
-
last_feedback_at: new Date().toISOString()
|
|
27404
|
-
}
|
|
27405
|
-
});
|
|
27406
|
-
adjusted++;
|
|
27407
|
-
} else if (hadBlockers) {
|
|
27408
|
-
const { entity } = await client3.getMemoryEntity(entry.entityId);
|
|
27409
|
-
const e = entity;
|
|
27410
|
-
const newConfidence = Math.max((e.confidence ?? 0.5) - 0.02, 0.1);
|
|
27411
|
-
await client3.updateMemoryEntity(entry.entityId, {
|
|
27412
|
-
confidence: newConfidence,
|
|
27413
|
-
metadata: {
|
|
27414
|
-
last_feedback_at: new Date().toISOString()
|
|
27415
|
-
}
|
|
27416
|
-
});
|
|
27417
|
-
adjusted++;
|
|
27418
|
-
}
|
|
27419
|
-
} catch {}
|
|
27420
|
-
}
|
|
27421
|
-
sessionAssemblyMap.delete(cardId);
|
|
27422
|
-
return { adjusted };
|
|
27423
|
-
}
|
|
28028
|
+
// src/server.ts
|
|
28029
|
+
init_context_assembly();
|
|
27424
28030
|
|
|
27425
28031
|
// src/lifecycle-maintenance.ts
|
|
28032
|
+
init_src();
|
|
27426
28033
|
async function runLifecycleMaintenance(client3, workspaceId, projectId) {
|
|
27427
28034
|
const result = {
|
|
27428
28035
|
archived: 0,
|
|
@@ -27491,428 +28098,97 @@ async function runLifecycleMaintenance(client3, workspaceId, projectId) {
|
|
|
27491
28098
|
return result;
|
|
27492
28099
|
}
|
|
27493
28100
|
|
|
27494
|
-
// src/
|
|
27495
|
-
var
|
|
27496
|
-
|
|
27497
|
-
|
|
27498
|
-
|
|
27499
|
-
|
|
27500
|
-
|
|
27501
|
-
|
|
27502
|
-
|
|
27503
|
-
|
|
27504
|
-
|
|
27505
|
-
new: "feature",
|
|
27506
|
-
design: "design",
|
|
27507
|
-
ui: "design",
|
|
27508
|
-
ux: "design",
|
|
27509
|
-
frontend: "design",
|
|
27510
|
-
styling: "design",
|
|
27511
|
-
review: "review",
|
|
27512
|
-
"code review": "review",
|
|
27513
|
-
pr: "review",
|
|
27514
|
-
feedback: "review",
|
|
27515
|
-
onboarding: "onboarding",
|
|
27516
|
-
documentation: "onboarding",
|
|
27517
|
-
docs: "onboarding",
|
|
27518
|
-
guide: "onboarding",
|
|
27519
|
-
tutorial: "onboarding",
|
|
27520
|
-
epic: "epic",
|
|
27521
|
-
initiative: "epic",
|
|
27522
|
-
project: "epic",
|
|
27523
|
-
milestone: "epic"
|
|
27524
|
-
};
|
|
27525
|
-
var DEFAULT_ROLE_FRAMINGS = {
|
|
27526
|
-
bug: {
|
|
27527
|
-
category: "bug",
|
|
27528
|
-
role: "Senior QA Engineer and Software Developer",
|
|
27529
|
-
perspective: "You are investigating and fixing a bug report.",
|
|
27530
|
-
focus: [
|
|
27531
|
-
"Root cause analysis",
|
|
27532
|
-
"Steps to reproduce",
|
|
27533
|
-
"Impact assessment",
|
|
27534
|
-
"Fix implementation",
|
|
27535
|
-
"Regression prevention",
|
|
27536
|
-
"Test cases to prevent recurrence"
|
|
27537
|
-
],
|
|
27538
|
-
outputSuggestions: [
|
|
27539
|
-
"Bug triage summary",
|
|
27540
|
-
"Root cause explanation",
|
|
27541
|
-
"Fix implementation plan",
|
|
27542
|
-
"Test cases"
|
|
27543
|
-
]
|
|
27544
|
-
},
|
|
27545
|
-
feature: {
|
|
27546
|
-
category: "feature",
|
|
27547
|
-
role: "Product Engineer",
|
|
27548
|
-
perspective: "You are implementing a new feature or enhancement.",
|
|
27549
|
-
focus: [
|
|
27550
|
-
"User requirements",
|
|
27551
|
-
"Technical specification",
|
|
27552
|
-
"Implementation approach",
|
|
27553
|
-
"Edge cases",
|
|
27554
|
-
"Acceptance criteria",
|
|
27555
|
-
"Integration points"
|
|
27556
|
-
],
|
|
27557
|
-
outputSuggestions: [
|
|
27558
|
-
"Technical specification",
|
|
27559
|
-
"Implementation tasks",
|
|
27560
|
-
"Acceptance criteria checklist",
|
|
27561
|
-
"API design"
|
|
27562
|
-
]
|
|
27563
|
-
},
|
|
27564
|
-
design: {
|
|
27565
|
-
category: "design",
|
|
27566
|
-
role: "UX Designer and Frontend Developer",
|
|
27567
|
-
perspective: "You are designing and implementing a user interface.",
|
|
27568
|
-
focus: [
|
|
27569
|
-
"User experience flow",
|
|
27570
|
-
"Visual design consistency",
|
|
27571
|
-
"Accessibility (WCAG)",
|
|
27572
|
-
"Responsive behavior",
|
|
27573
|
-
"Component architecture",
|
|
27574
|
-
"Interaction patterns"
|
|
27575
|
-
],
|
|
27576
|
-
outputSuggestions: [
|
|
27577
|
-
"User flow diagram",
|
|
27578
|
-
"Component specifications",
|
|
27579
|
-
"Accessibility checklist",
|
|
27580
|
-
"Responsive breakpoints"
|
|
27581
|
-
]
|
|
27582
|
-
},
|
|
27583
|
-
review: {
|
|
27584
|
-
category: "review",
|
|
27585
|
-
role: "Code Reviewer and Technical Lead",
|
|
27586
|
-
perspective: "You are reviewing code for quality, correctness, and maintainability.",
|
|
27587
|
-
focus: [
|
|
27588
|
-
"Code correctness",
|
|
27589
|
-
"Performance implications",
|
|
27590
|
-
"Security considerations",
|
|
27591
|
-
"Testing coverage",
|
|
27592
|
-
"Documentation",
|
|
27593
|
-
"Best practices adherence"
|
|
27594
|
-
],
|
|
27595
|
-
outputSuggestions: [
|
|
27596
|
-
"Review checklist",
|
|
27597
|
-
"Suggested improvements",
|
|
27598
|
-
"Test scenarios",
|
|
27599
|
-
"Security audit"
|
|
27600
|
-
]
|
|
27601
|
-
},
|
|
27602
|
-
onboarding: {
|
|
27603
|
-
category: "onboarding",
|
|
27604
|
-
role: "Technical Writer and Developer Advocate",
|
|
27605
|
-
perspective: "You are creating documentation or onboarding materials.",
|
|
27606
|
-
focus: [
|
|
27607
|
-
"Clear step-by-step instructions",
|
|
27608
|
-
"Prerequisites and setup",
|
|
27609
|
-
"Common pitfalls",
|
|
27610
|
-
"Examples and use cases",
|
|
27611
|
-
"Troubleshooting guide",
|
|
27612
|
-
"Related resources"
|
|
27613
|
-
],
|
|
27614
|
-
outputSuggestions: [
|
|
27615
|
-
"Getting started guide",
|
|
27616
|
-
"Step-by-step tutorial",
|
|
27617
|
-
"FAQ section",
|
|
27618
|
-
"Troubleshooting guide"
|
|
27619
|
-
]
|
|
27620
|
-
},
|
|
27621
|
-
epic: {
|
|
27622
|
-
category: "epic",
|
|
27623
|
-
role: "Technical Project Manager and Architect",
|
|
27624
|
-
perspective: "You are planning and coordinating a large initiative.",
|
|
27625
|
-
focus: [
|
|
27626
|
-
"Scope definition",
|
|
27627
|
-
"Task breakdown",
|
|
27628
|
-
"Dependencies",
|
|
27629
|
-
"Risk assessment",
|
|
27630
|
-
"Timeline considerations",
|
|
27631
|
-
"Success metrics"
|
|
27632
|
-
],
|
|
27633
|
-
outputSuggestions: [
|
|
27634
|
-
"Epic breakdown into stories",
|
|
27635
|
-
"Dependency graph",
|
|
27636
|
-
"Risk mitigation plan",
|
|
27637
|
-
"Success criteria"
|
|
27638
|
-
]
|
|
27639
|
-
},
|
|
27640
|
-
custom: {
|
|
27641
|
-
category: "custom",
|
|
27642
|
-
role: "Software Engineer",
|
|
27643
|
-
perspective: "You are working on a software task.",
|
|
27644
|
-
focus: [
|
|
27645
|
-
"Understanding requirements",
|
|
27646
|
-
"Implementation approach",
|
|
27647
|
-
"Quality considerations",
|
|
27648
|
-
"Testing strategy"
|
|
27649
|
-
],
|
|
27650
|
-
outputSuggestions: [
|
|
27651
|
-
"Implementation plan",
|
|
27652
|
-
"Technical notes",
|
|
27653
|
-
"Task checklist"
|
|
27654
|
-
]
|
|
27655
|
-
}
|
|
27656
|
-
};
|
|
27657
|
-
var VARIANT_INSTRUCTIONS = {
|
|
27658
|
-
analysis: `ANALYSIS MODE: Analyze this task thoroughly. Identify requirements, constraints, edge cases, and potential challenges. Do NOT implement anything yet - focus on understanding and planning.`,
|
|
27659
|
-
draft: `DRAFT MODE: Create a detailed implementation plan with code structure, key decisions, and approach. Include pseudocode or skeleton code where helpful. This is for review before full implementation.`,
|
|
27660
|
-
execute: `EXECUTE MODE: Implement this task completely. Write production-ready code following best practices. Include necessary tests and documentation.`
|
|
27661
|
-
};
|
|
27662
|
-
function inferCategoryFromLabels(labels) {
|
|
27663
|
-
for (const label of labels) {
|
|
27664
|
-
const normalizedName = label.name.toLowerCase().trim();
|
|
27665
|
-
if (LABEL_CATEGORY_MAP[normalizedName]) {
|
|
27666
|
-
return LABEL_CATEGORY_MAP[normalizedName];
|
|
27667
|
-
}
|
|
27668
|
-
for (const [key, category] of Object.entries(LABEL_CATEGORY_MAP)) {
|
|
27669
|
-
if (normalizedName.includes(key) || key.includes(normalizedName)) {
|
|
27670
|
-
return category;
|
|
27671
|
-
}
|
|
27672
|
-
}
|
|
27673
|
-
}
|
|
27674
|
-
return "custom";
|
|
27675
|
-
}
|
|
27676
|
-
function getRoleFraming(category) {
|
|
27677
|
-
return DEFAULT_ROLE_FRAMINGS[category];
|
|
27678
|
-
}
|
|
27679
|
-
function estimateTokens2(text) {
|
|
27680
|
-
return Math.ceil(text.length / 4);
|
|
27681
|
-
}
|
|
27682
|
-
function formatSubtasks(subtasks) {
|
|
27683
|
-
if (subtasks.length === 0)
|
|
27684
|
-
return "";
|
|
27685
|
-
const completed = subtasks.filter((s) => s.completed).length;
|
|
27686
|
-
const lines = subtasks.map((s) => ` ${s.completed ? "[x]" : "[ ]"} ${s.title}`);
|
|
27687
|
-
return `
|
|
27688
|
-
## Subtasks (${completed}/${subtasks.length} completed)
|
|
27689
|
-
${lines.join(`
|
|
27690
|
-
`)}`;
|
|
27691
|
-
}
|
|
27692
|
-
function formatLabels(labels) {
|
|
27693
|
-
if (labels.length === 0)
|
|
27694
|
-
return "";
|
|
27695
|
-
return `
|
|
27696
|
-
**Labels:** ${labels.map((l) => l.name).join(", ")}`;
|
|
27697
|
-
}
|
|
27698
|
-
function formatLinkedCards(links) {
|
|
27699
|
-
if (!links || links.length === 0)
|
|
27700
|
-
return "";
|
|
27701
|
-
const lines = links.map((link) => {
|
|
27702
|
-
const prefix = link.direction === "outgoing" ? "->" : "<-";
|
|
27703
|
-
return ` ${prefix} #${link.target_card.short_id}: ${link.target_card.title} (${link.display_type})`;
|
|
28101
|
+
// src/server.ts
|
|
28102
|
+
var memorySessions = new Map;
|
|
28103
|
+
function initMemorySession(cardId, agentIdentifier, agentName) {
|
|
28104
|
+
memorySessions.set(cardId, {
|
|
28105
|
+
cardId,
|
|
28106
|
+
agentIdentifier,
|
|
28107
|
+
agentName,
|
|
28108
|
+
memoryReadCount: 0,
|
|
28109
|
+
pendingActions: [],
|
|
28110
|
+
allActions: [],
|
|
28111
|
+
dirty: false
|
|
27704
28112
|
});
|
|
27705
|
-
return `
|
|
27706
|
-
## Related Cards
|
|
27707
|
-
${lines.join(`
|
|
27708
|
-
`)}`;
|
|
27709
28113
|
}
|
|
27710
|
-
function
|
|
27711
|
-
|
|
27712
|
-
card,
|
|
27713
|
-
column,
|
|
27714
|
-
variant,
|
|
27715
|
-
customConstraints,
|
|
27716
|
-
memories,
|
|
27717
|
-
assembledContext,
|
|
27718
|
-
assemblyId
|
|
27719
|
-
} = options;
|
|
27720
|
-
const contextOpts = {
|
|
27721
|
-
includeTitle: true,
|
|
27722
|
-
includeDescription: true,
|
|
27723
|
-
includeLabels: true,
|
|
27724
|
-
includeSubtasks: true,
|
|
27725
|
-
includeActivity: false,
|
|
27726
|
-
includeAssignee: true,
|
|
27727
|
-
includeDueDate: true,
|
|
27728
|
-
includePriority: true,
|
|
27729
|
-
includeLinks: true,
|
|
27730
|
-
includeColumn: true,
|
|
27731
|
-
...options.contextOptions
|
|
27732
|
-
};
|
|
27733
|
-
const labels = card.labels || [];
|
|
27734
|
-
const subtasks = card.subtasks || [];
|
|
27735
|
-
const links = card.links || [];
|
|
27736
|
-
const category = inferCategoryFromLabels(labels);
|
|
27737
|
-
const roleFraming = getRoleFraming(category);
|
|
27738
|
-
const sections = [];
|
|
27739
|
-
sections.push(`# Role: ${roleFraming.role}
|
|
27740
|
-
`);
|
|
27741
|
-
sections.push(roleFraming.perspective);
|
|
27742
|
-
sections.push("");
|
|
27743
|
-
sections.push(VARIANT_INSTRUCTIONS[variant]);
|
|
27744
|
-
sections.push("");
|
|
27745
|
-
sections.push(`# Task: ${card.title}`);
|
|
27746
|
-
if (contextOpts.includeColumn && column) {
|
|
27747
|
-
sections.push(`**Status:** ${column.name}`);
|
|
27748
|
-
}
|
|
27749
|
-
if (contextOpts.includePriority) {
|
|
27750
|
-
sections.push(`**Priority:** ${card.priority}`);
|
|
27751
|
-
}
|
|
27752
|
-
if (contextOpts.includeDueDate && card.due_date) {
|
|
27753
|
-
sections.push(`**Due:** ${card.due_date}`);
|
|
27754
|
-
}
|
|
27755
|
-
if (contextOpts.includeAssignee && card.assignee) {
|
|
27756
|
-
sections.push(`**Assignee:** ${card.assignee.full_name || card.assignee.email}`);
|
|
27757
|
-
}
|
|
27758
|
-
if (contextOpts.includeLabels && labels.length > 0) {
|
|
27759
|
-
sections.push(formatLabels(labels));
|
|
27760
|
-
}
|
|
27761
|
-
if (contextOpts.includeDescription && card.description) {
|
|
27762
|
-
sections.push(`
|
|
27763
|
-
## Description
|
|
27764
|
-
${card.description}`);
|
|
27765
|
-
}
|
|
27766
|
-
if (contextOpts.includeSubtasks && subtasks.length > 0) {
|
|
27767
|
-
sections.push(formatSubtasks(subtasks));
|
|
27768
|
-
}
|
|
27769
|
-
if (contextOpts.includeLinks && links.length > 0) {
|
|
27770
|
-
sections.push(formatLinkedCards(links));
|
|
27771
|
-
}
|
|
27772
|
-
sections.push(`
|
|
27773
|
-
## Focus Areas`);
|
|
27774
|
-
roleFraming.focus.forEach((f) => {
|
|
27775
|
-
sections.push(`- ${f}`);
|
|
27776
|
-
});
|
|
27777
|
-
sections.push(`
|
|
27778
|
-
## Suggested Outputs`);
|
|
27779
|
-
roleFraming.outputSuggestions.forEach((s) => {
|
|
27780
|
-
sections.push(`- ${s}`);
|
|
27781
|
-
});
|
|
27782
|
-
if (assembledContext) {
|
|
27783
|
-
sections.push(`
|
|
27784
|
-
${assembledContext}`);
|
|
27785
|
-
} else if (memories && memories.length > 0) {
|
|
27786
|
-
sections.push(`
|
|
27787
|
-
## Relevant Memories`);
|
|
27788
|
-
sections.push(`*${memories.length} memories recalled from knowledge graph:*`);
|
|
27789
|
-
for (const memory of memories) {
|
|
27790
|
-
const tags = memory.tags.length > 0 ? ` [${memory.tags.join(", ")}]` : "";
|
|
27791
|
-
sections.push(`
|
|
27792
|
-
### ${memory.title} (${memory.type}, confidence: ${memory.confidence})${tags}`);
|
|
27793
|
-
sections.push(memory.content);
|
|
27794
|
-
}
|
|
27795
|
-
}
|
|
27796
|
-
const oneThingLine = synthesizeOneThing(card, subtasks, links, assembledContext);
|
|
27797
|
-
if (oneThingLine) {
|
|
27798
|
-
sections.push(`
|
|
27799
|
-
## Recommended Next Step
|
|
27800
|
-
${oneThingLine}`);
|
|
27801
|
-
}
|
|
27802
|
-
if (variant === "execute") {
|
|
27803
|
-
sections.push(`
|
|
27804
|
-
## Progress Tracking
|
|
27805
|
-
Update your progress by calling \`harmony_update_agent_progress\` with \`currentTask\` describing what you're doing now:
|
|
27806
|
-
- After exploring the codebase and understanding requirements (~20%)
|
|
27807
|
-
- When you start implementing changes (~50%)
|
|
27808
|
-
- When you move to testing or verification (~80%)
|
|
27809
|
-
- When done, before ending the session (100%)
|
|
27810
|
-
|
|
27811
|
-
Keep \`currentTask\` specific (e.g., "Refactoring auth middleware" not "Working on card").`);
|
|
27812
|
-
}
|
|
27813
|
-
if (customConstraints) {
|
|
27814
|
-
sections.push(`
|
|
27815
|
-
## Additional Instructions
|
|
27816
|
-
${customConstraints}`);
|
|
27817
|
-
}
|
|
27818
|
-
sections.push(`
|
|
27819
|
-
---
|
|
27820
|
-
*Card #${card.short_id} | Generated for ${variant} mode*`);
|
|
27821
|
-
const prompt = sections.join(`
|
|
27822
|
-
`);
|
|
27823
|
-
const memoryCount = assembledContext ? (assembledContext.match(/^### /gm) || []).length : memories?.length || 0;
|
|
27824
|
-
return {
|
|
27825
|
-
prompt,
|
|
27826
|
-
variant,
|
|
27827
|
-
category,
|
|
27828
|
-
role: roleFraming.role,
|
|
27829
|
-
contextSummary: {
|
|
27830
|
-
hasDescription: !!card.description,
|
|
27831
|
-
labelCount: labels.length,
|
|
27832
|
-
subtaskCount: subtasks.length,
|
|
27833
|
-
completedSubtasks: subtasks.filter((s) => s.completed).length,
|
|
27834
|
-
linkedCardCount: links.length,
|
|
27835
|
-
memoryCount
|
|
27836
|
-
},
|
|
27837
|
-
tokenEstimate: estimateTokens2(prompt),
|
|
27838
|
-
...assemblyId && { assemblyId }
|
|
27839
|
-
};
|
|
28114
|
+
function getMemorySession(cardId) {
|
|
28115
|
+
return memorySessions.get(cardId);
|
|
27840
28116
|
}
|
|
27841
|
-
function
|
|
27842
|
-
const
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
|
|
27852
|
-
|
|
27853
|
-
|
|
27854
|
-
|
|
27855
|
-
|
|
27856
|
-
}
|
|
27857
|
-
const taskMatch = latest.match(/Final task:\s*(.+)/);
|
|
27858
|
-
if (taskMatch)
|
|
27859
|
-
result.lastSessionTask = taskMatch[1].trim();
|
|
27860
|
-
const progressMatch = latest.match(/Progress:\s*(\d+)%/);
|
|
27861
|
-
if (progressMatch)
|
|
27862
|
-
result.lastSessionProgress = parseInt(progressMatch[1], 10);
|
|
27863
|
-
}
|
|
27864
|
-
const blockerMatches = assembledContext.match(/(?:blocker|blocked by|blocking):\s*(.+)/gi);
|
|
27865
|
-
if (blockerMatches) {
|
|
27866
|
-
result.blockers = blockerMatches.map((m) => m.replace(/(?:blocker|blocked by|blocking):\s*/i, "").trim());
|
|
27867
|
-
}
|
|
27868
|
-
const stepMatches = assembledContext.match(/^\d+\.\s+(?!.*\*\*\[key step\]\*\*.*✓)(.+?)(?:\s*\*\*\[key step\]\*\*)?$/gm);
|
|
27869
|
-
if (stepMatches && stepMatches.length > 0) {
|
|
27870
|
-
result.procedureNextStep = stepMatches[0].replace(/^\d+\.\s+/, "").replace(/\s*\*\*\[key step\]\*\*.*$/, "").trim();
|
|
27871
|
-
}
|
|
27872
|
-
return result;
|
|
28117
|
+
function appendMemoryAction(cardId, action) {
|
|
28118
|
+
const session = memorySessions.get(cardId);
|
|
28119
|
+
if (!session)
|
|
28120
|
+
return;
|
|
28121
|
+
const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
|
|
28122
|
+
const entry = { action: truncated, ts: new Date().toISOString() };
|
|
28123
|
+
session.pendingActions.push(entry);
|
|
28124
|
+
session.dirty = true;
|
|
28125
|
+
}
|
|
28126
|
+
function incrementMemoryReads(cardId) {
|
|
28127
|
+
const session = memorySessions.get(cardId);
|
|
28128
|
+
if (!session)
|
|
28129
|
+
return;
|
|
28130
|
+
session.memoryReadCount++;
|
|
28131
|
+
session.dirty = true;
|
|
27873
28132
|
}
|
|
27874
|
-
function
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
|
|
27884
|
-
|
|
27885
|
-
}
|
|
27886
|
-
if (session?.lastSessionStatus === "paused" && session.lastSessionTask) {
|
|
27887
|
-
const progress = session.lastSessionProgress ? ` (was ${session.lastSessionProgress}% complete)` : "";
|
|
27888
|
-
return `Resume previous session${progress}: "${session.lastSessionTask}".`;
|
|
27889
|
-
}
|
|
27890
|
-
if (subtasks.length > 0) {
|
|
27891
|
-
const completed = subtasks.filter((s) => s.completed).length;
|
|
27892
|
-
if (completed === subtasks.length) {
|
|
27893
|
-
return "All subtasks completed. Review the work and mark the card as done.";
|
|
28133
|
+
async function flushMemoryActions(client3, cardId) {
|
|
28134
|
+
const session = memorySessions.get(cardId);
|
|
28135
|
+
if (!session || !session.dirty)
|
|
28136
|
+
return;
|
|
28137
|
+
try {
|
|
28138
|
+
if (session.memoryReadCount > 0) {
|
|
28139
|
+
session.allActions.push({
|
|
28140
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
28141
|
+
ts: new Date().toISOString()
|
|
28142
|
+
});
|
|
28143
|
+
session.memoryReadCount = 0;
|
|
27894
28144
|
}
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
28145
|
+
if (session.pendingActions.length > 0) {
|
|
28146
|
+
session.allActions.push(...session.pendingActions);
|
|
28147
|
+
session.pendingActions = [];
|
|
27898
28148
|
}
|
|
28149
|
+
if (session.allActions.length > 10) {
|
|
28150
|
+
session.allActions = session.allActions.slice(-10);
|
|
28151
|
+
}
|
|
28152
|
+
await client3.updateAgentProgress(cardId, {
|
|
28153
|
+
agentIdentifier: session.agentIdentifier,
|
|
28154
|
+
agentName: session.agentName,
|
|
28155
|
+
recentActions: session.allActions
|
|
28156
|
+
});
|
|
28157
|
+
session.dirty = false;
|
|
28158
|
+
} catch (err) {
|
|
28159
|
+
console.error("[memory-session] flush failed:", err);
|
|
28160
|
+
}
|
|
28161
|
+
}
|
|
28162
|
+
function mergeMemoryActionsInto(cardId, callerActions) {
|
|
28163
|
+
const session = memorySessions.get(cardId);
|
|
28164
|
+
if (!session)
|
|
28165
|
+
return callerActions;
|
|
28166
|
+
if (session.memoryReadCount > 0) {
|
|
28167
|
+
session.allActions.push({
|
|
28168
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
28169
|
+
ts: new Date().toISOString()
|
|
28170
|
+
});
|
|
28171
|
+
session.memoryReadCount = 0;
|
|
27899
28172
|
}
|
|
27900
|
-
if (session
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
if (session?.lastSessionStatus === "completed" && session.lastSessionTask) {
|
|
27904
|
-
return `Previous session completed ("${session.lastSessionTask}"). Review results and continue with remaining work.`;
|
|
27905
|
-
}
|
|
27906
|
-
if (card.due_date && (card.priority === "urgent" || card.priority === "high")) {
|
|
27907
|
-
return `High-priority task with deadline ${card.due_date}. Start implementation immediately.`;
|
|
28173
|
+
if (session.pendingActions.length > 0) {
|
|
28174
|
+
session.allActions.push(...session.pendingActions);
|
|
28175
|
+
session.pendingActions = [];
|
|
27908
28176
|
}
|
|
27909
|
-
|
|
27910
|
-
|
|
28177
|
+
const merged = [...callerActions, ...session.allActions];
|
|
28178
|
+
const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
|
|
28179
|
+
session.allActions = trimmed;
|
|
28180
|
+
session.dirty = false;
|
|
28181
|
+
return trimmed;
|
|
28182
|
+
}
|
|
28183
|
+
function getActiveMemorySession() {
|
|
28184
|
+
for (const session of memorySessions.values()) {
|
|
28185
|
+
return session;
|
|
27911
28186
|
}
|
|
27912
|
-
return
|
|
28187
|
+
return;
|
|
28188
|
+
}
|
|
28189
|
+
function cleanupMemorySession(cardId) {
|
|
28190
|
+
memorySessions.delete(cardId);
|
|
27913
28191
|
}
|
|
27914
|
-
|
|
27915
|
-
// src/server.ts
|
|
27916
28192
|
var TOOLS = {
|
|
27917
28193
|
harmony_create_card: {
|
|
27918
28194
|
description: "Create a new card in a Kanban column",
|
|
@@ -29426,7 +29702,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
29426
29702
|
const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
|
|
29427
29703
|
if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
|
|
29428
29704
|
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
29429
|
-
` + `You can generate an API key at https://gethmy.com → Settings → API Keys.
|
|
29705
|
+
` + `You can generate an API key at https://app.gethmy.com → Settings → API Keys.
|
|
29430
29706
|
` + 'Or use "harmony_onboard" to create an account and configure automatically.');
|
|
29431
29707
|
}
|
|
29432
29708
|
const client3 = deps.isConfigured() ? deps.getClient() : null;
|
|
@@ -29729,7 +30005,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29729
30005
|
currentTask: args.currentTask,
|
|
29730
30006
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
29731
30007
|
});
|
|
29732
|
-
markExplicit(cardId);
|
|
30008
|
+
markExplicit(cardId, { agentIdentifier, agentName });
|
|
30009
|
+
initMemorySession(cardId, agentIdentifier, agentName);
|
|
29733
30010
|
let prefetchedMemoryIds = [];
|
|
29734
30011
|
try {
|
|
29735
30012
|
const workspaceId = deps.getActiveWorkspaceId();
|
|
@@ -29776,6 +30053,14 @@ async function handleToolCall(name, args, deps) {
|
|
|
29776
30053
|
const agentIdentifier = exports_external.string().min(1).max(100).parse(args.agentIdentifier);
|
|
29777
30054
|
const agentName = exports_external.string().min(1).max(100).parse(args.agentName);
|
|
29778
30055
|
const progressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
30056
|
+
const callerRecentActions = args.recentActions;
|
|
30057
|
+
const memSession = getMemorySession(cardId);
|
|
30058
|
+
let mergedRecentActions;
|
|
30059
|
+
if (memSession?.dirty) {
|
|
30060
|
+
mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
|
|
30061
|
+
} else if (callerRecentActions) {
|
|
30062
|
+
mergedRecentActions = callerRecentActions;
|
|
30063
|
+
}
|
|
29779
30064
|
const result = await client3.updateAgentProgress(cardId, {
|
|
29780
30065
|
agentIdentifier,
|
|
29781
30066
|
agentName,
|
|
@@ -29783,7 +30068,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29783
30068
|
progressPercent,
|
|
29784
30069
|
currentTask: args.currentTask,
|
|
29785
30070
|
blockers: args.blockers,
|
|
29786
|
-
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
30071
|
+
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
30072
|
+
...mergedRecentActions && { recentActions: mergedRecentActions }
|
|
29787
30073
|
});
|
|
29788
30074
|
let midSessionLearnings = 0;
|
|
29789
30075
|
try {
|
|
@@ -29808,6 +30094,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29808
30094
|
const moveToColumn = args.moveToColumn;
|
|
29809
30095
|
const sessionStatus = args.status || "completed";
|
|
29810
30096
|
const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
30097
|
+
await flushMemoryActions(client3, cardId);
|
|
30098
|
+
cleanupMemorySession(cardId);
|
|
29811
30099
|
const result = await client3.endAgentSession(cardId, {
|
|
29812
30100
|
status: sessionStatus,
|
|
29813
30101
|
progressPercent: endProgressPercent
|
|
@@ -29881,12 +30169,9 @@ async function handleToolCall(name, args, deps) {
|
|
|
29881
30169
|
return { success: true, ...result };
|
|
29882
30170
|
}
|
|
29883
30171
|
case "harmony_generate_prompt": {
|
|
29884
|
-
let
|
|
29885
|
-
let columnData = null;
|
|
30172
|
+
let cardId;
|
|
29886
30173
|
if (args.cardId) {
|
|
29887
|
-
|
|
29888
|
-
const cardResult = await client3.getCard(cardId);
|
|
29889
|
-
cardData = cardResult.card;
|
|
30174
|
+
cardId = exports_external.string().uuid().parse(args.cardId);
|
|
29890
30175
|
} else if (args.shortId !== undefined) {
|
|
29891
30176
|
const shortId = exports_external.number().int().positive().parse(args.shortId);
|
|
29892
30177
|
const projectId = args.projectId || deps.getActiveProjectId();
|
|
@@ -29894,24 +30179,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
29894
30179
|
throw new Error("Project ID required when using shortId. Use harmony_set_project_context or provide projectId.");
|
|
29895
30180
|
}
|
|
29896
30181
|
const cardResult = await client3.getCardByShortId(projectId, shortId);
|
|
29897
|
-
|
|
30182
|
+
cardId = cardResult.card.id;
|
|
29898
30183
|
} else {
|
|
29899
30184
|
throw new Error("Either cardId or shortId must be provided");
|
|
29900
30185
|
}
|
|
29901
|
-
const projectIdForBoard = args.projectId || getActiveProjectId() || cardData.project_id;
|
|
29902
|
-
if (projectIdForBoard) {
|
|
29903
|
-
try {
|
|
29904
|
-
const board = await client3.getBoard(projectIdForBoard, {
|
|
29905
|
-
summary: true
|
|
29906
|
-
});
|
|
29907
|
-
const columnId = cardData.column_id;
|
|
29908
|
-
const column = board.columns.find((col) => col.id === columnId);
|
|
29909
|
-
if (column) {
|
|
29910
|
-
columnData = { name: column.name };
|
|
29911
|
-
}
|
|
29912
|
-
} catch {}
|
|
29913
|
-
}
|
|
29914
|
-
const variant = args.variant || "execute";
|
|
29915
30186
|
const contextOptions = {};
|
|
29916
30187
|
if (args.includeSubtasks !== undefined) {
|
|
29917
30188
|
contextOptions.includeSubtasks = args.includeSubtasks === true || args.includeSubtasks === "true";
|
|
@@ -29922,67 +30193,19 @@ async function handleToolCall(name, args, deps) {
|
|
|
29922
30193
|
if (args.includeDescription !== undefined) {
|
|
29923
30194
|
contextOptions.includeDescription = args.includeDescription === true || args.includeDescription === "true";
|
|
29924
30195
|
}
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
if (workspaceId && cardData.title) {
|
|
29931
|
-
const cardLabels = (cardData.labels || []).map((l) => l.name);
|
|
29932
|
-
const taskContext = [cardData.title, cardData.description || ""].filter(Boolean).join(" ");
|
|
29933
|
-
const assembled = await assembleContext({
|
|
29934
|
-
workspaceId,
|
|
29935
|
-
projectId: getActiveProjectId() || undefined,
|
|
29936
|
-
taskContext,
|
|
29937
|
-
cardLabels,
|
|
29938
|
-
cardId: cardData.id,
|
|
29939
|
-
client: client3
|
|
29940
|
-
});
|
|
29941
|
-
if (assembled.context) {
|
|
29942
|
-
assembledContextStr = assembled.context;
|
|
29943
|
-
assemblyId = assembled.manifest.assemblyId;
|
|
29944
|
-
cacheManifest(assembled.manifest);
|
|
29945
|
-
}
|
|
29946
|
-
}
|
|
29947
|
-
} catch {
|
|
29948
|
-
try {
|
|
29949
|
-
const workspaceId = deps.getActiveWorkspaceId();
|
|
29950
|
-
if (workspaceId && cardData.title) {
|
|
29951
|
-
const memoryResult = await client3.searchMemoryEntities(workspaceId, cardData.title, {
|
|
29952
|
-
project_id: getActiveProjectId() || undefined,
|
|
29953
|
-
limit: 5
|
|
29954
|
-
});
|
|
29955
|
-
if (memoryResult.entities?.length > 0) {
|
|
29956
|
-
memories = memoryResult.entities.map((e) => {
|
|
29957
|
-
const entity = e;
|
|
29958
|
-
return {
|
|
29959
|
-
id: entity.id,
|
|
29960
|
-
type: entity.type,
|
|
29961
|
-
title: entity.title,
|
|
29962
|
-
content: entity.content,
|
|
29963
|
-
confidence: entity.confidence,
|
|
29964
|
-
tags: entity.tags || []
|
|
29965
|
-
};
|
|
29966
|
-
});
|
|
29967
|
-
}
|
|
29968
|
-
}
|
|
29969
|
-
} catch {}
|
|
29970
|
-
}
|
|
29971
|
-
const result = generatePrompt({
|
|
29972
|
-
card: cardData,
|
|
29973
|
-
column: columnData,
|
|
29974
|
-
variant,
|
|
29975
|
-
contextOptions,
|
|
30196
|
+
const result = await client3.generateCardPrompt({
|
|
30197
|
+
cardId,
|
|
30198
|
+
workspaceId: deps.getActiveWorkspaceId() || "",
|
|
30199
|
+
projectId: args.projectId || getActiveProjectId() || undefined,
|
|
30200
|
+
variant: args.variant || "execute",
|
|
29976
30201
|
customConstraints: args.customConstraints,
|
|
29977
|
-
|
|
29978
|
-
assembledContext: assembledContextStr,
|
|
29979
|
-
assemblyId
|
|
30202
|
+
contextOptions
|
|
29980
30203
|
});
|
|
30204
|
+
if (result.assemblyId) {
|
|
30205
|
+
trackSessionAssembly(cardId, result.assemblyId);
|
|
30206
|
+
}
|
|
29981
30207
|
return {
|
|
29982
30208
|
success: true,
|
|
29983
|
-
cardId: cardData.id,
|
|
29984
|
-
shortId: cardData.short_id,
|
|
29985
|
-
title: cardData.title,
|
|
29986
30209
|
...result
|
|
29987
30210
|
};
|
|
29988
30211
|
}
|
|
@@ -29995,6 +30218,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
29995
30218
|
}
|
|
29996
30219
|
const entityType = args.type || "context";
|
|
29997
30220
|
const entityTags = args.tags || [];
|
|
30221
|
+
const activeMemSession = getActiveMemorySession();
|
|
29998
30222
|
const result = await client3.createMemoryEntity({
|
|
29999
30223
|
workspace_id: workspaceId,
|
|
30000
30224
|
project_id: args.projectId || deps.getActiveProjectId() || undefined,
|
|
@@ -30006,7 +30230,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
30006
30230
|
metadata: args.metadata,
|
|
30007
30231
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined,
|
|
30008
30232
|
tags: entityTags.length > 0 ? entityTags : undefined,
|
|
30009
|
-
agent_identifier:
|
|
30233
|
+
agent_identifier: activeMemSession?.agentIdentifier || undefined
|
|
30010
30234
|
});
|
|
30011
30235
|
const newEntityIdForGraph = result.entity?.id;
|
|
30012
30236
|
if (newEntityIdForGraph) {
|
|
@@ -30019,6 +30243,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30019
30243
|
potentialContradictions = await detectContradictions(client3, newEntityId, entityType, title, content, entityTags, workspaceId, args.projectId || deps.getActiveProjectId() || undefined);
|
|
30020
30244
|
} catch {}
|
|
30021
30245
|
}
|
|
30246
|
+
if (activeMemSession) {
|
|
30247
|
+
appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
|
|
30248
|
+
flushMemoryActions(client3, activeMemSession.cardId).catch(() => {});
|
|
30249
|
+
}
|
|
30022
30250
|
return {
|
|
30023
30251
|
success: true,
|
|
30024
30252
|
...result,
|
|
@@ -30082,6 +30310,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30082
30310
|
} catch (_) {}
|
|
30083
30311
|
})).catch(() => {});
|
|
30084
30312
|
}
|
|
30313
|
+
const recallMemSession = getActiveMemorySession();
|
|
30314
|
+
if (recallMemSession) {
|
|
30315
|
+
incrementMemoryReads(recallMemSession.cardId);
|
|
30316
|
+
}
|
|
30085
30317
|
return markdown || "No memories found.";
|
|
30086
30318
|
}
|
|
30087
30319
|
case "harmony_update_memory": {
|
|
@@ -30102,11 +30334,29 @@ async function handleToolCall(name, args, deps) {
|
|
|
30102
30334
|
if (args.metadata !== undefined)
|
|
30103
30335
|
updates.metadata = args.metadata;
|
|
30104
30336
|
const result = await client3.updateMemoryEntity(entityId, updates);
|
|
30337
|
+
const updateMemSession = getActiveMemorySession();
|
|
30338
|
+
if (updateMemSession) {
|
|
30339
|
+
const updateTitle = updates.title || entityId.slice(0, 8);
|
|
30340
|
+
appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
|
|
30341
|
+
flushMemoryActions(client3, updateMemSession.cardId).catch(() => {});
|
|
30342
|
+
}
|
|
30105
30343
|
return { success: true, ...result };
|
|
30106
30344
|
}
|
|
30107
30345
|
case "harmony_forget": {
|
|
30108
30346
|
const entityId = exports_external.string().uuid().parse(args.entityId);
|
|
30347
|
+
let forgetTitle = null;
|
|
30348
|
+
const forgetMemSession = getActiveMemorySession();
|
|
30349
|
+
if (forgetMemSession) {
|
|
30350
|
+
try {
|
|
30351
|
+
const { entity } = await client3.getMemoryEntity(entityId);
|
|
30352
|
+
forgetTitle = entity?.title || null;
|
|
30353
|
+
} catch {}
|
|
30354
|
+
}
|
|
30109
30355
|
await client3.deleteMemoryEntity(entityId);
|
|
30356
|
+
if (forgetMemSession) {
|
|
30357
|
+
appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
|
|
30358
|
+
flushMemoryActions(client3, forgetMemSession.cardId).catch(() => {});
|
|
30359
|
+
}
|
|
30110
30360
|
return { success: true };
|
|
30111
30361
|
}
|
|
30112
30362
|
case "harmony_relate": {
|
|
@@ -30128,6 +30378,11 @@ async function handleToolCall(name, args, deps) {
|
|
|
30128
30378
|
relation_type: relationType,
|
|
30129
30379
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined
|
|
30130
30380
|
});
|
|
30381
|
+
const relateMemSession = getActiveMemorySession();
|
|
30382
|
+
if (relateMemSession) {
|
|
30383
|
+
appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
|
|
30384
|
+
flushMemoryActions(client3, relateMemSession.cardId).catch(() => {});
|
|
30385
|
+
}
|
|
30131
30386
|
return { success: true, ...result };
|
|
30132
30387
|
}
|
|
30133
30388
|
case "harmony_memory_search": {
|
|
@@ -30141,6 +30396,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30141
30396
|
type: args.type,
|
|
30142
30397
|
limit: args.limit
|
|
30143
30398
|
});
|
|
30399
|
+
const searchMemSession = getActiveMemorySession();
|
|
30400
|
+
if (searchMemSession) {
|
|
30401
|
+
incrementMemoryReads(searchMemSession.cardId);
|
|
30402
|
+
}
|
|
30144
30403
|
return markdown || "No memories found.";
|
|
30145
30404
|
}
|
|
30146
30405
|
case "harmony_vault_index": {
|
|
@@ -30235,7 +30494,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
30235
30494
|
source: args.source || "agent",
|
|
30236
30495
|
tasks: args.tasks
|
|
30237
30496
|
});
|
|
30238
|
-
const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
|
|
30497
|
+
const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
|
|
30239
30498
|
return {
|
|
30240
30499
|
success: true,
|
|
30241
30500
|
planId: result.plan.id,
|
|
@@ -30730,7 +30989,10 @@ class HarmonyMCPServer {
|
|
|
30730
30989
|
const configDeps = createConfigDeps();
|
|
30731
30990
|
initAutoSession(async (client3, cardId, status) => {
|
|
30732
30991
|
await runEndSessionPipeline(client3, configDeps, cardId, status);
|
|
30733
|
-
}, () => getClient())
|
|
30992
|
+
}, () => getClient(), () => {
|
|
30993
|
+
const cv = this.server.getClientVersion();
|
|
30994
|
+
return cv ? { name: cv.name, version: cv.version } : null;
|
|
30995
|
+
});
|
|
30734
30996
|
const handleShutdown = async () => {
|
|
30735
30997
|
try {
|
|
30736
30998
|
await shutdownAllSessions();
|
|
@@ -31000,7 +31262,7 @@ Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with
|
|
|
31000
31262
|
## [Plan Title]
|
|
31001
31263
|
**Status:** draft/active/archived | **Phase:** plan/execute/verify/done
|
|
31002
31264
|
**Tasks:** N total (X pending, Y in_progress, Z completed)
|
|
31003
|
-
**URL:** https://gethmy.com/plans/{id}
|
|
31265
|
+
**URL:** https://app.gethmy.com/plans/{id}
|
|
31004
31266
|
\\\`\\\`\\\`
|
|
31005
31267
|
|
|
31006
31268
|
If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
|
|
@@ -31034,7 +31296,7 @@ Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
|
|
|
31034
31296
|
#### Option A — Single card
|
|
31035
31297
|
1. Call \`harmony_create_card\` with:
|
|
31036
31298
|
- \`title\`: Plan title
|
|
31037
|
-
- \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
|
|
31299
|
+
- \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://app.gethmy.com/plans/{planId})\`
|
|
31038
31300
|
- \`priority\`: based on plan task priorities (use highest)
|
|
31039
31301
|
2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
|
|
31040
31302
|
|
|
@@ -31156,7 +31418,7 @@ Call \`harmony_create_plan\` with:
|
|
|
31156
31418
|
### 2B.5 — User Approval
|
|
31157
31419
|
|
|
31158
31420
|
Show the user:
|
|
31159
|
-
- Plan URL: \`https://gethmy.com/plans/{id}\`
|
|
31421
|
+
- Plan URL: \`https://app.gethmy.com/plans/{id}\`
|
|
31160
31422
|
- Number of tasks created
|
|
31161
31423
|
- Brief summary
|
|
31162
31424
|
|
|
@@ -32936,7 +33198,7 @@ function getWriteSummary(files, options = {}) {
|
|
|
32936
33198
|
|
|
32937
33199
|
// src/tui/setup.ts
|
|
32938
33200
|
var GLOBAL_SKILLS_DIR = join5(homedir4(), ".agents", "skills");
|
|
32939
|
-
var API_URL = "https://gethmy.com/api";
|
|
33201
|
+
var API_URL = "https://app.gethmy.com/api";
|
|
32940
33202
|
async function registerMcpServer() {
|
|
32941
33203
|
try {
|
|
32942
33204
|
const { execSync } = await import("node:child_process");
|
|
@@ -33286,7 +33548,7 @@ async function runSetup(options = {}) {
|
|
|
33286
33548
|
if (!validation.valid) {
|
|
33287
33549
|
spinner.stop(colors.error("API key validation failed"));
|
|
33288
33550
|
M2.error(validation.error || "Could not connect to Harmony API");
|
|
33289
|
-
M2.info("Get an API key at: https://gethmy.com/user/keys");
|
|
33551
|
+
M2.info("Get an API key at: https://app.gethmy.com/user/keys");
|
|
33290
33552
|
process.exit(1);
|
|
33291
33553
|
}
|
|
33292
33554
|
if (!userEmail) {
|
|
@@ -33586,7 +33848,7 @@ async function runSetup(options = {}) {
|
|
|
33586
33848
|
}
|
|
33587
33849
|
console.log("");
|
|
33588
33850
|
console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
|
|
33589
|
-
console.log(` ${colors.dim("Need help? Visit https://gethmy.com/docs/mcp")}`);
|
|
33851
|
+
console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
|
|
33590
33852
|
console.log("");
|
|
33591
33853
|
}
|
|
33592
33854
|
|
|
@@ -33650,7 +33912,7 @@ Context:`);
|
|
|
33650
33912
|
console.log(`Status: Not configured
|
|
33651
33913
|
`);
|
|
33652
33914
|
console.log("Run: npx @gethmy/mcp setup");
|
|
33653
|
-
console.log("Get an API key at: https://gethmy.com/user/keys");
|
|
33915
|
+
console.log("Get an API key at: https://app.gethmy.com/user/keys");
|
|
33654
33916
|
}
|
|
33655
33917
|
});
|
|
33656
33918
|
program.command("reset").description("Remove stored configuration").action(() => {
|