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.
- package/dist/args/commands/upload_ai_blame.mjs +21 -1
- package/dist/index.mjs +101 -10
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
20245
|
-
|
|
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,
|