@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.
@@ -11,7 +11,118 @@ import { consolidateMemories } from "./consolidation.js";
11
11
  import { assembleContext, cacheManifest, computeRelevanceScore, getCachedManifest, mapToContextEntity, recordContextFeedback, trackSessionAssembly, } from "./context-assembly.js";
12
12
  import { autoExpandGraph } from "./graph-expansion.js";
13
13
  import { runLifecycleMaintenance } from "./lifecycle-maintenance.js";
14
- import { generatePrompt, } from "./prompt-builder.js";
14
+ const memorySessions = new Map();
15
+ function initMemorySession(cardId, agentIdentifier, agentName) {
16
+ memorySessions.set(cardId, {
17
+ cardId,
18
+ agentIdentifier,
19
+ agentName,
20
+ memoryReadCount: 0,
21
+ pendingActions: [],
22
+ allActions: [],
23
+ dirty: false,
24
+ });
25
+ }
26
+ function getMemorySession(cardId) {
27
+ return memorySessions.get(cardId);
28
+ }
29
+ function appendMemoryAction(cardId, action) {
30
+ const session = memorySessions.get(cardId);
31
+ if (!session)
32
+ return;
33
+ const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
34
+ const entry = { action: truncated, ts: new Date().toISOString() };
35
+ session.pendingActions.push(entry);
36
+ session.dirty = true;
37
+ }
38
+ function incrementMemoryReads(cardId) {
39
+ const session = memorySessions.get(cardId);
40
+ if (!session)
41
+ return;
42
+ session.memoryReadCount++;
43
+ session.dirty = true;
44
+ }
45
+ /**
46
+ * Flush pending memory actions to the backend via updateAgentProgress.
47
+ * Fire-and-forget: errors are logged but never thrown.
48
+ */
49
+ async function flushMemoryActions(client, cardId) {
50
+ const session = memorySessions.get(cardId);
51
+ if (!session || !session.dirty)
52
+ return;
53
+ try {
54
+ // Batch reads into a single summary line
55
+ if (session.memoryReadCount > 0) {
56
+ session.allActions.push({
57
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
58
+ ts: new Date().toISOString(),
59
+ });
60
+ session.memoryReadCount = 0;
61
+ }
62
+ // Move pending writes to allActions
63
+ if (session.pendingActions.length > 0) {
64
+ session.allActions.push(...session.pendingActions);
65
+ session.pendingActions = [];
66
+ }
67
+ // Trim to max 10 (drop oldest)
68
+ if (session.allActions.length > 10) {
69
+ session.allActions = session.allActions.slice(-10);
70
+ }
71
+ await client.updateAgentProgress(cardId, {
72
+ agentIdentifier: session.agentIdentifier,
73
+ agentName: session.agentName,
74
+ recentActions: session.allActions,
75
+ });
76
+ session.dirty = false;
77
+ }
78
+ catch (err) {
79
+ // Fire-and-forget: log but don't propagate
80
+ console.error("[memory-session] flush failed:", err);
81
+ }
82
+ }
83
+ /**
84
+ * Merge memory actions into an existing recentActions array from a progress update.
85
+ * Returns the merged array (caller's actions first, then memory actions appended).
86
+ */
87
+ function mergeMemoryActionsInto(cardId, callerActions) {
88
+ const session = memorySessions.get(cardId);
89
+ if (!session)
90
+ return callerActions;
91
+ // Flush reads into allActions
92
+ if (session.memoryReadCount > 0) {
93
+ session.allActions.push({
94
+ action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
95
+ ts: new Date().toISOString(),
96
+ });
97
+ session.memoryReadCount = 0;
98
+ }
99
+ // Move pending to allActions
100
+ if (session.pendingActions.length > 0) {
101
+ session.allActions.push(...session.pendingActions);
102
+ session.pendingActions = [];
103
+ }
104
+ // Merge: caller actions + memory actions, trim to 10
105
+ const merged = [...callerActions, ...session.allActions];
106
+ const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
107
+ // Update allActions to the merged state
108
+ session.allActions = trimmed;
109
+ session.dirty = false;
110
+ return trimmed;
111
+ }
112
+ /**
113
+ * Get the first (and typically only) active memory session.
114
+ * Memory tools don't carry a cardId, so we find the active session.
115
+ */
116
+ function getActiveMemorySession() {
117
+ // Return the first active session (there should be at most one)
118
+ for (const session of memorySessions.values()) {
119
+ return session;
120
+ }
121
+ return undefined;
122
+ }
123
+ function cleanupMemorySession(cardId) {
124
+ memorySessions.delete(cardId);
125
+ }
15
126
  // Tool definitions
