@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
@@ -15786,6 +15811,10 @@ var ListActivitySessionsQuery = external_exports.object({
15786
15811
  from: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15787
15812
  /** Upper bound on startedAt; omitted defaults to now. */
15788
15813
  to: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15814
+ /** Exclude zero-activity sessions — roots whose only recorded children are
15815
+ * bookkeeping rows (hooks, config scans), typically background `claude`
15816
+ * launches. Omitted = list everything. `z.stringbool()` per the note above. */
15817
+ excludeEmpty: external_exports.stringbool().optional(),
15789
15818
  /** Page size, 1–100; out-of-range values are a 400. `z.coerce` — query params arrive as strings. */
15790
15819
  limit: external_exports.coerce.number().int().min(1).max(100).default(50),
15791
15820
  /** Opaque pagination cursor (most-recent first). */
@@ -15794,7 +15823,11 @@ var ListActivitySessionsQuery = external_exports.object({
15794
15823
  var ListActivitySessionsResponse = external_exports.object({
15795
15824
  items: external_exports.array(ActivitySessionSummary),
15796
15825
  /** `null` once the last page is reached. */
15797
- nextCursor: external_exports.string().nullable()
15826
+ nextCursor: external_exports.string().nullable(),
15827
+ /** Zero-activity sessions matching the query's filters/range (whether or
15828
+ * not `excludeEmpty` dropped them from `items`) — the count a UI toggle
15829
+ * shows when collapsing them. */
15830
+ emptyCount: external_exports.number().int().nonnegative()
15798
15831
  }).meta({ id: "ListActivitySessionsResponse" });
15799
15832
  var ListSessionEventsQuery = external_exports.object({
15800
15833
  /** Default 100, range 1–500. */
@@ -15829,6 +15862,12 @@ var EventMetadata = external_exports.object({
15829
15862
  sessionId: external_exports.string().optional(),
15830
15863
  repo: external_exports.string().optional(),
15831
15864
  filePath: external_exports.string().optional(),
15865
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
15866
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
15867
+ // arguments or output, which can carry the very value a finding masked
15868
+ // (metadata is stored unredacted). Gives findings on non-file captures a
15869
+ // display location ("via Bash") when no filePath exists.
15870
+ toolName: external_exports.string().optional(),
15832
15871
  // Set (true) by the worktree scanner when the file is excluded by the
15833
15872
  // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
15834
15873
  // generated code can leak real secrets — but the provenance is recorded so
@@ -16160,7 +16199,10 @@ var ExceptionBundleEntry = DetectionException.pick({
16160
16199
  var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16161
16200
  var KeywordMatcher = external_exports.object({
16162
16201
  type: external_exports.literal("keyword"),
16163
- keywords: external_exports.array(external_exports.string()).min(1),
16202
+ // An empty keyword matches at every position, yielding one zero-length span
16203
+ // per character. Rejected here because a keyword that matches everything is
16204
+ // never intentional.
16205
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16164
16206
  caseSensitive: external_exports.boolean().default(false)
16165
16207
  });
16166
16208
  function isValidRegex(pattern, flags2) {
@@ -16414,6 +16456,10 @@ var ListEventsResponse = external_exports.object({
16414
16456
  items: external_exports.array(Event),
16415
16457
  nextCursor: external_exports.string().nullable()
16416
16458
  }).meta({ id: "ListEventsResponse" });
16459
+ var IngestResponse = external_exports.object({
16460
+ accepted: external_exports.number().int().nonnegative(),
16461
+ duplicates: external_exports.number().int().nonnegative()
16462
+ }).meta({ id: "IngestResponse" });
16417
16463
  var ListFindingsQuery = external_exports.object({
16418
16464
  cursor: external_exports.string().optional(),
16419
16465
  limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
@@ -16803,6 +16849,7 @@ function buildFindingGroups(rows, opts = {}) {
16803
16849
  provider: toApiProvider(r.sourceTool),
16804
16850
  repo: r.repo,
16805
16851
  file: r.file,
16852
+ ...r.toolName === void 0 ? {} : { toolName: r.toolName },
16806
16853
  action: toApiAction(effectiveDbAction),
16807
16854
  detectedAt: r.occurredAt,
16808
16855
  confidence: r.confidence,
@@ -16875,6 +16922,7 @@ function buildHaystack(g, extra) {
16875
16922
  g.id,
16876
16923
  ...g.instances.map((i) => i.repo),
16877
16924
  ...g.instances.map((i) => i.file),
16925
+ ...g.instances.map((i) => i.toolName ? `via ${i.toolName}` : ""),
16878
16926
  ...g.instances.map((i) => i.id),
16879
16927
  ...extra === void 0 ? [] : [extra]
16880
16928
  ].join(" ").toLowerCase();
@@ -17141,6 +17189,71 @@ var ProjectFilesScan = external_exports.object({
17141
17189
  scannedAt: external_exports.string()
17142
17190
  });
17143
17191
 
17192
+ // ../../packages/schema/src/zod/ranges.ts
17193
+ var TIME_RANGES = ["7d", "30d", "3m", "6m"];
17194
+ var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
17195
+ var DEFAULT_TIME_RANGE = "7d";
17196
+ var RANGE_DAYS = {
17197
+ "7d": 7,
17198
+ "30d": 30,
17199
+ "3m": 90,
17200
+ "6m": 180
17201
+ };
17202
+ var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
17203
+
17204
+ // ../../packages/schema/src/zod/remediation.ts
17205
+ var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
17206
+ var MaskedFindingLocation = external_exports.object({
17207
+ filePath: external_exports.string(),
17208
+ span: Span.optional()
17209
+ }).strict();
17210
+ var MaskedSecretFinding = external_exports.object({
17211
+ provider: external_exports.string(),
17212
+ maskedToken: external_exports.string(),
17213
+ where: MaskedFindingLocation,
17214
+ state: SecretFindingState,
17215
+ observedAt: external_exports.iso.datetime().optional()
17216
+ }).strict();
17217
+ var RotationChecklistEntry = external_exports.object({
17218
+ provider: external_exports.string(),
17219
+ maskedToken: external_exports.string(),
17220
+ consolePath: external_exports.string(),
17221
+ occurrenceSpread: external_exports.number().int().positive()
17222
+ }).strict();
17223
+ var RemediationOption = external_exports.enum([
17224
+ "redact-rotation-checklist",
17225
+ "redact-only",
17226
+ "set-secret-redact",
17227
+ "leave"
17228
+ ]);
17229
+ var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
17230
+ var RemediationEntryContext = external_exports.object({
17231
+ entrySource: RemediationEntrySource
17232
+ }).strict();
17233
+ var RemediationOptionChoice = external_exports.object({
17234
+ id: RemediationOption,
17235
+ label: external_exports.string()
17236
+ });
17237
+ var BatchedRemediationDecision = external_exports.object({
17238
+ kind: external_exports.literal("decision"),
17239
+ entrySource: RemediationEntrySource,
17240
+ secretCount: external_exports.number().int().positive(),
17241
+ prompt: external_exports.string(),
17242
+ options: external_exports.tuple([
17243
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
17244
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
17245
+ RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
17246
+ RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
17247
+ ])
17248
+ });
17249
+ var NoRemediationDecision = external_exports.object({
17250
+ kind: external_exports.literal("no-decision")
17251
+ });
17252
+ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
17253
+ BatchedRemediationDecision,
17254
+ NoRemediationDecision
17255
+ ]);
17256
+
17144
17257
  // ../../packages/schema/src/zod/rule-test.ts
17145
17258
  var TestRulesRequest = external_exports.object({
17146
17259
  rules: external_exports.array(Rule).min(1).max(100),
@@ -17199,10 +17312,8 @@ var SeveritySummaryResponse = external_exports.object({
17199
17312
  // All four severity levels are always present (count may be 0).
17200
17313
  bySeverity: external_exports.array(SeveritySummaryItem)
17201
17314
  }).meta({ id: "SeveritySummaryResponse" });
17202
- var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
17203
- var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
17204
17315
  var SecurityRangeQuery = external_exports.object({
17205
- range: external_exports.enum(SECURITY_RANGES).default("30d")
17316
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
17206
17317
  });
17207
17318
  var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
17208
17319
  var EnforcementAction = external_exports.object({
@@ -17212,7 +17323,7 @@ var EnforcementAction = external_exports.object({
17212
17323
  delta: external_exports.number().int()
17213
17324
  }).meta({ id: "EnforcementAction" });
17214
17325
  var EnforcementActionsResponse = external_exports.object({
17215
- range: SecurityRange,
17326
+ range: TimeRange,
17216
17327
  // Sum of actions[].count in the window.
17217
17328
  total: external_exports.number().int().nonnegative(),
17218
17329
  // One entry per kind, always all three present (count may be 0).
@@ -17227,7 +17338,7 @@ var FindingsTimeseriesPoint = external_exports.object({
17227
17338
  medium: external_exports.number().int().nonnegative()
17228
17339
  }).meta({ id: "FindingsTimeseriesPoint" });
17229
17340
  var FindingsTimeseriesResponse = external_exports.object({
17230
- range: SecurityRange,
17341
+ range: TimeRange,
17231
17342
  granularity: TimeseriesGranularity,
17232
17343
  points: external_exports.array(FindingsTimeseriesPoint)
17233
17344
  }).meta({ id: "FindingsTimeseriesResponse" });
@@ -17242,7 +17353,7 @@ var MttrTrendPoint = external_exports.object({
17242
17353
  })
17243
17354
  }).meta({ id: "MttrTrendPoint" });
17244
17355
  var MttrTrendResponse = external_exports.object({
17245
- range: SecurityRange,
17356
+ range: TimeRange,
17246
17357
  granularity: TimeseriesGranularity,
17247
17358
  points: external_exports.array(MttrTrendPoint)
17248
17359
  }).meta({ id: "MttrTrendResponse" });
@@ -17269,11 +17380,11 @@ var TopSource = external_exports.object({
17269
17380
  findingsCount: external_exports.number().int().nonnegative()
17270
17381
  }).meta({ id: "TopSource" });
17271
17382
  var TopSourcesResponse = external_exports.object({
17272
- range: SecurityRange,
17383
+ range: TimeRange,
17273
17384
  items: external_exports.array(TopSource)
17274
17385
  }).meta({ id: "TopSourcesResponse" });
17275
17386
  var TopSourcesQuery = external_exports.object({
17276
- range: external_exports.enum(SECURITY_RANGES).default("30d"),
17387
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
17277
17388
  limit: external_exports.coerce.number().int().min(1).max(50).default(5),
17278
17389
  // Omit for both kinds.
17279
17390
  kind: external_exports.enum(SOURCE_KINDS).optional()
@@ -17286,7 +17397,7 @@ var ScanCoverageProvider = external_exports.object({
17286
17397
  supported: external_exports.boolean()
17287
17398
  }).meta({ id: "ScanCoverageProvider" });
17288
17399
  var ScanCoverageResponse = external_exports.object({
17289
- range: SecurityRange,
17400
+ range: TimeRange,
17290
17401
  providers: external_exports.array(ScanCoverageProvider)
17291
17402
  }).meta({ id: "ScanCoverageResponse" });
17292
17403
  var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
@@ -17335,6 +17446,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
17335
17446
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
17336
17447
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
17337
17448
 
17449
+ // ../../packages/schema/src/zod/triage.ts
17450
+ var TriageHit = external_exports.object({
17451
+ ruleId: external_exports.string(),
17452
+ category: DetectionCategory,
17453
+ severity: Severity,
17454
+ maskedMatch: external_exports.string(),
17455
+ rawMatch: external_exports.string(),
17456
+ context: external_exports.string(),
17457
+ filePath: external_exports.string().optional(),
17458
+ confidence: external_exports.number().min(0).max(1),
17459
+ id: external_exports.string().optional(),
17460
+ valueFingerprint: external_exports.string().optional(),
17461
+ keyVersion: external_exports.number().int().nonnegative().optional()
17462
+ });
17463
+ var TriagePolicy = BuiltinPolicyId;
17464
+ var TriageCategoryRec = external_exports.object({
17465
+ category: DetectionCategory,
17466
+ action: TriagePolicy,
17467
+ reasoning: external_exports.string(),
17468
+ genuineCount: external_exports.number().int().nonnegative(),
17469
+ fpCount: external_exports.number().int().nonnegative(),
17470
+ // TriageHit ids judged false-positive in this category. fpCount must equal
17471
+ // this array's length — enforced by the consumer, not this schema.
17472
+ fpIds: external_exports.array(external_exports.string())
17473
+ });
17474
+ var TriageRecommendation = external_exports.object({
17475
+ perCategory: external_exports.array(TriageCategoryRec),
17476
+ notes: external_exports.string()
17477
+ });
17478
+
17479
+ // ../../packages/schema/src/zod/setup-frame.ts
17480
+ var CalibrationCounts = external_exports.object({
17481
+ total: external_exports.number().int().nonnegative(),
17482
+ important: external_exports.number().int().nonnegative(),
17483
+ routine: external_exports.number().int().nonnegative()
17484
+ }).refine((c) => c.total === c.important + c.routine, {
17485
+ message: "total must equal important + routine",
17486
+ path: ["total"]
17487
+ });
17488
+ var FalsePositivePatternValue = external_exports.object({
17489
+ ruleId: external_exports.string(),
17490
+ category: DetectionCategory,
17491
+ valueFingerprint: external_exports.string(),
17492
+ keyVersion: external_exports.number().int().nonnegative()
17493
+ });
17494
+ var FalsePositivePatternGroup = external_exports.object({
17495
+ pattern: external_exports.string(),
17496
+ count: external_exports.number().int().nonnegative(),
17497
+ values: external_exports.array(FalsePositivePatternValue).min(1)
17498
+ });
17499
+ var CalibrationFindingKind = external_exports.object({
17500
+ category: DetectionCategory,
17501
+ count: external_exports.number().int().nonnegative(),
17502
+ egress: external_exports.boolean()
17503
+ });
17504
+ var CalibrationFrame = external_exports.object({
17505
+ counts: CalibrationCounts,
17506
+ routineCategories: external_exports.array(DetectionCategory),
17507
+ surfacedCategories: external_exports.array(DetectionCategory),
17508
+ findingKinds: external_exports.array(CalibrationFindingKind),
17509
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
17510
+ maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
17511
+ falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
17512
+ });
17513
+ var CalibrationPreviewCategory = TriageCategoryRec.pick({
17514
+ category: true,
17515
+ genuineCount: true,
17516
+ fpCount: true
17517
+ }).extend({
17518
+ egress: external_exports.boolean()
17519
+ });
17520
+ var CalibrationPreview = external_exports.object({
17521
+ categories: external_exports.array(CalibrationPreviewCategory),
17522
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
17523
+ });
17524
+ var CalibrationResult = external_exports.object({
17525
+ frame: CalibrationFrame,
17526
+ copy: external_exports.string()
17527
+ });
17528
+ var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
17529
+ var SetupHandoffOption = external_exports.object({
17530
+ id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
17531
+ label: external_exports.string()
17532
+ });
17533
+ var DashboardHandoffOptions = external_exports.tuple([
17534
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17535
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17536
+ ]);
17537
+ var ComposedRemediationOptions = external_exports.tuple([
17538
+ SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
17539
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
17540
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
17541
+ ]);
17542
+ var SetupHandoffOffer = external_exports.object({
17543
+ worthALook: external_exports.number().int().nonnegative(),
17544
+ liveKeys: external_exports.number().int().nonnegative().optional(),
17545
+ options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
17546
+ }).refine(
17547
+ (o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
17548
+ {
17549
+ message: "the chain-entry option is present exactly when liveKeys > 0",
17550
+ path: ["options"]
17551
+ }
17552
+ ).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
17553
+ message: "liveKeys is a subset of worthALook and cannot exceed it",
17554
+ path: ["liveKeys"]
17555
+ });
17556
+
17338
17557
  // ../../packages/schema/src/zod/shares.ts
17339
17558
  var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
17340
17559
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
@@ -17520,36 +17739,6 @@ function reviewSeverityRank(reasons) {
17520
17739
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
17521
17740
  }
17522
17741
 
17523
- // ../../packages/schema/src/zod/triage.ts
17524
- var TriageHit = external_exports.object({
17525
- ruleId: external_exports.string(),
17526
- category: DetectionCategory,
17527
- severity: Severity,
17528
- maskedMatch: external_exports.string(),
17529
- rawMatch: external_exports.string(),
17530
- context: external_exports.string(),
17531
- filePath: external_exports.string().optional(),
17532
- confidence: external_exports.number().min(0).max(1),
17533
- id: external_exports.string().optional(),
17534
- valueFingerprint: external_exports.string().optional(),
17535
- keyVersion: external_exports.number().int().nonnegative().optional()
17536
- });
17537
- var TriagePolicy = BuiltinPolicyId;
17538
- var TriageCategoryRec = external_exports.object({
17539
- category: DetectionCategory,
17540
- action: TriagePolicy,
17541
- reasoning: external_exports.string(),
17542
- genuineCount: external_exports.number().int().nonnegative(),
17543
- fpCount: external_exports.number().int().nonnegative(),
17544
- // TriageHit ids judged false-positive in this category. fpCount must equal
17545
- // this array's length — enforced by the consumer, not this schema.
17546
- fpIds: external_exports.array(external_exports.string())
17547
- });
17548
- var TriageRecommendation = external_exports.object({
17549
- perCategory: external_exports.array(TriageCategoryRec),
17550
- notes: external_exports.string()
17551
- });
17552
-
17553
17742
  // ../../packages/persistence/src/internal/sql-text.ts
17554
17743
  function escapeLikePattern(s) {
17555
17744
  return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
@@ -17634,9 +17823,9 @@ function schemaObjectExists(db, kind, name) {
17634
17823
  function indexExists(db, name) {
17635
17824
  return schemaObjectExists(db, "index", name);
17636
17825
  }
17637
- function columnNames(db, table, opts) {
17826
+ function columnNames(db, table2, opts) {
17638
17827
  const pragma = opts?.includeGenerated ? "table_xinfo" : "table_info";
17639
- const columns = db.prepare(`PRAGMA ${pragma}(${table})`).all();
17828
+ const columns = db.prepare(`PRAGMA ${pragma}(${table2})`).all();
17640
17829
  return columns.map((c) => c.name);
17641
17830
  }
17642
17831
  function evidenceExists(db, object2) {
@@ -17804,12 +17993,12 @@ function reconcileSourceProjectIds(db) {
17804
17993
  const repointCallSite = db.prepare(
17805
17994
  "UPDATE share_call_site SET project_id = ? WHERE project_id = ?"
17806
17995
  );
17807
- const pathTables = ["project_file", "file_access_override"].map((table) => ({
17996
+ const pathTables = ["project_file", "file_access_override"].map((table2) => ({
17808
17997
  dropCollisions: db.prepare(
17809
- `DELETE FROM ${table} WHERE project_id = ?
17810
- AND path IN (SELECT path FROM ${table} WHERE project_id = ?)`
17998
+ `DELETE FROM ${table2} WHERE project_id = ?
17999
+ AND path IN (SELECT path FROM ${table2} WHERE project_id = ?)`
17811
18000
  ),
17812
- repoint: db.prepare(`UPDATE ${table} SET project_id = ? WHERE project_id = ?`)
18001
+ repoint: db.prepare(`UPDATE ${table2} SET project_id = ? WHERE project_id = ?`)
17813
18002
  }));
17814
18003
  const deleteLegacy = db.prepare("DELETE FROM source_project WHERE id = ?");
17815
18004
  withTransaction(
@@ -17843,9 +18032,9 @@ function isForeignSqliteLineage(db) {
17843
18032
  if (schemaObjectExists(db, "table", "tenants")) return true;
17844
18033
  return columnNames(db, "events").includes("tenant_id");
17845
18034
  }
17846
- function ensureSyncedAtColumn(db, table) {
17847
- if (!columnNames(db, table).includes("synced_at")) {
17848
- db.exec(`ALTER TABLE ${table} ADD COLUMN synced_at integer`);
18035
+ function ensureSyncedAtColumn(db, table2) {
18036
+ if (!columnNames(db, table2).includes("synced_at")) {
18037
+ db.exec(`ALTER TABLE ${table2} ADD COLUMN synced_at integer`);
17849
18038
  }
17850
18039
  }
17851
18040
  function ensureScanLedgerTable(db) {
@@ -18112,6 +18301,10 @@ var TIMELINE_COLUMNS = `
18112
18301
  json_extract(attributes, '$.internal') AS internal,
18113
18302
  json_extract(attributes, '$.flagged') AS flagged`;
18114
18303
  var SESSION_ROOT = `event_type = 'session'`;
18304
+ var HAS_ACTIVITY = `EXISTS (
18305
+ SELECT 1 FROM audit_events c
18306
+ WHERE c.root_session_id = audit_events.id
18307
+ AND c.event_type NOT IN ('hook', 'config_scan'))`;
18115
18308
  var SqliteActivityRepository = class {
18116
18309
  constructor(db, now = () => Date.now()) {
18117
18310
  this.db = db;
@@ -18125,7 +18318,8 @@ var SqliteActivityRepository = class {
18125
18318
  const sessionsToday = countScalar(
18126
18319
  this.db,
18127
18320
  `SELECT count(*) AS n FROM audit_events
18128
- WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?`,
18321
+ WHERE ${SESSION_ROOT} AND started_at >= ? AND started_at < ?
18322
+ AND ${HAS_ACTIVITY}`,
18129
18323
  [startMs, endMs]
18130
18324
  );
18131
18325
  const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
@@ -18197,6 +18391,13 @@ var SqliteActivityRepository = class {
18197
18391
  );
18198
18392
  params.push(pattern, pattern, pattern, pattern, pattern, pattern);
18199
18393
  }
18394
+ const emptyCount = countScalar(
18395
+ this.db,
18396
+ `SELECT count(*) AS n FROM audit_events
18397
+ WHERE ${[...conditions, `NOT ${HAS_ACTIVITY}`].join(" AND ")}`,
18398
+ params
18399
+ );
18400
+ if (query.excludeEmpty) conditions.push(HAS_ACTIVITY);
18200
18401
  if (cursor) {
18201
18402
  conditions.push("(started_at < ? OR (started_at = ? AND id < ?))");
18202
18403
  params.push(cursor.startedAtMs, cursor.startedAtMs, cursor.id);
@@ -18233,7 +18434,7 @@ var SqliteActivityRepository = class {
18233
18434
  );
18234
18435
  const last = page[page.length - 1];
18235
18436
  const nextCursor = hasMore && last ? encodeCursor({ startedAtMs: last.started_at, id: last.id }) : null;
18236
- return Promise.resolve({ items, nextCursor });
18437
+ return Promise.resolve({ items, nextCursor, emptyCount });
18237
18438
  }
18238
18439
  getSession(sessionId) {
18239
18440
  const rootRow = getRow(
@@ -19539,9 +19740,43 @@ var SqliteFindingsRepository = class {
19539
19740
  }))
19540
19741
  );
19541
19742
  }
19743
+ /** Live-enforced findings recorded for one session — a bare COUNT over the
19744
+ * session-stamped events (served by idx_events_session_id), so the Activity
19745
+ * page can label its findings link without the grouped pipeline. */
19746
+ sessionFindingsCount(sessionId) {
19747
+ if (!sessionId) return Promise.resolve(0);
19748
+ return Promise.resolve(
19749
+ countScalar(
19750
+ this.db,
19751
+ `SELECT count(*) AS n FROM findings f
19752
+ JOIN events e ON e.id = f.event_id
19753
+ WHERE json_extract(e.metadata, '$.sessionId') = :sessionId`,
19754
+ { sessionId }
19755
+ )
19756
+ );
19757
+ }
19758
+ /** Per-rule transcript firing tally for one session — reads the OTHER finding
19759
+ * store (inspection_findings, keyed to audit_events): every detection the
19760
+ * transcript pass recorded, counted per firing rather than per unique value.
19761
+ * Rides on session-scoped grouped responses so the findings view can
19762
+ * reconcile the Activity page's tally with the deduped groups it lists. */
19763
+ sessionFirings(sessionId) {
19764
+ return Object.fromEntries(
19765
+ countBy(
19766
+ this.db,
19767
+ `SELECT d.rule_id AS k, count(*) AS n
19768
+ FROM inspection_findings f
19769
+ JOIN audit_events e ON e.id = f.audit_event_id
19770
+ JOIN inspection_definitions d ON d.id = f.inspection_definition_id
19771
+ WHERE e.root_session_id = :sessionId
19772
+ GROUP BY d.rule_id`,
19773
+ { sessionId }
19774
+ )
19775
+ );
19776
+ }
19542
19777
  /**
19543
- * Grouped findings for the dashboard — joins findings⋈events (repo/file
19544
- * from event metadata), groups by ruleId, computes per-filter-excluded facets,
19778
+ * Grouped findings for the dashboard — joins findings⋈events (repo/file/
19779
+ * toolName from event metadata), groups by ruleId, computes per-filter-excluded facets,
19545
19780
  * applies the requested filters, and sorts by severity then recency. Filtering
19546
19781
  * and faceting run in JS via the shared @akasecurity/schema helpers. `totals`
19547
19782
  * reflect the full filtered set; `items` is the requested
@@ -19558,11 +19793,16 @@ var SqliteFindingsRepository = class {
19558
19793
  * rule is ever restated in SQL.
19559
19794
  */
19560
19795
  listGroupedFindings(query) {
19561
- const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "");
19796
+ const sessionPredicate = query.sessionId ? `WHERE json_extract(e.metadata, '$.sessionId') = :sessionId` : "";
19797
+ const sessionParams = query.sessionId ? { sessionId: query.sessionId } : {};
19798
+ const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
19799
+ predicate: sessionPredicate,
19800
+ params: sessionParams
19801
+ });
19562
19802
  const rows = allRows(
19563
19803
  this.db.prepare(
19564
19804
  `SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
19565
- occurred_at, source_tool, repo, file, kind, finding_key, latest_status
19805
+ occurred_at, source_tool, repo, file, tool_name, kind, finding_key, latest_status
19566
19806
  FROM (
19567
19807
  SELECT f.id AS id, f.rule_id AS rule_id, f.category AS category,
19568
19808
  f.severity AS severity, f.masked_match AS masked_match,
@@ -19570,6 +19810,7 @@ var SqliteFindingsRepository = class {
19570
19810
  e.occurred_at AS occurred_at, e.source_tool AS source_tool,
19571
19811
  json_extract(e.metadata, '$.repo') AS repo,
19572
19812
  json_extract(e.metadata, '$.filePath') AS file,
19813
+ json_extract(e.metadata, '$.toolName') AS tool_name,
19573
19814
  e.kind AS kind, f.finding_key AS finding_key,
19574
19815
  latest.status AS latest_status,
19575
19816
  ROW_NUMBER() OVER (
@@ -19580,11 +19821,12 @@ var SqliteFindingsRepository = class {
19580
19821
  JOIN events e ON e.id = f.event_id
19581
19822
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19582
19823
  ON latest.finding_key = f.finding_key
19824
+ ${sessionPredicate}
19583
19825
  )
19584
19826
  WHERE rn <= :cap
19585
19827
  ORDER BY occurred_at DESC, id DESC`
19586
19828
  ),
19587
- { cap: PREVIEW_INSTANCES_PER_GROUP }
19829
+ { cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
19588
19830
  );
19589
19831
  const groupable = rows.map((r) => ({
19590
19832
  id: r.id,
@@ -19598,6 +19840,7 @@ var SqliteFindingsRepository = class {
19598
19840
  sourceTool: r.source_tool,
19599
19841
  repo: r.repo ?? "",
19600
19842
  file: r.file ?? "",
19843
+ ...r.tool_name === null ? {} : { toolName: r.tool_name },
19601
19844
  status: deriveInstanceStatus(r)
19602
19845
  }));
19603
19846
  const allGroups = buildFindingGroups(groupable, { aggregates });
@@ -19616,7 +19859,13 @@ var SqliteFindingsRepository = class {
19616
19859
  };
19617
19860
  const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
19618
19861
  const items = sorted.slice(0, limit);
19619
- return Promise.resolve({ totals, facets, items, nextCursor: null });
19862
+ return Promise.resolve({
19863
+ totals,
19864
+ facets,
19865
+ items,
19866
+ nextCursor: null,
19867
+ ...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
19868
+ });
19620
19869
  }
19621
19870
  /**
19622
19871
  * One row per rule_id, folding EVERY instance of the group into the values
@@ -19640,9 +19889,10 @@ var SqliteFindingsRepository = class {
19640
19889
  * would silently lose, so it is fetched only when the request actually
19641
19890
  * carries a `q`.
19642
19891
  */
19643
- groupAggregates(withSearchText) {
19892
+ groupAggregates(withSearchText, scope) {
19644
19893
  const searchTextColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.metadata, '$.repo')) AS repos,
19645
- group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files` : `, NULL AS repos, NULL AS files`;
19894
+ group_concat(DISTINCT json_extract(e.metadata, '$.filePath')) AS files,
19895
+ group_concat(DISTINCT 'via ' || json_extract(e.metadata, '$.toolName')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
19646
19896
  const rows = this.db.prepare(
19647
19897
  `SELECT f.rule_id AS rule_id,
19648
19898
  count(*) AS instance_count,
@@ -19659,8 +19909,9 @@ var SqliteFindingsRepository = class {
19659
19909
  JOIN events e ON e.id = f.event_id
19660
19910
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
19661
19911
  ON latest.finding_key = f.finding_key
19912
+ ${scope.predicate}
19662
19913
  GROUP BY f.rule_id`
19663
- ).all();
19914
+ ).all(scope.params);
19664
19915
  return new Map(
19665
19916
  rows.map((r) => [
19666
19917
  r.rule_id,
@@ -19684,7 +19935,9 @@ var SqliteFindingsRepository = class {
19684
19935
  // Left undefined (not '') when unfetched, so buildFindingGroups can
19685
19936
  // tell "no q this request" from "a group with no repo/file at all"
19686
19937
  // and skip priming a haystack nothing will read.
19687
- ...withSearchText ? { searchText: [r.repos ?? "", r.files ?? ""].filter((s) => s !== "").join(" ") } : {}
19938
+ ...withSearchText ? {
19939
+ searchText: [r.repos ?? "", r.files ?? "", r.tool_names ?? ""].filter((s) => s !== "").join(" ")
19940
+ } : {}
19688
19941
  }
19689
19942
  ])
19690
19943
  );
@@ -21420,7 +21673,6 @@ var SqliteScanLedgerRepository = class {
21420
21673
  // ../../packages/persistence/src/repositories/security.ts
21421
21674
  var DAY_MS4 = 864e5;
21422
21675
  var SEVERITIES = ["critical", "high", "medium", "low"];
21423
- var RANGE_DAYS = { "7d": 7, "30d": 30, "3m": 90, "6m": 180 };
21424
21676
  var ACTION_TO_KIND = {
21425
21677
  block: "blocked",
21426
21678
  redact: "redacted",
@@ -21435,8 +21687,14 @@ var SCAN_COVERAGE = [
21435
21687
  { provider: "copilot", coverage: 0, supported: false },
21436
21688
  { provider: "api", coverage: 0, supported: false }
21437
21689
  ];
21690
+ var GRANULARITY = {
21691
+ "7d": "day",
21692
+ "30d": "day",
21693
+ "3m": "week",
21694
+ "6m": "week"
21695
+ };
21438
21696
  function granularityFor(range) {
21439
- return range === "7d" || range === "30d" ? "day" : "week";
21697
+ return GRANULARITY[range];
21440
21698
  }
21441
21699
  function startOfUtcDay2(ms) {
21442
21700
  return Math.floor(ms / DAY_MS4) * DAY_MS4;
@@ -22610,21 +22868,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
22610
22868
  import { homedir as homedir2 } from "os";
22611
22869
  import { basename as basename2, join as join7 } from "path";
22612
22870
 
22871
+ // ../../packages/detections/src/escape-regexp.ts
22872
+ function escapeRegExp(value) {
22873
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22874
+ }
22875
+
22876
+ // ../../packages/detections/src/matchers/limits.ts
22877
+ var MAX_MATCHES_PER_RULE = 1e4;
22878
+
22613
22879
  // ../../packages/detections/src/matchers/keyword.ts
22614
22880
  var KeywordMatcher2 = class {
22615
22881
  match(text, rule) {
22616
22882
  if (rule.matcher.type !== "keyword") return [];
22617
22883
  const { keywords, caseSensitive } = rule.matcher;
22618
- const haystack = caseSensitive ? text : text.toLowerCase();
22619
22884
  const spans = [];
22620
22885
  for (const kw of keywords) {
22621
- const needle = caseSensitive ? kw : kw.toLowerCase();
22622
- let idx = 0;
22623
- while (idx < haystack.length) {
22624
- const pos = haystack.indexOf(needle, idx);
22625
- if (pos === -1) break;
22626
- spans.push({ start: pos, end: pos + kw.length });
22627
- idx = pos + 1;
22886
+ if (kw.length === 0) continue;
22887
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
22888
+ const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
22889
+ let m;
22890
+ while ((m = re.exec(text)) !== null) {
22891
+ spans.push({ start: m.index, end: m.index + m[0].length });
22892
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
22628
22893
  }
22629
22894
  }
22630
22895
  return spans;
@@ -22632,7 +22897,6 @@ var KeywordMatcher2 = class {
22632
22897
  };
22633
22898
 
22634
22899
  // ../../packages/detections/src/matchers/regex.ts
22635
- var MAX_MATCHES_PER_RULE = 1e4;
22636
22900
  var RegexMatcher2 = class {
22637
22901
  match(text, rule) {
22638
22902
  if (rule.matcher.type !== "regex") return [];
@@ -22776,6 +23040,68 @@ function parsePosture(json2) {
22776
23040
  return out;
22777
23041
  }
22778
23042
 
23043
+ // src/setup-show.ts
23044
+ var SHOW_BEGIN = "<<<AKA_SHOW";
23045
+ var SHOW_END = "AKA_SHOW>>>";
23046
+ function showBlock(body) {
23047
+ return `${SHOW_BEGIN}
23048
+ ${body}
23049
+ ${SHOW_END}
23050
+ `;
23051
+ }
23052
+
23053
+ // src/present.ts
23054
+ var SHADE = {
23055
+ light: "\u2591",
23056
+ medium: "\u2592",
23057
+ dark: "\u2593",
23058
+ full: "\u2588"
23059
+ };
23060
+ var fg = (hex3) => (text) => {
23061
+ const r = Number.parseInt(hex3.slice(1, 3), 16);
23062
+ const g = Number.parseInt(hex3.slice(3, 5), 16);
23063
+ const b = Number.parseInt(hex3.slice(5, 7), 16);
23064
+ return `\x1B[38;2;${String(r)};${String(g)};${String(b)}m${text}\x1B[0m`;
23065
+ };
23066
+ var paint = {
23067
+ brand: fg("#33e6c6"),
23068
+ // --color-brand · ▸▸ AKA wordmark (accent text)
23069
+ dim: fg("#838995"),
23070
+ // --color-text-3 · separators · "/100" · the "unreviewed" label
23071
+ bold: (text) => `\x1B[1m${text}\x1B[0m`,
23072
+ // the health score number
23073
+ ok: fg("#0db15f"),
23074
+ // --color-ok · healthy ● dot
23075
+ critical: fg("#e63448"),
23076
+ // --color-sev-critical · ■ and the open-findings flag
23077
+ high: fg("#e97a0a"),
23078
+ // --color-sev-high · ■ and the mid-health dot
23079
+ medium: fg("#f7bd00"),
23080
+ // --color-sev-medium · ■
23081
+ low: fg("#0581d4")
23082
+ // --color-sev-low · ■ (azure blue, not purple)
23083
+ };
23084
+ function show(body) {
23085
+ return showBlock(body);
23086
+ }
23087
+
23088
+ // src/command-registry.ts
23089
+ import { readdirSync as readdirSync3 } from "fs";
23090
+ import { fileURLToPath } from "url";
23091
+ var COMMANDS_DIR = fileURLToPath(new URL("../commands", import.meta.url));
23092
+
23093
+ // src/render.ts
23094
+ var SEVERITY_GLYPH = {
23095
+ critical: SHADE.full,
23096
+ high: SHADE.dark,
23097
+ medium: SHADE.medium,
23098
+ low: SHADE.light
23099
+ };
23100
+ var CATEGORY_ORDER2 = DetectionCategory.options;
23101
+ function renderCategoriesTuned(categoriesTuned) {
23102
+ return `\u2713 Set all ${String(categoriesTuned)} detection categories`;
23103
+ }
23104
+
22779
23105
  // src/onboard.ts
22780
23106
  function parseFlags(argv) {
22781
23107
  const flags2 = /* @__PURE__ */ new Map();
@@ -22825,10 +23151,7 @@ if (Object.keys(answers).length === 0 && rawPosture === void 0 && !useFloor) {
22825
23151
  if (Object.keys(answers).length > 0) {
22826
23152
  try {
22827
23153
  const settings = applyOnboarding(answers);
22828
- process.stdout.write(
22829
- `AKA configured: policy=${settings.policy}, historicalAccess=${settings.historicalAccess}. Settings saved to ~/.aka/settings/settings.json.
22830
- `
22831
- );
23154
+ process.stdout.write(show("Got it \u2014 I'll look over Claude's recent work to tune things."));
22832
23155
  try {
22833
23156
  const dataDir2 = loadConfig().dataDir;
22834
23157
  const db = openLocalDatabase(dataDir2);
@@ -22836,8 +23159,9 @@ if (Object.keys(answers).length > 0) {
22836
23159
  const { capped } = capWarnEraEnforcementOnce(db, settings.policy, dataDir2);
22837
23160
  if (capped > 0) {
22838
23161
  process.stdout.write(
22839
- `AKA: kept ${String(capped)} existing block/redact categories at warn (the global "warn only" handling was retired). Confirm per-category enforcement in this setup.
22840
- `
23162
+ show(
23163
+ `I eased ${String(capped)} detection level${capped === 1 ? "" : "s"} back to "warn" to match the new defaults \u2014 you can raise any of them again in this setup.`
23164
+ )
22841
23165
  );
22842
23166
  }
22843
23167
  } finally {
@@ -22846,7 +23170,7 @@ if (Object.keys(answers).length > 0) {
22846
23170
  } catch {
22847
23171
  }
22848
23172
  } catch (err) {
22849
- fail(err instanceof Error ? err.message : "could not write settings.json");
23173
+ fail(err instanceof Error ? err.message : "could not save your settings");
22850
23174
  }
22851
23175
  }
22852
23176
  if (rawPosture !== void 0 || useFloor) {
@@ -22861,8 +23185,7 @@ if (rawPosture !== void 0 || useFloor) {
22861
23185
  applyCategoryPosture(posture, db.policies, mode);
22862
23186
  const categoryCount = Object.keys(posture).length;
22863
23187
  process.stdout.write(
22864
- `AKA per-category posture saved (${String(categoryCount)} categories${useFloor ? ", severity floor" : ""}).
22865
- `
23188
+ show(renderCategoriesTuned(categoryCount) + (useFloor ? " \u2014 safe defaults" : ""))
22866
23189
  );
22867
23190
  } finally {
22868
23191
  db.close();