@gethmy/mcp 2.1.2 → 2.2.0

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 CHANGED
@@ -8735,7 +8735,7 @@ var {
8735
8735
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8736
8736
  import { homedir } from "node:os";
8737
8737
  import { join } from "node:path";
8738
- var DEFAULT_API_URL = "https://gethmy.com/api";
8738
+ var DEFAULT_API_URL = "https://app.gethmy.com/api";
8739
8739
  var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
8740
8740
  function getConfigDir() {
8741
8741
  return join(homedir(), ".harmony-mcp");
@@ -26672,18 +26672,22 @@ async function trackActivity(cardId, options) {
26672
26672
  agentName: "Auto-detected Agent"
26673
26673
  });
26674
26674
  }
26675
- function markExplicit(cardId) {
26675
+ function markExplicit(cardId, options) {
26676
26676
  const existing = activeSessions.get(cardId);
26677
26677
  if (existing) {
26678
26678
  existing.isExplicit = true;
26679
+ if (options?.agentIdentifier)
26680
+ existing.agentIdentifier = options.agentIdentifier;
26681
+ if (options?.agentName)
26682
+ existing.agentName = options.agentName;
26679
26683
  } else {
26680
26684
  activeSessions.set(cardId, {
26681
26685
  cardId,
26682
26686
  startedAt: Date.now(),
26683
26687
  lastActivityAt: Date.now(),
26684
26688
  isExplicit: true,
26685
- agentIdentifier: "explicit",
26686
- agentName: "Explicit Agent"
26689
+ agentIdentifier: options?.agentIdentifier ?? "explicit",
26690
+ agentName: options?.agentName ?? "Explicit Agent"
26687
26691
  });
26688
26692
  }
26689
26693
  }
@@ -27774,6 +27778,7 @@ ${card.description}`);
27774
27778
  roleFraming.focus.forEach((f) => {
27775
27779
  sections.push(`- ${f}`);
27776
27780
  });
27781
+ 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).`);
27777
27782
  sections.push(`
27778
27783
  ## Suggested Outputs`);