16
127
  const TOOLS = {
17
128
  // Card operations
@@ -1597,7 +1708,7 @@ async function handleToolCall(name, args, deps) {
1597
1708
  const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
1598
1709
  if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
1599
1710
  throw new Error('Not configured. Run "npx @gethmy/mcp setup" to set your API key.\n' +
1600
- "You can generate an API key at https://gethmy.com → Settings → API Keys.\n" +
1711
+ "You can generate an API key at https://app.gethmy.com → Settings → API Keys.\n" +
1601
1712
  'Or use "harmony_onboard" to create an account and configure automatically.');
1602
1713
  }
1603
1714
  const client = deps.isConfigured()
@@ -1944,7 +2055,9 @@ async function handleToolCall(name, args, deps) {
1944
2055
  estimatedMinutesRemaining: args.estimatedMinutesRemaining,
1945
2056
  });
1946
2057
  // Mark as explicit so auto-session won't interfere
1947
- markExplicit(cardId);
2058
+ markExplicit(cardId, { agentIdentifier, agentName });
2059
+ // Initialize memory session tracking for action visibility
2060
+ initMemorySession(cardId, agentIdentifier, agentName);
1948
2061
  // Prefetch relevant context (non-blocking, best-effort)
1949
2062
  let prefetchedMemoryIds = [];
