@akasecurity/ai-tc-claude-code 0.8.2 → 0.9.0

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.
@@ -538,6 +538,10 @@ var SQLITE_MIGRATIONS = [
538
538
  {
539
539
  tag: "0009_findings_path_expression_index",
540
540
  sql: "-- Custom migration: partial expression index for the resolver's per-path reads.\n--\n-- openAtRestKeysForPath / resolvedAtRestKeysForPath (and the scanner's tier-3\n-- open-key probe) filter at-rest events by\n-- e.kind = 'code_change' AND json_extract(e.metadata, '$.filePath') = :path\n-- once per changed/deleted file on every scan \u2014 previously a full events scan\n-- per file. json_extract is deterministic, so SQLite allows it in an index;\n-- the WHERE kind = 'code_change' keeps the index to exactly the rows those\n-- queries can match (in-flight events are never path-addressed).\nCREATE INDEX `idx_events_code_change_path` ON `events` (json_extract(`metadata`, '$.filePath')) WHERE `kind` = 'code_change';\n"
541
+ },
542
+ {
543
+ tag: "0010_events_session_expression_index",
544
+ sql: "-- Custom migration: partial expression index for session-scoped finding reads.\n--\n-- sessionFindingsCount, the session-scoped listGroupedFindings paths, and the\n-- insert-time session dedup all filter live-capture events by\n-- json_extract(e.metadata, '$.sessionId') = :sessionId\n-- \u2014 previously a full findings-join scan with a JSON parse per row. The\n-- IS NOT NULL predicate keeps the index to session-stamped events only (an\n-- equality probe implies non-null, so SQLite still uses it).\nCREATE INDEX `idx_events_session_id` ON `events` (json_extract(`metadata`, '$.sessionId')) WHERE json_extract(`metadata`, '$.sessionId') IS NOT NULL;\n"
541
545
  }
542
546
  ];
543
547
 
@@ -640,6 +644,12 @@ var defaultCostModel = {
640
644
  }
641
645
  };
642
646
 
647
+ // ../../packages/schema/src/token/format.ts
648
+ var COMPACT = new Intl.NumberFormat("en-US", {
649
+ notation: "compact",
650
+ maximumFractionDigits: 1
651
+ });
652
+
643
653
  // ../../packages/schema/src/token/token-report.ts
644
654
  var num = (value) => value ?? 0;
