@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.
@@ -541,6 +541,10 @@ var SQLITE_MIGRATIONS = [
541
541
  {
542
542
  tag: "0009_findings_path_expression_index",
543
543
  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"
544
+ },
545
+ {
546
+ tag: "0010_events_session_expression_index",
547
+ 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"
544
548
  }
545
549
  ];
546
550
 
@@ -643,6 +647,12 @@ var defaultCostModel = {
643
647
  }
644
648
  };
645
649
 
650
+ // ../../packages/schema/src/token/format.ts
651
+ var COMPACT = new Intl.NumberFormat("en-US", {
652
+ notation: "compact",
653
+ maximumFractionDigits: 1
654
+ });
655
+
646
656
  // ../../packages/schema/src/token/token-report.ts
647
657
  var num = (value) => value ?? 0;
648
658
  function costUsageOf(a) {
@@ -15314,6 +15324,12 @@ var FindingInstance = external_exports.object({
15314
15324
  provider: FindingProvider,
15315
15325
  repo: external_exports.string(),
15316
15326
  file: external_exports.string(),
15327
+ // Host tool that produced the scanned text (event metadata's toolName).
15328
+ // Present whenever the capturing hook recorded one — including
15329
+ // file-attributed captures (views prefer `file`); its display value is
15330
+ // the location fallback ("via Bash") when no filePath exists. Absent for
15331
+ // legacy rows and non-tool captures (prompts, worktree scans).
15332
+ toolName: external_exports.string().optional(),
15317
15333
  // Effective action: override.action ?? actionTaken, translated to FindingAction.
15318
15334
  action: FindingAction,
15319
15335
  detectedAt: external_exports.iso.datetime(),
@@ -15370,6 +15386,9 @@ var ListGroupedFindingsQuery = external_exports.object({
15370
15386
  provider: external_exports.array(FindingProvider).optional(),
15371
15387
  action: external_exports.array(FindingAction).optional(),
15372
15388
  q: external_exports.string().optional(),
15389
+ // Scope to findings whose event carries this session id (the Activity page's
15390
+ // session → findings drilldown). Findings without a session never match.
15391
+ sessionId: external_exports.string().optional(),
15373
15392
  groupBy: external_exports.literal("type").optional(),
15374
15393
  limit: external_exports.coerce.number().int().min(1).max(100).optional(),
15375
15394
  cursor: external_exports.string().optional()
@@ -15381,7 +15400,13 @@ var ListGroupedFindingsResponse = external_exports.object({
15381
15400
  }),
15382
15401
  facets: FindingFacets,
15383
15402
  items: external_exports.array(FindingGroup),
15384
- nextCursor: external_exports.string().nullable()
15403
+ nextCursor: external_exports.string().nullable(),
15404
+ // Present only on session-scoped queries (`sessionId` set): per ruleId, how
15405
+ // many times that rule fired in the session's persisted transcript. Findings
15406
+ // here are deduplicated to unique values while the transcript tally counts
15407
+ // every firing, so the two numbers legitimately differ — this map lets a
15408
+ // session-scoped view show both.
15409
+ sessionFirings: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()).optional()
15385
15410
  }).meta({ id: "ListGroupedFindingsResponse" });
15386
15411
  var ApplyFindingActionRequest = external_exports.object({
15387
15412
  // 'quarantined' is system-assigned (see FindingAction) — clients may not set
@@ -15792,6 +15817,10 @@ var ListActivitySessionsQuery = external_exports.object({
15792
15817
  from: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15793
15818
  /** Upper bound on startedAt; omitted defaults to now. */
15794
15819
  to: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15820
+ /** Exclude zero-activity sessions — roots whose only recorded children are
15821
+ * bookkeeping rows (hooks, config scans), typically background `claude`
15822
+ * launches. Omitted = list everything. `z.stringbool()` per the note above. */
15823
+ excludeEmpty: external_exports.stringbool().optional(),
15795
15824
  /** Page size, 1–100; out-of-range values are a 400. `z.coerce` — query params arrive as strings. */
15796
15825
  limit: external_exports.coerce.number().int().min(1).max(100).default(50),
15797
15826
  /** Opaque pagination cursor (most-recent first). */
@@ -15800,7 +15829,11 @@ var ListActivitySessionsQuery = external_exports.object({
15800
15829
  var ListActivitySessionsResponse = external_exports.object({
15801
15830
  items: external_exports.array(ActivitySessionSummary),
15802
15831
  /** `null` once the last page is reached. */
15803
- nextCursor: external_exports.string().nullable()
15832
+ nextCursor: external_exports.string().nullable(),
15833
+ /** Zero-activity sessions matching the query's filters/range (whether or
15834
+ * not `excludeEmpty` dropped them from `items`) — the count a UI toggle
15835
+ * shows when collapsing them. */
15836
+ emptyCount: external_exports.number().int().nonnegative()
15804
15837
  }).meta({ id: "ListActivitySessionsResponse" });
15805
15838
  var ListSessionEventsQuery = external_exports.object({
15806
15839
  /** Default 100, range 1–500. */
@@ -15835,6 +15868,12 @@ var EventMetadata = external_exports.object({
15835
15868
  sessionId: external_exports.string().optional(),
15836
15869
  repo: external_exports.string().optional(),
15837
15870
  filePath: external_exports.string().optional(),
15871
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
15872
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
15873
+ // arguments or output, which can carry the very value a finding masked
15874
+ // (metadata is stored unredacted). Gives findings on non-file captures a
15875
+ // display location ("via Bash") when no filePath exists.
15876
+ toolName: external_exports.string().optional(),
15838
15877
  // Set (true) by the worktree scanner when the file is excluded by the
15839
15878
  // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
15840
15879
  // generated code can leak real secrets — but the provenance is recorded so
@@ -16166,7 +16205,10 @@ var ExceptionBundleEntry = DetectionException.pick({
16166
16205
  var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16167
16206
  var KeywordMatcher = external_exports.object({
16168
16207
  type: external_exports.literal("keyword"),
16169
- keywords: external_exports.array(external_exports.string()).min(1),
16208
+ // An empty keyword matches at every position, yielding one zero-length span
16209
+ // per character. Rejected here because a keyword that matches everything is
16210
+ // never intentional.
16211
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16170
16212
  caseSensitive: external_exports.boolean().default(false)
16171
16213
  });
16172
16214
  function isValidRegex(pattern, flags) {
@@ -16415,6 +16457,10 @@ var ListEventsResponse = external_exports.object({
16415
16457
  items: external_exports.array(Event),
16416
16458
  nextCursor: external_exports.string().nullable()
16417
16459
  }).meta({ id: "ListEventsResponse" });
16460
+ var IngestResponse = external_exports.object({
16461
+ accepted: external_exports.number().int().nonnegative(),
16462
+ duplicates: external_exports.number().int().nonnegative()
16463
+ }).meta({ id: "IngestResponse" });
16418
16464
  var ListFindingsQuery = external_exports.object({
16419
16465
  cursor: external_exports.string().optional(),
16420
16466
  limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
@@ -16918,6 +16964,7 @@ function buildFindingGroups(rows, opts = {}) {
16918
16964
  provider: toApiProvider(r.sourceTool),
16919
16965
  repo: r.repo,
16920
16966
  file: r.file,
16967
+ ...r.toolName === void 0 ? {} : { toolName: r.toolName },
16921
16968
  action: toApiAction(effectiveDbAction),
16922
16969
  detectedAt: r.occurredAt,
16923
16970
  confidence: r.confidence,
@@ -16990,6 +17037,7 @@ function buildHaystack(g, extra) {
16990
17037
  g.id,
16991
17038
  ...g.instances.map((i) => i.repo),
16992
17039
  ...g.instances.map((i) => i.file),
17040
+ ...g.instances.map((i) => i.toolName ? `via ${i.toolName}` : ""),
16993
17041
  ...g.instances.map((i) => i.id),
16994
17042
  ...extra === void 0 ? [] : [extra]
16995
17043
  ].join(" ").toLowerCase();
@@ -17256,6 +17304,71 @@ var ProjectFilesScan = external_exports.object({
17256
17304
  scannedAt: external_exports.string()
17257
17305
  });
17258
17306
 
17307
+ // ../../packages/schema/src/zod/ranges.ts
17308
+ var TIME_RANGES = ["7d", "30d", "3m", "6m"];
17309
+ var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
17310
+ var DEFAULT_TIME_RANGE = "7d";
17311
+ var RANGE_DAYS = {
17312
+ "7d": 7,
17313
+ "30d": 30,
17314
+ "3m": 90,
17315
+ "6m": 180
17316
+ };
17317
+ var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
17318
+
17319
+ // ../../packages/schema/src/zod/remediation.ts
17320
+ var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
17321
+ var MaskedFindingLocation = external_exports.object({
17322
+ filePath: external_exports.string(),
17323
+ span: Span.optional()
17324
+ }).strict();
17325
+ var MaskedSecretFinding = external_exports.object({
17326
+ provider: external_exports.string(),
17327
+ maskedToken: external_exports.string(),
17328
+ where: MaskedFindingLocation,
17329
+ state: SecretFindingState,
17330
+ observedAt: external_exports.iso.datetime().optional()
17331
+ }).strict();
17332
+ var RotationChecklistEntry = external_exports.object({
17333
+ provider: external_exports.string(),
17334
+ maskedToken: external_exports.string(),
17335
+ consolePath: external_exports.string(),
17336
+ occurrenceSpread: external_exports.number().int().positive()
17337
+ }).strict();
17338
+ var RemediationOption = external_exports.enum([
17339
+ "redact-rotation-checklist",
17340
+ "redact-only",
17341
+ "set-secret-redact",
17342
+ "leave"
17343
+ ]);
17344
+ var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
17345
+ var RemediationEntryContext = external_exports.object({
17346
+ entrySource: RemediationEntrySource
17347
+ }).strict();
17348
+ var RemediationOptionChoice = external_exports.object({
17349
+ id: RemediationOption,
17350
+ label: external_exports.string()
17351
+ });
17352
+ var BatchedRemediationDecision = external_exports.object({
17353
+ kind: external_exports.literal("decision"),
17354
+ entrySource: RemediationEntrySource,
17355
+ secretCount: external_exports.number().int().positive(),
17356
+ prompt: external_exports.string(),
17357
+ options: external_exports.tuple([
17358
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
17359
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
17360
+ RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
17361
+ RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
17362
+ ])
17363
+ });
17364
+ var NoRemediationDecision = external_exports.object({
17365
+ kind: external_exports.literal("no-decision")
17366
+ });
17367
+ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
17368
+ BatchedRemediationDecision,
17369
+ NoRemediationDecision
17370
+ ]);
17371
+
17259
17372
  // ../../packages/schema/src/zod/rule-test.ts
17260
17373
  var TestRulesRequest = external_exports.object({
17261
17374
  rules: external_exports.array(Rule).min(1).max(100),
@@ -17314,10 +17427,8 @@ var SeveritySummaryResponse = external_exports.object({
17314
17427
  // All four severity levels are always present (count may be 0).
17315
17428
  bySeverity: external_exports.array(SeveritySummaryItem)
17316
17429
  }).meta({ id: "SeveritySummaryResponse" });
17317
- var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
17318
- var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
17319
17430
  var SecurityRangeQuery = external_exports.object({
17320
- range: external_exports.enum(SECURITY_RANGES).default("30d")
17431
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
17321
17432
  });
17322
17433
  var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
17323
17434
  var EnforcementAction = external_exports.object({
@@ -17327,7 +17438,7 @@ var EnforcementAction = external_exports.object({
17327
17438
  delta: external_exports.number().int()
17328
17439
  }).meta({ id: "EnforcementAction" });
17329
17440
  var EnforcementActionsResponse = external_exports.object({
17330
- range: SecurityRange,
17441
+ range: TimeRange,
17331
17442
  // Sum of actions[].count in the window.
17332
17443
  total: external_exports.number().int().nonnegative(),
17333
17444
  // One entry per kind, always all three present (count may be 0).
@@ -17342,7 +17453,7 @@ var FindingsTimeseriesPoint = external_exports.object({
17342
17453
  medium: external_exports.number().int().nonnegative()
17343
17454
  }).meta({ id: "FindingsTimeseriesPoint" });
17344
17455
  var FindingsTimeseriesResponse = external_exports.object({
17345
- range: SecurityRange,
17456
+ range: TimeRange,
17346
17457
  granularity: TimeseriesGranularity,
17347
17458
  points: external_exports.array(FindingsTimeseriesPoint)
17348
17459
  }).meta({ id: "FindingsTimeseriesResponse" });
@@ -17357,7 +17468,7 @@ var MttrTrendPoint = external_exports.object({
17357
17468
  })
17358
17469
  }).meta({ id: "MttrTrendPoint" });
17359
17470
  var MttrTrendResponse = external_exports.object({
17360
- range: SecurityRange,
17471
+ range: TimeRange,
17361
17472
  granularity: TimeseriesGranularity,
17362
17473
  points: external_exports.array(MttrTrendPoint)
17363
17474
  }).meta({ id: "MttrTrendResponse" });
@@ -17384,11 +17495,11 @@ var TopSource = external_exports.object({
17384
17495
  findingsCount: external_exports.number().int().nonnegative()
17385
17496
  }).meta({ id: "TopSource" });
17386
17497
  var TopSourcesResponse = external_exports.object({
17387
- range: SecurityRange,
17498
+ range: TimeRange,
17388
17499
  items: external_exports.array(TopSource)
17389
17500
  }).meta({ id: "TopSourcesResponse" });
17390
17501
  var TopSourcesQuery = external_exports.object({
17391
- range: external_exports.enum(SECURITY_RANGES).default("30d"),
17502
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
17392
17503
  limit: external_exports.coerce.number().int().min(1).max(50).default(5),
17393
17504
  // Omit for both kinds.
17394
17505
  kind: external_exports.enum(SOURCE_KINDS).optional()
@@ -17401,7 +17512,7 @@ var ScanCoverageProvider = external_exports.object({
17401
17512
  supported: external_exports.boolean()
17402
17513
  }).meta({ id: "ScanCoverageProvider" });
17403
17514
  var ScanCoverageResponse = external_exports.object({
17404
- range: SecurityRange,
17515
+ range: TimeRange,
17405
17516
  providers: external_exports.array(ScanCoverageProvider)
17406
17517
  }).meta({ id: "ScanCoverageResponse" });
17407
17518
  var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
@@ -17450,6 +17561,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
17450
17561
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
17451
17562
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
17452
17563
 
17564
+ // ../../packages/schema/src/zod/triage.ts
17565
+ var TriageHit = external_exports.object({
17566
+ ruleId: external_exports.string(),
17567
+ category: DetectionCategory,
17568
+ severity: Severity,
17569
+ maskedMatch: external_exports.string(),
17570
+ rawMatch: external_exports.string(),
17571
+ context: external_exports.string(),
17572
+ filePath: external_exports.string().optional(),
17573
+ confidence: external_exports.number().min(0).max(1),
17574
+ id: external_exports.string().optional(),
17575
+ valueFingerprint: external_exports.string().optional(),
17576
+ keyVersion: external_exports.number().int().nonnegative().optional()
17577
+ });
17578
+ var TriagePolicy = BuiltinPolicyId;
17579
+ var TriageCategoryRec = external_exports.object({
17580
+ category: DetectionCategory,
17581
+ action: TriagePolicy,
17582
+ reasoning: external_exports.string(),
17583
+ genuineCount: external_exports.number().int().nonnegative(),
17584
+ fpCount: external_exports.number().int().nonnegative(),
17585
+ // TriageHit ids judged false-positive in this category. fpCount must equal
17586
+ // this array's length — enforced by the consumer, not this schema.
17587
+ fpIds: external_exports.array(external_exports.string())
17588
+ });
17589
+ var TriageRecommendation = external_exports.object({
17590
+ perCategory: external_exports.array(TriageCategoryRec),
17591
+ notes: external_exports.string()
17592
+ });
17593
+
17594
+ // ../../packages/schema/src/zod/setup-frame.ts
17595
+ var CalibrationCounts = external_exports.object({
17596
+ total: external_exports.number().int().nonnegative(),
17597
+ important: external_exports.number().int().nonnegative(),
17598
+ routine: external_exports.number().int().nonnegative()
17599
+ }).refine((c) => c.total === c.important + c.routine, {
17600
+ message: "total must equal important + routine",
17601
+ path: ["total"]
17602
+ });
17603
+ var FalsePositivePatternValue = external_exports.object({
17604
+ ruleId: external_exports.string(),
17605
+ category: DetectionCategory,
17606
+ valueFingerprint: external_exports.string(),
17607
+ keyVersion: external_exports.number().int().nonnegative()
17608
+ });
17609
+ var FalsePositivePatternGroup = external_exports.object({
17610
+ pattern: external_exports.string(),
17611
+ count: external_exports.number().int().nonnegative(),
17612
+ values: external_exports.array(FalsePositivePatternValue).min(1)
17613
+ });
17614
+ var CalibrationFindingKind = external_exports.object({
17615
+ category: DetectionCategory,
17616
+ count: external_exports.number().int().nonnegative(),
17617
+ egress: external_exports.boolean()
17618
+ });
17619
+ var CalibrationFrame = external_exports.object({
17620
+ counts: CalibrationCounts,
17621
+ routineCategories: external_exports.array(DetectionCategory),
17622
+ surfacedCategories: external_exports.array(DetectionCategory),
17623
+ findingKinds: external_exports.array(CalibrationFindingKind),
17624
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
17625
+ maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
17626
+ falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
17627
+ });
17628
+ var CalibrationPreviewCategory = TriageCategoryRec.pick({
17629
+ category: true,
17630
+ genuineCount: true,
17631
+ fpCount: true
17632
+ }).extend({
17633
+ egress: external_exports.boolean()
17634
+ });
17635
+ var CalibrationPreview = external_exports.object({
17636
+ categories: external_exports.array(CalibrationPreviewCategory),
17637
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
17638
+ });
17639
+ var CalibrationResult = external_exports.object({
17640
+ frame: CalibrationFrame,
17641
+ copy: external_exports.string()
17642
+ });
17643
+ var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
17644
+ var SetupHandoffOption = external_exports.object({
17645
+ id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
17646
+ label: external_exports.string()
17647
+ });
17648
+ var DashboardHandoffOptions = external_exports.tuple([
17649
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17650
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17651
+ ]);
17652
+ var ComposedRemediationOptions = external_exports.tuple([
17653
+ SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
17654
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17655
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17656
+ ]);
17657
+ var SetupHandoffOffer = external_exports.object({
17658
+ worthALook: external_exports.number().int().nonnegative(),
17659
+ liveKeys: external_exports.number().int().nonnegative().optional(),
17660
+ options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
17661
+ }).refine(
17662
+ (o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
17663
+ {
17664
+ message: "the chain-entry option is present exactly when liveKeys > 0",
17665
+ path: ["options"]
17666
+ }
17667
+ ).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
17668
+ message: "liveKeys is a subset of worthALook and cannot exceed it",
17669
+ path: ["liveKeys"]
17670
+ });
17671
+
17453
17672
  // ../../packages/schema/src/zod/shares.ts
17454
17673
  var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
17455
17674
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
@@ -17635,36 +17854,6 @@ function reviewSeverityRank(reasons) {
17635
17854
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
17636
17855
  }
17637
17856
 
17638
- // ../../packages/schema/src/zod/triage.ts
17639
- var TriageHit = external_exports.object({
17640
- ruleId: external_exports.string(),
17641
- category: DetectionCategory,
17642
- severity: Severity,
17643
- maskedMatch: external_exports.string(),
17644
- rawMatch: external_exports.string(),
17645
- context: external_exports.string(),
17646
- filePath: external_exports.string().optional(),
17647
- confidence: external_exports.number().min(0).max(1),
17648
- id: external_exports.string().optional(),
17649
- valueFingerprint: external_exports.string().optional(),
17650
- keyVersion: external_exports.number().int().nonnegative().optional()
17651
- });
17652
- var TriagePolicy = BuiltinPolicyId;
17653
- var TriageCategoryRec = external_exports.object({
17654
- category: DetectionCategory,
17655
- action: TriagePolicy,
17656
- reasoning: external_exports.string(),
17657
- genuineCount: external_exports.number().int().nonnegative(),
17658
- fpCount: external_exports.number().int().nonnegative(),
17659
- // TriageHit ids judged false-positive in this category. fpCount must equal
17660
- // this array's length — enforced by the consumer, not this schema.
17661
- fpIds: external_exports.array(external_exports.string())
17662
- });
17663
- var TriageRecommendation = external_exports.object({
17664
- perCategory: external_exports.array(TriageCategoryRec),
17665
- notes: external_exports.string()
17666
- });
17667
-
17668
17857
  // ../../packages/persistence/src/internal/sql-text.ts
17669
17858
  function escapeLikePattern(s) {
17670
17859
  return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
@@ -18238,6 +18427,10 @@ var TIMELINE_COLUMNS = `
18238
18427
  json_extract(attributes, '$.internal') AS internal,
18239
18428
  json_extract(attributes, '$.flagged') AS flagged`;
18240
18429
  var SESSION_ROOT = `event_type = 'session'`;
18430
+ var HAS_ACTIVITY = `EXISTS (
18431
+ SELECT 1 FROM audit_events c
18432
+ WHERE c.root_session_id = audit_events.id
18433
+ AND c.event_type NOT IN ('hook', 'config_scan'))`;
18241
18434
  var SqliteActivityRepository = class {
18242
18435
  constructor(db, now = () => Date.now()) {
18243
18436
  this.db = db;
@@ -18251,7 +18444,8 @@ var SqliteActivityRepository = class {
18251
18444
  const sessionsToday = countScalar(
18252
18445
  this.db,
18253
18446
  `SELECT count(*) AS n FROM audit_events
18254
- WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?`,
18447
+ WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?
18448
+ AND ${HAS_ACTIVITY}`,
18255
18449
  [startMs, endMs]
18256
18450
  );
18257
18451
  const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
@@ -18323,6 +18517,13 @@ var SqliteActivityRepository = class {
18323
18517
  );
18324
18518
  params.push(pattern, pattern, pattern, pattern, pattern, pattern);
18325
18519
  }
18520
+ const emptyCount = countScalar(
18521
+ this.db,
18522
+ `SELECT count(*) AS n FROM audit_events
18523
+ WHERE ${[...conditions, `NOT ${HAS_ACTIVITY}`].join(" AND ")}`,
18524
+ params
18525
+ );
18526
+ if (query.excludeEmpty) conditions.push(HAS_ACTIVITY);
18326
18527
  if (cursor) {
18327
18528
  conditions.push("(started_at < ? OR (started_at = ? AND id < ?))");
18328
18529
  params.push(cursor.startedAtMs, cursor.startedAtMs, cursor.id);
@@ -18359,7 +18560,7 @@ var SqliteActivityRepository = class {
18359
18560
  );
18360
18561
  const last = page[page.length - 1];
18361
18562
  const nextCursor = hasMore && last ? encodeCursor({ startedAtMs: last.started_at, id: last.id }) : null;
18362
- return Promise.resolve({ items, nextCursor });
18563
+ return Promise.resolve({ items, nextCursor, emptyCount });
18363
18564
  }
18364
18565
  getSession(sessionId) {
18365
18566
  const rootRow = getRow(
@@ -19665,9 +19866,43 @@ var SqliteFindingsRepository = class {
19665
19866
  }))
19666
19867
  );
19667
19868
  }
19869
+ /** Live-enforced findings recorded for one session — a bare COUNT over the
19870
+ * session-stamped events (served by idx_events_session_id), so the Activity
19871
+ * page can label its findings link without the grouped pipeline. */
19872
+ sessionFindingsCount(sessionId) {
19873
+ if (!sessionId) return Promise.resolve(0);
19874
+ return Promise.resolve(
19875
+ countScalar(
19876
+ this.db,
19877
+ `SELECT count(*) AS n FROM findings f
19878
+ JOIN events e ON e.id = f.event_id
19879
+ WHERE json_extract(e.metadata, '$.sessionId') = :sessionId`,
19880
+ { sessionId }
19881
+ )
19882
+ );
19883
+ }
19884
+ /** Per-rule transcript firing tally for one session — reads the OTHER finding
19885
+ * store (inspection_findings, keyed to audit_events): every detection the
19886
+ * transcript pass recorded, counted per firing rather than per unique value.
19887
+ * Rides on session-scoped grouped responses so the findings view can
19888
+ * reconcile the Activity page's tally with the deduped groups it lists. */
19889
+ sessionFirings(sessionId) {
19890
+ return Object.fromEntries(
19891
+ countBy(
19892
+ this.db,
19893
+ `SELECT d.rule_id AS k, count(*) AS n
19894
+ FROM inspection_findings f
19895
+ JOIN audit_events e ON e.id = f.audit_event_id
19896
+ JOIN inspection_definitions d ON d.id = f.inspection_definition_id
19897
+ WHERE e.root_session_id = :sessionId
19898
+ GROUP BY d.rule_id`,
19899
+ { sessionId }
19900
+ )
19901
+ );
19902
+ }
19668
19903
  /**
19669
- * Grouped findings for the dashboard — joins findings⋈events (repo/file
19670
- * from event metadata), groups by ruleId, computes per-filter-excluded facets,
19904
+ * Grouped findings for the dashboard — joins findings⋈events (repo/file/
19905
+ * toolName from event metadata), groups by ruleId, computes per-filter-excluded facets,
19671
19906
  * applies the requested filters, and sorts by severity then recency. Filtering
19672
19907
  * and faceting run in JS via the shared @akasecurity/schema helpers. `totals`
19673
19908
  * reflect the full filtered set; `items` is the requested
@@ -19684,11 +19919,16 @@ var SqliteFindingsRepository = class {
19684
19919
  * rule is ever restated in SQL.
19685
19920
  */
19686
19921
  listGroupedFindings(query) {
19687
- const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "");
19922
+ const sessionPredicate = query.sessionId ? `WHERE json_extract(e.metadata, '$.sessionId') = :sessionId` : "";
19923
+ const sessionParams = query.sessionId ? { sessionId: query.sessionId } : {};
19924
+ const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
19925
+ predicate: sessionPredicate,
19926
+ params: sessionParams
19927
+ });
19688
19928
  const rows = allRows(
19689
19929
  this.db.prepare(
19690
19930
  `SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
19691
- occurred_at, source_tool, repo, file, kind, finding_key, latest_status
19931
+ occurred_at, source_tool, repo, file, tool_name, kind, finding_key, latest_status
19692
19932
  FROM (
19693
19933
  SELECT f.id AS id, f.rule_id AS rule_id, f.category AS category,
19694
19934
  f.severity AS severity, f.masked_match AS masked_match,
@@ -19696,6 +19936,7 @@ var SqliteFindingsRepository = class {
19696
19936
  e.occurred_at AS occurred_at, e.source_tool AS source_tool,
19697
19937
  json_extract(e.metadata, '$.repo') AS repo,
19698
19938
  json_extract(e.metadata, '$.filePath') AS file,
19939
+ json_extract(e.metadata, '$.toolName') AS tool_name,
19699
19940
  e.kind AS kind, f.finding_key AS finding_key,
19700
19941
  latest.status AS latest_status,
19701
19942
  ROW_NUMBER() OVER (
@@ -19706,11 +19947,12 @@ var SqliteFindingsRepository = class {
19706
19947
  JOIN events e ON e.id = f.event_id
19707
19948
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19708
19949
  ON latest.finding_key = f.finding_key
19950
+ ${sessionPredicate}
19709
19951
  )
19710
19952
  WHERE rn <= :cap
19711
19953
  ORDER BY occurred_at DESC, id DESC`
19712
19954
  ),
19713
- { cap: PREVIEW_INSTANCES_PER_GROUP }
19955
+ { cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
19714
19956
  );
19715
19957
  const groupable = rows.map((r) => ({
19716
19958
  id: r.id,
@@ -19724,6 +19966,7 @@ var SqliteFindingsRepository = class {
19724
19966
  sourceTool: r.source_tool,
19725
19967
  repo: r.repo ?? "",
19726
19968
  file: r.file ?? "",
19969
+ ...r.tool_name === null ? {} : { toolName: r.tool_name },
19727
19970
  status: deriveInstanceStatus(r)
19728
19971
  }));
19729
19972
  const allGroups = buildFindingGroups(groupable, { aggregates });
@@ -19742,7 +19985,13 @@ var SqliteFindingsRepository = class {
19742
19985
  };
19743
19986
  const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
19744
19987
  const items = sorted.slice(0, limit);
19745
- return Promise.resolve({ totals, facets, items, nextCursor: null });
19988
+ return Promise.resolve({
19989
+ totals,
19990
+ facets,
19991
+ items,
19992
+ nextCursor: null,
19993
+ ...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
19994
+ });
19746
19995
  }
19747
19996
  /**
19748
19997
  * One row per rule_id, folding EVERY instance of the group into the values
@@ -19766,9 +20015,10 @@ var SqliteFindingsRepository = class {
19766
20015
  * would silently lose, so it is fetched only when the request actually
19767
20016
  * carries a `q`.
19768
20017
  */
19769
- groupAggregates(withSearchText) {
20018
+ groupAggregates(withSearchText, scope) {
19770
20019
  const searchTextColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.metadata, '$.repo')) AS repos,
19771
- group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files` : `, NULL AS repos, NULL AS files`;
20020
+ group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files,
20021
+ group_concat(DISTINCT 'via ' || json_extract(e.metadata, '$.toolName')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
19772
20022
  const rows = this.db.prepare(
19773
20023
  `SELECT f.rule_id AS rule_id,
19774
20024
  count(*) AS instance_count,
@@ -19785,8 +20035,9 @@ var SqliteFindingsRepository = class {
19785
20035
  JOIN events e ON e.id = f.event_id
19786
20036
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19787
20037
  ON latest.finding_key = f.finding_key
20038
+ ${scope.predicate}
19788
20039
  GROUP BY f.rule_id`
19789
- ).all();
20040
+ ).all(scope.params);
19790
20041
  return new Map(
19791
20042
  rows.map((r) => [
19792
20043
  r.rule_id,
@@ -19810,7 +20061,9 @@ var SqliteFindingsRepository = class {
19810
20061
  // Left undefined (not '') when unfetched, so buildFindingGroups can
19811
20062
  // tell "no q this request" from "a group with no repo/file at all"
19812
20063
  // and skip priming a haystack nothing will read.
19813
- ...withSearchText ? { searchText: [r.repos ?? "", r.files ?? ""].filter((s) => s !== "").join(" ") } : {}
20064
+ ...withSearchText ? {
20065
+ searchText: [r.repos ?? "", r.files ?? "", r.tool_names ?? ""].filter((s) => s !== "").join(" ")
20066
+ } : {}
19814
20067
  }
19815
20068
  ])
19816
20069
  );
@@ -21546,7 +21799,6 @@ var SqliteScanLedgerRepository = class {
21546
21799
  // ../../packages/persistence/src/repositories/security.ts
21547
21800
  var DAY_MS4 = 864e5;
21548
21801
  var SEVERITIES = ["critical", "high", "medium", "low"];
21549
- var RANGE_DAYS = { "7d": 7, "30d": 30, "3m": 90, "6m": 180 };
21550
21802
  var ACTION_TO_KIND = {
21551
21803
  block: "blocked",
21552
21804
  redact: "redacted",
@@ -21561,8 +21813,14 @@ var SCAN_COVERAGE = [
21561
21813
  { provider: "copilot", coverage: 0, supported: false },
21562
21814
  { provider: "api", coverage: 0, supported: false }
21563
21815
  ];
21816
+ var GRANULARITY = {
21817
+ "7d": "day",
21818
+ "30d": "day",
21819
+ "3m": "week",
21820
+ "6m": "week"
21821
+ };
21564
21822
  function granularityFor(range) {
21565
- return range === "7d" || range === "30d" ? "day" : "week";
21823
+ return GRANULARITY[range];
21566
21824
  }
21567
21825
  function startOfUtcDay2(ms) {
21568
21826
  return Math.floor(ms / DAY_MS4) * DAY_MS4;
@@ -22752,21 +23010,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
22752
23010
  import { homedir as homedir2 } from "os";
22753
23011
  import { basename as basename3, join as join7 } from "path";
22754
23012
 
23013
+ // ../../packages/detections/src/escape-regexp.ts
23014
+ function escapeRegExp(value) {
23015
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23016
+ }
23017
+
23018
+ // ../../packages/detections/src/matchers/limits.ts
23019
+ var MAX_MATCHES_PER_RULE = 1e4;
23020
+
22755
23021
  // ../../packages/detections/src/matchers/keyword.ts
22756
23022
  var KeywordMatcher2 = class {
22757
23023
  match(text, rule) {
22758
23024
  if (rule.matcher.type !== "keyword") return [];
22759
23025
  const { keywords, caseSensitive } = rule.matcher;
22760
- const haystack = caseSensitive ? text : text.toLowerCase();
22761
23026
  const spans = [];
22762
23027
  for (const kw of keywords) {
22763
- const needle = caseSensitive ? kw : kw.toLowerCase();
22764
- let idx = 0;
22765
- while (idx < haystack.length) {
22766
- const pos = haystack.indexOf(needle, idx);
22767
- if (pos === -1) break;
22768
- spans.push({ start: pos, end: pos + kw.length });
22769
- idx = pos + 1;
23028
+ if (kw.length === 0) continue;
23029
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
23030
+ const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
23031
+ let m;
23032
+ while ((m = re.exec(text)) !== null) {
23033
+ spans.push({ start: m.index, end: m.index + m[0].length });
23034
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
22770
23035
  }
22771
23036
  }
22772
23037
  return spans;
@@ -22774,7 +23039,6 @@ var KeywordMatcher2 = class {
22774
23039
  };
22775
23040
 
22776
23041
  // ../../packages/detections/src/matchers/regex.ts
22777
- var MAX_MATCHES_PER_RULE = 1e4;
22778
23042
  var RegexMatcher2 = class {
22779
23043
  match(text, rule) {
22780
23044
  if (rule.matcher.type !== "regex") return [];
@@ -22860,9 +23124,6 @@ function registerPack(pack) {
22860
23124
  function getLoadedRules() {
22861
23125
  return [...packs.values()].flatMap((p) => p.rules);
22862
23126
  }
22863
- function escapeRegExp(value) {
22864
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22865
- }
22866
23127
  function isCorroborated(candidate, candidates, text) {
22867
23128
  const req = candidate.rule.requiresNearby;
22868
23129
  if (!req) return true;
@@ -23726,7 +23987,6 @@ var db_table_name_default = {
23726
23987
  "SELECT * FROM ",
23727
23988
  "SELECT COUNT(*) FROM ",
23728
23989
  "INSERT INTO ",
23729
- "UPDATE ",
23730
23990
  "DELETE FROM ",
23731
23991
  "CREATE TABLE ",
23732
23992
  "ALTER TABLE ",