27779
27784
  roleFraming.outputSuggestions.forEach((s) => {
@@ -27913,6 +27918,96 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
27913
27918
  }
27914
27919
 
27915
27920
  // src/server.ts
27921
+ var memorySessions = new Map;
27922
+ function initMemorySession(cardId, agentIdentifier, agentName) {
27923
+ memorySessions.set(cardId, {
27924
+ cardId,
27925
+ agentIdentifier,
27926
+ agentName,
27927
+ memoryReadCount: 0,
27928
+ pendingActions: [],
27929
+ allActions: [],
27930
+ dirty: false
27931
+ });
27932
+ }
27933
+ function getMemorySession(cardId) {
27934
+ return memorySessions.get(cardId);
27935
+ }
27936
+ function appendMemoryAction(cardId, action) {
27937
+ const session = memorySessions.get(cardId);
27938
+ if (!session)
27939
+ return;
27940
+ const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
27941
+ const entry = { action: truncated, ts: new Date().toISOString() };
27942
+ session.pendingActions.push(entry);
27943
+ session.dirty = true;
27944
+ }
27945
+ function incrementMemoryReads(cardId) {
27946
+ const session = memorySessions.get(cardId);
27947
+ if (!session)
27948
+ return;
27949
+ session.memoryReadCount++;
27950
+ session.dirty = true;
27951
+ }
27952
+ async function flushMemoryActions(client3, cardId) {
27953
+ const session = memorySessions.get(cardId);
27954
+ if (!session || !session.dirty)
27955
+ return;
27956
+ try {
27957
+ if (session.memoryReadCount > 0) {
27958
+ session.allActions.push({
27959
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
27960
+ ts: new Date().toISOString()
27961
+ });
27962
+ session.memoryReadCount = 0;
27963
+ }
27964
+ if (session.pendingActions.length > 0) {
27965
+ session.allActions.push(...session.pendingActions);
27966
+ session.pendingActions = [];
27967
+ }
27968
+ if (session.allActions.length > 10) {
27969
+ session.allActions = session.allActions.slice(-10);
27970
+ }
27971
+ await client3.updateAgentProgress(cardId, {
27972
+ agentIdentifier: session.agentIdentifier,
27973
+ agentName: session.agentName,
27974
+ recentActions: session.allActions
27975
+ });
27976
+ session.dirty = false;
27977
+ } catch (err) {
27978
+ console.error("[memory-session] flush failed:", err);
27979
+ }
27980
+ }
27981
+ function mergeMemoryActionsInto(cardId, callerActions) {
27982
+ const session = memorySessions.get(cardId);
27983
+ if (!session)
27984
+ return callerActions;
27985
+ if (session.memoryReadCount > 0) {
27986
+ session.allActions.push({
27987
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
27988
+ ts: new Date().toISOString()
27989
+ });
27990
+ session.memoryReadCount = 0;
27991
+ }
27992
+ if (session.pendingActions.length > 0) {
27993
+ session.allActions.push(...session.pendingActions);
27994
+ session.pendingActions = [];
27995
+ }
27996
+ const merged = [...callerActions, ...session.allActions];
27997
+ const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
27998
+ session.allActions = trimmed;
27999
+ session.dirty = false;
28000
+ return trimmed;
28001
+ }
28002
+ function getActiveMemorySession() {
28003
+ for (const session of memorySessions.values()) {
28004
+ return session;
28005
+ }
28006
+ return;
28007
+ }
28008
+ function cleanupMemorySession(cardId) {
28009
+ memorySessions.delete(cardId);
28010
+ }
27916
28011
  var TOOLS = {
27917
28012
  harmony_create_card: {
27918
28013
  description: "Create a new card in a Kanban column",
@@ -29426,7 +29521,7 @@ async function handleToolCall(name, args, deps) {
29426
29521
  const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
29427
29522
  if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
29428
29523
  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.
29524
+ ` + `You can generate an API key at https://app.gethmy.com → Settings → API Keys.
29430
29525
  ` + 'Or use "harmony_onboard" to create an account and configure automatically.');
29431
29526
  }
29432
29527
  const client3 = deps.isConfigured() ? deps.getClient() : null;
@@ -29729,7 +29824,8 @@ async function handleToolCall(name, args, deps) {
29729
29824
  currentTask: args.currentTask,
29730
29825
  estimatedMinutesRemaining: args.estimatedMinutesRemaining
29731
29826
  });
29732
- markExplicit(cardId);
29827
+ markExplicit(cardId, { agentIdentifier, agentName });
29828
+ initMemorySession(cardId, agentIdentifier, agentName);
29733
29829
  let prefetchedMemoryIds = [];
29734
29830
  try {
29735
29831
  const workspaceId = deps.getActiveWorkspaceId();
@@ -29776,6 +29872,14 @@ async function handleToolCall(name, args, deps) {
29776
29872
  const agentIdentifier = exports_external.string().min(1).max(100).parse(args.agentIdentifier);
29777
29873
  const agentName = exports_external.string().min(1).max(100).parse(args.agentName);
29778
29874
  const progressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
29875
+ const callerRecentActions = args.recentActions;
29876
+ const memSession = getMemorySession(cardId);
29877
+ let mergedRecentActions;
29878
+ if (memSession?.dirty) {
29879
+ mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
29880
+ } else if (callerRecentActions) {
29881
+ mergedRecentActions = callerRecentActions;
29882
+ }
29779
29883
  const result = await client3.updateAgentProgress(cardId, {
29780
29884
  agentIdentifier,
29781
29885
  agentName,
@@ -29783,7 +29887,8 @@ async function handleToolCall(name, args, deps) {
29783
29887
  progressPercent,
29784
29888
  currentTask: args.currentTask,
29785
29889
  blockers: args.blockers,
29786
- estimatedMinutesRemaining: args.estimatedMinutesRemaining
29890
+ estimatedMinutesRemaining: args.estimatedMinutesRemaining,
29891
+ ...mergedRecentActions && { recentActions: mergedRecentActions }
29787
29892
  });
29788
29893
  let midSessionLearnings = 0;
29789
29894
  try {
@@ -29808,6 +29913,8 @@ async function handleToolCall(name, args, deps) {
29808
29913
  const moveToColumn = args.moveToColumn;
29809
29914
  const sessionStatus = args.status || "completed";
29810
29915
  const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
29916
+ await flushMemoryActions(client3, cardId);
29917
+ cleanupMemorySession(cardId);
29811
29918
  const result = await client3.endAgentSession(cardId, {
29812
29919
  status: sessionStatus,
29813
29920
  progressPercent: endProgressPercent
@@ -29995,6 +30102,7 @@ async function handleToolCall(name, args, deps) {
29995
30102
  }
29996
30103
  const entityType = args.type || "context";
29997
30104
  const entityTags = args.tags || [];
30105
+ const activeMemSession = getActiveMemorySession();
29998
30106
  const result = await client3.createMemoryEntity({
29999
30107
  workspace_id: workspaceId,
30000
30108
  project_id: args.projectId || deps.getActiveProjectId() || undefined,
@@ -30006,7 +30114,7 @@ async function handleToolCall(name, args, deps) {
30006
30114
  metadata: args.metadata,
30007
30115
  confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined,
30008
30116
  tags: entityTags.length > 0 ? entityTags : undefined,
30009
- agent_identifier: "claude-code"
30117
+ agent_identifier: activeMemSession?.agentIdentifier || undefined
30010
30118
  });
30011
30119
  const newEntityIdForGraph = result.entity?.id;
30012
30120
  if (newEntityIdForGraph) {
@@ -30019,6 +30127,10 @@ async function handleToolCall(name, args, deps) {
30019
30127
  potentialContradictions = await detectContradictions(client3, newEntityId, entityType, title, content, entityTags, workspaceId, args.projectId || deps.getActiveProjectId() || undefined);
30020
30128
  } catch {}
30021
30129
  }
30130
+ if (activeMemSession) {
30131
+ appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
30132
+ flushMemoryActions(client3, activeMemSession.cardId).catch(() => {});
30133
+ }
30022
30134
  return {
30023
30135
  success: true,
30024
30136
  ...result,
@@ -30082,6 +30194,10 @@ async function handleToolCall(name, args, deps) {
30082
30194
  } catch (_) {}
30083
30195
  })).catch(() => {});
30084
30196
  }
30197
+ const recallMemSession = getActiveMemorySession();
30198
+ if (recallMemSession) {
30199
+ incrementMemoryReads(recallMemSession.cardId);
30200
+ }
30085
30201
  return markdown || "No memories found.";
30086
30202
  }
30087
30203
  case "harmony_update_memory": {
@@ -30102,11 +30218,29 @@ async function handleToolCall(name, args, deps) {
30102
30218
  if (args.metadata !== undefined)
30103
30219
  updates.metadata = args.metadata;
30104
30220
  const result = await client3.updateMemoryEntity(entityId, updates);
30221
+ const updateMemSession = getActiveMemorySession();
30222
+ if (updateMemSession) {
30223
+ const updateTitle = updates.title || entityId.slice(0, 8);
30224
+ appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
30225
+ flushMemoryActions(client3, updateMemSession.cardId).catch(() => {});
30226
+ }
30105
30227
  return { success: true, ...result };
30106
30228
  }
30107
30229
  case "harmony_forget": {
30108
30230
  const entityId = exports_external.string().uuid().parse(args.entityId);
30231
+ let forgetTitle = null;
30232
+ const forgetMemSession = getActiveMemorySession();
30233
+ if (forgetMemSession) {
30234
+ try {
30235
+ const { entity } = await client3.getMemoryEntity(entityId);
30236
+ forgetTitle = entity?.title || null;
30237
+ } catch {}
30238
+ }
30109
30239
  await client3.deleteMemoryEntity(entityId);
30240
+ if (forgetMemSession) {
30241
+ appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
30242
+ flushMemoryActions(client3, forgetMemSession.cardId).catch(() => {});
30243
+ }
30110
30244
  return { success: true };
30111
30245
  }
30112
30246
  case "harmony_relate": {
@@ -30128,6 +30262,11 @@ async function handleToolCall(name, args, deps) {
30128
30262
  relation_type: relationType,
30129
30263
  confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined
30130
30264
  });
30265
+ const relateMemSession = getActiveMemorySession();
30266
+ if (relateMemSession) {
30267
+ appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
30268
+ flushMemoryActions(client3, relateMemSession.cardId).catch(() => {});
30269
+ }
30131
30270
  return { success: true, ...result };
30132
30271
  }
30133
30272
  case "harmony_memory_search": {
@@ -30141,6 +30280,10 @@ async function handleToolCall(name, args, deps) {
30141
30280
  type: args.type,
30142
30281
  limit: args.limit
30143
30282
  });
30283
+ const searchMemSession = getActiveMemorySession();
30284
+ if (searchMemSession) {
30285
+ incrementMemoryReads(searchMemSession.cardId);
30286
+ }
30144
30287
  return markdown || "No memories found.";
30145
30288
  }
30146
30289
  case "harmony_vault_index": {
@@ -30235,7 +30378,7 @@ async function handleToolCall(name, args, deps) {
30235
30378
  source: args.source || "agent",
30236
30379
  tasks: args.tasks
30237
30380
  });
30238
- const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
30381
+ const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
30239
30382
  return {
30240
30383
  success: true,
30241
30384
  planId: result.plan.id,
@@ -31000,7 +31143,7 @@ Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with
31000
31143
  ## [Plan Title]
31001
31144
  **Status:** draft/active/archived | **Phase:** plan/execute/verify/done
31002
31145
  **Tasks:** N total (X pending, Y in_progress, Z completed)
31003
- **URL:** https://gethmy.com/plans/{id}
31146
+ **URL:** https://app.gethmy.com/plans/{id}
31004
31147
  \\\`\\\`\\\`
31005
31148
 
31006
31149
  If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
@@ -31034,7 +31177,7 @@ Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
31034
31177
  #### Option A — Single card
31035
31178
  1. Call \`harmony_create_card\` with:
31036
31179
  - \`title\`: Plan title
31037
- - \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
31180
+ - \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://app.gethmy.com/plans/{planId})\`
31038
31181
  - \`priority\`: based on plan task priorities (use highest)
31039
31182
  2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
31040
31183
 
@@ -31156,7 +31299,7 @@ Call \`harmony_create_plan\` with:
31156
31299
  ### 2B.5 — User Approval
31157
31300
 
31158
31301
  Show the user:
31159
- - Plan URL: \`https://gethmy.com/plans/{id}\`
31302
+ - Plan URL: \`https://app.gethmy.com/plans/{id}\`
31160
31303
  - Number of tasks created
31161
31304
  - Brief summary
31162
31305
 
@@ -32936,7 +33079,7 @@ function getWriteSummary(files, options = {}) {
32936
33079
 
32937
33080
  // src/tui/setup.ts
32938
33081
  var GLOBAL_SKILLS_DIR = join5(homedir4(), ".agents", "skills");
32939
- var API_URL = "https://gethmy.com/api";
33082
+ var API_URL = "https://app.gethmy.com/api";
32940
33083
  async function registerMcpServer() {
32941
33084
  try {
32942
33085
  const { execSync } = await import("node:child_process");
@@ -33286,7 +33429,7 @@ async function runSetup(options = {}) {
33286
33429
  if (!validation.valid) {
33287
33430
  spinner.stop(colors.error("API key validation failed"));
33288
33431
  M2.error(validation.error || "Could not connect to Harmony API");
33289
- M2.info("Get an API key at: https://gethmy.com/user/keys");
33432
+ M2.info("Get an API key at: https://app.gethmy.com/user/keys");
33290
33433
  process.exit(1);
33291
33434
  }
33292
33435
  if (!userEmail) {
@@ -33467,7 +33610,9 @@ async function runSetup(options = {}) {
33467
33610
  console.log(` ${colors.bold("Project:")} ${selectedProjectName || selectedProjectId}`);
33468
33611
  }
33469
33612
  if (allFiles.length > 0) {
33470
- const summary = getWriteSummary(allFiles, { force: options.force });
33613
+ const summary = getWriteSummary(allFiles, {
33614
+ force: options.force || needsSkills
33615
+ });
33471
33616
  if (summary.toCreate.length > 0) {
33472
33617
  console.log("");
33473
33618
  console.log(` ${colors.success("Files to create:")}`);
@@ -33508,7 +33653,9 @@ async function runSetup(options = {}) {
33508
33653
  }
33509
33654
  console.log("");
33510
33655
  if (allFiles.length > 0) {
33511
- await writeFilesWithProgress(allFiles, { force: options.force });
33656
+ await writeFilesWithProgress(allFiles, {
33657
+ force: options.force || needsSkills
33658
+ });
33512
33659
  }
33513
33660
  if (allSymlinks.length > 0) {
33514
33661
  for (const symlink of allSymlinks) {
@@ -33582,7 +33729,7 @@ async function runSetup(options = {}) {
33582
33729
  }
33583
33730
  console.log("");
33584
33731
  console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
33585
- console.log(` ${colors.dim("Need help? Visit https://gethmy.com/docs/mcp")}`);
33732
+ console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
33586
33733
  console.log("");
33587
33734
  }
33588
33735
 
@@ -33646,7 +33793,7 @@ Context:`);
33646
33793
  console.log(`Status: Not configured
33647
33794
  `);
33648
33795
  console.log("Run: npx @gethmy/mcp setup");
33649
- console.log("Get an API key at: https://gethmy.com/user/keys");
33796
+ console.log("Get an API key at: https://app.gethmy.com/user/keys");
33650
33797
  }
33651
33798
  });
