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.
Files changed (3) hide show
  1. package/dist/cli.js +183 -3
  2. package/dist/index.js +183 -3
  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 : history?.lastExtractionAt ?? 0
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 : history?.lastExtractionAt ?? 0
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harmony-mcp",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"