mobbdev 1.4.18 → 1.4.19

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.mjs +80 -9
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -19448,7 +19448,7 @@ function createLogger(config2) {
19448
19448
 
19449
19449
  // src/features/claude_code/hook_logger.ts
19450
19450
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
19451
- var CLI_VERSION = true ? "1.4.18" : "unknown";
19451
+ var CLI_VERSION = true ? "1.4.19" : "unknown";
19452
19452
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
19453
19453
  var claudeCodeVersion;
19454
19454
  function buildDdTags() {
@@ -19940,7 +19940,12 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19940
19940
  rawData: rawEntry,
19941
19941
  repositoryUrl: sampledRepoState.repositoryUrl ?? void 0,
19942
19942
  branch: sampledRepoState.branch,
19943
- commitSha: sampledRepoState.commitSha
19943
+ commitSha: sampledRepoState.commitSha,
19944
+ // T-510: sub-agent attribution stamped per record. Defaults preserve
19945
+ // main-session semantics so the upload schema's `isSubagent` defaults
19946
+ // to false on the wire for older clients / main-transcript files.
19947
+ isSubagent: input.isSubagent ?? false,
19948
+ agentType: input.agentType ?? null
19944
19949
  };
19945
19950
  });
19946
19951
  let totalRawDataBytes = 0;
@@ -19980,9 +19985,16 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19980
19985
  lastModel: lastSeenModel ?? void 0
19981
19986
  };
19982
19987
  sessionStore.set(cursorKey, cursor);
19988
+ const isSubagentBatch = input.isSubagent === true;
19983
19989
  log2.heartbeat("Upload ok", {
19984
19990
  entriesUploaded: entries.length,
19985
19991
  entriesSkipped: filteredOut,
19992
+ subagentEntriesUploaded: isSubagentBatch ? entries.length : 0,
19993
+ // Renamed for clarity: this counts rows uploaded with no agent_type
19994
+ // because the sibling .meta.json was missing/unreadable at scan
19995
+ // time. Should be ~0 in steady state; a non-zero rate is a signal
19996
+ // that Claude Code's meta convention may have changed.
19997
+ subagentEntriesUploadedMissingMeta: isSubagentBatch && input.agentType == null ? entries.length : 0,
19986
19998
  claudeCodeVersion: getClaudeCodeVersion()
19987
19999
  });
