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