@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,691 +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, flags2) {
16440
- try {
16441
- new RegExp(pattern, flags2);
16442
- return true;
16443
- } catch {
16444
- return false;
16445
- }
16446
- }
16447
- function matchesEmptyString(pattern, flags2) {
16448
- try {
16449
- const re = new RegExp(pattern, flags2.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
- function severityFloorPosture() {
16609
- const out = {};
16610
- for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
16611
- return out;
16612
- }
16613
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16614
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16615
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16616
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16617
- var BUILTIN_POLICY_SPECS = {
16618
- monitor: {
16619
- name: "Monitor",
16620
- action: "log",
16621
- description: "Log every match for audit. The request is allowed through untouched."
16622
- },
16623
- warn: {
16624
- name: "Warn",
16625
- action: "warn",
16626
- description: "Allow the request, but warn the user inline before it is sent."
16627
- },
16628
- redact: {
16629
- name: "Redact",
16630
- action: "redact",
16631
- description: "Automatically strip the matched value from the request, then continue."
16632
- },
16633
- block: {
16634
- name: "Block",
16635
- action: "block",
16636
- description: "Refuse the request entirely whenever any rule in this detection matches."
16637
- }
16638
- };
16639
- function builtinPolicyToAction(id) {
16640
- return BUILTIN_POLICY_SPECS[id].action;
16641
- }
16642
- var DEFAULT_ACTIONS = Object.fromEntries(
16643
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16644
- );
16645
- var BUILTIN_POLICIES = Object.fromEntries(
16646
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16647
- );
16648
- var DEFAULT_PACK_POLICY_ID = "monitor";
16649
- function policyIdToAction(policyId) {
16650
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16651
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16652
- return BUILTIN_POLICIES[id].action;
16653
- }
16654
- var UsedByItem = external_exports.object({
16655
- id: external_exports.string(),
16656
- name: external_exports.string(),
16657
- ruleCount: external_exports.number().int().nonnegative(),
16658
- enabled: external_exports.boolean()
16659
- }).meta({ id: "UsedByItem" });
16660
- var PolicyListItem = external_exports.object({
16661
- id: external_exports.string(),
16662
- kind: PolicyKind,
16663
- name: external_exports.string(),
16664
- enabled: external_exports.boolean(),
16665
- usedByCount: external_exports.number().int().nonnegative()
16666
- }).meta({ id: "PolicyListItem" });
16667
- var PolicyDetail = external_exports.object({
16668
- specVersion: external_exports.literal(1),
16669
- id: external_exports.string(),
16670
- kind: PolicyKind,
16671
- name: external_exports.string(),
16672
- enabled: external_exports.boolean(),
16673
- description: external_exports.string(),
16674
- usedBy: external_exports.array(UsedByItem)
16675
- }).meta({ id: "PolicyDetail" });
16676
- var PolicyStatsResponse = external_exports.object({
16677
- policies: external_exports.number().int().nonnegative(),
16678
- builtin: external_exports.number().int().nonnegative(),
16679
- custom: external_exports.number().int().nonnegative(),
16680
- detectionsGoverned: external_exports.number().int().nonnegative()
16681
- }).meta({ id: "PolicyStatsResponse" });
16682
-
16683
- // ../../packages/schema/src/zod/api.ts
16684
- var LIST_QUERY_MAX_LIMIT = 200;
16685
- var ListEventsQuery = external_exports.object({
16686
- cursor: external_exports.string().optional(),
16687
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16688
- sourceTool: external_exports.string().optional(),
16689
- kind: external_exports.string().optional(),
16690
- from: external_exports.iso.datetime().optional(),
16691
- to: external_exports.iso.datetime().optional()
16692
- });
16693
- var ListEventsResponse = external_exports.object({
16694
- items: external_exports.array(Event),
16695
- nextCursor: external_exports.string().nullable()
16696
- }).meta({ id: "ListEventsResponse" });
16697
- var IngestResponse = external_exports.object({
16698
- accepted: external_exports.number().int().nonnegative(),
16699
- duplicates: external_exports.number().int().nonnegative()
16700
- }).meta({ id: "IngestResponse" });
16701
- var ListFindingsQuery = external_exports.object({
16702
- cursor: external_exports.string().optional(),
16703
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16704
- severity: external_exports.string().optional(),
16705
- category: external_exports.string().optional(),
16706
- eventId: external_exports.guid().optional()
16707
- });
16708
- var ListFindingsResponse = external_exports.object({
16709
- items: external_exports.array(Finding),
16710
- nextCursor: external_exports.string().nullable()
16711
- }).meta({ id: "ListFindingsResponse" });
16712
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16713
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16714
- id: "CreatePolicyRequest"
16715
- });
16716
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16717
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16718
- var ErrorResponse = external_exports.object({
16719
- error: external_exports.object({
16720
- code: external_exports.string(),
16721
- message: external_exports.string(),
16722
- details: external_exports.unknown().optional()
16723
- })
16724
- }).meta({ id: "ErrorResponse" });
16725
-
16726
- // ../../packages/schema/src/zod/config-inventory.ts
16727
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16728
- var SkillScanEntry = external_exports.object({
16729
- name: external_exports.string().min(1),
16730
- // The identity source: a marketplace repo for plugin skills (e.g.
16731
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16732
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16733
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16734
- source: external_exports.string().min(1),
16735
- scope: ConfigScope,
16736
- pluginName: external_exports.string().optional(),
16737
- // Volatile — rides the attribute bag, never the identity hash.
16738
- version: external_exports.string().optional(),
16739
- description: external_exports.string().optional(),
16740
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16741
- updatedAt: external_exports.iso.datetime().optional(),
16742
- // Filesystem path — the promoted inventory `location` column.
16743
- location: external_exports.string().optional()
16744
- });
16745
- var HookScanEntry = external_exports.object({
16746
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16747
- // set is harness-defined and grows without a schema change.
16748
- event: external_exports.string().min(1),
16749
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16750
- matcher: external_exports.string().optional(),
16751
- command: external_exports.string().min(1),
16752
- timeout: external_exports.number().optional(),
16753
- scope: ConfigScope,
16754
- pluginName: external_exports.string().optional(),
16755
- // The settings file / hooks.json the entry came from.
16756
- 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()
16757
16126
  });
16758
16127
  var McpServerScanEntry = external_exports.object({
16759
16128
  // The server's config key ("github", "filesystem", …) — identity, with the
@@ -16835,6 +16204,164 @@ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16835
16204
  var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16836
16205
  var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16837
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, flags2) {
16219
+ try {
16220
+ new RegExp(pattern, flags2);
16221
+ return true;
16222
+ } catch {
16223
+ return false;
16224
+ }
16225
+ }
16226
+ function probeFlags(flags2) {
16227
+ return flags2.replace(/[gy]/g, "");
16228
+ }
16229
+ function matchesEmptyString(pattern, flags2) {
16230
+ try {
16231
+ const re = new RegExp(pattern, probeFlags(flags2));
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, flags2) {
16241
+ try {
16242
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags2));
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
+
16838
16365
  // ../../packages/schema/src/zod/detection.ts
16839
16366
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
16840
16367
  var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
@@ -17033,6 +16560,231 @@ function buildDetectionsList(summaries, query) {
17033
16560
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17034
16561
  }
17035
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
+
17036
16788
  // ../../packages/schema/src/zod/shares.ts
17037
16789
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17038
16790
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17239,6 +16991,127 @@ var EgressWriteSummary = external_exports.object({
17239
16991
  droppedFiles: external_exports.array(external_exports.string()).default([])
17240
16992
  }).meta({ id: "EgressWriteSummary" });
17241
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
+
17242
17115
  // ../../packages/schema/src/zod/exception-action.ts
17243
17116
  var confirmation = external_exports.string().optional();
17244
17117
  var ApproveBlockedInput = external_exports.object({
@@ -17281,10 +17154,11 @@ function toApiAction(dbVal) {
17281
17154
  }
17282
17155
  function toApiCategory(dbVal) {
17283
17156
  if (dbVal === "code_context") return "source_code";
17284
- return dbVal;
17157
+ const parsed = FindingCategory.safeParse(dbVal);
17158
+ return parsed.success ? parsed.data : "custom";
17285
17159
  }
17286
17160
  function toApiProvider(sourceTool) {
17287
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17161
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17288
17162
  }
17289
17163
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17290
17164
  function foldGroupStatus(instanceStatuses) {
@@ -17858,25 +17732,35 @@ var VaultConsent = external_exports.object({
17858
17732
  acknowledgedAt: external_exports.iso.datetime(),
17859
17733
  version: external_exports.number().int().positive()
17860
17734
  });
17735
+ function isVaultConsentValid(consent) {
17736
+ return consent?.version === VAULT_CONSENT_VERSION;
17737
+ }
17861
17738
 
17862
17739
  // ../../packages/schema/src/zod/local.ts
17863
17740
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17864
17741
  var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17865
- var RunMode = external_exports.enum(["standalone"]);
17742
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17743
+ var ControlPlaneConnection = external_exports.object({
17744
+ endpoint: external_exports.string().min(1),
17745
+ // Display name for the deployment, shown instead of the raw endpoint.
17746
+ label: external_exports.string().min(1).optional(),
17747
+ attachedAt: external_exports.iso.datetime()
17748
+ }).meta({ id: "ControlPlaneConnection" });
17866
17749
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17867
17750
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17868
17751
  var ModelJudgeConsent = external_exports.object({
17869
17752
  acknowledgedAt: external_exports.iso.datetime(),
17870
17753
  payloadVersion: external_exports.number().int().positive()
17871
17754
  });
17755
+ function isModelJudgeConsentValid(consent) {
17756
+ return consent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION;
17757
+ }
17872
17758
  var WorkspaceSettings = external_exports.object({
17873
17759
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17874
- // Settings files written by earlier releases may carry the retired 'attached'
17875
- // value; it parses as 'standalone' so those files keep loading.
17876
- runMode: external_exports.preprocess(
17877
- (v) => v === "attached" ? "standalone" : v,
17878
- RunMode.default("standalone")
17879
- ),
17760
+ runMode: RunMode.default("standalone"),
17761
+ // Present only while attached; a detach clears it. Its presence is what makes
17762
+ // `runMode: 'attached'` mean anything — see isAttached.
17763
+ controlPlane: ControlPlaneConnection.optional(),
17880
17764
  policy: SimpleDetectionPolicy.default("redact"),
17881
17765
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17882
17766
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -18012,6 +17896,217 @@ function toCaptureDefinitionInput(finding) {
18012
17896
  };
18013
17897
  }
18014
17898
 
17899
+ // ../../packages/schema/src/zod/managed.ts
17900
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
17901
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
17902
+ var ManagedSettingKey = external_exports.enum([
17903
+ "runMode",
17904
+ "historicalAccess",
17905
+ "vaultConsent",
17906
+ "vaultKeyCustody",
17907
+ "vaultInlineReveal",
17908
+ "modelJudgeConsent",
17909
+ "dataSharesInPlace"
17910
+ ]).meta({ id: "ManagedSettingKey" });
17911
+ var ManagedSettingsValues = external_exports.object({
17912
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
17913
+ controlPlane: external_exports.object({
17914
+ endpoint: external_exports.string().min(1),
17915
+ label: external_exports.string().min(1).optional()
17916
+ }).optional(),
17917
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
17918
+ vaultConsent: external_exports.boolean().optional(),
17919
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
17920
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
17921
+ modelJudgeConsent: external_exports.boolean().optional(),
17922
+ dataSharesInPlace: external_exports.boolean().optional()
17923
+ }).meta({ id: "ManagedSettingsValues" });
17924
+ var ManagedSettings = external_exports.object({
17925
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
17926
+ // Shown on every locked control, so the user can tell an administrative
17927
+ // decision from a bug. Absent renders as a generic "your organization".
17928
+ organization: external_exports.string().min(1).optional(),
17929
+ // What the administrator pinned.
17930
+ values: ManagedSettingsValues.default({}),
17931
+ // Which of those the user may not change. A key here with no matching value
17932
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
17933
+ // the user may still override. The two are separable on purpose.
17934
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
17935
+ }).meta({ id: "ManagedSettings" });
17936
+ var NO_MANAGED_CONTEXT = { present: false, lockedFields: [] };
17937
+
17938
+ // ../../packages/schema/src/zod/policy.ts
17939
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
17940
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
17941
+ var Policy = external_exports.object({
17942
+ id: external_exports.guid(),
17943
+ scope: PolicyScope,
17944
+ target: PolicyTarget,
17945
+ action: ActionTaken,
17946
+ enabled: external_exports.boolean().default(true),
17947
+ customKeywords: external_exports.array(external_exports.string()).optional(),
17948
+ // Display name — optional so older policy rows without name still parse.
17949
+ // Added for the findings API (policy.name column migration).
17950
+ name: external_exports.string().optional()
17951
+ }).meta({ id: "Policy" });
17952
+ var PolicyBundle = external_exports.object({
17953
+ version: external_exports.string(),
17954
+ policies: external_exports.array(Policy),
17955
+ // Rules from the installed marketplace packs (snapshotted by the
17956
+ // control plane). The plugin registers these in addition to its bundled
17957
+ // packs. Optional so older backends — and older on-disk caches — that omit
17958
+ // the field still parse; consumers read `bundle.rules ?? []`.
17959
+ rules: external_exports.array(Rule).optional(),
17960
+ // When true, `rules` IS the complete effective ruleset and the runtime must
17961
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
17962
+ // after reading the user's installed snapshot (installed_packs, enabled
17963
+ // packs only), which is how detection updates stay manual: new bundled
17964
+ // rules run only after the user applies the pack update. Absent/false keeps
17965
+ // the historical composition (bundled packs + rules) — older caches.
17966
+ rulesComplete: external_exports.boolean().optional(),
17967
+ // Active detection exceptions, evaluation subset only (see
17968
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
17969
+ // on-disk caches — that omit the field still parse; consumers read
17970
+ // `bundle.exceptions ?? []`.
17971
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
17972
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
17973
+ // A second axis over the same `redact` action, carried beside the policies
17974
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
17975
+ // widening Policy itself would change a persisted shape to express something
17976
+ // only the in-memory bundle needs. Optional so an older producer — or an
17977
+ // older on-disk cache — still parses; consumers read `?? []` and get the
17978
+ // pre-existing one-way behaviour, which is the safe direction to default.
17979
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
17980
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
17981
+ // from a versioned installed pack. Optional so older backends — and older
17982
+ // on-disk caches — that omit the field still parse; consumers fall back to
17983
+ // the rule's own spec version. NOT the bundle version above — see
17984
+ // installedRuleset's ruleVersions for the source of truth.
17985
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
17986
+ customKeywords: external_exports.array(external_exports.string()),
17987
+ fetchedAt: external_exports.iso.datetime()
17988
+ }).meta({ id: "PolicyBundle" });
17989
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
17990
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
17991
+ var CATEGORY_PEAK_SEVERITY = {
17992
+ secret: "critical",
17993
+ financial: "critical",
17994
+ // core-financial/credit-card
17995
+ code_flaw: "critical",
17996
+ pii: "high",
17997
+ phi: "high",
17998
+ custom: "high",
17999
+ // user-defined; conservative
18000
+ code_context: "low",
18001
+ config: "low"
18002
+ // observe-only; floors to monitor regardless
18003
+ };
18004
+ function severityFloorPolicy(category) {
18005
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18006
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18007
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18008
+ }
18009
+ function severityFloorPosture() {
18010
+ const out = {};
18011
+ for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
18012
+ return out;
18013
+ }
18014
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18015
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18016
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18017
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18018
+ var BUILTIN_POLICY_SPECS = {
18019
+ monitor: {
18020
+ name: "Monitor",
18021
+ action: "log",
18022
+ reversible: false,
18023
+ description: "Log every match for audit. The request is allowed through untouched."
18024
+ },
18025
+ warn: {
18026
+ name: "Warn",
18027
+ action: "warn",
18028
+ reversible: false,
18029
+ description: "Allow the request, but warn the user inline before it is sent."
18030
+ },
18031
+ redact: {
18032
+ name: "Redact",
18033
+ action: "redact",
18034
+ reversible: false,
18035
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18036
+ },
18037
+ vault: {
18038
+ name: "Redact & Vault",
18039
+ action: "redact",
18040
+ reversible: true,
18041
+ 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."
18042
+ },
18043
+ block: {
18044
+ name: "Block",
18045
+ action: "block",
18046
+ reversible: false,
18047
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18048
+ }
18049
+ };
18050
+ function builtinPolicyToAction(id) {
18051
+ return BUILTIN_POLICY_SPECS[id].action;
18052
+ }
18053
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18054
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18055
+ );
18056
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18057
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18058
+ );
18059
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18060
+ function builtinPolicyIsReversible(id) {
18061
+ return BUILTIN_POLICY_SPECS[id].reversible;
18062
+ }
18063
+ function policyIdIsReversible(policyId) {
18064
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18065
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18066
+ return builtinPolicyIsReversible(id);
18067
+ }
18068
+ var DEFAULT_ACTIONS = Object.fromEntries(
18069
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18070
+ );
18071
+ var BUILTIN_POLICIES = Object.fromEntries(
18072
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18073
+ );
18074
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18075
+ function policyIdToAction(policyId) {
18076
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18077
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18078
+ return BUILTIN_POLICIES[id].action;
18079
+ }
18080
+ var UsedByItem = external_exports.object({
18081
+ id: external_exports.string(),
18082
+ name: external_exports.string(),
18083
+ ruleCount: external_exports.number().int().nonnegative(),
18084
+ enabled: external_exports.boolean()
18085
+ }).meta({ id: "UsedByItem" });
18086
+ var PolicyListItem = external_exports.object({
18087
+ id: external_exports.string(),
18088
+ kind: PolicyKind,
18089
+ name: external_exports.string(),
18090
+ enabled: external_exports.boolean(),
18091
+ usedByCount: external_exports.number().int().nonnegative()
18092
+ }).meta({ id: "PolicyListItem" });
18093
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18094
+ var PolicyDetail = external_exports.object({
18095
+ specVersion: external_exports.literal(1),
18096
+ id: external_exports.string(),
18097
+ kind: PolicyKind,
18098
+ name: external_exports.string(),
18099
+ enabled: external_exports.boolean(),
18100
+ description: external_exports.string(),
18101
+ usedBy: external_exports.array(UsedByItem)
18102
+ }).meta({ id: "PolicyDetail" });
18103
+ var PolicyStatsResponse = external_exports.object({
18104
+ policies: external_exports.number().int().nonnegative(),
18105
+ builtin: external_exports.number().int().nonnegative(),
18106
+ custom: external_exports.number().int().nonnegative(),
18107
+ detectionsGoverned: external_exports.number().int().nonnegative()
18108
+ }).meta({ id: "PolicyStatsResponse" });
18109
+
18015
18110
  // ../../packages/schema/src/zod/project-files.ts
18016
18111
  var ProjectFileInput = external_exports.object({
18017
18112
  path: external_exports.string().min(1),
@@ -18090,44 +18185,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18090
18185
  NoRemediationDecision
18091
18186
  ]);
18092
18187
 
18093
- // ../../packages/schema/src/zod/rule-test.ts
18094
- var TestRulesRequest = external_exports.object({
18095
- rules: external_exports.array(Rule).min(1).max(100),
18096
- text: external_exports.string().max(5e4).optional(),
18097
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18098
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18099
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18100
- }).meta({ id: "TestRulesRequest" });
18101
- var RuleTestMatch = external_exports.object({
18102
- ruleId: external_exports.string(),
18103
- category: DetectionCategory,
18104
- severity: Severity,
18105
- span: Span,
18106
- confidence: external_exports.number().min(0).max(1),
18107
- match: external_exports.string()
18108
- }).meta({ id: "RuleTestMatch" });
18109
- var FixtureResult = external_exports.object({
18110
- label: external_exports.string(),
18111
- shouldMatch: external_exports.boolean(),
18112
- didMatch: external_exports.boolean(),
18113
- passed: external_exports.boolean(),
18114
- matches: external_exports.array(RuleTestMatch)
18115
- }).meta({ id: "FixtureResult" });
18116
- var TestRulesResponse = external_exports.object({
18117
- // Present only when the request supplied `text`.
18118
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18119
- fixtures: external_exports.array(FixtureResult),
18120
- summary: external_exports.object({
18121
- total: external_exports.number().int().nonnegative(),
18122
- passed: external_exports.number().int().nonnegative(),
18123
- failed: external_exports.number().int().nonnegative()
18124
- }),
18125
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18126
- // `validator`), so they silently never match. Surfaced so an author is not
18127
- // misled by a green run that actually skipped a rule.
18128
- unsupportedRuleIds: external_exports.array(external_exports.string())
18129
- }).meta({ id: "TestRulesResponse" });
18130
-
18131
18188
  // ../../packages/schema/src/zod/security.ts
18132
18189
  var SeveritySummaryItem = external_exports.object({
18133
18190
  severity: Severity,
@@ -18225,10 +18282,22 @@ var TopSourcesQuery = external_exports.object({
18225
18282
  // Omit for both kinds.
18226
18283
  kind: external_exports.enum(SOURCE_KINDS).optional()
18227
18284
  });
18228
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18285
+ var Provider = Harness.extract([
18286
+ "ClaudeCode",
18287
+ "Cursor",
18288
+ "Codex",
18289
+ "Antigravity",
18290
+ "ClaudeAi",
18291
+ "ChatGpt",
18292
+ "Copilot",
18293
+ "Api"
18294
+ ]).meta({ id: "Provider" });
18229
18295
  var ScanCoverageProvider = external_exports.object({
18230
18296
  provider: Provider,
18231
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18297
+ // Percent of that provider's traffic the shipped capture surface reaches.
18298
+ // A curated business fact, constant across every `range` — not a measured
18299
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18300
+ // above the block for where these numbers are decided.
18232
18301
  coverage: external_exports.number().int().min(0).max(100),
18233
18302
  supported: external_exports.boolean()
18234
18303
  }).meta({ id: "ScanCoverageProvider" });
@@ -18282,6 +18351,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18282
18351
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18283
18352
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18284
18353
 
18354
+ // ../../packages/schema/src/zod/settings-action.ts
18355
+ var SaveSettingsInput = external_exports.object({
18356
+ historicalAccess: external_exports.string(),
18357
+ modelJudgeConsent: external_exports.boolean(),
18358
+ vaultConsent: external_exports.string(),
18359
+ vaultInlineReveal: external_exports.string()
18360
+ });
18361
+ var AttachInput = external_exports.object({
18362
+ endpoint: external_exports.string(),
18363
+ label: external_exports.string().optional()
18364
+ });
18365
+
18285
18366
  // ../../packages/schema/src/zod/triage.ts
18286
18367
  var TriageHit = external_exports.object({
18287
18368
  ruleId: external_exports.string(),
@@ -18296,7 +18377,7 @@ var TriageHit = external_exports.object({
18296
18377
  valueFingerprint: external_exports.string().optional(),
18297
18378
  keyVersion: external_exports.number().int().nonnegative().optional()
18298
18379
  });
18299
- var TriagePolicy = BuiltinPolicyId;
18380
+ var TriagePolicy = CategoryPolicyId;
18300
18381
  var TriageCategoryRec = external_exports.object({
18301
18382
  category: DetectionCategory,
18302
18383
  action: TriagePolicy,
@@ -18503,8 +18584,11 @@ function chmodBestEffort(path, mode) {
18503
18584
  function tightenDir(dir) {
18504
18585
  chmodBestEffort(dir, DATA_DIR_MODE);
18505
18586
  }
18587
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18588
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18589
+ }
18506
18590
  function ensureDataDirSync(dir) {
18507
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18591
+ mkdirOwnerOnlySync(dir, true);
18508
18592
  tightenDir(dir);
18509
18593
  }
18510
18594
  function dbSidecars(file2) {
@@ -18516,6 +18600,9 @@ function tightenFile(file2) {
18516
18600
  function tightenPerms(file2) {
18517
18601
  for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
18518
18602
  }
18603
+ function writeExclusiveOwnerOnlySync(file2, data) {
18604
+ writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
18605
+ }
18519
18606
  function writeOwnerOnlyFileSync(file2, data) {
18520
18607
  const tmp = `${file2}.${String(process.pid)}.tmp`;
18521
18608
  try {
@@ -18523,7 +18610,7 @@ function writeOwnerOnlyFileSync(file2, data) {
18523
18610
  } catch {
18524
18611
  }
18525
18612
  try {
18526
- writeFileSync(tmp, data, { mode: DATA_FILE_MODE, flag: "wx" });
18613
+ writeExclusiveOwnerOnlySync(tmp, data);
18527
18614
  renameSync(tmp, file2);
18528
18615
  } finally {
18529
18616
  try {
@@ -18539,6 +18626,25 @@ function backupPath(file2, tag) {
18539
18626
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18540
18627
  }
18541
18628
  var STALE_PARTIAL_MS = 5 * 6e4;
18629
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18630
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18631
+ var SNAPSHOT_STAGING_COPY = "copy";
18632
+ function createSnapshotStaging(backup) {
18633
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18634
+ rmSync2(stage, { recursive: true, force: true });
18635
+ mkdirOwnerOnlySync(stage);
18636
+ tightenDir(stage);
18637
+ return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
18638
+ }
18639
+ function idleMs(entry) {
18640
+ for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
18641
+ try {
18642
+ return Date.now() - statSync(candidate).mtimeMs;
18643
+ } catch {
18644
+ }
18645
+ }
18646
+ return null;
18647
+ }
18542
18648
  function reapStalePartials(file2) {
18543
18649
  const dir = dirname(file2);
18544
18650
  const prefix = `${basename(file2)}.`;
@@ -18549,30 +18655,34 @@ function reapStalePartials(file2) {
18549
18655
  return;
18550
18656
  }
18551
18657
  for (const name of entries) {
18552
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18553
- const partial2 = join(dir, name);
18658
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18659
+ const staging = join(dir, name);
18554
18660
  try {
18555
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18556
- rmSync2(partial2, { force: true });
18661
+ const idle = idleMs(staging);
18662
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18663
+ rmSync2(staging, { recursive: true, force: true });
18557
18664
  }
18558
18665
  } catch {
18559
18666
  }
18560
18667
  }
18561
18668
  }
18562
18669
  function snapshotStore(db, backup) {
18563
- const partial2 = `${backup}.partial`;
18670
+ const { stage, copy } = createSnapshotStaging(backup);
18564
18671
  try {
18565
- rmSync2(partial2, { force: true });
18566
- db.prepare("VACUUM INTO ?").run(partial2);
18567
- tightenFile(partial2);
18568
- renameSync2(partial2, backup);
18672
+ db.prepare("VACUUM INTO ?").run(copy);
18673
+ tightenFile(copy);
18674
+ renameSync2(copy, backup);
18569
18675
  } catch (error51) {
18570
18676
  try {
18571
- rmSync2(partial2, { force: true });
18677
+ rmSync2(stage, { recursive: true, force: true });
18572
18678
  } catch {
18573
18679
  }
18574
18680
  throw error51;
18575
18681
  }
18682
+ try {
18683
+ rmSync2(stage, { recursive: true, force: true });
18684
+ } catch {
18685
+ }
18576
18686
  }
18577
18687
  function moveStoreAside(file2, backup) {
18578
18688
  const undo = [];
@@ -19300,9 +19410,10 @@ function safeParseStringArray(raw) {
19300
19410
  const parsed = safeJson(raw, null);
19301
19411
  return Array.isArray(parsed) ? parsed : [];
19302
19412
  }
19413
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19303
19414
  function toHarness(raw) {
19304
19415
  const parsed = Harness.safeParse(raw);
19305
- return parsed.success ? parsed.data : "claudecode";
19416
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19306
19417
  }
19307
19418
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19308
19419
  if (row.status) {
@@ -19433,7 +19544,7 @@ var SqliteActivityRepository = class {
19433
19544
  const params = [];
19434
19545
  if (query.harness && query.harness.length > 0) {
19435
19546
  conditions.push(
19436
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19547
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19437
19548
  );
19438
19549
  params.push(...query.harness);
19439
19550
  }
@@ -19640,13 +19751,13 @@ var SqliteActivityRepository = class {
19640
19751
  * The DISTINCT harnesses that actually have sessions (optionally within a
19641
19752
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19642
19753
  * present rather than the full enum. Each stored value is normalized through
19643
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19644
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19754
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19755
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19645
19756
  */
19646
19757
  harnessFacets(fromMs) {
19647
19758
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19648
19759
  const stmt = this.db.prepare(
19649
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19760
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19650
19761
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19651
19762
  );
19652
19763
  const rows = allRows(
@@ -19860,8 +19971,8 @@ var SqliteAuditEventsRepository = class {
19860
19971
  }
19861
19972
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19862
19973
  // (which takes a caller-supplied random id), the id here is MINTED internally
19863
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19864
- // sibling local-store ids (the local store is single-tenant). The deterministic
19974
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
19975
+ // session and message alone, like the sibling local-store ids. The deterministic
19865
19976
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19866
19977
  // AND converge a streaming partial/final split across two incremental passes:
19867
19978
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20826,6 +20937,42 @@ var SqliteFindingsRepository = class {
20826
20937
  this.db = db;
20827
20938
  }
20828
20939
  db;
20940
+ /**
20941
+ * The newest `limit` findings, newest first.
20942
+ *
20943
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
20944
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
20945
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
20946
+ * not on the driving table: SQLite sorts every finding in the store through a
20947
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
20948
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
20949
+ * rather than a constant.
20950
+ *
20951
+ * What it takes to make `started_at` order come out of an index instead:
20952
+ *
20953
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
20954
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
20955
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
20956
+ * satisfying a global `started_at` order across them needs a range merge
20957
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
20958
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
20959
+ * filters the type per row, which lets the LIMIT stop the scan early.
20960
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
20961
+ * purely to stop the tables being reordered. With plain JOINs the planner
20962
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
20963
+ * unary plus ALONE recovers almost none of the win. Both are needed.
20964
+ *
20965
+ * Neither is a micro-optimisation that a later reader should tidy away, and
20966
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
20967
+ * the temp B-tree comes back.
20968
+ *
20969
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
20970
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
20971
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
20972
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
20973
+ * a store whose recent captures carry no findings at all, where the scan walks
20974
+ * the whole index; that is still no worse than the full sort it replaced.
20975
+ */
20829
20976
  recentFindings(opts) {
20830
20977
  const limit = opts?.limit ?? 50;
20831
20978
  const rows = allRows(
@@ -20834,10 +20981,10 @@ var SqliteFindingsRepository = class {
20834
20981
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20835
20982
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20836
20983
  e.event_type AS kind
20837
- FROM inspection_findings f
20838
- JOIN audit_events e ON e.id = f.audit_event_id
20839
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20840
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20984
+ FROM audit_events e
20985
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
20986
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20987
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20841
20988
  ORDER BY e.started_at DESC, f.rowid DESC
20842
20989
  LIMIT :limit`
20843
20990
  ),
@@ -21481,7 +21628,8 @@ var SqliteInspectionDefinitionsRepository = class {
21481
21628
  }
21482
21629
  db;
21483
21630
  insertStmt;
21484
- // Idempotent upsert; returns the content-addressed definition id.
21631
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21632
+ // present keeps the stored row untouched — see the class doc.
21485
21633
  upsert(input) {
21486
21634
  const id = inspectionDefinitionId(input.ruleId, input.version);
21487
21635
  const row = toInspectionDefinitionRow(input, id);
@@ -21625,11 +21773,23 @@ function isParseableBinaryVersion(version2) {
21625
21773
 
21626
21774
  // ../../packages/persistence/src/repositories/installed-packs.ts
21627
21775
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21776
+ function printableRuleId(entry) {
21777
+ if (typeof entry !== "object" || entry === null) return null;
21778
+ const candidate = entry.id;
21779
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21780
+ }
21781
+ function firstIssueReason(error51) {
21782
+ const issue2 = error51.issues[0];
21783
+ if (!issue2) return "unknown";
21784
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21785
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21786
+ }
21787
+ var REJECTED_RULE_DETAIL_CAP = 10;
21628
21788
  function inventorySignature(packs) {
21629
21789
  return packs.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21630
21790
  }
21631
21791
  function hashRules(rulesJson) {
21632
- return createHash2("sha1").update(rulesJson).digest("hex");
21792
+ return createHash2("sha256").update(rulesJson).digest("hex");
21633
21793
  }
21634
21794
  function parseVersion(v) {
21635
21795
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21841,10 +22001,21 @@ var SqliteInstalledPacksRepository = class {
21841
22001
  * (all detection off) instead of falling back to the bundled packs. Every
21842
22002
  * JSON-level failure therefore counts as invalid.
21843
22003
  */
22004
+ /**
22005
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
22006
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
22007
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
22008
+ * winner is whatever order SQLite happens to return, which makes a collision
22009
+ * resolve differently on two machines holding identical stores. Ordering by
22010
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
22011
+ */
21844
22012
  installedRuleset() {
21845
22013
  const rows = allRows(
21846
22014
  this.db.prepare(
21847
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
22015
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
22016
+ namespace, pack_id AS packId
22017
+ FROM installed_packs
22018
+ ORDER BY namespace, pack_id`
21848
22019
  )
21849
22020
  );
21850
22021
  const out = {
@@ -21852,22 +22023,32 @@ var SqliteInstalledPacksRepository = class {
21852
22023
  enabledPacks: 0,
21853
22024
  rules: [],
21854
22025
  invalidRules: 0,
22026
+ rejectedRules: [],
21855
22027
  ruleActions: /* @__PURE__ */ new Map(),
21856
- ruleVersions: /* @__PURE__ */ new Map()
22028
+ ruleVersions: /* @__PURE__ */ new Map(),
22029
+ reversibleRules: /* @__PURE__ */ new Set()
22030
+ };
22031
+ const reject = (pack, ruleId, reason) => {
22032
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22033
+ out.rejectedRules.push({ pack, ruleId, reason });
21857
22034
  };
21858
22035
  for (const row of rows) {
21859
22036
  if (!intToBool(row.enabled)) continue;
21860
22037
  out.enabledPacks += 1;
21861
22038
  const action = policyIdToAction(row.policyId);
22039
+ const reversible = policyIdIsReversible(row.policyId);
22040
+ const pack = `${row.namespace}/${row.packId}`;
21862
22041
  let raw;
21863
22042
  try {
21864
22043
  raw = JSON.parse(row.rulesJson);
21865
22044
  } catch {
21866
22045
  out.invalidRules += 1;
22046
+ reject(pack, null, "rules_json: malformed JSON");
21867
22047
  continue;
21868
22048
  }
21869
22049
  if (!Array.isArray(raw)) {
21870
22050
  out.invalidRules += 1;
22051
+ reject(pack, null, "rules_json: not an array");
21871
22052
  continue;
21872
22053
  }
21873
22054
  for (const entry of raw) {
@@ -21876,7 +22057,12 @@ var SqliteInstalledPacksRepository = class {
21876
22057
  out.rules.push(parsed.data);
21877
22058
  out.ruleActions.set(parsed.data.id, action);
21878
22059
  out.ruleVersions.set(parsed.data.id, row.version);
21879
- } else out.invalidRules += 1;
22060
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22061
+ else out.reversibleRules.delete(parsed.data.id);
22062
+ } else {
22063
+ out.invalidRules += 1;
22064
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22065
+ }
21880
22066
  }
21881
22067
  }
21882
22068
  return out;
@@ -22070,31 +22256,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22070
22256
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22071
22257
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22072
22258
  var HARNESS_LABELS = {
22073
- claudecode: "Claude Code",
22074
- cursor: "Cursor",
22075
- codex: "Codex",
22076
- antigravity: "Antigravity"
22259
+ [HARNESS.ClaudeCode]: "Claude Code",
22260
+ [HARNESS.Cursor]: "Cursor",
22261
+ [HARNESS.Codex]: "Codex",
22262
+ [HARNESS.Antigravity]: "Antigravity"
22077
22263
  };
22078
22264
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22079
22265
  var EMPTY_PROJECT_AGG = {
22080
22266
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22081
22267
  findingsCount: 0
22082
22268
  };
22269
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22270
+ var TITLE_NEEDLES = {
22271
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22272
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22273
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22274
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22275
+ };
22083
22276
  function resolveHarnessId(attrs, row) {
22084
22277
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22085
22278
  return attrs.provider;
22086
22279
  }
22087
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22088
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22089
- if (t.includes("cursor")) return "cursor";
22090
- if (t.includes("codex")) return "codex";
22091
- if (t.includes("antigravity")) return "antigravity";
22280
+ const t = stripSeparators(row.title ?? "");
22281
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22282
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22283
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22284
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22092
22285
  return null;
22093
22286
  }
22094
22287
  function isLiveRealClaudeCode(rows) {
22095
22288
  return rows.some((r) => {
22096
22289
  const attrs = safeJson(r.attributes, {});
22097
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22290
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22098
22291
  });
22099
22292
  }
22100
22293
  function toAssetSummary(row) {
@@ -22362,7 +22555,7 @@ var SqliteInventoryAssetsRepository = class {
22362
22555
  const isRealHarness = rows.some(
22363
22556
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22364
22557
  );
22365
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22558
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22366
22559
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22367
22560
  if (q && assets.length === 0) continue;
22368
22561
  const firstRow = rows[0];
@@ -23036,9 +23229,10 @@ var SqliteProjectFilesRepository = class {
23036
23229
  // ../../packages/persistence/src/repositories/resolutions.ts
23037
23230
  import { randomUUID as randomUUID7 } from "crypto";
23038
23231
  var SqliteResolutionsRepository = class {
23039
- constructor(db, now = () => Date.now()) {
23232
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23040
23233
  this.db = db;
23041
23234
  this.now = now;
23235
+ this.newId = newId;
23042
23236
  this.insertStmt = db.prepare(
23043
23237
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23044
23238
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23071,12 +23265,14 @@ var SqliteResolutionsRepository = class {
23071
23265
  }
23072
23266
  db;
23073
23267
  now;
23268
+ newId;
23074
23269
  insertStmt;
23075
23270
  latestStmt;
23076
23271
  openAtRestStmt;
23077
23272
  resolvedAtRestStmt;
23078
23273
  /**
23079
- * Insert one disposition row. The repo mints the id and stamps created_at.
23274
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23275
+ * both through the constructor's injectable seams.
23080
23276
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23081
23277
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23082
23278
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23088,7 +23284,7 @@ var SqliteResolutionsRepository = class {
23088
23284
  */
23089
23285
  insertResolution(r) {
23090
23286
  this.insertStmt.run({
23091
- id: randomUUID7(),
23287
+ id: this.newId(),
23092
23288
  findingKey: r.findingKey,
23093
23289
  status: FindingStatus.parse(r.status),
23094
23290
  method: ResolutionMethod.parse(r.method),
@@ -23667,16 +23863,16 @@ var ACTION_TO_KIND = {
23667
23863
  warn: "warned"
23668
23864
  };
23669
23865
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23670
- var SCAN_COVERAGE = [
23671
- { provider: "claudecode", coverage: 100, supported: true },
23672
- { provider: "cursor", coverage: 0, supported: false },
23673
- { provider: "codex", coverage: 80, supported: true },
23674
- { provider: "antigravity", coverage: 60, supported: true },
23675
- { provider: "claudeai", coverage: 0, supported: false },
23676
- { provider: "chatgpt", coverage: 0, supported: false },
23677
- { provider: "copilot", coverage: 0, supported: false },
23678
- { provider: "api", coverage: 0, supported: false }
23679
- ];
23866
+ var SCAN_COVERAGE = {
23867
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
23868
+ [HARNESS.Api]: { coverage: 0, supported: false },
23869
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23870
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23871
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23872
+ [HARNESS.Codex]: { coverage: 80, supported: true },
23873
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
23874
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
23875
+ };
23680
23876
  var GRANULARITY = {
23681
23877
  "7d": "day",
23682
23878
  "30d": "day",
@@ -23775,9 +23971,22 @@ var SqliteSecurityRepository = class {
23775
23971
  return Promise.resolve({ total, needsRemediation, bySeverity });
23776
23972
  }
23777
23973
  // Range is echoed but does not change the result today — coverage is a constant
23778
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
23974
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
23975
+ // comes from Provider.options (the enum's declaration order), not from
23976
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
23977
+ // leave key order unspecified (ES2015 guarantees insertion order for these
23978
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
23979
+ // reliable too). The reason is the schema comment's promise: the returned
23980
+ // order must mirror the generated OpenAPI enum list, which is Provider's
23981
+ // contract, not this table's.
23779
23982
  scanCoverage(range) {
23780
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
23983
+ return Promise.resolve({
23984
+ range,
23985
+ providers: Provider.options.map((provider) => ({
23986
+ provider,
23987
+ ...SCAN_COVERAGE[provider]
23988
+ }))
23989
+ });
23781
23990
  }
23782
23991
  enforcementActions(range) {
23783
23992
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23835,10 +24044,10 @@ var SqliteSecurityRepository = class {
23835
24044
  // count; a superseding open/redetected row means the finding is not
23836
24045
  // remediated and is excluded, same invariant as severitySummary. Legacy
23837
24046
  // at-rest findings with finding_key IS NULL can never have a resolution row
23838
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23839
- // outright. One raw-row query (fetch every trackable finding + its latest
23840
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23841
- // mirroring this file's other methods.
24047
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24048
+ // set below. One raw-row query (fetch the findings with resolution activity in
24049
+ // the window + each one's latest resolution status/method/resolved_at) +
24050
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23842
24051
  mttrTrend(range) {
23843
24052
  const granularity = granularityFor(range);
23844
24053
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23854,29 +24063,80 @@ var SqliteSecurityRepository = class {
23854
24063
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23855
24064
  // COALESCE onto the parent event's started_at defends against any
23856
24065
  // legacy/edge row the backfill left null.
23857
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24066
+ `SELECT DISTINCT f.finding_key AS finding_key,
24067
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23858
24068
  latest.status AS latest_status,
23859
24069
  latest.method AS latest_method,
23860
24070
  latest.resolved_at AS latest_resolved_at
23861
- FROM inspection_findings f
23862
- JOIN audit_events e ON e.id = f.audit_event_id
23863
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24071
+ FROM finding_resolution fr
24072
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24073
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24074
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23864
24075
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23865
24076
  ON latest.finding_key = f.finding_key
23866
- WHERE f.finding_key IS NOT NULL
23867
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23868
- AND EXISTS (
23869
- SELECT 1 FROM finding_resolution fr
23870
- WHERE fr.finding_key = f.finding_key
23871
- AND fr.resolved_at >= :windowStart
23872
- )`
23873
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23874
- // any resolution activity at/after the window start — a row this method
24077
+ WHERE fr.resolved_at >= :windowStart
24078
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24079
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23875
24080
  // ultimately counts has its LATEST resolution inside the window, which
23876
- // implies such a row exists, so nothing wanted is dropped. The exact
23877
- // latest-wins + status/method + window gate stays in JS below,
23878
- // dialect-agnostic. Without this, a
23879
- // 7d request evaluated the store's entire trackable-findings history.
24081
+ // implies a resolution row at/after the window start exists, so nothing
24082
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24083
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24084
+ // implied rather than dropped — the join key comes from
24085
+ // finding_resolution, whose finding_key is NOT NULL.
24086
+ //
24087
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24088
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24089
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24090
+ // event_type, joined every capture event to its findings, and evaluated
24091
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24092
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24093
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24094
+ // both cases returning rows for a window holding a fraction of it.
24095
+ //
24096
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24097
+ // stating precisely, because the obvious reading (both are needed for the
24098
+ // speed) is wrong and was measured to be wrong:
24099
+ //
24100
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24101
+ // keyword is semantically identical to JOIN and exists only to stop the
24102
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24103
+ // the outside, because with no ANALYZE statistics it prices
24104
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24105
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24106
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24107
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24108
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24109
+ // range had none. It buys NO flatness in store size: remove it and the
24110
+ // ratio above does not move, because the latest-resolution derived
24111
+ // table already passes over the whole of finding_resolution, so this
24112
+ // read is O(resolutions) either way and resolutions are not the store.
24113
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24114
+ // — no hot read may pass over a table with no index — and that is the
24115
+ // guard that goes red when it is dropped. Neither test catches the
24116
+ // other's defect.
24117
+ //
24118
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24119
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24120
+ // redetected and fixed again carries several rows inside one window and
24121
+ // matches once per row — and the value below is a MEAN, so a key matched
24122
+ // three times is a key weighted three times.
24123
+ //
24124
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24125
+ // recording. Duplicate rows for ONE key are identical (every projected
24126
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24127
+ // preserved), so sums and counts scale together and that key's own mean
24128
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24129
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24130
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24131
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24132
+ // it has inside the window" — which needed a fixture built for it, since no
24133
+ // single-key case can show it.
24134
+ //
24135
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24136
+ // by value tuple. On the other columns alone, two genuinely different
24137
+ // findings sharing a severity, a first-detection event and a resolution
24138
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24139
+ // collapsing them would under-count in the other direction.
23880
24140
  ),
23881
24141
  { windowStart }
23882
24142
  );
@@ -23934,16 +24194,47 @@ var SqliteSecurityRepository = class {
23934
24194
  }
23935
24195
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23936
24196
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23937
- // same latest-resolution-wins correlated subquery as severitySummary /
23938
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23939
- // resolution row for a key rather than just its current disposition). A key
23940
- // whose latest row is a superseding 'open'/'redetected' row (the same
23941
- // secret came back) is excluded — it is not currently resolved. Legacy
23942
- // at-rest findings with finding_key IS NULL are excluded outright (the
23943
- // resolution lifecycle can never attach to them). Path comes from the
23944
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23945
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23946
- // DESC, capped at `limit`.
24197
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24198
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24199
+ // a key rather than just its current disposition). A key whose latest row is
24200
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24201
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24202
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24203
+ // never attach to them). Path comes from the finding's parent event
24204
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24205
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24206
+ //
24207
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24208
+ // the plan rather than a preference. Written the other way round — driving
24209
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24210
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24211
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24212
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24213
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24214
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24215
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24216
+ // because an empty finding_resolution table makes the inner side empty and the
24217
+ // cross product collapses to nothing — so the shape is only observable on a
24218
+ // corpus that seeds resolutions.
24219
+ //
24220
+ // Driving from `latest` instead makes every step below it a unique-index or
24221
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24222
+ // so the cost is the derived table's own — linear in resolutions, which is
24223
+ // what this feed is legitimately about.
24224
+ //
24225
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24226
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24227
+ // exists only to stop the optimizer reordering the tables. Written as plain
24228
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24229
+ // ANALYZE statistics to price the alternatives with, so it takes
24230
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24231
+ // cross product. The FROM order alone was measured to change the plan not at
24232
+ // all.
24233
+ //
24234
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24235
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24236
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24237
+ // reading as though the findings side could drive.
23947
24238
  recentlyResolved(limit = 20) {
23948
24239
  const rows = allRows(
23949
24240
  this.db.prepare(
@@ -23953,17 +24244,15 @@ var SqliteSecurityRepository = class {
23953
24244
  json_extract(e.attributes, '$.file_path') AS path,
23954
24245
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
23955
24246
  latest.resolved_at AS latest_resolved_at
23956
- FROM inspection_findings f
23957
- JOIN audit_events e ON e.id = f.audit_event_id
23958
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23959
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23960
- ON latest.finding_key = f.finding_key
23961
- WHERE e.event_type = 'code_change'
23962
- AND f.finding_key IS NOT NULL
23963
- AND latest.status = 'resolved'
24247
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24248
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24249
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24250
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24251
+ WHERE latest.status = 'resolved'
23964
24252
  AND latest.method = 'fixed-at-source'
23965
24253
  AND latest.resolved_at IS NOT NULL
23966
- ORDER BY latest_resolved_at DESC
24254
+ AND e.event_type = 'code_change'
24255
+ ORDER BY latest.resolved_at DESC
23967
24256
  LIMIT :limit`
23968
24257
  ),
23969
24258
  { limit }
@@ -24900,6 +25189,7 @@ function openAndInitialize(file2) {
24900
25189
  function openLocalDatabase(dir) {
24901
25190
  ensureDataDirSync(dir);
24902
25191
  const file2 = join2(dir, DB_FILENAME);
25192
+ reapStalePartials(file2);
24903
25193
  const {
24904
25194
  db,
24905
25195
  events,
@@ -25387,11 +25677,91 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25387
25677
  }
25388
25678
  }
25389
25679
 
25390
- // ../../packages/persistence/src/settings.ts
25680
+ // ../../packages/persistence/src/managed-settings.ts
25391
25681
  import { readFileSync as readFileSync3 } from "fs";
25682
+ import { posix, win32 } from "path";
25683
+ function managedSettingsPaths(platform2 = process.platform) {
25684
+ if (platform2 === "darwin") {
25685
+ return [
25686
+ posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
25687
+ posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
25688
+ ];
25689
+ }
25690
+ if (platform2 === "win32") {
25691
+ return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
25692
+ }
25693
+ return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
25694
+ }
25695
+ function readManagedSettings(paths = managedSettingsPaths()) {
25696
+ for (const path of paths) {
25697
+ let text;
25698
+ try {
25699
+ text = readFileSync3(path, "utf8");
25700
+ } catch {
25701
+ continue;
25702
+ }
25703
+ const record2 = parseJsonObject(text);
25704
+ if (!record2) continue;
25705
+ const parsed = ManagedSettings.safeParse(record2);
25706
+ if (parsed.success) return parsed.data;
25707
+ }
25708
+ return null;
25709
+ }
25710
+ function managedContextOf(managed) {
25711
+ if (!managed) return NO_MANAGED_CONTEXT;
25712
+ return {
25713
+ present: true,
25714
+ ...managed.organization === void 0 ? {} : { organization: managed.organization },
25715
+ lockedFields: managed.lockedFields
25716
+ };
25717
+ }
25718
+ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25719
+ if (!managed) return settings;
25720
+ const { values } = managed;
25721
+ const merged = { ...settings };
25722
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25723
+ if (values.controlPlane !== void 0) {
25724
+ merged.controlPlane = {
25725
+ ...values.controlPlane,
25726
+ // The administrator pinned WHICH deployment, not WHEN this machine
25727
+ // joined it. Keep the user's own attach time when the endpoint is
25728
+ // unchanged, so a managed machine does not appear to re-attach on every
25729
+ // read; stamp a fresh one when the administrator moved it.
25730
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25731
+ };
25732
+ }
25733
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25734
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25735
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25736
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25737
+ if (values.vaultConsent !== void 0) {
25738
+ merged.vaultConsent = values.vaultConsent ? (
25739
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25740
+ // at the current version otherwise.
25741
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25742
+ ) : void 0;
25743
+ }
25744
+ if (values.modelJudgeConsent !== void 0) {
25745
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25746
+ acknowledgedAt: now().toISOString(),
25747
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25748
+ } : void 0;
25749
+ }
25750
+ return merged;
25751
+ }
25752
+ function lockedAmong(context, requested) {
25753
+ if (!context.present) return [];
25754
+ return requested.filter((key) => context.lockedFields.includes(key));
25755
+ }
25756
+
25757
+ // ../../packages/persistence/src/settings.ts
25758
+ import { readFileSync as readFileSync4 } from "fs";
25392
25759
  import { join as join5 } from "path";
25393
25760
  var SETTINGS_FILENAME = "settings.json";
25394
25761
  function readWorkspaceSettings(base = defaultDataDir()) {
25762
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25763
+ }
25764
+ function readUserSettings(base) {
25395
25765
  const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25396
25766
  if (!record2) return defaultWorkspaceSettings();
25397
25767
  try {
@@ -25400,16 +25770,81 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25400
25770
  return defaultWorkspaceSettings();
25401
25771
  }
25402
25772
  }
25403
- function applyOnboarding(answers2, base = defaultDataDir()) {
25773
+ var ManagedFieldError = class extends Error {
25774
+ fields;
25775
+ constructor(fields) {
25776
+ super(`refusing to write administratively locked settings: ${fields.join(", ")}`);
25777
+ this.name = "ManagedFieldError";
25778
+ this.fields = fields;
25779
+ }
25780
+ };
25781
+ function lockableKeysTouched(current, applied) {
25782
+ const keys = [];
25783
+ const changed = (key) => key in applied && applied[key] !== current[key];
25784
+ const descriptorChanged = "controlPlane" in applied && (applied.controlPlane?.endpoint !== current.controlPlane?.endpoint || applied.controlPlane?.label !== current.controlPlane?.label);
25785
+ if (changed("runMode") || descriptorChanged) keys.push("runMode");
25786
+ if (changed("historicalAccess")) keys.push("historicalAccess");
25787
+ if (changed("vaultKeyCustody")) keys.push("vaultKeyCustody");
25788
+ if (changed("vaultInlineReveal")) keys.push("vaultInlineReveal");
25789
+ if (changed("dataSharesInPlace")) keys.push("dataSharesInPlace");
25790
+ if ("vaultConsent" in applied && isVaultConsentValid(applied.vaultConsent) !== isVaultConsentValid(current.vaultConsent)) {
25791
+ keys.push("vaultConsent");
25792
+ }
25793
+ if ("modelJudgeConsent" in applied && isModelJudgeConsentValid(applied.modelJudgeConsent) !== isModelJudgeConsentValid(current.modelJudgeConsent)) {
25794
+ keys.push("modelJudgeConsent");
25795
+ }
25796
+ return keys;
25797
+ }
25798
+ function pinnedKeys(managed) {
25799
+ if (!managed) return [];
25800
+ const { values } = managed;
25801
+ const keys = [];
25802
+ if (values.runMode !== void 0 || values.controlPlane !== void 0) keys.push("runMode");
25803
+ if (values.historicalAccess !== void 0) keys.push("historicalAccess");
25804
+ if (values.vaultConsent !== void 0) keys.push("vaultConsent");
25805
+ if (values.vaultKeyCustody !== void 0) keys.push("vaultKeyCustody");
25806
+ if (values.vaultInlineReveal !== void 0) keys.push("vaultInlineReveal");
25807
+ if (values.modelJudgeConsent !== void 0) keys.push("modelJudgeConsent");
25808
+ if (values.dataSharesInPlace !== void 0) keys.push("dataSharesInPlace");
25809
+ return keys;
25810
+ }
25811
+ function withoutManagedKeys(applied, managed, pinned, touched) {
25812
+ if (!managed.present) return applied;
25813
+ const strip = (key) => (managed.lockedFields.includes(key) || pinned.includes(key)) && !touched.includes(key);
25814
+ const out = { ...applied };
25815
+ if (strip("runMode")) {
25816
+ delete out.runMode;
25817
+ delete out.controlPlane;
25818
+ }
25819
+ if (strip("historicalAccess")) delete out.historicalAccess;
25820
+ if (strip("vaultConsent")) delete out.vaultConsent;
25821
+ if (strip("vaultKeyCustody")) delete out.vaultKeyCustody;
25822
+ if (strip("vaultInlineReveal")) delete out.vaultInlineReveal;
25823
+ if (strip("modelJudgeConsent")) delete out.modelJudgeConsent;
25824
+ if (strip("dataSharesInPlace")) delete out.dataSharesInPlace;
25825
+ return out;
25826
+ }
25827
+ function applyOnboarding(answers2, base = defaultDataDir(), managedOverride) {
25404
25828
  const dir = settingsDir(base);
25405
25829
  ensureDataDirSync(dir);
25406
25830
  const file2 = join5(dir, SETTINGS_FILENAME);
25831
+ const managedSettings = managedOverride === void 0 ? readManagedSettings() : managedOverride;
25832
+ const managed = managedContextOf(managedSettings);
25407
25833
  return withFileLock(file2, () => {
25408
- const current = readWorkspaceSettings(base);
25834
+ const current = readUserSettings(base);
25409
25835
  const applied = typeof answers2 === "function" ? answers2(current) : answers2;
25836
+ const effective = overlayManagedSettings(current, managedSettings);
25837
+ const touched = lockableKeysTouched(effective, applied);
25838
+ const refused = lockedAmong(managed, touched);
25839
+ if (refused.length > 0) throw new ManagedFieldError(refused);
25410
25840
  const merged = WorkspaceSettings.parse({
25411
25841
  ...current,
25412
- ...applied,
25842
+ // Locked keys are stripped rather than merged. Everything still here is,
25843
+ // by the refusal above, an unchanged ECHO of the administrator's value —
25844
+ // so dropping it discards no answer of the user's, and writing it would
25845
+ // persist the pin into their file, where it would outlive the managed
25846
+ // file and read as their own choice once the lock was gone.
25847
+ ...withoutManagedKeys(applied, managed, pinnedKeys(managedSettings), touched),
25413
25848
  // First setup stamps the time; later edits keep the original completion mark.
25414
25849
  onboardedAt: applied.onboardedAt ?? current.onboardedAt ?? (/* @__PURE__ */ new Date()).toISOString()
25415
25850
  });
@@ -25421,7 +25856,7 @@ function applyOnboarding(answers2, base = defaultDataDir()) {
25421
25856
  function readJson(file2) {
25422
25857
  let text;
25423
25858
  try {
25424
- text = readFileSync3(file2, "utf8");
25859
+ text = readFileSync4(file2, "utf8");
25425
25860
  } catch {
25426
25861
  return null;
25427
25862
  }
@@ -25440,15 +25875,7 @@ import {
25440
25875
  // ../../packages/persistence/src/vault/key-provider.ts
25441
25876
  import { execFileSync } from "child_process";
25442
25877
  import { randomBytes as randomBytes2 } from "crypto";
25443
- import {
25444
- chmodSync as chmodSync2,
25445
- mkdirSync as mkdirSync2,
25446
- readFileSync as readFileSync4,
25447
- renameSync as renameSync4,
25448
- rmSync as rmSync4,
25449
- statSync as statSync3,
25450
- writeFileSync as writeFileSync3
25451
- } from "fs";
25878
+ import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
25452
25879
  import { join as join6 } from "path";
25453
25880
 
25454
25881
  // ../../packages/persistence/src/vault/vault.ts
@@ -25548,7 +25975,7 @@ function resolveProviderSafe(resolveProviderFn) {
25548
25975
  }
25549
25976
 
25550
25977
  // ../../packages/plugin-sdk/src/config-inventory.ts
25551
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
25978
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
25552
25979
  import { homedir as homedir2 } from "os";
25553
25980
  import { basename as basename3, join as join10 } from "path";
25554
25981
 
@@ -26280,6 +26707,7 @@ var CONFIG_POSTURE_RULES = [
26280
26707
  ];
26281
26708
 
26282
26709
  // ../../packages/detections/src/security/redos-probe.ts
26710
+ var BUDGET_MS = 100;
26283
26711
  var EXPONENTIAL_UNITS = [
26284
26712
  "a",
26285
26713
  "0",
@@ -26303,9 +26731,11 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
26303
26731
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
26304
26732
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
26305
26733
  );
26734
+ var CPU_CORROBORATION_SHARE = 0.2;
26735
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
26306
26736
 
26307
26737
  // ../../packages/plugin-sdk/src/repo.ts
26308
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
26738
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
26309
26739
  import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
26310
26740
 
26311
26741
  // ../../packages/plugin-sdk/src/events.ts
@@ -26316,12 +26746,17 @@ import { existsSync as existsSync7 } from "fs";
26316
26746
  import { fileURLToPath } from "url";
26317
26747
  import { Worker } from "worker_threads";
26318
26748
 
26749
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
26750
+ var import_ignore = __toESM(require_ignore(), 1);
26751
+ import { readFileSync as readFileSync8 } from "fs";
26752
+ import { join as join11 } from "path";
26753
+
26319
26754
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
26320
26755
  import { arch, hostname as hostname4, platform, release as release2 } from "os";
26321
26756
 
26322
26757
  // ../../packages/plugin-sdk/src/nudge.ts
26323
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
26324
- import { join as join11 } from "path";
26758
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
26759
+ import { join as join12 } from "path";
26325
26760
 
26326
26761
  // ../../packages/plugin-sdk/src/paths.ts
26327
26762
  import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
@@ -26338,9 +26773,8 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
26338
26773
  }
26339
26774
 
26340
26775
  // ../../packages/plugin-sdk/src/project-files.ts
26341
- var import_ignore = __toESM(require_ignore(), 1);
26342
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
26343
- import { basename as basename5, join as join12 } from "path";
26776
+ import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
26777
+ import { basename as basename5, join as join13 } from "path";
26344
26778
 
26345
26779
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
26346
26780
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -26375,8 +26809,8 @@ import { randomUUID as randomUUID14 } from "crypto";
26375
26809
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
26376
26810
 
26377
26811
  // ../../packages/plugin-sdk/src/throttle.ts
26378
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
26379
- import { join as join13 } from "path";
26812
+ import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
26813
+ import { join as join14 } from "path";
26380
26814
 
26381
26815
  // ../../packages/setup-wizard/src/onboard-posture.ts
26382
26816
  function parsePosture(json2) {
@@ -26386,10 +26820,10 @@ function parsePosture(json2) {
26386
26820
  for (const [key, value] of Object.entries(raw)) {
26387
26821
  const cat = DetectionCategory.safeParse(key);
26388
26822
  if (!cat.success) throw new Error(`unknown category "${key}"`);
26389
- const act = BuiltinPolicyId.safeParse(value);
26823
+ const act = CategoryPolicyId.safeParse(value);
26390
26824
  if (!act.success)
26391
26825
  throw new Error(
26392
- `invalid action "${String(value)}" for ${key} (expected monitor/warn/redact/block)`
26826
+ `invalid action "${String(value)}" for ${key} (expected ${CATEGORY_EXPRESSIBLE_IDS.join("/")})`
26393
26827
  );
26394
26828
  out[cat.data] = act.data;
26395
26829
  }
@@ -26399,12 +26833,17 @@ function parsePosture(json2) {
26399
26833
 
26400
26834
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
26401
26835
  import { writeFileSync as writeFileSync7 } from "fs";
26402
- import { join as join14 } from "path";
26836
+ import { join as join15 } from "path";
26837
+
26838
+ // ../../packages/setup-wizard/src/triage/merge.ts
26839
+ var RANK = Object.fromEntries(
26840
+ KNOWN_BUILTIN_IDS.map((id, i) => [id, i])
26841
+ );
26403
26842
 
26404
26843
  // ../../packages/setup-wizard/src/triage/plan-file.ts
26405
- import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
26844
+ import { mkdtempSync, readFileSync as readFileSync10, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
26406
26845
  import { tmpdir } from "os";
26407
- import { basename as basename6, dirname as dirname4, join as join15 } from "path";
26846
+ import { basename as basename6, dirname as dirname4, join as join16 } from "path";
26408
26847
  var SuppressionEntrySchema = external_exports.object({
26409
26848
  ruleId: external_exports.string(),
26410
26849
  category: DetectionCategory,