@devness/useai 0.5.4 → 0.5.5

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 (2) hide show
  1. package/dist/index.js +80 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1713,7 +1713,7 @@ function resolveClient(server2, session2) {
1713
1713
  }
1714
1714
  session2.setClient(detectClient());
1715
1715
  }
1716
- function registerTools(server2, session2) {
1716
+ function registerTools(server2, session2, opts) {
1717
1717
  server2.tool(
1718
1718
  "useai_start",
1719
1719
  `Start tracking an AI coding session. Call this at the beginning of every response. Generate a session title from the user's prompt: a generic public "title" (no project/file names) and a detailed "private_title" (can include specifics).`,
@@ -1723,6 +1723,9 @@ function registerTools(server2, session2) {
1723
1723
  private_title: z2.string().optional().describe('Detailed session title for private records. Can include project names and specifics. Example: "Fix JWT refresh in UseAI login flow"')
1724
1724
  },
1725
1725
  async ({ task_type, title, private_title }) => {
1726
+ if (session2.sessionRecordCount > 0 && opts?.sealBeforeReset) {
1727
+ opts.sealBeforeReset();
1728
+ }
1726
1729
  session2.reset();
1727
1730
  resolveClient(server2, session2);
1728
1731
  session2.setTaskType(task_type ?? "coding");
@@ -1863,7 +1866,7 @@ function registerTools(server2, session2) {
1863
1866
  chain_end_hash: endRecord.hash,
1864
1867
  seal_signature: sealSignature
1865
1868
  };
1866
- const sessions2 = getSessions();
1869
+ const sessions2 = getSessions().filter((s) => s.session_id !== seal.session_id);
1867
1870
  sessions2.push(seal);
1868
1871
  writeJson(SESSIONS_FILE, sessions2);
1869
1872
  let milestoneCount = 0;
@@ -2250,6 +2253,9 @@ function sealOrphanFile(sessionId) {
2250
2253
  const client = startData["client"] ?? "unknown";
2251
2254
  const taskType = startData["task_type"] ?? "coding";
2252
2255
  const startTime = firstRecord.timestamp;
2256
+ const orphanTitle = startData["title"] ?? void 0;
2257
+ const orphanPrivateTitle = startData["private_title"] ?? void 0;
2258
+ const orphanProject = startData["project"] ?? void 0;
2253
2259
  let heartbeatCount = 0;
2254
2260
  for (const line of lines) {
2255
2261
  try {
@@ -2297,30 +2303,29 @@ function sealOrphanFile(sessionId) {
2297
2303
  renameSync3(filePath, join7(SEALED_DIR, `${sessionId}.jsonl`));
2298
2304
  } catch {
2299
2305
  }
2300
- if (!isSessionInIndex(sessionId)) {
2301
- const convId = startData["conversation_id"] ?? void 0;
2302
- const convIdx = startData["conversation_index"] ?? void 0;
2303
- const seal = {
2304
- session_id: sessionId,
2305
- conversation_id: convId,
2306
- conversation_index: convIdx,
2307
- client,
2308
- task_type: taskType,
2309
- languages: [],
2310
- files_touched: 0,
2311
- started_at: startTime,
2312
- ended_at: now,
2313
- duration_seconds: duration,
2314
- heartbeat_count: heartbeatCount,
2315
- record_count: lines.length + 2,
2316
- chain_start_hash: firstRecord.prev_hash,
2317
- chain_end_hash: endRecord.hash,
2318
- seal_signature: sealSignature
2319
- };
2320
- const allSessions = readJson(SESSIONS_FILE, []);
2321
- allSessions.push(seal);
2322
- writeJson(SESSIONS_FILE, allSessions);
2323
- }
2306
+ const convId = startData["conversation_id"] ?? void 0;
2307
+ const convIdx = startData["conversation_index"] ?? void 0;
2308
+ const seal = {
2309
+ session_id: sessionId,
2310
+ conversation_id: convId,
2311
+ conversation_index: convIdx,
2312
+ client,
2313
+ task_type: taskType,
2314
+ languages: [],
2315
+ files_touched: 0,
2316
+ project: orphanProject,
2317
+ title: orphanTitle,
2318
+ private_title: orphanPrivateTitle,
2319
+ started_at: startTime,
2320
+ ended_at: now,
2321
+ duration_seconds: duration,
2322
+ heartbeat_count: heartbeatCount,
2323
+ record_count: lines.length + 2,
2324
+ chain_start_hash: firstRecord.prev_hash,
2325
+ chain_end_hash: endRecord.hash,
2326
+ seal_signature: sealSignature
2327
+ };
2328
+ upsertSessionSeal(seal);
2324
2329
  } catch {
2325
2330
  }
2326
2331
  }
@@ -2346,10 +2351,6 @@ function isSessionAlreadySealed(session2) {
2346
2351
  const activePath = join7(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
2347
2352
  return !existsSync8(activePath);
2348
2353
  }
2349
- function isSessionInIndex(sessionId) {
2350
- const allSessions = readJson(SESSIONS_FILE, []);
2351
- return allSessions.some((s) => s.session_id === sessionId);
2352
- }
2353
2354
  function sealRichness(s) {
2354
2355
  let score = 0;
2355
2356
  if (s.title) score += 10;
@@ -2377,6 +2378,18 @@ function deduplicateSessionsIndex() {
2377
2378
  writeJson(SESSIONS_FILE, deduped);
2378
2379
  }
2379
2380
  }
2381
+ function upsertSessionSeal(seal) {
2382
+ const allSessions = readJson(SESSIONS_FILE, []);
2383
+ const existingIdx = allSessions.findIndex((s) => s.session_id === seal.session_id);
2384
+ if (existingIdx >= 0) {
2385
+ if (sealRichness(seal) >= sealRichness(allSessions[existingIdx])) {
2386
+ allSessions[existingIdx] = seal;
2387
+ }
2388
+ } else {
2389
+ allSessions.push(seal);
2390
+ }
2391
+ writeJson(SESSIONS_FILE, allSessions);
2392
+ }
2380
2393
  function autoSealSession(active) {
2381
2394
  const { session: session2 } = active;
2382
2395
  if (session2.sessionRecordCount === 0) return;
@@ -2399,6 +2412,9 @@ function autoSealSession(active) {
2399
2412
  task_type: session2.sessionTaskType,
2400
2413
  languages: [],
2401
2414
  files_touched: 0,
2415
+ project: session2.project ?? void 0,
2416
+ title: session2.sessionTitle ?? void 0,
2417
+ private_title: session2.sessionPrivateTitle ?? void 0,
2402
2418
  started_at: new Date(session2.sessionStartTime).toISOString(),
2403
2419
  ended_at: now,
2404
2420
  duration_seconds: duration,
@@ -2423,29 +2439,28 @@ function autoSealSession(active) {
2423
2439
  }
2424
2440
  } catch {
2425
2441
  }
2426
- if (!isSessionInIndex(session2.sessionId)) {
2427
- const chainStartHash = session2.chainTipHash === "GENESIS" ? "GENESIS" : session2.chainTipHash;
2428
- const seal = {
2429
- session_id: session2.sessionId,
2430
- conversation_id: session2.conversationId,
2431
- conversation_index: session2.conversationIndex,
2432
- client: session2.clientName,
2433
- task_type: session2.sessionTaskType,
2434
- languages: [],
2435
- files_touched: 0,
2436
- started_at: new Date(session2.sessionStartTime).toISOString(),
2437
- ended_at: now,
2438
- duration_seconds: duration,
2439
- heartbeat_count: session2.heartbeatCount,
2440
- record_count: session2.sessionRecordCount,
2441
- chain_start_hash: chainStartHash,
2442
- chain_end_hash: endRecord.hash,
2443
- seal_signature: sealSignature
2444
- };
2445
- const allSessions = readJson(SESSIONS_FILE, []);
2446
- allSessions.push(seal);
2447
- writeJson(SESSIONS_FILE, allSessions);
2448
- }
2442
+ const chainStartHash = session2.chainTipHash === "GENESIS" ? "GENESIS" : session2.chainTipHash;
2443
+ const seal = {
2444
+ session_id: session2.sessionId,
2445
+ conversation_id: session2.conversationId,
2446
+ conversation_index: session2.conversationIndex,
2447
+ client: session2.clientName,
2448
+ task_type: session2.sessionTaskType,
2449
+ languages: [],
2450
+ files_touched: 0,
2451
+ project: session2.project ?? void 0,
2452
+ title: session2.sessionTitle ?? void 0,
2453
+ private_title: session2.sessionPrivateTitle ?? void 0,
2454
+ started_at: new Date(session2.sessionStartTime).toISOString(),
2455
+ ended_at: now,
2456
+ duration_seconds: duration,
2457
+ heartbeat_count: session2.heartbeatCount,
2458
+ record_count: session2.sessionRecordCount,
2459
+ chain_start_hash: chainStartHash,
2460
+ chain_end_hash: endRecord.hash,
2461
+ seal_signature: sealSignature
2462
+ };
2463
+ upsertSessionSeal(seal);
2449
2464
  }
2450
2465
  function sealSessionData(active) {
2451
2466
  autoSealSession(active);
@@ -2613,7 +2628,18 @@ async function startDaemon(port) {
2613
2628
  name: "UseAI",
2614
2629
  version: VERSION
2615
2630
  });
2616
- registerTools(mcpServer, sessionState);
2631
+ registerTools(mcpServer, sessionState, {
2632
+ sealBeforeReset: () => {
2633
+ for (const [, active] of sessions) {
2634
+ if (active.session === sessionState) {
2635
+ if (active.session.sessionRecordCount > 0 && !isSessionAlreadySealed(active.session)) {
2636
+ autoSealSession(active);
2637
+ }
2638
+ break;
2639
+ }
2640
+ }
2641
+ }
2642
+ });
2617
2643
  const transport = new StreamableHTTPServerTransport({
2618
2644
  sessionIdGenerator: () => randomUUID4(),
2619
2645
  onsessioninitialized: (newSid) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devness/useai",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Track your AI-assisted development workflow. MCP server that records usage metrics across all your AI tools.",
5
5
  "keywords": [
6
6
  "mcp",