@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.
package/scripts/stop.js CHANGED
@@ -547,6 +547,12 @@ var PRICE_MAP = {
547
547
  };
548
548
  var CANONICAL_ANTHROPIC_MODELS = Object.keys(ANTHROPIC_PRICES);
549
549
 
550
+ // ../../packages/schema/src/token/format.ts
551
+ var COMPACT = new Intl.NumberFormat("en-US", {
552
+ notation: "compact",
553
+ maximumFractionDigits: 1
554
+ });
555
+
550
556
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
551
557
  var external_exports = {};
552
558
  __export(external_exports, {
@@ -15125,6 +15131,12 @@ var FindingInstance = external_exports.object({
15125
15131
  provider: FindingProvider,
15126
15132
  repo: external_exports.string(),
15127
15133
  file: external_exports.string(),
15134
+ // Host tool that produced the scanned text (event metadata's toolName).
15135
+ // Present whenever the capturing hook recorded one — including
15136
+ // file-attributed captures (views prefer `file`); its display value is
15137
+ // the location fallback ("via Bash") when no filePath exists. Absent for
15138
+ // legacy rows and non-tool captures (prompts, worktree scans).
15139
+ toolName: external_exports.string().optional(),
15128
15140
  // Effective action: override.action ?? actionTaken, translated to FindingAction.
15129
15141
  action: FindingAction,
15130
15142
  detectedAt: external_exports.iso.datetime(),
@@ -15180,6 +15192,9 @@ var ListGroupedFindingsQuery = external_exports.object({
15180
15192
  provider: external_exports.array(FindingProvider).optional(),
15181
15193
  action: external_exports.array(FindingAction).optional(),
15182
15194
  q: external_exports.string().optional(),
15195
+ // Scope to findings whose event carries this session id (the Activity page's
15196
+ // session → findings drilldown). Findings without a session never match.
15197
+ sessionId: external_exports.string().optional(),
15183
15198
  groupBy: external_exports.literal("type").optional(),
15184
15199
  limit: external_exports.coerce.number().int().min(1).max(100).optional(),
15185
15200
  cursor: external_exports.string().optional()
@@ -15191,7 +15206,13 @@ var ListGroupedFindingsResponse = external_exports.object({
15191
15206
  }),
15192
15207
  facets: FindingFacets,
15193
15208
  items: external_exports.array(FindingGroup),
15194
- nextCursor: external_exports.string().nullable()
15209
+ nextCursor: external_exports.string().nullable(),
15210
+ // Present only on session-scoped queries (`sessionId` set): per ruleId, how
15211
+ // many times that rule fired in the session's persisted transcript. Findings
15212
+ // here are deduplicated to unique values while the transcript tally counts
15213
+ // every firing, so the two numbers legitimately differ — this map lets a
15214
+ // session-scoped view show both.
15215
+ sessionFirings: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()).optional()
15195
15216
  }).meta({ id: "ListGroupedFindingsResponse" });
15196
15217
  var ApplyFindingActionRequest = external_exports.object({
15197
15218
  // 'quarantined' is system-assigned (see FindingAction) — clients may not set
@@ -15589,6 +15610,10 @@ var ListActivitySessionsQuery = external_exports.object({
15589
15610
  from: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15590
15611
  /** Upper bound on startedAt; omitted defaults to now. */
15591
15612
  to: external_exports.union([external_exports.iso.date(), external_exports.iso.datetime()]).optional(),
15613
+ /** Exclude zero-activity sessions — roots whose only recorded children are
15614
+ * bookkeeping rows (hooks, config scans), typically background `claude`
15615
+ * launches. Omitted = list everything. `z.stringbool()` per the note above. */
15616
+ excludeEmpty: external_exports.stringbool().optional(),
15592
15617
  /** Page size, 1–100; out-of-range values are a 400. `z.coerce` — query params arrive as strings. */
15593
15618
  limit: external_exports.coerce.number().int().min(1).max(100).default(50),
15594
15619
  /** Opaque pagination cursor (most-recent first). */
@@ -15597,7 +15622,11 @@ var ListActivitySessionsQuery = external_exports.object({
15597
15622
  var ListActivitySessionsResponse = external_exports.object({
15598
15623
  items: external_exports.array(ActivitySessionSummary),
15599
15624
  /** `null` once the last page is reached. */
15600
- nextCursor: external_exports.string().nullable()
15625
+ nextCursor: external_exports.string().nullable(),
15626
+ /** Zero-activity sessions matching the query's filters/range (whether or
15627
+ * not `excludeEmpty` dropped them from `items`) — the count a UI toggle
15628
+ * shows when collapsing them. */
15629
+ emptyCount: external_exports.number().int().nonnegative()
15601
15630
  }).meta({ id: "ListActivitySessionsResponse" });
15602
15631
  var ListSessionEventsQuery = external_exports.object({
15603
15632
  /** Default 100, range 1–500. */
@@ -15632,6 +15661,12 @@ var EventMetadata = external_exports.object({
15632
15661
  sessionId: external_exports.string().optional(),
15633
15662
  repo: external_exports.string().optional(),
15634
15663
  filePath: external_exports.string().optional(),
15664
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
15665
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
15666
+ // arguments or output, which can carry the very value a finding masked
15667
+ // (metadata is stored unredacted). Gives findings on non-file captures a
15668
+ // display location ("via Bash") when no filePath exists.
15669
+ toolName: external_exports.string().optional(),
15635
15670
  // Set (true) by the worktree scanner when the file is excluded by the
15636
15671
  // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
15637
15672
  // generated code can leak real secrets — but the provenance is recorded so
@@ -15963,7 +15998,10 @@ var ExceptionBundleEntry = DetectionException.pick({
15963
15998
  var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
15964
15999
  var KeywordMatcher = external_exports.object({
15965
16000
  type: external_exports.literal("keyword"),
15966
- keywords: external_exports.array(external_exports.string()).min(1),
16001
+ // An empty keyword matches at every position, yielding one zero-length span
16002
+ // per character. Rejected here because a keyword that matches everything is
16003
+ // never intentional.
16004
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
15967
16005
  caseSensitive: external_exports.boolean().default(false)
15968
16006
  });
15969
16007
  function isValidRegex(pattern, flags) {
@@ -16205,6 +16243,10 @@ var ListEventsResponse = external_exports.object({
16205
16243
  items: external_exports.array(Event),
16206
16244
  nextCursor: external_exports.string().nullable()
16207
16245
  }).meta({ id: "ListEventsResponse" });
16246
+ var IngestResponse = external_exports.object({
16247
+ accepted: external_exports.number().int().nonnegative(),
16248
+ duplicates: external_exports.number().int().nonnegative()
16249
+ }).meta({ id: "IngestResponse" });
16208
16250
  var ListFindingsQuery = external_exports.object({
16209
16251
  cursor: external_exports.string().optional(),
16210
16252
  limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
@@ -16528,6 +16570,65 @@ var ProjectFilesScan = external_exports.object({
16528
16570
  scannedAt: external_exports.string()
16529
16571
  });
16530
16572
 
16573
+ // ../../packages/schema/src/zod/ranges.ts
16574
+ var TIME_RANGES = ["7d", "30d", "3m", "6m"];
16575
+ var TimeRange = external_exports.enum(TIME_RANGES).meta({ id: "TimeRange" });
16576
+ var DEFAULT_TIME_RANGE = "7d";
16577
+ var TIME_RANGE_OR_DEFAULT = TimeRange.catch(DEFAULT_TIME_RANGE);
16578
+
16579
+ // ../../packages/schema/src/zod/remediation.ts
16580
+ var SecretFindingState = external_exports.enum(["still-valid", "unknown", "invalid"]);
16581
+ var MaskedFindingLocation = external_exports.object({
16582
+ filePath: external_exports.string(),
16583
+ span: Span.optional()
16584
+ }).strict();
16585
+ var MaskedSecretFinding = external_exports.object({
16586
+ provider: external_exports.string(),
16587
+ maskedToken: external_exports.string(),
16588
+ where: MaskedFindingLocation,
16589
+ state: SecretFindingState,
16590
+ observedAt: external_exports.iso.datetime().optional()
16591
+ }).strict();
16592
+ var RotationChecklistEntry = external_exports.object({
16593
+ provider: external_exports.string(),
16594
+ maskedToken: external_exports.string(),
16595
+ consolePath: external_exports.string(),
16596
+ occurrenceSpread: external_exports.number().int().positive()
16597
+ }).strict();
16598
+ var RemediationOption = external_exports.enum([
16599
+ "redact-rotation-checklist",
16600
+ "redact-only",
16601
+ "set-secret-redact",
16602
+ "leave"
16603
+ ]);
16604
+ var RemediationEntrySource = external_exports.enum(["first-run", "pre-push", "secret-scan"]);
16605
+ var RemediationEntryContext = external_exports.object({
16606
+ entrySource: RemediationEntrySource
16607
+ }).strict();
16608
+ var RemediationOptionChoice = external_exports.object({
16609
+ id: RemediationOption,
16610
+ label: external_exports.string()
16611
+ });
16612
+ var BatchedRemediationDecision = external_exports.object({
16613
+ kind: external_exports.literal("decision"),
16614
+ entrySource: RemediationEntrySource,
16615
+ secretCount: external_exports.number().int().positive(),
16616
+ prompt: external_exports.string(),
16617
+ options: external_exports.tuple([
16618
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-rotation-checklist") }),
16619
+ RemediationOptionChoice.extend({ id: external_exports.literal("redact-only") }),
16620
+ RemediationOptionChoice.extend({ id: external_exports.literal("set-secret-redact") }),
16621
+ RemediationOptionChoice.extend({ id: external_exports.literal("leave") })
16622
+ ])
16623
+ });
16624
+ var NoRemediationDecision = external_exports.object({
16625
+ kind: external_exports.literal("no-decision")
16626
+ });
16627
+ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
16628
+ BatchedRemediationDecision,
16629
+ NoRemediationDecision
16630
+ ]);
16631
+
16531
16632
  // ../../packages/schema/src/zod/rule-test.ts
16532
16633
  var TestRulesRequest = external_exports.object({
16533
16634
  rules: external_exports.array(Rule).min(1).max(100),
@@ -16586,10 +16687,8 @@ var SeveritySummaryResponse = external_exports.object({
16586
16687
  // All four severity levels are always present (count may be 0).
16587
16688
  bySeverity: external_exports.array(SeveritySummaryItem)
16588
16689
  }).meta({ id: "SeveritySummaryResponse" });
16589
- var SECURITY_RANGES = ["7d", "30d", "3m", "6m"];
16590
- var SecurityRange = external_exports.enum(SECURITY_RANGES).meta({ id: "SecurityRange" });
16591
16690
  var SecurityRangeQuery = external_exports.object({
16592
- range: external_exports.enum(SECURITY_RANGES).default("30d")
16691
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE)
16593
16692
  });
16594
16693
  var EnforcementActionKind = external_exports.enum(["blocked", "redacted", "warned"]).meta({ id: "EnforcementActionKind" });
16595
16694
  var EnforcementAction = external_exports.object({
@@ -16599,7 +16698,7 @@ var EnforcementAction = external_exports.object({
16599
16698
  delta: external_exports.number().int()
16600
16699
  }).meta({ id: "EnforcementAction" });
16601
16700
  var EnforcementActionsResponse = external_exports.object({
16602
- range: SecurityRange,
16701
+ range: TimeRange,
16603
16702
  // Sum of actions[].count in the window.
16604
16703
  total: external_exports.number().int().nonnegative(),
16605
16704
  // One entry per kind, always all three present (count may be 0).
@@ -16614,7 +16713,7 @@ var FindingsTimeseriesPoint = external_exports.object({
16614
16713
  medium: external_exports.number().int().nonnegative()
16615
16714
  }).meta({ id: "FindingsTimeseriesPoint" });
16616
16715
  var FindingsTimeseriesResponse = external_exports.object({
16617
- range: SecurityRange,
16716
+ range: TimeRange,
16618
16717
  granularity: TimeseriesGranularity,
16619
16718
  points: external_exports.array(FindingsTimeseriesPoint)
16620
16719
  }).meta({ id: "FindingsTimeseriesResponse" });
@@ -16629,7 +16728,7 @@ var MttrTrendPoint = external_exports.object({
16629
16728
  })
16630
16729
  }).meta({ id: "MttrTrendPoint" });
16631
16730
  var MttrTrendResponse = external_exports.object({
16632
- range: SecurityRange,
16731
+ range: TimeRange,
16633
16732
  granularity: TimeseriesGranularity,
16634
16733
  points: external_exports.array(MttrTrendPoint)
16635
16734
  }).meta({ id: "MttrTrendResponse" });
@@ -16656,11 +16755,11 @@ var TopSource = external_exports.object({
16656
16755
  findingsCount: external_exports.number().int().nonnegative()
16657
16756
  }).meta({ id: "TopSource" });
16658
16757
  var TopSourcesResponse = external_exports.object({
16659
- range: SecurityRange,
16758
+ range: TimeRange,
16660
16759
  items: external_exports.array(TopSource)
16661
16760
  }).meta({ id: "TopSourcesResponse" });
16662
16761
  var TopSourcesQuery = external_exports.object({
16663
- range: external_exports.enum(SECURITY_RANGES).default("30d"),
16762
+ range: external_exports.enum(TIME_RANGES).default(DEFAULT_TIME_RANGE),
16664
16763
  limit: external_exports.coerce.number().int().min(1).max(50).default(5),
16665
16764
  // Omit for both kinds.
16666
16765
  kind: external_exports.enum(SOURCE_KINDS).optional()
@@ -16673,7 +16772,7 @@ var ScanCoverageProvider = external_exports.object({
16673
16772
  supported: external_exports.boolean()
16674
16773
  }).meta({ id: "ScanCoverageProvider" });
16675
16774
  var ScanCoverageResponse = external_exports.object({
16676
- range: SecurityRange,
16775
+ range: TimeRange,
16677
16776
  providers: external_exports.array(ScanCoverageProvider)
16678
16777
  }).meta({ id: "ScanCoverageResponse" });
16679
16778
  var SubjectType = external_exports.enum(["repo", "user", "team", "policy", "share", "rule"]).meta({
@@ -16722,6 +16821,114 @@ var ApplyRecommendedActionResponse = external_exports.object({
16722
16821
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
16723
16822
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
16724
16823
 
16824
+ // ../../packages/schema/src/zod/triage.ts
16825
+ var TriageHit = external_exports.object({
16826
+ ruleId: external_exports.string(),
16827
+ category: DetectionCategory,
16828
+ severity: Severity,
16829
+ maskedMatch: external_exports.string(),
16830
+ rawMatch: external_exports.string(),
16831
+ context: external_exports.string(),
16832
+ filePath: external_exports.string().optional(),
16833
+ confidence: external_exports.number().min(0).max(1),
16834
+ id: external_exports.string().optional(),
16835
+ valueFingerprint: external_exports.string().optional(),
16836
+ keyVersion: external_exports.number().int().nonnegative().optional()
16837
+ });
16838
+ var TriagePolicy = BuiltinPolicyId;
16839
+ var TriageCategoryRec = external_exports.object({
16840
+ category: DetectionCategory,
16841
+ action: TriagePolicy,
16842
+ reasoning: external_exports.string(),
16843
+ genuineCount: external_exports.number().int().nonnegative(),
16844
+ fpCount: external_exports.number().int().nonnegative(),
16845
+ // TriageHit ids judged false-positive in this category. fpCount must equal
16846
+ // this array's length — enforced by the consumer, not this schema.
16847
+ fpIds: external_exports.array(external_exports.string())
16848
+ });
16849
+ var TriageRecommendation = external_exports.object({
16850
+ perCategory: external_exports.array(TriageCategoryRec),
16851
+ notes: external_exports.string()
16852
+ });
16853
+
16854
+ // ../../packages/schema/src/zod/setup-frame.ts
16855
+ var CalibrationCounts = external_exports.object({
16856
+ total: external_exports.number().int().nonnegative(),
16857
+ important: external_exports.number().int().nonnegative(),
16858
+ routine: external_exports.number().int().nonnegative()
16859
+ }).refine((c) => c.total === c.important + c.routine, {
16860
+ message: "total must equal important + routine",
16861
+ path: ["total"]
16862
+ });
16863
+ var FalsePositivePatternValue = external_exports.object({
16864
+ ruleId: external_exports.string(),
16865
+ category: DetectionCategory,
16866
+ valueFingerprint: external_exports.string(),
16867
+ keyVersion: external_exports.number().int().nonnegative()
16868
+ });
16869
+ var FalsePositivePatternGroup = external_exports.object({
16870
+ pattern: external_exports.string(),
16871
+ count: external_exports.number().int().nonnegative(),
16872
+ values: external_exports.array(FalsePositivePatternValue).min(1)
16873
+ });
16874
+ var CalibrationFindingKind = external_exports.object({
16875
+ category: DetectionCategory,
16876
+ count: external_exports.number().int().nonnegative(),
16877
+ egress: external_exports.boolean()
16878
+ });
16879
+ var CalibrationFrame = external_exports.object({
16880
+ counts: CalibrationCounts,
16881
+ routineCategories: external_exports.array(DetectionCategory),
16882
+ surfacedCategories: external_exports.array(DetectionCategory),
16883
+ findingKinds: external_exports.array(CalibrationFindingKind),
16884
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
16885
+ maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
16886
+ falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
16887
+ });
16888
+ var CalibrationPreviewCategory = TriageCategoryRec.pick({
16889
+ category: true,
16890
+ genuineCount: true,
16891
+ fpCount: true
16892
+ }).extend({
16893
+ egress: external_exports.boolean()
16894
+ });
16895
+ var CalibrationPreview = external_exports.object({
16896
+ categories: external_exports.array(CalibrationPreviewCategory),
16897
+ posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
16898
+ });
16899
+ var CalibrationResult = external_exports.object({
16900
+ frame: CalibrationFrame,
16901
+ copy: external_exports.string()
16902
+ });
16903
+ var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
16904
+ var SetupHandoffOption = external_exports.object({
16905
+ id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
16906
+ label: external_exports.string()
16907
+ });
16908
+ var DashboardHandoffOptions = external_exports.tuple([
16909
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
16910
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
16911
+ ]);
16912
+ var ComposedRemediationOptions = external_exports.tuple([
16913
+ SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
16914
+ SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
16915
+ SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
16916
+ ]);
16917
+ var SetupHandoffOffer = external_exports.object({
16918
+ worthALook: external_exports.number().int().nonnegative(),
16919
+ liveKeys: external_exports.number().int().nonnegative().optional(),
16920
+ options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
16921
+ }).refine(
16922
+ (o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
16923
+ {
16924
+ message: "the chain-entry option is present exactly when liveKeys > 0",
16925
+ path: ["options"]
16926
+ }
16927
+ ).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
16928
+ message: "liveKeys is a subset of worthALook and cannot exceed it",
16929
+ path: ["liveKeys"]
16930
+ });
16931
+
16725
16932
  // ../../packages/schema/src/zod/shares.ts
16726
16933
  var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
16727
16934
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
@@ -16861,36 +17068,6 @@ var ExportSharesQuery = external_exports.object({
16861
17068
  kind: external_exports.array(DestinationKind).optional()
16862
17069
  });
16863
17070
 
16864
- // ../../packages/schema/src/zod/triage.ts
16865
- var TriageHit = external_exports.object({
16866
- ruleId: external_exports.string(),
16867
- category: DetectionCategory,
16868
- severity: Severity,
16869
- maskedMatch: external_exports.string(),
16870
- rawMatch: external_exports.string(),
16871
- context: external_exports.string(),
16872
- filePath: external_exports.string().optional(),
16873
- confidence: external_exports.number().min(0).max(1),
16874
- id: external_exports.string().optional(),
16875
- valueFingerprint: external_exports.string().optional(),
16876
- keyVersion: external_exports.number().int().nonnegative().optional()
16877
- });
16878
- var TriagePolicy = BuiltinPolicyId;
16879
- var TriageCategoryRec = external_exports.object({
16880
- category: DetectionCategory,
16881
- action: TriagePolicy,
16882
- reasoning: external_exports.string(),
16883
- genuineCount: external_exports.number().int().nonnegative(),
16884
- fpCount: external_exports.number().int().nonnegative(),
16885
- // TriageHit ids judged false-positive in this category. fpCount must equal
16886
- // this array's length — enforced by the consumer, not this schema.
16887
- fpIds: external_exports.array(external_exports.string())
16888
- });
16889
- var TriageRecommendation = external_exports.object({
16890
- perCategory: external_exports.array(TriageCategoryRec),
16891
- notes: external_exports.string()
16892
- });
16893
-
16894
17071
  // ../../packages/persistence/src/ids.ts
16895
17072
  import { createHash } from "crypto";
16896
17073
 
@@ -17078,21 +17255,28 @@ import { readdirSync, readFileSync as readFileSync4, realpathSync, statSync as s
17078
17255
  import { homedir as homedir2 } from "os";
17079
17256
  import { basename as basename2, join as join7 } from "path";
17080
17257
 
17258
+ // ../../packages/detections/src/escape-regexp.ts
17259
+ function escapeRegExp(value) {
17260
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17261
+ }
17262
+
17263
+ // ../../packages/detections/src/matchers/limits.ts
17264
+ var MAX_MATCHES_PER_RULE = 1e4;
17265
+
17081
17266
  // ../../packages/detections/src/matchers/keyword.ts
17082
17267
  var KeywordMatcher2 = class {
17083
17268
  match(text, rule) {
17084
17269
  if (rule.matcher.type !== "keyword") return [];
17085
17270
  const { keywords, caseSensitive } = rule.matcher;
17086
- const haystack = caseSensitive ? text : text.toLowerCase();
17087
17271
  const spans = [];
17088
17272
  for (const kw of keywords) {
17089
- const needle = caseSensitive ? kw : kw.toLowerCase();
17090
- let idx = 0;
17091
- while (idx < haystack.length) {
17092
- const pos = haystack.indexOf(needle, idx);
17093
- if (pos === -1) break;
17094
- spans.push({ start: pos, end: pos + kw.length });
17095
- idx = pos + 1;
17273
+ if (kw.length === 0) continue;
17274
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
17275
+ const re = new RegExp(escapeRegExp(kw), caseSensitive ? "gu" : "giu");
17276
+ let m;
17277
+ while ((m = re.exec(text)) !== null) {
17278
+ spans.push({ start: m.index, end: m.index + m[0].length });
17279
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
17096
17280
  }
17097
17281
  }
17098
17282
  return spans;
@@ -17100,7 +17284,6 @@ var KeywordMatcher2 = class {
17100
17284
  };
17101
17285
 
17102
17286
  // ../../packages/detections/src/matchers/regex.ts
17103
- var MAX_MATCHES_PER_RULE = 1e4;
17104
17287
  var RegexMatcher2 = class {
17105
17288
  match(text, rule) {
17106
17289
  if (rule.matcher.type !== "regex") return [];
@@ -1,6 +1,6 @@
1
1
  # AKA setup-wizard triage prompt (draft — validation harness artifact)
2
2
 
3
- You are the AKA Control Plane setup wizard's inline triage step. You have just
3
+ You are the AKA Security setup wizard's inline triage step. You have just
4
4
  scanned a workspace (working tree and/or history) and detected a set of raw
5
5
  hits — matches from AKA's deterministic regex detection rules, shown to you
6
6
  with their real (unmasked) value and surrounding context so you can judge them
@@ -66,7 +66,8 @@ one** fenced JSON block containing a `TriageRecommendation`:
66
66
  "action": "block",
67
67
  "reasoning": "one or two sentences, no negative framing of the FPs",
68
68
  "genuineCount": 2,
69
- "fpCount": 2
69
+ "fpCount": 2,
70
+ "fpIds": ["3", "17"]
70
71
  }
71
72
  ],
72
73
  "notes": ""
@@ -78,6 +79,13 @@ one** fenced JSON block containing a `TriageRecommendation`:
78
79
  - `action` must be exactly one of `monitor`, `warn`, `redact`, `block`.
79
80
  - `genuineCount` and `fpCount` are your counts of genuine vs. false-positive
80
81
  hits you identified within that category (both required, both ≥ 0).
82
+ - `fpIds` (required) lists the `id` of every hit in that category you judged a
83
+ false positive. **Copy each `id` verbatim from the hit you are describing** —
84
+ they are stable identifiers assigned by the scanner, not positions in the
85
+ input. Do not renumber them, do not start from 1, and never emit an `id` that
86
+ is not present in the hits you were given: ids you were not shown belong to
87
+ other hits, and naming one would silence a detection you never examined.
88
+ `fpCount` must equal `fpIds.length`.
81
89
  - `notes` is optional free text (empty string if nothing to add) for anything
82
90
  that doesn't fit a single category — e.g. cross-category observations.
83
91
  - The fenced ```json block must be the last thing in your reply and must