harmony-mcp 1.13.1 → 1.13.2
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 +183 -3
- package/dist/index.js +183 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25371,6 +25371,31 @@ async function extractMidSessionLearnings(client2, ctx) {
|
|
|
25371
25371
|
const now = Date.now();
|
|
25372
25372
|
const entityIds = [];
|
|
25373
25373
|
const history = sessionTaskHistory.get(ctx.cardId);
|
|
25374
|
+
if (ctx.currentTask) {
|
|
25375
|
+
const previousTask = history?.lastTask || "";
|
|
25376
|
+
const similarity = levenshteinSimilarity(previousTask, ctx.currentTask);
|
|
25377
|
+
const existingSteps = history?.steps || [];
|
|
25378
|
+
if (existingSteps.length === 0 || similarity < 0.6 && previousTask.length > 0) {
|
|
25379
|
+
const newStep = {
|
|
25380
|
+
task: ctx.currentTask,
|
|
25381
|
+
progress: ctx.progressPercent ?? 0,
|
|
25382
|
+
timestamp: now
|
|
25383
|
+
};
|
|
25384
|
+
if (existingSteps.length === 0 && previousTask.length > 0) {
|
|
25385
|
+
existingSteps.push({
|
|
25386
|
+
task: previousTask,
|
|
25387
|
+
progress: 0,
|
|
25388
|
+
timestamp: history?.lastExtractionAt ?? now
|
|
25389
|
+
});
|
|
25390
|
+
}
|
|
25391
|
+
existingSteps.push(newStep);
|
|
25392
|
+
sessionTaskHistory.set(ctx.cardId, {
|
|
25393
|
+
lastTask: ctx.currentTask,
|
|
25394
|
+
lastExtractionAt: history?.lastExtractionAt ?? 0,
|
|
25395
|
+
steps: existingSteps
|
|
25396
|
+
});
|
|
25397
|
+
}
|
|
25398
|
+
}
|
|
25374
25399
|
if (history && now - history.lastExtractionAt < MID_SESSION_RATE_LIMIT_MS) {
|
|
25375
25400
|
if (ctx.status !== "blocked" || !ctx.blockers?.length) {
|
|
25376
25401
|
return { count: 0, entityIds: [] };
|
|
@@ -25408,7 +25433,8 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
25408
25433
|
}
|
|
25409
25434
|
sessionTaskHistory.set(ctx.cardId, {
|
|
25410
25435
|
lastTask: ctx.currentTask || "",
|
|
25411
|
-
lastExtractionAt: now
|
|
25436
|
+
lastExtractionAt: now,
|
|
25437
|
+
steps: history?.steps || []
|
|
25412
25438
|
});
|
|
25413
25439
|
return { count: entityIds.length, entityIds };
|
|
25414
25440
|
}
|
|
@@ -25444,9 +25470,11 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
25444
25470
|
entityIds.push(entity.id);
|
|
25445
25471
|
} catch {}
|
|
25446
25472
|
}
|
|
25473
|
+
const currentHistory = sessionTaskHistory.get(ctx.cardId);
|
|
25447
25474
|
sessionTaskHistory.set(ctx.cardId, {
|
|
25448
25475
|
lastTask: ctx.currentTask,
|
|
25449
|
-
lastExtractionAt: entityIds.length > 0 ? now :
|
|
25476
|
+
lastExtractionAt: entityIds.length > 0 ? now : currentHistory?.lastExtractionAt ?? 0,
|
|
25477
|
+
steps: currentHistory?.steps || []
|
|
25450
25478
|
});
|
|
25451
25479
|
}
|
|
25452
25480
|
return { count: entityIds.length, entityIds };
|
|
@@ -25454,6 +25482,128 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
25454
25482
|
function clearMidSessionTracking(cardId) {
|
|
25455
25483
|
sessionTaskHistory.delete(cardId);
|
|
25456
25484
|
}
|
|
25485
|
+
function enrichSteps(steps) {
|
|
25486
|
+
return steps.map((step, i) => {
|
|
25487
|
+
const prevProgress = i > 0 ? steps[i - 1].progress : 0;
|
|
25488
|
+
const prevTimestamp = i > 0 ? steps[i - 1].timestamp : step.timestamp;
|
|
25489
|
+
const progressDelta = step.progress - prevProgress;
|
|
25490
|
+
return {
|
|
25491
|
+
task: step.task,
|
|
25492
|
+
progress: step.progress,
|
|
25493
|
+
progressDelta,
|
|
25494
|
+
durationMs: step.timestamp - prevTimestamp,
|
|
25495
|
+
isKeyDecision: progressDelta >= 20
|
|
25496
|
+
};
|
|
25497
|
+
});
|
|
25498
|
+
}
|
|
25499
|
+
function buildProcedureContent(session, enrichedSteps, wikiLinksLine) {
|
|
25500
|
+
const triggerLabels = session.cardLabels.length > 0 ? `Labels: ${session.cardLabels.join(", ")}` : "";
|
|
25501
|
+
const stepsMarkdown = enrichedSteps.map((s, i) => {
|
|
25502
|
+
const marker = s.isKeyDecision ? " **[key step]**" : "";
|
|
25503
|
+
const duration3 = s.durationMs > 0 ? ` (~${Math.round(s.durationMs / 60000)}min)` : "";
|
|
25504
|
+
return `${i + 1}. ${s.task} (${s.progress}%, +${s.progressDelta}%)${marker}${duration3}`;
|
|
25505
|
+
}).join(`
|
|
25506
|
+
`);
|
|
25507
|
+
const subtaskSection = session.cardSubtasks && session.cardSubtasks.length > 0 ? [
|
|
25508
|
+
"",
|
|
25509
|
+
"## Subtasks Completed",
|
|
25510
|
+
...session.cardSubtasks.map((s) => `- [${s.done ? "x" : " "}] ${s.title}`)
|
|
25511
|
+
].join(`
|
|
25512
|
+
`) : "";
|
|
25513
|
+
const durationInfo = session.sessionDurationMs ? `
|
|
25514
|
+
Duration: ${Math.round(session.sessionDurationMs / 60000)} minutes` : "";
|
|
25515
|
+
return [
|
|
25516
|
+
"## Trigger",
|
|
25517
|
+
`When working on: "${session.cardTitle}"`,
|
|
25518
|
+
triggerLabels,
|
|
25519
|
+
"",
|
|
25520
|
+
"## Steps",
|
|
25521
|
+
stepsMarkdown,
|
|
25522
|
+
subtaskSection,
|
|
25523
|
+
"",
|
|
25524
|
+
"## Outcome",
|
|
25525
|
+
`Completed at ${session.progressPercent ?? "unknown"}%`,
|
|
25526
|
+
session.currentTask ? `Final state: ${session.currentTask}` : "",
|
|
25527
|
+
`Agent: ${session.agentName}`,
|
|
25528
|
+
durationInfo,
|
|
25529
|
+
wikiLinksLine
|
|
25530
|
+
].filter((line) => line !== undefined).join(`
|
|
25531
|
+
`);
|
|
25532
|
+
}
|
|
25533
|
+
async function extractOrReinforceProcedure(client2, session, steps, workspaceId, projectId, wikiLinksLine = "") {
|
|
25534
|
+
const enrichedSteps = enrichSteps(steps);
|
|
25535
|
+
const newContent = buildProcedureContent(session, enrichedSteps, wikiLinksLine);
|
|
25536
|
+
const tags = [
|
|
25537
|
+
"auto-extracted",
|
|
25538
|
+
"procedure",
|
|
25539
|
+
...session.cardLabels.slice(0, 3)
|
|
25540
|
+
];
|
|
25541
|
+
try {
|
|
25542
|
+
const similar = await findSimilarEntities(client2, `Procedure: ${session.cardTitle}`, newContent, workspaceId, { projectId, limit: 5, minRrfScore: 0.03 });
|
|
25543
|
+
const matchingProcedure = similar.find((e) => e.type === "procedure" && (e.rrf_score ?? 0) >= 0.05);
|
|
25544
|
+
if (matchingProcedure) {
|
|
25545
|
+
const { entity: rawEntity } = await client2.getMemoryEntity(matchingProcedure.id);
|
|
25546
|
+
const fullEntity = rawEntity;
|
|
25547
|
+
const currentMeta = fullEntity.metadata || {};
|
|
25548
|
+
const sourceCards = (currentMeta.source_cards || []).slice();
|
|
25549
|
+
if (!sourceCards.includes(session.cardId)) {
|
|
25550
|
+
sourceCards.push(session.cardId);
|
|
25551
|
+
}
|
|
25552
|
+
const reuseCount = (currentMeta.reuse_count || 0) + 1;
|
|
25553
|
+
const currentConfidence = fullEntity.confidence ?? 0.7;
|
|
25554
|
+
const newConfidence = Math.min(0.95, currentConfidence + 0.05);
|
|
25555
|
+
const stepsAppendix = enrichedSteps.map((s, i) => `${i + 1}. ${s.task} (${s.progress}%)`).join(`
|
|
25556
|
+
`);
|
|
25557
|
+
const appendix = `
|
|
25558
|
+
|
|
25559
|
+
---
|
|
25560
|
+
### Execution ${reuseCount + 1}: ${session.cardTitle}
|
|
25561
|
+
${stepsAppendix}
|
|
25562
|
+
Agent: ${session.agentName} | ${new Date().toISOString().split("T")[0]}`;
|
|
25563
|
+
const updatedMeta = {
|
|
25564
|
+
...currentMeta,
|
|
25565
|
+
reuse_count: reuseCount,
|
|
25566
|
+
source_cards: sourceCards,
|
|
25567
|
+
last_reinforced_at: new Date().toISOString(),
|
|
25568
|
+
step_count: Math.max(currentMeta.step_count || 0, steps.length)
|
|
25569
|
+
};
|
|
25570
|
+
const shouldPromote = reuseCount >= 2 && fullEntity.memory_tier !== "reference";
|
|
25571
|
+
await client2.updateMemoryEntity(fullEntity.id, {
|
|
25572
|
+
content: (fullEntity.content || "") + appendix,
|
|
25573
|
+
confidence: newConfidence,
|
|
25574
|
+
metadata: {
|
|
25575
|
+
...updatedMeta,
|
|
25576
|
+
...shouldPromote ? {
|
|
25577
|
+
promoted_reason: `Reinforced by ${reuseCount + 1} successful sessions`,
|
|
25578
|
+
promoted_at: new Date().toISOString()
|
|
25579
|
+
} : {}
|
|
25580
|
+
},
|
|
25581
|
+
...shouldPromote ? { memory_tier: "reference" } : {}
|
|
25582
|
+
});
|
|
25583
|
+
return { mode: "reinforced", entityId: fullEntity.id };
|
|
25584
|
+
}
|
|
25585
|
+
} catch {}
|
|
25586
|
+
return {
|
|
25587
|
+
mode: "created",
|
|
25588
|
+
learning: {
|
|
25589
|
+
title: `Procedure: ${session.cardTitle}`,
|
|
25590
|
+
content: newContent,
|
|
25591
|
+
type: "procedure",
|
|
25592
|
+
tier: "episode",
|
|
25593
|
+
confidence: 0.7,
|
|
25594
|
+
tags,
|
|
25595
|
+
metadata: {
|
|
25596
|
+
source: "active_learning",
|
|
25597
|
+
card_id: session.cardId,
|
|
25598
|
+
source_cards: [session.cardId],
|
|
25599
|
+
step_count: steps.length,
|
|
25600
|
+
key_step_count: enrichedSteps.filter((s) => s.isKeyDecision).length,
|
|
25601
|
+
reuse_count: 0,
|
|
25602
|
+
review_status: "pending"
|
|
25603
|
+
}
|
|
25604
|
+
}
|
|
25605
|
+
};
|
|
25606
|
+
}
|
|
25457
25607
|
async function extractLearnings(client2, session) {
|
|
25458
25608
|
const workspaceId = getActiveWorkspaceId();
|
|
25459
25609
|
if (!workspaceId) {
|
|
@@ -25554,6 +25704,19 @@ Agent: ${session.agentName}`,
|
|
|
25554
25704
|
});
|
|
25555
25705
|
}
|
|
25556
25706
|
const entityIds = [];
|
|
25707
|
+
const stepHistory = sessionTaskHistory.get(session.cardId);
|
|
25708
|
+
const hasEnoughSteps = stepHistory && stepHistory.steps.length >= 2;
|
|
25709
|
+
const isSuccessful = session.status === "completed" && (session.progressPercent === undefined || session.progressPercent >= 85) && !session.blockers?.length;
|
|
25710
|
+
if (isSuccessful && hasEnoughSteps) {
|
|
25711
|
+
const procedureResult = await extractOrReinforceProcedure(client2, session, stepHistory.steps, workspaceId, projectId, wikiLinksLine);
|
|
25712
|
+
if (procedureResult) {
|
|
25713
|
+
if (procedureResult.mode === "created") {
|
|
25714
|
+
learnings.push(procedureResult.learning);
|
|
25715
|
+
} else {
|
|
25716
|
+
entityIds.push(procedureResult.entityId);
|
|
25717
|
+
}
|
|
25718
|
+
}
|
|
25719
|
+
}
|
|
25557
25720
|
const createdPairs = [];
|
|
25558
25721
|
for (const learning of learnings) {
|
|
25559
25722
|
try {
|
|
@@ -29088,11 +29251,18 @@ async function handleToolCall(name, args, deps) {
|
|
|
29088
29251
|
let learningsExtracted = 0;
|
|
29089
29252
|
let cardTitle = "";
|
|
29090
29253
|
let cardLabels = [];
|
|
29254
|
+
let cardDescription = "";
|
|
29255
|
+
let cardSubtasks = [];
|
|
29091
29256
|
try {
|
|
29092
29257
|
const { card } = await client3.getCard(cardId);
|
|
29093
29258
|
const typedCard = card;
|
|
29094
29259
|
cardTitle = typedCard.title || "";
|
|
29095
29260
|
cardLabels = (typedCard.labels || []).map((l) => l.name);
|
|
29261
|
+
cardDescription = typedCard.description || "";
|
|
29262
|
+
cardSubtasks = (typedCard.subtasks || []).map((s) => ({
|
|
29263
|
+
title: s.title,
|
|
29264
|
+
done: s.done
|
|
29265
|
+
}));
|
|
29096
29266
|
const projectId = typedCard.project_id;
|
|
29097
29267
|
if (moveToColumn && projectId) {
|
|
29098
29268
|
const board = await client3.getBoard(projectId, {
|
|
@@ -29108,6 +29278,13 @@ async function handleToolCall(name, args, deps) {
|
|
|
29108
29278
|
} catch {}
|
|
29109
29279
|
try {
|
|
29110
29280
|
const session = result.session;
|
|
29281
|
+
let sessionDurationMs;
|
|
29282
|
+
if (session?.created_at) {
|
|
29283
|
+
const startTime = new Date(session.created_at).getTime();
|
|
29284
|
+
if (!Number.isNaN(startTime)) {
|
|
29285
|
+
sessionDurationMs = Date.now() - startTime;
|
|
29286
|
+
}
|
|
29287
|
+
}
|
|
29111
29288
|
const sessionContext = {
|
|
29112
29289
|
cardId,
|
|
29113
29290
|
cardTitle,
|
|
@@ -29117,7 +29294,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
29117
29294
|
status: sessionStatus,
|
|
29118
29295
|
progressPercent: endProgressPercent,
|
|
29119
29296
|
blockers: session?.blockers || undefined,
|
|
29120
|
-
currentTask: session?.current_task || undefined
|
|
29297
|
+
currentTask: session?.current_task || undefined,
|
|
29298
|
+
sessionDurationMs,
|
|
29299
|
+
cardDescription: cardDescription || undefined,
|
|
29300
|
+
cardSubtasks: cardSubtasks.length > 0 ? cardSubtasks : undefined
|
|
29121
29301
|
};
|
|
29122
29302
|
const learningResult = await extractLearnings(client3, sessionContext);
|
|
29123
29303
|
learningsExtracted = learningResult.count;
|
package/dist/index.js
CHANGED
|
@@ -23131,6 +23131,31 @@ async function extractMidSessionLearnings(client2, ctx) {
|
|
|
23131
23131
|
const now = Date.now();
|
|
23132
23132
|
const entityIds = [];
|
|
23133
23133
|
const history = sessionTaskHistory.get(ctx.cardId);
|
|
23134
|
+
if (ctx.currentTask) {
|
|
23135
|
+
const previousTask = history?.lastTask || "";
|
|
23136
|
+
const similarity = levenshteinSimilarity(previousTask, ctx.currentTask);
|
|
23137
|
+
const existingSteps = history?.steps || [];
|
|
23138
|
+
if (existingSteps.length === 0 || similarity < 0.6 && previousTask.length > 0) {
|
|
23139
|
+
const newStep = {
|
|
23140
|
+
task: ctx.currentTask,
|
|
23141
|
+
progress: ctx.progressPercent ?? 0,
|
|
23142
|
+
timestamp: now
|
|
23143
|
+
};
|
|
23144
|
+
if (existingSteps.length === 0 && previousTask.length > 0) {
|
|
23145
|
+
existingSteps.push({
|
|
23146
|
+
task: previousTask,
|
|
23147
|
+
progress: 0,
|
|
23148
|
+
timestamp: history?.lastExtractionAt ?? now
|
|
23149
|
+
});
|
|
23150
|
+
}
|
|
23151
|
+
existingSteps.push(newStep);
|
|
23152
|
+
sessionTaskHistory.set(ctx.cardId, {
|
|
23153
|
+
lastTask: ctx.currentTask,
|
|
23154
|
+
lastExtractionAt: history?.lastExtractionAt ?? 0,
|
|
23155
|
+
steps: existingSteps
|
|
23156
|
+
});
|
|
23157
|
+
}
|
|
23158
|
+
}
|
|
23134
23159
|
if (history && now - history.lastExtractionAt < MID_SESSION_RATE_LIMIT_MS) {
|
|
23135
23160
|
if (ctx.status !== "blocked" || !ctx.blockers?.length) {
|
|
23136
23161
|
return { count: 0, entityIds: [] };
|
|
@@ -23168,7 +23193,8 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
23168
23193
|
}
|
|
23169
23194
|
sessionTaskHistory.set(ctx.cardId, {
|
|
23170
23195
|
lastTask: ctx.currentTask || "",
|
|
23171
|
-
lastExtractionAt: now
|
|
23196
|
+
lastExtractionAt: now,
|
|
23197
|
+
steps: history?.steps || []
|
|
23172
23198
|
});
|
|
23173
23199
|
return { count: entityIds.length, entityIds };
|
|
23174
23200
|
}
|
|
@@ -23204,9 +23230,11 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
23204
23230
|
entityIds.push(entity.id);
|
|
23205
23231
|
} catch {}
|
|
23206
23232
|
}
|
|
23233
|
+
const currentHistory = sessionTaskHistory.get(ctx.cardId);
|
|
23207
23234
|
sessionTaskHistory.set(ctx.cardId, {
|
|
23208
23235
|
lastTask: ctx.currentTask,
|
|
23209
|
-
lastExtractionAt: entityIds.length > 0 ? now :
|
|
23236
|
+
lastExtractionAt: entityIds.length > 0 ? now : currentHistory?.lastExtractionAt ?? 0,
|
|
23237
|
+
steps: currentHistory?.steps || []
|
|
23210
23238
|
});
|
|
23211
23239
|
}
|
|
23212
23240
|
return { count: entityIds.length, entityIds };
|
|
@@ -23214,6 +23242,128 @@ Progress: ${ctx.progressPercent ?? "unknown"}%`,
|
|
|
23214
23242
|
function clearMidSessionTracking(cardId) {
|
|
23215
23243
|
sessionTaskHistory.delete(cardId);
|
|
23216
23244
|
}
|
|
23245
|
+
function enrichSteps(steps) {
|
|
23246
|
+
return steps.map((step, i) => {
|
|
23247
|
+
const prevProgress = i > 0 ? steps[i - 1].progress : 0;
|
|
23248
|
+
const prevTimestamp = i > 0 ? steps[i - 1].timestamp : step.timestamp;
|
|
23249
|
+
const progressDelta = step.progress - prevProgress;
|
|
23250
|
+
return {
|
|
23251
|
+
task: step.task,
|
|
23252
|
+
progress: step.progress,
|
|
23253
|
+
progressDelta,
|
|
23254
|
+
durationMs: step.timestamp - prevTimestamp,
|
|
23255
|
+
isKeyDecision: progressDelta >= 20
|
|
23256
|
+
};
|
|
23257
|
+
});
|
|
23258
|
+
}
|
|
23259
|
+
function buildProcedureContent(session, enrichedSteps, wikiLinksLine) {
|
|
23260
|
+
const triggerLabels = session.cardLabels.length > 0 ? `Labels: ${session.cardLabels.join(", ")}` : "";
|
|
23261
|
+
const stepsMarkdown = enrichedSteps.map((s, i) => {
|
|
23262
|
+
const marker = s.isKeyDecision ? " **[key step]**" : "";
|
|
23263
|
+
const duration3 = s.durationMs > 0 ? ` (~${Math.round(s.durationMs / 60000)}min)` : "";
|
|
23264
|
+
return `${i + 1}. ${s.task} (${s.progress}%, +${s.progressDelta}%)${marker}${duration3}`;
|
|
23265
|
+
}).join(`
|
|
23266
|
+
`);
|
|
23267
|
+
const subtaskSection = session.cardSubtasks && session.cardSubtasks.length > 0 ? [
|
|
23268
|
+
"",
|
|
23269
|
+
"## Subtasks Completed",
|
|
23270
|
+
...session.cardSubtasks.map((s) => `- [${s.done ? "x" : " "}] ${s.title}`)
|
|
23271
|
+
].join(`
|
|
23272
|
+
`) : "";
|
|
23273
|
+
const durationInfo = session.sessionDurationMs ? `
|
|
23274
|
+
Duration: ${Math.round(session.sessionDurationMs / 60000)} minutes` : "";
|
|
23275
|
+
return [
|
|
23276
|
+
"## Trigger",
|
|
23277
|
+
`When working on: "${session.cardTitle}"`,
|
|
23278
|
+
triggerLabels,
|
|
23279
|
+
"",
|
|
23280
|
+
"## Steps",
|
|
23281
|
+
stepsMarkdown,
|
|
23282
|
+
subtaskSection,
|
|
23283
|
+
"",
|
|
23284
|
+
"## Outcome",
|
|
23285
|
+
`Completed at ${session.progressPercent ?? "unknown"}%`,
|
|
23286
|
+
session.currentTask ? `Final state: ${session.currentTask}` : "",
|
|
23287
|
+
`Agent: ${session.agentName}`,
|
|
23288
|
+
durationInfo,
|
|
23289
|
+
wikiLinksLine
|
|
23290
|
+
].filter((line) => line !== undefined).join(`
|
|
23291
|
+
`);
|
|
23292
|
+
}
|
|
23293
|
+
async function extractOrReinforceProcedure(client2, session, steps, workspaceId, projectId, wikiLinksLine = "") {
|
|
23294
|
+
const enrichedSteps = enrichSteps(steps);
|
|
23295
|
+
const newContent = buildProcedureContent(session, enrichedSteps, wikiLinksLine);
|
|
23296
|
+
const tags = [
|
|
23297
|
+
"auto-extracted",
|
|
23298
|
+
"procedure",
|
|
23299
|
+
...session.cardLabels.slice(0, 3)
|
|
23300
|
+
];
|
|
23301
|
+
try {
|
|
23302
|
+
const similar = await findSimilarEntities(client2, `Procedure: ${session.cardTitle}`, newContent, workspaceId, { projectId, limit: 5, minRrfScore: 0.03 });
|
|
23303
|
+
const matchingProcedure = similar.find((e) => e.type === "procedure" && (e.rrf_score ?? 0) >= 0.05);
|
|
23304
|
+
if (matchingProcedure) {
|
|
23305
|
+
const { entity: rawEntity } = await client2.getMemoryEntity(matchingProcedure.id);
|
|
23306
|
+
const fullEntity = rawEntity;
|
|
23307
|
+
const currentMeta = fullEntity.metadata || {};
|
|
23308
|
+
const sourceCards = (currentMeta.source_cards || []).slice();
|
|
23309
|
+
if (!sourceCards.includes(session.cardId)) {
|
|
23310
|
+
sourceCards.push(session.cardId);
|
|
23311
|
+
}
|
|
23312
|
+
const reuseCount = (currentMeta.reuse_count || 0) + 1;
|
|
23313
|
+
const currentConfidence = fullEntity.confidence ?? 0.7;
|
|
23314
|
+
const newConfidence = Math.min(0.95, currentConfidence + 0.05);
|
|
23315
|
+
const stepsAppendix = enrichedSteps.map((s, i) => `${i + 1}. ${s.task} (${s.progress}%)`).join(`
|
|
23316
|
+
`);
|
|
23317
|
+
const appendix = `
|
|
23318
|
+
|
|
23319
|
+
---
|
|
23320
|
+
### Execution ${reuseCount + 1}: ${session.cardTitle}
|
|
23321
|
+
${stepsAppendix}
|
|
23322
|
+
Agent: ${session.agentName} | ${new Date().toISOString().split("T")[0]}`;
|
|
23323
|
+
const updatedMeta = {
|
|
23324
|
+
...currentMeta,
|
|
23325
|
+
reuse_count: reuseCount,
|
|
23326
|
+
source_cards: sourceCards,
|
|
23327
|
+
last_reinforced_at: new Date().toISOString(),
|
|
23328
|
+
step_count: Math.max(currentMeta.step_count || 0, steps.length)
|
|
23329
|
+
};
|
|
23330
|
+
const shouldPromote = reuseCount >= 2 && fullEntity.memory_tier !== "reference";
|
|
23331
|
+
await client2.updateMemoryEntity(fullEntity.id, {
|
|
23332
|
+
content: (fullEntity.content || "") + appendix,
|
|
23333
|
+
confidence: newConfidence,
|
|
23334
|
+
metadata: {
|
|
23335
|
+
...updatedMeta,
|
|
23336
|
+
...shouldPromote ? {
|
|
23337
|
+
promoted_reason: `Reinforced by ${reuseCount + 1} successful sessions`,
|
|
23338
|
+
promoted_at: new Date().toISOString()
|
|
23339
|
+
} : {}
|
|
23340
|
+
},
|
|
23341
|
+
...shouldPromote ? { memory_tier: "reference" } : {}
|
|
23342
|
+
});
|
|
23343
|
+
return { mode: "reinforced", entityId: fullEntity.id };
|
|
23344
|
+
}
|
|
23345
|
+
} catch {}
|
|
23346
|
+
return {
|
|
23347
|
+
mode: "created",
|
|
23348
|
+
learning: {
|
|
23349
|
+
title: `Procedure: ${session.cardTitle}`,
|
|
23350
|
+
content: newContent,
|
|
23351
|
+
type: "procedure",
|
|
23352
|
+
tier: "episode",
|
|
23353
|
+
confidence: 0.7,
|
|
23354
|
+
tags,
|
|
23355
|
+
metadata: {
|
|
23356
|
+
source: "active_learning",
|
|
23357
|
+
card_id: session.cardId,
|
|
23358
|
+
source_cards: [session.cardId],
|
|
23359
|
+
step_count: steps.length,
|
|
23360
|
+
key_step_count: enrichedSteps.filter((s) => s.isKeyDecision).length,
|
|
23361
|
+
reuse_count: 0,
|
|
23362
|
+
review_status: "pending"
|
|
23363
|
+
}
|
|
23364
|
+
}
|
|
23365
|
+
};
|
|
23366
|
+
}
|
|
23217
23367
|
async function extractLearnings(client2, session) {
|
|
23218
23368
|
const workspaceId = getActiveWorkspaceId();
|
|
23219
23369
|
if (!workspaceId) {
|
|
@@ -23314,6 +23464,19 @@ Agent: ${session.agentName}`,
|
|
|
23314
23464
|
});
|
|
23315
23465
|
}
|
|
23316
23466
|
const entityIds = [];
|
|
23467
|
+
const stepHistory = sessionTaskHistory.get(session.cardId);
|
|
23468
|
+
const hasEnoughSteps = stepHistory && stepHistory.steps.length >= 2;
|
|
23469
|
+
const isSuccessful = session.status === "completed" && (session.progressPercent === undefined || session.progressPercent >= 85) && !session.blockers?.length;
|
|
23470
|
+
if (isSuccessful && hasEnoughSteps) {
|
|
23471
|
+
const procedureResult = await extractOrReinforceProcedure(client2, session, stepHistory.steps, workspaceId, projectId, wikiLinksLine);
|
|
23472
|
+
if (procedureResult) {
|
|
23473
|
+
if (procedureResult.mode === "created") {
|
|
23474
|
+
learnings.push(procedureResult.learning);
|
|
23475
|
+
} else {
|
|
23476
|
+
entityIds.push(procedureResult.entityId);
|
|
23477
|
+
}
|
|
23478
|
+
}
|
|
23479
|
+
}
|
|
23317
23480
|
const createdPairs = [];
|
|
23318
23481
|
for (const learning of learnings) {
|
|
23319
23482
|
try {
|
|
@@ -26848,11 +27011,18 @@ async function handleToolCall(name, args, deps) {
|
|
|
26848
27011
|
let learningsExtracted = 0;
|
|
26849
27012
|
let cardTitle = "";
|
|
26850
27013
|
let cardLabels = [];
|
|
27014
|
+
let cardDescription = "";
|
|
27015
|
+
let cardSubtasks = [];
|
|
26851
27016
|
try {
|
|
26852
27017
|
const { card } = await client3.getCard(cardId);
|
|
26853
27018
|
const typedCard = card;
|
|
26854
27019
|
cardTitle = typedCard.title || "";
|
|
26855
27020
|
cardLabels = (typedCard.labels || []).map((l) => l.name);
|
|
27021
|
+
cardDescription = typedCard.description || "";
|
|
27022
|
+
cardSubtasks = (typedCard.subtasks || []).map((s) => ({
|
|
27023
|
+
title: s.title,
|
|
27024
|
+
done: s.done
|
|
27025
|
+
}));
|
|
26856
27026
|
const projectId = typedCard.project_id;
|
|
26857
27027
|
if (moveToColumn && projectId) {
|
|
26858
27028
|
const board = await client3.getBoard(projectId, {
|
|
@@ -26868,6 +27038,13 @@ async function handleToolCall(name, args, deps) {
|
|
|
26868
27038
|
} catch {}
|
|
26869
27039
|
try {
|
|
26870
27040
|
const session = result.session;
|
|
27041
|
+
let sessionDurationMs;
|
|
27042
|
+
if (session?.created_at) {
|
|
27043
|
+
const startTime = new Date(session.created_at).getTime();
|
|
27044
|
+
if (!Number.isNaN(startTime)) {
|
|
27045
|
+
sessionDurationMs = Date.now() - startTime;
|
|
27046
|
+
}
|
|
27047
|
+
}
|
|
26871
27048
|
const sessionContext = {
|
|
26872
27049
|
cardId,
|
|
26873
27050
|
cardTitle,
|
|
@@ -26877,7 +27054,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
26877
27054
|
status: sessionStatus,
|
|
26878
27055
|
progressPercent: endProgressPercent,
|
|
26879
27056
|
blockers: session?.blockers || undefined,
|
|
26880
|
-
currentTask: session?.current_task || undefined
|
|
27057
|
+
currentTask: session?.current_task || undefined,
|
|
27058
|
+
sessionDurationMs,
|
|
27059
|
+
cardDescription: cardDescription || undefined,
|
|
27060
|
+
cardSubtasks: cardSubtasks.length > 0 ? cardSubtasks : undefined
|
|
26881
27061
|
};
|
|
26882
27062
|
const learningResult = await extractLearnings(client3, sessionContext);
|
|
26883
27063
|
learningsExtracted = learningResult.count;
|