1950
2063
  try {
@@ -2011,6 +2124,16 @@ async function handleToolCall(name, args, deps) {
2011
2124
  const progressPercent = args.progressPercent !== undefined
2012
2125
  ? z.number().min(0).max(100).parse(args.progressPercent)
2013
2126
  : undefined;
2127
+ // Merge any pending memory actions into the progress update
2128
+ const callerRecentActions = args.recentActions;
2129
+ const memSession = getMemorySession(cardId);
2130
+ let mergedRecentActions;
2131
+ if (memSession?.dirty) {
2132
+ mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
2133
+ }
2134
+ else if (callerRecentActions) {
2135
+ mergedRecentActions = callerRecentActions;
2136
+ }
2014
2137
  const result = await client.updateAgentProgress(cardId, {
2015
2138
  agentIdentifier,
2016
2139
  agentName,
@@ -2019,6 +2142,7 @@ async function handleToolCall(name, args, deps) {
2019
2142
  currentTask: args.currentTask,
2020
2143
  blockers: args.blockers,
2021
2144
  estimatedMinutesRemaining: args.estimatedMinutesRemaining,
2145
+ ...(mergedRecentActions && { recentActions: mergedRecentActions }),
2022
2146
  });
2023
2147
  // Mid-session learning extraction (fire-and-forget)
2024
2148
  let midSessionLearnings = 0;
@@ -2049,6 +2173,9 @@ async function handleToolCall(name, args, deps) {
2049
2173
  const endProgressPercent = args.progressPercent !== undefined
2050
2174
  ? z.number().min(0).max(100).parse(args.progressPercent)
2051
2175
  : undefined;
2176
+ // Final flush of any pending memory actions before ending the session
2177
+ await flushMemoryActions(client, cardId);
2178
+ cleanupMemorySession(cardId);
2052
2179
  const result = await client.endAgentSession(cardId, {
2053
2180
  status: sessionStatus,
2054
2181
  progressPercent: endProgressPercent,
@@ -2133,13 +2260,10 @@ async function handleToolCall(name, args, deps) {
2133
2260
  }
2134
2261
  // Prompt generation
2135
2262
  case "harmony_generate_prompt": {
2136
- // Get card - either by UUID or short ID
2137
- let cardData;
2138
- let columnData = null;
2263
+ // Resolve card ID either directly or via short ID
2264
+ let cardId;
2139
2265
  if (args.cardId) {
2140
- const cardId = z.string().uuid().parse(args.cardId);
2141
- const cardResult = await client.getCard(cardId);
2142
- cardData = cardResult.card;
2266
+ cardId = z.string().uuid().parse(args.cardId);
2143
2267
  }
2144
2268
  else if (args.shortId !== undefined) {
2145
2269
  const shortId = z.number().int().positive().parse(args.shortId);
@@ -2148,32 +2272,12 @@ async function handleToolCall(name, args, deps) {
2148
2272
  throw new Error("Project ID required when using shortId. Use harmony_set_project_context or provide projectId.");
2149
2273
  }
2150
2274
  const cardResult = await client.getCardByShortId(projectId, shortId);
2151
- cardData = cardResult.card;
2275
+ cardId = cardResult.card.id;
2152
2276
  }
2153
2277
  else {
2154
2278
  throw new Error("Either cardId or shortId must be provided");
2155
2279
  }
2156
- // Try to get column info from board
2157
- const projectIdForBoard = args.projectId ||
2158
- getActiveProjectId() ||
2159
- cardData.project_id;
2160
- if (projectIdForBoard) {
2161
- try {
2162
- const board = await client.getBoard(projectIdForBoard, {
2163
- summary: true,
2164
- });
2165
- const columnId = cardData
2166
- .column_id;
2167
- const column = board.columns.find((col) => col.id === columnId);
2168
- if (column) {
2169
- columnData = { name: column.name };
2170
- }
2171
- }
2172
- catch {
2173
- // Column info not available, continue without it
2174
- }
2175
- }
2176
- const variant = args.variant || "execute";
2280
+ // Parse MCP-specific context options
2177
2281
  const contextOptions = {};
2178
2282
  if (args.includeSubtasks !== undefined) {
2179
2283
  contextOptions.includeSubtasks =
@@ -2188,75 +2292,21 @@ async function handleToolCall(name, args, deps) {
2188
2292
  args.includeDescription === true ||
2189
2293
  args.includeDescription === "true";
2190
2294
  }
2191
- // Assemble context using the context assembly engine
2192
- let assembledContextStr;
2193
- let assemblyId;
2194
- let memories;
2195
- try {
2196
- const workspaceId = deps.getActiveWorkspaceId();
2197
- if (workspaceId && cardData.title) {
2198
- const cardLabels = (cardData.labels || []).map((l) => l.name);
2199
- const taskContext = [cardData.title, cardData.description || ""]
2200
- .filter(Boolean)
2201
- .join(" ");
2202
- const assembled = await assembleContext({
2203
- workspaceId,
2204
- projectId: getActiveProjectId() || undefined,
2205
- taskContext,
2206
- cardLabels,
2207
- cardId: cardData.id,
2208
- client,
2209
- });
2210
- if (assembled.context) {
2211
- assembledContextStr = assembled.context;
2212
- assemblyId = assembled.manifest.assemblyId;
2213
- cacheManifest(assembled.manifest);
2214
- }
2215
- }
2216
- }
2217
- catch {
2218
- // Context assembly failed, try legacy fallback
2219
- try {
2220
- const workspaceId = deps.getActiveWorkspaceId();
2221
- if (workspaceId && cardData.title) {
2222
- const memoryResult = await client.searchMemoryEntities(workspaceId, cardData.title, {
2223
- project_id: getActiveProjectId() || undefined,
2224
- limit: 5,
2225
- });
2226
- if (memoryResult.entities?.length > 0) {
2227
- memories = memoryResult.entities.map((e) => {
2228
- const entity = e;
2229
- return {
2230
- id: entity.id,
2231
- type: entity.type,
2232
- title: entity.title,
2233
- content: entity.content,
2234
- confidence: entity.confidence,
2235
- tags: entity.tags || [],
2236
- };
2237
- });
2238
- }
2239
- }
2240
- }
2241
- catch {
2242
- // Memory fetch also failed, continue without memories
2243
- }
2244
- }
2245
- const result = generatePrompt({
2246
- card: cardData,
2247
- column: columnData,
2248
- variant,
2249
- contextOptions,
2295
+ // Delegate to the shared prompt generation pipeline
2296
+ const result = await client.generateCardPrompt({
2297
+ cardId,
2298
+ workspaceId: deps.getActiveWorkspaceId() || "",
2299
+ projectId: args.projectId || getActiveProjectId() || undefined,
2300
+ variant: args.variant || "execute",
2250
2301
  customConstraints: args.customConstraints,
2251
- memories,
2252
- assembledContext: assembledContextStr,
2253
- assemblyId,
2302
+ contextOptions,
2254
2303
  });
2304
+ // MCP-specific: cache the assembly manifest for the feedback loop
2305
+ if (result.assemblyId) {
2306
+ trackSessionAssembly(cardId, result.assemblyId);
2307
+ }
2255
2308
  return {
2256
2309
  success: true,
2257
- cardId: cardData.id,
2258
- shortId: cardData.short_id,
2259
- title: cardData.title,
2260
2310
  ...result,
2261
2311
  };
2262
2312
  }
@@ -2270,6 +2320,8 @@ async function handleToolCall(name, args, deps) {
2270
2320
  }
2271
2321
  const entityType = args.type || "context";
2272
2322
  const entityTags = args.tags || [];
2323
+ // Use session's agent identifier if available, otherwise null
2324
+ const activeMemSession = getActiveMemorySession();
2273
2325
  const result = await client.createMemoryEntity({
2274
2326
  workspace_id: workspaceId,
2275
2327
  project_id: args.projectId || deps.getActiveProjectId() || undefined,
@@ -2283,7 +2335,7 @@ async function handleToolCall(name, args, deps) {
2283
2335
  ? z.number().min(0).max(1).parse(args.confidence)
2284
2336
  : undefined,
2285
2337
  tags: entityTags.length > 0 ? entityTags : undefined,
2286
- agent_identifier: "claude-code",
2338
+ agent_identifier: activeMemSession?.agentIdentifier || undefined,
2287
2339
  });
2288
2340
  // Fire-and-forget graph expansion: link new entity to semantically similar ones
2289
2341
  const newEntityIdForGraph = result.entity?.id;
@@ -2303,6 +2355,11 @@ async function handleToolCall(name, args, deps) {
2303
2355
  // Don't block creation if contradiction detection fails
2304
2356
  }
2305
2357
  }
2358
+ // Track memory write action and flush (fire-and-forget)
2359
+ if (activeMemSession) {
2360
+ appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
2361
+ flushMemoryActions(client, activeMemSession.cardId).catch(() => { });
2362
+ }
2306
2363
  return {
2307
2364
  success: true,
2308
2365
  ...result,
@@ -2373,6 +2430,11 @@ async function handleToolCall(name, args, deps) {
2373
2430
  }
2374
2431
  })).catch(() => { });
2375
2432
  }
2433
+ // Track memory read (batched on flush)
2434
+ const recallMemSession = getActiveMemorySession();
2435
+ if (recallMemSession) {
2436
+ incrementMemoryReads(recallMemSession.cardId);
2437
+ }
2376
2438
  return markdown || "No memories found.";
2377
2439
  }
2378
2440
  case "harmony_update_memory": {
@@ -2393,11 +2455,35 @@ async function handleToolCall(name, args, deps) {
2393
2455
  if (args.metadata !== undefined)
2394
2456
  updates.metadata = args.metadata;
2395
2457
  const result = await client.updateMemoryEntity(entityId, updates);
2458
+ // Track memory write action and flush (fire-and-forget)
2459
+ const updateMemSession = getActiveMemorySession();
2460
+ if (updateMemSession) {
2461
+ const updateTitle = updates.title || entityId.slice(0, 8);
2462
+ appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
2463
+ flushMemoryActions(client, updateMemSession.cardId).catch(() => { });
2464
+ }
2396
2465
  return { success: true, ...result };
2397
2466
  }
2398
2467
  case "harmony_forget": {
2399
2468
  const entityId = z.string().uuid().parse(args.entityId);
2469
+ // Fetch title before deletion for action tracking
2470
+ let forgetTitle = null;
2471
+ const forgetMemSession = getActiveMemorySession();
2472
+ if (forgetMemSession) {
2473
+ try {
2474
+ const { entity } = await client.getMemoryEntity(entityId);
2475
+ forgetTitle = entity?.title || null;
2476
+ }
2477
+ catch {
2478
+ // Non-fatal: use truncated ID if fetch fails
2479
+ }
2480
+ }
2400
2481
  await client.deleteMemoryEntity(entityId);
2482
+ // Track memory write action and flush (fire-and-forget)
2483
+ if (forgetMemSession) {
2484
+ appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
2485
+ flushMemoryActions(client, forgetMemSession.cardId).catch(() => { });
2486
+ }
2401
2487
  return { success: true };
2402
2488
  }
2403
2489
  case "harmony_relate": {
@@ -2423,6 +2509,12 @@ async function handleToolCall(name, args, deps) {
2423
2509
  ? z.number().min(0).max(1).parse(args.confidence)
2424
2510
  : undefined,
2425
2511
  });
2512
+ // Track memory write action and flush (fire-and-forget)
2513
+ const relateMemSession = getActiveMemorySession();
2514
+ if (relateMemSession) {
2515
+ appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
2516
+ flushMemoryActions(client, relateMemSession.cardId).catch(() => { });
2517
+ }
2426
2518
  return { success: true, ...result };
2427
2519
  }
2428
2520
  case "harmony_memory_search": {
@@ -2438,6 +2530,11 @@ async function handleToolCall(name, args, deps) {
2438
2530
  type: args.type,
2439
2531
  limit: args.limit,
2440
2532
  });
2533
+ // Track memory read (batched on flush)
2534
+ const searchMemSession = getActiveMemorySession();
2535
+ if (searchMemSession) {
2536
+ incrementMemoryReads(searchMemSession.cardId);
2537
+ }
2441
2538
  return markdown || "No memories found.";
2442
2539
  }
2443
2540
  // Vault index
@@ -2537,7 +2634,7 @@ async function handleToolCall(name, args, deps) {
2537
2634
  tasks: args.tasks,
2538
2635
  });
2539
2636
  // Build URL for viewing the plan
2540
- const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
2637
+ const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
2541
2638
  return {
2542
2639
  success: true,
2543
2640
  planId: result.plan.id,
@@ -3092,11 +3189,14 @@ export class HarmonyMCPServer {
3092
3189
  const transport = new StdioServerTransport();
3093
3190
  await this.server.connect(transport);
3094
3191
  console.error("Harmony MCP server running on stdio");
3095
- // Initialize auto-session tracking
3192
+ // Initialize auto-session tracking with MCP client identity detection
3096
3193
  const configDeps = createConfigDeps();
3097
3194
  initAutoSession(async (client, cardId, status) => {
3098
3195
  await runEndSessionPipeline(client, configDeps, cardId, status);
3099
- }, () => getClient());
3196
+ }, () => getClient(), () => {
3197
+ const cv = this.server.getClientVersion();
3198
+ return cv ? { name: cv.name, version: cv.version } : null;
3199
+ });
3100
3200
  // Graceful shutdown: end all auto-sessions
3101
3201
  const handleShutdown = async () => {
3102
3202
  try {
@@ -246,7 +246,7 @@ Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with
246
246
  ## [Plan Title]
247
247
  **Status:** draft/active/archived | **Phase:** plan/execute/verify/done
248
248
  **Tasks:** N total (X pending, Y in_progress, Z completed)
249
- **URL:** https://gethmy.com/plans/{id}
249
+ **URL:** https://app.gethmy.com/plans/{id}
250
250
  \\\`\\\`\\\`
251
251
 
252
252
  If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
@@ -280,7 +280,7 @@ Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
280
280
  #### Option A — Single card
281
281
  1. Call \`harmony_create_card\` with:
282
282
  - \`title\`: Plan title
283
- - \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
283
+ - \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://app.gethmy.com/plans/{planId})\`
284
284
  - \`priority\`: based on plan task priorities (use highest)
285
285
  2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
286
286
 
@@ -402,7 +402,7 @@ Call \`harmony_create_plan\` with:
402
402
  ### 2B.5 — User Approval
403
403
 
404
404
  Show the user:
405
- - Plan URL: \`https://gethmy.com/plans/{id}\`
405
+ - Plan URL: \`https://app.gethmy.com/plans/{id}\`
406
406
  - Number of tasks created
407
407
  - Brief summary
408
408
 
@@ -11,7 +11,7 @@ import { getWriteSummary, writeFilesWithProgress } from "./writer.js";
11
11
  // Central skills directory for global installation
12
12
  const GLOBAL_SKILLS_DIR = join(homedir(), ".agents", "skills");
13
13
  // API base URL
14
- const API_URL = "https://gethmy.com/api";
14
+ const API_URL = "https://app.gethmy.com/api";
15
15
  /**
16
16
  * Register MCP server using Claude CLI
17
17
  * Returns true if successful, false if CLI unavailable or failed
@@ -422,7 +422,7 @@ export async function runSetup(options = {}) {
422
422
  if (!validation.valid) {
423
423
  spinner.stop(colors.error("API key validation failed"));
424
424
  p.log.error(validation.error || "Could not connect to Harmony API");
425
- p.log.info("Get an API key at: https://gethmy.com/user/keys");
425
+ p.log.info("Get an API key at: https://app.gethmy.com/user/keys");
426
426
  process.exit(1);
427
427
  }
428
428
  if (!userEmail) {
@@ -765,6 +765,6 @@ export async function runSetup(options = {}) {
765
765
  }
766
766
  console.log("");
767
767
  console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
768
- console.log(` ${colors.dim("Need help? Visit https://gethmy.com/docs/mcp")}`);
768
+ console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
769
769
  console.log("");
770
770
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.1.3",
3
+ "version": "2.2.1",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -51,16 +51,16 @@ describe("auto-start", () => {
51
51
 
52
52
  expect(client.startAgentSession).toHaveBeenCalledTimes(1);
53
53
  expect(client.startAgentSession).toHaveBeenCalledWith(CARD_A, {
54
- agentIdentifier: "auto",
55
- agentName: "Auto-detected Agent",
54
+ agentIdentifier: "unknown",
55
+ agentName: "Unknown Agent",
56
56
  status: "working",
57
57
  });
58
58
  expect(getActiveSessions().size).toBe(1);
59
59
  const session = getActiveSessions().get(CARD_A);
60
60
  expect(session).toBeDefined();
61
61
  expect(session!.isExplicit).toBe(false);
62
- expect(session!.agentIdentifier).toBe("auto");
63
- expect(session!.agentName).toBe("Auto-detected Agent");
62
+ expect(session!.agentIdentifier).toBe("unknown");
63
+ expect(session!.agentName).toBe("Unknown Agent");
64
64
  });
65
65
 
66
66
  test("does NOT trigger on autoStart=false", async () => {
@@ -109,7 +109,7 @@ describe("auto-start", () => {
109
109
 
110
110
  // Should still be tracked despite API error
111
111
  expect(getActiveSessions().size).toBe(1);
112
- expect(getActiveSessions().get(CARD_A)!.agentIdentifier).toBe("auto");
112
+ expect(getActiveSessions().get(CARD_A)!.agentIdentifier).toBe("unknown");
113
113
  });
114
114
 
115
115
  test("uses clientGetter when no client in options", async () => {
@@ -328,8 +328,8 @@ describe("inactivity timeout", () => {
328
328
  startedAt: Date.now() - INACTIVITY_TIMEOUT_MS - 1000,
329
329
  lastActivityAt: Date.now() - INACTIVITY_TIMEOUT_MS - 1000,
330
330
  isExplicit: false,
331
- agentIdentifier: "auto",
332
- agentName: "Auto-detected Agent",
331
+ agentIdentifier: "unknown",
332
+ agentName: "Unknown Agent",
333
333
  });
334
334
 
335
335
  checkInactivity();
@@ -355,8 +355,8 @@ describe("inactivity timeout", () => {
355
355
  startedAt: Date.now(),
356
356
  lastActivityAt: Date.now(), // just now — well within timeout
357
357
  isExplicit: false,
358
- agentIdentifier: "auto",
359
- agentName: "Auto-detected Agent",
358
+ agentIdentifier: "unknown",
359
+ agentName: "Unknown Agent",
360
360
  });
361
361
 
362
362
  checkInactivity();
@@ -398,8 +398,8 @@ describe("inactivity timeout", () => {
398
398
  startedAt: Date.now() - INACTIVITY_TIMEOUT_MS - 5000,
399
399
  lastActivityAt: Date.now() - INACTIVITY_TIMEOUT_MS - 5000,
400
400
  isExplicit: false,
401
- agentIdentifier: "auto",
402
- agentName: "Auto-detected Agent",
401
+ agentIdentifier: "unknown",
402
+ agentName: "Unknown Agent",
403
403
  });
404
404
 
405
405
  // CARD_B — still active
@@ -408,8 +408,8 @@ describe("inactivity timeout", () => {
408
408
  startedAt: Date.now() - 1000,
409
409
  lastActivityAt: Date.now() - 1000,
410
410
  isExplicit: false,
411
- agentIdentifier: "auto",
412
- agentName: "Auto-detected Agent",
411
+ agentIdentifier: "unknown",
412
+ agentName: "Unknown Agent",
413
413
  });
414
414
 
415
415
  // CARD_C — timed out but explicit
@@ -442,8 +442,8 @@ describe("inactivity timeout", () => {
442
442
  startedAt: 0,
443
443
  lastActivityAt: 0,
444
444
  isExplicit: false,
445
- agentIdentifier: "auto",
446
- agentName: "Auto-detected Agent",
445
+ agentIdentifier: "unknown",
446
+ agentName: "Unknown Agent",
447
447
  });
448
448
 
449
449
  // Should not throw
@@ -464,8 +464,8 @@ describe("inactivity timeout", () => {
464
464
  startedAt: Date.now() - INACTIVITY_TIMEOUT_MS - 5000,
465
465
  lastActivityAt: Date.now() - INACTIVITY_TIMEOUT_MS - 5000,
466
466
  isExplicit: false,
467
- agentIdentifier: "auto",
468
- agentName: "Auto-detected Agent",
467
+ agentIdentifier: "unknown",
468
+ agentName: "Unknown Agent",
469
469
  });
470
470
 
471
471
  // Refresh activity
@@ -495,8 +495,8 @@ describe("markExplicit", () => {
495
495
 
496
496
  markExplicit(CARD_A);
497
497
  expect(getActiveSessions().get(CARD_A)!.isExplicit).toBe(true);
498
- // agentIdentifier should remain "auto" since it was auto-started
499
- expect(getActiveSessions().get(CARD_A)!.agentIdentifier).toBe("auto");
498
+ // agentIdentifier should remain "unknown" since no clientInfo was provided
499
+ expect(getActiveSessions().get(CARD_A)!.agentIdentifier).toBe("unknown");
500
500
  });
501
501
 
502
502
  test("creates new tracking entry for unknown card", () => {
@@ -583,8 +583,8 @@ describe("shutdown", () => {
583
583
  startedAt: Date.now(),
584
584
  lastActivityAt: Date.now(),
585
585
  isExplicit: false,
586
- agentIdentifier: "auto",
587
- agentName: "Auto-detected Agent",
586
+ agentIdentifier: "unknown",
587
+ agentName: "Unknown Agent",
588
588
  });
589
589
 
590
590
  await shutdownAllSessions();
@@ -606,8 +606,8 @@ describe("shutdown", () => {
606
606
  startedAt: Date.now(),
607
607
  lastActivityAt: Date.now(),
608
608
  isExplicit: false,
609
- agentIdentifier: "auto",
610
- agentName: "Auto-detected Agent",
609
+ agentIdentifier: "unknown",
610
+ agentName: "Unknown Agent",
611
611
  });
612
612
  getActiveSessions().set(CARD_B, {
613
613
  cardId: CARD_B,
@@ -633,8 +633,8 @@ describe("shutdown", () => {
633
633
  startedAt: Date.now(),
634
634
  lastActivityAt: Date.now(),
635
635
  isExplicit: false,
636
- agentIdentifier: "auto",
637
- agentName: "Auto-detected Agent",
636
+ agentIdentifier: "unknown",
637
+ agentName: "Unknown Agent",
638
638
  });
639
639
 
640
640
  // Should not throw
@@ -660,16 +660,16 @@ describe("shutdown", () => {
660
660
  startedAt: Date.now(),
661
661
  lastActivityAt: Date.now(),
662
662
  isExplicit: false,
663
- agentIdentifier: "auto",
664
- agentName: "Auto-detected Agent",
663
+ agentIdentifier: "unknown",
664
+ agentName: "Unknown Agent",
665
665
  });
666
666
  getActiveSessions().set(CARD_B, {
667
667
  cardId: CARD_B,
668
668
  startedAt: Date.now(),
669
669
  lastActivityAt: Date.now(),
670
670
  isExplicit: false,
671
- agentIdentifier: "auto",
672
- agentName: "Auto-detected Agent",
671
+ agentIdentifier: "unknown",
672
+ agentName: "Unknown Agent",
673
673
  });
674
674
 
675
675
  await shutdownAllSessions();
@@ -826,8 +826,8 @@ describe("edge cases", () => {
826
826
  startedAt: 0,
827
827
  lastActivityAt: 0,
828
828
  isExplicit: false,
829
- agentIdentifier: "auto",
830
- agentName: "Auto-detected Agent",
829
+ agentIdentifier: "unknown",
830
+ agentName: "Unknown Agent",
831
831
  });
832
832
 
833
833
  checkInactivity();
@@ -850,8 +850,8 @@ describe("edge cases", () => {
850
850
  startedAt: 0,
851
851
  lastActivityAt: 0,
852
852
  isExplicit: false,
853
- agentIdentifier: "auto",
854
- agentName: "Auto-detected Agent",
853
+ agentIdentifier: "unknown",
854
+ agentName: "Unknown Agent",
855
855
  });
856
856
 
857
857
  checkInactivity();