mobbdev 1.4.18 → 1.4.20

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.
@@ -291,6 +291,7 @@ var init_client_generates = __esm({
291
291
  IssueType_Enum2["InsufficientLogging"] = "INSUFFICIENT_LOGGING";
292
292
  IssueType_Enum2["J2EeGetConnection"] = "J2EE_GET_CONNECTION";
293
293
  IssueType_Enum2["JqueryDeprecatedSymbols"] = "JQUERY_DEPRECATED_SYMBOLS";
294
+ IssueType_Enum2["JwtDecodeWithoutVerify"] = "JWT_DECODE_WITHOUT_VERIFY";
294
295
  IssueType_Enum2["LeftoverDebugCode"] = "LEFTOVER_DEBUG_CODE";
295
296
  IssueType_Enum2["LocaleDependentComparison"] = "LOCALE_DEPENDENT_COMPARISON";
296
297
  IssueType_Enum2["LogForging"] = "LOG_FORGING";
@@ -353,6 +354,7 @@ var init_client_generates = __esm({
353
354
  IssueType_Enum2["UnencryptedAwsSqsQueue"] = "UNENCRYPTED_AWS_SQS_QUEUE";
354
355
  IssueType_Enum2["UnnecessaryImports"] = "UNNECESSARY_IMPORTS";
355
356
  IssueType_Enum2["UnsafeDeserialization"] = "UNSAFE_DESERIALIZATION";
357
+ IssueType_Enum2["UnsafeReflection"] = "UNSAFE_REFLECTION";
356
358
  IssueType_Enum2["UnsafeTargetBlank"] = "UNSAFE_TARGET_BLANK";
357
359
  IssueType_Enum2["UnsafeWebThread"] = "UNSAFE_WEB_THREAD";
358
360
  IssueType_Enum2["UnvalidatedPublicMethodArgument"] = "UNVALIDATED_PUBLIC_METHOD_ARGUMENT";
@@ -1778,6 +1780,7 @@ var init_getIssueType = __esm({
1778
1780
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: "Trust Boundary Violation",
1779
1781
  ["NULL_DEREFERENCE" /* NullDereference */]: "Null Dereference",
1780
1782
  ["UNSAFE_DESERIALIZATION" /* UnsafeDeserialization */]: "Unsafe deserialization",
1783
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: "Unsafe Reflection",
1781
1784
  ["INSECURE_BINDER_CONFIGURATION" /* InsecureBinderConfiguration */]: "Insecure Binder Configuration",
1782
1785
  ["UNSAFE_TARGET_BLANK" /* UnsafeTargetBlank */]: "Unsafe use of target blank",
1783
1786
  ["IFRAME_WITHOUT_SANDBOX" /* IframeWithoutSandbox */]: "Client use of iframe without sandbox",
@@ -1892,7 +1895,8 @@ var init_getIssueType = __esm({
1892
1895
  ["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: "Often Misused: Boolean.getBoolean()",
1893
1896
  ["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: "AWS SQS Queue Unencrypted",
1894
1897
  ["INSECURE_DESERIALIZATION" /* InsecureDeserialization */]: "Insecure Deserialization",
1895
- ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled"
1898
+ ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled",
1899
+ ["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification"
1896
1900
  };
1897
1901
  issueTypeZ = z5.nativeEnum(IssueType_Enum);
1898
1902
  getIssueTypeFriendlyString = (issueType) => {
@@ -2049,6 +2053,7 @@ var init_types = __esm({
2049
2053
  expirationOn: z7.string().nullable(),
2050
2054
  state: z7.nativeEnum(Fix_Report_State_Enum),
2051
2055
  failReason: z7.string().nullable(),
2056
+ candidateToRerun: z7.boolean(),
2052
2057
  fixes: z7.array(
2053
2058
  z7.object({
2054
2059
  id: z7.string().uuid(),
@@ -5134,6 +5139,7 @@ var fixDetailsData = {
5134
5139
  ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
5135
5140
  ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0,
5136
5141
  ["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: void 0,
5142
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: void 0,
5137
5143
  ["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: {
5138
5144
  issueDescription: "AWS SQS queue contents are unencrypted; data could be read if the queue is compromised.",
5139
5145
  fixInstructions: "Enable server-side encryption by setting sqs_managed_sse_enabled = true, or supply a KMS key via kms_master_key_id."
@@ -5142,6 +5148,10 @@ var fixDetailsData = {
5142
5148
  ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: {
5143
5149
  issueDescription: "AWS DynamoDB table has point-in-time recovery disabled; accidental or malicious writes/deletes cannot be rolled back from a known-good snapshot.",
5144
5150
  fixInstructions: "Enable point-in-time recovery by adding `point_in_time_recovery { enabled = true }` to the aws_dynamodb_table resource."
5151
+ },
5152
+ ["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: {
5153
+ issueDescription: "Decoding a JWT with `JWT.decode()` only base64-decodes the token without checking its signature, so an attacker can forge a token with arbitrary claims (identity, roles, expiration) and have it trusted. CWE-345, OWASP A08:2021 Software and Data Integrity Failures.",
5154
+ fixInstructions: "Verify the signature before trusting any claims: build a verifier with the expected algorithm and secret/key (e.g. `JWT.require(Algorithm.HMAC256(secret)).build().verify(token)`) instead of calling `JWT.decode(token)`. After merging, confirm the verifier is configured with the same algorithm and secret/key used to sign your tokens \u2014 an incorrect or placeholder secret will make verification throw `JWTVerificationException` at runtime and reject legitimate tokens."
5145
5155
  }
5146
5156
  };
5147
5157
 
@@ -6172,6 +6182,15 @@ var uncheckedLoopCondition = {
6172
6182
  }
6173
6183
  };
6174
6184
 
6185
+ // src/features/analysis/scm/shared/src/storedQuestionData/java/unsafeReflection.ts
6186
+ var unsafeReflection = {
6187
+ classAllowlist: {
6188
+ content: () => "Allowed class names for reflection",
6189
+ description: () => `Provide a comma-separated list of fully-qualified class names that are permitted to be loaded via reflection (e.g. \`com.example.MyClass\`). Any other class name will be rejected at runtime.`,
6190
+ guidance: () => ""
6191
+ }
6192
+ };
6193
+
6175
6194
  // src/features/analysis/scm/shared/src/storedQuestionData/java/useOfSystemOutputStream.ts
6176
6195
  var useOfSystemOutputStream2 = {
6177
6196
  noLoggerAction: {
@@ -6257,6 +6276,7 @@ var vulnerabilities13 = {
6257
6276
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition,
6258
6277
  ["INSECURE_COOKIE" /* InsecureCookie */]: insecureCookie2,
6259
6278
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: trustBoundaryViolation2,
6279
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: unsafeReflection,
6260
6280
  ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection2,
6261
6281
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: leftoverDebugCode,
6262
6282
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: erroneousStringCompare,
package/dist/index.mjs CHANGED
@@ -291,6 +291,7 @@ var init_client_generates = __esm({
291
291
  IssueType_Enum2["InsufficientLogging"] = "INSUFFICIENT_LOGGING";
292
292
  IssueType_Enum2["J2EeGetConnection"] = "J2EE_GET_CONNECTION";
293
293
  IssueType_Enum2["JqueryDeprecatedSymbols"] = "JQUERY_DEPRECATED_SYMBOLS";
294
+ IssueType_Enum2["JwtDecodeWithoutVerify"] = "JWT_DECODE_WITHOUT_VERIFY";
294
295
  IssueType_Enum2["LeftoverDebugCode"] = "LEFTOVER_DEBUG_CODE";
295
296
  IssueType_Enum2["LocaleDependentComparison"] = "LOCALE_DEPENDENT_COMPARISON";
296
297
  IssueType_Enum2["LogForging"] = "LOG_FORGING";
@@ -353,6 +354,7 @@ var init_client_generates = __esm({
353
354
  IssueType_Enum2["UnencryptedAwsSqsQueue"] = "UNENCRYPTED_AWS_SQS_QUEUE";
354
355
  IssueType_Enum2["UnnecessaryImports"] = "UNNECESSARY_IMPORTS";
355
356
  IssueType_Enum2["UnsafeDeserialization"] = "UNSAFE_DESERIALIZATION";
357
+ IssueType_Enum2["UnsafeReflection"] = "UNSAFE_REFLECTION";
356
358
  IssueType_Enum2["UnsafeTargetBlank"] = "UNSAFE_TARGET_BLANK";
357
359
  IssueType_Enum2["UnsafeWebThread"] = "UNSAFE_WEB_THREAD";
358
360
  IssueType_Enum2["UnvalidatedPublicMethodArgument"] = "UNVALIDATED_PUBLIC_METHOD_ARGUMENT";
@@ -1444,6 +1446,7 @@ var init_getIssueType = __esm({
1444
1446
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: "Trust Boundary Violation",
1445
1447
  ["NULL_DEREFERENCE" /* NullDereference */]: "Null Dereference",
1446
1448
  ["UNSAFE_DESERIALIZATION" /* UnsafeDeserialization */]: "Unsafe deserialization",
1449
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: "Unsafe Reflection",
1447
1450
  ["INSECURE_BINDER_CONFIGURATION" /* InsecureBinderConfiguration */]: "Insecure Binder Configuration",
1448
1451
  ["UNSAFE_TARGET_BLANK" /* UnsafeTargetBlank */]: "Unsafe use of target blank",
1449
1452
  ["IFRAME_WITHOUT_SANDBOX" /* IframeWithoutSandbox */]: "Client use of iframe without sandbox",
@@ -1558,7 +1561,8 @@ var init_getIssueType = __esm({
1558
1561
  ["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: "Often Misused: Boolean.getBoolean()",
1559
1562
  ["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: "AWS SQS Queue Unencrypted",
1560
1563
  ["INSECURE_DESERIALIZATION" /* InsecureDeserialization */]: "Insecure Deserialization",
1561
- ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled"
1564
+ ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: "AWS DynamoDB Point-in-Time Recovery Disabled",
1565
+ ["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: "JWT Decoded Without Signature Verification"
1562
1566
  };
1563
1567
  issueTypeZ = z.nativeEnum(IssueType_Enum);
1564
1568
  getIssueTypeFriendlyString = (issueType) => {
@@ -2094,6 +2098,7 @@ var init_types = __esm({
2094
2098
  expirationOn: z11.string().nullable(),
2095
2099
  state: z11.nativeEnum(Fix_Report_State_Enum),
2096
2100
  failReason: z11.string().nullable(),
2101
+ candidateToRerun: z11.boolean(),
2097
2102
  fixes: z11.array(
2098
2103
  z11.object({
2099
2104
  id: z11.string().uuid(),
@@ -4838,6 +4843,7 @@ var fixDetailsData = {
4838
4843
  ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
4839
4844
  ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0,
4840
4845
  ["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: void 0,
4846
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: void 0,
4841
4847
  ["UNENCRYPTED_AWS_SQS_QUEUE" /* UnencryptedAwsSqsQueue */]: {
4842
4848
  issueDescription: "AWS SQS queue contents are unencrypted; data could be read if the queue is compromised.",
4843
4849
  fixInstructions: "Enable server-side encryption by setting sqs_managed_sse_enabled = true, or supply a KMS key via kms_master_key_id."
@@ -4846,6 +4852,10 @@ var fixDetailsData = {
4846
4852
  ["AWS_DYNAMODB_POINT_IN_TIME_RECOVERY_DISABLED" /* AwsDynamodbPointInTimeRecoveryDisabled */]: {
4847
4853
  issueDescription: "AWS DynamoDB table has point-in-time recovery disabled; accidental or malicious writes/deletes cannot be rolled back from a known-good snapshot.",
4848
4854
  fixInstructions: "Enable point-in-time recovery by adding `point_in_time_recovery { enabled = true }` to the aws_dynamodb_table resource."
4855
+ },
4856
+ ["JWT_DECODE_WITHOUT_VERIFY" /* JwtDecodeWithoutVerify */]: {
4857
+ issueDescription: "Decoding a JWT with `JWT.decode()` only base64-decodes the token without checking its signature, so an attacker can forge a token with arbitrary claims (identity, roles, expiration) and have it trusted. CWE-345, OWASP A08:2021 Software and Data Integrity Failures.",
4858
+ fixInstructions: "Verify the signature before trusting any claims: build a verifier with the expected algorithm and secret/key (e.g. `JWT.require(Algorithm.HMAC256(secret)).build().verify(token)`) instead of calling `JWT.decode(token)`. After merging, confirm the verifier is configured with the same algorithm and secret/key used to sign your tokens \u2014 an incorrect or placeholder secret will make verification throw `JWTVerificationException` at runtime and reject legitimate tokens."
4849
4859
  }
4850
4860
  };
4851
4861
 
@@ -5964,6 +5974,15 @@ var uncheckedLoopCondition = {
5964
5974
  }
5965
5975
  };
5966
5976
 
5977
+ // src/features/analysis/scm/shared/src/storedQuestionData/java/unsafeReflection.ts
5978
+ var unsafeReflection = {
5979
+ classAllowlist: {
5980
+ content: () => "Allowed class names for reflection",
5981
+ description: () => `Provide a comma-separated list of fully-qualified class names that are permitted to be loaded via reflection (e.g. \`com.example.MyClass\`). Any other class name will be rejected at runtime.`,
5982
+ guidance: () => ""
5983
+ }
5984
+ };
5985
+
5967
5986
  // src/features/analysis/scm/shared/src/storedQuestionData/java/useOfSystemOutputStream.ts
5968
5987
  var useOfSystemOutputStream2 = {
5969
5988
  noLoggerAction: {
@@ -6049,6 +6068,7 @@ var vulnerabilities13 = {
6049
6068
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition,
6050
6069
  ["INSECURE_COOKIE" /* InsecureCookie */]: insecureCookie2,
6051
6070
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: trustBoundaryViolation2,
6071
+ ["UNSAFE_REFLECTION" /* UnsafeReflection */]: unsafeReflection,
6052
6072
  ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection2,
6053
6073
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: leftoverDebugCode,
6054
6074
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: erroneousStringCompare,
@@ -19448,7 +19468,7 @@ function createLogger(config2) {
19448
19468
 
19449
19469
  // src/features/claude_code/hook_logger.ts
19450
19470
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
19451
- var CLI_VERSION = true ? "1.4.18" : "unknown";
19471
+ var CLI_VERSION = true ? "1.4.20" : "unknown";
19452
19472
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
19453
19473
  var claudeCodeVersion;
19454
19474
  function buildDdTags() {
@@ -19940,7 +19960,12 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19940
19960
  rawData: rawEntry,
19941
19961
  repositoryUrl: sampledRepoState.repositoryUrl ?? void 0,
19942
19962
  branch: sampledRepoState.branch,
19943
- commitSha: sampledRepoState.commitSha
19963
+ commitSha: sampledRepoState.commitSha,
19964
+ // T-510: sub-agent attribution stamped per record. Defaults preserve
19965
+ // main-session semantics so the upload schema's `isSubagent` defaults
19966
+ // to false on the wire for older clients / main-transcript files.
19967
+ isSubagent: input.isSubagent ?? false,
19968
+ agentType: input.agentType ?? null
19944
19969
  };
19945
19970
  });
19946
19971
  let totalRawDataBytes = 0;
@@ -19980,9 +20005,16 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19980
20005
  lastModel: lastSeenModel ?? void 0
19981
20006
  };
19982
20007
  sessionStore.set(cursorKey, cursor);
20008
+ const isSubagentBatch = input.isSubagent === true;
19983
20009
  log2.heartbeat("Upload ok", {
19984
20010
  entriesUploaded: entries.length,
19985
20011
  entriesSkipped: filteredOut,
20012
+ subagentEntriesUploaded: isSubagentBatch ? entries.length : 0,
20013
+ // Renamed for clarity: this counts rows uploaded with no agent_type
20014
+ // because the sibling .meta.json was missing/unreadable at scan
20015
+ // time. Should be ~0 in steady state; a non-zero rate is a signal
20016
+ // that Claude Code's meta convention may have changed.
20017
+ subagentEntriesUploadedMissingMeta: isSubagentBatch && input.agentType == null ? entries.length : 0,
19986
20018
  claudeCodeVersion: getClaudeCodeVersion()
19987
20019
  });
19988
20020
  return {
@@ -20224,7 +20256,7 @@ async function installMobbHooks(options = {}) {
20224
20256
  }
20225
20257
 
20226
20258
  // src/features/claude_code/transcript_scanner.ts
20227
- import { open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
20259
+ import { lstat as lstat2, open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
20228
20260
  import os7 from "os";
20229
20261
  import path23 from "path";
20230
20262
  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 +20270,59 @@ function getClaudeProjectsDirs() {
20238
20270
  dirs.push(path23.join(os7.homedir(), ".claude", "projects"));
20239
20271
  return dirs;
20240
20272
  }
20241
- async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
20273
+ var META_MAX_BYTES = 4096;
20274
+ var AGENT_TYPE_RE = /^[A-Za-z0-9_.\- ]+$/;
20275
+ var agentTypeCache = /* @__PURE__ */ new Map();
20276
+ var knownAbsentSubagentDirs = /* @__PURE__ */ new Set();
20277
+ async function readAgentType(jsonlPath, jsonlMtimeMs) {
20278
+ const cached = agentTypeCache.get(jsonlPath);
20279
+ if (cached && cached.mtimeMs === jsonlMtimeMs) {
20280
+ return cached.value;
20281
+ }
20282
+ const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
20283
+ let fh;
20284
+ let value = null;
20285
+ try {
20286
+ const lst = await lstat2(metaPath);
20287
+ if (!lst.isFile() || lst.size > META_MAX_BYTES) {
20288
+ agentTypeCache.set(jsonlPath, { mtimeMs: jsonlMtimeMs, value: null });
20289
+ return null;
20290
+ }
20291
+ fh = await open5(metaPath, "r");
20292
+ const buf = Buffer.alloc(Math.min(lst.size, META_MAX_BYTES));
20293
+ const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
20294
+ const raw = buf.toString("utf8", 0, bytesRead);
20295
+ const parsed = JSON.parse(raw);
20296
+ if (parsed && typeof parsed === "object") {
20297
+ const candidate = parsed.agentType;
20298
+ if (typeof candidate === "string" && candidate.length > 0 && candidate.length <= 128 && AGENT_TYPE_RE.test(candidate.trim())) {
20299
+ value = candidate.trim();
20300
+ }
20301
+ }
20302
+ } catch {
20303
+ } finally {
20304
+ if (fh) {
20305
+ await fh.close().catch(() => void 0);
20306
+ }
20307
+ }
20308
+ agentTypeCache.set(jsonlPath, { mtimeMs: jsonlMtimeMs, value });
20309
+ return value;
20310
+ }
20311
+ async function collectJsonlFiles(files, dir, projectDir, seen, now, results, override) {
20242
20312
  for (const file of files) {
20243
20313
  if (!file.endsWith(".jsonl")) continue;
20244
- const sessionId = file.replace(".jsonl", "");
20245
- if (!UUID_RE.test(sessionId)) continue;
20314
+ let sessionId;
20315
+ let isSubagent;
20316
+ if (override) {
20317
+ if (!/^agent-[A-Za-z0-9_-]+\.jsonl$/.test(file)) continue;
20318
+ sessionId = override.parentUuid;
20319
+ isSubagent = true;
20320
+ } else {
20321
+ const basename2 = file.replace(".jsonl", "");
20322
+ if (!UUID_RE.test(basename2)) continue;
20323
+ sessionId = basename2;
20324
+ isSubagent = false;
20325
+ }
20246
20326
  const filePath = path23.join(dir, file);
20247
20327
  if (seen.has(filePath)) continue;
20248
20328
  seen.add(filePath);
@@ -20253,12 +20333,15 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
20253
20333
  continue;
20254
20334
  }
20255
20335
  if (now - fileStat.mtimeMs > TRANSCRIPT_MAX_AGE_MS) continue;
20336
+ const agentType = isSubagent ? await readAgentType(filePath, fileStat.mtimeMs) : null;
20256
20337
  results.push({
20257
20338
  filePath,
20258
20339
  sessionId,
20259
20340
  projectDir,
20260
20341
  mtimeMs: fileStat.mtimeMs,
20261
- size: fileStat.size
20342
+ size: fileStat.size,
20343
+ isSubagent,
20344
+ agentType
20262
20345
  });
20263
20346
  }
20264
20347
  }
@@ -20292,6 +20375,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
20292
20375
  for (const entry of files) {
20293
20376
  if (!UUID_RE.test(entry)) continue;
20294
20377
  const subagentsDir = path23.join(projPath, entry, "subagents");
20378
+ if (knownAbsentSubagentDirs.has(subagentsDir)) continue;
20295
20379
  try {
20296
20380
  const s = await stat4(subagentsDir);
20297
20381
  if (!s.isDirectory()) continue;
@@ -20302,9 +20386,11 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
20302
20386
  projPath,
20303
20387
  seen,
20304
20388
  now,
20305
- results
20389
+ results,
20390
+ { parentUuid: entry }
20306
20391
  );
20307
20392
  } catch {
20393
+ knownAbsentSubagentDirs.add(subagentsDir);
20308
20394
  }
20309
20395
  }
20310
20396
  }
@@ -20535,7 +20621,12 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
20535
20621
  {
20536
20622
  session_id: transcript.sessionId,
20537
20623
  transcript_path: transcript.filePath,
20538
- cwd
20624
+ cwd,
20625
+ // T-510: forward sub-agent attribution from transcript_scanner.ts.
20626
+ // For main-session files these are false / null and behave
20627
+ // identically to the pre-T-510 wire.
20628
+ isSubagent: transcript.isSubagent,
20629
+ agentType: transcript.agentType
20539
20630
  },
20540
20631
  sessionStore,
20541
20632
  log2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.4.18",
3
+ "version": "1.4.20",
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",