19988
20000
  return {
@@ -20224,7 +20236,7 @@ async function installMobbHooks(options = {}) {
20224
20236
  }
20225
20237
 
20226
20238
  // src/features/claude_code/transcript_scanner.ts
20227
- import { open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
20239
+ import { lstat as lstat2, open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
20228
20240
  import os7 from "os";
20229
20241
  import path23 from "path";
20230
20242
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -20238,11 +20250,59 @@ function getClaudeProjectsDirs() {
20238
20250
  dirs.push(path23.join(os7.homedir(), ".claude", "projects"));
20239
20251
  return dirs;
20240
20252
  }
20241
- async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
20253
+ var META_MAX_BYTES = 4096;
20254
+ var AGENT_TYPE_RE = /^[A-Za-z0-9_.\- ]+$/;
20255
+ var agentTypeCache = /* @__PURE__ */ new Map();
20256
+ var knownAbsentSubagentDirs = /* @__PURE__ */ new Set();
20257
+ async function readAgentType(jsonlPath, jsonlMtimeMs) {
20258
+ const cached = agentTypeCache.get(jsonlPath);
20259
+ if (cached && cached.mtimeMs === jsonlMtimeMs) {
20260
+ return cached.value;
20261
+ }
20262
+ const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
20263
+ let fh;
20264
+ let value = null;
20265
+ try {
20266
+ const lst = await lstat2(metaPath);
20267
+ if (!lst.isFile() || lst.size > META_MAX_BYTES) {
20268
+ agentTypeCache.set(jsonlPath, { mtimeMs: jsonlMtimeMs, value: null });
20269
+ return null;
20270
+ }
20271
+ fh = await open5(metaPath, "r");
20272
+ const buf = Buffer.alloc(Math.min(lst.size, META_MAX_BYTES));
20273
+ const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
20274
+ const raw = buf.toString("utf8", 0, bytesRead);
20275
+ const parsed = JSON.parse(raw);
20276
+ if (parsed && typeof parsed === "object") {
20277
+ const candidate = parsed.agentType;
20278
+ if (typeof candidate === "string" && candidate.length > 0 && candidate.length <= 128 && AGENT_TYPE_RE.test(candidate.trim())) {
20279
+ value = candidate.trim();
20280
+ }
20281
+ }
20282
+ } catch {
20283
+ } finally {
20284
+ if (fh) {
20285
+ await fh.close().catch(() => void 0);
20286
+ }
20287
+ }
20288
+ agentTypeCache.set(jsonlPath, { mtimeMs: jsonlMtimeMs, value });
20289
+ return value;
20290
+ }
20291
+ async function collectJsonlFiles(files, dir, projectDir, seen, now, results, override) {
20242
20292
  for (const file of files) {
20243
20293
  if (!file.endsWith(".jsonl")) continue;
20244
- const sessionId = file.replace(".jsonl", "");
20245
- if (!UUID_RE.test(sessionId)) continue;
20294
+ let sessionId;
20295
+ let isSubagent;
20296
+ if (override) {
20297
+ if (!/^agent-[A-Za-z0-9_-]+\.jsonl$/.test(file)) continue;
20298
+ sessionId = override.parentUuid;
20299
+ isSubagent = true;
20300
+ } else {
20301
+ const basename2 = file.replace(".jsonl", "");
20302
+ if (!UUID_RE.test(basename2)) continue;
20303
+ sessionId = basename2;
20304
+ isSubagent = false;
20305
+ }
20246
20306
  const filePath = path23.join(dir, file);
20247
20307
  if (seen.has(filePath)) continue;
20248
20308
  seen.add(filePath);
@@ -20253,12 +20313,15 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
20253
20313
  continue;
20254
20314
  }
20255
20315
  if (now - fileStat.mtimeMs > TRANSCRIPT_MAX_AGE_MS) continue;
20316
+ const agentType = isSubagent ? await readAgentType(filePath, fileStat.mtimeMs) : null;
20256
20317
  results.push({
20257
20318
  filePath,
20258
20319
  sessionId,
20259
20320
  projectDir,
20260
20321
  mtimeMs: fileStat.mtimeMs,
20261
- size: fileStat.size
20322
+ size: fileStat.size,
20323
+ isSubagent,
20324
+ agentType
20262
20325
  });
20263
20326
  }
20264
20327
  }
@@ -20292,6 +20355,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
20292
20355
  for (const entry of files) {
20293
20356
  if (!UUID_RE.test(entry)) continue;
20294
20357
  const subagentsDir = path23.join(projPath, entry, "subagents");
20358
+ if (knownAbsentSubagentDirs.has(subagentsDir)) continue;
20295
20359
  try {
20296
20360
  const s = await stat4(subagentsDir);
20297
20361
  if (!s.isDirectory()) continue;
@@ -20302,9 +20366,11 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
20302
20366
  projPath,
20303
20367
  seen,
20304
20368
  now,
20305
- results
20369
+ results,
20370
+ { parentUuid: entry }
20306
20371
  );
20307
20372
  } catch {
20373
+ knownAbsentSubagentDirs.add(subagentsDir);
20308
20374
  }
20309
20375
  }
20310
20376
  }
@@ -20535,7 +20601,12 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
20535
20601
  {
20536
20602
  session_id: transcript.sessionId,
20537
20603
  transcript_path: transcript.filePath,
20538
- cwd
20604
+ cwd,
20605
+ // T-510: forward sub-agent attribution from transcript_scanner.ts.
20606
+ // For main-session files these are false / null and behave
20607
+ // identically to the pre-T-510 wire.
20608
+ isSubagent: transcript.isSubagent,
20609
+ agentType: transcript.agentType
20539
20610
  },
20540
20611
  sessionStore,
20541
20612
  log2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.4.18",
3
+ "version": "1.4.19",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "git+https://github.com/mobb-dev/bugsy.git",
6
6
  "main": "dist/index.mjs",