@akasecurity/ai-tc-claude-code 0.9.5 → 0.9.7

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.
@@ -585,6 +585,10 @@ var SQLITE_MIGRATIONS = [
585
585
  {
586
586
  tag: "0020_secret_vault_pagination_indexes",
587
587
  sql: "CREATE INDEX `idx_secret_vault_last_seen` ON `secret_vault` (`last_seen`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_reuse` ON `secret_vault` (`occurrence_count`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_at` ON `secret_vault_deref` (`at`,`id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_signal` ON `secret_vault_deref` (`at`,`id`) WHERE `reason` NOT IN ('display', 'view-render');"
588
+ },
589
+ {
590
+ tag: "0021_finding_resolution_resolved_at_index",
591
+ sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
588
592
  }
589
593
  ];
590
594
 
@@ -15300,6 +15304,47 @@ function date4(params) {
15300
15304
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15301
15305
  config(en_default());
15302
15306
 
15307
+ // ../../packages/schema/src/zod/harness-map.ts
15308
+ var HARNESS = {
15309
+ ClaudeCode: "claudecode",
15310
+ Cursor: "cursor",
15311
+ Copilot: "copilot",
15312
+ Codex: "codex",
15313
+ Antigravity: "antigravity",
15314
+ Windsurf: "windsurf",
15315
+ ClaudeDesktop: "claudedesktop",
15316
+ ChatGpt: "chatgpt",
15317
+ ClaudeAi: "claudeai",
15318
+ Api: "api"
15319
+ };
15320
+ var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
15321
+ var SOURCE_TOOL = {
15322
+ ClaudeCode: "claude-code",
15323
+ ClaudeDesktop: "claude-desktop",
15324
+ Cursor: "cursor",
15325
+ ChatGpt: "chatgpt",
15326
+ ClaudeAi: "claude-ai",
15327
+ Copilot: "github-copilot",
15328
+ Codex: "codex",
15329
+ Antigravity: "antigravity",
15330
+ // No harness counterpart, deliberately: the CLI's own captures and a capture
15331
+ // whose tool could not be identified both render through the read side's
15332
+ // miss path rather than as a harness of their own.
15333
+ Cli: "cli",
15334
+ Unknown: "unknown"
15335
+ };
15336
+ var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
15337
+ var TOOL_TO_HARNESS = {
15338
+ [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
15339
+ [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
15340
+ [SOURCE_TOOL.Copilot]: HARNESS.Copilot,
15341
+ [SOURCE_TOOL.Cursor]: HARNESS.Cursor,
15342
+ [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
15343
+ [SOURCE_TOOL.Codex]: HARNESS.Codex,
15344
+ [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
15345
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
15346
+ };
15347
+
15303
15348
  // ../../packages/schema/src/zod/finding.ts
15304
15349
  var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
15305
15350
  var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
@@ -15322,21 +15367,22 @@ var Finding = external_exports.object({
15322
15367
  }).meta({ id: "Finding" });
15323
15368
  var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
15324
15369
  var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
15325
- var FindingProvider = external_exports.enum([
15326
- "claudecode",
15327
- "claudedesktop",
15328
- "cursor",
15329
- "copilot",
15330
- "chatgpt",
15331
- "claudeai",
15332
- "codex",
15333
- "antigravity",
15334
- "api"
15370
+ var FindingProvider = Harness.extract([
15371
+ "ClaudeCode",
15372
+ "ClaudeDesktop",
15373
+ "Cursor",
15374
+ "Copilot",
15375
+ "ChatGpt",
15376
+ "ClaudeAi",
15377
+ "Codex",
15378
+ "Antigravity",
15379
+ "Api"
15335
15380
  ]).meta({ id: "FindingProvider" });
15336
15381
  var FindingCategory = external_exports.enum([
15337
15382
  "secret",
15338
15383
  "pii",
15339
15384
  "source_code",
15385
+ "code_flaw",
15340
15386
  "external_share",
15341
15387
  "mcp_server",
15342
15388
  "customer_data",
@@ -15516,6 +15562,7 @@ var FindingInstanceDetail = FindingInstance.extend({
15516
15562
  policy: FindingPolicyRef
15517
15563
  }).meta({ id: "FindingInstanceDetail" });
15518
15564
  var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
15565
+ var MAX_FLAT_FINDINGS_LIMIT = 200;
15519
15566
  var ListFindingInstancesQuery = external_exports.object({
15520
15567
  severity: external_exports.array(Severity).optional(),
15521
15568
  // Rule ids, the same vocabulary the grouped list's `subtype` carries.
@@ -15535,7 +15582,7 @@ var ListFindingInstancesQuery = external_exports.object({
15535
15582
  q: external_exports.string().optional(),
15536
15583
  sessionId: external_exports.string().optional(),
15537
15584
  from: external_exports.iso.datetime().optional(),
15538
- limit: external_exports.coerce.number().int().min(1).max(200).optional(),
15585
+ limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
15539
15586
  cursor: external_exports.string().optional()
15540
15587
  });
15541
15588
  var ListFindingInstancesResponse = external_exports.object({
@@ -15597,30 +15644,6 @@ var ListFindingLocationsResponse = external_exports.object({
15597
15644
  hasMore: external_exports.boolean()
15598
15645
  }).meta({ id: "ListFindingLocationsResponse" });
15599
15646
 
15600
- // ../../packages/schema/src/zod/harness-map.ts
15601
- var Harness = external_exports.enum([
15602
- "claudecode",
15603
- "cursor",
15604
- "copilot",
15605
- "codex",
15606
- "antigravity",
15607
- "windsurf",
15608
- "claudedesktop",
15609
- "chatgpt",
15610
- "claudeai",
15611
- "api"
15612
- ]).meta({ id: "Harness" });
15613
- var TOOL_TO_HARNESS = {
15614
- "claude-code": "claudecode",
15615
- "claude-desktop": "claudedesktop",
15616
- "github-copilot": "copilot",
15617
- cursor: "cursor",
15618
- chatgpt: "chatgpt",
15619
- codex: "codex",
15620
- antigravity: "antigravity",
15621
- "claude-ai": "claudeai"
15622
- };
15623
-
15624
15647
  // ../../packages/schema/src/zod/meta.ts
15625
15648
  var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
15626
15649
  var AuditEventType = external_exports.enum([
@@ -16073,686 +16096,37 @@ var ActivityOverviewResponse = external_exports.object({
16073
16096
  sessions: ListActivitySessionsResponse
16074
16097
  }).meta({ id: "ActivityOverviewResponse" });
16075
16098
 
16076
- // ../../packages/schema/src/zod/event.ts
16077
- var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
16078
- var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
16079
- var SourceTool = external_exports.enum([
16080
- "claude-code",
16081
- "claude-desktop",
16082
- "cursor",
16083
- "chatgpt",
16084
- "claude-ai",
16085
- "github-copilot",
16086
- "codex",
16087
- "antigravity",
16088
- "cli",
16089
- "unknown"
16090
- ]).meta({ id: "SourceTool" });
16091
- var EventMetadata = external_exports.object({
16092
- sessionId: external_exports.string().optional(),
16093
- repo: external_exports.string().optional(),
16094
- filePath: external_exports.string().optional(),
16095
- // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
16096
- // set by the tool-scanning hooks. The tool NAME only — never the tool's
16097
- // arguments or output, which can carry the very value a finding masked
16098
- // (metadata is stored unredacted). Gives findings on non-file captures a
16099
- // display location ("via Bash") when no filePath exists.
16100
- toolName: external_exports.string().optional(),
16101
- // Set (true) by the worktree scanner when the file is excluded by the
16102
- // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
16103
- // generated code can leak real secrets — but the provenance is recorded so
16104
- // policy/dashboards can treat those findings as informational rather than
16105
- // blocking. Omitted (not false) for tracked files and non-scan events.
16106
- gitignored: external_exports.boolean().optional(),
16107
- // Set (true) ONLY when the event's `content` is the COMPLETE file at
16108
- // capture time (a worktree scan reading from disk). Hook-captured edits
16109
- // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
16110
- // this. The resolver-on-ingest keys its fixed-at-source dropout
16111
- // diff on this marker: only a whole-file snapshot can prove a previously
16112
- // open finding is gone; a fragment's absence proves nothing (the secret
16113
- // may live outside the hunk). Omitted (not false) for fragments and
16114
- // non-scan events, so pre-marker clients safely default to the
16115
- // non-authoritative path.
16116
- wholeFile: external_exports.boolean().optional(),
16117
- model: external_exports.string().optional(),
16118
- turnIndex: external_exports.number().int().nonnegative().optional(),
16119
- // Distributed-tracing correlation. `correlationId` ties a recorded event back
16120
- // to the request that captured/ingested it (a UUID, generated independently of
16121
- // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
16122
- // originating span when telemetry is enabled. Both optional + backward
16123
- // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
16124
- correlationId: external_exports.uuid().optional(),
16125
- traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
16126
- // Ids of the detection exceptions that downgraded findings in this capture
16127
- // to 'allow' — the enforcement audit trail's link back to the grant that
16128
- // authorized the bypass. Absent on captures where no exception applied.
16129
- exceptionIds: external_exports.array(external_exports.guid()).optional()
16130
- }).meta({ id: "EventMetadata" });
16131
- var Event = external_exports.object({
16132
- id: external_exports.guid(),
16133
- sourceTool: SourceTool,
16134
- kind: EventKind,
16135
- occurredAt: external_exports.iso.datetime(),
16136
- contentHash: external_exports.string(),
16137
- content: external_exports.string(),
16138
- metadata: EventMetadata.optional()
16139
- }).meta({ id: "Event" });
16140
- var IngestEvent = Event.meta({ id: "IngestEvent" });
16141
- var IngestBatch = external_exports.object({
16142
- events: external_exports.array(IngestEvent).min(1).max(100),
16143
- // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
16144
- // additionally rejects any event whose contentHash the store has already
16145
- // recorded — for re-runnable bulk ingest (worktree scan, transcript
16146
- // backfill), where a re-run mints fresh event ids for identical content and
16147
- // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
16148
- // two genuinely separate prompts can be byte-identical and both belong on
16149
- // the timeline.
16150
- dedupe: external_exports.literal("content-hash").optional()
16151
- }).meta({ id: "IngestBatch" });
16152
-
16153
- // ../../packages/schema/src/zod/inventory.ts
16154
- var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16155
- var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16156
- var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16157
- var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16158
- var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16159
- var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16160
- var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16161
- var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
16162
- var AccessCounts = external_exports.object({
16163
- open: external_exports.number().int().nonnegative(),
16164
- approved: external_exports.number().int().nonnegative(),
16165
- blocked: external_exports.number().int().nonnegative(),
16166
- total: external_exports.number().int().nonnegative()
16167
- }).meta({ id: "AccessCounts" });
16168
- var AssetSummary = external_exports.object({
16169
- id: external_exports.string(),
16170
- type: AssetType,
16171
- name: external_exports.string(),
16172
- sub: external_exports.string(),
16173
- flags: external_exports.array(Flag),
16174
- /** MCP servers only — omitted for all other types. */
16175
- trust: TrustLevel.optional()
16176
- }).meta({ id: "AssetSummary" });
16177
- var ProjectSummary = external_exports.object({
16178
- id: external_exports.string(),
16179
- name: external_exports.string(),
16180
- repo: external_exports.string(),
16181
- visibility: Visibility,
16182
- language: external_exports.string(),
16183
- policyDefault: AccessLevel,
16184
- updatedAt: external_exports.iso.datetime(),
16185
- accessCounts: AccessCounts,
16186
- findingsCount: external_exports.number().int().nonnegative()
16187
- }).meta({ id: "ProjectSummary" });
16188
- var HarnessCategory = external_exports.object({
16189
- /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16190
- type: AssetType,
16191
- assets: external_exports.array(AssetSummary)
16099
+ // ../../packages/schema/src/zod/config-inventory.ts
16100
+ var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16101
+ var SkillScanEntry = external_exports.object({
16102
+ name: external_exports.string().min(1),
16103
+ // The identity source: a marketplace repo for plugin skills (e.g.
16104
+ // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16105
+ // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16106
+ // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16107
+ source: external_exports.string().min(1),
16108
+ scope: ConfigScope,
16109
+ pluginName: external_exports.string().optional(),
16110
+ // Volatile — rides the attribute bag, never the identity hash.
16111
+ version: external_exports.string().optional(),
16112
+ description: external_exports.string().optional(),
16113
+ // Skill directory mtime (ISO) the "updated Nd ago" freshness signal.
16114
+ updatedAt: external_exports.iso.datetime().optional(),
16115
+ // Filesystem path — the promoted inventory `location` column.
16116
+ location: external_exports.string().optional()
16192
16117
  });
16193
- var HarnessSummary = external_exports.object({
16194
- id: HarnessId,
16195
- label: external_exports.string(),
16196
- kind: external_exports.string(),
16197
- version: external_exports.string(),
16198
- sessions: external_exports.number().int().nonnegative(),
16199
- assetCount: external_exports.number().int().nonnegative(),
16200
- flagCount: external_exports.number().int().nonnegative(),
16201
- projects: external_exports.array(ProjectSummary),
16202
- categories: external_exports.array(HarnessCategory)
16203
- }).meta({ id: "HarnessSummary" });
16204
- var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16205
- var AssetGroup = external_exports.object({
16206
- /** Group key — never project (enforced at service layer). */
16207
- type: AssetType,
16208
- total: external_exports.number().int().nonnegative(),
16209
- /**
16210
- * MCP group only — omitted for all other types.
16211
- * Partial: only TrustLevel keys with non-zero counts are included.
16212
- * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16213
- */
16214
- trustRollup: external_exports.object({
16215
- "known-good": external_exports.number().int().nonnegative(),
16216
- risky: external_exports.number().int().nonnegative(),
16217
- unapproved: external_exports.number().int().nonnegative()
16218
- }).partial().strict().optional(),
16219
- /**
16220
- * Partial: only Flag keys with non-zero counts are included.
16221
- * Strict: unknown keys are rejected — only Flag values are valid keys.
16222
- */
16223
- flagRollup: external_exports.object({
16224
- update: external_exports.number().int().nonnegative(),
16225
- stale: external_exports.number().int().nonnegative(),
16226
- conflict: external_exports.number().int().nonnegative(),
16227
- unknown: external_exports.number().int().nonnegative(),
16228
- change: external_exports.number().int().nonnegative(),
16229
- untracked: external_exports.number().int().nonnegative(),
16230
- risk: external_exports.number().int().nonnegative(),
16231
- findings: external_exports.number().int().nonnegative()
16232
- }).partial().strict(),
16233
- items: external_exports.array(AssetSummary)
16234
- }).meta({ id: "AssetGroup" });
16235
- var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16236
- var McpTool = external_exports.object({
16237
- name: external_exports.string(),
16238
- signature: external_exports.string(),
16239
- description: external_exports.string(),
16240
- write: external_exports.boolean(),
16241
- /** Non-null string when tool is dangerous / blocked; null otherwise. */
16242
- risk: external_exports.string().nullable()
16243
- }).meta({ id: "McpTool" });
16244
- var AssetFindingRef = external_exports.object({
16245
- id: external_exports.string(),
16246
- title: external_exports.string(),
16247
- note: external_exports.string()
16248
- });
16249
- var AssetDetail = AssetSummary.extend({
16250
- /** string | null — null when no description is available. */
16251
- description: external_exports.string().nullable(),
16252
- /** trustLevel | null — null for non-MCP assets. */
16253
- trust: TrustLevel.nullable(),
16254
- /** Type-specific raw key/values — FE renders the grid. */
16255
- meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16256
- /** always present — object when there is an active finding, null when absent. */
16257
- finding: AssetFindingRef.nullable(),
16258
- /** MCP exposed-tools list — omitted for non-mcp. */
16259
- tools: external_exports.array(McpTool).optional()
16260
- }).meta({ id: "AssetDetail" });
16261
- var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16262
- var InventoryStats = external_exports.object({
16263
- /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16264
- attention: external_exports.number().int().nonnegative(),
16265
- byType: external_exports.object({
16266
- project: external_exports.number().int().nonnegative(),
16267
- skill: external_exports.number().int().nonnegative(),
16268
- mcp: external_exports.number().int().nonnegative(),
16269
- hook: external_exports.number().int().nonnegative(),
16270
- config: external_exports.number().int().nonnegative()
16271
- }),
16272
- harnesses: external_exports.number().int().nonnegative(),
16273
- mcpTrust: external_exports.object({
16274
- "known-good": external_exports.number().int().nonnegative(),
16275
- risky: external_exports.number().int().nonnegative(),
16276
- unapproved: external_exports.number().int().nonnegative()
16277
- })
16278
- }).meta({ id: "InventoryStats" });
16279
- var FileSummary = external_exports.object({
16280
- path: external_exports.string(),
16281
- name: external_exports.string(),
16282
- origin: Origin,
16283
- /** Effective access (override applied). */
16284
- access: AccessLevel,
16285
- /** True when a file_access_override differs from the computed default. */
16286
- isCustom: external_exports.boolean(),
16287
- findings: external_exports.number().int().nonnegative(),
16288
- /** When the file was auto-blocked by a detection; null when not blocked. */
16289
- blockedAt: external_exports.iso.datetime().nullable().optional(),
16290
- /** Why the file was blocked; null when absent. */
16291
- note: external_exports.string().nullable().optional()
16292
- }).meta({ id: "FileSummary" });
16293
- var FolderSummary = external_exports.object({
16294
- name: external_exports.string(),
16295
- path: external_exports.string(),
16296
- /** Rollup of effective access across all descendants. */
16297
- accessCounts: AccessCounts
16298
- }).meta({ id: "FolderSummary" });
16299
- var ProjectTreeResponse = external_exports.object({
16300
- project: external_exports.object({
16301
- id: external_exports.string(),
16302
- repo: external_exports.string(),
16303
- visibility: Visibility
16304
- }),
16305
- path: external_exports.string(),
16306
- /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16307
- folders: external_exports.array(FolderSummary).optional(),
16308
- files: external_exports.array(FileSummary)
16309
- }).meta({ id: "ProjectTreeResponse" });
16310
- var FileDetail = FileSummary.extend({
16311
- project: external_exports.object({
16312
- repo: external_exports.string(),
16313
- visibility: Visibility,
16314
- language: external_exports.string(),
16315
- policyDefault: AccessLevel,
16316
- updatedAt: external_exports.iso.datetime()
16317
- }),
16318
- findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16319
- }).meta({ id: "FileDetail" });
16320
- var SetFileAccessBody = external_exports.object({
16321
- path: external_exports.string(),
16322
- access: AccessLevel
16323
- }).meta({ id: "SetFileAccessBody" });
16324
- var SetFileAccessResponse = external_exports.object({
16325
- file: FileSummary,
16326
- accessCounts: AccessCounts
16327
- }).meta({ id: "SetFileAccessResponse" });
16328
- var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16329
- var HarnessEventItem = external_exports.object({
16330
- kind: HarnessEventKind,
16331
- title: external_exports.string(),
16332
- detail: external_exports.string(),
16333
- occurredAt: external_exports.iso.datetime(),
16334
- findingId: external_exports.string().nullable().optional()
16335
- }).meta({ id: "HarnessEventItem" });
16336
- var HarnessEventsResponse = external_exports.object({
16337
- counts: external_exports.object({
16338
- block: external_exports.number().int().nonnegative(),
16339
- redact: external_exports.number().int().nonnegative(),
16340
- warn: external_exports.number().int().nonnegative()
16341
- }),
16342
- items: external_exports.array(HarnessEventItem)
16343
- }).meta({ id: "HarnessEventsResponse" });
16344
- var RescanResponse = external_exports.object({
16345
- jobId: external_exports.string(),
16346
- startedAt: external_exports.iso.datetime()
16347
- }).meta({ id: "RescanResponse" });
16348
- var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16349
- var ListAssetsQuery = external_exports.object({
16350
- /** Filter by one or more AssetType values; absent means all types. */
16351
- type: external_exports.array(AssetType).optional(),
16352
- /** Free-text search term. */
16353
- q: external_exports.string().optional()
16354
- });
16355
- var GetProjectTreeQuery = external_exports.object({
16356
- /** Subtree root path; defaults to repository root when absent. */
16357
- path: external_exports.string().optional(),
16358
- /** Free-text filter applied to file paths. */
16359
- q: external_exports.string().optional(),
16360
- /**
16361
- * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16362
- * file across the whole repo (folders omitted, most-recent first), ignoring
16363
- * `path`/`q` — powers the project-wide "recently blocked" strip.
16364
- */
16365
- filter: external_exports.enum(["blocked"]).optional()
16366
- });
16367
- var GetProjectFileQuery = external_exports.object({
16368
- /** Repository-relative file path; absent or empty → 400. */
16369
- path: external_exports.string()
16370
- });
16371
- var GetHarnessEventsQuery = external_exports.object({
16372
- /** Maximum number of events to return. Range: 1–50; default: 7. */
16373
- limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16374
- });
16375
-
16376
- // ../../packages/schema/src/zod/exception.ts
16377
- var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
16378
- var ExceptionConditions = external_exports.object({
16379
- repo: external_exports.string().optional(),
16380
- sourceTool: external_exports.string().optional(),
16381
- provider: external_exports.string().optional()
16382
- }).strict();
16383
- var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
16384
- var DetectionException = external_exports.object({
16385
- id: external_exports.guid(),
16386
- ruleId: external_exports.string(),
16387
- // Denormalized from the rule, for reporting — never matched on.
16388
- category: DetectionCategory,
16389
- // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
16390
- // fingerprint, never the raw value, and never reversible. Matching recomputes
16391
- // the fingerprint from a fresh capture; the value itself is never stored.
16392
- // Shape-constrained so a malformed — or accidentally raw — value is rejected
16393
- // at the boundary rather than persisted.
16394
- valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
16395
- // Version of the fingerprint key the grant was written under; a rotated key
16396
- // invalidates old grants rather than silently mismatching them.
16397
- keyVersion: external_exports.number().int().positive(),
16398
- // maskMatch() preview of the approved value — never the raw value.
16399
- maskedValue: external_exports.string(),
16400
- capability: ExceptionCapability.default("suppress"),
16401
- scope: ExceptionScope,
16402
- expiresAt: external_exports.iso.datetime().nullable(),
16403
- maxUses: external_exports.number().int().positive().nullable(),
16404
- useCount: external_exports.number().int().nonnegative(),
16405
- lastUsedAt: external_exports.iso.datetime().nullable(),
16406
- // Mandatory: every grant carries the human reason it exists.
16407
- justification: external_exports.string().min(1),
16408
- conditions: ExceptionConditions.nullable(),
16409
- createdBy: external_exports.string(),
16410
- createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
16411
- createdAt: external_exports.iso.datetime(),
16412
- updatedAt: external_exports.iso.datetime(),
16413
- // Revocation is terminal and retained — consumed/expired/revoked rows are
16414
- // audit evidence; nothing in the exception lifecycle hard-deletes.
16415
- revokedAt: external_exports.iso.datetime().nullable(),
16416
- revokedBy: external_exports.string().nullable(),
16417
- revokeReason: external_exports.string().nullable()
16418
- });
16419
- var ExceptionBundleEntry = DetectionException.pick({
16420
- id: true,
16421
- ruleId: true,
16422
- valueFingerprint: true,
16423
- keyVersion: true,
16424
- capability: true,
16425
- expiresAt: true,
16426
- maxUses: true,
16427
- useCount: true,
16428
- conditions: true
16429
- });
16430
- var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
16431
-
16432
- // ../../packages/schema/src/zod/rule.ts
16433
- var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16434
- var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16435
- var KeywordMatcher = external_exports.object({
16436
- type: external_exports.literal("keyword"),
16437
- // An empty keyword matches at every position, yielding one zero-length span
16438
- // per character. Rejected here because a keyword that matches everything is
16439
- // never intentional.
16440
- keywords: external_exports.array(external_exports.string().min(1)).min(1),
16441
- caseSensitive: external_exports.boolean().default(false)
16442
- });
16443
- function isValidRegex(pattern, flags) {
16444
- try {
16445
- new RegExp(pattern, flags);
16446
- return true;
16447
- } catch {
16448
- return false;
16449
- }
16450
- }
16451
- function matchesEmptyString(pattern, flags) {
16452
- try {
16453
- const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
16454
- return re.exec("")?.[0].length === 0;
16455
- } catch {
16456
- return false;
16457
- }
16458
- }
16459
- var MAX_PATTERN_LENGTH = 2e3;
16460
- var RegexMatcher = external_exports.object({
16461
- type: external_exports.literal("regex"),
16462
- pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16463
- flags: external_exports.string().default("gi"),
16464
- captureGroup: external_exports.number().int().nonnegative().optional()
16465
- }).refine((v) => isValidRegex(v.pattern, v.flags), {
16466
- message: "pattern/flags do not form a valid JavaScript regular expression",
16467
- path: ["pattern"]
16468
- }).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
16469
- message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
16470
- path: ["pattern"]
16471
- });
16472
- var ValidatorMatcher = external_exports.object({
16473
- type: external_exports.literal("validator"),
16474
- name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
16475
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16476
- });
16477
- var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
16478
- var AppliesTo = external_exports.object({
16479
- // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16480
- extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16481
- }).meta({ id: "AppliesTo" });
16482
- var PostValidatorRef = external_exports.union([
16483
- external_exports.string(),
16484
- external_exports.object({
16485
- name: external_exports.string(),
16486
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16487
- })
16488
- ]).meta({ id: "PostValidatorRef" });
16489
- var RequiresNearby = external_exports.object({
16490
- // Each array, when present, must be non-empty and contain non-empty strings —
16491
- // an empty/blank criterion would either never fire or (for labels) match
16492
- // everything.
16493
- categories: external_exports.array(DetectionCategory).min(1).optional(),
16494
- ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16495
- labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16496
- windowChars: external_exports.number().int().positive().default(160),
16497
- // Optional confidence bump applied when a gated match is corroborated. Capped
16498
- // small: it nudges confidence, it does not assert certainty.
16499
- confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16500
- }).refine(
16501
- (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16502
- { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16503
- ).meta({ id: "RequiresNearby" });
16504
- var RuleFixture = external_exports.object({
16505
- label: external_exports.string(),
16506
- text: external_exports.string().max(5e4),
16507
- shouldMatch: external_exports.boolean(),
16508
- // Simulated file context for the scan, so fixtures can assert `appliesTo`
16509
- // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16510
- filePath: external_exports.string().optional(),
16511
- expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
16512
- }).meta({ id: "RuleFixture" });
16513
- var Rule = external_exports.object({
16514
- specVersion: external_exports.literal(1),
16515
- // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16516
- // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16517
- // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16518
- // therefore carries no namespace and is not globally unique across publishers;
16519
- // never feed one to splitDetectionId. `category` below (per-rule) is the
16520
- // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16521
- id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16522
- name: external_exports.string(),
16523
- category: DetectionCategory,
16524
- severity: Severity,
16525
- matcher: Matcher,
16526
- appliesTo: AppliesTo.optional(),
16527
- postValidators: external_exports.array(PostValidatorRef).optional(),
16528
- requiresNearby: RequiresNearby.optional(),
16529
- examples: external_exports.array(external_exports.string()).optional()
16530
- }).meta({ id: "Rule" });
16531
- var Author = external_exports.object({
16532
- name: external_exports.string(),
16533
- email: external_exports.email().optional(),
16534
- url: external_exports.url().optional()
16535
- }).meta({ id: "Author" });
16536
- var PackManifest = external_exports.object({
16537
- specVersion: external_exports.literal(1),
16538
- id: external_exports.string(),
16539
- name: external_exports.string(),
16540
- version: external_exports.string(),
16541
- rules: external_exports.array(external_exports.string()),
16542
- // Optional attribution/provenance — consumed by the rule marketplace.
16543
- description: external_exports.string().optional(),
16544
- author: Author.optional(),
16545
- license: external_exports.string().optional(),
16546
- sourceUrl: external_exports.url().optional()
16547
- }).meta({ id: "PackManifest" });
16548
-
16549
- // ../../packages/schema/src/zod/policy.ts
16550
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
16551
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
16552
- var Policy = external_exports.object({
16553
- id: external_exports.guid(),
16554
- scope: PolicyScope,
16555
- target: PolicyTarget,
16556
- action: ActionTaken,
16557
- enabled: external_exports.boolean().default(true),
16558
- customKeywords: external_exports.array(external_exports.string()).optional(),
16559
- // Display name — optional so older policy rows without name still parse.
16560
- // Added for the findings API (policy.name column migration).
16561
- name: external_exports.string().optional()
16562
- }).meta({ id: "Policy" });
16563
- var PolicyBundle = external_exports.object({
16564
- version: external_exports.string(),
16565
- policies: external_exports.array(Policy),
16566
- // Rules from the installed marketplace packs (snapshotted by the
16567
- // control plane). The plugin registers these in addition to its bundled
16568
- // packs. Optional so older backends — and older on-disk caches — that omit
16569
- // the field still parse; consumers read `bundle.rules ?? []`.
16570
- rules: external_exports.array(Rule).optional(),
16571
- // When true, `rules` IS the complete effective ruleset and the runtime must
16572
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
16573
- // after reading the user's installed snapshot (installed_packs, enabled
16574
- // packs only), which is how detection updates stay manual: new bundled
16575
- // rules run only after the user applies the pack update. Absent/false keeps
16576
- // the historical composition (bundled packs + rules) — older caches.
16577
- rulesComplete: external_exports.boolean().optional(),
16578
- // Active detection exceptions, evaluation subset only (see
16579
- // ExceptionBundleEntry). Optional so older bundle producers — and older
16580
- // on-disk caches — that omit the field still parse; consumers read
16581
- // `bundle.exceptions ?? []`.
16582
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
16583
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
16584
- // from a versioned installed pack. Optional so older backends — and older
16585
- // on-disk caches — that omit the field still parse; consumers fall back to
16586
- // the rule's own spec version. NOT the bundle version above — see
16587
- // installedRuleset's ruleVersions for the source of truth.
16588
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
16589
- customKeywords: external_exports.array(external_exports.string()),
16590
- fetchedAt: external_exports.iso.datetime()
16591
- }).meta({ id: "PolicyBundle" });
16592
- var OBSERVE_ONLY_CATEGORIES = ["config"];
16593
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
16594
- var CATEGORY_PEAK_SEVERITY = {
16595
- secret: "critical",
16596
- financial: "critical",
16597
- // core-financial/credit-card
16598
- code_flaw: "critical",
16599
- pii: "high",
16600
- phi: "high",
16601
- custom: "high",
16602
- // user-defined; conservative
16603
- code_context: "low",
16604
- config: "low"
16605
- // observe-only; floors to monitor regardless
16606
- };
16607
- function severityFloorPolicy(category) {
16608
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
16609
- const peak = CATEGORY_PEAK_SEVERITY[category];
16610
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
16611
- }
16612
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16613
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16614
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16615
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16616
- var BUILTIN_POLICY_SPECS = {
16617
- monitor: {
16618
- name: "Monitor",
16619
- action: "log",
16620
- description: "Log every match for audit. The request is allowed through untouched."
16621
- },
16622
- warn: {
16623
- name: "Warn",
16624
- action: "warn",
16625
- description: "Allow the request, but warn the user inline before it is sent."
16626
- },
16627
- redact: {
16628
- name: "Redact",
16629
- action: "redact",
16630
- description: "Automatically strip the matched value from the request, then continue."
16631
- },
16632
- block: {
16633
- name: "Block",
16634
- action: "block",
16635
- description: "Refuse the request entirely whenever any rule in this detection matches."
16636
- }
16637
- };
16638
- function builtinPolicyToAction(id) {
16639
- return BUILTIN_POLICY_SPECS[id].action;
16640
- }
16641
- var DEFAULT_ACTIONS = Object.fromEntries(
16642
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16643
- );
16644
- var BUILTIN_POLICIES = Object.fromEntries(
16645
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16646
- );
16647
- var DEFAULT_PACK_POLICY_ID = "monitor";
16648
- function policyIdToAction(policyId) {
16649
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16650
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16651
- return BUILTIN_POLICIES[id].action;
16652
- }
16653
- var UsedByItem = external_exports.object({
16654
- id: external_exports.string(),
16655
- name: external_exports.string(),
16656
- ruleCount: external_exports.number().int().nonnegative(),
16657
- enabled: external_exports.boolean()
16658
- }).meta({ id: "UsedByItem" });
16659
- var PolicyListItem = external_exports.object({
16660
- id: external_exports.string(),
16661
- kind: PolicyKind,
16662
- name: external_exports.string(),
16663
- enabled: external_exports.boolean(),
16664
- usedByCount: external_exports.number().int().nonnegative()
16665
- }).meta({ id: "PolicyListItem" });
16666
- var PolicyDetail = external_exports.object({
16667
- specVersion: external_exports.literal(1),
16668
- id: external_exports.string(),
16669
- kind: PolicyKind,
16670
- name: external_exports.string(),
16671
- enabled: external_exports.boolean(),
16672
- description: external_exports.string(),
16673
- usedBy: external_exports.array(UsedByItem)
16674
- }).meta({ id: "PolicyDetail" });
16675
- var PolicyStatsResponse = external_exports.object({
16676
- policies: external_exports.number().int().nonnegative(),
16677
- builtin: external_exports.number().int().nonnegative(),
16678
- custom: external_exports.number().int().nonnegative(),
16679
- detectionsGoverned: external_exports.number().int().nonnegative()
16680
- }).meta({ id: "PolicyStatsResponse" });
16681
-
16682
- // ../../packages/schema/src/zod/api.ts
16683
- var LIST_QUERY_MAX_LIMIT = 200;
16684
- var ListEventsQuery = external_exports.object({
16685
- cursor: external_exports.string().optional(),
16686
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16687
- sourceTool: external_exports.string().optional(),
16688
- kind: external_exports.string().optional(),
16689
- from: external_exports.iso.datetime().optional(),
16690
- to: external_exports.iso.datetime().optional()
16691
- });
16692
- var ListEventsResponse = external_exports.object({
16693
- items: external_exports.array(Event),
16694
- nextCursor: external_exports.string().nullable()
16695
- }).meta({ id: "ListEventsResponse" });
16696
- var IngestResponse = external_exports.object({
16697
- accepted: external_exports.number().int().nonnegative(),
16698
- duplicates: external_exports.number().int().nonnegative()
16699
- }).meta({ id: "IngestResponse" });
16700
- var ListFindingsQuery = external_exports.object({
16701
- cursor: external_exports.string().optional(),
16702
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16703
- severity: external_exports.string().optional(),
16704
- category: external_exports.string().optional(),
16705
- eventId: external_exports.guid().optional()
16706
- });
16707
- var ListFindingsResponse = external_exports.object({
16708
- items: external_exports.array(Finding),
16709
- nextCursor: external_exports.string().nullable()
16710
- }).meta({ id: "ListFindingsResponse" });
16711
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16712
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16713
- id: "CreatePolicyRequest"
16714
- });
16715
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16716
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16717
- var ErrorResponse = external_exports.object({
16718
- error: external_exports.object({
16719
- code: external_exports.string(),
16720
- message: external_exports.string(),
16721
- details: external_exports.unknown().optional()
16722
- })
16723
- }).meta({ id: "ErrorResponse" });
16724
-
16725
- // ../../packages/schema/src/zod/config-inventory.ts
16726
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16727
- var SkillScanEntry = external_exports.object({
16728
- name: external_exports.string().min(1),
16729
- // The identity source: a marketplace repo for plugin skills (e.g.
16730
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16731
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16732
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16733
- source: external_exports.string().min(1),
16734
- scope: ConfigScope,
16735
- pluginName: external_exports.string().optional(),
16736
- // Volatile — rides the attribute bag, never the identity hash.
16737
- version: external_exports.string().optional(),
16738
- description: external_exports.string().optional(),
16739
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16740
- updatedAt: external_exports.iso.datetime().optional(),
16741
- // Filesystem path — the promoted inventory `location` column.
16742
- location: external_exports.string().optional()
16743
- });
16744
- var HookScanEntry = external_exports.object({
16745
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16746
- // set is harness-defined and grows without a schema change.
16747
- event: external_exports.string().min(1),
16748
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16749
- matcher: external_exports.string().optional(),
16750
- command: external_exports.string().min(1),
16751
- timeout: external_exports.number().optional(),
16752
- scope: ConfigScope,
16753
- pluginName: external_exports.string().optional(),
16754
- // The settings file / hooks.json the entry came from.
16755
- location: external_exports.string().optional()
16118
+ var HookScanEntry = external_exports.object({
16119
+ // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16120
+ // set is harness-defined and grows without a schema change.
16121
+ event: external_exports.string().min(1),
16122
+ // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16123
+ matcher: external_exports.string().optional(),
16124
+ command: external_exports.string().min(1),
16125
+ timeout: external_exports.number().optional(),
16126
+ scope: ConfigScope,
16127
+ pluginName: external_exports.string().optional(),
16128
+ // The settings file / hooks.json the entry came from.
16129
+ location: external_exports.string().optional()
16756
16130
  });
16757
16131
  var McpServerScanEntry = external_exports.object({
16758
16132
  // The server's config key ("github", "filesystem", …) — identity, with the
@@ -16834,6 +16208,164 @@ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16834
16208
  var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16835
16209
  var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16836
16210
 
16211
+ // ../../packages/schema/src/zod/rule.ts
16212
+ var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
16213
+ var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16214
+ var KeywordMatcher = external_exports.strictObject({
16215
+ type: external_exports.literal("keyword"),
16216
+ // An empty keyword matches at every position, yielding one zero-length span
16217
+ // per character. Rejected here because a keyword that matches everything is
16218
+ // never intentional.
16219
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16220
+ caseSensitive: external_exports.boolean().default(false)
16221
+ });
16222
+ function isValidRegex(pattern, flags) {
16223
+ try {
16224
+ new RegExp(pattern, flags);
16225
+ return true;
16226
+ } catch {
16227
+ return false;
16228
+ }
16229
+ }
16230
+ function probeFlags(flags) {
16231
+ return flags.replace(/[gy]/g, "");
16232
+ }
16233
+ function matchesEmptyString(pattern, flags) {
16234
+ try {
16235
+ const re = new RegExp(pattern, probeFlags(flags));
16236
+ return re.exec("")?.[0].length === 0;
16237
+ } catch {
16238
+ return false;
16239
+ }
16240
+ }
16241
+ function spansWholeMatch(captureGroup) {
16242
+ return captureGroup === void 0 || captureGroup === 0;
16243
+ }
16244
+ function captureGroupCount(pattern, flags) {
16245
+ try {
16246
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags));
16247
+ const result = probe.exec("");
16248
+ return result ? result.length - 1 : void 0;
16249
+ } catch {
16250
+ return void 0;
16251
+ }
16252
+ }
16253
+ var MAX_PATTERN_LENGTH = 2e3;
16254
+ var RegexMatcher = external_exports.strictObject({
16255
+ type: external_exports.literal("regex"),
16256
+ pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16257
+ flags: external_exports.string().default("gi"),
16258
+ captureGroup: external_exports.number().int().nonnegative().optional()
16259
+ }).refine((v) => isValidRegex(v.pattern, v.flags), {
16260
+ message: "pattern/flags do not form a valid JavaScript regular expression",
16261
+ path: ["pattern"]
16262
+ }).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
16263
+ message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
16264
+ path: ["pattern"]
16265
+ }).superRefine((v, ctx) => {
16266
+ if (v.captureGroup === void 0) return;
16267
+ const groups = captureGroupCount(v.pattern, v.flags);
16268
+ if (groups === void 0 || v.captureGroup <= groups) return;
16269
+ ctx.addIssue({
16270
+ code: "custom",
16271
+ path: ["captureGroup"],
16272
+ message: `captureGroup ${String(v.captureGroup)} is out of range \u2014 the pattern declares ${String(groups)} capture group(s), so valid values are 0-${String(groups)}. An out-of-range group never matches, which would make the rule silently never fire.`
16273
+ });
16274
+ });
16275
+ var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
16276
+ var MATCHER_TYPES = MatcherType.options;
16277
+ var AppliesTo = external_exports.strictObject({
16278
+ // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16279
+ extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16280
+ }).meta({ id: "AppliesTo" });
16281
+ var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
16282
+ var PostValidatorRef = external_exports.union(
16283
+ [
16284
+ PostValidatorName,
16285
+ external_exports.strictObject({
16286
+ name: PostValidatorName,
16287
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16288
+ })
16289
+ ],
16290
+ {
16291
+ // A union reports one collapsed issue for every way its arms can fail, so
16292
+ // this has to describe the whole shape rather than just the name — it is
16293
+ // what an author sees for a misspelled name AND for a stray key in the
16294
+ // object form. The names come from the enum so the message cannot go
16295
+ // stale. Without it Zod says only "Invalid input", which is precisely the
16296
+ // no-feedback outcome this schema exists to remove.
16297
+ error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
16298
+ " or "
16299
+ )}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
16300
+ }
16301
+ ).meta({ id: "PostValidatorRef" });
16302
+ var RequiresNearby = external_exports.strictObject({
16303
+ // Each array, when present, must be non-empty and contain non-empty strings —
16304
+ // an empty/blank criterion would either never fire or (for labels) match
16305
+ // everything.
16306
+ categories: external_exports.array(DetectionCategory).min(1).optional(),
16307
+ ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16308
+ labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16309
+ windowChars: external_exports.number().int().positive().default(160),
16310
+ // Optional confidence bump applied when a gated match is corroborated. Capped
16311
+ // small: it nudges confidence, it does not assert certainty.
16312
+ confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16313
+ }).refine(
16314
+ (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16315
+ { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16316
+ ).meta({ id: "RequiresNearby" });
16317
+ var RuleFixture = external_exports.strictObject({
16318
+ label: external_exports.string(),
16319
+ text: external_exports.string().max(5e4),
16320
+ shouldMatch: external_exports.boolean(),
16321
+ // Simulated file context for the scan, so fixtures can assert `appliesTo`
16322
+ // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16323
+ filePath: external_exports.string().optional(),
16324
+ expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
16325
+ }).meta({ id: "RuleFixture" });
16326
+ var Rule = external_exports.strictObject({
16327
+ // A pinned literal over a STRICT object, and the two together decide how this
16328
+ // format may grow. A rule carrying a key not listed below is refused with
16329
+ // `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
16330
+ // `invalid_value`. So the only additive path is adding an OPTIONAL field here
16331
+ // — that keeps every rule authored before it valid — and a rule author has no
16332
+ // way to introduce a field of their own or to opt into a later version.
16333
+ // Widening the format means changing this literal and every consumer of it.
16334
+ specVersion: external_exports.literal(1),
16335
+ // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16336
+ // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16337
+ // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16338
+ // therefore carries no namespace and is not globally unique across publishers;
16339
+ // never feed one to splitDetectionId. `category` below (per-rule) is the
16340
+ // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16341
+ id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16342
+ name: external_exports.string(),
16343
+ category: DetectionCategory,
16344
+ severity: Severity,
16345
+ matcher: Matcher,
16346
+ appliesTo: AppliesTo.optional(),
16347
+ postValidators: external_exports.array(PostValidatorRef).optional(),
16348
+ requiresNearby: RequiresNearby.optional(),
16349
+ examples: external_exports.array(external_exports.string()).optional()
16350
+ }).meta({ id: "Rule" });
16351
+ var Author = external_exports.object({
16352
+ name: external_exports.string(),
16353
+ email: external_exports.email().optional(),
16354
+ url: external_exports.url().optional()
16355
+ }).meta({ id: "Author" });
16356
+ var PackManifest = external_exports.object({
16357
+ specVersion: external_exports.literal(1),
16358
+ id: external_exports.string(),
16359
+ name: external_exports.string(),
16360
+ version: external_exports.string(),
16361
+ rules: external_exports.array(external_exports.string()),
16362
+ // Optional attribution/provenance — consumed by the rule marketplace.
16363
+ description: external_exports.string().optional(),
16364
+ author: Author.optional(),
16365
+ license: external_exports.string().optional(),
16366
+ sourceUrl: external_exports.url().optional()
16367
+ }).meta({ id: "PackManifest" });
16368
+
16837
16369
  // ../../packages/schema/src/zod/detection.ts
16838
16370
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
16839
16371
  var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
@@ -17032,6 +16564,231 @@ function buildDetectionsList(summaries, query) {
17032
16564
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17033
16565
  }
17034
16566
 
16567
+ // ../../packages/schema/src/zod/inventory.ts
16568
+ var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16569
+ var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16570
+ var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16571
+ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16572
+ var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16573
+ var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16574
+ var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16575
+ var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
16576
+ id: "HarnessId"
16577
+ });
16578
+ var AccessCounts = external_exports.object({
16579
+ open: external_exports.number().int().nonnegative(),
16580
+ approved: external_exports.number().int().nonnegative(),
16581
+ blocked: external_exports.number().int().nonnegative(),
16582
+ total: external_exports.number().int().nonnegative()
16583
+ }).meta({ id: "AccessCounts" });
16584
+ var AssetSummary = external_exports.object({
16585
+ id: external_exports.string(),
16586
+ type: AssetType,
16587
+ name: external_exports.string(),
16588
+ sub: external_exports.string(),
16589
+ flags: external_exports.array(Flag),
16590
+ /** MCP servers only — omitted for all other types. */
16591
+ trust: TrustLevel.optional()
16592
+ }).meta({ id: "AssetSummary" });
16593
+ var ProjectSummary = external_exports.object({
16594
+ id: external_exports.string(),
16595
+ name: external_exports.string(),
16596
+ repo: external_exports.string(),
16597
+ visibility: Visibility,
16598
+ language: external_exports.string(),
16599
+ policyDefault: AccessLevel,
16600
+ updatedAt: external_exports.iso.datetime(),
16601
+ accessCounts: AccessCounts,
16602
+ findingsCount: external_exports.number().int().nonnegative()
16603
+ }).meta({ id: "ProjectSummary" });
16604
+ var HarnessCategory = external_exports.object({
16605
+ /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16606
+ type: AssetType,
16607
+ assets: external_exports.array(AssetSummary)
16608
+ });
16609
+ var HarnessSummary = external_exports.object({
16610
+ id: HarnessId,
16611
+ label: external_exports.string(),
16612
+ kind: external_exports.string(),
16613
+ version: external_exports.string(),
16614
+ sessions: external_exports.number().int().nonnegative(),
16615
+ assetCount: external_exports.number().int().nonnegative(),
16616
+ flagCount: external_exports.number().int().nonnegative(),
16617
+ projects: external_exports.array(ProjectSummary),
16618
+ categories: external_exports.array(HarnessCategory)
16619
+ }).meta({ id: "HarnessSummary" });
16620
+ var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16621
+ var AssetGroup = external_exports.object({
16622
+ /** Group key — never project (enforced at service layer). */
16623
+ type: AssetType,
16624
+ total: external_exports.number().int().nonnegative(),
16625
+ /**
16626
+ * MCP group only — omitted for all other types.
16627
+ * Partial: only TrustLevel keys with non-zero counts are included.
16628
+ * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16629
+ */
16630
+ trustRollup: external_exports.object({
16631
+ "known-good": external_exports.number().int().nonnegative(),
16632
+ risky: external_exports.number().int().nonnegative(),
16633
+ unapproved: external_exports.number().int().nonnegative()
16634
+ }).partial().strict().optional(),
16635
+ /**
16636
+ * Partial: only Flag keys with non-zero counts are included.
16637
+ * Strict: unknown keys are rejected — only Flag values are valid keys.
16638
+ */
16639
+ flagRollup: external_exports.object({
16640
+ update: external_exports.number().int().nonnegative(),
16641
+ stale: external_exports.number().int().nonnegative(),
16642
+ conflict: external_exports.number().int().nonnegative(),
16643
+ unknown: external_exports.number().int().nonnegative(),
16644
+ change: external_exports.number().int().nonnegative(),
16645
+ untracked: external_exports.number().int().nonnegative(),
16646
+ risk: external_exports.number().int().nonnegative(),
16647
+ findings: external_exports.number().int().nonnegative()
16648
+ }).partial().strict(),
16649
+ items: external_exports.array(AssetSummary)
16650
+ }).meta({ id: "AssetGroup" });
16651
+ var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16652
+ var McpTool = external_exports.object({
16653
+ name: external_exports.string(),
16654
+ signature: external_exports.string(),
16655
+ description: external_exports.string(),
16656
+ write: external_exports.boolean(),
16657
+ /** Non-null string when tool is dangerous / blocked; null otherwise. */
16658
+ risk: external_exports.string().nullable()
16659
+ }).meta({ id: "McpTool" });
16660
+ var AssetFindingRef = external_exports.object({
16661
+ id: external_exports.string(),
16662
+ title: external_exports.string(),
16663
+ note: external_exports.string()
16664
+ });
16665
+ var AssetDetail = AssetSummary.extend({
16666
+ /** string | null — null when no description is available. */
16667
+ description: external_exports.string().nullable(),
16668
+ /** trustLevel | null — null for non-MCP assets. */
16669
+ trust: TrustLevel.nullable(),
16670
+ /** Type-specific raw key/values — FE renders the grid. */
16671
+ meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16672
+ /** always present — object when there is an active finding, null when absent. */
16673
+ finding: AssetFindingRef.nullable(),
16674
+ /** MCP exposed-tools list — omitted for non-mcp. */
16675
+ tools: external_exports.array(McpTool).optional()
16676
+ }).meta({ id: "AssetDetail" });
16677
+ var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16678
+ var InventoryStats = external_exports.object({
16679
+ /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16680
+ attention: external_exports.number().int().nonnegative(),
16681
+ byType: external_exports.object({
16682
+ project: external_exports.number().int().nonnegative(),
16683
+ skill: external_exports.number().int().nonnegative(),
16684
+ mcp: external_exports.number().int().nonnegative(),
16685
+ hook: external_exports.number().int().nonnegative(),
16686
+ config: external_exports.number().int().nonnegative()
16687
+ }),
16688
+ harnesses: external_exports.number().int().nonnegative(),
16689
+ mcpTrust: external_exports.object({
16690
+ "known-good": external_exports.number().int().nonnegative(),
16691
+ risky: external_exports.number().int().nonnegative(),
16692
+ unapproved: external_exports.number().int().nonnegative()
16693
+ })
16694
+ }).meta({ id: "InventoryStats" });
16695
+ var FileSummary = external_exports.object({
16696
+ path: external_exports.string(),
16697
+ name: external_exports.string(),
16698
+ origin: Origin,
16699
+ /** Effective access (override applied). */
16700
+ access: AccessLevel,
16701
+ /** True when a file_access_override differs from the computed default. */
16702
+ isCustom: external_exports.boolean(),
16703
+ findings: external_exports.number().int().nonnegative(),
16704
+ /** When the file was auto-blocked by a detection; null when not blocked. */
16705
+ blockedAt: external_exports.iso.datetime().nullable().optional(),
16706
+ /** Why the file was blocked; null when absent. */
16707
+ note: external_exports.string().nullable().optional()
16708
+ }).meta({ id: "FileSummary" });
16709
+ var FolderSummary = external_exports.object({
16710
+ name: external_exports.string(),
16711
+ path: external_exports.string(),
16712
+ /** Rollup of effective access across all descendants. */
16713
+ accessCounts: AccessCounts
16714
+ }).meta({ id: "FolderSummary" });
16715
+ var ProjectTreeResponse = external_exports.object({
16716
+ project: external_exports.object({
16717
+ id: external_exports.string(),
16718
+ repo: external_exports.string(),
16719
+ visibility: Visibility
16720
+ }),
16721
+ path: external_exports.string(),
16722
+ /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16723
+ folders: external_exports.array(FolderSummary).optional(),
16724
+ files: external_exports.array(FileSummary)
16725
+ }).meta({ id: "ProjectTreeResponse" });
16726
+ var FileDetail = FileSummary.extend({
16727
+ project: external_exports.object({
16728
+ repo: external_exports.string(),
16729
+ visibility: Visibility,
16730
+ language: external_exports.string(),
16731
+ policyDefault: AccessLevel,
16732
+ updatedAt: external_exports.iso.datetime()
16733
+ }),
16734
+ findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16735
+ }).meta({ id: "FileDetail" });
16736
+ var SetFileAccessBody = external_exports.object({
16737
+ path: external_exports.string(),
16738
+ access: AccessLevel
16739
+ }).meta({ id: "SetFileAccessBody" });
16740
+ var SetFileAccessResponse = external_exports.object({
16741
+ file: FileSummary,
16742
+ accessCounts: AccessCounts
16743
+ }).meta({ id: "SetFileAccessResponse" });
16744
+ var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16745
+ var HarnessEventItem = external_exports.object({
16746
+ kind: HarnessEventKind,
16747
+ title: external_exports.string(),
16748
+ detail: external_exports.string(),
16749
+ occurredAt: external_exports.iso.datetime(),
16750
+ findingId: external_exports.string().nullable().optional()
16751
+ }).meta({ id: "HarnessEventItem" });
16752
+ var HarnessEventsResponse = external_exports.object({
16753
+ counts: external_exports.object({
16754
+ block: external_exports.number().int().nonnegative(),
16755
+ redact: external_exports.number().int().nonnegative(),
16756
+ warn: external_exports.number().int().nonnegative()
16757
+ }),
16758
+ items: external_exports.array(HarnessEventItem)
16759
+ }).meta({ id: "HarnessEventsResponse" });
16760
+ var RescanResponse = external_exports.object({
16761
+ jobId: external_exports.string(),
16762
+ startedAt: external_exports.iso.datetime()
16763
+ }).meta({ id: "RescanResponse" });
16764
+ var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16765
+ var ListAssetsQuery = external_exports.object({
16766
+ /** Filter by one or more AssetType values; absent means all types. */
16767
+ type: external_exports.array(AssetType).optional(),
16768
+ /** Free-text search term. */
16769
+ q: external_exports.string().optional()
16770
+ });
16771
+ var GetProjectTreeQuery = external_exports.object({
16772
+ /** Subtree root path; defaults to repository root when absent. */
16773
+ path: external_exports.string().optional(),
16774
+ /** Free-text filter applied to file paths. */
16775
+ q: external_exports.string().optional(),
16776
+ /**
16777
+ * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16778
+ * file across the whole repo (folders omitted, most-recent first), ignoring
16779
+ * `path`/`q` — powers the project-wide "recently blocked" strip.
16780
+ */
16781
+ filter: external_exports.enum(["blocked"]).optional()
16782
+ });
16783
+ var GetProjectFileQuery = external_exports.object({
16784
+ /** Repository-relative file path; absent or empty → 400. */
16785
+ path: external_exports.string()
16786
+ });
16787
+ var GetHarnessEventsQuery = external_exports.object({
16788
+ /** Maximum number of events to return. Range: 1–50; default: 7. */
16789
+ limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16790
+ });
16791
+
17035
16792
  // ../../packages/schema/src/zod/shares.ts
17036
16793
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17037
16794
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17238,6 +16995,127 @@ var EgressWriteSummary = external_exports.object({
17238
16995
  droppedFiles: external_exports.array(external_exports.string()).default([])
17239
16996
  }).meta({ id: "EgressWriteSummary" });
17240
16997
 
16998
+ // ../../packages/schema/src/zod/event.ts
16999
+ var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
17000
+ var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
17001
+ var EventMetadata = external_exports.object({
17002
+ sessionId: external_exports.string().optional(),
17003
+ repo: external_exports.string().optional(),
17004
+ filePath: external_exports.string().optional(),
17005
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
17006
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
17007
+ // arguments or output, which can carry the very value a finding masked
17008
+ // (metadata is stored unredacted). Gives findings on non-file captures a
17009
+ // display location ("via Bash") when no filePath exists.
17010
+ toolName: external_exports.string().optional(),
17011
+ // Set (true) by the worktree scanner when the file is excluded by the
17012
+ // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
17013
+ // generated code can leak real secrets — but the provenance is recorded so
17014
+ // policy/dashboards can treat those findings as informational rather than
17015
+ // blocking. Omitted (not false) for tracked files and non-scan events.
17016
+ gitignored: external_exports.boolean().optional(),
17017
+ // Set (true) ONLY when the event's `content` is the COMPLETE file at
17018
+ // capture time (a worktree scan reading from disk). Hook-captured edits
17019
+ // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
17020
+ // this. The resolver-on-ingest keys its fixed-at-source dropout
17021
+ // diff on this marker: only a whole-file snapshot can prove a previously
17022
+ // open finding is gone; a fragment's absence proves nothing (the secret
17023
+ // may live outside the hunk). Omitted (not false) for fragments and
17024
+ // non-scan events, so pre-marker clients safely default to the
17025
+ // non-authoritative path.
17026
+ wholeFile: external_exports.boolean().optional(),
17027
+ model: external_exports.string().optional(),
17028
+ turnIndex: external_exports.number().int().nonnegative().optional(),
17029
+ // Distributed-tracing correlation. `correlationId` ties a recorded event back
17030
+ // to the request that captured/ingested it (a UUID, generated independently of
17031
+ // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
17032
+ // originating span when telemetry is enabled. Both optional + backward
17033
+ // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
17034
+ correlationId: external_exports.uuid().optional(),
17035
+ traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
17036
+ // Ids of the detection exceptions that downgraded findings in this capture
17037
+ // to 'allow' — the enforcement audit trail's link back to the grant that
17038
+ // authorized the bypass. Absent on captures where no exception applied.
17039
+ exceptionIds: external_exports.array(external_exports.guid()).optional()
17040
+ }).meta({ id: "EventMetadata" });
17041
+ var Event = external_exports.object({
17042
+ id: external_exports.guid(),
17043
+ sourceTool: SourceTool,
17044
+ kind: EventKind,
17045
+ occurredAt: external_exports.iso.datetime(),
17046
+ contentHash: external_exports.string(),
17047
+ content: external_exports.string(),
17048
+ metadata: EventMetadata.optional()
17049
+ }).meta({ id: "Event" });
17050
+ var IngestEvent = Event.meta({ id: "IngestEvent" });
17051
+ var IngestBatch = external_exports.object({
17052
+ events: external_exports.array(IngestEvent).min(1).max(100),
17053
+ // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
17054
+ // additionally rejects any event whose contentHash the store has already
17055
+ // recorded — for re-runnable bulk ingest (worktree scan, transcript
17056
+ // backfill), where a re-run mints fresh event ids for identical content and
17057
+ // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
17058
+ // two genuinely separate prompts can be byte-identical and both belong on
17059
+ // the timeline.
17060
+ dedupe: external_exports.literal("content-hash").optional()
17061
+ }).meta({ id: "IngestBatch" });
17062
+
17063
+ // ../../packages/schema/src/zod/exception.ts
17064
+ var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
17065
+ var ExceptionConditions = external_exports.object({
17066
+ repo: external_exports.string().optional(),
17067
+ sourceTool: external_exports.string().optional(),
17068
+ provider: external_exports.string().optional()
17069
+ }).strict();
17070
+ var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
17071
+ var DetectionException = external_exports.object({
17072
+ id: external_exports.guid(),
17073
+ ruleId: external_exports.string(),
17074
+ // Denormalized from the rule, for reporting — never matched on.
17075
+ category: DetectionCategory,
17076
+ // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
17077
+ // fingerprint, never the raw value, and never reversible. Matching recomputes
17078
+ // the fingerprint from a fresh capture; the value itself is never stored.
17079
+ // Shape-constrained so a malformed — or accidentally raw — value is rejected
17080
+ // at the boundary rather than persisted.
17081
+ valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
17082
+ // Version of the fingerprint key the grant was written under; a rotated key
17083
+ // invalidates old grants rather than silently mismatching them.
17084
+ keyVersion: external_exports.number().int().positive(),
17085
+ // maskMatch() preview of the approved value — never the raw value.
17086
+ maskedValue: external_exports.string(),
17087
+ capability: ExceptionCapability.default("suppress"),
17088
+ scope: ExceptionScope,
17089
+ expiresAt: external_exports.iso.datetime().nullable(),
17090
+ maxUses: external_exports.number().int().positive().nullable(),
17091
+ useCount: external_exports.number().int().nonnegative(),
17092
+ lastUsedAt: external_exports.iso.datetime().nullable(),
17093
+ // Mandatory: every grant carries the human reason it exists.
17094
+ justification: external_exports.string().min(1),
17095
+ conditions: ExceptionConditions.nullable(),
17096
+ createdBy: external_exports.string(),
17097
+ createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
17098
+ createdAt: external_exports.iso.datetime(),
17099
+ updatedAt: external_exports.iso.datetime(),
17100
+ // Revocation is terminal and retained — consumed/expired/revoked rows are
17101
+ // audit evidence; nothing in the exception lifecycle hard-deletes.
17102
+ revokedAt: external_exports.iso.datetime().nullable(),
17103
+ revokedBy: external_exports.string().nullable(),
17104
+ revokeReason: external_exports.string().nullable()
17105
+ });
17106
+ var ExceptionBundleEntry = DetectionException.pick({
17107
+ id: true,
17108
+ ruleId: true,
17109
+ valueFingerprint: true,
17110
+ keyVersion: true,
17111
+ capability: true,
17112
+ expiresAt: true,
17113
+ maxUses: true,
17114
+ useCount: true,
17115
+ conditions: true
17116
+ });
17117
+ var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
17118
+
17241
17119
  // ../../packages/schema/src/zod/exception-action.ts
17242
17120
  var confirmation = external_exports.string().optional();
17243
17121
  var ApproveBlockedInput = external_exports.object({
@@ -17280,10 +17158,11 @@ function toApiAction(dbVal) {
17280
17158
  }
17281
17159
  function toApiCategory(dbVal) {
17282
17160
  if (dbVal === "code_context") return "source_code";
17283
- return dbVal;
17161
+ const parsed = FindingCategory.safeParse(dbVal);
17162
+ return parsed.success ? parsed.data : "custom";
17284
17163
  }
17285
17164
  function toApiProvider(sourceTool) {
17286
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17165
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17287
17166
  }
17288
17167
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17289
17168
  function foldGroupStatus(instanceStatuses) {
@@ -17852,6 +17731,7 @@ var ListVaultDerefsResponse = external_exports.object({
17852
17731
  });
17853
17732
  var VaultKeyCustody = external_exports.string();
17854
17733
  var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
17734
+ var VAULT_CONSENT_VERSION = 1;
17855
17735
  var VaultConsent = external_exports.object({
17856
17736
  acknowledgedAt: external_exports.iso.datetime(),
17857
17737
  version: external_exports.number().int().positive()
@@ -17859,7 +17739,14 @@ var VaultConsent = external_exports.object({
17859
17739
 
17860
17740
  // ../../packages/schema/src/zod/local.ts
17861
17741
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17862
- var RunMode = external_exports.enum(["standalone"]);
17742
+ var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17743
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17744
+ var ControlPlaneConnection = external_exports.object({
17745
+ endpoint: external_exports.string().min(1),
17746
+ // Display name for the deployment, shown instead of the raw endpoint.
17747
+ label: external_exports.string().min(1).optional(),
17748
+ attachedAt: external_exports.iso.datetime()
17749
+ }).meta({ id: "ControlPlaneConnection" });
17863
17750
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17864
17751
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17865
17752
  var ModelJudgeConsent = external_exports.object({
@@ -17868,12 +17755,10 @@ var ModelJudgeConsent = external_exports.object({
17868
17755
  });
17869
17756
  var WorkspaceSettings = external_exports.object({
17870
17757
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17871
- // Settings files written by earlier releases may carry the retired 'attached'
17872
- // value; it parses as 'standalone' so those files keep loading.
17873
- runMode: external_exports.preprocess(
17874
- (v) => v === "attached" ? "standalone" : v,
17875
- RunMode.default("standalone")
17876
- ),
17758
+ runMode: RunMode.default("standalone"),
17759
+ // Present only while attached; a detach clears it. Its presence is what makes
17760
+ // `runMode: 'attached'` mean anything — see isAttached.
17761
+ controlPlane: ControlPlaneConnection.optional(),
17877
17762
  policy: SimpleDetectionPolicy.default("redact"),
17878
17763
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17879
17764
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -18009,6 +17894,211 @@ function toCaptureDefinitionInput(finding) {
18009
17894
  };
18010
17895
  }
18011
17896
 
17897
+ // ../../packages/schema/src/zod/managed.ts
17898
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
17899
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
17900
+ var ManagedSettingKey = external_exports.enum([
17901
+ "runMode",
17902
+ "historicalAccess",
17903
+ "vaultConsent",
17904
+ "vaultKeyCustody",
17905
+ "vaultInlineReveal",
17906
+ "modelJudgeConsent",
17907
+ "dataSharesInPlace"
17908
+ ]).meta({ id: "ManagedSettingKey" });
17909
+ var ManagedSettingsValues = external_exports.object({
17910
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
17911
+ controlPlane: external_exports.object({
17912
+ endpoint: external_exports.string().min(1),
17913
+ label: external_exports.string().min(1).optional()
17914
+ }).optional(),
17915
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
17916
+ vaultConsent: external_exports.boolean().optional(),
17917
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
17918
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
17919
+ modelJudgeConsent: external_exports.boolean().optional(),
17920
+ dataSharesInPlace: external_exports.boolean().optional()
17921
+ }).meta({ id: "ManagedSettingsValues" });
17922
+ var ManagedSettings = external_exports.object({
17923
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
17924
+ // Shown on every locked control, so the user can tell an administrative
17925
+ // decision from a bug. Absent renders as a generic "your organization".
17926
+ organization: external_exports.string().min(1).optional(),
17927
+ // What the administrator pinned.
17928
+ values: ManagedSettingsValues.default({}),
17929
+ // Which of those the user may not change. A key here with no matching value
17930
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
17931
+ // the user may still override. The two are separable on purpose.
17932
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
17933
+ }).meta({ id: "ManagedSettings" });
17934
+
17935
+ // ../../packages/schema/src/zod/policy.ts
17936
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
17937
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
17938
+ var Policy = external_exports.object({
17939
+ id: external_exports.guid(),
17940
+ scope: PolicyScope,
17941
+ target: PolicyTarget,
17942
+ action: ActionTaken,
17943
+ enabled: external_exports.boolean().default(true),
17944
+ customKeywords: external_exports.array(external_exports.string()).optional(),
17945
+ // Display name — optional so older policy rows without name still parse.
17946
+ // Added for the findings API (policy.name column migration).
17947
+ name: external_exports.string().optional()
17948
+ }).meta({ id: "Policy" });
17949
+ var PolicyBundle = external_exports.object({
17950
+ version: external_exports.string(),
17951
+ policies: external_exports.array(Policy),
17952
+ // Rules from the installed marketplace packs (snapshotted by the
17953
+ // control plane). The plugin registers these in addition to its bundled
17954
+ // packs. Optional so older backends — and older on-disk caches — that omit
17955
+ // the field still parse; consumers read `bundle.rules ?? []`.
17956
+ rules: external_exports.array(Rule).optional(),
17957
+ // When true, `rules` IS the complete effective ruleset and the runtime must
17958
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
17959
+ // after reading the user's installed snapshot (installed_packs, enabled
17960
+ // packs only), which is how detection updates stay manual: new bundled
17961
+ // rules run only after the user applies the pack update. Absent/false keeps
17962
+ // the historical composition (bundled packs + rules) — older caches.
17963
+ rulesComplete: external_exports.boolean().optional(),
17964
+ // Active detection exceptions, evaluation subset only (see
17965
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
17966
+ // on-disk caches — that omit the field still parse; consumers read
17967
+ // `bundle.exceptions ?? []`.
17968
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
17969
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
17970
+ // A second axis over the same `redact` action, carried beside the policies
17971
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
17972
+ // widening Policy itself would change a persisted shape to express something
17973
+ // only the in-memory bundle needs. Optional so an older producer — or an
17974
+ // older on-disk cache — still parses; consumers read `?? []` and get the
17975
+ // pre-existing one-way behaviour, which is the safe direction to default.
17976
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
17977
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
17978
+ // from a versioned installed pack. Optional so older backends — and older
17979
+ // on-disk caches — that omit the field still parse; consumers fall back to
17980
+ // the rule's own spec version. NOT the bundle version above — see
17981
+ // installedRuleset's ruleVersions for the source of truth.
17982
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
17983
+ customKeywords: external_exports.array(external_exports.string()),
17984
+ fetchedAt: external_exports.iso.datetime()
17985
+ }).meta({ id: "PolicyBundle" });
17986
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
17987
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
17988
+ var CATEGORY_PEAK_SEVERITY = {
17989
+ secret: "critical",
17990
+ financial: "critical",
17991
+ // core-financial/credit-card
17992
+ code_flaw: "critical",
17993
+ pii: "high",
17994
+ phi: "high",
17995
+ custom: "high",
17996
+ // user-defined; conservative
17997
+ code_context: "low",
17998
+ config: "low"
17999
+ // observe-only; floors to monitor regardless
18000
+ };
18001
+ function severityFloorPolicy(category) {
18002
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18003
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18004
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18005
+ }
18006
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18007
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18008
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18009
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18010
+ var BUILTIN_POLICY_SPECS = {
18011
+ monitor: {
18012
+ name: "Monitor",
18013
+ action: "log",
18014
+ reversible: false,
18015
+ description: "Log every match for audit. The request is allowed through untouched."
18016
+ },
18017
+ warn: {
18018
+ name: "Warn",
18019
+ action: "warn",
18020
+ reversible: false,
18021
+ description: "Allow the request, but warn the user inline before it is sent."
18022
+ },
18023
+ redact: {
18024
+ name: "Redact",
18025
+ action: "redact",
18026
+ reversible: false,
18027
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18028
+ },
18029
+ vault: {
18030
+ name: "Redact & Vault",
18031
+ action: "redact",
18032
+ reversible: true,
18033
+ description: "Strip the matched value from the request and keep an encrypted, recoverable copy in the local vault, leaving a pointer in its place. Needs the vault consent granted under Settings; without it this behaves as Redact."
18034
+ },
18035
+ block: {
18036
+ name: "Block",
18037
+ action: "block",
18038
+ reversible: false,
18039
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18040
+ }
18041
+ };
18042
+ function builtinPolicyToAction(id) {
18043
+ return BUILTIN_POLICY_SPECS[id].action;
18044
+ }
18045
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18046
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18047
+ );
18048
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18049
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18050
+ );
18051
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18052
+ function builtinPolicyIsReversible(id) {
18053
+ return BUILTIN_POLICY_SPECS[id].reversible;
18054
+ }
18055
+ function policyIdIsReversible(policyId) {
18056
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18057
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18058
+ return builtinPolicyIsReversible(id);
18059
+ }
18060
+ var DEFAULT_ACTIONS = Object.fromEntries(
18061
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18062
+ );
18063
+ var BUILTIN_POLICIES = Object.fromEntries(
18064
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18065
+ );
18066
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18067
+ function policyIdToAction(policyId) {
18068
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18069
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18070
+ return BUILTIN_POLICIES[id].action;
18071
+ }
18072
+ var UsedByItem = external_exports.object({
18073
+ id: external_exports.string(),
18074
+ name: external_exports.string(),
18075
+ ruleCount: external_exports.number().int().nonnegative(),
18076
+ enabled: external_exports.boolean()
18077
+ }).meta({ id: "UsedByItem" });
18078
+ var PolicyListItem = external_exports.object({
18079
+ id: external_exports.string(),
18080
+ kind: PolicyKind,
18081
+ name: external_exports.string(),
18082
+ enabled: external_exports.boolean(),
18083
+ usedByCount: external_exports.number().int().nonnegative()
18084
+ }).meta({ id: "PolicyListItem" });
18085
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18086
+ var PolicyDetail = external_exports.object({
18087
+ specVersion: external_exports.literal(1),
18088
+ id: external_exports.string(),
18089
+ kind: PolicyKind,
18090
+ name: external_exports.string(),
18091
+ enabled: external_exports.boolean(),
18092
+ description: external_exports.string(),
18093
+ usedBy: external_exports.array(UsedByItem)
18094
+ }).meta({ id: "PolicyDetail" });
18095
+ var PolicyStatsResponse = external_exports.object({
18096
+ policies: external_exports.number().int().nonnegative(),
18097
+ builtin: external_exports.number().int().nonnegative(),
18098
+ custom: external_exports.number().int().nonnegative(),
18099
+ detectionsGoverned: external_exports.number().int().nonnegative()
18100
+ }).meta({ id: "PolicyStatsResponse" });
18101
+
18012
18102
  // ../../packages/schema/src/zod/project-files.ts
18013
18103
  var ProjectFileInput = external_exports.object({
18014
18104
  path: external_exports.string().min(1),
@@ -18087,44 +18177,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18087
18177
  NoRemediationDecision
18088
18178
  ]);
18089
18179
 
18090
- // ../../packages/schema/src/zod/rule-test.ts
18091
- var TestRulesRequest = external_exports.object({
18092
- rules: external_exports.array(Rule).min(1).max(100),
18093
- text: external_exports.string().max(5e4).optional(),
18094
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18095
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18096
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18097
- }).meta({ id: "TestRulesRequest" });
18098
- var RuleTestMatch = external_exports.object({
18099
- ruleId: external_exports.string(),
18100
- category: DetectionCategory,
18101
- severity: Severity,
18102
- span: Span,
18103
- confidence: external_exports.number().min(0).max(1),
18104
- match: external_exports.string()
18105
- }).meta({ id: "RuleTestMatch" });
18106
- var FixtureResult = external_exports.object({
18107
- label: external_exports.string(),
18108
- shouldMatch: external_exports.boolean(),
18109
- didMatch: external_exports.boolean(),
18110
- passed: external_exports.boolean(),
18111
- matches: external_exports.array(RuleTestMatch)
18112
- }).meta({ id: "FixtureResult" });
18113
- var TestRulesResponse = external_exports.object({
18114
- // Present only when the request supplied `text`.
18115
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18116
- fixtures: external_exports.array(FixtureResult),
18117
- summary: external_exports.object({
18118
- total: external_exports.number().int().nonnegative(),
18119
- passed: external_exports.number().int().nonnegative(),
18120
- failed: external_exports.number().int().nonnegative()
18121
- }),
18122
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18123
- // `validator`), so they silently never match. Surfaced so an author is not
18124
- // misled by a green run that actually skipped a rule.
18125
- unsupportedRuleIds: external_exports.array(external_exports.string())
18126
- }).meta({ id: "TestRulesResponse" });
18127
-
18128
18180
  // ../../packages/schema/src/zod/security.ts
18129
18181
  var SeveritySummaryItem = external_exports.object({
18130
18182
  severity: Severity,
@@ -18222,10 +18274,22 @@ var TopSourcesQuery = external_exports.object({
18222
18274
  // Omit for both kinds.
18223
18275
  kind: external_exports.enum(SOURCE_KINDS).optional()
18224
18276
  });
18225
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18277
+ var Provider = Harness.extract([
18278
+ "ClaudeCode",
18279
+ "Cursor",
18280
+ "Codex",
18281
+ "Antigravity",
18282
+ "ClaudeAi",
18283
+ "ChatGpt",
18284
+ "Copilot",
18285
+ "Api"
18286
+ ]).meta({ id: "Provider" });
18226
18287
  var ScanCoverageProvider = external_exports.object({
18227
18288
  provider: Provider,
18228
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18289
+ // Percent of that provider's traffic the shipped capture surface reaches.
18290
+ // A curated business fact, constant across every `range` — not a measured
18291
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18292
+ // above the block for where these numbers are decided.
18229
18293
  coverage: external_exports.number().int().min(0).max(100),
18230
18294
  supported: external_exports.boolean()
18231
18295
  }).meta({ id: "ScanCoverageProvider" });
@@ -18279,6 +18343,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18279
18343
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18280
18344
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18281
18345
 
18346
+ // ../../packages/schema/src/zod/settings-action.ts
18347
+ var SaveSettingsInput = external_exports.object({
18348
+ historicalAccess: external_exports.string(),
18349
+ modelJudgeConsent: external_exports.boolean(),
18350
+ vaultConsent: external_exports.string(),
18351
+ vaultInlineReveal: external_exports.string()
18352
+ });
18353
+ var AttachInput = external_exports.object({
18354
+ endpoint: external_exports.string(),
18355
+ label: external_exports.string().optional()
18356
+ });
18357
+
18282
18358
  // ../../packages/schema/src/zod/triage.ts
18283
18359
  var TriageHit = external_exports.object({
18284
18360
  ruleId: external_exports.string(),
@@ -18293,7 +18369,7 @@ var TriageHit = external_exports.object({
18293
18369
  valueFingerprint: external_exports.string().optional(),
18294
18370
  keyVersion: external_exports.number().int().nonnegative().optional()
18295
18371
  });
18296
- var TriagePolicy = BuiltinPolicyId;
18372
+ var TriagePolicy = CategoryPolicyId;
18297
18373
  var TriageCategoryRec = external_exports.object({
18298
18374
  category: DetectionCategory,
18299
18375
  action: TriagePolicy,
@@ -18511,8 +18587,11 @@ function chmodBestEffort(path, mode) {
18511
18587
  function tightenDir(dir) {
18512
18588
  chmodBestEffort(dir, DATA_DIR_MODE);
18513
18589
  }
18590
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18591
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18592
+ }
18514
18593
  function ensureDataDirSync(dir) {
18515
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18594
+ mkdirOwnerOnlySync(dir, true);
18516
18595
  tightenDir(dir);
18517
18596
  }
18518
18597
  function dbSidecars(file2) {
@@ -18530,6 +18609,25 @@ function backupPath(file2, tag) {
18530
18609
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18531
18610
  }
18532
18611
  var STALE_PARTIAL_MS = 5 * 6e4;
18612
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18613
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18614
+ var SNAPSHOT_STAGING_COPY = "copy";
18615
+ function createSnapshotStaging(backup) {
18616
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18617
+ rmSync2(stage, { recursive: true, force: true });
18618
+ mkdirOwnerOnlySync(stage);
18619
+ tightenDir(stage);
18620
+ return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
18621
+ }
18622
+ function idleMs(entry) {
18623
+ for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
18624
+ try {
18625
+ return Date.now() - statSync(candidate).mtimeMs;
18626
+ } catch {
18627
+ }
18628
+ }
18629
+ return null;
18630
+ }
18533
18631
  function reapStalePartials(file2) {
18534
18632
  const dir = dirname(file2);
18535
18633
  const prefix = `${basename(file2)}.`;
@@ -18540,30 +18638,34 @@ function reapStalePartials(file2) {
18540
18638
  return;
18541
18639
  }
18542
18640
  for (const name of entries) {
18543
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18544
- const partial2 = join(dir, name);
18641
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18642
+ const staging = join(dir, name);
18545
18643
  try {
18546
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18547
- rmSync2(partial2, { force: true });
18644
+ const idle = idleMs(staging);
18645
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18646
+ rmSync2(staging, { recursive: true, force: true });
18548
18647
  }
18549
18648
  } catch {
18550
18649
  }
18551
18650
  }
18552
18651
  }
18553
18652
  function snapshotStore(db, backup) {
18554
- const partial2 = `${backup}.partial`;
18653
+ const { stage, copy } = createSnapshotStaging(backup);
18555
18654
  try {
18556
- rmSync2(partial2, { force: true });
18557
- db.prepare("VACUUM INTO ?").run(partial2);
18558
- tightenFile(partial2);
18559
- renameSync2(partial2, backup);
18655
+ db.prepare("VACUUM INTO ?").run(copy);
18656
+ tightenFile(copy);
18657
+ renameSync2(copy, backup);
18560
18658
  } catch (error51) {
18561
18659
  try {
18562
- rmSync2(partial2, { force: true });
18660
+ rmSync2(stage, { recursive: true, force: true });
18563
18661
  } catch {
18564
18662
  }
18565
18663
  throw error51;
18566
18664
  }
18665
+ try {
18666
+ rmSync2(stage, { recursive: true, force: true });
18667
+ } catch {
18668
+ }
18567
18669
  }
18568
18670
  function moveStoreAside(file2, backup) {
18569
18671
  const undo = [];
@@ -19291,9 +19393,10 @@ function safeParseStringArray(raw) {
19291
19393
  const parsed = safeJson(raw, null);
19292
19394
  return Array.isArray(parsed) ? parsed : [];
19293
19395
  }
19396
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19294
19397
  function toHarness(raw) {
19295
19398
  const parsed = Harness.safeParse(raw);
19296
- return parsed.success ? parsed.data : "claudecode";
19399
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19297
19400
  }
19298
19401
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19299
19402
  if (row.status) {
@@ -19424,7 +19527,7 @@ var SqliteActivityRepository = class {
19424
19527
  const params = [];
19425
19528
  if (query.harness && query.harness.length > 0) {
19426
19529
  conditions.push(
19427
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19530
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19428
19531
  );
19429
19532
  params.push(...query.harness);
19430
19533
  }
@@ -19631,13 +19734,13 @@ var SqliteActivityRepository = class {
19631
19734
  * The DISTINCT harnesses that actually have sessions (optionally within a
19632
19735
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19633
19736
  * present rather than the full enum. Each stored value is normalized through
19634
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19635
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19737
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19738
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19636
19739
  */
19637
19740
  harnessFacets(fromMs) {
19638
19741
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19639
19742
  const stmt = this.db.prepare(
19640
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19743
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19641
19744
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19642
19745
  );
19643
19746
  const rows = allRows(
@@ -19851,8 +19954,8 @@ var SqliteAuditEventsRepository = class {
19851
19954
  }
19852
19955
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19853
19956
  // (which takes a caller-supplied random id), the id here is MINTED internally
19854
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19855
- // sibling local-store ids (the local store is single-tenant). The deterministic
19957
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
19958
+ // session and message alone, like the sibling local-store ids. The deterministic
19856
19959
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19857
19960
  // AND converge a streaming partial/final split across two incremental passes:
19858
19961
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20817,6 +20920,42 @@ var SqliteFindingsRepository = class {
20817
20920
  this.db = db;
20818
20921
  }
20819
20922
  db;
20923
+ /**
20924
+ * The newest `limit` findings, newest first.
20925
+ *
20926
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
20927
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
20928
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
20929
+ * not on the driving table: SQLite sorts every finding in the store through a
20930
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
20931
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
20932
+ * rather than a constant.
20933
+ *
20934
+ * What it takes to make `started_at` order come out of an index instead:
20935
+ *
20936
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
20937
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
20938
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
20939
+ * satisfying a global `started_at` order across them needs a range merge
20940
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
20941
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
20942
+ * filters the type per row, which lets the LIMIT stop the scan early.
20943
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
20944
+ * purely to stop the tables being reordered. With plain JOINs the planner
20945
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
20946
+ * unary plus ALONE recovers almost none of the win. Both are needed.
20947
+ *
20948
+ * Neither is a micro-optimisation that a later reader should tidy away, and
20949
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
20950
+ * the temp B-tree comes back.
20951
+ *
20952
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
20953
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
20954
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
20955
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
20956
+ * a store whose recent captures carry no findings at all, where the scan walks
20957
+ * the whole index; that is still no worse than the full sort it replaced.
20958
+ */
20820
20959
  recentFindings(opts) {
20821
20960
  const limit = opts?.limit ?? 50;
20822
20961
  const rows = allRows(
@@ -20825,10 +20964,10 @@ var SqliteFindingsRepository = class {
20825
20964
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20826
20965
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20827
20966
  e.event_type AS kind
20828
- FROM inspection_findings f
20829
- JOIN audit_events e ON e.id = f.audit_event_id
20830
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20831
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20967
+ FROM audit_events e
20968
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
20969
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20970
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20832
20971
  ORDER BY e.started_at DESC, f.rowid DESC
20833
20972
  LIMIT :limit`
20834
20973
  ),
@@ -21472,7 +21611,8 @@ var SqliteInspectionDefinitionsRepository = class {
21472
21611
  }
21473
21612
  db;
21474
21613
  insertStmt;
21475
- // Idempotent upsert; returns the content-addressed definition id.
21614
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21615
+ // present keeps the stored row untouched — see the class doc.
21476
21616
  upsert(input) {
21477
21617
  const id = inspectionDefinitionId(input.ruleId, input.version);
21478
21618
  const row = toInspectionDefinitionRow(input, id);
@@ -21616,11 +21756,23 @@ function isParseableBinaryVersion(version2) {
21616
21756
 
21617
21757
  // ../../packages/persistence/src/repositories/installed-packs.ts
21618
21758
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21759
+ function printableRuleId(entry) {
21760
+ if (typeof entry !== "object" || entry === null) return null;
21761
+ const candidate = entry.id;
21762
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21763
+ }
21764
+ function firstIssueReason(error51) {
21765
+ const issue2 = error51.issues[0];
21766
+ if (!issue2) return "unknown";
21767
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21768
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21769
+ }
21770
+ var REJECTED_RULE_DETAIL_CAP = 10;
21619
21771
  function inventorySignature(packs) {
21620
21772
  return packs.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21621
21773
  }
21622
21774
  function hashRules(rulesJson) {
21623
- return createHash2("sha1").update(rulesJson).digest("hex");
21775
+ return createHash2("sha256").update(rulesJson).digest("hex");
21624
21776
  }
21625
21777
  function parseVersion(v) {
21626
21778
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21832,10 +21984,21 @@ var SqliteInstalledPacksRepository = class {
21832
21984
  * (all detection off) instead of falling back to the bundled packs. Every
21833
21985
  * JSON-level failure therefore counts as invalid.
21834
21986
  */
21987
+ /**
21988
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
21989
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
21990
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
21991
+ * winner is whatever order SQLite happens to return, which makes a collision
21992
+ * resolve differently on two machines holding identical stores. Ordering by
21993
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
21994
+ */
21835
21995
  installedRuleset() {
21836
21996
  const rows = allRows(
21837
21997
  this.db.prepare(
21838
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
21998
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
21999
+ namespace, pack_id AS packId
22000
+ FROM installed_packs
22001
+ ORDER BY namespace, pack_id`
21839
22002
  )
21840
22003
  );
21841
22004
  const out = {
@@ -21843,22 +22006,32 @@ var SqliteInstalledPacksRepository = class {
21843
22006
  enabledPacks: 0,
21844
22007
  rules: [],
21845
22008
  invalidRules: 0,
22009
+ rejectedRules: [],
21846
22010
  ruleActions: /* @__PURE__ */ new Map(),
21847
- ruleVersions: /* @__PURE__ */ new Map()
22011
+ ruleVersions: /* @__PURE__ */ new Map(),
22012
+ reversibleRules: /* @__PURE__ */ new Set()
22013
+ };
22014
+ const reject = (pack, ruleId, reason) => {
22015
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22016
+ out.rejectedRules.push({ pack, ruleId, reason });
21848
22017
  };
21849
22018
  for (const row of rows) {
21850
22019
  if (!intToBool(row.enabled)) continue;
21851
22020
  out.enabledPacks += 1;
21852
22021
  const action = policyIdToAction(row.policyId);
22022
+ const reversible = policyIdIsReversible(row.policyId);
22023
+ const pack = `${row.namespace}/${row.packId}`;
21853
22024
  let raw;
21854
22025
  try {
21855
22026
  raw = JSON.parse(row.rulesJson);
21856
22027
  } catch {
21857
22028
  out.invalidRules += 1;
22029
+ reject(pack, null, "rules_json: malformed JSON");
21858
22030
  continue;
21859
22031
  }
21860
22032
  if (!Array.isArray(raw)) {
21861
22033
  out.invalidRules += 1;
22034
+ reject(pack, null, "rules_json: not an array");
21862
22035
  continue;
21863
22036
  }
21864
22037
  for (const entry of raw) {
@@ -21867,7 +22040,12 @@ var SqliteInstalledPacksRepository = class {
21867
22040
  out.rules.push(parsed.data);
21868
22041
  out.ruleActions.set(parsed.data.id, action);
21869
22042
  out.ruleVersions.set(parsed.data.id, row.version);
21870
- } else out.invalidRules += 1;
22043
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22044
+ else out.reversibleRules.delete(parsed.data.id);
22045
+ } else {
22046
+ out.invalidRules += 1;
22047
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22048
+ }
21871
22049
  }
21872
22050
  }
21873
22051
  return out;
@@ -22061,31 +22239,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22061
22239
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22062
22240
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22063
22241
  var HARNESS_LABELS = {
22064
- claudecode: "Claude Code",
22065
- cursor: "Cursor",
22066
- codex: "Codex",
22067
- antigravity: "Antigravity"
22242
+ [HARNESS.ClaudeCode]: "Claude Code",
22243
+ [HARNESS.Cursor]: "Cursor",
22244
+ [HARNESS.Codex]: "Codex",
22245
+ [HARNESS.Antigravity]: "Antigravity"
22068
22246
  };
22069
22247
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22070
22248
  var EMPTY_PROJECT_AGG = {
22071
22249
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22072
22250
  findingsCount: 0
22073
22251
  };
22252
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22253
+ var TITLE_NEEDLES = {
22254
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22255
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22256
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22257
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22258
+ };
22074
22259
  function resolveHarnessId(attrs, row) {
22075
22260
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22076
22261
  return attrs.provider;
22077
22262
  }
22078
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22079
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22080
- if (t.includes("cursor")) return "cursor";
22081
- if (t.includes("codex")) return "codex";
22082
- if (t.includes("antigravity")) return "antigravity";
22263
+ const t = stripSeparators(row.title ?? "");
22264
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22265
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22266
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22267
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22083
22268
  return null;
22084
22269
  }
22085
22270
  function isLiveRealClaudeCode(rows) {
22086
22271
  return rows.some((r) => {
22087
22272
  const attrs = safeJson(r.attributes, {});
22088
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22273
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22089
22274
  });
22090
22275
  }
22091
22276
  function toAssetSummary(row) {
@@ -22353,7 +22538,7 @@ var SqliteInventoryAssetsRepository = class {
22353
22538
  const isRealHarness = rows.some(
22354
22539
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22355
22540
  );
22356
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22541
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22357
22542
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22358
22543
  if (q && assets.length === 0) continue;
22359
22544
  const firstRow = rows[0];
@@ -23027,9 +23212,10 @@ var SqliteProjectFilesRepository = class {
23027
23212
  // ../../packages/persistence/src/repositories/resolutions.ts
23028
23213
  import { randomUUID as randomUUID7 } from "crypto";
23029
23214
  var SqliteResolutionsRepository = class {
23030
- constructor(db, now = () => Date.now()) {
23215
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23031
23216
  this.db = db;
23032
23217
  this.now = now;
23218
+ this.newId = newId;
23033
23219
  this.insertStmt = db.prepare(
23034
23220
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23035
23221
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23062,12 +23248,14 @@ var SqliteResolutionsRepository = class {
23062
23248
  }
23063
23249
  db;
23064
23250
  now;
23251
+ newId;
23065
23252
  insertStmt;
23066
23253
  latestStmt;
23067
23254
  openAtRestStmt;
23068
23255
  resolvedAtRestStmt;
23069
23256
  /**
23070
- * Insert one disposition row. The repo mints the id and stamps created_at.
23257
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23258
+ * both through the constructor's injectable seams.
23071
23259
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23072
23260
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23073
23261
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23079,7 +23267,7 @@ var SqliteResolutionsRepository = class {
23079
23267
  */
23080
23268
  insertResolution(r) {
23081
23269
  this.insertStmt.run({
23082
- id: randomUUID7(),
23270
+ id: this.newId(),
23083
23271
  findingKey: r.findingKey,
23084
23272
  status: FindingStatus.parse(r.status),
23085
23273
  method: ResolutionMethod.parse(r.method),
@@ -23658,16 +23846,16 @@ var ACTION_TO_KIND = {
23658
23846
  warn: "warned"
23659
23847
  };
23660
23848
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23661
- var SCAN_COVERAGE = [
23662
- { provider: "claudecode", coverage: 100, supported: true },
23663
- { provider: "cursor", coverage: 0, supported: false },
23664
- { provider: "codex", coverage: 80, supported: true },
23665
- { provider: "antigravity", coverage: 60, supported: true },
23666
- { provider: "claudeai", coverage: 0, supported: false },
23667
- { provider: "chatgpt", coverage: 0, supported: false },
23668
- { provider: "copilot", coverage: 0, supported: false },
23669
- { provider: "api", coverage: 0, supported: false }
23670
- ];
23849
+ var SCAN_COVERAGE = {
23850
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
23851
+ [HARNESS.Api]: { coverage: 0, supported: false },
23852
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23853
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23854
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23855
+ [HARNESS.Codex]: { coverage: 80, supported: true },
23856
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
23857
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
23858
+ };
23671
23859
  var GRANULARITY = {
23672
23860
  "7d": "day",
23673
23861
  "30d": "day",
@@ -23766,9 +23954,22 @@ var SqliteSecurityRepository = class {
23766
23954
  return Promise.resolve({ total, needsRemediation, bySeverity });
23767
23955
  }
23768
23956
  // Range is echoed but does not change the result today — coverage is a constant
23769
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
23957
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
23958
+ // comes from Provider.options (the enum's declaration order), not from
23959
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
23960
+ // leave key order unspecified (ES2015 guarantees insertion order for these
23961
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
23962
+ // reliable too). The reason is the schema comment's promise: the returned
23963
+ // order must mirror the generated OpenAPI enum list, which is Provider's
23964
+ // contract, not this table's.
23770
23965
  scanCoverage(range) {
23771
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
23966
+ return Promise.resolve({
23967
+ range,
23968
+ providers: Provider.options.map((provider) => ({
23969
+ provider,
23970
+ ...SCAN_COVERAGE[provider]
23971
+ }))
23972
+ });
23772
23973
  }
23773
23974
  enforcementActions(range) {
23774
23975
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23826,10 +24027,10 @@ var SqliteSecurityRepository = class {
23826
24027
  // count; a superseding open/redetected row means the finding is not
23827
24028
  // remediated and is excluded, same invariant as severitySummary. Legacy
23828
24029
  // at-rest findings with finding_key IS NULL can never have a resolution row
23829
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23830
- // outright. One raw-row query (fetch every trackable finding + its latest
23831
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23832
- // mirroring this file's other methods.
24030
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24031
+ // set below. One raw-row query (fetch the findings with resolution activity in
24032
+ // the window + each one's latest resolution status/method/resolved_at) +
24033
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23833
24034
  mttrTrend(range) {
23834
24035
  const granularity = granularityFor(range);
23835
24036
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23845,29 +24046,80 @@ var SqliteSecurityRepository = class {
23845
24046
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23846
24047
  // COALESCE onto the parent event's started_at defends against any
23847
24048
  // legacy/edge row the backfill left null.
23848
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24049
+ `SELECT DISTINCT f.finding_key AS finding_key,
24050
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23849
24051
  latest.status AS latest_status,
23850
24052
  latest.method AS latest_method,
23851
24053
  latest.resolved_at AS latest_resolved_at
23852
- FROM inspection_findings f
23853
- JOIN audit_events e ON e.id = f.audit_event_id
23854
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24054
+ FROM finding_resolution fr
24055
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24056
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24057
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23855
24058
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23856
24059
  ON latest.finding_key = f.finding_key
23857
- WHERE f.finding_key IS NOT NULL
23858
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23859
- AND EXISTS (
23860
- SELECT 1 FROM finding_resolution fr
23861
- WHERE fr.finding_key = f.finding_key
23862
- AND fr.resolved_at >= :windowStart
23863
- )`
23864
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23865
- // any resolution activity at/after the window start — a row this method
24060
+ WHERE fr.resolved_at >= :windowStart
24061
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24062
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23866
24063
  // ultimately counts has its LATEST resolution inside the window, which
23867
- // implies such a row exists, so nothing wanted is dropped. The exact
23868
- // latest-wins + status/method + window gate stays in JS below,
23869
- // dialect-agnostic. Without this, a
23870
- // 7d request evaluated the store's entire trackable-findings history.
24064
+ // implies a resolution row at/after the window start exists, so nothing
24065
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24066
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24067
+ // implied rather than dropped — the join key comes from
24068
+ // finding_resolution, whose finding_key is NOT NULL.
24069
+ //
24070
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24071
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24072
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24073
+ // event_type, joined every capture event to its findings, and evaluated
24074
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24075
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24076
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24077
+ // both cases returning rows for a window holding a fraction of it.
24078
+ //
24079
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24080
+ // stating precisely, because the obvious reading (both are needed for the
24081
+ // speed) is wrong and was measured to be wrong:
24082
+ //
24083
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24084
+ // keyword is semantically identical to JOIN and exists only to stop the
24085
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24086
+ // the outside, because with no ANALYZE statistics it prices
24087
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24088
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24089
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24090
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24091
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24092
+ // range had none. It buys NO flatness in store size: remove it and the
24093
+ // ratio above does not move, because the latest-resolution derived
24094
+ // table already passes over the whole of finding_resolution, so this
24095
+ // read is O(resolutions) either way and resolutions are not the store.
24096
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24097
+ // — no hot read may pass over a table with no index — and that is the
24098
+ // guard that goes red when it is dropped. Neither test catches the
24099
+ // other's defect.
24100
+ //
24101
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24102
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24103
+ // redetected and fixed again carries several rows inside one window and
24104
+ // matches once per row — and the value below is a MEAN, so a key matched
24105
+ // three times is a key weighted three times.
24106
+ //
24107
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24108
+ // recording. Duplicate rows for ONE key are identical (every projected
24109
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24110
+ // preserved), so sums and counts scale together and that key's own mean
24111
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24112
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24113
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24114
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24115
+ // it has inside the window" — which needed a fixture built for it, since no
24116
+ // single-key case can show it.
24117
+ //
24118
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24119
+ // by value tuple. On the other columns alone, two genuinely different
24120
+ // findings sharing a severity, a first-detection event and a resolution
24121
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24122
+ // collapsing them would under-count in the other direction.
23871
24123
  ),
23872
24124
  { windowStart }
23873
24125
  );
@@ -23925,16 +24177,47 @@ var SqliteSecurityRepository = class {
23925
24177
  }
23926
24178
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23927
24179
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23928
- // same latest-resolution-wins correlated subquery as severitySummary /
23929
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23930
- // resolution row for a key rather than just its current disposition). A key
23931
- // whose latest row is a superseding 'open'/'redetected' row (the same
23932
- // secret came back) is excluded — it is not currently resolved. Legacy
23933
- // at-rest findings with finding_key IS NULL are excluded outright (the
23934
- // resolution lifecycle can never attach to them). Path comes from the
23935
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23936
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23937
- // DESC, capped at `limit`.
24180
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24181
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24182
+ // a key rather than just its current disposition). A key whose latest row is
24183
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24184
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24185
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24186
+ // never attach to them). Path comes from the finding's parent event
24187
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24188
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24189
+ //
24190
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24191
+ // the plan rather than a preference. Written the other way round — driving
24192
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24193
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24194
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24195
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24196
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24197
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24198
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24199
+ // because an empty finding_resolution table makes the inner side empty and the
24200
+ // cross product collapses to nothing — so the shape is only observable on a
24201
+ // corpus that seeds resolutions.
24202
+ //
24203
+ // Driving from `latest` instead makes every step below it a unique-index or
24204
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24205
+ // so the cost is the derived table's own — linear in resolutions, which is
24206
+ // what this feed is legitimately about.
24207
+ //
24208
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24209
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24210
+ // exists only to stop the optimizer reordering the tables. Written as plain
24211
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24212
+ // ANALYZE statistics to price the alternatives with, so it takes
24213
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24214
+ // cross product. The FROM order alone was measured to change the plan not at
24215
+ // all.
24216
+ //
24217
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24218
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24219
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24220
+ // reading as though the findings side could drive.
23938
24221
  recentlyResolved(limit = 20) {
23939
24222
  const rows = allRows(
23940
24223
  this.db.prepare(
@@ -23944,17 +24227,15 @@ var SqliteSecurityRepository = class {
23944
24227
  json_extract(e.attributes, '$.file_path') AS path,
23945
24228
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
23946
24229
  latest.resolved_at AS latest_resolved_at
23947
- FROM inspection_findings f
23948
- JOIN audit_events e ON e.id = f.audit_event_id
23949
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23950
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23951
- ON latest.finding_key = f.finding_key
23952
- WHERE e.event_type = 'code_change'
23953
- AND f.finding_key IS NOT NULL
23954
- AND latest.status = 'resolved'
24230
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24231
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24232
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24233
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24234
+ WHERE latest.status = 'resolved'
23955
24235
  AND latest.method = 'fixed-at-source'
23956
24236
  AND latest.resolved_at IS NOT NULL
23957
- ORDER BY latest_resolved_at DESC
24237
+ AND e.event_type = 'code_change'
24238
+ ORDER BY latest.resolved_at DESC
23958
24239
  LIMIT :limit`
23959
24240
  ),
23960
24241
  { limit }
@@ -24891,6 +25172,7 @@ function openAndInitialize(file2) {
24891
25172
  function openLocalDatabase(dir) {
24892
25173
  ensureDataDirSync(dir);
24893
25174
  const file2 = join2(dir, DB_FILENAME);
25175
+ reapStalePartials(file2);
24894
25176
  const {
24895
25177
  db,
24896
25178
  events,
@@ -25211,11 +25493,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25211
25493
  }
25212
25494
  }
25213
25495
 
25214
- // ../../packages/persistence/src/settings.ts
25496
+ // ../../packages/persistence/src/managed-settings.ts
25215
25497
  import { readFileSync as readFileSync3 } from "fs";
25498
+ import { posix, win32 } from "path";
25499
+ function managedSettingsPaths(platform2 = process.platform) {
25500
+ if (platform2 === "darwin") {
25501
+ return [
25502
+ posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
25503
+ posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
25504
+ ];
25505
+ }
25506
+ if (platform2 === "win32") {
25507
+ return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
25508
+ }
25509
+ return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
25510
+ }
25511
+ function readManagedSettings(paths = managedSettingsPaths()) {
25512
+ for (const path of paths) {
25513
+ let text;
25514
+ try {
25515
+ text = readFileSync3(path, "utf8");
25516
+ } catch {
25517
+ continue;
25518
+ }
25519
+ const record2 = parseJsonObject(text);
25520
+ if (!record2) continue;
25521
+ const parsed = ManagedSettings.safeParse(record2);
25522
+ if (parsed.success) return parsed.data;
25523
+ }
25524
+ return null;
25525
+ }
25526
+ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25527
+ if (!managed) return settings;
25528
+ const { values } = managed;
25529
+ const merged = { ...settings };
25530
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25531
+ if (values.controlPlane !== void 0) {
25532
+ merged.controlPlane = {
25533
+ ...values.controlPlane,
25534
+ // The administrator pinned WHICH deployment, not WHEN this machine
25535
+ // joined it. Keep the user's own attach time when the endpoint is
25536
+ // unchanged, so a managed machine does not appear to re-attach on every
25537
+ // read; stamp a fresh one when the administrator moved it.
25538
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25539
+ };
25540
+ }
25541
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25542
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25543
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25544
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25545
+ if (values.vaultConsent !== void 0) {
25546
+ merged.vaultConsent = values.vaultConsent ? (
25547
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25548
+ // at the current version otherwise.
25549
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25550
+ ) : void 0;
25551
+ }
25552
+ if (values.modelJudgeConsent !== void 0) {
25553
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25554
+ acknowledgedAt: now().toISOString(),
25555
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25556
+ } : void 0;
25557
+ }
25558
+ return merged;
25559
+ }
25560
+
25561
+ // ../../packages/persistence/src/settings.ts
25562
+ import { readFileSync as readFileSync4 } from "fs";
25216
25563
  import { join as join5 } from "path";
25217
25564
  var SETTINGS_FILENAME = "settings.json";
25218
25565
  function readWorkspaceSettings(base = defaultDataDir()) {
25566
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25567
+ }
25568
+ function readUserSettings(base) {
25219
25569
  const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25220
25570
  if (!record2) return defaultWorkspaceSettings();
25221
25571
  try {
@@ -25227,7 +25577,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25227
25577
  function readJson(file2) {
25228
25578
  let text;
25229
25579
  try {
25230
- text = readFileSync3(file2, "utf8");
25580
+ text = readFileSync4(file2, "utf8");
25231
25581
  } catch {
25232
25582
  return null;
25233
25583
  }
@@ -25246,15 +25596,7 @@ import {
25246
25596
  // ../../packages/persistence/src/vault/key-provider.ts
25247
25597
  import { execFileSync } from "child_process";
25248
25598
  import { randomBytes as randomBytes2 } from "crypto";
25249
- import {
25250
- chmodSync as chmodSync2,
25251
- mkdirSync as mkdirSync2,
25252
- readFileSync as readFileSync4,
25253
- renameSync as renameSync4,
25254
- rmSync as rmSync4,
25255
- statSync as statSync3,
25256
- writeFileSync as writeFileSync3
25257
- } from "fs";
25599
+ import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
25258
25600
  import { join as join6 } from "path";
25259
25601
 
25260
25602
  // ../../packages/persistence/src/vault/vault.ts
@@ -25350,7 +25692,7 @@ function resolveProviderSafe(resolveProviderFn) {
25350
25692
  }
25351
25693
 
25352
25694
  // ../../packages/plugin-sdk/src/config-inventory.ts
25353
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
25695
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
25354
25696
  import { homedir as homedir2 } from "os";
25355
25697
  import { basename as basename3, join as join10 } from "path";
25356
25698
 
@@ -25926,6 +26268,40 @@ function escapeRegExp2(value) {
25926
26268
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25927
26269
  }
25928
26270
 
26271
+ // ../../packages/detections/src/regex-cache.ts
26272
+ var singles = /* @__PURE__ */ new WeakMap();
26273
+ var keywordLists = /* @__PURE__ */ new WeakMap();
26274
+ var labelLists = /* @__PURE__ */ new WeakMap();
26275
+ function listCache(kind) {
26276
+ return kind === "keyword" ? keywordLists : labelLists;
26277
+ }
26278
+ function memoizedRegExp(owner, build) {
26279
+ const cached2 = singles.get(owner);
26280
+ if (cached2 !== void 0) {
26281
+ cached2.lastIndex = 0;
26282
+ return cached2;
26283
+ }
26284
+ const compiled = build();
26285
+ singles.set(owner, compiled);
26286
+ return compiled;
26287
+ }
26288
+ function memoizedRegExpList(kind, owner, build) {
26289
+ const cache = listCache(kind);
26290
+ const cached2 = cache.get(owner);
26291
+ if (cached2 !== void 0) {
26292
+ if (cached2.stateful) {
26293
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
26294
+ }
26295
+ return cached2.entries;
26296
+ }
26297
+ const entries = build();
26298
+ cache.set(owner, {
26299
+ entries,
26300
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
26301
+ });
26302
+ return entries;
26303
+ }
26304
+
25929
26305
  // ../../packages/detections/src/matchers/limits.ts
25930
26306
  var MAX_MATCHES_PER_RULE = 1e4;
25931
26307
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -25936,10 +26312,17 @@ var KeywordMatcher2 = class {
25936
26312
  if (rule.matcher.type !== "keyword") return [];
25937
26313
  const { keywords, caseSensitive } = rule.matcher;
25938
26314
  const spans = [];
25939
- for (const kw of keywords) {
25940
- if (kw.length === 0) continue;
26315
+ const compiled = memoizedRegExpList(
26316
+ "keyword",
26317
+ rule.matcher,
26318
+ () => keywords.map((kw) => {
26319
+ if (kw.length === 0) return void 0;
26320
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26321
+ })
26322
+ );
26323
+ for (const re of compiled) {
26324
+ if (re === void 0) continue;
25941
26325
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
25942
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
25943
26326
  let m;
25944
26327
  while ((m = re.exec(text)) !== null) {
25945
26328
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -25955,7 +26338,10 @@ var RegexMatcher2 = class {
25955
26338
  match(text, rule) {
25956
26339
  if (rule.matcher.type !== "regex") return [];
25957
26340
  const { pattern, flags, captureGroup } = rule.matcher;
25958
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
26341
+ const re = memoizedRegExp(
26342
+ rule.matcher,
26343
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
26344
+ );
25959
26345
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
25960
26346
  const spans = [];
25961
26347
  let m;
@@ -26038,6 +26424,7 @@ var CONFIG_POSTURE_RULES = [
26038
26424
  ];
26039
26425
 
26040
26426
  // ../../packages/detections/src/security/redos-probe.ts
26427
+ var BUDGET_MS = 100;
26041
26428
  var EXPONENTIAL_UNITS = [
26042
26429
  "a",
26043
26430
  "0",
@@ -26061,6 +26448,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
26061
26448
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
26062
26449
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
26063
26450
  );
26451
+ var CPU_CORROBORATION_SHARE = 0.2;
26452
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
26064
26453
 
26065
26454
  // ../../rules/code-flaws/auth-jwt-no-verify.json
26066
26455
  var auth_jwt_no_verify_default = {
@@ -28088,7 +28477,7 @@ function bundledDetections() {
28088
28477
  }
28089
28478
 
28090
28479
  // ../../packages/plugin-sdk/src/repo.ts
28091
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
28480
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
28092
28481
  import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
28093
28482
 
28094
28483
  // ../../packages/plugin-sdk/src/events.ts
@@ -28099,21 +28488,25 @@ import { existsSync as existsSync7 } from "fs";
28099
28488
  import { fileURLToPath } from "url";
28100
28489
  import { Worker } from "worker_threads";
28101
28490
 
28491
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
28492
+ var import_ignore = __toESM(require_ignore(), 1);
28493
+ import { readFileSync as readFileSync8 } from "fs";
28494
+ import { join as join11 } from "path";
28495
+
28102
28496
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
28103
28497
  import { arch, hostname as hostname4, platform, release } from "os";
28104
28498
 
28105
28499
  // ../../packages/plugin-sdk/src/nudge.ts
28106
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
28107
- import { join as join11 } from "path";
28500
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
28501
+ import { join as join12 } from "path";
28108
28502
 
28109
28503
  // ../../packages/plugin-sdk/src/paths.ts
28110
28504
  import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
28111
28505
  import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
28112
28506
 
28113
28507
  // ../../packages/plugin-sdk/src/project-files.ts
28114
- var import_ignore = __toESM(require_ignore(), 1);
28115
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
28116
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
28508
+ import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
28509
+ import { basename as basename5, join as join13 } from "path";
28117
28510
 
28118
28511
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28119
28512
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -28148,8 +28541,8 @@ import { randomUUID as randomUUID14 } from "crypto";
28148
28541
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
28149
28542
 
28150
28543
  // ../../packages/plugin-sdk/src/throttle.ts
28151
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28152
- import { join as join13 } from "path";
28544
+ import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28545
+ import { join as join14 } from "path";
28153
28546
 
28154
28547
  // ../../packages/plugin-runtime/src/standalone-gateway.ts
28155
28548
  import { randomUUID as randomUUID15 } from "crypto";
@@ -28162,6 +28555,8 @@ var StandaloneDataGateway = class {
28162
28555
  db;
28163
28556
  // Kept for the fingerprint key lookup (exception.key lives beside the store).
28164
28557
  dataDir;
28558
+ // One notice per gateway — see warnRulesetDiscarded.
28559
+ warnedRulesetDiscarded = false;
28165
28560
  constructor(dataDir2, detections = [], meta3) {
28166
28561
  this.db = openLocalDatabase(dataDir2);
28167
28562
  this.dataDir = dataDir2;
@@ -28281,28 +28676,69 @@ var StandaloneDataGateway = class {
28281
28676
  // - ANY invalid rule among enabled packs (all-invalid, partial corruption,
28282
28677
  // or a single malformed entry) → undefined → bundled fallback. Serving a
28283
28678
  // reduced "complete" set would silently drop exactly the corrupted rules
28284
- // with no fallback; the bundled packs are a superset, so falling back
28285
- // never loses coverage. Steady-state installed rules are all valid
28286
- // (generated + Zod-checked), so this only fires on a genuinely
28287
- // malformed/foreign store;
28679
+ // with no fallback. The bundled packs are a superset of AKA's OWN packs,
28680
+ // so falling back never loses coverage there but they contain no
28681
+ // pulled or custom pack, so for those this trades a partial ruleset for
28682
+ // none of them plus the loss of every pack's per-detection enforcement
28683
+ // action. That is deliberate (a store this machine cannot fully validate
28684
+ // is not authoritative), and it is why the cost of REJECTING a rule
28685
+ // matters: `Rule` is strict, so one unrecognized key in one custom rule
28686
+ // reaches this branch, not just a genuinely malformed or foreign store.
28687
+ // `installed-packs.test.ts` pins that per-rule counting;
28288
28688
  // - enabled packs that produce ZERO rules with no invalids (e.g. every
28289
28689
  // enabled pack's rules_json is `[]`) → undefined → bundled fallback: an
28290
28690
  // enabled pack contributing nothing is untrustworthy, not a real
28291
28691
  // "detect nothing" (that is expressed by disabling packs, handled above);
28292
28692
  // - otherwise → the enabled packs' validated rules, marked complete.
28693
+ /**
28694
+ * The discard above is the one ruleset decision this gateway reaches on its
28695
+ * own, and it is the most expensive one here: ONE rejected entry costs the
28696
+ * user every custom rule and every per-detection enforcement action, replaced
28697
+ * by bundled packs that contain neither. Nothing else reports it — a hook is a
28698
+ * short-lived process whose stderr is the only channel it has — so name what
28699
+ * was rejected and where the rest of the list lives.
28700
+ *
28701
+ * Unlike a quarantine verdict this caches nothing: the rejection is re-derived
28702
+ * from the store on every run, so the recovery is to fix or reinstall the pack,
28703
+ * and no line here may offer a command that clears a stored verdict.
28704
+ *
28705
+ * Written at most once per gateway — a second getPolicyBundle() in the same
28706
+ * process would re-report the same finding.
28707
+ */
28708
+ warnRulesetDiscarded(snapshot) {
28709
+ if (this.warnedRulesetDiscarded) return;
28710
+ this.warnedRulesetDiscarded = true;
28711
+ const listed = snapshot.rejectedRules.map((r) => `${r.pack}${r.ruleId === null ? "" : ` "${r.ruleId}"`} (${r.reason})`).join(", ");
28712
+ const undisclosed = snapshot.invalidRules - snapshot.rejectedRules.length;
28713
+ const more = undisclosed > 0 ? `, and ${String(undisclosed)} more` : "";
28714
+ process.stderr.write(
28715
+ `[aka] installed ruleset not used: ${String(snapshot.invalidRules)} rule(s) under enabled packs failed validation, so scanning fell back to the bundled packs and no custom rule or per-detection action is enforced \u2014 rejected: ${listed}${more}; review them with \`aka detections\`
28716
+ `
28717
+ );
28718
+ }
28293
28719
  installedScanRules() {
28294
28720
  try {
28295
28721
  const snapshot = this.db.installedPacks.installedRuleset();
28296
28722
  if (snapshot.installedPacks === 0) return void 0;
28297
28723
  if (snapshot.enabledPacks === 0) {
28298
- return { rules: [], ruleActions: /* @__PURE__ */ new Map(), ruleVersions: /* @__PURE__ */ new Map(), complete: true };
28724
+ return {
28725
+ rules: [],
28726
+ ruleActions: /* @__PURE__ */ new Map(),
28727
+ ruleVersions: /* @__PURE__ */ new Map(),
28728
+ reversibleRules: /* @__PURE__ */ new Set(),
28729
+ complete: true
28730
+ };
28731
+ }
28732
+ if (snapshot.invalidRules > 0) {
28733
+ this.warnRulesetDiscarded(snapshot);
28734
+ return void 0;
28299
28735
  }
28300
- if (snapshot.invalidRules > 0) return void 0;
28301
28736
  if (snapshot.rules.length === 0) return void 0;
28302
28737
  return {
28303
28738
  rules: snapshot.rules,
28304
28739
  ruleActions: snapshot.ruleActions,
28305
28740
  ruleVersions: snapshot.ruleVersions,
28741
+ reversibleRules: snapshot.reversibleRules,
28306
28742
  complete: true
28307
28743
  };
28308
28744
  } catch {
@@ -28330,6 +28766,11 @@ var StandaloneDataGateway = class {
28330
28766
  return {
28331
28767
  version: "local",
28332
28768
  policies: [...policies, ...rulePolicies],
28769
+ // The reversibility half of each pack's assignment. Emitted only under the
28770
+ // authoritative installed snapshot, exactly like rulePolicies above: the
28771
+ // bundled-packs fallback carries no per-pack assignment, so it carries no
28772
+ // reversibility either and every redaction there stays one-way.
28773
+ reversibleRuleIds: installed ? [...installed.reversibleRules] : [],
28333
28774
  rules: installed ? installed.rules : [],
28334
28775
  ...installed ? { rulesComplete: true } : {},
28335
28776
  ...installed ? { ruleVersions: Object.fromEntries(installed.ruleVersions) } : {},
@@ -28348,29 +28789,32 @@ var StandaloneDataGateway = class {
28348
28789
  return this.db.exceptions.recordBlocked(entry);
28349
28790
  }
28350
28791
  // Retention sweep over TERMINAL exception rows (revoked / expired / budget
28351
- // exhausted) — standalone-only store maintenance, invoked from SessionStart,
28352
- // not part of the DataGateway port. Active grants are never touched.
28792
+ // exhausted) — local-store maintenance, invoked from SessionStart through the
28793
+ // LocalStoreMaintenance capability rather than the DataGateway port. Active
28794
+ // grants are never touched.
28353
28795
  sweepTerminalExceptions(retentionMs) {
28354
28796
  return this.db.exceptions.sweepTerminal(retentionMs);
28355
28797
  }
28356
- // The warn-era enforcement cap, standalone-only store maintenance invoked
28357
- // from SessionStart, not part of the DataGateway port. Returns the number
28358
- // of block/redact rows capped to warn (0 for a redact-policy store or an
28359
- // already-capped one).
28798
+ // The warn-era enforcement cap — local-store maintenance, invoked from
28799
+ // SessionStart through the LocalStoreMaintenance capability rather than
28800
+ // the DataGateway port. Returns the number of block/redact rows capped to
28801
+ // warn (0 for a redact-policy store or an already-capped one).
28360
28802
  capWarnEraEnforcement(policyMode) {
28361
28803
  const { capped } = capWarnEraEnforcementOnce(this.db, policyMode, this.dataDir);
28362
28804
  return { capped };
28363
28805
  }
28364
28806
  // One project-file scan → the local project_file tree (one transaction inside
28365
- // the LocalDatabase, fail-open there). Like the sweep above, this is
28366
- // NOT part of the DataGateway port: the file tree is a local-store read model.
28807
+ // the LocalDatabase, fail-open there). Like the sweep above, this is reached
28808
+ // through the LocalStoreMaintenance capability rather than the DataGateway
28809
+ // port: the file tree is a local-store read model.
28367
28810
  recordProjectFiles(projectId, scan2) {
28368
28811
  this.db.recordProjectFiles(projectId, scan2);
28369
28812
  return Promise.resolve();
28370
28813
  }
28371
28814
  // Fold ghost source_project rows minted by the pre-worktree-fix resolver
28372
- // (checkout-path identities) into the repo's canonical row. Standalone-only
28373
- // store maintenance, invoked from SessionStart. Fail-open in the store.
28815
+ // (checkout-path identities) into the repo's canonical row. Local-store
28816
+ // maintenance, invoked from SessionStart through the LocalStoreMaintenance
28817
+ // capability. Fail-open in the store.
28374
28818
  reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
28375
28819
  this.db.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
28376
28820
  return Promise.resolve();
@@ -28381,10 +28825,10 @@ var StandaloneDataGateway = class {
28381
28825
  * executing the plugin generation they started with (Claude Code caches
28382
28826
  * plugin versions), and the write gate makes their installed-pack writes
28383
28827
  * silent no-ops — this is the one-line nudge telling the user WHY, and that
28384
- * a restart picks the newer plugin up. Standalone-only, invoked from
28385
- * SessionStart, not part of the DataGateway port. Fail-open: any error →
28386
- * null (no notice), and unparseable versions compare equal so garbage can
28387
- * never fire it.
28828
+ * a restart picks the newer plugin up. Local-store maintenance, invoked
28829
+ * from SessionStart through the LocalStoreMaintenance capability rather
28830
+ * than the DataGateway port. Fail-open: any error → null (no notice), and
28831
+ * unparseable versions compare equal so garbage can never fire it.
28388
28832
  */
28389
28833
  staleBinaryNotice(currentVersion) {
28390
28834
  try {
@@ -28462,7 +28906,8 @@ var StandaloneDataGateway = class {
28462
28906
 
28463
28907
  // ../../packages/plugin-runtime/src/resolve.ts
28464
28908
  var standaloneGatewayFactory = (config2, meta3) => new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
28465
- function resolveDataGateway(config2, meta3, gatewayFactory = standaloneGatewayFactory) {
28909
+ var defaultGatewayFactory = standaloneGatewayFactory;
28910
+ function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
28466
28911
  return gatewayFactory(config2, meta3);
28467
28912
  }
28468
28913
 
@@ -28496,12 +28941,17 @@ async function readStdin() {
28496
28941
 
28497
28942
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
28498
28943
  import { writeFileSync as writeFileSync7 } from "fs";
28499
- import { join as join14 } from "path";
28944
+ import { join as join15 } from "path";
28945
+
28946
+ // ../../packages/setup-wizard/src/triage/merge.ts
28947
+ var RANK = Object.fromEntries(
28948
+ KNOWN_BUILTIN_IDS.map((id, i) => [id, i])
28949
+ );
28500
28950
 
28501
28951
  // ../../packages/setup-wizard/src/triage/plan-file.ts
28502
- import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
28952
+ import { mkdtempSync, readFileSync as readFileSync10, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
28503
28953
  import { tmpdir } from "os";
28504
- import { basename as basename6, dirname as dirname4, join as join15 } from "path";
28954
+ import { basename as basename6, dirname as dirname4, join as join16 } from "path";
28505
28955
  var SuppressionEntrySchema = external_exports.object({
28506
28956
  ruleId: external_exports.string(),
28507
28957
  category: DetectionCategory,
@@ -28598,14 +29048,14 @@ function renderStatusBar(s, opts = {}) {
28598
29048
  const unreviewed = `unreviewed ${SHADE.full}${String(u.critical)} ${SHADE.dark}${String(u.high)} ${SHADE.medium}${String(u.medium)} ${SHADE.light}${String(u.low)}`;
28599
29049
  return `\u25B8\u25B8 AKA health ${String(s.score)}/100 ${unreviewed} \u2691 ${String(s.openFindings)} open findings`;
28600
29050
  }
28601
- const sep5 = ` ${paint.dim("\u2502")} `;
29051
+ const sep4 = ` ${paint.dim("\u2502")} `;
28602
29052
  const sq = "\u25A0";
28603
29053
  const dot = s.score >= 80 ? paint.ok("\u25CF") : s.score >= 50 ? paint.high("\u25CF") : paint.critical("\u25CF");
28604
29054
  const score = `${dot} health ${paint.bold(String(s.score))}${paint.dim("/100")}`;
28605
29055
  const tally = `${paint.dim("unreviewed")} ${paint.critical(sq)}${String(u.critical)} ${paint.high(sq)}${String(u.high)} ${paint.medium(sq)}${String(u.medium)} ${paint.low(sq)}${String(u.low)}`;
28606
29056
  const flag = s.openFindings > 0 ? paint.critical("\u2691") : paint.dim("\u2691");
28607
29057
  const open2 = `${flag} ${String(s.openFindings)} open findings`;
28608
- return `${paint.brand("\u25B8\u25B8 AKA")}${sep5}${score}${sep5}${tally}${sep5}${open2}`;
29058
+ return `${paint.brand("\u25B8\u25B8 AKA")}${sep4}${score}${sep4}${tally}${sep4}${open2}`;
28609
29059
  }
28610
29060
  function renderStatusLine(summary) {
28611
29061
  return renderStatusBar(findingStatus(summary), { color: true });