33652
33799
  program.command("reset").description("Remove stored configuration").action(() => {
package/dist/index.js CHANGED
@@ -22849,7 +22849,7 @@ class StdioServerTransport {
22849
22849
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
22850
22850
  import { homedir } from "node:os";
22851
22851
  import { join as join2 } from "node:path";
22852
- var DEFAULT_API_URL = "https://gethmy.com/api";
22852
+ var DEFAULT_API_URL = "https://app.gethmy.com/api";
22853
22853
  var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
22854
22854
  function getConfigDir() {
22855
22855
  return join2(homedir(), ".harmony-mcp");
@@ -24432,18 +24432,22 @@ async function trackActivity(cardId, options) {
24432
24432
  agentName: "Auto-detected Agent"
24433
24433
  });
24434
24434
  }
24435
- function markExplicit(cardId) {
24435
+ function markExplicit(cardId, options) {
24436
24436
  const existing = activeSessions.get(cardId);
24437
24437
  if (existing) {
24438
24438
  existing.isExplicit = true;
24439
+ if (options?.agentIdentifier)
24440
+ existing.agentIdentifier = options.agentIdentifier;
24441
+ if (options?.agentName)
24442
+ existing.agentName = options.agentName;
24439
24443
  } else {
24440
24444
  activeSessions.set(cardId, {
24441
24445
  cardId,
24442
24446
  startedAt: Date.now(),
24443
24447
  lastActivityAt: Date.now(),
24444
24448
  isExplicit: true,
24445
- agentIdentifier: "explicit",
24446
- agentName: "Explicit Agent"
24449
+ agentIdentifier: options?.agentIdentifier ?? "explicit",
24450
+ agentName: options?.agentName ?? "Explicit Agent"
24447
24451
  });
24448
24452
  }
24449
24453
  }
@@ -25534,6 +25538,7 @@ ${card.description}`);
25534
25538
  roleFraming.focus.forEach((f) => {
25535
25539
  sections.push(`- ${f}`);
25536
25540
  });
25541
+ 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).`);
25537
25542
  sections.push(`
25538
25543
  ## Suggested Outputs`);
25539
25544
  roleFraming.outputSuggestions.forEach((s) => {
@@ -25673,6 +25678,96 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
25673
25678
  }
25674
25679
 
25675
25680
  // src/server.ts
25681
+ var memorySessions = new Map;
25682
+ function initMemorySession(cardId, agentIdentifier, agentName) {
25683
+ memorySessions.set(cardId, {
25684
+ cardId,
25685
+ agentIdentifier,
25686
+ agentName,
25687
+ memoryReadCount: 0,
25688
+ pendingActions: [],
25689
+ allActions: [],
25690
+ dirty: false
25691
+ });
25692
+ }
25693
+ function getMemorySession(cardId) {
25694
+ return memorySessions.get(cardId);
25695
+ }
25696
+ function appendMemoryAction(cardId, action) {
25697
+ const session = memorySessions.get(cardId);
25698
+ if (!session)
25699
+ return;
25700
+ const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
25701
+ const entry = { action: truncated, ts: new Date().toISOString() };
25702
+ session.pendingActions.push(entry);
25703
+ session.dirty = true;
25704
+ }
25705
+ function incrementMemoryReads(cardId) {
25706
+ const session = memorySessions.get(cardId);
25707
+ if (!session)
25708
+ return;
25709
+ session.memoryReadCount++;
25710
+ session.dirty = true;
25711
+ }
25712
+ async function flushMemoryActions(client3, cardId) {
25713
+ const session = memorySessions.get(cardId);
25714
+ if (!session || !session.dirty)
25715
+ return;
25716
+ try {
25717
+ if (session.memoryReadCount > 0) {
25718
+ session.allActions.push({
25719
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
25720
+ ts: new Date().toISOString()
25721
+ });
25722
+ session.memoryReadCount = 0;
25723
+ }
25724
+ if (session.pendingActions.length > 0) {
25725
+ session.allActions.push(...session.pendingActions);
25726
+ session.pendingActions = [];
25727
+ }
25728
+ if (session.allActions.length > 10) {
25729
+ session.allActions = session.allActions.slice(-10);
25730
+ }
25731
+ await client3.updateAgentProgress(cardId, {
25732
+ agentIdentifier: session.agentIdentifier,
25733
+ agentName: session.agentName,
25734
+ recentActions: session.allActions
25735
+ });
25736
+ session.dirty = false;
25737
+ } catch (err) {
25738
+ console.error("[memory-session] flush failed:", err);
25739
+ }
25740
+ }
25741
+ function mergeMemoryActionsInto(cardId, callerActions) {
25742
+ const session = memorySessions.get(cardId);
25743
+ if (!session)
25744
+ return callerActions;
25745
+ if (session.memoryReadCount > 0) {
25746
+ session.allActions.push({
25747
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
25748
+ ts: new Date().toISOString()
25749
+ });
25750
+ session.memoryReadCount = 0;
25751
+ }
25752
+ if (session.pendingActions.length > 0) {
25753
+ session.allActions.push(...session.pendingActions);
25754
+ session.pendingActions = [];
25755
+ }
25756
+ const merged = [...callerActions, ...session.allActions];
25757
+ const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
25758
+ session.allActions = trimmed;
25759
+ session.dirty = false;
25760
+ return trimmed;
25761
+ }
25762
+ function getActiveMemorySession() {
25763
+ for (const session of memorySessions.values()) {
25764
+ return session;
25765
+ }
25766
+ return;
25767
+ }
25768
+ function cleanupMemorySession(cardId) {
25769
+ memorySessions.delete(cardId);
25770
+ }
25676
25771
  var TOOLS = {
25677
25772
  harmony_create_card: {
25678
25773
  description: "Create a new card in a Kanban column",
@@ -27186,7 +27281,7 @@ async function handleToolCall(name, args, deps) {
27186
27281
  const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
27187
27282
  if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
27188
27283
  throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
27189
- ` + `You can generate an API key at https://gethmy.com → Settings → API Keys.
27284
+ ` + `You can generate an API key at https://app.gethmy.com → Settings → API Keys.
27190
27285
  ` + 'Or use "harmony_onboard" to create an account and configure automatically.');
27191
27286
  }
27192
27287
  const client3 = deps.isConfigured() ? deps.getClient() : null;
@@ -27489,7 +27584,8 @@ async function handleToolCall(name, args, deps) {
27489
27584
  currentTask: args.currentTask,
27490
27585
  estimatedMinutesRemaining: args.estimatedMinutesRemaining
27491
27586
  });
27492
- markExplicit(cardId);
27587
+ markExplicit(cardId, { agentIdentifier, agentName });
27588
+ initMemorySession(cardId, agentIdentifier, agentName);
27493
27589
  let prefetchedMemoryIds = [];
27494
27590
  try {
27495
27591
  const workspaceId = deps.getActiveWorkspaceId();
@@ -27536,6 +27632,14 @@ async function handleToolCall(name, args, deps) {
27536
27632
  const agentIdentifier = exports_external.string().min(1).max(100).parse(args.agentIdentifier);
27537
27633
  const agentName = exports_external.string().min(1).max(100).parse(args.agentName);
27538
27634
  const progressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
27635
+ const callerRecentActions = args.recentActions;
27636
+ const memSession = getMemorySession(cardId);
27637
+ let mergedRecentActions;
27638
+ if (memSession?.dirty) {
27639
+ mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
27640
+ } else if (callerRecentActions) {
27641
+ mergedRecentActions = callerRecentActions;
27642
+ }
27539
27643
  const result = await client3.updateAgentProgress(cardId, {
27540
27644
  agentIdentifier,
27541
27645
  agentName,
@@ -27543,7 +27647,8 @@ async function handleToolCall(name, args, deps) {
27543
27647
  progressPercent,
27544
27648
  currentTask: args.currentTask,
27545
27649
  blockers: args.blockers,
27546
- estimatedMinutesRemaining: args.estimatedMinutesRemaining
27650
+ estimatedMinutesRemaining: args.estimatedMinutesRemaining,
27651
+ ...mergedRecentActions && { recentActions: mergedRecentActions }
27547
27652
  });
27548
27653
  let midSessionLearnings = 0;
27549
27654
  try {
@@ -27568,6 +27673,8 @@ async function handleToolCall(name, args, deps) {
27568
27673
  const moveToColumn = args.moveToColumn;
27569
27674
  const sessionStatus = args.status || "completed";
27570
27675
  const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
27676
+ await flushMemoryActions(client3, cardId);
27677
+ cleanupMemorySession(cardId);
27571
27678
  const result = await client3.endAgentSession(cardId, {
27572
27679
  status: sessionStatus,
27573
27680
  progressPercent: endProgressPercent
@@ -27755,6 +27862,7 @@ async function handleToolCall(name, args, deps) {
27755
27862
  }
27756
27863
  const entityType = args.type || "context";
27757
27864
  const entityTags = args.tags || [];
27865
+ const activeMemSession = getActiveMemorySession();
27758
27866
  const result = await client3.createMemoryEntity({
27759
27867
  workspace_id: workspaceId,
27760
27868
  project_id: args.projectId || deps.getActiveProjectId() || undefined,
@@ -27766,7 +27874,7 @@ async function handleToolCall(name, args, deps) {
27766
27874
  metadata: args.metadata,
27767
27875
  confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined,
27768
27876
  tags: entityTags.length > 0 ? entityTags : undefined,
27769
- agent_identifier: "claude-code"
27877
+ agent_identifier: activeMemSession?.agentIdentifier || undefined
27770
27878
  });
27771
27879
  const newEntityIdForGraph = result.entity?.id;
27772
27880
  if (newEntityIdForGraph) {
@@ -27779,6 +27887,10 @@ async function handleToolCall(name, args, deps) {
27779
27887
  potentialContradictions = await detectContradictions(client3, newEntityId, entityType, title, content, entityTags, workspaceId, args.projectId || deps.getActiveProjectId() || undefined);
27780
27888
  } catch {}
27781
27889
  }
27890
+ if (activeMemSession) {
27891
+ appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
27892
+ flushMemoryActions(client3, activeMemSession.cardId).catch(() => {});
27893
+ }
27782
27894
  return {
27783
27895
  success: true,
27784
27896
  ...result,
@@ -27842,6 +27954,10 @@ async function handleToolCall(name, args, deps) {
27842
27954
  } catch (_) {}
27843
27955
  })).catch(() => {});
27844
27956
  }
27957
+ const recallMemSession = getActiveMemorySession();
27958
+ if (recallMemSession) {
27959
+ incrementMemoryReads(recallMemSession.cardId);
27960
+ }
27845
27961
  return markdown || "No memories found.";
27846
27962
  }
27847
27963
  case "harmony_update_memory": {
@@ -27862,11 +27978,29 @@ async function handleToolCall(name, args, deps) {
27862
27978
  if (args.metadata !== undefined)
27863
27979
  updates.metadata = args.metadata;
27864
27980
  const result = await client3.updateMemoryEntity(entityId, updates);
27981
+ const updateMemSession = getActiveMemorySession();
27982
+ if (updateMemSession) {
27983
+ const updateTitle = updates.title || entityId.slice(0, 8);
27984
+ appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
27985
+ flushMemoryActions(client3, updateMemSession.cardId).catch(() => {});
27986
+ }
27865
27987
  return { success: true, ...result };
27866
27988
  }
27867
27989
  case "harmony_forget": {
27868
27990
  const entityId = exports_external.string().uuid().parse(args.entityId);
27991
+ let forgetTitle = null;
27992
+ const forgetMemSession = getActiveMemorySession();
27993
+ if (forgetMemSession) {
27994
+ try {
27995
+ const { entity } = await client3.getMemoryEntity(entityId);
27996
+ forgetTitle = entity?.title || null;
27997
+ } catch {}
27998
+ }
27869
27999
  await client3.deleteMemoryEntity(entityId);
28000
+ if (forgetMemSession) {
28001
+ appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
28002
+ flushMemoryActions(client3, forgetMemSession.cardId).catch(() => {});
28003
+ }
27870
28004
  return { success: true };
27871
28005
  }
27872
28006
  case "harmony_relate": {
@@ -27888,6 +28022,11 @@ async function handleToolCall(name, args, deps) {
27888
28022
  relation_type: relationType,
27889
28023
  confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined
27890
28024
  });
28025
+ const relateMemSession = getActiveMemorySession();
28026
+ if (relateMemSession) {
28027
+ appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
28028
+ flushMemoryActions(client3, relateMemSession.cardId).catch(() => {});
28029
+ }
27891
28030
  return { success: true, ...result };
27892
28031
  }
27893
28032
  case "harmony_memory_search": {
@@ -27901,6 +28040,10 @@ async function handleToolCall(name, args, deps) {
27901
28040
  type: args.type,
27902
28041
  limit: args.limit
27903
28042
  });
28043
+ const searchMemSession = getActiveMemorySession();
28044
+ if (searchMemSession) {
28045
+ incrementMemoryReads(searchMemSession.cardId);
28046
+ }
27904
28047
  return markdown || "No memories found.";
27905
28048
  }
27906
28049
  case "harmony_vault_index": {
@@ -27995,7 +28138,7 @@ async function handleToolCall(name, args, deps) {
27995
28138
  source: args.source || "agent",
27996
28139
  tasks: args.tasks
27997
28140
  });
27998
- const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
28141
+ const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
27999
28142
  return {
28000
28143
  success: true,
28001
28144
  planId: result.plan.id,
@@ -82,11 +82,16 @@ export async function trackActivity(cardId, options) {
82
82
  }
83
83
  /**
84
84
  * Mark a session as explicitly started (won't be auto-ended by card switching or inactivity).
85
+ * Optionally accepts the real agent identifier/name to store in the session.
85
86
  */
86
- export function markExplicit(cardId) {
87
+ export function markExplicit(cardId, options) {
87
88
  const existing = activeSessions.get(cardId);
88
89
  if (existing) {
89
90
  existing.isExplicit = true;
91
+ if (options?.agentIdentifier)
92
+ existing.agentIdentifier = options.agentIdentifier;
93
+ if (options?.agentName)
94
+ existing.agentName = options.agentName;
90
95
  }
91
96
  else {
92
97
  // Track the explicit session even if we didn't auto-start it
@@ -95,8 +100,8 @@ export function markExplicit(cardId) {
95
100
  startedAt: Date.now(),
96
101
  lastActivityAt: Date.now(),
97
102
  isExplicit: true,
98
- agentIdentifier: "explicit",
99
- agentName: "Explicit Agent",
103
+ agentIdentifier: options?.agentIdentifier ?? "explicit",
104
+ agentName: options?.agentName ?? "Explicit Agent",
100
105
  });
101
106
  }
102
107
  }