@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.
package/scripts/query.js CHANGED
@@ -581,6 +581,10 @@ var SQLITE_MIGRATIONS = [
581
581
  {
582
582
  tag: "0020_secret_vault_pagination_indexes",
583
583
  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');"
584
+ },
585
+ {
586
+ tag: "0021_finding_resolution_resolved_at_index",
587
+ sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
584
588
  }
585
589
  ];
586
590
 
@@ -15356,6 +15360,47 @@ function date4(params) {
15356
15360
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15357
15361
  config(en_default());
15358
15362
 
15363
+ // ../../packages/schema/src/zod/harness-map.ts
15364
+ var HARNESS = {
15365
+ ClaudeCode: "claudecode",
15366
+ Cursor: "cursor",
15367
+ Copilot: "copilot",
15368
+ Codex: "codex",
15369
+ Antigravity: "antigravity",
15370
+ Windsurf: "windsurf",
15371
+ ClaudeDesktop: "claudedesktop",
15372
+ ChatGpt: "chatgpt",
15373
+ ClaudeAi: "claudeai",
15374
+ Api: "api"
15375
+ };
15376
+ var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
15377
+ var SOURCE_TOOL = {
15378
+ ClaudeCode: "claude-code",
15379
+ ClaudeDesktop: "claude-desktop",
15380
+ Cursor: "cursor",
15381
+ ChatGpt: "chatgpt",
15382
+ ClaudeAi: "claude-ai",
15383
+ Copilot: "github-copilot",
15384
+ Codex: "codex",
15385
+ Antigravity: "antigravity",
15386
+ // No harness counterpart, deliberately: the CLI's own captures and a capture
15387
+ // whose tool could not be identified both render through the read side's
15388
+ // miss path rather than as a harness of their own.
15389
+ Cli: "cli",
15390
+ Unknown: "unknown"
15391
+ };
15392
+ var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
15393
+ var TOOL_TO_HARNESS = {
15394
+ [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
15395
+ [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
15396
+ [SOURCE_TOOL.Copilot]: HARNESS.Copilot,
15397
+ [SOURCE_TOOL.Cursor]: HARNESS.Cursor,
15398
+ [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
15399
+ [SOURCE_TOOL.Codex]: HARNESS.Codex,
15400
+ [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
15401
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
15402
+ };
15403
+
15359
15404
  // ../../packages/schema/src/zod/finding.ts
15360
15405
  var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
15361
15406
  var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
@@ -15378,21 +15423,22 @@ var Finding = external_exports.object({
15378
15423
  }).meta({ id: "Finding" });
15379
15424
  var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
15380
15425
  var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
15381
- var FindingProvider = external_exports.enum([
15382
- "claudecode",
15383
- "claudedesktop",
15384
- "cursor",
15385
- "copilot",
15386
- "chatgpt",
15387
- "claudeai",
15388
- "codex",
15389
- "antigravity",
15390
- "api"
15426
+ var FindingProvider = Harness.extract([
15427
+ "ClaudeCode",
15428
+ "ClaudeDesktop",
15429
+ "Cursor",
15430
+ "Copilot",
15431
+ "ChatGpt",
15432
+ "ClaudeAi",
15433
+ "Codex",
15434
+ "Antigravity",
15435
+ "Api"
15391
15436
  ]).meta({ id: "FindingProvider" });
15392
15437
  var FindingCategory = external_exports.enum([
15393
15438
  "secret",
15394
15439
  "pii",
15395
15440
  "source_code",
15441
+ "code_flaw",
15396
15442
  "external_share",
15397
15443
  "mcp_server",
15398
15444
  "customer_data",
@@ -15572,6 +15618,7 @@ var FindingInstanceDetail = FindingInstance.extend({
15572
15618
  policy: FindingPolicyRef
15573
15619
  }).meta({ id: "FindingInstanceDetail" });
15574
15620
  var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
15621
+ var MAX_FLAT_FINDINGS_LIMIT = 200;
15575
15622
  var ListFindingInstancesQuery = external_exports.object({
15576
15623
  severity: external_exports.array(Severity).optional(),
15577
15624
  // Rule ids, the same vocabulary the grouped list's `subtype` carries.
@@ -15591,7 +15638,7 @@ var ListFindingInstancesQuery = external_exports.object({
15591
15638
  q: external_exports.string().optional(),
15592
15639
  sessionId: external_exports.string().optional(),
15593
15640
  from: external_exports.iso.datetime().optional(),
15594
- limit: external_exports.coerce.number().int().min(1).max(200).optional(),
15641
+ limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
15595
15642
  cursor: external_exports.string().optional()
15596
15643
  });
15597
15644
  var ListFindingInstancesResponse = external_exports.object({
@@ -15653,30 +15700,6 @@ var ListFindingLocationsResponse = external_exports.object({
15653
15700
  hasMore: external_exports.boolean()
15654
15701
  }).meta({ id: "ListFindingLocationsResponse" });
15655
15702
 
15656
- // ../../packages/schema/src/zod/harness-map.ts
15657
- var Harness = external_exports.enum([
15658
- "claudecode",
15659
- "cursor",
15660
- "copilot",
15661
- "codex",
15662
- "antigravity",
15663
- "windsurf",
15664
- "claudedesktop",
15665
- "chatgpt",
15666
- "claudeai",
15667
- "api"
15668
- ]).meta({ id: "Harness" });
15669
- var TOOL_TO_HARNESS = {
15670
- "claude-code": "claudecode",
15671
- "claude-desktop": "claudedesktop",
15672
- "github-copilot": "copilot",
15673
- cursor: "cursor",
15674
- chatgpt: "chatgpt",
15675
- codex: "codex",
15676
- antigravity: "antigravity",
15677
- "claude-ai": "claudeai"
15678
- };
15679
-
15680
15703
  // ../../packages/schema/src/zod/meta.ts
15681
15704
  var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
15682
15705
  var AuditEventType = external_exports.enum([
@@ -16129,686 +16152,37 @@ var ActivityOverviewResponse = external_exports.object({
16129
16152
  sessions: ListActivitySessionsResponse
16130
16153
  }).meta({ id: "ActivityOverviewResponse" });
16131
16154
 
16132
- // ../../packages/schema/src/zod/event.ts
16133
- var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
16134
- var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
16135
- var SourceTool = external_exports.enum([
16136
- "claude-code",
16137
- "claude-desktop",
16138
- "cursor",
16139
- "chatgpt",
16140
- "claude-ai",
16141
- "github-copilot",
16142
- "codex",
16143
- "antigravity",
16144
- "cli",
16145
- "unknown"
16146
- ]).meta({ id: "SourceTool" });
16147
- var EventMetadata = external_exports.object({
16148
- sessionId: external_exports.string().optional(),
16149
- repo: external_exports.string().optional(),
16150
- filePath: external_exports.string().optional(),
16151
- // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
16152
- // set by the tool-scanning hooks. The tool NAME only — never the tool's
16153
- // arguments or output, which can carry the very value a finding masked
16154
- // (metadata is stored unredacted). Gives findings on non-file captures a
16155
- // display location ("via Bash") when no filePath exists.
16156
- toolName: external_exports.string().optional(),
16157
- // Set (true) by the worktree scanner when the file is excluded by the
16158
- // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
16159
- // generated code can leak real secrets — but the provenance is recorded so
16160
- // policy/dashboards can treat those findings as informational rather than
16161
- // blocking. Omitted (not false) for tracked files and non-scan events.
16162
- gitignored: external_exports.boolean().optional(),
16163
- // Set (true) ONLY when the event's `content` is the COMPLETE file at
16164
- // capture time (a worktree scan reading from disk). Hook-captured edits
16165
- // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
16166
- // this. The resolver-on-ingest keys its fixed-at-source dropout
16167
- // diff on this marker: only a whole-file snapshot can prove a previously
16168
- // open finding is gone; a fragment's absence proves nothing (the secret
16169
- // may live outside the hunk). Omitted (not false) for fragments and
16170
- // non-scan events, so pre-marker clients safely default to the
16171
- // non-authoritative path.
16172
- wholeFile: external_exports.boolean().optional(),
16173
- model: external_exports.string().optional(),
16174
- turnIndex: external_exports.number().int().nonnegative().optional(),
16175
- // Distributed-tracing correlation. `correlationId` ties a recorded event back
16176
- // to the request that captured/ingested it (a UUID, generated independently of
16177
- // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
16178
- // originating span when telemetry is enabled. Both optional + backward
16179
- // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
16180
- correlationId: external_exports.uuid().optional(),
16181
- traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
16182
- // Ids of the detection exceptions that downgraded findings in this capture
16183
- // to 'allow' — the enforcement audit trail's link back to the grant that
16184
- // authorized the bypass. Absent on captures where no exception applied.
16185
- exceptionIds: external_exports.array(external_exports.guid()).optional()
16186
- }).meta({ id: "EventMetadata" });
16187
- var Event = external_exports.object({
16188
- id: external_exports.guid(),
16189
- sourceTool: SourceTool,
16190
- kind: EventKind,
16191
- occurredAt: external_exports.iso.datetime(),
16192
- contentHash: external_exports.string(),
16193
- content: external_exports.string(),
16194
- metadata: EventMetadata.optional()
16195
- }).meta({ id: "Event" });
16196
- var IngestEvent = Event.meta({ id: "IngestEvent" });
16197
- var IngestBatch = external_exports.object({
16198
- events: external_exports.array(IngestEvent).min(1).max(100),
16199
- // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
16200
- // additionally rejects any event whose contentHash the store has already
16201
- // recorded — for re-runnable bulk ingest (worktree scan, transcript
16202
- // backfill), where a re-run mints fresh event ids for identical content and
16203
- // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
16204
- // two genuinely separate prompts can be byte-identical and both belong on
16205
- // the timeline.
16206
- dedupe: external_exports.literal("content-hash").optional()
16207
- }).meta({ id: "IngestBatch" });
16208
-
16209
- // ../../packages/schema/src/zod/inventory.ts
16210
- var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16211
- var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16212
- var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16213
- var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16214
- var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16215
- var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16216
- var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16217
- var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
16218
- var AccessCounts = external_exports.object({
16219
- open: external_exports.number().int().nonnegative(),
16220
- approved: external_exports.number().int().nonnegative(),
16221
- blocked: external_exports.number().int().nonnegative(),
16222
- total: external_exports.number().int().nonnegative()
16223
- }).meta({ id: "AccessCounts" });
16224
- var AssetSummary = external_exports.object({
16225
- id: external_exports.string(),
16226
- type: AssetType,
16227
- name: external_exports.string(),
16228
- sub: external_exports.string(),
16229
- flags: external_exports.array(Flag),
16230
- /** MCP servers only — omitted for all other types. */
16231
- trust: TrustLevel.optional()
16232
- }).meta({ id: "AssetSummary" });
16233
- var ProjectSummary = external_exports.object({
16234
- id: external_exports.string(),
16235
- name: external_exports.string(),
16236
- repo: external_exports.string(),
16237
- visibility: Visibility,
16238
- language: external_exports.string(),
16239
- policyDefault: AccessLevel,
16240
- updatedAt: external_exports.iso.datetime(),
16241
- accessCounts: AccessCounts,
16242
- findingsCount: external_exports.number().int().nonnegative()
16243
- }).meta({ id: "ProjectSummary" });
16244
- var HarnessCategory = external_exports.object({
16245
- /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16246
- type: AssetType,
16247
- assets: external_exports.array(AssetSummary)
16155
+ // ../../packages/schema/src/zod/config-inventory.ts
16156
+ var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16157
+ var SkillScanEntry = external_exports.object({
16158
+ name: external_exports.string().min(1),
16159
+ // The identity source: a marketplace repo for plugin skills (e.g.
16160
+ // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16161
+ // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16162
+ // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16163
+ source: external_exports.string().min(1),
16164
+ scope: ConfigScope,
16165
+ pluginName: external_exports.string().optional(),
16166
+ // Volatile — rides the attribute bag, never the identity hash.
16167
+ version: external_exports.string().optional(),
16168
+ description: external_exports.string().optional(),
16169
+ // Skill directory mtime (ISO) the "updated Nd ago" freshness signal.
16170
+ updatedAt: external_exports.iso.datetime().optional(),
16171
+ // Filesystem path — the promoted inventory `location` column.
16172
+ location: external_exports.string().optional()
16248
16173
  });
16249
- var HarnessSummary = external_exports.object({
16250
- id: HarnessId,
16251
- label: external_exports.string(),
16252
- kind: external_exports.string(),
16253
- version: external_exports.string(),
16254
- sessions: external_exports.number().int().nonnegative(),
16255
- assetCount: external_exports.number().int().nonnegative(),
16256
- flagCount: external_exports.number().int().nonnegative(),
16257
- projects: external_exports.array(ProjectSummary),
16258
- categories: external_exports.array(HarnessCategory)
16259
- }).meta({ id: "HarnessSummary" });
16260
- var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16261
- var AssetGroup = external_exports.object({
16262
- /** Group key — never project (enforced at service layer). */
16263
- type: AssetType,
16264
- total: external_exports.number().int().nonnegative(),
16265
- /**
16266
- * MCP group only — omitted for all other types.
16267
- * Partial: only TrustLevel keys with non-zero counts are included.
16268
- * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16269
- */
16270
- trustRollup: external_exports.object({
16271
- "known-good": external_exports.number().int().nonnegative(),
16272
- risky: external_exports.number().int().nonnegative(),
16273
- unapproved: external_exports.number().int().nonnegative()
16274
- }).partial().strict().optional(),
16275
- /**
16276
- * Partial: only Flag keys with non-zero counts are included.
16277
- * Strict: unknown keys are rejected — only Flag values are valid keys.
16278
- */
16279
- flagRollup: external_exports.object({
16280
- update: external_exports.number().int().nonnegative(),
16281
- stale: external_exports.number().int().nonnegative(),
16282
- conflict: external_exports.number().int().nonnegative(),
16283
- unknown: external_exports.number().int().nonnegative(),
16284
- change: external_exports.number().int().nonnegative(),
16285
- untracked: external_exports.number().int().nonnegative(),
16286
- risk: external_exports.number().int().nonnegative(),
16287
- findings: external_exports.number().int().nonnegative()
16288
- }).partial().strict(),
16289
- items: external_exports.array(AssetSummary)
16290
- }).meta({ id: "AssetGroup" });
16291
- var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16292
- var McpTool = external_exports.object({
16293
- name: external_exports.string(),
16294
- signature: external_exports.string(),
16295
- description: external_exports.string(),
16296
- write: external_exports.boolean(),
16297
- /** Non-null string when tool is dangerous / blocked; null otherwise. */
16298
- risk: external_exports.string().nullable()
16299
- }).meta({ id: "McpTool" });
16300
- var AssetFindingRef = external_exports.object({
16301
- id: external_exports.string(),
16302
- title: external_exports.string(),
16303
- note: external_exports.string()
16304
- });
16305
- var AssetDetail = AssetSummary.extend({
16306
- /** string | null — null when no description is available. */
16307
- description: external_exports.string().nullable(),
16308
- /** trustLevel | null — null for non-MCP assets. */
16309
- trust: TrustLevel.nullable(),
16310
- /** Type-specific raw key/values — FE renders the grid. */
16311
- meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16312
- /** always present — object when there is an active finding, null when absent. */
16313
- finding: AssetFindingRef.nullable(),
16314
- /** MCP exposed-tools list — omitted for non-mcp. */
16315
- tools: external_exports.array(McpTool).optional()
16316
- }).meta({ id: "AssetDetail" });
16317
- var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16318
- var InventoryStats = external_exports.object({
16319
- /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16320
- attention: external_exports.number().int().nonnegative(),
16321
- byType: external_exports.object({
16322
- project: external_exports.number().int().nonnegative(),
16323
- skill: external_exports.number().int().nonnegative(),
16324
- mcp: external_exports.number().int().nonnegative(),
16325
- hook: external_exports.number().int().nonnegative(),
16326
- config: external_exports.number().int().nonnegative()
16327
- }),
16328
- harnesses: external_exports.number().int().nonnegative(),
16329
- mcpTrust: external_exports.object({
16330
- "known-good": external_exports.number().int().nonnegative(),
16331
- risky: external_exports.number().int().nonnegative(),
16332
- unapproved: external_exports.number().int().nonnegative()
16333
- })
16334
- }).meta({ id: "InventoryStats" });
16335
- var FileSummary = external_exports.object({
16336
- path: external_exports.string(),
16337
- name: external_exports.string(),
16338
- origin: Origin,
16339
- /** Effective access (override applied). */
16340
- access: AccessLevel,
16341
- /** True when a file_access_override differs from the computed default. */
16342
- isCustom: external_exports.boolean(),
16343
- findings: external_exports.number().int().nonnegative(),
16344
- /** When the file was auto-blocked by a detection; null when not blocked. */
16345
- blockedAt: external_exports.iso.datetime().nullable().optional(),
16346
- /** Why the file was blocked; null when absent. */
16347
- note: external_exports.string().nullable().optional()
16348
- }).meta({ id: "FileSummary" });
16349
- var FolderSummary = external_exports.object({
16350
- name: external_exports.string(),
16351
- path: external_exports.string(),
16352
- /** Rollup of effective access across all descendants. */
16353
- accessCounts: AccessCounts
16354
- }).meta({ id: "FolderSummary" });
16355
- var ProjectTreeResponse = external_exports.object({
16356
- project: external_exports.object({
16357
- id: external_exports.string(),
16358
- repo: external_exports.string(),
16359
- visibility: Visibility
16360
- }),
16361
- path: external_exports.string(),
16362
- /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16363
- folders: external_exports.array(FolderSummary).optional(),
16364
- files: external_exports.array(FileSummary)
16365
- }).meta({ id: "ProjectTreeResponse" });
16366
- var FileDetail = FileSummary.extend({
16367
- project: external_exports.object({
16368
- repo: external_exports.string(),
16369
- visibility: Visibility,
16370
- language: external_exports.string(),
16371
- policyDefault: AccessLevel,
16372
- updatedAt: external_exports.iso.datetime()
16373
- }),
16374
- findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16375
- }).meta({ id: "FileDetail" });
16376
- var SetFileAccessBody = external_exports.object({
16377
- path: external_exports.string(),
16378
- access: AccessLevel
16379
- }).meta({ id: "SetFileAccessBody" });
16380
- var SetFileAccessResponse = external_exports.object({
16381
- file: FileSummary,
16382
- accessCounts: AccessCounts
16383
- }).meta({ id: "SetFileAccessResponse" });
16384
- var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16385
- var HarnessEventItem = external_exports.object({
16386
- kind: HarnessEventKind,
16387
- title: external_exports.string(),
16388
- detail: external_exports.string(),
16389
- occurredAt: external_exports.iso.datetime(),
16390
- findingId: external_exports.string().nullable().optional()
16391
- }).meta({ id: "HarnessEventItem" });
16392
- var HarnessEventsResponse = external_exports.object({
16393
- counts: external_exports.object({
16394
- block: external_exports.number().int().nonnegative(),
16395
- redact: external_exports.number().int().nonnegative(),
16396
- warn: external_exports.number().int().nonnegative()
16397
- }),
16398
- items: external_exports.array(HarnessEventItem)
16399
- }).meta({ id: "HarnessEventsResponse" });
16400
- var RescanResponse = external_exports.object({
16401
- jobId: external_exports.string(),
16402
- startedAt: external_exports.iso.datetime()
16403
- }).meta({ id: "RescanResponse" });
16404
- var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16405
- var ListAssetsQuery = external_exports.object({
16406
- /** Filter by one or more AssetType values; absent means all types. */
16407
- type: external_exports.array(AssetType).optional(),
16408
- /** Free-text search term. */
16409
- q: external_exports.string().optional()
16410
- });
16411
- var GetProjectTreeQuery = external_exports.object({
16412
- /** Subtree root path; defaults to repository root when absent. */
16413
- path: external_exports.string().optional(),
16414
- /** Free-text filter applied to file paths. */
16415
- q: external_exports.string().optional(),
16416
- /**
16417
- * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16418
- * file across the whole repo (folders omitted, most-recent first), ignoring
16419
- * `path`/`q` — powers the project-wide "recently blocked" strip.
16420
- */
16421
- filter: external_exports.enum(["blocked"]).optional()
16422
- });
16423
- var GetProjectFileQuery = external_exports.object({
16424
- /** Repository-relative file path; absent or empty → 400. */
16425
- path: external_exports.string()
16426
- });
16427
- var GetHarnessEventsQuery = external_exports.object({
16428
- /** Maximum number of events to return. Range: 1–50; default: 7. */
16429
- limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16430
- });
16431
-
16432
- // ../../packages/schema/src/zod/exception.ts
16433
- var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
16434
- var ExceptionConditions = external_exports.object({
16435
- repo: external_exports.string().optional(),
16436
- sourceTool: external_exports.string().optional(),
16437
- provider: external_exports.string().optional()
16438
- }).strict();
16439
- var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
16440
- var DetectionException = external_exports.object({
16441
- id: external_exports.guid(),
16442
- ruleId: external_exports.string(),
16443
- // Denormalized from the rule, for reporting — never matched on.
16444
- category: DetectionCategory,
16445
- // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
16446
- // fingerprint, never the raw value, and never reversible. Matching recomputes
16447
- // the fingerprint from a fresh capture; the value itself is never stored.
16448
- // Shape-constrained so a malformed — or accidentally raw — value is rejected
16449
- // at the boundary rather than persisted.
16450
- valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
16451
- // Version of the fingerprint key the grant was written under; a rotated key
16452
- // invalidates old grants rather than silently mismatching them.
16453
- keyVersion: external_exports.number().int().positive(),
16454
- // maskMatch() preview of the approved value — never the raw value.
16455
- maskedValue: external_exports.string(),
16456
- capability: ExceptionCapability.default("suppress"),
16457
- scope: ExceptionScope,
16458
- expiresAt: external_exports.iso.datetime().nullable(),
16459
- maxUses: external_exports.number().int().positive().nullable(),
16460
- useCount: external_exports.number().int().nonnegative(),
16461
- lastUsedAt: external_exports.iso.datetime().nullable(),
16462
- // Mandatory: every grant carries the human reason it exists.
16463
- justification: external_exports.string().min(1),
16464
- conditions: ExceptionConditions.nullable(),
16465
- createdBy: external_exports.string(),
16466
- createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
16467
- createdAt: external_exports.iso.datetime(),
16468
- updatedAt: external_exports.iso.datetime(),
16469
- // Revocation is terminal and retained — consumed/expired/revoked rows are
16470
- // audit evidence; nothing in the exception lifecycle hard-deletes.
16471
- revokedAt: external_exports.iso.datetime().nullable(),
16472
- revokedBy: external_exports.string().nullable(),
16473
- revokeReason: external_exports.string().nullable()
16474
- });
16475
- var ExceptionBundleEntry = DetectionException.pick({
16476
- id: true,
16477
- ruleId: true,
16478
- valueFingerprint: true,
16479
- keyVersion: true,
16480
- capability: true,
16481
- expiresAt: true,
16482
- maxUses: true,
16483
- useCount: true,
16484
- conditions: true
16485
- });
16486
- var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
16487
-
16488
- // ../../packages/schema/src/zod/rule.ts
16489
- var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16490
- var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16491
- var KeywordMatcher = external_exports.object({
16492
- type: external_exports.literal("keyword"),
16493
- // An empty keyword matches at every position, yielding one zero-length span
16494
- // per character. Rejected here because a keyword that matches everything is
16495
- // never intentional.
16496
- keywords: external_exports.array(external_exports.string().min(1)).min(1),
16497
- caseSensitive: external_exports.boolean().default(false)
16498
- });
16499
- function isValidRegex(pattern, flags) {
16500
- try {
16501
- new RegExp(pattern, flags);
16502
- return true;
16503
- } catch {
16504
- return false;
16505
- }
16506
- }
16507
- function matchesEmptyString(pattern, flags) {
16508
- try {
16509
- const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
16510
- return re.exec("")?.[0].length === 0;
16511
- } catch {
16512
- return false;
16513
- }
16514
- }
16515
- var MAX_PATTERN_LENGTH = 2e3;
16516
- var RegexMatcher = external_exports.object({
16517
- type: external_exports.literal("regex"),
16518
- pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16519
- flags: external_exports.string().default("gi"),
16520
- captureGroup: external_exports.number().int().nonnegative().optional()
16521
- }).refine((v) => isValidRegex(v.pattern, v.flags), {
16522
- message: "pattern/flags do not form a valid JavaScript regular expression",
16523
- path: ["pattern"]
16524
- }).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
16525
- 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',
16526
- path: ["pattern"]
16527
- });
16528
- var ValidatorMatcher = external_exports.object({
16529
- type: external_exports.literal("validator"),
16530
- name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
16531
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16532
- });
16533
- var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
16534
- var AppliesTo = external_exports.object({
16535
- // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16536
- extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16537
- }).meta({ id: "AppliesTo" });
16538
- var PostValidatorRef = external_exports.union([
16539
- external_exports.string(),
16540
- external_exports.object({
16541
- name: external_exports.string(),
16542
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16543
- })
16544
- ]).meta({ id: "PostValidatorRef" });
16545
- var RequiresNearby = external_exports.object({
16546
- // Each array, when present, must be non-empty and contain non-empty strings —
16547
- // an empty/blank criterion would either never fire or (for labels) match
16548
- // everything.
16549
- categories: external_exports.array(DetectionCategory).min(1).optional(),
16550
- ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16551
- labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16552
- windowChars: external_exports.number().int().positive().default(160),
16553
- // Optional confidence bump applied when a gated match is corroborated. Capped
16554
- // small: it nudges confidence, it does not assert certainty.
16555
- confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16556
- }).refine(
16557
- (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16558
- { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16559
- ).meta({ id: "RequiresNearby" });
16560
- var RuleFixture = external_exports.object({
16561
- label: external_exports.string(),
16562
- text: external_exports.string().max(5e4),
16563
- shouldMatch: external_exports.boolean(),
16564
- // Simulated file context for the scan, so fixtures can assert `appliesTo`
16565
- // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16566
- filePath: external_exports.string().optional(),
16567
- expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
16568
- }).meta({ id: "RuleFixture" });
16569
- var Rule = external_exports.object({
16570
- specVersion: external_exports.literal(1),
16571
- // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16572
- // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16573
- // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16574
- // therefore carries no namespace and is not globally unique across publishers;
16575
- // never feed one to splitDetectionId. `category` below (per-rule) is the
16576
- // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16577
- id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16578
- name: external_exports.string(),
16579
- category: DetectionCategory,
16580
- severity: Severity,
16581
- matcher: Matcher,
16582
- appliesTo: AppliesTo.optional(),
16583
- postValidators: external_exports.array(PostValidatorRef).optional(),
16584
- requiresNearby: RequiresNearby.optional(),
16585
- examples: external_exports.array(external_exports.string()).optional()
16586
- }).meta({ id: "Rule" });
16587
- var Author = external_exports.object({
16588
- name: external_exports.string(),
16589
- email: external_exports.email().optional(),
16590
- url: external_exports.url().optional()
16591
- }).meta({ id: "Author" });
16592
- var PackManifest = external_exports.object({
16593
- specVersion: external_exports.literal(1),
16594
- id: external_exports.string(),
16595
- name: external_exports.string(),
16596
- version: external_exports.string(),
16597
- rules: external_exports.array(external_exports.string()),
16598
- // Optional attribution/provenance — consumed by the rule marketplace.
16599
- description: external_exports.string().optional(),
16600
- author: Author.optional(),
16601
- license: external_exports.string().optional(),
16602
- sourceUrl: external_exports.url().optional()
16603
- }).meta({ id: "PackManifest" });
16604
-
16605
- // ../../packages/schema/src/zod/policy.ts
16606
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
16607
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
16608
- var Policy = external_exports.object({
16609
- id: external_exports.guid(),
16610
- scope: PolicyScope,
16611
- target: PolicyTarget,
16612
- action: ActionTaken,
16613
- enabled: external_exports.boolean().default(true),
16614
- customKeywords: external_exports.array(external_exports.string()).optional(),
16615
- // Display name — optional so older policy rows without name still parse.
16616
- // Added for the findings API (policy.name column migration).
16617
- name: external_exports.string().optional()
16618
- }).meta({ id: "Policy" });
16619
- var PolicyBundle = external_exports.object({
16620
- version: external_exports.string(),
16621
- policies: external_exports.array(Policy),
16622
- // Rules from the installed marketplace packs (snapshotted by the
16623
- // control plane). The plugin registers these in addition to its bundled
16624
- // packs. Optional so older backends — and older on-disk caches — that omit
16625
- // the field still parse; consumers read `bundle.rules ?? []`.
16626
- rules: external_exports.array(Rule).optional(),
16627
- // When true, `rules` IS the complete effective ruleset and the runtime must
16628
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
16629
- // after reading the user's installed snapshot (installed_packs, enabled
16630
- // packs only), which is how detection updates stay manual: new bundled
16631
- // rules run only after the user applies the pack update. Absent/false keeps
16632
- // the historical composition (bundled packs + rules) — older caches.
16633
- rulesComplete: external_exports.boolean().optional(),
16634
- // Active detection exceptions, evaluation subset only (see
16635
- // ExceptionBundleEntry). Optional so older bundle producers — and older
16636
- // on-disk caches — that omit the field still parse; consumers read
16637
- // `bundle.exceptions ?? []`.
16638
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
16639
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
16640
- // from a versioned installed pack. Optional so older backends — and older
16641
- // on-disk caches — that omit the field still parse; consumers fall back to
16642
- // the rule's own spec version. NOT the bundle version above — see
16643
- // installedRuleset's ruleVersions for the source of truth.
16644
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
16645
- customKeywords: external_exports.array(external_exports.string()),
16646
- fetchedAt: external_exports.iso.datetime()
16647
- }).meta({ id: "PolicyBundle" });
16648
- var OBSERVE_ONLY_CATEGORIES = ["config"];
16649
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
16650
- var CATEGORY_PEAK_SEVERITY = {
16651
- secret: "critical",
16652
- financial: "critical",
16653
- // core-financial/credit-card
16654
- code_flaw: "critical",
16655
- pii: "high",
16656
- phi: "high",
16657
- custom: "high",
16658
- // user-defined; conservative
16659
- code_context: "low",
16660
- config: "low"
16661
- // observe-only; floors to monitor regardless
16662
- };
16663
- function severityFloorPolicy(category) {
16664
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
16665
- const peak = CATEGORY_PEAK_SEVERITY[category];
16666
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
16667
- }
16668
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16669
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16670
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16671
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16672
- var BUILTIN_POLICY_SPECS = {
16673
- monitor: {
16674
- name: "Monitor",
16675
- action: "log",
16676
- description: "Log every match for audit. The request is allowed through untouched."
16677
- },
16678
- warn: {
16679
- name: "Warn",
16680
- action: "warn",
16681
- description: "Allow the request, but warn the user inline before it is sent."
16682
- },
16683
- redact: {
16684
- name: "Redact",
16685
- action: "redact",
16686
- description: "Automatically strip the matched value from the request, then continue."
16687
- },
16688
- block: {
16689
- name: "Block",
16690
- action: "block",
16691
- description: "Refuse the request entirely whenever any rule in this detection matches."
16692
- }
16693
- };
16694
- function builtinPolicyToAction(id) {
16695
- return BUILTIN_POLICY_SPECS[id].action;
16696
- }
16697
- var DEFAULT_ACTIONS = Object.fromEntries(
16698
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16699
- );
16700
- var BUILTIN_POLICIES = Object.fromEntries(
16701
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16702
- );
16703
- var DEFAULT_PACK_POLICY_ID = "monitor";
16704
- function policyIdToAction(policyId) {
16705
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16706
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16707
- return BUILTIN_POLICIES[id].action;
16708
- }
16709
- var UsedByItem = external_exports.object({
16710
- id: external_exports.string(),
16711
- name: external_exports.string(),
16712
- ruleCount: external_exports.number().int().nonnegative(),
16713
- enabled: external_exports.boolean()
16714
- }).meta({ id: "UsedByItem" });
16715
- var PolicyListItem = external_exports.object({
16716
- id: external_exports.string(),
16717
- kind: PolicyKind,
16718
- name: external_exports.string(),
16719
- enabled: external_exports.boolean(),
16720
- usedByCount: external_exports.number().int().nonnegative()
16721
- }).meta({ id: "PolicyListItem" });
16722
- var PolicyDetail = external_exports.object({
16723
- specVersion: external_exports.literal(1),
16724
- id: external_exports.string(),
16725
- kind: PolicyKind,
16726
- name: external_exports.string(),
16727
- enabled: external_exports.boolean(),
16728
- description: external_exports.string(),
16729
- usedBy: external_exports.array(UsedByItem)
16730
- }).meta({ id: "PolicyDetail" });
16731
- var PolicyStatsResponse = external_exports.object({
16732
- policies: external_exports.number().int().nonnegative(),
16733
- builtin: external_exports.number().int().nonnegative(),
16734
- custom: external_exports.number().int().nonnegative(),
16735
- detectionsGoverned: external_exports.number().int().nonnegative()
16736
- }).meta({ id: "PolicyStatsResponse" });
16737
-
16738
- // ../../packages/schema/src/zod/api.ts
16739
- var LIST_QUERY_MAX_LIMIT = 200;
16740
- var ListEventsQuery = external_exports.object({
16741
- cursor: external_exports.string().optional(),
16742
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16743
- sourceTool: external_exports.string().optional(),
16744
- kind: external_exports.string().optional(),
16745
- from: external_exports.iso.datetime().optional(),
16746
- to: external_exports.iso.datetime().optional()
16747
- });
16748
- var ListEventsResponse = external_exports.object({
16749
- items: external_exports.array(Event),
16750
- nextCursor: external_exports.string().nullable()
16751
- }).meta({ id: "ListEventsResponse" });
16752
- var IngestResponse = external_exports.object({
16753
- accepted: external_exports.number().int().nonnegative(),
16754
- duplicates: external_exports.number().int().nonnegative()
16755
- }).meta({ id: "IngestResponse" });
16756
- var ListFindingsQuery = external_exports.object({
16757
- cursor: external_exports.string().optional(),
16758
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16759
- severity: external_exports.string().optional(),
16760
- category: external_exports.string().optional(),
16761
- eventId: external_exports.guid().optional()
16762
- });
16763
- var ListFindingsResponse = external_exports.object({
16764
- items: external_exports.array(Finding),
16765
- nextCursor: external_exports.string().nullable()
16766
- }).meta({ id: "ListFindingsResponse" });
16767
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16768
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16769
- id: "CreatePolicyRequest"
16770
- });
16771
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16772
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16773
- var ErrorResponse = external_exports.object({
16774
- error: external_exports.object({
16775
- code: external_exports.string(),
16776
- message: external_exports.string(),
16777
- details: external_exports.unknown().optional()
16778
- })
16779
- }).meta({ id: "ErrorResponse" });
16780
-
16781
- // ../../packages/schema/src/zod/config-inventory.ts
16782
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16783
- var SkillScanEntry = external_exports.object({
16784
- name: external_exports.string().min(1),
16785
- // The identity source: a marketplace repo for plugin skills (e.g.
16786
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16787
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16788
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16789
- source: external_exports.string().min(1),
16790
- scope: ConfigScope,
16791
- pluginName: external_exports.string().optional(),
16792
- // Volatile — rides the attribute bag, never the identity hash.
16793
- version: external_exports.string().optional(),
16794
- description: external_exports.string().optional(),
16795
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16796
- updatedAt: external_exports.iso.datetime().optional(),
16797
- // Filesystem path — the promoted inventory `location` column.
16798
- location: external_exports.string().optional()
16799
- });
16800
- var HookScanEntry = external_exports.object({
16801
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16802
- // set is harness-defined and grows without a schema change.
16803
- event: external_exports.string().min(1),
16804
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16805
- matcher: external_exports.string().optional(),
16806
- command: external_exports.string().min(1),
16807
- timeout: external_exports.number().optional(),
16808
- scope: ConfigScope,
16809
- pluginName: external_exports.string().optional(),
16810
- // The settings file / hooks.json the entry came from.
16811
- location: external_exports.string().optional()
16174
+ var HookScanEntry = external_exports.object({
16175
+ // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16176
+ // set is harness-defined and grows without a schema change.
16177
+ event: external_exports.string().min(1),
16178
+ // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16179
+ matcher: external_exports.string().optional(),
16180
+ command: external_exports.string().min(1),
16181
+ timeout: external_exports.number().optional(),
16182
+ scope: ConfigScope,
16183
+ pluginName: external_exports.string().optional(),
16184
+ // The settings file / hooks.json the entry came from.
16185
+ location: external_exports.string().optional()
16812
16186
  });
16813
16187
  var McpServerScanEntry = external_exports.object({
16814
16188
  // The server's config key ("github", "filesystem", …) — identity, with the
@@ -16890,6 +16264,164 @@ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16890
16264
  var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16891
16265
  var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16892
16266
 
16267
+ // ../../packages/schema/src/zod/rule.ts
16268
+ var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
16269
+ var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16270
+ var KeywordMatcher = external_exports.strictObject({
16271
+ type: external_exports.literal("keyword"),
16272
+ // An empty keyword matches at every position, yielding one zero-length span
16273
+ // per character. Rejected here because a keyword that matches everything is
16274
+ // never intentional.
16275
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16276
+ caseSensitive: external_exports.boolean().default(false)
16277
+ });
16278
+ function isValidRegex(pattern, flags) {
16279
+ try {
16280
+ new RegExp(pattern, flags);
16281
+ return true;
16282
+ } catch {
16283
+ return false;
16284
+ }
16285
+ }
16286
+ function probeFlags(flags) {
16287
+ return flags.replace(/[gy]/g, "");
16288
+ }
16289
+ function matchesEmptyString(pattern, flags) {
16290
+ try {
16291
+ const re = new RegExp(pattern, probeFlags(flags));
16292
+ return re.exec("")?.[0].length === 0;
16293
+ } catch {
16294
+ return false;
16295
+ }
16296
+ }
16297
+ function spansWholeMatch(captureGroup) {
16298
+ return captureGroup === void 0 || captureGroup === 0;
16299
+ }
16300
+ function captureGroupCount(pattern, flags) {
16301
+ try {
16302
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags));
16303
+ const result = probe.exec("");
16304
+ return result ? result.length - 1 : void 0;
16305
+ } catch {
16306
+ return void 0;
16307
+ }
16308
+ }
16309
+ var MAX_PATTERN_LENGTH = 2e3;
16310
+ var RegexMatcher = external_exports.strictObject({
16311
+ type: external_exports.literal("regex"),
16312
+ pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16313
+ flags: external_exports.string().default("gi"),
16314
+ captureGroup: external_exports.number().int().nonnegative().optional()
16315
+ }).refine((v) => isValidRegex(v.pattern, v.flags), {
16316
+ message: "pattern/flags do not form a valid JavaScript regular expression",
16317
+ path: ["pattern"]
16318
+ }).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
16319
+ 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',
16320
+ path: ["pattern"]
16321
+ }).superRefine((v, ctx) => {
16322
+ if (v.captureGroup === void 0) return;
16323
+ const groups = captureGroupCount(v.pattern, v.flags);
16324
+ if (groups === void 0 || v.captureGroup <= groups) return;
16325
+ ctx.addIssue({
16326
+ code: "custom",
16327
+ path: ["captureGroup"],
16328
+ 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.`
16329
+ });
16330
+ });
16331
+ var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
16332
+ var MATCHER_TYPES = MatcherType.options;
16333
+ var AppliesTo = external_exports.strictObject({
16334
+ // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16335
+ extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16336
+ }).meta({ id: "AppliesTo" });
16337
+ var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
16338
+ var PostValidatorRef = external_exports.union(
16339
+ [
16340
+ PostValidatorName,
16341
+ external_exports.strictObject({
16342
+ name: PostValidatorName,
16343
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16344
+ })
16345
+ ],
16346
+ {
16347
+ // A union reports one collapsed issue for every way its arms can fail, so
16348
+ // this has to describe the whole shape rather than just the name — it is
16349
+ // what an author sees for a misspelled name AND for a stray key in the
16350
+ // object form. The names come from the enum so the message cannot go
16351
+ // stale. Without it Zod says only "Invalid input", which is precisely the
16352
+ // no-feedback outcome this schema exists to remove.
16353
+ error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
16354
+ " or "
16355
+ )}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
16356
+ }
16357
+ ).meta({ id: "PostValidatorRef" });
16358
+ var RequiresNearby = external_exports.strictObject({
16359
+ // Each array, when present, must be non-empty and contain non-empty strings —
16360
+ // an empty/blank criterion would either never fire or (for labels) match
16361
+ // everything.
16362
+ categories: external_exports.array(DetectionCategory).min(1).optional(),
16363
+ ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16364
+ labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16365
+ windowChars: external_exports.number().int().positive().default(160),
16366
+ // Optional confidence bump applied when a gated match is corroborated. Capped
16367
+ // small: it nudges confidence, it does not assert certainty.
16368
+ confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16369
+ }).refine(
16370
+ (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16371
+ { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16372
+ ).meta({ id: "RequiresNearby" });
16373
+ var RuleFixture = external_exports.strictObject({
16374
+ label: external_exports.string(),
16375
+ text: external_exports.string().max(5e4),
16376
+ shouldMatch: external_exports.boolean(),
16377
+ // Simulated file context for the scan, so fixtures can assert `appliesTo`
16378
+ // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16379
+ filePath: external_exports.string().optional(),
16380
+ expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
16381
+ }).meta({ id: "RuleFixture" });
16382
+ var Rule = external_exports.strictObject({
16383
+ // A pinned literal over a STRICT object, and the two together decide how this
16384
+ // format may grow. A rule carrying a key not listed below is refused with
16385
+ // `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
16386
+ // `invalid_value`. So the only additive path is adding an OPTIONAL field here
16387
+ // — that keeps every rule authored before it valid — and a rule author has no
16388
+ // way to introduce a field of their own or to opt into a later version.
16389
+ // Widening the format means changing this literal and every consumer of it.
16390
+ specVersion: external_exports.literal(1),
16391
+ // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16392
+ // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16393
+ // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16394
+ // therefore carries no namespace and is not globally unique across publishers;
16395
+ // never feed one to splitDetectionId. `category` below (per-rule) is the
16396
+ // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16397
+ id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16398
+ name: external_exports.string(),
16399
+ category: DetectionCategory,
16400
+ severity: Severity,
16401
+ matcher: Matcher,
16402
+ appliesTo: AppliesTo.optional(),
16403
+ postValidators: external_exports.array(PostValidatorRef).optional(),
16404
+ requiresNearby: RequiresNearby.optional(),
16405
+ examples: external_exports.array(external_exports.string()).optional()
16406
+ }).meta({ id: "Rule" });
16407
+ var Author = external_exports.object({
16408
+ name: external_exports.string(),
16409
+ email: external_exports.email().optional(),
16410
+ url: external_exports.url().optional()
16411
+ }).meta({ id: "Author" });
16412
+ var PackManifest = external_exports.object({
16413
+ specVersion: external_exports.literal(1),
16414
+ id: external_exports.string(),
16415
+ name: external_exports.string(),
16416
+ version: external_exports.string(),
16417
+ rules: external_exports.array(external_exports.string()),
16418
+ // Optional attribution/provenance — consumed by the rule marketplace.
16419
+ description: external_exports.string().optional(),
16420
+ author: Author.optional(),
16421
+ license: external_exports.string().optional(),
16422
+ sourceUrl: external_exports.url().optional()
16423
+ }).meta({ id: "PackManifest" });
16424
+
16893
16425
  // ../../packages/schema/src/zod/detection.ts
16894
16426
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
16895
16427
  var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
@@ -17088,6 +16620,231 @@ function buildDetectionsList(summaries, query) {
17088
16620
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17089
16621
  }
17090
16622
 
16623
+ // ../../packages/schema/src/zod/inventory.ts
16624
+ var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16625
+ var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16626
+ var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16627
+ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16628
+ var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16629
+ var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16630
+ var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16631
+ var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
16632
+ id: "HarnessId"
16633
+ });
16634
+ var AccessCounts = external_exports.object({
16635
+ open: external_exports.number().int().nonnegative(),
16636
+ approved: external_exports.number().int().nonnegative(),
16637
+ blocked: external_exports.number().int().nonnegative(),
16638
+ total: external_exports.number().int().nonnegative()
16639
+ }).meta({ id: "AccessCounts" });
16640
+ var AssetSummary = external_exports.object({
16641
+ id: external_exports.string(),
16642
+ type: AssetType,
16643
+ name: external_exports.string(),
16644
+ sub: external_exports.string(),
16645
+ flags: external_exports.array(Flag),
16646
+ /** MCP servers only — omitted for all other types. */
16647
+ trust: TrustLevel.optional()
16648
+ }).meta({ id: "AssetSummary" });
16649
+ var ProjectSummary = external_exports.object({
16650
+ id: external_exports.string(),
16651
+ name: external_exports.string(),
16652
+ repo: external_exports.string(),
16653
+ visibility: Visibility,
16654
+ language: external_exports.string(),
16655
+ policyDefault: AccessLevel,
16656
+ updatedAt: external_exports.iso.datetime(),
16657
+ accessCounts: AccessCounts,
16658
+ findingsCount: external_exports.number().int().nonnegative()
16659
+ }).meta({ id: "ProjectSummary" });
16660
+ var HarnessCategory = external_exports.object({
16661
+ /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16662
+ type: AssetType,
16663
+ assets: external_exports.array(AssetSummary)
16664
+ });
16665
+ var HarnessSummary = external_exports.object({
16666
+ id: HarnessId,
16667
+ label: external_exports.string(),
16668
+ kind: external_exports.string(),
16669
+ version: external_exports.string(),
16670
+ sessions: external_exports.number().int().nonnegative(),
16671
+ assetCount: external_exports.number().int().nonnegative(),
16672
+ flagCount: external_exports.number().int().nonnegative(),
16673
+ projects: external_exports.array(ProjectSummary),
16674
+ categories: external_exports.array(HarnessCategory)
16675
+ }).meta({ id: "HarnessSummary" });
16676
+ var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16677
+ var AssetGroup = external_exports.object({
16678
+ /** Group key — never project (enforced at service layer). */
16679
+ type: AssetType,
16680
+ total: external_exports.number().int().nonnegative(),
16681
+ /**
16682
+ * MCP group only — omitted for all other types.
16683
+ * Partial: only TrustLevel keys with non-zero counts are included.
16684
+ * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16685
+ */
16686
+ trustRollup: external_exports.object({
16687
+ "known-good": external_exports.number().int().nonnegative(),
16688
+ risky: external_exports.number().int().nonnegative(),
16689
+ unapproved: external_exports.number().int().nonnegative()
16690
+ }).partial().strict().optional(),
16691
+ /**
16692
+ * Partial: only Flag keys with non-zero counts are included.
16693
+ * Strict: unknown keys are rejected — only Flag values are valid keys.
16694
+ */
16695
+ flagRollup: external_exports.object({
16696
+ update: external_exports.number().int().nonnegative(),
16697
+ stale: external_exports.number().int().nonnegative(),
16698
+ conflict: external_exports.number().int().nonnegative(),
16699
+ unknown: external_exports.number().int().nonnegative(),
16700
+ change: external_exports.number().int().nonnegative(),
16701
+ untracked: external_exports.number().int().nonnegative(),
16702
+ risk: external_exports.number().int().nonnegative(),
16703
+ findings: external_exports.number().int().nonnegative()
16704
+ }).partial().strict(),
16705
+ items: external_exports.array(AssetSummary)
16706
+ }).meta({ id: "AssetGroup" });
16707
+ var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16708
+ var McpTool = external_exports.object({
16709
+ name: external_exports.string(),
16710
+ signature: external_exports.string(),
16711
+ description: external_exports.string(),
16712
+ write: external_exports.boolean(),
16713
+ /** Non-null string when tool is dangerous / blocked; null otherwise. */
16714
+ risk: external_exports.string().nullable()
16715
+ }).meta({ id: "McpTool" });
16716
+ var AssetFindingRef = external_exports.object({
16717
+ id: external_exports.string(),
16718
+ title: external_exports.string(),
16719
+ note: external_exports.string()
16720
+ });
16721
+ var AssetDetail = AssetSummary.extend({
16722
+ /** string | null — null when no description is available. */
16723
+ description: external_exports.string().nullable(),
16724
+ /** trustLevel | null — null for non-MCP assets. */
16725
+ trust: TrustLevel.nullable(),
16726
+ /** Type-specific raw key/values — FE renders the grid. */
16727
+ meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16728
+ /** always present — object when there is an active finding, null when absent. */
16729
+ finding: AssetFindingRef.nullable(),
16730
+ /** MCP exposed-tools list — omitted for non-mcp. */
16731
+ tools: external_exports.array(McpTool).optional()
16732
+ }).meta({ id: "AssetDetail" });
16733
+ var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16734
+ var InventoryStats = external_exports.object({
16735
+ /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16736
+ attention: external_exports.number().int().nonnegative(),
16737
+ byType: external_exports.object({
16738
+ project: external_exports.number().int().nonnegative(),
16739
+ skill: external_exports.number().int().nonnegative(),
16740
+ mcp: external_exports.number().int().nonnegative(),
16741
+ hook: external_exports.number().int().nonnegative(),
16742
+ config: external_exports.number().int().nonnegative()
16743
+ }),
16744
+ harnesses: external_exports.number().int().nonnegative(),
16745
+ mcpTrust: external_exports.object({
16746
+ "known-good": external_exports.number().int().nonnegative(),
16747
+ risky: external_exports.number().int().nonnegative(),
16748
+ unapproved: external_exports.number().int().nonnegative()
16749
+ })
16750
+ }).meta({ id: "InventoryStats" });
16751
+ var FileSummary = external_exports.object({
16752
+ path: external_exports.string(),
16753
+ name: external_exports.string(),
16754
+ origin: Origin,
16755
+ /** Effective access (override applied). */
16756
+ access: AccessLevel,
16757
+ /** True when a file_access_override differs from the computed default. */
16758
+ isCustom: external_exports.boolean(),
16759
+ findings: external_exports.number().int().nonnegative(),
16760
+ /** When the file was auto-blocked by a detection; null when not blocked. */
16761
+ blockedAt: external_exports.iso.datetime().nullable().optional(),
16762
+ /** Why the file was blocked; null when absent. */
16763
+ note: external_exports.string().nullable().optional()
16764
+ }).meta({ id: "FileSummary" });
16765
+ var FolderSummary = external_exports.object({
16766
+ name: external_exports.string(),
16767
+ path: external_exports.string(),
16768
+ /** Rollup of effective access across all descendants. */
16769
+ accessCounts: AccessCounts
16770
+ }).meta({ id: "FolderSummary" });
16771
+ var ProjectTreeResponse = external_exports.object({
16772
+ project: external_exports.object({
16773
+ id: external_exports.string(),
16774
+ repo: external_exports.string(),
16775
+ visibility: Visibility
16776
+ }),
16777
+ path: external_exports.string(),
16778
+ /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16779
+ folders: external_exports.array(FolderSummary).optional(),
16780
+ files: external_exports.array(FileSummary)
16781
+ }).meta({ id: "ProjectTreeResponse" });
16782
+ var FileDetail = FileSummary.extend({
16783
+ project: external_exports.object({
16784
+ repo: external_exports.string(),
16785
+ visibility: Visibility,
16786
+ language: external_exports.string(),
16787
+ policyDefault: AccessLevel,
16788
+ updatedAt: external_exports.iso.datetime()
16789
+ }),
16790
+ findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16791
+ }).meta({ id: "FileDetail" });
16792
+ var SetFileAccessBody = external_exports.object({
16793
+ path: external_exports.string(),
16794
+ access: AccessLevel
16795
+ }).meta({ id: "SetFileAccessBody" });
16796
+ var SetFileAccessResponse = external_exports.object({
16797
+ file: FileSummary,
16798
+ accessCounts: AccessCounts
16799
+ }).meta({ id: "SetFileAccessResponse" });
16800
+ var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16801
+ var HarnessEventItem = external_exports.object({
16802
+ kind: HarnessEventKind,
16803
+ title: external_exports.string(),
16804
+ detail: external_exports.string(),
16805
+ occurredAt: external_exports.iso.datetime(),
16806
+ findingId: external_exports.string().nullable().optional()
16807
+ }).meta({ id: "HarnessEventItem" });
16808
+ var HarnessEventsResponse = external_exports.object({
16809
+ counts: external_exports.object({
16810
+ block: external_exports.number().int().nonnegative(),
16811
+ redact: external_exports.number().int().nonnegative(),
16812
+ warn: external_exports.number().int().nonnegative()
16813
+ }),
16814
+ items: external_exports.array(HarnessEventItem)
16815
+ }).meta({ id: "HarnessEventsResponse" });
16816
+ var RescanResponse = external_exports.object({
16817
+ jobId: external_exports.string(),
16818
+ startedAt: external_exports.iso.datetime()
16819
+ }).meta({ id: "RescanResponse" });
16820
+ var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16821
+ var ListAssetsQuery = external_exports.object({
16822
+ /** Filter by one or more AssetType values; absent means all types. */
16823
+ type: external_exports.array(AssetType).optional(),
16824
+ /** Free-text search term. */
16825
+ q: external_exports.string().optional()
16826
+ });
16827
+ var GetProjectTreeQuery = external_exports.object({
16828
+ /** Subtree root path; defaults to repository root when absent. */
16829
+ path: external_exports.string().optional(),
16830
+ /** Free-text filter applied to file paths. */
16831
+ q: external_exports.string().optional(),
16832
+ /**
16833
+ * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16834
+ * file across the whole repo (folders omitted, most-recent first), ignoring
16835
+ * `path`/`q` — powers the project-wide "recently blocked" strip.
16836
+ */
16837
+ filter: external_exports.enum(["blocked"]).optional()
16838
+ });
16839
+ var GetProjectFileQuery = external_exports.object({
16840
+ /** Repository-relative file path; absent or empty → 400. */
16841
+ path: external_exports.string()
16842
+ });
16843
+ var GetHarnessEventsQuery = external_exports.object({
16844
+ /** Maximum number of events to return. Range: 1–50; default: 7. */
16845
+ limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16846
+ });
16847
+
17091
16848
  // ../../packages/schema/src/zod/shares.ts
17092
16849
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17093
16850
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17294,6 +17051,127 @@ var EgressWriteSummary = external_exports.object({
17294
17051
  droppedFiles: external_exports.array(external_exports.string()).default([])
17295
17052
  }).meta({ id: "EgressWriteSummary" });
17296
17053
 
17054
+ // ../../packages/schema/src/zod/event.ts
17055
+ var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
17056
+ var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
17057
+ var EventMetadata = external_exports.object({
17058
+ sessionId: external_exports.string().optional(),
17059
+ repo: external_exports.string().optional(),
17060
+ filePath: external_exports.string().optional(),
17061
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
17062
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
17063
+ // arguments or output, which can carry the very value a finding masked
17064
+ // (metadata is stored unredacted). Gives findings on non-file captures a
17065
+ // display location ("via Bash") when no filePath exists.
17066
+ toolName: external_exports.string().optional(),
17067
+ // Set (true) by the worktree scanner when the file is excluded by the
17068
+ // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
17069
+ // generated code can leak real secrets — but the provenance is recorded so
17070
+ // policy/dashboards can treat those findings as informational rather than
17071
+ // blocking. Omitted (not false) for tracked files and non-scan events.
17072
+ gitignored: external_exports.boolean().optional(),
17073
+ // Set (true) ONLY when the event's `content` is the COMPLETE file at
17074
+ // capture time (a worktree scan reading from disk). Hook-captured edits
17075
+ // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
17076
+ // this. The resolver-on-ingest keys its fixed-at-source dropout
17077
+ // diff on this marker: only a whole-file snapshot can prove a previously
17078
+ // open finding is gone; a fragment's absence proves nothing (the secret
17079
+ // may live outside the hunk). Omitted (not false) for fragments and
17080
+ // non-scan events, so pre-marker clients safely default to the
17081
+ // non-authoritative path.
17082
+ wholeFile: external_exports.boolean().optional(),
17083
+ model: external_exports.string().optional(),
17084
+ turnIndex: external_exports.number().int().nonnegative().optional(),
17085
+ // Distributed-tracing correlation. `correlationId` ties a recorded event back
17086
+ // to the request that captured/ingested it (a UUID, generated independently of
17087
+ // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
17088
+ // originating span when telemetry is enabled. Both optional + backward
17089
+ // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
17090
+ correlationId: external_exports.uuid().optional(),
17091
+ traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
17092
+ // Ids of the detection exceptions that downgraded findings in this capture
17093
+ // to 'allow' — the enforcement audit trail's link back to the grant that
17094
+ // authorized the bypass. Absent on captures where no exception applied.
17095
+ exceptionIds: external_exports.array(external_exports.guid()).optional()
17096
+ }).meta({ id: "EventMetadata" });
17097
+ var Event = external_exports.object({
17098
+ id: external_exports.guid(),
17099
+ sourceTool: SourceTool,
17100
+ kind: EventKind,
17101
+ occurredAt: external_exports.iso.datetime(),
17102
+ contentHash: external_exports.string(),
17103
+ content: external_exports.string(),
17104
+ metadata: EventMetadata.optional()
17105
+ }).meta({ id: "Event" });
17106
+ var IngestEvent = Event.meta({ id: "IngestEvent" });
17107
+ var IngestBatch = external_exports.object({
17108
+ events: external_exports.array(IngestEvent).min(1).max(100),
17109
+ // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
17110
+ // additionally rejects any event whose contentHash the store has already
17111
+ // recorded — for re-runnable bulk ingest (worktree scan, transcript
17112
+ // backfill), where a re-run mints fresh event ids for identical content and
17113
+ // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
17114
+ // two genuinely separate prompts can be byte-identical and both belong on
17115
+ // the timeline.
17116
+ dedupe: external_exports.literal("content-hash").optional()
17117
+ }).meta({ id: "IngestBatch" });
17118
+
17119
+ // ../../packages/schema/src/zod/exception.ts
17120
+ var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
17121
+ var ExceptionConditions = external_exports.object({
17122
+ repo: external_exports.string().optional(),
17123
+ sourceTool: external_exports.string().optional(),
17124
+ provider: external_exports.string().optional()
17125
+ }).strict();
17126
+ var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
17127
+ var DetectionException = external_exports.object({
17128
+ id: external_exports.guid(),
17129
+ ruleId: external_exports.string(),
17130
+ // Denormalized from the rule, for reporting — never matched on.
17131
+ category: DetectionCategory,
17132
+ // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
17133
+ // fingerprint, never the raw value, and never reversible. Matching recomputes
17134
+ // the fingerprint from a fresh capture; the value itself is never stored.
17135
+ // Shape-constrained so a malformed — or accidentally raw — value is rejected
17136
+ // at the boundary rather than persisted.
17137
+ valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
17138
+ // Version of the fingerprint key the grant was written under; a rotated key
17139
+ // invalidates old grants rather than silently mismatching them.
17140
+ keyVersion: external_exports.number().int().positive(),
17141
+ // maskMatch() preview of the approved value — never the raw value.
17142
+ maskedValue: external_exports.string(),
17143
+ capability: ExceptionCapability.default("suppress"),
17144
+ scope: ExceptionScope,
17145
+ expiresAt: external_exports.iso.datetime().nullable(),
17146
+ maxUses: external_exports.number().int().positive().nullable(),
17147
+ useCount: external_exports.number().int().nonnegative(),
17148
+ lastUsedAt: external_exports.iso.datetime().nullable(),
17149
+ // Mandatory: every grant carries the human reason it exists.
17150
+ justification: external_exports.string().min(1),
17151
+ conditions: ExceptionConditions.nullable(),
17152
+ createdBy: external_exports.string(),
17153
+ createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
17154
+ createdAt: external_exports.iso.datetime(),
17155
+ updatedAt: external_exports.iso.datetime(),
17156
+ // Revocation is terminal and retained — consumed/expired/revoked rows are
17157
+ // audit evidence; nothing in the exception lifecycle hard-deletes.
17158
+ revokedAt: external_exports.iso.datetime().nullable(),
17159
+ revokedBy: external_exports.string().nullable(),
17160
+ revokeReason: external_exports.string().nullable()
17161
+ });
17162
+ var ExceptionBundleEntry = DetectionException.pick({
17163
+ id: true,
17164
+ ruleId: true,
17165
+ valueFingerprint: true,
17166
+ keyVersion: true,
17167
+ capability: true,
17168
+ expiresAt: true,
17169
+ maxUses: true,
17170
+ useCount: true,
17171
+ conditions: true
17172
+ });
17173
+ var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
17174
+
17297
17175
  // ../../packages/schema/src/zod/exception-action.ts
17298
17176
  var confirmation = external_exports.string().optional();
17299
17177
  var ApproveBlockedInput = external_exports.object({
@@ -17336,10 +17214,11 @@ function toApiAction(dbVal) {
17336
17214
  }
17337
17215
  function toApiCategory(dbVal) {
17338
17216
  if (dbVal === "code_context") return "source_code";
17339
- return dbVal;
17217
+ const parsed = FindingCategory.safeParse(dbVal);
17218
+ return parsed.success ? parsed.data : "custom";
17340
17219
  }
17341
17220
  function toApiProvider(sourceTool) {
17342
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17221
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17343
17222
  }
17344
17223
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17345
17224
  function foldGroupStatus(instanceStatuses) {
@@ -17908,6 +17787,7 @@ var ListVaultDerefsResponse = external_exports.object({
17908
17787
  });
17909
17788
  var VaultKeyCustody = external_exports.string();
17910
17789
  var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
17790
+ var VAULT_CONSENT_VERSION = 1;
17911
17791
  var VaultConsent = external_exports.object({
17912
17792
  acknowledgedAt: external_exports.iso.datetime(),
17913
17793
  version: external_exports.number().int().positive()
@@ -17915,7 +17795,14 @@ var VaultConsent = external_exports.object({
17915
17795
 
17916
17796
  // ../../packages/schema/src/zod/local.ts
17917
17797
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17918
- var RunMode = external_exports.enum(["standalone"]);
17798
+ var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17799
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17800
+ var ControlPlaneConnection = external_exports.object({
17801
+ endpoint: external_exports.string().min(1),
17802
+ // Display name for the deployment, shown instead of the raw endpoint.
17803
+ label: external_exports.string().min(1).optional(),
17804
+ attachedAt: external_exports.iso.datetime()
17805
+ }).meta({ id: "ControlPlaneConnection" });
17919
17806
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17920
17807
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17921
17808
  var ModelJudgeConsent = external_exports.object({
@@ -17924,12 +17811,10 @@ var ModelJudgeConsent = external_exports.object({
17924
17811
  });
17925
17812
  var WorkspaceSettings = external_exports.object({
17926
17813
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17927
- // Settings files written by earlier releases may carry the retired 'attached'
17928
- // value; it parses as 'standalone' so those files keep loading.
17929
- runMode: external_exports.preprocess(
17930
- (v) => v === "attached" ? "standalone" : v,
17931
- RunMode.default("standalone")
17932
- ),
17814
+ runMode: RunMode.default("standalone"),
17815
+ // Present only while attached; a detach clears it. Its presence is what makes
17816
+ // `runMode: 'attached'` mean anything — see isAttached.
17817
+ controlPlane: ControlPlaneConnection.optional(),
17933
17818
  policy: SimpleDetectionPolicy.default("redact"),
17934
17819
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17935
17820
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -18005,65 +17890,275 @@ function toClassifiedDataRow(input, id) {
18005
17890
  attributes: input.attributes ? JSON.stringify(input.attributes) : null
18006
17891
  };
18007
17892
  }
18008
- function toInspectionDefinitionRow(input, id) {
18009
- return {
18010
- id,
18011
- ruleId: input.ruleId,
18012
- name: input.name,
18013
- category: input.category,
18014
- severity: input.severity,
18015
- definition: input.definition,
18016
- version: input.version
18017
- };
17893
+ function toInspectionDefinitionRow(input, id) {
17894
+ return {
17895
+ id,
17896
+ ruleId: input.ruleId,
17897
+ name: input.name,
17898
+ category: input.category,
17899
+ severity: input.severity,
17900
+ definition: input.definition,
17901
+ version: input.version
17902
+ };
17903
+ }
17904
+ function toInspectionFindingRow(input) {
17905
+ return {
17906
+ id: input.id,
17907
+ auditEventId: input.auditEventId,
17908
+ inspectionDefinitionId: input.inspectionDefinitionId,
17909
+ classifiedDataId: input.classifiedDataId ?? null,
17910
+ spanStart: input.span.start,
17911
+ spanEnd: input.span.end,
17912
+ maskedMatch: input.maskedMatch,
17913
+ actionTaken: input.actionTaken,
17914
+ confidence: input.confidence,
17915
+ findingKey: input.findingKey ?? null,
17916
+ firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
17917
+ };
17918
+ }
17919
+ function toCaptureAttributes(event) {
17920
+ const metadata = event.metadata;
17921
+ return {
17922
+ source_tool: event.sourceTool,
17923
+ ...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
17924
+ ...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
17925
+ ...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
17926
+ ...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
17927
+ ...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
17928
+ ...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
17929
+ ...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
17930
+ ...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
17931
+ // `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
17932
+ // has ever populated either), but every legacy metadata key still rides
17933
+ // the bag rather than being silently dropped — CaptureAttributes'
17934
+ // `.catchall(z.unknown())` carries the long tail.
17935
+ ...metadata?.model !== void 0 ? { model: metadata.model } : {},
17936
+ ...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
17937
+ };
17938
+ }
17939
+ function captureDefinitionVersion(finding) {
17940
+ return `capture/${finding.category}/${finding.severity}`;
17941
+ }
17942
+ function toCaptureDefinitionInput(finding) {
17943
+ return {
17944
+ ruleId: finding.ruleId,
17945
+ version: captureDefinitionVersion(finding),
17946
+ name: finding.ruleId,
17947
+ category: finding.category,
17948
+ severity: finding.severity,
17949
+ definition: JSON.stringify({ ruleId: finding.ruleId })
17950
+ };
17951
+ }
17952
+
17953
+ // ../../packages/schema/src/zod/managed.ts
17954
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
17955
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
17956
+ var ManagedSettingKey = external_exports.enum([
17957
+ "runMode",
17958
+ "historicalAccess",
17959
+ "vaultConsent",
17960
+ "vaultKeyCustody",
17961
+ "vaultInlineReveal",
17962
+ "modelJudgeConsent",
17963
+ "dataSharesInPlace"
17964
+ ]).meta({ id: "ManagedSettingKey" });
17965
+ var ManagedSettingsValues = external_exports.object({
17966
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
17967
+ controlPlane: external_exports.object({
17968
+ endpoint: external_exports.string().min(1),
17969
+ label: external_exports.string().min(1).optional()
17970
+ }).optional(),
17971
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
17972
+ vaultConsent: external_exports.boolean().optional(),
17973
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
17974
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
17975
+ modelJudgeConsent: external_exports.boolean().optional(),
17976
+ dataSharesInPlace: external_exports.boolean().optional()
17977
+ }).meta({ id: "ManagedSettingsValues" });
17978
+ var ManagedSettings = external_exports.object({
17979
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
17980
+ // Shown on every locked control, so the user can tell an administrative
17981
+ // decision from a bug. Absent renders as a generic "your organization".
17982
+ organization: external_exports.string().min(1).optional(),
17983
+ // What the administrator pinned.
17984
+ values: ManagedSettingsValues.default({}),
17985
+ // Which of those the user may not change. A key here with no matching value
17986
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
17987
+ // the user may still override. The two are separable on purpose.
17988
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
17989
+ }).meta({ id: "ManagedSettings" });
17990
+
17991
+ // ../../packages/schema/src/zod/policy.ts
17992
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
17993
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
17994
+ var Policy = external_exports.object({
17995
+ id: external_exports.guid(),
17996
+ scope: PolicyScope,
17997
+ target: PolicyTarget,
17998
+ action: ActionTaken,
17999
+ enabled: external_exports.boolean().default(true),
18000
+ customKeywords: external_exports.array(external_exports.string()).optional(),
18001
+ // Display name — optional so older policy rows without name still parse.
18002
+ // Added for the findings API (policy.name column migration).
18003
+ name: external_exports.string().optional()
18004
+ }).meta({ id: "Policy" });
18005
+ var PolicyBundle = external_exports.object({
18006
+ version: external_exports.string(),
18007
+ policies: external_exports.array(Policy),
18008
+ // Rules from the installed marketplace packs (snapshotted by the
18009
+ // control plane). The plugin registers these in addition to its bundled
18010
+ // packs. Optional so older backends — and older on-disk caches — that omit
18011
+ // the field still parse; consumers read `bundle.rules ?? []`.
18012
+ rules: external_exports.array(Rule).optional(),
18013
+ // When true, `rules` IS the complete effective ruleset and the runtime must
18014
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
18015
+ // after reading the user's installed snapshot (installed_packs, enabled
18016
+ // packs only), which is how detection updates stay manual: new bundled
18017
+ // rules run only after the user applies the pack update. Absent/false keeps
18018
+ // the historical composition (bundled packs + rules) — older caches.
18019
+ rulesComplete: external_exports.boolean().optional(),
18020
+ // Active detection exceptions, evaluation subset only (see
18021
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
18022
+ // on-disk caches — that omit the field still parse; consumers read
18023
+ // `bundle.exceptions ?? []`.
18024
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
18025
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
18026
+ // A second axis over the same `redact` action, carried beside the policies
18027
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
18028
+ // widening Policy itself would change a persisted shape to express something
18029
+ // only the in-memory bundle needs. Optional so an older producer — or an
18030
+ // older on-disk cache — still parses; consumers read `?? []` and get the
18031
+ // pre-existing one-way behaviour, which is the safe direction to default.
18032
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
18033
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
18034
+ // from a versioned installed pack. Optional so older backends — and older
18035
+ // on-disk caches — that omit the field still parse; consumers fall back to
18036
+ // the rule's own spec version. NOT the bundle version above — see
18037
+ // installedRuleset's ruleVersions for the source of truth.
18038
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
18039
+ customKeywords: external_exports.array(external_exports.string()),
18040
+ fetchedAt: external_exports.iso.datetime()
18041
+ }).meta({ id: "PolicyBundle" });
18042
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
18043
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
18044
+ var CATEGORY_PEAK_SEVERITY = {
18045
+ secret: "critical",
18046
+ financial: "critical",
18047
+ // core-financial/credit-card
18048
+ code_flaw: "critical",
18049
+ pii: "high",
18050
+ phi: "high",
18051
+ custom: "high",
18052
+ // user-defined; conservative
18053
+ code_context: "low",
18054
+ config: "low"
18055
+ // observe-only; floors to monitor regardless
18056
+ };
18057
+ function severityFloorPolicy(category) {
18058
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18059
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18060
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18061
+ }
18062
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18063
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18064
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18065
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18066
+ var BUILTIN_POLICY_SPECS = {
18067
+ monitor: {
18068
+ name: "Monitor",
18069
+ action: "log",
18070
+ reversible: false,
18071
+ description: "Log every match for audit. The request is allowed through untouched."
18072
+ },
18073
+ warn: {
18074
+ name: "Warn",
18075
+ action: "warn",
18076
+ reversible: false,
18077
+ description: "Allow the request, but warn the user inline before it is sent."
18078
+ },
18079
+ redact: {
18080
+ name: "Redact",
18081
+ action: "redact",
18082
+ reversible: false,
18083
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18084
+ },
18085
+ vault: {
18086
+ name: "Redact & Vault",
18087
+ action: "redact",
18088
+ reversible: true,
18089
+ 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."
18090
+ },
18091
+ block: {
18092
+ name: "Block",
18093
+ action: "block",
18094
+ reversible: false,
18095
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18096
+ }
18097
+ };
18098
+ function builtinPolicyToAction(id) {
18099
+ return BUILTIN_POLICY_SPECS[id].action;
18018
18100
  }
18019
- function toInspectionFindingRow(input) {
18020
- return {
18021
- id: input.id,
18022
- auditEventId: input.auditEventId,
18023
- inspectionDefinitionId: input.inspectionDefinitionId,
18024
- classifiedDataId: input.classifiedDataId ?? null,
18025
- spanStart: input.span.start,
18026
- spanEnd: input.span.end,
18027
- maskedMatch: input.maskedMatch,
18028
- actionTaken: input.actionTaken,
18029
- confidence: input.confidence,
18030
- findingKey: input.findingKey ?? null,
18031
- firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
18032
- };
18101
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18102
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18103
+ );
18104
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18105
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18106
+ );
18107
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18108
+ function builtinPolicyIsReversible(id) {
18109
+ return BUILTIN_POLICY_SPECS[id].reversible;
18033
18110
  }
18034
- function toCaptureAttributes(event) {
18035
- const metadata = event.metadata;
18036
- return {
18037
- source_tool: event.sourceTool,
18038
- ...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
18039
- ...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
18040
- ...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
18041
- ...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
18042
- ...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
18043
- ...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
18044
- ...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
18045
- ...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
18046
- // `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
18047
- // has ever populated either), but every legacy metadata key still rides
18048
- // the bag rather than being silently dropped — CaptureAttributes'
18049
- // `.catchall(z.unknown())` carries the long tail.
18050
- ...metadata?.model !== void 0 ? { model: metadata.model } : {},
18051
- ...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
18052
- };
18111
+ function policyIdIsReversible(policyId) {
18112
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18113
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18114
+ return builtinPolicyIsReversible(id);
18053
18115
  }
18054
- function captureDefinitionVersion(finding) {
18055
- return `capture/${finding.category}/${finding.severity}`;
18116
+ var DEFAULT_ACTIONS = Object.fromEntries(
18117
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18118
+ );
18119
+ var BUILTIN_POLICIES = Object.fromEntries(
18120
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18121
+ );
18122
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18123
+ function policyDisplayName(policyId) {
18124
+ const id = policyId ?? DEFAULT_PACK_POLICY_ID;
18125
+ const parsed = BuiltinPolicyId.safeParse(id);
18126
+ return parsed.success ? BUILTIN_POLICIES[parsed.data].name : id;
18056
18127
  }
18057
- function toCaptureDefinitionInput(finding) {
18058
- return {
18059
- ruleId: finding.ruleId,
18060
- version: captureDefinitionVersion(finding),
18061
- name: finding.ruleId,
18062
- category: finding.category,
18063
- severity: finding.severity,
18064
- definition: JSON.stringify({ ruleId: finding.ruleId })
18065
- };
18128
+ function policyIdToAction(policyId) {
18129
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18130
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18131
+ return BUILTIN_POLICIES[id].action;
18066
18132
  }
18133
+ var UsedByItem = external_exports.object({
18134
+ id: external_exports.string(),
18135
+ name: external_exports.string(),
18136
+ ruleCount: external_exports.number().int().nonnegative(),
18137
+ enabled: external_exports.boolean()
18138
+ }).meta({ id: "UsedByItem" });
18139
+ var PolicyListItem = external_exports.object({
18140
+ id: external_exports.string(),
18141
+ kind: PolicyKind,
18142
+ name: external_exports.string(),
18143
+ enabled: external_exports.boolean(),
18144
+ usedByCount: external_exports.number().int().nonnegative()
18145
+ }).meta({ id: "PolicyListItem" });
18146
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18147
+ var PolicyDetail = external_exports.object({
18148
+ specVersion: external_exports.literal(1),
18149
+ id: external_exports.string(),
18150
+ kind: PolicyKind,
18151
+ name: external_exports.string(),
18152
+ enabled: external_exports.boolean(),
18153
+ description: external_exports.string(),
18154
+ usedBy: external_exports.array(UsedByItem)
18155
+ }).meta({ id: "PolicyDetail" });
18156
+ var PolicyStatsResponse = external_exports.object({
18157
+ policies: external_exports.number().int().nonnegative(),
18158
+ builtin: external_exports.number().int().nonnegative(),
18159
+ custom: external_exports.number().int().nonnegative(),
18160
+ detectionsGoverned: external_exports.number().int().nonnegative()
18161
+ }).meta({ id: "PolicyStatsResponse" });
18067
18162
 
18068
18163
  // ../../packages/schema/src/zod/project-files.ts
18069
18164
  var ProjectFileInput = external_exports.object({
@@ -18143,44 +18238,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18143
18238
  NoRemediationDecision
18144
18239
  ]);
18145
18240
 
18146
- // ../../packages/schema/src/zod/rule-test.ts
18147
- var TestRulesRequest = external_exports.object({
18148
- rules: external_exports.array(Rule).min(1).max(100),
18149
- text: external_exports.string().max(5e4).optional(),
18150
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18151
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18152
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18153
- }).meta({ id: "TestRulesRequest" });
18154
- var RuleTestMatch = external_exports.object({
18155
- ruleId: external_exports.string(),
18156
- category: DetectionCategory,
18157
- severity: Severity,
18158
- span: Span,
18159
- confidence: external_exports.number().min(0).max(1),
18160
- match: external_exports.string()
18161
- }).meta({ id: "RuleTestMatch" });
18162
- var FixtureResult = external_exports.object({
18163
- label: external_exports.string(),
18164
- shouldMatch: external_exports.boolean(),
18165
- didMatch: external_exports.boolean(),
18166
- passed: external_exports.boolean(),
18167
- matches: external_exports.array(RuleTestMatch)
18168
- }).meta({ id: "FixtureResult" });
18169
- var TestRulesResponse = external_exports.object({
18170
- // Present only when the request supplied `text`.
18171
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18172
- fixtures: external_exports.array(FixtureResult),
18173
- summary: external_exports.object({
18174
- total: external_exports.number().int().nonnegative(),
18175
- passed: external_exports.number().int().nonnegative(),
18176
- failed: external_exports.number().int().nonnegative()
18177
- }),
18178
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18179
- // `validator`), so they silently never match. Surfaced so an author is not
18180
- // misled by a green run that actually skipped a rule.
18181
- unsupportedRuleIds: external_exports.array(external_exports.string())
18182
- }).meta({ id: "TestRulesResponse" });
18183
-
18184
18241
  // ../../packages/schema/src/zod/security.ts
18185
18242
  var SeveritySummaryItem = external_exports.object({
18186
18243
  severity: Severity,
@@ -18278,10 +18335,22 @@ var TopSourcesQuery = external_exports.object({
18278
18335
  // Omit for both kinds.
18279
18336
  kind: external_exports.enum(SOURCE_KINDS).optional()
18280
18337
  });
18281
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18338
+ var Provider = Harness.extract([
18339
+ "ClaudeCode",
18340
+ "Cursor",
18341
+ "Codex",
18342
+ "Antigravity",
18343
+ "ClaudeAi",
18344
+ "ChatGpt",
18345
+ "Copilot",
18346
+ "Api"
18347
+ ]).meta({ id: "Provider" });
18282
18348
  var ScanCoverageProvider = external_exports.object({
18283
18349
  provider: Provider,
18284
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18350
+ // Percent of that provider's traffic the shipped capture surface reaches.
18351
+ // A curated business fact, constant across every `range` — not a measured
18352
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18353
+ // above the block for where these numbers are decided.
18285
18354
  coverage: external_exports.number().int().min(0).max(100),
18286
18355
  supported: external_exports.boolean()
18287
18356
  }).meta({ id: "ScanCoverageProvider" });
@@ -18335,6 +18404,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18335
18404
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18336
18405
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18337
18406
 
18407
+ // ../../packages/schema/src/zod/settings-action.ts
18408
+ var SaveSettingsInput = external_exports.object({
18409
+ historicalAccess: external_exports.string(),
18410
+ modelJudgeConsent: external_exports.boolean(),
18411
+ vaultConsent: external_exports.string(),
18412
+ vaultInlineReveal: external_exports.string()
18413
+ });
18414
+ var AttachInput = external_exports.object({
18415
+ endpoint: external_exports.string(),
18416
+ label: external_exports.string().optional()
18417
+ });
18418
+
18338
18419
  // ../../packages/schema/src/zod/triage.ts
18339
18420
  var TriageHit = external_exports.object({
18340
18421
  ruleId: external_exports.string(),
@@ -18349,7 +18430,7 @@ var TriageHit = external_exports.object({
18349
18430
  valueFingerprint: external_exports.string().optional(),
18350
18431
  keyVersion: external_exports.number().int().nonnegative().optional()
18351
18432
  });
18352
- var TriagePolicy = BuiltinPolicyId;
18433
+ var TriagePolicy = CategoryPolicyId;
18353
18434
  var TriageCategoryRec = external_exports.object({
18354
18435
  category: DetectionCategory,
18355
18436
  action: TriagePolicy,
@@ -18567,8 +18648,11 @@ function chmodBestEffort(path, mode) {
18567
18648
  function tightenDir(dir) {
18568
18649
  chmodBestEffort(dir, DATA_DIR_MODE);
18569
18650
  }
18651
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18652
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18653
+ }
18570
18654
  function ensureDataDirSync(dir) {
18571
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18655
+ mkdirOwnerOnlySync(dir, true);
18572
18656
  tightenDir(dir);
18573
18657
  }
18574
18658
  function dbSidecars(file2) {
@@ -18586,6 +18670,25 @@ function backupPath(file2, tag) {
18586
18670
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18587
18671
  }
18588
18672
  var STALE_PARTIAL_MS = 5 * 6e4;
18673
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18674
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18675
+ var SNAPSHOT_STAGING_COPY = "copy";
18676
+ function createSnapshotStaging(backup) {
18677
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18678
+ rmSync2(stage, { recursive: true, force: true });
18679
+ mkdirOwnerOnlySync(stage);
18680
+ tightenDir(stage);
18681
+ return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
18682
+ }
18683
+ function idleMs(entry) {
18684
+ for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
18685
+ try {
18686
+ return Date.now() - statSync(candidate).mtimeMs;
18687
+ } catch {
18688
+ }
18689
+ }
18690
+ return null;
18691
+ }
18589
18692
  function reapStalePartials(file2) {
18590
18693
  const dir = dirname(file2);
18591
18694
  const prefix = `${basename(file2)}.`;
@@ -18596,30 +18699,34 @@ function reapStalePartials(file2) {
18596
18699
  return;
18597
18700
  }
18598
18701
  for (const name of entries) {
18599
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18600
- const partial2 = join(dir, name);
18702
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18703
+ const staging = join(dir, name);
18601
18704
  try {
18602
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18603
- rmSync2(partial2, { force: true });
18705
+ const idle = idleMs(staging);
18706
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18707
+ rmSync2(staging, { recursive: true, force: true });
18604
18708
  }
18605
18709
  } catch {
18606
18710
  }
18607
18711
  }
18608
18712
  }
18609
18713
  function snapshotStore(db, backup) {
18610
- const partial2 = `${backup}.partial`;
18714
+ const { stage, copy } = createSnapshotStaging(backup);
18611
18715
  try {
18612
- rmSync2(partial2, { force: true });
18613
- db.prepare("VACUUM INTO ?").run(partial2);
18614
- tightenFile(partial2);
18615
- renameSync2(partial2, backup);
18716
+ db.prepare("VACUUM INTO ?").run(copy);
18717
+ tightenFile(copy);
18718
+ renameSync2(copy, backup);
18616
18719
  } catch (error51) {
18617
18720
  try {
18618
- rmSync2(partial2, { force: true });
18721
+ rmSync2(stage, { recursive: true, force: true });
18619
18722
  } catch {
18620
18723
  }
18621
18724
  throw error51;
18622
18725
  }
18726
+ try {
18727
+ rmSync2(stage, { recursive: true, force: true });
18728
+ } catch {
18729
+ }
18623
18730
  }
18624
18731
  function moveStoreAside(file2, backup) {
18625
18732
  const undo = [];
@@ -19347,9 +19454,10 @@ function safeParseStringArray(raw) {
19347
19454
  const parsed = safeJson(raw, null);
19348
19455
  return Array.isArray(parsed) ? parsed : [];
19349
19456
  }
19457
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19350
19458
  function toHarness(raw) {
19351
19459
  const parsed = Harness.safeParse(raw);
19352
- return parsed.success ? parsed.data : "claudecode";
19460
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19353
19461
  }
19354
19462
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19355
19463
  if (row.status) {
@@ -19480,7 +19588,7 @@ var SqliteActivityRepository = class {
19480
19588
  const params = [];
19481
19589
  if (query.harness && query.harness.length > 0) {
19482
19590
  conditions.push(
19483
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19591
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19484
19592
  );
19485
19593
  params.push(...query.harness);
19486
19594
  }
@@ -19687,13 +19795,13 @@ var SqliteActivityRepository = class {
19687
19795
  * The DISTINCT harnesses that actually have sessions (optionally within a
19688
19796
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19689
19797
  * present rather than the full enum. Each stored value is normalized through
19690
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19691
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19798
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19799
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19692
19800
  */
19693
19801
  harnessFacets(fromMs) {
19694
19802
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19695
19803
  const stmt = this.db.prepare(
19696
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19804
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19697
19805
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19698
19806
  );
19699
19807
  const rows = allRows(
@@ -19907,8 +20015,8 @@ var SqliteAuditEventsRepository = class {
19907
20015
  }
19908
20016
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19909
20017
  // (which takes a caller-supplied random id), the id here is MINTED internally
19910
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19911
- // sibling local-store ids (the local store is single-tenant). The deterministic
20018
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
20019
+ // session and message alone, like the sibling local-store ids. The deterministic
19912
20020
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19913
20021
  // AND converge a streaming partial/final split across two incremental passes:
19914
20022
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20873,6 +20981,42 @@ var SqliteFindingsRepository = class {
20873
20981
  this.db = db;
20874
20982
  }
20875
20983
  db;
20984
+ /**
20985
+ * The newest `limit` findings, newest first.
20986
+ *
20987
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
20988
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
20989
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
20990
+ * not on the driving table: SQLite sorts every finding in the store through a
20991
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
20992
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
20993
+ * rather than a constant.
20994
+ *
20995
+ * What it takes to make `started_at` order come out of an index instead:
20996
+ *
20997
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
20998
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
20999
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
21000
+ * satisfying a global `started_at` order across them needs a range merge
21001
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
21002
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
21003
+ * filters the type per row, which lets the LIMIT stop the scan early.
21004
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
21005
+ * purely to stop the tables being reordered. With plain JOINs the planner
21006
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
21007
+ * unary plus ALONE recovers almost none of the win. Both are needed.
21008
+ *
21009
+ * Neither is a micro-optimisation that a later reader should tidy away, and
21010
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
21011
+ * the temp B-tree comes back.
21012
+ *
21013
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
21014
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
21015
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
21016
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
21017
+ * a store whose recent captures carry no findings at all, where the scan walks
21018
+ * the whole index; that is still no worse than the full sort it replaced.
21019
+ */
20876
21020
  recentFindings(opts) {
20877
21021
  const limit = opts?.limit ?? 50;
20878
21022
  const rows = allRows(
@@ -20881,10 +21025,10 @@ var SqliteFindingsRepository = class {
20881
21025
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20882
21026
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20883
21027
  e.event_type AS kind
20884
- FROM inspection_findings f
20885
- JOIN audit_events e ON e.id = f.audit_event_id
20886
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20887
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
21028
+ FROM audit_events e
21029
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
21030
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
21031
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20888
21032
  ORDER BY e.started_at DESC, f.rowid DESC
20889
21033
  LIMIT :limit`
20890
21034
  ),
@@ -21528,7 +21672,8 @@ var SqliteInspectionDefinitionsRepository = class {
21528
21672
  }
21529
21673
  db;
21530
21674
  insertStmt;
21531
- // Idempotent upsert; returns the content-addressed definition id.
21675
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21676
+ // present keeps the stored row untouched — see the class doc.
21532
21677
  upsert(input) {
21533
21678
  const id = inspectionDefinitionId(input.ruleId, input.version);
21534
21679
  const row = toInspectionDefinitionRow(input, id);
@@ -21672,11 +21817,23 @@ function isParseableBinaryVersion(version2) {
21672
21817
 
21673
21818
  // ../../packages/persistence/src/repositories/installed-packs.ts
21674
21819
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21820
+ function printableRuleId(entry) {
21821
+ if (typeof entry !== "object" || entry === null) return null;
21822
+ const candidate = entry.id;
21823
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21824
+ }
21825
+ function firstIssueReason(error51) {
21826
+ const issue2 = error51.issues[0];
21827
+ if (!issue2) return "unknown";
21828
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21829
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21830
+ }
21831
+ var REJECTED_RULE_DETAIL_CAP = 10;
21675
21832
  function inventorySignature(packs) {
21676
21833
  return packs.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21677
21834
  }
21678
21835
  function hashRules(rulesJson) {
21679
- return createHash2("sha1").update(rulesJson).digest("hex");
21836
+ return createHash2("sha256").update(rulesJson).digest("hex");
21680
21837
  }
21681
21838
  function parseVersion(v) {
21682
21839
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21888,10 +22045,21 @@ var SqliteInstalledPacksRepository = class {
21888
22045
  * (all detection off) instead of falling back to the bundled packs. Every
21889
22046
  * JSON-level failure therefore counts as invalid.
21890
22047
  */
22048
+ /**
22049
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
22050
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
22051
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
22052
+ * winner is whatever order SQLite happens to return, which makes a collision
22053
+ * resolve differently on two machines holding identical stores. Ordering by
22054
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
22055
+ */
21891
22056
  installedRuleset() {
21892
22057
  const rows = allRows(
21893
22058
  this.db.prepare(
21894
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
22059
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
22060
+ namespace, pack_id AS packId
22061
+ FROM installed_packs
22062
+ ORDER BY namespace, pack_id`
21895
22063
  )
21896
22064
  );
21897
22065
  const out = {
@@ -21899,22 +22067,32 @@ var SqliteInstalledPacksRepository = class {
21899
22067
  enabledPacks: 0,
21900
22068
  rules: [],
21901
22069
  invalidRules: 0,
22070
+ rejectedRules: [],
21902
22071
  ruleActions: /* @__PURE__ */ new Map(),
21903
- ruleVersions: /* @__PURE__ */ new Map()
22072
+ ruleVersions: /* @__PURE__ */ new Map(),
22073
+ reversibleRules: /* @__PURE__ */ new Set()
22074
+ };
22075
+ const reject = (pack, ruleId, reason) => {
22076
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22077
+ out.rejectedRules.push({ pack, ruleId, reason });
21904
22078
  };
21905
22079
  for (const row of rows) {
21906
22080
  if (!intToBool(row.enabled)) continue;
21907
22081
  out.enabledPacks += 1;
21908
22082
  const action = policyIdToAction(row.policyId);
22083
+ const reversible = policyIdIsReversible(row.policyId);
22084
+ const pack = `${row.namespace}/${row.packId}`;
21909
22085
  let raw;
21910
22086
  try {
21911
22087
  raw = JSON.parse(row.rulesJson);
21912
22088
  } catch {
21913
22089
  out.invalidRules += 1;
22090
+ reject(pack, null, "rules_json: malformed JSON");
21914
22091
  continue;
21915
22092
  }
21916
22093
  if (!Array.isArray(raw)) {
21917
22094
  out.invalidRules += 1;
22095
+ reject(pack, null, "rules_json: not an array");
21918
22096
  continue;
21919
22097
  }
21920
22098
  for (const entry of raw) {
@@ -21923,7 +22101,12 @@ var SqliteInstalledPacksRepository = class {
21923
22101
  out.rules.push(parsed.data);
21924
22102
  out.ruleActions.set(parsed.data.id, action);
21925
22103
  out.ruleVersions.set(parsed.data.id, row.version);
21926
- } else out.invalidRules += 1;
22104
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22105
+ else out.reversibleRules.delete(parsed.data.id);
22106
+ } else {
22107
+ out.invalidRules += 1;
22108
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22109
+ }
21927
22110
  }
21928
22111
  }
21929
22112
  return out;
@@ -22117,31 +22300,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22117
22300
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22118
22301
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22119
22302
  var HARNESS_LABELS = {
22120
- claudecode: "Claude Code",
22121
- cursor: "Cursor",
22122
- codex: "Codex",
22123
- antigravity: "Antigravity"
22303
+ [HARNESS.ClaudeCode]: "Claude Code",
22304
+ [HARNESS.Cursor]: "Cursor",
22305
+ [HARNESS.Codex]: "Codex",
22306
+ [HARNESS.Antigravity]: "Antigravity"
22124
22307
  };
22125
22308
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22126
22309
  var EMPTY_PROJECT_AGG = {
22127
22310
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22128
22311
  findingsCount: 0
22129
22312
  };
22313
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22314
+ var TITLE_NEEDLES = {
22315
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22316
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22317
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22318
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22319
+ };
22130
22320
  function resolveHarnessId(attrs, row) {
22131
22321
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22132
22322
  return attrs.provider;
22133
22323
  }
22134
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22135
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22136
- if (t.includes("cursor")) return "cursor";
22137
- if (t.includes("codex")) return "codex";
22138
- if (t.includes("antigravity")) return "antigravity";
22324
+ const t = stripSeparators(row.title ?? "");
22325
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22326
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22327
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22328
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22139
22329
  return null;
22140
22330
  }
22141
22331
  function isLiveRealClaudeCode(rows) {
22142
22332
  return rows.some((r) => {
22143
22333
  const attrs = safeJson(r.attributes, {});
22144
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22334
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22145
22335
  });
22146
22336
  }
22147
22337
  function toAssetSummary(row) {
@@ -22409,7 +22599,7 @@ var SqliteInventoryAssetsRepository = class {
22409
22599
  const isRealHarness = rows.some(
22410
22600
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22411
22601
  );
22412
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22602
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22413
22603
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22414
22604
  if (q && assets.length === 0) continue;
22415
22605
  const firstRow = rows[0];
@@ -23083,9 +23273,10 @@ var SqliteProjectFilesRepository = class {
23083
23273
  // ../../packages/persistence/src/repositories/resolutions.ts
23084
23274
  import { randomUUID as randomUUID7 } from "crypto";
23085
23275
  var SqliteResolutionsRepository = class {
23086
- constructor(db, now = () => Date.now()) {
23276
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23087
23277
  this.db = db;
23088
23278
  this.now = now;
23279
+ this.newId = newId;
23089
23280
  this.insertStmt = db.prepare(
23090
23281
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23091
23282
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23118,12 +23309,14 @@ var SqliteResolutionsRepository = class {
23118
23309
  }
23119
23310
  db;
23120
23311
  now;
23312
+ newId;
23121
23313
  insertStmt;
23122
23314
  latestStmt;
23123
23315
  openAtRestStmt;
23124
23316
  resolvedAtRestStmt;
23125
23317
  /**
23126
- * Insert one disposition row. The repo mints the id and stamps created_at.
23318
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23319
+ * both through the constructor's injectable seams.
23127
23320
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23128
23321
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23129
23322
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23135,7 +23328,7 @@ var SqliteResolutionsRepository = class {
23135
23328
  */
23136
23329
  insertResolution(r) {
23137
23330
  this.insertStmt.run({
23138
- id: randomUUID7(),
23331
+ id: this.newId(),
23139
23332
  findingKey: r.findingKey,
23140
23333
  status: FindingStatus.parse(r.status),
23141
23334
  method: ResolutionMethod.parse(r.method),
@@ -23714,16 +23907,16 @@ var ACTION_TO_KIND = {
23714
23907
  warn: "warned"
23715
23908
  };
23716
23909
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23717
- var SCAN_COVERAGE = [
23718
- { provider: "claudecode", coverage: 100, supported: true },
23719
- { provider: "cursor", coverage: 0, supported: false },
23720
- { provider: "codex", coverage: 80, supported: true },
23721
- { provider: "antigravity", coverage: 60, supported: true },
23722
- { provider: "claudeai", coverage: 0, supported: false },
23723
- { provider: "chatgpt", coverage: 0, supported: false },
23724
- { provider: "copilot", coverage: 0, supported: false },
23725
- { provider: "api", coverage: 0, supported: false }
23726
- ];
23910
+ var SCAN_COVERAGE = {
23911
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
23912
+ [HARNESS.Api]: { coverage: 0, supported: false },
23913
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23914
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23915
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23916
+ [HARNESS.Codex]: { coverage: 80, supported: true },
23917
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
23918
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
23919
+ };
23727
23920
  var GRANULARITY = {
23728
23921
  "7d": "day",
23729
23922
  "30d": "day",
@@ -23822,9 +24015,22 @@ var SqliteSecurityRepository = class {
23822
24015
  return Promise.resolve({ total, needsRemediation, bySeverity });
23823
24016
  }
23824
24017
  // Range is echoed but does not change the result today — coverage is a constant
23825
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
24018
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
24019
+ // comes from Provider.options (the enum's declaration order), not from
24020
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
24021
+ // leave key order unspecified (ES2015 guarantees insertion order for these
24022
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
24023
+ // reliable too). The reason is the schema comment's promise: the returned
24024
+ // order must mirror the generated OpenAPI enum list, which is Provider's
24025
+ // contract, not this table's.
23826
24026
  scanCoverage(range) {
23827
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
24027
+ return Promise.resolve({
24028
+ range,
24029
+ providers: Provider.options.map((provider) => ({
24030
+ provider,
24031
+ ...SCAN_COVERAGE[provider]
24032
+ }))
24033
+ });
23828
24034
  }
23829
24035
  enforcementActions(range) {
23830
24036
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23882,10 +24088,10 @@ var SqliteSecurityRepository = class {
23882
24088
  // count; a superseding open/redetected row means the finding is not
23883
24089
  // remediated and is excluded, same invariant as severitySummary. Legacy
23884
24090
  // at-rest findings with finding_key IS NULL can never have a resolution row
23885
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23886
- // outright. One raw-row query (fetch every trackable finding + its latest
23887
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23888
- // mirroring this file's other methods.
24091
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24092
+ // set below. One raw-row query (fetch the findings with resolution activity in
24093
+ // the window + each one's latest resolution status/method/resolved_at) +
24094
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23889
24095
  mttrTrend(range) {
23890
24096
  const granularity = granularityFor(range);
23891
24097
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23901,29 +24107,80 @@ var SqliteSecurityRepository = class {
23901
24107
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23902
24108
  // COALESCE onto the parent event's started_at defends against any
23903
24109
  // legacy/edge row the backfill left null.
23904
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24110
+ `SELECT DISTINCT f.finding_key AS finding_key,
24111
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23905
24112
  latest.status AS latest_status,
23906
24113
  latest.method AS latest_method,
23907
24114
  latest.resolved_at AS latest_resolved_at
23908
- FROM inspection_findings f
23909
- JOIN audit_events e ON e.id = f.audit_event_id
23910
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24115
+ FROM finding_resolution fr
24116
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24117
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24118
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23911
24119
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23912
24120
  ON latest.finding_key = f.finding_key
23913
- WHERE f.finding_key IS NOT NULL
23914
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23915
- AND EXISTS (
23916
- SELECT 1 FROM finding_resolution fr
23917
- WHERE fr.finding_key = f.finding_key
23918
- AND fr.resolved_at >= :windowStart
23919
- )`
23920
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23921
- // any resolution activity at/after the window start — a row this method
24121
+ WHERE fr.resolved_at >= :windowStart
24122
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24123
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23922
24124
  // ultimately counts has its LATEST resolution inside the window, which
23923
- // implies such a row exists, so nothing wanted is dropped. The exact
23924
- // latest-wins + status/method + window gate stays in JS below,
23925
- // dialect-agnostic. Without this, a
23926
- // 7d request evaluated the store's entire trackable-findings history.
24125
+ // implies a resolution row at/after the window start exists, so nothing
24126
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24127
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24128
+ // implied rather than dropped — the join key comes from
24129
+ // finding_resolution, whose finding_key is NOT NULL.
24130
+ //
24131
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24132
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24133
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24134
+ // event_type, joined every capture event to its findings, and evaluated
24135
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24136
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24137
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24138
+ // both cases returning rows for a window holding a fraction of it.
24139
+ //
24140
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24141
+ // stating precisely, because the obvious reading (both are needed for the
24142
+ // speed) is wrong and was measured to be wrong:
24143
+ //
24144
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24145
+ // keyword is semantically identical to JOIN and exists only to stop the
24146
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24147
+ // the outside, because with no ANALYZE statistics it prices
24148
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24149
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24150
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24151
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24152
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24153
+ // range had none. It buys NO flatness in store size: remove it and the
24154
+ // ratio above does not move, because the latest-resolution derived
24155
+ // table already passes over the whole of finding_resolution, so this
24156
+ // read is O(resolutions) either way and resolutions are not the store.
24157
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24158
+ // — no hot read may pass over a table with no index — and that is the
24159
+ // guard that goes red when it is dropped. Neither test catches the
24160
+ // other's defect.
24161
+ //
24162
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24163
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24164
+ // redetected and fixed again carries several rows inside one window and
24165
+ // matches once per row — and the value below is a MEAN, so a key matched
24166
+ // three times is a key weighted three times.
24167
+ //
24168
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24169
+ // recording. Duplicate rows for ONE key are identical (every projected
24170
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24171
+ // preserved), so sums and counts scale together and that key's own mean
24172
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24173
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24174
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24175
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24176
+ // it has inside the window" — which needed a fixture built for it, since no
24177
+ // single-key case can show it.
24178
+ //
24179
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24180
+ // by value tuple. On the other columns alone, two genuinely different
24181
+ // findings sharing a severity, a first-detection event and a resolution
24182
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24183
+ // collapsing them would under-count in the other direction.
23927
24184
  ),
23928
24185
  { windowStart }
23929
24186
  );
@@ -23981,16 +24238,47 @@ var SqliteSecurityRepository = class {
23981
24238
  }
23982
24239
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23983
24240
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23984
- // same latest-resolution-wins correlated subquery as severitySummary /
23985
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23986
- // resolution row for a key rather than just its current disposition). A key
23987
- // whose latest row is a superseding 'open'/'redetected' row (the same
23988
- // secret came back) is excluded — it is not currently resolved. Legacy
23989
- // at-rest findings with finding_key IS NULL are excluded outright (the
23990
- // resolution lifecycle can never attach to them). Path comes from the
23991
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23992
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23993
- // DESC, capped at `limit`.
24241
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24242
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24243
+ // a key rather than just its current disposition). A key whose latest row is
24244
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24245
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24246
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24247
+ // never attach to them). Path comes from the finding's parent event
24248
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24249
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24250
+ //
24251
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24252
+ // the plan rather than a preference. Written the other way round — driving
24253
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24254
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24255
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24256
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24257
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24258
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24259
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24260
+ // because an empty finding_resolution table makes the inner side empty and the
24261
+ // cross product collapses to nothing — so the shape is only observable on a
24262
+ // corpus that seeds resolutions.
24263
+ //
24264
+ // Driving from `latest` instead makes every step below it a unique-index or
24265
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24266
+ // so the cost is the derived table's own — linear in resolutions, which is
24267
+ // what this feed is legitimately about.
24268
+ //
24269
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24270
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24271
+ // exists only to stop the optimizer reordering the tables. Written as plain
24272
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24273
+ // ANALYZE statistics to price the alternatives with, so it takes
24274
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24275
+ // cross product. The FROM order alone was measured to change the plan not at
24276
+ // all.
24277
+ //
24278
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24279
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24280
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24281
+ // reading as though the findings side could drive.
23994
24282
  recentlyResolved(limit = 20) {
23995
24283
  const rows = allRows(
23996
24284
  this.db.prepare(
@@ -24000,17 +24288,15 @@ var SqliteSecurityRepository = class {
24000
24288
  json_extract(e.attributes, '$.file_path') AS path,
24001
24289
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
24002
24290
  latest.resolved_at AS latest_resolved_at
24003
- FROM inspection_findings f
24004
- JOIN audit_events e ON e.id = f.audit_event_id
24005
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24006
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24007
- ON latest.finding_key = f.finding_key
24008
- WHERE e.event_type = 'code_change'
24009
- AND f.finding_key IS NOT NULL
24010
- AND latest.status = 'resolved'
24291
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24292
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24293
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24294
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24295
+ WHERE latest.status = 'resolved'
24011
24296
  AND latest.method = 'fixed-at-source'
24012
24297
  AND latest.resolved_at IS NOT NULL
24013
- ORDER BY latest_resolved_at DESC
24298
+ AND e.event_type = 'code_change'
24299
+ ORDER BY latest.resolved_at DESC
24014
24300
  LIMIT :limit`
24015
24301
  ),
24016
24302
  { limit }
@@ -24947,6 +25233,7 @@ function openAndInitialize(file2) {
24947
25233
  function openLocalDatabase(dir) {
24948
25234
  ensureDataDirSync(dir);
24949
25235
  const file2 = join2(dir, DB_FILENAME);
25236
+ reapStalePartials(file2);
24950
25237
  const {
24951
25238
  db,
24952
25239
  events,
@@ -25267,11 +25554,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25267
25554
  }
25268
25555
  }
25269
25556
 
25270
- // ../../packages/persistence/src/settings.ts
25557
+ // ../../packages/persistence/src/managed-settings.ts
25271
25558
  import { readFileSync as readFileSync3 } from "fs";
25559
+ import { posix, win32 } from "path";
25560
+ function managedSettingsPaths(platform2 = process.platform) {
25561
+ if (platform2 === "darwin") {
25562
+ return [
25563
+ posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
25564
+ posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
25565
+ ];
25566
+ }
25567
+ if (platform2 === "win32") {
25568
+ return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
25569
+ }
25570
+ return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
25571
+ }
25572
+ function readManagedSettings(paths = managedSettingsPaths()) {
25573
+ for (const path of paths) {
25574
+ let text;
25575
+ try {
25576
+ text = readFileSync3(path, "utf8");
25577
+ } catch {
25578
+ continue;
25579
+ }
25580
+ const record2 = parseJsonObject(text);
25581
+ if (!record2) continue;
25582
+ const parsed = ManagedSettings.safeParse(record2);
25583
+ if (parsed.success) return parsed.data;
25584
+ }
25585
+ return null;
25586
+ }
25587
+ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25588
+ if (!managed) return settings;
25589
+ const { values } = managed;
25590
+ const merged = { ...settings };
25591
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25592
+ if (values.controlPlane !== void 0) {
25593
+ merged.controlPlane = {
25594
+ ...values.controlPlane,
25595
+ // The administrator pinned WHICH deployment, not WHEN this machine
25596
+ // joined it. Keep the user's own attach time when the endpoint is
25597
+ // unchanged, so a managed machine does not appear to re-attach on every
25598
+ // read; stamp a fresh one when the administrator moved it.
25599
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25600
+ };
25601
+ }
25602
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25603
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25604
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25605
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25606
+ if (values.vaultConsent !== void 0) {
25607
+ merged.vaultConsent = values.vaultConsent ? (
25608
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25609
+ // at the current version otherwise.
25610
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25611
+ ) : void 0;
25612
+ }
25613
+ if (values.modelJudgeConsent !== void 0) {
25614
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25615
+ acknowledgedAt: now().toISOString(),
25616
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25617
+ } : void 0;
25618
+ }
25619
+ return merged;
25620
+ }
25621
+
25622
+ // ../../packages/persistence/src/settings.ts
25623
+ import { readFileSync as readFileSync4 } from "fs";
25272
25624
  import { join as join5 } from "path";
25273
25625
  var SETTINGS_FILENAME = "settings.json";
25274
25626
  function readWorkspaceSettings(base = defaultDataDir()) {
25627
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25628
+ }
25629
+ function readUserSettings(base) {
25275
25630
  const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25276
25631
  if (!record2) return defaultWorkspaceSettings();
25277
25632
  try {
@@ -25283,7 +25638,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25283
25638
  function readJson(file2) {
25284
25639
  let text;
25285
25640
  try {
25286
- text = readFileSync3(file2, "utf8");
25641
+ text = readFileSync4(file2, "utf8");
25287
25642
  } catch {
25288
25643
  return null;
25289
25644
  }
@@ -25302,15 +25657,7 @@ import {
25302
25657
  // ../../packages/persistence/src/vault/key-provider.ts
25303
25658
  import { execFileSync } from "child_process";
25304
25659
  import { randomBytes as randomBytes2 } from "crypto";
25305
- import {
25306
- chmodSync as chmodSync2,
25307
- mkdirSync as mkdirSync2,
25308
- readFileSync as readFileSync4,
25309
- renameSync as renameSync4,
25310
- rmSync as rmSync4,
25311
- statSync as statSync3,
25312
- writeFileSync as writeFileSync3
25313
- } from "fs";
25660
+ import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
25314
25661
  import { join as join6 } from "path";
25315
25662
 
25316
25663
  // ../../packages/persistence/src/vault/vault.ts
@@ -25410,7 +25757,7 @@ function resolveProviderSafe(resolveProviderFn) {
25410
25757
  }
25411
25758
 
25412
25759
  // ../../packages/plugin-sdk/src/config-inventory.ts
25413
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
25760
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
25414
25761
  import { homedir as homedir2 } from "os";
25415
25762
  import { basename as basename3, join as join10 } from "path";
25416
25763
 
@@ -25986,6 +26333,40 @@ function escapeRegExp2(value) {
25986
26333
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25987
26334
  }
25988
26335
 
26336
+ // ../../packages/detections/src/regex-cache.ts
26337
+ var singles = /* @__PURE__ */ new WeakMap();
26338
+ var keywordLists = /* @__PURE__ */ new WeakMap();
26339
+ var labelLists = /* @__PURE__ */ new WeakMap();
26340
+ function listCache(kind) {
26341
+ return kind === "keyword" ? keywordLists : labelLists;
26342
+ }
26343
+ function memoizedRegExp(owner, build) {
26344
+ const cached2 = singles.get(owner);
26345
+ if (cached2 !== void 0) {
26346
+ cached2.lastIndex = 0;
26347
+ return cached2;
26348
+ }
26349
+ const compiled = build();
26350
+ singles.set(owner, compiled);
26351
+ return compiled;
26352
+ }
26353
+ function memoizedRegExpList(kind, owner, build) {
26354
+ const cache = listCache(kind);
26355
+ const cached2 = cache.get(owner);
26356
+ if (cached2 !== void 0) {
26357
+ if (cached2.stateful) {
26358
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
26359
+ }
26360
+ return cached2.entries;
26361
+ }
26362
+ const entries = build();
26363
+ cache.set(owner, {
26364
+ entries,
26365
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
26366
+ });
26367
+ return entries;
26368
+ }
26369
+
25989
26370
  // ../../packages/detections/src/matchers/limits.ts
25990
26371
  var MAX_MATCHES_PER_RULE = 1e4;
25991
26372
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -25996,10 +26377,17 @@ var KeywordMatcher2 = class {
25996
26377
  if (rule.matcher.type !== "keyword") return [];
25997
26378
  const { keywords, caseSensitive } = rule.matcher;
25998
26379
  const spans = [];
25999
- for (const kw of keywords) {
26000
- if (kw.length === 0) continue;
26380
+ const compiled = memoizedRegExpList(
26381
+ "keyword",
26382
+ rule.matcher,
26383
+ () => keywords.map((kw) => {
26384
+ if (kw.length === 0) return void 0;
26385
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26386
+ })
26387
+ );
26388
+ for (const re of compiled) {
26389
+ if (re === void 0) continue;
26001
26390
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
26002
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26003
26391
  let m;
26004
26392
  while ((m = re.exec(text)) !== null) {
26005
26393
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -26015,7 +26403,10 @@ var RegexMatcher2 = class {
26015
26403
  match(text, rule) {
26016
26404
  if (rule.matcher.type !== "regex") return [];
26017
26405
  const { pattern, flags, captureGroup } = rule.matcher;
26018
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
26406
+ const re = memoizedRegExp(
26407
+ rule.matcher,
26408
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
26409
+ );
26019
26410
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
26020
26411
  const spans = [];
26021
26412
  let m;
@@ -26098,6 +26489,7 @@ var CONFIG_POSTURE_RULES = [
26098
26489
  ];
26099
26490
 
26100
26491
  // ../../packages/detections/src/security/redos-probe.ts
26492
+ var BUDGET_MS = 100;
26101
26493
  var EXPONENTIAL_UNITS = [
26102
26494
  "a",
26103
26495
  "0",
@@ -26121,6 +26513,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
26121
26513
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
26122
26514
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
26123
26515
  );
26516
+ var CPU_CORROBORATION_SHARE = 0.2;
26517
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
26124
26518
 
26125
26519
  // ../../rules/code-flaws/auth-jwt-no-verify.json
26126
26520
  var auth_jwt_no_verify_default = {
@@ -28148,7 +28542,7 @@ function bundledDetections() {
28148
28542
  }
28149
28543
 
28150
28544
  // ../../packages/plugin-sdk/src/repo.ts
28151
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
28545
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
28152
28546
  import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
28153
28547
 
28154
28548
  // ../../packages/plugin-sdk/src/events.ts
@@ -28159,21 +28553,25 @@ import { existsSync as existsSync7 } from "fs";
28159
28553
  import { fileURLToPath } from "url";
28160
28554
  import { Worker } from "worker_threads";
28161
28555
 
28556
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
28557
+ var import_ignore = __toESM(require_ignore(), 1);
28558
+ import { readFileSync as readFileSync8 } from "fs";
28559
+ import { join as join11 } from "path";
28560
+
28162
28561
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
28163
28562
  import { arch, hostname as hostname4, platform, release } from "os";
28164
28563
 
28165
28564
  // ../../packages/plugin-sdk/src/nudge.ts
28166
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
28167
- import { join as join11 } from "path";
28565
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
28566
+ import { join as join12 } from "path";
28168
28567
 
28169
28568
  // ../../packages/plugin-sdk/src/paths.ts
28170
28569
  import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
28171
28570
  import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
28172
28571
 
28173
28572
  // ../../packages/plugin-sdk/src/project-files.ts
28174
- var import_ignore = __toESM(require_ignore(), 1);
28175
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
28176
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
28573
+ import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
28574
+ import { basename as basename5, join as join13 } from "path";
28177
28575
 
28178
28576
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28179
28577
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -28208,8 +28606,8 @@ import { randomUUID as randomUUID14 } from "crypto";
28208
28606
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
28209
28607
 
28210
28608
  // ../../packages/plugin-sdk/src/throttle.ts
28211
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28212
- import { join as join13 } from "path";
28609
+ import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28610
+ import { join as join14 } from "path";
28213
28611
 
28214
28612
  // ../../packages/plugin-runtime/src/standalone-gateway.ts
28215
28613
  import { randomUUID as randomUUID15 } from "crypto";
@@ -28222,6 +28620,8 @@ var StandaloneDataGateway = class {
28222
28620
  db;
28223
28621
  // Kept for the fingerprint key lookup (exception.key lives beside the store).
28224
28622
  dataDir;
28623
+ // One notice per gateway — see warnRulesetDiscarded.
28624
+ warnedRulesetDiscarded = false;
28225
28625
  constructor(dataDir2, detections = [], meta3) {
28226
28626
  this.db = openLocalDatabase(dataDir2);
28227
28627
  this.dataDir = dataDir2;
@@ -28341,28 +28741,69 @@ var StandaloneDataGateway = class {
28341
28741
  // - ANY invalid rule among enabled packs (all-invalid, partial corruption,
28342
28742
  // or a single malformed entry) → undefined → bundled fallback. Serving a
28343
28743
  // reduced "complete" set would silently drop exactly the corrupted rules
28344
- // with no fallback; the bundled packs are a superset, so falling back
28345
- // never loses coverage. Steady-state installed rules are all valid
28346
- // (generated + Zod-checked), so this only fires on a genuinely
28347
- // malformed/foreign store;
28744
+ // with no fallback. The bundled packs are a superset of AKA's OWN packs,
28745
+ // so falling back never loses coverage there but they contain no
28746
+ // pulled or custom pack, so for those this trades a partial ruleset for
28747
+ // none of them plus the loss of every pack's per-detection enforcement
28748
+ // action. That is deliberate (a store this machine cannot fully validate
28749
+ // is not authoritative), and it is why the cost of REJECTING a rule
28750
+ // matters: `Rule` is strict, so one unrecognized key in one custom rule
28751
+ // reaches this branch, not just a genuinely malformed or foreign store.
28752
+ // `installed-packs.test.ts` pins that per-rule counting;
28348
28753
  // - enabled packs that produce ZERO rules with no invalids (e.g. every
28349
28754
  // enabled pack's rules_json is `[]`) → undefined → bundled fallback: an
28350
28755
  // enabled pack contributing nothing is untrustworthy, not a real
28351
28756
  // "detect nothing" (that is expressed by disabling packs, handled above);
28352
28757
  // - otherwise → the enabled packs' validated rules, marked complete.
28758
+ /**
28759
+ * The discard above is the one ruleset decision this gateway reaches on its
28760
+ * own, and it is the most expensive one here: ONE rejected entry costs the
28761
+ * user every custom rule and every per-detection enforcement action, replaced
28762
+ * by bundled packs that contain neither. Nothing else reports it — a hook is a
28763
+ * short-lived process whose stderr is the only channel it has — so name what
28764
+ * was rejected and where the rest of the list lives.
28765
+ *
28766
+ * Unlike a quarantine verdict this caches nothing: the rejection is re-derived
28767
+ * from the store on every run, so the recovery is to fix or reinstall the pack,
28768
+ * and no line here may offer a command that clears a stored verdict.
28769
+ *
28770
+ * Written at most once per gateway — a second getPolicyBundle() in the same
28771
+ * process would re-report the same finding.
28772
+ */
28773
+ warnRulesetDiscarded(snapshot) {
28774
+ if (this.warnedRulesetDiscarded) return;
28775
+ this.warnedRulesetDiscarded = true;
28776
+ const listed = snapshot.rejectedRules.map((r) => `${r.pack}${r.ruleId === null ? "" : ` "${r.ruleId}"`} (${r.reason})`).join(", ");
28777
+ const undisclosed = snapshot.invalidRules - snapshot.rejectedRules.length;
28778
+ const more = undisclosed > 0 ? `, and ${String(undisclosed)} more` : "";
28779
+ process.stderr.write(
28780
+ `[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\`
28781
+ `
28782
+ );
28783
+ }
28353
28784
  installedScanRules() {
28354
28785
  try {
28355
28786
  const snapshot = this.db.installedPacks.installedRuleset();
28356
28787
  if (snapshot.installedPacks === 0) return void 0;
28357
28788
  if (snapshot.enabledPacks === 0) {
28358
- return { rules: [], ruleActions: /* @__PURE__ */ new Map(), ruleVersions: /* @__PURE__ */ new Map(), complete: true };
28789
+ return {
28790
+ rules: [],
28791
+ ruleActions: /* @__PURE__ */ new Map(),
28792
+ ruleVersions: /* @__PURE__ */ new Map(),
28793
+ reversibleRules: /* @__PURE__ */ new Set(),
28794
+ complete: true
28795
+ };
28796
+ }
28797
+ if (snapshot.invalidRules > 0) {
28798
+ this.warnRulesetDiscarded(snapshot);
28799
+ return void 0;
28359
28800
  }
28360
- if (snapshot.invalidRules > 0) return void 0;
28361
28801
  if (snapshot.rules.length === 0) return void 0;
28362
28802
  return {
28363
28803
  rules: snapshot.rules,
28364
28804
  ruleActions: snapshot.ruleActions,
28365
28805
  ruleVersions: snapshot.ruleVersions,
28806
+ reversibleRules: snapshot.reversibleRules,
28366
28807
  complete: true
28367
28808
  };
28368
28809
  } catch {
@@ -28390,6 +28831,11 @@ var StandaloneDataGateway = class {
28390
28831
  return {
28391
28832
  version: "local",
28392
28833
  policies: [...policies, ...rulePolicies],
28834
+ // The reversibility half of each pack's assignment. Emitted only under the
28835
+ // authoritative installed snapshot, exactly like rulePolicies above: the
28836
+ // bundled-packs fallback carries no per-pack assignment, so it carries no
28837
+ // reversibility either and every redaction there stays one-way.
28838
+ reversibleRuleIds: installed ? [...installed.reversibleRules] : [],
28393
28839
  rules: installed ? installed.rules : [],
28394
28840
  ...installed ? { rulesComplete: true } : {},
28395
28841
  ...installed ? { ruleVersions: Object.fromEntries(installed.ruleVersions) } : {},
@@ -28408,29 +28854,32 @@ var StandaloneDataGateway = class {
28408
28854
  return this.db.exceptions.recordBlocked(entry);
28409
28855
  }
28410
28856
  // Retention sweep over TERMINAL exception rows (revoked / expired / budget
28411
- // exhausted) — standalone-only store maintenance, invoked from SessionStart,
28412
- // not part of the DataGateway port. Active grants are never touched.
28857
+ // exhausted) — local-store maintenance, invoked from SessionStart through the
28858
+ // LocalStoreMaintenance capability rather than the DataGateway port. Active
28859
+ // grants are never touched.
28413
28860
  sweepTerminalExceptions(retentionMs) {
28414
28861
  return this.db.exceptions.sweepTerminal(retentionMs);
28415
28862
  }
28416
- // The warn-era enforcement cap, standalone-only store maintenance invoked
28417
- // from SessionStart, not part of the DataGateway port. Returns the number
28418
- // of block/redact rows capped to warn (0 for a redact-policy store or an
28419
- // already-capped one).
28863
+ // The warn-era enforcement cap — local-store maintenance, invoked from
28864
+ // SessionStart through the LocalStoreMaintenance capability rather than
28865
+ // the DataGateway port. Returns the number of block/redact rows capped to
28866
+ // warn (0 for a redact-policy store or an already-capped one).
28420
28867
  capWarnEraEnforcement(policyMode) {
28421
28868
  const { capped } = capWarnEraEnforcementOnce(this.db, policyMode, this.dataDir);
28422
28869
  return { capped };
28423
28870
  }
28424
28871
  // One project-file scan → the local project_file tree (one transaction inside
28425
- // the LocalDatabase, fail-open there). Like the sweep above, this is
28426
- // NOT part of the DataGateway port: the file tree is a local-store read model.
28872
+ // the LocalDatabase, fail-open there). Like the sweep above, this is reached
28873
+ // through the LocalStoreMaintenance capability rather than the DataGateway
28874
+ // port: the file tree is a local-store read model.
28427
28875
  recordProjectFiles(projectId, scan2) {
28428
28876
  this.db.recordProjectFiles(projectId, scan2);
28429
28877
  return Promise.resolve();
28430
28878
  }
28431
28879
  // Fold ghost source_project rows minted by the pre-worktree-fix resolver
28432
- // (checkout-path identities) into the repo's canonical row. Standalone-only
28433
- // store maintenance, invoked from SessionStart. Fail-open in the store.
28880
+ // (checkout-path identities) into the repo's canonical row. Local-store
28881
+ // maintenance, invoked from SessionStart through the LocalStoreMaintenance
28882
+ // capability. Fail-open in the store.
28434
28883
  reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
28435
28884
  this.db.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
28436
28885
  return Promise.resolve();
@@ -28441,10 +28890,10 @@ var StandaloneDataGateway = class {
28441
28890
  * executing the plugin generation they started with (Claude Code caches
28442
28891
  * plugin versions), and the write gate makes their installed-pack writes
28443
28892
  * silent no-ops — this is the one-line nudge telling the user WHY, and that
28444
- * a restart picks the newer plugin up. Standalone-only, invoked from
28445
- * SessionStart, not part of the DataGateway port. Fail-open: any error →
28446
- * null (no notice), and unparseable versions compare equal so garbage can
28447
- * never fire it.
28893
+ * a restart picks the newer plugin up. Local-store maintenance, invoked
28894
+ * from SessionStart through the LocalStoreMaintenance capability rather
28895
+ * than the DataGateway port. Fail-open: any error → null (no notice), and
28896
+ * unparseable versions compare equal so garbage can never fire it.
28448
28897
  */
28449
28898
  staleBinaryNotice(currentVersion) {
28450
28899
  try {
@@ -28522,7 +28971,8 @@ var StandaloneDataGateway = class {
28522
28971
 
28523
28972
  // ../../packages/plugin-runtime/src/resolve.ts
28524
28973
  var standaloneGatewayFactory = (config2, meta3) => new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
28525
- function resolveDataGateway(config2, meta3, gatewayFactory = standaloneGatewayFactory) {
28974
+ var defaultGatewayFactory = standaloneGatewayFactory;
28975
+ function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
28526
28976
  return gatewayFactory(config2, meta3);
28527
28977
  }
28528
28978
 
@@ -28624,8 +29074,8 @@ function table(headers, rows, opts = {}) {
28624
29074
  const widths = headers.map(
28625
29075
  (h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
28626
29076
  );
28627
- const sep5 = " ".repeat(gap);
28628
- const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
29077
+ const sep4 = " ".repeat(gap);
29078
+ const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep4);
28629
29079
  const headerLine = fmt(headers.map((h) => h.toUpperCase()));
28630
29080
  if (opts.rowSep === true) {
28631
29081
  const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
@@ -28637,7 +29087,7 @@ function table(headers, rows, opts = {}) {
28637
29087
  });
28638
29088
  return [headerLine, rule, ...body].join("\n");
28639
29089
  }
28640
- const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
29090
+ const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep4);
28641
29091
  return [headerLine, ruleLine, ...rows.map(fmt)].join("\n");
28642
29092
  }
28643
29093
  function fenced(body) {
@@ -28648,12 +29098,17 @@ function fenced(body) {
28648
29098
 
28649
29099
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
28650
29100
  import { writeFileSync as writeFileSync7 } from "fs";
28651
- import { join as join14 } from "path";
29101
+ import { join as join15 } from "path";
29102
+
29103
+ // ../../packages/setup-wizard/src/triage/merge.ts
29104
+ var RANK = Object.fromEntries(
29105
+ KNOWN_BUILTIN_IDS.map((id, i) => [id, i])
29106
+ );
28652
29107
 
28653
29108
  // ../../packages/setup-wizard/src/triage/plan-file.ts
28654
- import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
29109
+ import { mkdtempSync, readFileSync as readFileSync10, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
28655
29110
  import { tmpdir } from "os";
28656
- import { basename as basename6, dirname as dirname4, join as join15 } from "path";
29111
+ import { basename as basename6, dirname as dirname4, join as join16 } from "path";
28657
29112
  var SuppressionEntrySchema = external_exports.object({
28658
29113
  ruleId: external_exports.string(),
28659
29114
  category: DetectionCategory,
@@ -28778,14 +29233,14 @@ function renderStatusBar(s, opts = {}) {
28778
29233
  const unreviewed = `unreviewed ${SHADE.full}${String(u.critical)} ${SHADE.dark}${String(u.high)} ${SHADE.medium}${String(u.medium)} ${SHADE.light}${String(u.low)}`;
28779
29234
  return `\u25B8\u25B8 AKA health ${String(s.score)}/100 ${unreviewed} \u2691 ${String(s.openFindings)} open findings`;
28780
29235
  }
28781
- const sep5 = ` ${paint.dim("\u2502")} `;
29236
+ const sep4 = ` ${paint.dim("\u2502")} `;
28782
29237
  const sq = "\u25A0";
28783
29238
  const dot = s.score >= 80 ? paint.ok("\u25CF") : s.score >= 50 ? paint.high("\u25CF") : paint.critical("\u25CF");
28784
29239
  const score = `${dot} health ${paint.bold(String(s.score))}${paint.dim("/100")}`;
28785
29240
  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)}`;
28786
29241
  const flag = s.openFindings > 0 ? paint.critical("\u2691") : paint.dim("\u2691");
28787
29242
  const open2 = `${flag} ${String(s.openFindings)} open findings`;
28788
- return `${paint.brand("\u25B8\u25B8 AKA")}${sep5}${score}${sep5}${tally}${sep5}${open2}`;
29243
+ return `${paint.brand("\u25B8\u25B8 AKA")}${sep4}${score}${sep4}${tally}${sep4}${open2}`;
28789
29244
  }
28790
29245
  function findingStatus(summary) {
28791
29246
  return {
@@ -29047,7 +29502,7 @@ function renderDetections(items) {
29047
29502
  i.latestVersion ? `v${i.latestVersion}` : `v${i.version}`,
29048
29503
  String(i.ruleCount),
29049
29504
  i.enabled ? "yes" : "no",
29050
- i.policyId ?? "monitor",
29505
+ policyDisplayName(i.policyId),
29051
29506
  i.latestVersion ? "\u2B06 update available" : "\u2713 up to date"
29052
29507
  ]);
29053
29508
  const updates = items.filter((i) => i.latestVersion != null);