645
655
  function costUsageOf(a) {
@@ -15311,6 +15321,12 @@ var FindingInstance = external_exports.object({
15311
15321
  provider: FindingProvider,
15312
15322
  repo: external_exports.string(),
15313
15323
  file: external_exports.string(),
15324
+ // Host tool that produced the scanned text (event metadata's toolName).
15325
+ // Present whenever the capturing hook recorded one — including
15326
+ // file-attributed captures (views prefer `file`); its display value is
15327
+ // the location fallback ("via Bash") when no filePath exists. Absent for
15328
+ // legacy rows and non-tool captures (prompts, worktree scans).
15329
+ toolName: external_exports.string().optional(),
15314
15330
  // Effective action: override.action ?? actionTaken, translated to FindingAction.
15315
15331
  action: FindingAction,
15316
15332
  detectedAt: external_exports.iso.datetime(),
@@ -15367,6 +15383,9 @@ var ListGroupedFindingsQuery = external_exports.object({
15367
15383
  provider: external_exports.array(FindingProvider).optional(),
15368
15384
  action: external_exports.array(FindingAction).optional(),
15369
15385
  q: external_exports.string().optional(),
15386
+ // Scope to findings whose event carries this session id (the Activity page's
15387
+ // session → findings drilldown). Findings without a session never match.
15388
+ sessionId: external_exports.string().optional(),
15370
15389
  groupBy: external_exports.literal("type").optional(),
15371
15390
  limit: external_exports.coerce.number().int().min(1).max(100).optional(),
15372
15391
  cursor: external_exports.string().optional()
@@ -15378,7 +15397,13 @@ var ListGroupedFindingsResponse = external_exports.object({
15378
15397
  }),
15379
15398
  facets: FindingFacets,
15380
15399
  items: external_exports.array(FindingGroup),
15381
- nextCursor: external_exports.string().nullable()
15400
+ nextCursor: external_exports.string().nullable(),
15401
+ // Present only on session-scoped queries (`sessionId` set): per ruleId, how
15402
+ // many times that rule fired in the session's persisted transcript. Findings
15403
+ // here are deduplicated to unique values while the transcript tally counts
15404
+ // every firing, so the two numbers legitimately differ — this map lets a
15405
+ // session-scoped view show both.
15406
+ sessionFirings: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()).optional()
15382
15407
  }).meta({ id: "ListGroupedFindingsResponse" });
15383
15408
  var ApplyFindingActionRequest = external_exports.object({
15384
15409
  // 'quarantined' is system-assigned (see FindingAction) — clients may not set
@@ -15789,6 +15814,10 @@ var ListActivitySessionsQuery = external_exports.object({
15789
15814
  from: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15790
15815
  /** Upper bound on startedAt; omitted defaults to now. */
15791
15816
  to: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15817
+ /** Exclude zero-activity sessions — roots whose only recorded children are
15818
+ * bookkeeping rows (hooks, config scans), typically background `claude`
15819
+ * launches. Omitted = list everything. `z.stringbool()` per the note above. */
15820
+ excludeEmpty: external_exports.stringbool().optional(),
15792
15821
  /** Page size, 1–100; out-of-range values are a 400. `z.coerce` — query params arrive as strings. */
15793
15822
  limit: external_exports.coerce.number().int().min(1).max(100).default(50),
15794
15823
  /** Opaque pagination cursor (most-recent first). */
@@ -15797,7 +15826,11 @@ var ListActivitySessionsQuery = external_exports.object({
15797
15826
  var ListActivitySessionsResponse = external_exports.object({
15798
15827
  items: external_exports.array(ActivitySessionSummary),
15799
15828
  /** `null` once the last page is reached. */
15800
- nextCursor: external_exports.string().nullable()
15829
+ nextCursor: external_exports.string().nullable(),
15830
+ /** Zero-activity sessions matching the query's filters/range (whether or
15831
+ * not `excludeEmpty` dropped them from `items`) — the count a UI toggle
15832
+ * shows when collapsing them. */
15833
+ emptyCount: external_exports.number().int().nonnegative()
15801
15834
  }).meta({ id: "ListActivitySessionsResponse" });
15802
15835
  var ListSessionEventsQuery = external_exports.object({
15803
15836
  /** Default 100, range 1–500. */
@@ -15832,6 +15865,12 @@ var EventMetadata = external_exports.object({
15832
15865
  sessionId: external_exports.string().optional(),
15833
15866
  repo: external_exports.string().optional(),
15834
15867
  filePath: external_exports.string().optional(),
15868
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
15869
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
15870
+ // arguments or output, which can carry the very value a finding masked
15871
+ // (metadata is stored unredacted). Gives findings on non-file captures a
15872
+ // display location ("via Bash") when no filePath exists.
15873
+ toolName: external_exports.string().optional(),
15835
15874
  // Set (true) by the worktree scanner when the file is excluded by the
15836
15875
  // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
15837
15876
  // generated code can leak real secrets — but the provenance is recorded so
@@ -16163,7 +16202,10 @@ var ExceptionBundleEntry = DetectionException.pick({
16163
16202
  var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16164
16203
  var KeywordMatcher = external_exports.object({
16165
16204
  type: external_exports.literal("keyword"),
16166
- keywords: external_exports.array(external_exports.string()).min(1),
16205
+ // An empty keyword matches at every position, yielding one zero-length span
16206
+ // per character. Rejected here because a keyword that matches everything is
16207
+ // never intentional.
16208
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16167
16209
  caseSensitive: external_exports.boolean().default(false)
16168
16210
  });
16169
16211
  function isValidRegex(pattern, flags) {
@@ -16412,6 +16454,10 @@ var ListEventsResponse = external_exports.object({
16412
16454
  items: external_exports.array(Event),
16413
16455
  nextCursor: external_exports.string().nullable()
16414
16456
  }).meta({ id: "ListEventsResponse" });
16457
+ var IngestResponse = external_exports.object({
16458
+ accepted: external_exports.number().int().nonnegative(),
16459
+ duplicates: external_exports.number().int().nonnegative()
16460
+ }).meta({ id: "IngestResponse" });
16415
16461
  var ListFindingsQuery = external_exports.object({
16416
16462
  cursor: external_exports.string().optional(),
16417
16463
  limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
@@ -16801,6 +16847,7 @@ function buildFindingGroups(rows, opts = {}) {
16801
16847
  provider: toApiProvider(r.sourceTool),
16802
16848
  repo: r.repo,
16803
16849
  file: r.file,
16850
+ ...r.toolName === void 0 ? {} : { toolName: r.toolName },
16804
16851
  action: toApiAction(effectiveDbAction),
16805
16852
  detectedAt: r.occurredAt,
16806
16853
  confidence: r.confidence,
@@ -16873,6 +16920,7 @@ function buildHaystack(g, extra) {
16873
16920
  g.id,
16874
16921
  ...g.instances.map((i) => i.repo),
16875
16922
  ...g.instances.map((i) => i.file),
16923
+ ...g.instances.map((i) => i.toolName ? `via ${i.toolName}` : ""),
16876
16924
  ...g.instances.map((i) => i.id),
16877
16925
  ...extra === void 0 ? [] : [extra]
16878
16926
  ].join(" ").toLowerCase();
@@ -17139,6 +17187,71 @@ var ProjectFilesScan = external_exports.object({
17139
17187
  scannedAt: external_exports.string()
17140
17188
  });
17141
17189
 
17190
+ // ../../packages/schema/src/zod/ranges.ts
17191
+ var TIME_RANGES = ["7d", "30d", "3m", "6m"];
17192
+ var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
17193
+ var DEFAULT_TIME_RANGE = "7d";
17194
+ var RANGE_DAYS = {
17195
+ "7d": 7,
17196
+ "30d": 30,
17197
+ "3m": 90,
17198
+ "6m": 180
17199
+ };
17200
+ var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
17201
+
17202
+ // ../../packages/schema/src/zod/remediation.ts
17203
+ var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
17204
+ var MaskedFindingLocation = external_exports.object({
17205
+ filePath: external_exports.string(),
17206
+ span: Span.optional()
17207
+ }).strict();
17208
+ var MaskedSecretFinding = external_exports.object({
17209
+ provider: external_exports.string(),
17210
+ maskedToken: external_exports.string(),
17211
+ where: MaskedFindingLocation,
17212
+ state: SecretFindingState,
17213
+ observedAt: external_exports.iso.datetime().optional()
17214
+ }).strict();
17215
+ var RotationChecklistEntry = external_exports.object({
17216
+ provider: external_exports.string(),
17217
+ maskedToken: external_exports.string(),
17218
+ consolePath: external_exports.string(),
17219
+ occurrenceSpread: external_exports.number().int().positive()
17220
+ }).strict();
17221
+ var RemediationOption = external_exports.enum([
17222
+ "redact-rotation-checklist",
17223
+ "redact-only",
17224
+ "set-secret-redact",
17225
+ "leave"
17226
+ ]);
17227
+ var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
17228
+ var RemediationEntryContext = external_exports.object({
17229
+ entrySource: RemediationEntrySource
17230
+ }).strict();
17231
+ var RemediationOptionChoice = external_exports.object({
17232
+ id: RemediationOption,
17233
+ label: external_exports.string()
17234
+ });
17235
+ var BatchedRemediationDecision = external_exports.object({
17236
+ kind: external_exports.literal("decision"),
17237
+ entrySource: RemediationEntrySource,
17238
+ secretCount: external_exports.number().int().positive(),
17239
+ prompt: external_exports.string(),
17240
+ options: external_exports.tuple([
17241
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
17242
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
17243
+ RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
17244
+ RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
17245
+ ])
17246
+ });
17247
+ var NoRemediationDecision = external_exports.object({
17248
+ kind: external_exports.literal("no-decision")
17249
+ });
17250
+ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
17251
+ BatchedRemediationDecision,
17252
+ NoRemediationDecision
17253
+ ]);
17254
+
17142
17255
  // ../../packages/schema/src/zod/rule-test.ts
17143
17256
  var TestRulesRequest = external_exports.object({
17144
17257
  rules: external_exports.array(Rule).min(1).max(100),
@@ -17197,10 +17310,8 @@ var SeveritySummaryResponse = external_exports.object({
17197
17310
  // All four severity levels are always present (count may be 0).
17198
17311
  bySeverity: external_exports.array(SeveritySummaryItem)
17199
17312
  }).meta({ id: "SeveritySummaryResponse" });
17200
- var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
17201
- var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
17202
17313
  var SecurityRangeQuery = external_exports.object({
17203
- range: external_exports.enum(SECURITY_RANGES).default("30d")
17314
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
17204
17315
  });
17205
17316
  var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
17206
17317
  var EnforcementAction = external_exports.object({
@@ -17210,7 +17321,7 @@ var EnforcementAction = external_exports.object({
17210
17321
  delta: external_exports.number().int()
17211
17322
  }).meta({ id: "EnforcementAction" });
17212
17323
  var EnforcementActionsResponse = external_exports.object({
17213
- range: SecurityRange,
17324
+ range: TimeRange,
17214
17325
  // Sum of actions[].count in the window.
17215
17326
  total: external_exports.number().int().nonnegative(),
17216
17327
  // One entry per kind, always all three present (count may be 0).
@@ -17225,7 +17336,7 @@ var FindingsTimeseriesPoint = external_exports.object({
17225
17336
  medium: external_exports.number().int().nonnegative()
17226
17337
  }).meta({ id: "FindingsTimeseriesPoint" });
17227
17338
  var FindingsTimeseriesResponse = external_exports.object({
17228
- range: SecurityRange,
17339
+ range: TimeRange,
17229
17340
  granularity: TimeseriesGranularity,
17230
17341
  points: external_exports.array(FindingsTimeseriesPoint)
17231
17342
  }).meta({ id: "FindingsTimeseriesResponse" });
@@ -17240,7 +17351,7 @@ var MttrTrendPoint = external_exports.object({
17240
17351
  })
17241
17352
  }).meta({ id: "MttrTrendPoint" });
17242
17353
  var MttrTrendResponse = external_exports.object({
17243
- range: SecurityRange,
17354
+ range: TimeRange,
17244
17355
  granularity: TimeseriesGranularity,
17245
17356
  points: external_exports.array(MttrTrendPoint)
17246
17357
  }).meta({ id: "MttrTrendResponse" });
@@ -17267,11 +17378,11 @@ var TopSource = external_exports.object({
17267
17378
  findingsCount: external_exports.number().int().nonnegative()
17268
17379
  }).meta({ id: "TopSource" });
17269
17380
  var TopSourcesResponse = external_exports.object({
17270
- range: SecurityRange,
17381
+ range: TimeRange,
17271
17382
  items: external_exports.array(TopSource)
17272
17383
  }).meta({ id: "TopSourcesResponse" });
17273
17384
  var TopSourcesQuery = external_exports.object({
17274
- range: external_exports.enum(SECURITY_RANGES).default("30d"),
17385
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
17275
17386
  limit: external_exports.coerce.number().int().min(1).max(50).default(5),
17276
17387
  // Omit for both kinds.
17277
17388
  kind: external_exports.enum(SOURCE_KINDS).optional()
@@ -17284,7 +17395,7 @@ var ScanCoverageProvider = external_exports.object({
17284
17395
  supported: external_exports.boolean()
17285
17396
  }).meta({ id: "ScanCoverageProvider" });
17286
17397
  var ScanCoverageResponse = external_exports.object({
17287
- range: SecurityRange,
17398
+ range: TimeRange,
17288
17399
  providers: external_exports.array(ScanCoverageProvider)
17289
17400
  }).meta({ id: "ScanCoverageResponse" });
17290
17401
  var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
@@ -17333,6 +17444,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
17333
17444
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
17334
17445
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
17335
17446
 
17447
+ // ../../packages/schema/src/zod/triage.ts
17448
+ var TriageHit = external_exports.object({
17449
+ ruleId: external_exports.string(),
17450
+ category: DetectionCategory,
17451
+ severity: Severity,
17452
+ maskedMatch: external_exports.string(),
17453
+ rawMatch: external_exports.string(),
17454
+ context: external_exports.string(),
17455
+ filePath: external_exports.string().optional(),
17456
+ confidence: external_exports.number().min(0).max(1),
17457
+ id: external_exports.string().optional(),
17458
+ valueFingerprint: external_exports.string().optional(),
17459
+ keyVersion: external_exports.number().int().nonnegative().optional()
17460
+ });
17461
+ var TriagePolicy = BuiltinPolicyId;
17462
+ var TriageCategoryRec = external_exports.object({
17463
+ category: DetectionCategory,
17464
+ action: TriagePolicy,
17465
+ reasoning: external_exports.string(),
17466
+ genuineCount: external_exports.number().int().nonnegative(),
17467
+ fpCount: external_exports.number().int().nonnegative(),
17468
+ // TriageHit ids judged false-positive in this category. fpCount must equal
17469
+ // this array's length — enforced by the consumer, not this schema.
17470
+ fpIds: external_exports.array(external_exports.string())
17471
+ });
17472
+ var TriageRecommendation = external_exports.object({
17473
+ perCategory: external_exports.array(TriageCategoryRec),
17474
+ notes: external_exports.string()
17475
+ });
17476
+
17477
+ // ../../packages/schema/src/zod/setup-frame.ts
17478
+ var CalibrationCounts = external_exports.object({
17479
+ total: external_exports.number().int().nonnegative(),
17480
+ important: external_exports.number().int().nonnegative(),
17481
+ routine: external_exports.number().int().nonnegative()
17482
+ }).refine((c) => c.total === c.important + c.routine, {
17483
+ message: "total must equal important + routine",
17484
+ path: ["total"]
17485
+ });
17486
+ var FalsePositivePatternValue = external_exports.object({
17487
+ ruleId: external_exports.string(),
17488
+ category: DetectionCategory,
17489
+ valueFingerprint: external_exports.string(),
17490
+ keyVersion: external_exports.number().int().nonnegative()
17491
+ });
17492
+ var FalsePositivePatternGroup = external_exports.object({
17493
+ pattern: external_exports.string(),
17494
+ count: external_exports.number().int().nonnegative(),
17495
+ values: external_exports.array(FalsePositivePatternValue).min(1)
17496
+ });
17497
+ var CalibrationFindingKind = external_exports.object({
17498
+ category: DetectionCategory,
17499
+ count: external_exports.number().int().nonnegative(),
17500
+ egress: external_exports.boolean()
17501
+ });
17502
+ var CalibrationFrame = external_exports.object({
17503
+ counts: CalibrationCounts,
17504
+ routineCategories: external_exports.array(DetectionCategory),
17505
+ surfacedCategories: external_exports.array(DetectionCategory),
17506
+ findingKinds: external_exports.array(CalibrationFindingKind),
17507
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
17508
+ maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
17509
+ falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
17510
+ });
17511
+ var CalibrationPreviewCategory = TriageCategoryRec.pick({
17512
+ category: true,
17513
+ genuineCount: true,
17514
+ fpCount: true
17515
+ }).extend({
17516
+ egress: external_exports.boolean()
17517
+ });
17518
+ var CalibrationPreview = external_exports.object({
17519
+ categories: external_exports.array(CalibrationPreviewCategory),
17520
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
17521
+ });
17522
+ var CalibrationResult = external_exports.object({
17523
+ frame: CalibrationFrame,
17524
+ copy: external_exports.string()
17525
+ });
17526
+ var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
17527
+ var SetupHandoffOption = external_exports.object({
17528
+ id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
17529
+ label: external_exports.string()
17530
+ });
17531
+ var DashboardHandoffOptions = external_exports.tuple([
17532
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17533
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17534
+ ]);
17535
+ var ComposedRemediationOptions = external_exports.tuple([
17536
+ SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
17537
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17538
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17539
+ ]);
17540
+ var SetupHandoffOffer = external_exports.object({
17541
+ worthALook: external_exports.number().int().nonnegative(),
17542
+ liveKeys: external_exports.number().int().nonnegative().optional(),
17543
+ options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
17544
+ }).refine(
17545
+ (o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
17546
+ {
17547
+ message: "the chain-entry option is present exactly when liveKeys > 0",
17548
+ path: ["options"]
17549
+ }
17550
+ ).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
17551
+ message: "liveKeys is a subset of worthALook and cannot exceed it",
17552
+ path: ["liveKeys"]
17553
+ });
17554
+
17336
17555
  // ../../packages/schema/src/zod/shares.ts
17337
17556
  var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
17338
17557
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
@@ -17518,36 +17737,6 @@ function reviewSeverityRank(reasons) {
17518
17737
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
17519
17738
  }
17520
17739
 
17521
- // ../../packages/schema/src/zod/triage.ts
17522
- var TriageHit = external_exports.object({
17523
- ruleId: external_exports.string(),
17524
- category: DetectionCategory,
17525
- severity: Severity,
17526
- maskedMatch: external_exports.string(),
17527
- rawMatch: external_exports.string(),
17528
- context: external_exports.string(),
17529
- filePath: external_exports.string().optional(),
17530
- confidence: external_exports.number().min(0).max(1),
17531
- id: external_exports.string().optional(),
17532
- valueFingerprint: external_exports.string().optional(),
17533
- keyVersion: external_exports.number().int().nonnegative().optional()
17534
- });
17535
- var TriagePolicy = BuiltinPolicyId;
17536
- var TriageCategoryRec = external_exports.object({
17537
- category: DetectionCategory,
17538
- action: TriagePolicy,
17539
- reasoning: external_exports.string(),
17540
- genuineCount: external_exports.number().int().nonnegative(),
17541
- fpCount: external_exports.number().int().nonnegative(),
17542
- // TriageHit ids judged false-positive in this category. fpCount must equal
17543
- // this array's length — enforced by the consumer, not this schema.
17544
- fpIds: external_exports.array(external_exports.string())
17545
- });
17546
- var TriageRecommendation = external_exports.object({
17547
- perCategory: external_exports.array(TriageCategoryRec),
17548
- notes: external_exports.string()
17549
- });
17550
-
17551
17740
  // ../../packages/persistence/src/internal/sql-text.ts
17552
17741
  function escapeLikePattern(s) {
17553
17742
  return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
@@ -18121,6 +18310,10 @@ var TIMELINE_COLUMNS = `
18121
18310
  json_extract(attributes, '$.internal') AS internal,
18122
18311
  json_extract(attributes, '$.flagged') AS flagged`;
18123
18312
  var SESSION_ROOT = `event_type = 'session'`;
18313
+ var HAS_ACTIVITY = `EXISTS (
18314
+ SELECT 1 FROM audit_events c
18315
+ WHERE c.root_session_id = audit_events.id
18316
+ AND c.event_type NOT IN ('hook', 'config_scan'))`;
18124
18317
  var SqliteActivityRepository = class {
18125
18318
  constructor(db, now = () => Date.now()) {
18126
18319
  this.db = db;
@@ -18134,7 +18327,8 @@ var SqliteActivityRepository = class {
18134
18327
  const sessionsToday = countScalar(
18135
18328
  this.db,
18136
18329
  `SELECT count(*) AS n FROM audit_events
18137
- WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?`,
18330
+ WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?
18331
+ AND ${HAS_ACTIVITY}`,
18138
18332
  [startMs, endMs]
18139
18333
  );
18140
18334
  const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
@@ -18206,6 +18400,13 @@ var SqliteActivityRepository = class {
18206
18400
  );
18207
18401
  params.push(pattern, pattern, pattern, pattern, pattern, pattern);
18208
18402
  }
18403
+ const emptyCount = countScalar(
18404
+ this.db,
18405
+ `SELECT count(*) AS n FROM audit_events
18406
+ WHERE ${[...conditions, `NOT ${HAS_ACTIVITY}`].join(" AND ")}`,
18407
+ params
18408
+ );
18409
+ if (query.excludeEmpty) conditions.push(HAS_ACTIVITY);
18209
18410
  if (cursor) {
18210
18411
  conditions.push("(started_at < ? OR (started_at = ? AND id < ?))");
18211
18412
  params.push(cursor.startedAtMs, cursor.startedAtMs, cursor.id);
@@ -18242,7 +18443,7 @@ var SqliteActivityRepository = class {
18242
18443
  );
18243
18444
  const last = page[page.length - 1];
18244
18445
  const nextCursor = hasMore && last ? encodeCursor({ startedAtMs: last.started_at, id: last.id }) : null;
18245
- return Promise.resolve({ items, nextCursor });
18446
+ return Promise.resolve({ items, nextCursor, emptyCount });
18246
18447
  }
18247
18448
  getSession(sessionId) {
18248
18449
  const rootRow = getRow(
@@ -19548,9 +19749,43 @@ var SqliteFindingsRepository = class {
19548
19749
  }))
19549
19750
  );
19550
19751
  }
19752
+ /** Live-enforced findings recorded for one session — a bare COUNT over the
19753
+ * session-stamped events (served by idx_events_session_id), so the Activity
19754
+ * page can label its findings link without the grouped pipeline. */
19755
+ sessionFindingsCount(sessionId) {
19756
+ if (!sessionId) return Promise.resolve(0);
19757
+ return Promise.resolve(
19758
+ countScalar(
19759
+ this.db,
19760
+ `SELECT count(*) AS n FROM findings f
19761
+ JOIN events e ON e.id = f.event_id
19762
+ WHERE json_extract(e.metadata, '$.sessionId') = :sessionId`,
19763
+ { sessionId }
19764
+ )
19765
+ );
19766
+ }
19767
+ /** Per-rule transcript firing tally for one session — reads the OTHER finding
19768
+ * store (inspection_findings, keyed to audit_events): every detection the
19769
+ * transcript pass recorded, counted per firing rather than per unique value.
19770
+ * Rides on session-scoped grouped responses so the findings view can
19771
+ * reconcile the Activity page's tally with the deduped groups it lists. */
19772
+ sessionFirings(sessionId) {
19773
+ return Object.fromEntries(
19774
+ countBy(
19775
+ this.db,
19776
+ `SELECT d.rule_id AS k, count(*) AS n
19777
+ FROM inspection_findings f
19778
+ JOIN audit_events e ON e.id = f.audit_event_id
19779
+ JOIN inspection_definitions d ON d.id = f.inspection_definition_id
19780
+ WHERE e.root_session_id = :sessionId
19781
+ GROUP BY d.rule_id`,
19782
+ { sessionId }
19783
+ )
19784
+ );
19785
+ }
19551
19786
  /**
19552
- * Grouped findings for the dashboard — joins findings⋈events (repo/file
19553
- * from event metadata), groups by ruleId, computes per-filter-excluded facets,
19787
+ * Grouped findings for the dashboard — joins findings⋈events (repo/file/
19788
+ * toolName from event metadata), groups by ruleId, computes per-filter-excluded facets,
19554
19789
  * applies the requested filters, and sorts by severity then recency. Filtering
19555
19790
  * and faceting run in JS via the shared @akasecurity/schema helpers. `totals`
19556
19791
  * reflect the full filtered set; `items` is the requested
@@ -19567,11 +19802,16 @@ var SqliteFindingsRepository = class {
19567
19802
  * rule is ever restated in SQL.
19568
19803
  */
19569
19804
  listGroupedFindings(query) {
19570
- const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "");
19805
+ const sessionPredicate = query.sessionId ? `WHERE json_extract(e.metadata, '$.sessionId') = :sessionId` : "";
19806
+ const sessionParams = query.sessionId ? { sessionId: query.sessionId } : {};
19807
+ const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
19808
+ predicate: sessionPredicate,
19809
+ params: sessionParams
19810
+ });
19571
19811
  const rows = allRows(
19572
19812
  this.db.prepare(
19573
19813
  `SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
19574
- occurred_at, source_tool, repo, file, kind, finding_key, latest_status
19814
+ occurred_at, source_tool, repo, file, tool_name, kind, finding_key, latest_status
19575
19815
  FROM (
19576
19816
  SELECT f.id AS id, f.rule_id AS rule_id, f.category AS category,
19577
19817
  f.severity AS severity, f.masked_match AS masked_match,
@@ -19579,6 +19819,7 @@ var SqliteFindingsRepository = class {
19579
19819
  e.occurred_at AS occurred_at, e.source_tool AS source_tool,
19580
19820
  json_extract(e.metadata, '$.repo') AS repo,
19581
19821
  json_extract(e.metadata, '$.filePath') AS file,
19822
+ json_extract(e.metadata, '$.toolName') AS tool_name,
19582
19823
  e.kind AS kind, f.finding_key AS finding_key,
19583
19824
  latest.status AS latest_status,
19584
19825
  ROW_NUMBER() OVER (
@@ -19589,11 +19830,12 @@ var SqliteFindingsRepository = class {
19589
19830
  JOIN events e ON e.id = f.event_id
19590
19831
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19591
19832
  ON latest.finding_key = f.finding_key
19833
+ ${sessionPredicate}
19592
19834
  )
19593
19835
  WHERE rn <= :cap
19594
19836
  ORDER BY occurred_at DESC, id DESC`
19595
19837
  ),
19596
- { cap: PREVIEW_INSTANCES_PER_GROUP }
19838
+ { cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
19597
19839
  );
19598
19840
  const groupable = rows.map((r) => ({
19599
19841
  id: r.id,
@@ -19607,6 +19849,7 @@ var SqliteFindingsRepository = class {
19607
19849
  sourceTool: r.source_tool,
19608
19850
  repo: r.repo ?? "",
19609
19851
  file: r.file ?? "",
19852
+ ...r.tool_name === null ? {} : { toolName: r.tool_name },
19610
19853
  status: deriveInstanceStatus(r)
19611
19854
  }));
19612
19855
  const allGroups = buildFindingGroups(groupable, { aggregates });
@@ -19625,7 +19868,13 @@ var SqliteFindingsRepository = class {
19625
19868
  };
19626
19869
  const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
19627
19870
  const items = sorted.slice(0, limit);
19628
- return Promise.resolve({ totals, facets, items, nextCursor: null });
19871
+ return Promise.resolve({
19872
+ totals,
19873
+ facets,
19874
+ items,
19875
+ nextCursor: null,
19876
+ ...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
19877
+ });
19629
19878
  }
19630
19879
  /**
19631
19880
  * One row per rule_id, folding EVERY instance of the group into the values
@@ -19649,9 +19898,10 @@ var SqliteFindingsRepository = class {
19649
19898
  * would silently lose, so it is fetched only when the request actually
19650
19899
  * carries a `q`.
19651
19900
  */
19652
- groupAggregates(withSearchText) {
19901
+ groupAggregates(withSearchText, scope) {
19653
19902
  const searchTextColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.metadata, '$.repo')) AS repos,
19654
- group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files` : `, NULL AS repos, NULL AS files`;
19903
+ group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files,
19904
+ group_concat(DISTINCT 'via ' || json_extract(e.metadata, '$.toolName')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
19655
19905
  const rows = this.db.prepare(
19656
19906
  `SELECT f.rule_id AS rule_id,
19657
19907
  count(*) AS instance_count,
@@ -19668,8 +19918,9 @@ var SqliteFindingsRepository = class {
19668
19918
  JOIN events e ON e.id = f.event_id
19669
19919
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19670
19920
  ON latest.finding_key = f.finding_key
19921
+ ${scope.predicate}
19671
19922
  GROUP BY f.rule_id`
19672
- ).all();
19923
+ ).all(scope.params);
19673
19924
  return new Map(
19674
19925
  rows.map((r) => [
19675
19926
  r.rule_id,
@@ -19693,7 +19944,9 @@ var SqliteFindingsRepository = class {
19693
19944
  // Left undefined (not '') when unfetched, so buildFindingGroups can
19694
19945
  // tell "no q this request" from "a group with no repo/file at all"
19695
19946
  // and skip priming a haystack nothing will read.
19696
- ...withSearchText ? { searchText: [r.repos ?? "", r.files ?? ""].filter((s) => s !== "").join(" ") } : {}
19947
+ ...withSearchText ? {
19948
+ searchText: [r.repos ?? "", r.files ?? "", r.tool_names ?? ""].filter((s) => s !== "").join(" ")
19949
+ } : {}
19697
19950
  }
19698
19951
  ])
19699
19952
  );
@@ -21429,7 +21682,6 @@ var SqliteScanLedgerRepository = class {
21429
21682
  // ../../packages/persistence/src/repositories/security.ts
21430
21683
  var DAY_MS4 = 864e5;
21431
21684
  var SEVERITIES = ["critical", "high", "medium", "low"];
21432
- var RANGE_DAYS = { "7d": 7, "30d": 30, "3m": 90, "6m": 180 };
21433
21685
  var ACTION_TO_KIND = {
21434
21686
  block: "blocked",
21435
21687
  redact: "redacted",
@@ -21444,8 +21696,14 @@ var SCAN_COVERAGE = [
21444
21696
  { provider: "copilot", coverage: 0, supported: false },
21445
21697
  { provider: "api", coverage: 0, supported: false }
21446
21698
  ];
21699
+ var GRANULARITY = {
21700
+ "7d": "day",
21701
+ "30d": "day",
21702
+ "3m": "week",
21703
+ "6m": "week"
21704
+ };
21447
21705
  function granularityFor(range) {
21448
- return range === "7d" || range === "30d" ? "day" : "week";
21706
+ return GRANULARITY[range];
21449
21707
  }
21450
21708
  function startOfUtcDay2(ms) {
21451
21709
  return Math.floor(ms / DAY_MS4) * DAY_MS4;
@@ -22645,21 +22903,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
22645
22903
  import { homedir as homedir2 } from "os";
22646
22904
  import { basename as basename2, join as join7 } from "path";
22647
22905
 
22906
+ // ../../packages/detections/src/escape-regexp.ts
22907
+ function escapeRegExp(value) {
22908
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22909
+ }
22910
+
22911
+ // ../../packages/detections/src/matchers/limits.ts
22912
+ var MAX_MATCHES_PER_RULE = 1e4;
22913
+
22648
22914
  // ../../packages/detections/src/matchers/keyword.ts
22649
22915
  var KeywordMatcher2 = class {
22650
22916
  match(text, rule) {
22651
22917
  if (rule.matcher.type !== "keyword") return [];
22652
22918
  const { keywords, caseSensitive } = rule.matcher;
22653
- const haystack = caseSensitive ? text : text.toLowerCase();
22654
22919
  const spans = [];
22655
22920
  for (const kw of keywords) {
22656
- const needle = caseSensitive ? kw : kw.toLowerCase();
22657
- let idx = 0;
22658
- while (idx < haystack.length) {
22659
- const pos = haystack.indexOf(needle, idx);
22660
- if (pos === -1) break;
22661
- spans.push({ start: pos, end: pos + kw.length });
22662
- idx = pos + 1;
22921
+ if (kw.length === 0) continue;
22922
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
22923
+ const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
22924
+ let m;
22925
+ while ((m = re.exec(text)) !== null) {
22926
+ spans.push({ start: m.index, end: m.index + m[0].length });
22927
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
22663
22928
  }
22664
22929
  }
22665
22930
  return spans;
@@ -22667,7 +22932,6 @@ var KeywordMatcher2 = class {
22667
22932
  };
22668
22933
 
22669
22934
  // ../../packages/detections/src/matchers/regex.ts
22670
- var MAX_MATCHES_PER_RULE = 1e4;
22671
22935
  var RegexMatcher2 = class {
22672
22936
  match(text, rule) {
22673
22937
  if (rule.matcher.type !== "regex") return [];
@@ -22753,9 +23017,6 @@ function registerPack(pack) {
22753
23017
  function getLoadedRules() {
22754
23018
  return [...packs.values()].flatMap((p) => p.rules);
22755
23019
  }
22756
- function escapeRegExp(value) {
22757
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22758
- }
22759
23020
  function isCorroborated(candidate, candidates, text) {
22760
23021
  const req = candidate.rule.requiresNearby;
22761
23022
  if (!req) return true;
@@ -23537,7 +23798,6 @@ var db_table_name_default = {
23537
23798
  "SELECT * FROM ",
23538
23799
  "SELECT COUNT(*) FROM ",
23539
23800
  "INSERT INTO ",
23540
- "UPDATE ",
23541
23801
  "DELETE FROM ",
23542
23802
  "CREATE TABLE ",
23543
23803
  "ALTER TABLE ",