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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -585,6 +585,10 @@ var SQLITE_MIGRATIONS = [
585
585
  {
586
586
  tag: "0020_secret_vault_pagination_indexes",
587
587
  sql: "CREATE INDEX `idx_secret_vault_last_seen` ON `secret_vault` (`last_seen`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_reuse` ON `secret_vault` (`occurrence_count`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_at` ON `secret_vault_deref` (`at`,`id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_signal` ON `secret_vault_deref` (`at`,`id`) WHERE `reason` NOT IN ('display', 'view-render');"
588
+ },
589
+ {
590
+ tag: "0021_finding_resolution_resolved_at_index",
591
+ sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
588
592
  }
589
593
  ];
590
594
 
@@ -15300,6 +15304,47 @@ function date4(params) {
15300
15304
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15301
15305
  config(en_default());
15302
15306
 
15307
+ // ../../packages/schema/src/zod/harness-map.ts
15308
+ var HARNESS = {
15309
+ ClaudeCode: "claudecode",
15310
+ Cursor: "cursor",
15311
+ Copilot: "copilot",
15312
+ Codex: "codex",
15313
+ Antigravity: "antigravity",
15314
+ Windsurf: "windsurf",
15315
+ ClaudeDesktop: "claudedesktop",
15316
+ ChatGpt: "chatgpt",
15317
+ ClaudeAi: "claudeai",
15318
+ Api: "api"
15319
+ };
15320
+ var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
15321
+ var SOURCE_TOOL = {
15322
+ ClaudeCode: "claude-code",
15323
+ ClaudeDesktop: "claude-desktop",
15324
+ Cursor: "cursor",
15325
+ ChatGpt: "chatgpt",
15326
+ ClaudeAi: "claude-ai",
15327
+ Copilot: "github-copilot",
15328
+ Codex: "codex",
15329
+ Antigravity: "antigravity",
15330
+ // No harness counterpart, deliberately: the CLI's own captures and a capture
15331
+ // whose tool could not be identified both render through the read side's
15332
+ // miss path rather than as a harness of their own.
15333
+ Cli: "cli",
15334
+ Unknown: "unknown"
15335
+ };
15336
+ var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
15337
+ var TOOL_TO_HARNESS = {
15338
+ [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
15339
+ [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
15340
+ [SOURCE_TOOL.Copilot]: HARNESS.Copilot,
15341
+ [SOURCE_TOOL.Cursor]: HARNESS.Cursor,
15342
+ [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
15343
+ [SOURCE_TOOL.Codex]: HARNESS.Codex,
15344
+ [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
15345
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
15346
+ };
15347
+
15303
15348
  // ../../packages/schema/src/zod/finding.ts
15304
15349
  var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
15305
15350
  var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
@@ -15322,21 +15367,22 @@ var Finding = external_exports.object({
15322
15367
  }).meta({ id: "Finding" });
15323
15368
  var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
15324
15369
  var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
15325
- var FindingProvider = external_exports.enum([
15326
- "claudecode",
15327
- "claudedesktop",
15328
- "cursor",
15329
- "copilot",
15330
- "chatgpt",
15331
- "claudeai",
15332
- "codex",
15333
- "antigravity",
15334
- "api"
15370
+ var FindingProvider = Harness.extract([
15371
+ "ClaudeCode",
15372
+ "ClaudeDesktop",
15373
+ "Cursor",
15374
+ "Copilot",
15375
+ "ChatGpt",
15376
+ "ClaudeAi",
15377
+ "Codex",
15378
+ "Antigravity",
15379
+ "Api"
15335
15380
  ]).meta({ id: "FindingProvider" });
15336
15381
  var FindingCategory = external_exports.enum([
15337
15382
  "secret",
15338
15383
  "pii",
15339
15384
  "source_code",
15385
+ "code_flaw",
15340
15386
  "external_share",
15341
15387
  "mcp_server",
15342
15388
  "customer_data",
@@ -15516,6 +15562,7 @@ var FindingInstanceDetail = FindingInstance.extend({
15516
15562
  policy: FindingPolicyRef
15517
15563
  }).meta({ id: "FindingInstanceDetail" });
15518
15564
  var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
15565
+ var MAX_FLAT_FINDINGS_LIMIT = 200;
15519
15566
  var ListFindingInstancesQuery = external_exports.object({
15520
15567
  severity: external_exports.array(Severity).optional(),
15521
15568
  // Rule ids, the same vocabulary the grouped list's `subtype` carries.
@@ -15535,7 +15582,7 @@ var ListFindingInstancesQuery = external_exports.object({
15535
15582
  q: external_exports.string().optional(),
15536
15583
  sessionId: external_exports.string().optional(),
15537
15584
  from: external_exports.iso.datetime().optional(),
15538
- limit: external_exports.coerce.number().int().min(1).max(200).optional(),
15585
+ limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
15539
15586
  cursor: external_exports.string().optional()
15540
15587
  });
15541
15588
  var ListFindingInstancesResponse = external_exports.object({
@@ -15597,30 +15644,6 @@ var ListFindingLocationsResponse = external_exports.object({
15597
15644
  hasMore: external_exports.boolean()
15598
15645
  }).meta({ id: "ListFindingLocationsResponse" });
15599
15646
 
15600
- // ../../packages/schema/src/zod/harness-map.ts
15601
- var Harness = external_exports.enum([
15602
- "claudecode",
15603
- "cursor",
15604
- "copilot",
15605
- "codex",
15606
- "antigravity",
15607
- "windsurf",
15608
- "claudedesktop",
15609
- "chatgpt",
15610
- "claudeai",
15611
- "api"
15612
- ]).meta({ id: "Harness" });
15613
- var TOOL_TO_HARNESS = {
15614
- "claude-code": "claudecode",
15615
- "claude-desktop": "claudedesktop",
15616
- "github-copilot": "copilot",
15617
- cursor: "cursor",
15618
- chatgpt: "chatgpt",
15619
- codex: "codex",
15620
- antigravity: "antigravity",
15621
- "claude-ai": "claudeai"
15622
- };
15623
-
15624
15647
  // ../../packages/schema/src/zod/meta.ts
15625
15648
  var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
15626
15649
  var AuditEventType = external_exports.enum([
@@ -16073,686 +16096,37 @@ var ActivityOverviewResponse = external_exports.object({
16073
16096
  sessions: ListActivitySessionsResponse
16074
16097
  }).meta({ id: "ActivityOverviewResponse" });
16075
16098
 
16076
- // ../../packages/schema/src/zod/event.ts
16077
- var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
16078
- var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
16079
- var SourceTool = external_exports.enum([
16080
- "claude-code",
16081
- "claude-desktop",
16082
- "cursor",
16083
- "chatgpt",
16084
- "claude-ai",
16085
- "github-copilot",
16086
- "codex",
16087
- "antigravity",
16088
- "cli",
16089
- "unknown"
16090
- ]).meta({ id: "SourceTool" });
16091
- var EventMetadata = external_exports.object({
16092
- sessionId: external_exports.string().optional(),
16093
- repo: external_exports.string().optional(),
16094
- filePath: external_exports.string().optional(),
16095
- // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
16096
- // set by the tool-scanning hooks. The tool NAME only — never the tool's
16097
- // arguments or output, which can carry the very value a finding masked
16098
- // (metadata is stored unredacted). Gives findings on non-file captures a
16099
- // display location ("via Bash") when no filePath exists.
16100
- toolName: external_exports.string().optional(),
16101
- // Set (true) by the worktree scanner when the file is excluded by the
16102
- // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
16103
- // generated code can leak real secrets — but the provenance is recorded so
16104
- // policy/dashboards can treat those findings as informational rather than
16105
- // blocking. Omitted (not false) for tracked files and non-scan events.
16106
- gitignored: external_exports.boolean().optional(),
16107
- // Set (true) ONLY when the event's `content` is the COMPLETE file at
16108
- // capture time (a worktree scan reading from disk). Hook-captured edits
16109
- // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
16110
- // this. The resolver-on-ingest keys its fixed-at-source dropout
16111
- // diff on this marker: only a whole-file snapshot can prove a previously
16112
- // open finding is gone; a fragment's absence proves nothing (the secret
16113
- // may live outside the hunk). Omitted (not false) for fragments and
16114
- // non-scan events, so pre-marker clients safely default to the
16115
- // non-authoritative path.
16116
- wholeFile: external_exports.boolean().optional(),
16117
- model: external_exports.string().optional(),
16118
- turnIndex: external_exports.number().int().nonnegative().optional(),
16119
- // Distributed-tracing correlation. `correlationId` ties a recorded event back
16120
- // to the request that captured/ingested it (a UUID, generated independently of
16121
- // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
16122
- // originating span when telemetry is enabled. Both optional + backward
16123
- // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
16124
- correlationId: external_exports.uuid().optional(),
16125
- traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
16126
- // Ids of the detection exceptions that downgraded findings in this capture
16127
- // to 'allow' — the enforcement audit trail's link back to the grant that
16128
- // authorized the bypass. Absent on captures where no exception applied.
16129
- exceptionIds: external_exports.array(external_exports.guid()).optional()
16130
- }).meta({ id: "EventMetadata" });
16131
- var Event = external_exports.object({
16132
- id: external_exports.guid(),
16133
- sourceTool: SourceTool,
16134
- kind: EventKind,
16135
- occurredAt: external_exports.iso.datetime(),
16136
- contentHash: external_exports.string(),
16137
- content: external_exports.string(),
16138
- metadata: EventMetadata.optional()
16139
- }).meta({ id: "Event" });
16140
- var IngestEvent = Event.meta({ id: "IngestEvent" });
16141
- var IngestBatch = external_exports.object({
16142
- events: external_exports.array(IngestEvent).min(1).max(100),
16143
- // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
16144
- // additionally rejects any event whose contentHash the store has already
16145
- // recorded — for re-runnable bulk ingest (worktree scan, transcript
16146
- // backfill), where a re-run mints fresh event ids for identical content and
16147
- // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
16148
- // two genuinely separate prompts can be byte-identical and both belong on
16149
- // the timeline.
16150
- dedupe: external_exports.literal("content-hash").optional()
16151
- }).meta({ id: "IngestBatch" });
16152
-
16153
- // ../../packages/schema/src/zod/inventory.ts
16154
- var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16155
- var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16156
- var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16157
- var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16158
- var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16159
- var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16160
- var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16161
- var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
16162
- var AccessCounts = external_exports.object({
16163
- open: external_exports.number().int().nonnegative(),
16164
- approved: external_exports.number().int().nonnegative(),
16165
- blocked: external_exports.number().int().nonnegative(),
16166
- total: external_exports.number().int().nonnegative()
16167
- }).meta({ id: "AccessCounts" });
16168
- var AssetSummary = external_exports.object({
16169
- id: external_exports.string(),
16170
- type: AssetType,
16171
- name: external_exports.string(),
16172
- sub: external_exports.string(),
16173
- flags: external_exports.array(Flag),
16174
- /** MCP servers only — omitted for all other types. */
16175
- trust: TrustLevel.optional()
16176
- }).meta({ id: "AssetSummary" });
16177
- var ProjectSummary = external_exports.object({
16178
- id: external_exports.string(),
16179
- name: external_exports.string(),
16180
- repo: external_exports.string(),
16181
- visibility: Visibility,
16182
- language: external_exports.string(),
16183
- policyDefault: AccessLevel,
16184
- updatedAt: external_exports.iso.datetime(),
16185
- accessCounts: AccessCounts,
16186
- findingsCount: external_exports.number().int().nonnegative()
16187
- }).meta({ id: "ProjectSummary" });
16188
- var HarnessCategory = external_exports.object({
16189
- /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16190
- type: AssetType,
16191
- assets: external_exports.array(AssetSummary)
16099
+ // ../../packages/schema/src/zod/config-inventory.ts
16100
+ var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16101
+ var SkillScanEntry = external_exports.object({
16102
+ name: external_exports.string().min(1),
16103
+ // The identity source: a marketplace repo for plugin skills (e.g.
16104
+ // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16105
+ // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16106
+ // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16107
+ source: external_exports.string().min(1),
16108
+ scope: ConfigScope,
16109
+ pluginName: external_exports.string().optional(),
16110
+ // Volatile — rides the attribute bag, never the identity hash.
16111
+ version: external_exports.string().optional(),
16112
+ description: external_exports.string().optional(),
16113
+ // Skill directory mtime (ISO) the "updated Nd ago" freshness signal.
16114
+ updatedAt: external_exports.iso.datetime().optional(),
16115
+ // Filesystem path — the promoted inventory `location` column.
16116
+ location: external_exports.string().optional()
16192
16117
  });
16193
- var HarnessSummary = external_exports.object({
16194
- id: HarnessId,
16195
- label: external_exports.string(),
16196
- kind: external_exports.string(),
16197
- version: external_exports.string(),
16198
- sessions: external_exports.number().int().nonnegative(),
16199
- assetCount: external_exports.number().int().nonnegative(),
16200
- flagCount: external_exports.number().int().nonnegative(),
16201
- projects: external_exports.array(ProjectSummary),
16202
- categories: external_exports.array(HarnessCategory)
16203
- }).meta({ id: "HarnessSummary" });
16204
- var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16205
- var AssetGroup = external_exports.object({
16206
- /** Group key — never project (enforced at service layer). */
16207
- type: AssetType,
16208
- total: external_exports.number().int().nonnegative(),
16209
- /**
16210
- * MCP group only — omitted for all other types.
16211
- * Partial: only TrustLevel keys with non-zero counts are included.
16212
- * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16213
- */
16214
- trustRollup: external_exports.object({
16215
- "known-good": external_exports.number().int().nonnegative(),
16216
- risky: external_exports.number().int().nonnegative(),
16217
- unapproved: external_exports.number().int().nonnegative()
16218
- }).partial().strict().optional(),
16219
- /**
16220
- * Partial: only Flag keys with non-zero counts are included.
16221
- * Strict: unknown keys are rejected — only Flag values are valid keys.
16222
- */
16223
- flagRollup: external_exports.object({
16224
- update: external_exports.number().int().nonnegative(),
16225
- stale: external_exports.number().int().nonnegative(),
16226
- conflict: external_exports.number().int().nonnegative(),
16227
- unknown: external_exports.number().int().nonnegative(),
16228
- change: external_exports.number().int().nonnegative(),
16229
- untracked: external_exports.number().int().nonnegative(),
16230
- risk: external_exports.number().int().nonnegative(),
16231
- findings: external_exports.number().int().nonnegative()
16232
- }).partial().strict(),
16233
- items: external_exports.array(AssetSummary)
16234
- }).meta({ id: "AssetGroup" });
16235
- var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16236
- var McpTool = external_exports.object({
16237
- name: external_exports.string(),
16238
- signature: external_exports.string(),
16239
- description: external_exports.string(),
16240
- write: external_exports.boolean(),
16241
- /** Non-null string when tool is dangerous / blocked; null otherwise. */
16242
- risk: external_exports.string().nullable()
16243
- }).meta({ id: "McpTool" });
16244
- var AssetFindingRef = external_exports.object({
16245
- id: external_exports.string(),
16246
- title: external_exports.string(),
16247
- note: external_exports.string()
16248
- });
16249
- var AssetDetail = AssetSummary.extend({
16250
- /** string | null — null when no description is available. */
16251
- description: external_exports.string().nullable(),
16252
- /** trustLevel | null — null for non-MCP assets. */
16253
- trust: TrustLevel.nullable(),
16254
- /** Type-specific raw key/values — FE renders the grid. */
16255
- meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16256
- /** always present — object when there is an active finding, null when absent. */
16257
- finding: AssetFindingRef.nullable(),
16258
- /** MCP exposed-tools list — omitted for non-mcp. */
16259
- tools: external_exports.array(McpTool).optional()
16260
- }).meta({ id: "AssetDetail" });
16261
- var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16262
- var InventoryStats = external_exports.object({
16263
- /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16264
- attention: external_exports.number().int().nonnegative(),
16265
- byType: external_exports.object({
16266
- project: external_exports.number().int().nonnegative(),
16267
- skill: external_exports.number().int().nonnegative(),
16268
- mcp: external_exports.number().int().nonnegative(),
16269
- hook: external_exports.number().int().nonnegative(),
16270
- config: external_exports.number().int().nonnegative()
16271
- }),
16272
- harnesses: external_exports.number().int().nonnegative(),
16273
- mcpTrust: external_exports.object({
16274
- "known-good": external_exports.number().int().nonnegative(),
16275
- risky: external_exports.number().int().nonnegative(),
16276
- unapproved: external_exports.number().int().nonnegative()
16277
- })
16278
- }).meta({ id: "InventoryStats" });
16279
- var FileSummary = external_exports.object({
16280
- path: external_exports.string(),
16281
- name: external_exports.string(),
16282
- origin: Origin,
16283
- /** Effective access (override applied). */
16284
- access: AccessLevel,
16285
- /** True when a file_access_override differs from the computed default. */
16286
- isCustom: external_exports.boolean(),
16287
- findings: external_exports.number().int().nonnegative(),
16288
- /** When the file was auto-blocked by a detection; null when not blocked. */
16289
- blockedAt: external_exports.iso.datetime().nullable().optional(),
16290
- /** Why the file was blocked; null when absent. */
16291
- note: external_exports.string().nullable().optional()
16292
- }).meta({ id: "FileSummary" });
16293
- var FolderSummary = external_exports.object({
16294
- name: external_exports.string(),
16295
- path: external_exports.string(),
16296
- /** Rollup of effective access across all descendants. */
16297
- accessCounts: AccessCounts
16298
- }).meta({ id: "FolderSummary" });
16299
- var ProjectTreeResponse = external_exports.object({
16300
- project: external_exports.object({
16301
- id: external_exports.string(),
16302
- repo: external_exports.string(),
16303
- visibility: Visibility
16304
- }),
16305
- path: external_exports.string(),
16306
- /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16307
- folders: external_exports.array(FolderSummary).optional(),
16308
- files: external_exports.array(FileSummary)
16309
- }).meta({ id: "ProjectTreeResponse" });
16310
- var FileDetail = FileSummary.extend({
16311
- project: external_exports.object({
16312
- repo: external_exports.string(),
16313
- visibility: Visibility,
16314
- language: external_exports.string(),
16315
- policyDefault: AccessLevel,
16316
- updatedAt: external_exports.iso.datetime()
16317
- }),
16318
- findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16319
- }).meta({ id: "FileDetail" });
16320
- var SetFileAccessBody = external_exports.object({
16321
- path: external_exports.string(),
16322
- access: AccessLevel
16323
- }).meta({ id: "SetFileAccessBody" });
16324
- var SetFileAccessResponse = external_exports.object({
16325
- file: FileSummary,
16326
- accessCounts: AccessCounts
16327
- }).meta({ id: "SetFileAccessResponse" });
16328
- var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16329
- var HarnessEventItem = external_exports.object({
16330
- kind: HarnessEventKind,
16331
- title: external_exports.string(),
16332
- detail: external_exports.string(),
16333
- occurredAt: external_exports.iso.datetime(),
16334
- findingId: external_exports.string().nullable().optional()
16335
- }).meta({ id: "HarnessEventItem" });
16336
- var HarnessEventsResponse = external_exports.object({
16337
- counts: external_exports.object({
16338
- block: external_exports.number().int().nonnegative(),
16339
- redact: external_exports.number().int().nonnegative(),
16340
- warn: external_exports.number().int().nonnegative()
16341
- }),
16342
- items: external_exports.array(HarnessEventItem)
16343
- }).meta({ id: "HarnessEventsResponse" });
16344
- var RescanResponse = external_exports.object({
16345
- jobId: external_exports.string(),
16346
- startedAt: external_exports.iso.datetime()
16347
- }).meta({ id: "RescanResponse" });
16348
- var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16349
- var ListAssetsQuery = external_exports.object({
16350
- /** Filter by one or more AssetType values; absent means all types. */
16351
- type: external_exports.array(AssetType).optional(),
16352
- /** Free-text search term. */
16353
- q: external_exports.string().optional()
16354
- });
16355
- var GetProjectTreeQuery = external_exports.object({
16356
- /** Subtree root path; defaults to repository root when absent. */
16357
- path: external_exports.string().optional(),
16358
- /** Free-text filter applied to file paths. */
16359
- q: external_exports.string().optional(),
16360
- /**
16361
- * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16362
- * file across the whole repo (folders omitted, most-recent first), ignoring
16363
- * `path`/`q` — powers the project-wide "recently blocked" strip.
16364
- */
16365
- filter: external_exports.enum(["blocked"]).optional()
16366
- });
16367
- var GetProjectFileQuery = external_exports.object({
16368
- /** Repository-relative file path; absent or empty → 400. */
16369
- path: external_exports.string()
16370
- });
16371
- var GetHarnessEventsQuery = external_exports.object({
16372
- /** Maximum number of events to return. Range: 1–50; default: 7. */
16373
- limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16374
- });
16375
-
16376
- // ../../packages/schema/src/zod/exception.ts
16377
- var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
16378
- var ExceptionConditions = external_exports.object({
16379
- repo: external_exports.string().optional(),
16380
- sourceTool: external_exports.string().optional(),
16381
- provider: external_exports.string().optional()
16382
- }).strict();
16383
- var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
16384
- var DetectionException = external_exports.object({
16385
- id: external_exports.guid(),
16386
- ruleId: external_exports.string(),
16387
- // Denormalized from the rule, for reporting — never matched on.
16388
- category: DetectionCategory,
16389
- // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
16390
- // fingerprint, never the raw value, and never reversible. Matching recomputes
16391
- // the fingerprint from a fresh capture; the value itself is never stored.
16392
- // Shape-constrained so a malformed — or accidentally raw — value is rejected
16393
- // at the boundary rather than persisted.
16394
- valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
16395
- // Version of the fingerprint key the grant was written under; a rotated key
16396
- // invalidates old grants rather than silently mismatching them.
16397
- keyVersion: external_exports.number().int().positive(),
16398
- // maskMatch() preview of the approved value — never the raw value.
16399
- maskedValue: external_exports.string(),
16400
- capability: ExceptionCapability.default("suppress"),
16401
- scope: ExceptionScope,
16402
- expiresAt: external_exports.iso.datetime().nullable(),
16403
- maxUses: external_exports.number().int().positive().nullable(),
16404
- useCount: external_exports.number().int().nonnegative(),
16405
- lastUsedAt: external_exports.iso.datetime().nullable(),
16406
- // Mandatory: every grant carries the human reason it exists.
16407
- justification: external_exports.string().min(1),
16408
- conditions: ExceptionConditions.nullable(),
16409
- createdBy: external_exports.string(),
16410
- createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
16411
- createdAt: external_exports.iso.datetime(),
16412
- updatedAt: external_exports.iso.datetime(),
16413
- // Revocation is terminal and retained — consumed/expired/revoked rows are
16414
- // audit evidence; nothing in the exception lifecycle hard-deletes.
16415
- revokedAt: external_exports.iso.datetime().nullable(),
16416
- revokedBy: external_exports.string().nullable(),
16417
- revokeReason: external_exports.string().nullable()
16418
- });
16419
- var ExceptionBundleEntry = DetectionException.pick({
16420
- id: true,
16421
- ruleId: true,
16422
- valueFingerprint: true,
16423
- keyVersion: true,
16424
- capability: true,
16425
- expiresAt: true,
16426
- maxUses: true,
16427
- useCount: true,
16428
- conditions: true
16429
- });
16430
- var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
16431
-
16432
- // ../../packages/schema/src/zod/rule.ts
16433
- var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16434
- var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16435
- var KeywordMatcher = external_exports.object({
16436
- type: external_exports.literal("keyword"),
16437
- // An empty keyword matches at every position, yielding one zero-length span
16438
- // per character. Rejected here because a keyword that matches everything is
16439
- // never intentional.
16440
- keywords: external_exports.array(external_exports.string().min(1)).min(1),
16441
- caseSensitive: external_exports.boolean().default(false)
16442
- });
16443
- function isValidRegex(pattern, flags) {
16444
- try {
16445
- new RegExp(pattern, flags);
16446
- return true;
16447
- } catch {
16448
- return false;
16449
- }
16450
- }
16451
- function matchesEmptyString(pattern, flags) {
16452
- try {
16453
- const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
16454
- return re.exec("")?.[0].length === 0;
16455
- } catch {
16456
- return false;
16457
- }
16458
- }
16459
- var MAX_PATTERN_LENGTH = 2e3;
16460
- var RegexMatcher = external_exports.object({
16461
- type: external_exports.literal("regex"),
16462
- pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16463
- flags: external_exports.string().default("gi"),
16464
- captureGroup: external_exports.number().int().nonnegative().optional()
16465
- }).refine((v) => isValidRegex(v.pattern, v.flags), {
16466
- message: "pattern/flags do not form a valid JavaScript regular expression",
16467
- path: ["pattern"]
16468
- }).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
16469
- message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
16470
- path: ["pattern"]
16471
- });
16472
- var ValidatorMatcher = external_exports.object({
16473
- type: external_exports.literal("validator"),
16474
- name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
16475
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16476
- });
16477
- var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
16478
- var AppliesTo = external_exports.object({
16479
- // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16480
- extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16481
- }).meta({ id: "AppliesTo" });
16482
- var PostValidatorRef = external_exports.union([
16483
- external_exports.string(),
16484
- external_exports.object({
16485
- name: external_exports.string(),
16486
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16487
- })
16488
- ]).meta({ id: "PostValidatorRef" });
16489
- var RequiresNearby = external_exports.object({
16490
- // Each array, when present, must be non-empty and contain non-empty strings —
16491
- // an empty/blank criterion would either never fire or (for labels) match
16492
- // everything.
16493
- categories: external_exports.array(DetectionCategory).min(1).optional(),
16494
- ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16495
- labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16496
- windowChars: external_exports.number().int().positive().default(160),
16497
- // Optional confidence bump applied when a gated match is corroborated. Capped
16498
- // small: it nudges confidence, it does not assert certainty.
16499
- confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16500
- }).refine(
16501
- (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16502
- { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16503
- ).meta({ id: "RequiresNearby" });
16504
- var RuleFixture = external_exports.object({
16505
- label: external_exports.string(),
16506
- text: external_exports.string().max(5e4),
16507
- shouldMatch: external_exports.boolean(),
16508
- // Simulated file context for the scan, so fixtures can assert `appliesTo`
16509
- // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16510
- filePath: external_exports.string().optional(),
16511
- expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
16512
- }).meta({ id: "RuleFixture" });
16513
- var Rule = external_exports.object({
16514
- specVersion: external_exports.literal(1),
16515
- // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16516
- // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16517
- // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16518
- // therefore carries no namespace and is not globally unique across publishers;
16519
- // never feed one to splitDetectionId. `category` below (per-rule) is the
16520
- // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16521
- id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16522
- name: external_exports.string(),
16523
- category: DetectionCategory,
16524
- severity: Severity,
16525
- matcher: Matcher,
16526
- appliesTo: AppliesTo.optional(),
16527
- postValidators: external_exports.array(PostValidatorRef).optional(),
16528
- requiresNearby: RequiresNearby.optional(),
16529
- examples: external_exports.array(external_exports.string()).optional()
16530
- }).meta({ id: "Rule" });
16531
- var Author = external_exports.object({
16532
- name: external_exports.string(),
16533
- email: external_exports.email().optional(),
16534
- url: external_exports.url().optional()
16535
- }).meta({ id: "Author" });
16536
- var PackManifest = external_exports.object({
16537
- specVersion: external_exports.literal(1),
16538
- id: external_exports.string(),
16539
- name: external_exports.string(),
16540
- version: external_exports.string(),
16541
- rules: external_exports.array(external_exports.string()),
16542
- // Optional attribution/provenance — consumed by the rule marketplace.
16543
- description: external_exports.string().optional(),
16544
- author: Author.optional(),
16545
- license: external_exports.string().optional(),
16546
- sourceUrl: external_exports.url().optional()
16547
- }).meta({ id: "PackManifest" });
16548
-
16549
- // ../../packages/schema/src/zod/policy.ts
16550
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
16551
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
16552
- var Policy = external_exports.object({
16553
- id: external_exports.guid(),
16554
- scope: PolicyScope,
16555
- target: PolicyTarget,
16556
- action: ActionTaken,
16557
- enabled: external_exports.boolean().default(true),
16558
- customKeywords: external_exports.array(external_exports.string()).optional(),
16559
- // Display name — optional so older policy rows without name still parse.
16560
- // Added for the findings API (policy.name column migration).
16561
- name: external_exports.string().optional()
16562
- }).meta({ id: "Policy" });
16563
- var PolicyBundle = external_exports.object({
16564
- version: external_exports.string(),
16565
- policies: external_exports.array(Policy),
16566
- // Rules from the installed marketplace packs (snapshotted by the
16567
- // control plane). The plugin registers these in addition to its bundled
16568
- // packs. Optional so older backends — and older on-disk caches — that omit
16569
- // the field still parse; consumers read `bundle.rules ?? []`.
16570
- rules: external_exports.array(Rule).optional(),
16571
- // When true, `rules` IS the complete effective ruleset and the runtime must
16572
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
16573
- // after reading the user's installed snapshot (installed_packs, enabled
16574
- // packs only), which is how detection updates stay manual: new bundled
16575
- // rules run only after the user applies the pack update. Absent/false keeps
16576
- // the historical composition (bundled packs + rules) — older caches.
16577
- rulesComplete: external_exports.boolean().optional(),
16578
- // Active detection exceptions, evaluation subset only (see
16579
- // ExceptionBundleEntry). Optional so older bundle producers — and older
16580
- // on-disk caches — that omit the field still parse; consumers read
16581
- // `bundle.exceptions ?? []`.
16582
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
16583
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
16584
- // from a versioned installed pack. Optional so older backends — and older
16585
- // on-disk caches — that omit the field still parse; consumers fall back to
16586
- // the rule's own spec version. NOT the bundle version above — see
16587
- // installedRuleset's ruleVersions for the source of truth.
16588
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
16589
- customKeywords: external_exports.array(external_exports.string()),
16590
- fetchedAt: external_exports.iso.datetime()
16591
- }).meta({ id: "PolicyBundle" });
16592
- var OBSERVE_ONLY_CATEGORIES = ["config"];
16593
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
16594
- var CATEGORY_PEAK_SEVERITY = {
16595
- secret: "critical",
16596
- financial: "critical",
16597
- // core-financial/credit-card
16598
- code_flaw: "critical",
16599
- pii: "high",
16600
- phi: "high",
16601
- custom: "high",
16602
- // user-defined; conservative
16603
- code_context: "low",
16604
- config: "low"
16605
- // observe-only; floors to monitor regardless
16606
- };
16607
- function severityFloorPolicy(category) {
16608
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
16609
- const peak = CATEGORY_PEAK_SEVERITY[category];
16610
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
16611
- }
16612
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16613
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16614
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16615
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16616
- var BUILTIN_POLICY_SPECS = {
16617
- monitor: {
16618
- name: "Monitor",
16619
- action: "log",
16620
- description: "Log every match for audit. The request is allowed through untouched."
16621
- },
16622
- warn: {
16623
- name: "Warn",
16624
- action: "warn",
16625
- description: "Allow the request, but warn the user inline before it is sent."
16626
- },
16627
- redact: {
16628
- name: "Redact",
16629
- action: "redact",
16630
- description: "Automatically strip the matched value from the request, then continue."
16631
- },
16632
- block: {
16633
- name: "Block",
16634
- action: "block",
16635
- description: "Refuse the request entirely whenever any rule in this detection matches."
16636
- }
16637
- };
16638
- function builtinPolicyToAction(id) {
16639
- return BUILTIN_POLICY_SPECS[id].action;
16640
- }
16641
- var DEFAULT_ACTIONS = Object.fromEntries(
16642
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16643
- );
16644
- var BUILTIN_POLICIES = Object.fromEntries(
16645
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16646
- );
16647
- var DEFAULT_PACK_POLICY_ID = "monitor";
16648
- function policyIdToAction(policyId) {
16649
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16650
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16651
- return BUILTIN_POLICIES[id].action;
16652
- }
16653
- var UsedByItem = external_exports.object({
16654
- id: external_exports.string(),
16655
- name: external_exports.string(),
16656
- ruleCount: external_exports.number().int().nonnegative(),
16657
- enabled: external_exports.boolean()
16658
- }).meta({ id: "UsedByItem" });
16659
- var PolicyListItem = external_exports.object({
16660
- id: external_exports.string(),
16661
- kind: PolicyKind,
16662
- name: external_exports.string(),
16663
- enabled: external_exports.boolean(),
16664
- usedByCount: external_exports.number().int().nonnegative()
16665
- }).meta({ id: "PolicyListItem" });
16666
- var PolicyDetail = external_exports.object({
16667
- specVersion: external_exports.literal(1),
16668
- id: external_exports.string(),
16669
- kind: PolicyKind,
16670
- name: external_exports.string(),
16671
- enabled: external_exports.boolean(),
16672
- description: external_exports.string(),
16673
- usedBy: external_exports.array(UsedByItem)
16674
- }).meta({ id: "PolicyDetail" });
16675
- var PolicyStatsResponse = external_exports.object({
16676
- policies: external_exports.number().int().nonnegative(),
16677
- builtin: external_exports.number().int().nonnegative(),
16678
- custom: external_exports.number().int().nonnegative(),
16679
- detectionsGoverned: external_exports.number().int().nonnegative()
16680
- }).meta({ id: "PolicyStatsResponse" });
16681
-
16682
- // ../../packages/schema/src/zod/api.ts
16683
- var LIST_QUERY_MAX_LIMIT = 200;
16684
- var ListEventsQuery = external_exports.object({
16685
- cursor: external_exports.string().optional(),
16686
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16687
- sourceTool: external_exports.string().optional(),
16688
- kind: external_exports.string().optional(),
16689
- from: external_exports.iso.datetime().optional(),
16690
- to: external_exports.iso.datetime().optional()
16691
- });
16692
- var ListEventsResponse = external_exports.object({
16693
- items: external_exports.array(Event),
16694
- nextCursor: external_exports.string().nullable()
16695
- }).meta({ id: "ListEventsResponse" });
16696
- var IngestResponse = external_exports.object({
16697
- accepted: external_exports.number().int().nonnegative(),
16698
- duplicates: external_exports.number().int().nonnegative()
16699
- }).meta({ id: "IngestResponse" });
16700
- var ListFindingsQuery = external_exports.object({
16701
- cursor: external_exports.string().optional(),
16702
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16703
- severity: external_exports.string().optional(),
16704
- category: external_exports.string().optional(),
16705
- eventId: external_exports.guid().optional()
16706
- });
16707
- var ListFindingsResponse = external_exports.object({
16708
- items: external_exports.array(Finding),
16709
- nextCursor: external_exports.string().nullable()
16710
- }).meta({ id: "ListFindingsResponse" });
16711
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16712
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16713
- id: "CreatePolicyRequest"
16714
- });
16715
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16716
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16717
- var ErrorResponse = external_exports.object({
16718
- error: external_exports.object({
16719
- code: external_exports.string(),
16720
- message: external_exports.string(),
16721
- details: external_exports.unknown().optional()
16722
- })
16723
- }).meta({ id: "ErrorResponse" });
16724
-
16725
- // ../../packages/schema/src/zod/config-inventory.ts
16726
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16727
- var SkillScanEntry = external_exports.object({
16728
- name: external_exports.string().min(1),
16729
- // The identity source: a marketplace repo for plugin skills (e.g.
16730
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16731
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16732
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16733
- source: external_exports.string().min(1),
16734
- scope: ConfigScope,
16735
- pluginName: external_exports.string().optional(),
16736
- // Volatile — rides the attribute bag, never the identity hash.
16737
- version: external_exports.string().optional(),
16738
- description: external_exports.string().optional(),
16739
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16740
- updatedAt: external_exports.iso.datetime().optional(),
16741
- // Filesystem path — the promoted inventory `location` column.
16742
- location: external_exports.string().optional()
16743
- });
16744
- var HookScanEntry = external_exports.object({
16745
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16746
- // set is harness-defined and grows without a schema change.
16747
- event: external_exports.string().min(1),
16748
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16749
- matcher: external_exports.string().optional(),
16750
- command: external_exports.string().min(1),
16751
- timeout: external_exports.number().optional(),
16752
- scope: ConfigScope,
16753
- pluginName: external_exports.string().optional(),
16754
- // The settings file / hooks.json the entry came from.
16755
- location: external_exports.string().optional()
16118
+ var HookScanEntry = external_exports.object({
16119
+ // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16120
+ // set is harness-defined and grows without a schema change.
16121
+ event: external_exports.string().min(1),
16122
+ // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16123
+ matcher: external_exports.string().optional(),
16124
+ command: external_exports.string().min(1),
16125
+ timeout: external_exports.number().optional(),
16126
+ scope: ConfigScope,
16127
+ pluginName: external_exports.string().optional(),
16128
+ // The settings file / hooks.json the entry came from.
16129
+ location: external_exports.string().optional()
16756
16130
  });
16757
16131
  var McpServerScanEntry = external_exports.object({
16758
16132
  // The server's config key ("github", "filesystem", …) — identity, with the
@@ -16827,12 +16201,170 @@ var ConfigScanRecord = external_exports.object({
16827
16201
  definitions: external_exports.array(InspectionDefinitionInput).optional(),
16828
16202
  findings: external_exports.array(ConfigPostureFindingInput).optional()
16829
16203
  });
16830
-
16831
- // ../../packages/schema/src/zod/registry.ts
16832
- var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16833
- var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16834
- var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16835
- var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16204
+
16205
+ // ../../packages/schema/src/zod/registry.ts
16206
+ var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16207
+ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16208
+ var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16209
+ var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16210
+
16211
+ // ../../packages/schema/src/zod/rule.ts
16212
+ var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
16213
+ var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16214
+ var KeywordMatcher = external_exports.strictObject({
16215
+ type: external_exports.literal("keyword"),
16216
+ // An empty keyword matches at every position, yielding one zero-length span
16217
+ // per character. Rejected here because a keyword that matches everything is
16218
+ // never intentional.
16219
+ keywords: external_exports.array(external_exports.string().min(1)).min(1),
16220
+ caseSensitive: external_exports.boolean().default(false)
16221
+ });
16222
+ function isValidRegex(pattern, flags) {
16223
+ try {
16224
+ new RegExp(pattern, flags);
16225
+ return true;
16226
+ } catch {
16227
+ return false;
16228
+ }
16229
+ }
16230
+ function probeFlags(flags) {
16231
+ return flags.replace(/[gy]/g, "");
16232
+ }
16233
+ function matchesEmptyString(pattern, flags) {
16234
+ try {
16235
+ const re = new RegExp(pattern, probeFlags(flags));
16236
+ return re.exec("")?.[0].length === 0;
16237
+ } catch {
16238
+ return false;
16239
+ }
16240
+ }
16241
+ function spansWholeMatch(captureGroup) {
16242
+ return captureGroup === void 0 || captureGroup === 0;
16243
+ }
16244
+ function captureGroupCount(pattern, flags) {
16245
+ try {
16246
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags));
16247
+ const result = probe.exec("");
16248
+ return result ? result.length - 1 : void 0;
16249
+ } catch {
16250
+ return void 0;
16251
+ }
16252
+ }
16253
+ var MAX_PATTERN_LENGTH = 2e3;
16254
+ var RegexMatcher = external_exports.strictObject({
16255
+ type: external_exports.literal("regex"),
16256
+ pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16257
+ flags: external_exports.string().default("gi"),
16258
+ captureGroup: external_exports.number().int().nonnegative().optional()
16259
+ }).refine((v) => isValidRegex(v.pattern, v.flags), {
16260
+ message: "pattern/flags do not form a valid JavaScript regular expression",
16261
+ path: ["pattern"]
16262
+ }).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
16263
+ message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
16264
+ path: ["pattern"]
16265
+ }).superRefine((v, ctx) => {
16266
+ if (v.captureGroup === void 0) return;
16267
+ const groups = captureGroupCount(v.pattern, v.flags);
16268
+ if (groups === void 0 || v.captureGroup <= groups) return;
16269
+ ctx.addIssue({
16270
+ code: "custom",
16271
+ path: ["captureGroup"],
16272
+ message: `captureGroup ${String(v.captureGroup)} is out of range \u2014 the pattern declares ${String(groups)} capture group(s), so valid values are 0-${String(groups)}. An out-of-range group never matches, which would make the rule silently never fire.`
16273
+ });
16274
+ });
16275
+ var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
16276
+ var MATCHER_TYPES = MatcherType.options;
16277
+ var AppliesTo = external_exports.strictObject({
16278
+ // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16279
+ extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16280
+ }).meta({ id: "AppliesTo" });
16281
+ var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
16282
+ var PostValidatorRef = external_exports.union(
16283
+ [
16284
+ PostValidatorName,
16285
+ external_exports.strictObject({
16286
+ name: PostValidatorName,
16287
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16288
+ })
16289
+ ],
16290
+ {
16291
+ // A union reports one collapsed issue for every way its arms can fail, so
16292
+ // this has to describe the whole shape rather than just the name — it is
16293
+ // what an author sees for a misspelled name AND for a stray key in the
16294
+ // object form. The names come from the enum so the message cannot go
16295
+ // stale. Without it Zod says only "Invalid input", which is precisely the
16296
+ // no-feedback outcome this schema exists to remove.
16297
+ error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
16298
+ " or "
16299
+ )}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
16300
+ }
16301
+ ).meta({ id: "PostValidatorRef" });
16302
+ var RequiresNearby = external_exports.strictObject({
16303
+ // Each array, when present, must be non-empty and contain non-empty strings —
16304
+ // an empty/blank criterion would either never fire or (for labels) match
16305
+ // everything.
16306
+ categories: external_exports.array(DetectionCategory).min(1).optional(),
16307
+ ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16308
+ labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16309
+ windowChars: external_exports.number().int().positive().default(160),
16310
+ // Optional confidence bump applied when a gated match is corroborated. Capped
16311
+ // small: it nudges confidence, it does not assert certainty.
16312
+ confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16313
+ }).refine(
16314
+ (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16315
+ { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16316
+ ).meta({ id: "RequiresNearby" });
16317
+ var RuleFixture = external_exports.strictObject({
16318
+ label: external_exports.string(),
16319
+ text: external_exports.string().max(5e4),
16320
+ shouldMatch: external_exports.boolean(),
16321
+ // Simulated file context for the scan, so fixtures can assert `appliesTo`
16322
+ // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16323
+ filePath: external_exports.string().optional(),
16324
+ expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
16325
+ }).meta({ id: "RuleFixture" });
16326
+ var Rule = external_exports.strictObject({
16327
+ // A pinned literal over a STRICT object, and the two together decide how this
16328
+ // format may grow. A rule carrying a key not listed below is refused with
16329
+ // `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
16330
+ // `invalid_value`. So the only additive path is adding an OPTIONAL field here
16331
+ // — that keeps every rule authored before it valid — and a rule author has no
16332
+ // way to introduce a field of their own or to opt into a later version.
16333
+ // Widening the format means changing this literal and every consumer of it.
16334
+ specVersion: external_exports.literal(1),
16335
+ // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16336
+ // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16337
+ // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16338
+ // therefore carries no namespace and is not globally unique across publishers;
16339
+ // never feed one to splitDetectionId. `category` below (per-rule) is the
16340
+ // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16341
+ id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16342
+ name: external_exports.string(),
16343
+ category: DetectionCategory,
16344
+ severity: Severity,
16345
+ matcher: Matcher,
16346
+ appliesTo: AppliesTo.optional(),
16347
+ postValidators: external_exports.array(PostValidatorRef).optional(),
16348
+ requiresNearby: RequiresNearby.optional(),
16349
+ examples: external_exports.array(external_exports.string()).optional()
16350
+ }).meta({ id: "Rule" });
16351
+ var Author = external_exports.object({
16352
+ name: external_exports.string(),
16353
+ email: external_exports.email().optional(),
16354
+ url: external_exports.url().optional()
16355
+ }).meta({ id: "Author" });
16356
+ var PackManifest = external_exports.object({
16357
+ specVersion: external_exports.literal(1),
16358
+ id: external_exports.string(),
16359
+ name: external_exports.string(),
16360
+ version: external_exports.string(),
16361
+ rules: external_exports.array(external_exports.string()),
16362
+ // Optional attribution/provenance — consumed by the rule marketplace.
16363
+ description: external_exports.string().optional(),
16364
+ author: Author.optional(),
16365
+ license: external_exports.string().optional(),
16366
+ sourceUrl: external_exports.url().optional()
16367
+ }).meta({ id: "PackManifest" });
16836
16368
 
16837
16369
  // ../../packages/schema/src/zod/detection.ts
16838
16370
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
@@ -17032,6 +16564,231 @@ function buildDetectionsList(summaries, query) {
17032
16564
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17033
16565
  }
17034
16566
 
16567
+ // ../../packages/schema/src/zod/inventory.ts
16568
+ var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16569
+ var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16570
+ var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16571
+ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16572
+ var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16573
+ var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16574
+ var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16575
+ var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
16576
+ id: "HarnessId"
16577
+ });
16578
+ var AccessCounts = external_exports.object({
16579
+ open: external_exports.number().int().nonnegative(),
16580
+ approved: external_exports.number().int().nonnegative(),
16581
+ blocked: external_exports.number().int().nonnegative(),
16582
+ total: external_exports.number().int().nonnegative()
16583
+ }).meta({ id: "AccessCounts" });
16584
+ var AssetSummary = external_exports.object({
16585
+ id: external_exports.string(),
16586
+ type: AssetType,
16587
+ name: external_exports.string(),
16588
+ sub: external_exports.string(),
16589
+ flags: external_exports.array(Flag),
16590
+ /** MCP servers only — omitted for all other types. */
16591
+ trust: TrustLevel.optional()
16592
+ }).meta({ id: "AssetSummary" });
16593
+ var ProjectSummary = external_exports.object({
16594
+ id: external_exports.string(),
16595
+ name: external_exports.string(),
16596
+ repo: external_exports.string(),
16597
+ visibility: Visibility,
16598
+ language: external_exports.string(),
16599
+ policyDefault: AccessLevel,
16600
+ updatedAt: external_exports.iso.datetime(),
16601
+ accessCounts: AccessCounts,
16602
+ findingsCount: external_exports.number().int().nonnegative()
16603
+ }).meta({ id: "ProjectSummary" });
16604
+ var HarnessCategory = external_exports.object({
16605
+ /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16606
+ type: AssetType,
16607
+ assets: external_exports.array(AssetSummary)
16608
+ });
16609
+ var HarnessSummary = external_exports.object({
16610
+ id: HarnessId,
16611
+ label: external_exports.string(),
16612
+ kind: external_exports.string(),
16613
+ version: external_exports.string(),
16614
+ sessions: external_exports.number().int().nonnegative(),
16615
+ assetCount: external_exports.number().int().nonnegative(),
16616
+ flagCount: external_exports.number().int().nonnegative(),
16617
+ projects: external_exports.array(ProjectSummary),
16618
+ categories: external_exports.array(HarnessCategory)
16619
+ }).meta({ id: "HarnessSummary" });
16620
+ var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16621
+ var AssetGroup = external_exports.object({
16622
+ /** Group key — never project (enforced at service layer). */
16623
+ type: AssetType,
16624
+ total: external_exports.number().int().nonnegative(),
16625
+ /**
16626
+ * MCP group only — omitted for all other types.
16627
+ * Partial: only TrustLevel keys with non-zero counts are included.
16628
+ * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16629
+ */
16630
+ trustRollup: external_exports.object({
16631
+ "known-good": external_exports.number().int().nonnegative(),
16632
+ risky: external_exports.number().int().nonnegative(),
16633
+ unapproved: external_exports.number().int().nonnegative()
16634
+ }).partial().strict().optional(),
16635
+ /**
16636
+ * Partial: only Flag keys with non-zero counts are included.
16637
+ * Strict: unknown keys are rejected — only Flag values are valid keys.
16638
+ */
16639
+ flagRollup: external_exports.object({
16640
+ update: external_exports.number().int().nonnegative(),
16641
+ stale: external_exports.number().int().nonnegative(),
16642
+ conflict: external_exports.number().int().nonnegative(),
16643
+ unknown: external_exports.number().int().nonnegative(),
16644
+ change: external_exports.number().int().nonnegative(),
16645
+ untracked: external_exports.number().int().nonnegative(),
16646
+ risk: external_exports.number().int().nonnegative(),
16647
+ findings: external_exports.number().int().nonnegative()
16648
+ }).partial().strict(),
16649
+ items: external_exports.array(AssetSummary)
16650
+ }).meta({ id: "AssetGroup" });
16651
+ var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16652
+ var McpTool = external_exports.object({
16653
+ name: external_exports.string(),
16654
+ signature: external_exports.string(),
16655
+ description: external_exports.string(),
16656
+ write: external_exports.boolean(),
16657
+ /** Non-null string when tool is dangerous / blocked; null otherwise. */
16658
+ risk: external_exports.string().nullable()
16659
+ }).meta({ id: "McpTool" });
16660
+ var AssetFindingRef = external_exports.object({
16661
+ id: external_exports.string(),
16662
+ title: external_exports.string(),
16663
+ note: external_exports.string()
16664
+ });
16665
+ var AssetDetail = AssetSummary.extend({
16666
+ /** string | null — null when no description is available. */
16667
+ description: external_exports.string().nullable(),
16668
+ /** trustLevel | null — null for non-MCP assets. */
16669
+ trust: TrustLevel.nullable(),
16670
+ /** Type-specific raw key/values — FE renders the grid. */
16671
+ meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16672
+ /** always present — object when there is an active finding, null when absent. */
16673
+ finding: AssetFindingRef.nullable(),
16674
+ /** MCP exposed-tools list — omitted for non-mcp. */
16675
+ tools: external_exports.array(McpTool).optional()
16676
+ }).meta({ id: "AssetDetail" });
16677
+ var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16678
+ var InventoryStats = external_exports.object({
16679
+ /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16680
+ attention: external_exports.number().int().nonnegative(),
16681
+ byType: external_exports.object({
16682
+ project: external_exports.number().int().nonnegative(),
16683
+ skill: external_exports.number().int().nonnegative(),
16684
+ mcp: external_exports.number().int().nonnegative(),
16685
+ hook: external_exports.number().int().nonnegative(),
16686
+ config: external_exports.number().int().nonnegative()
16687
+ }),
16688
+ harnesses: external_exports.number().int().nonnegative(),
16689
+ mcpTrust: external_exports.object({
16690
+ "known-good": external_exports.number().int().nonnegative(),
16691
+ risky: external_exports.number().int().nonnegative(),
16692
+ unapproved: external_exports.number().int().nonnegative()
16693
+ })
16694
+ }).meta({ id: "InventoryStats" });
16695
+ var FileSummary = external_exports.object({
16696
+ path: external_exports.string(),
16697
+ name: external_exports.string(),
16698
+ origin: Origin,
16699
+ /** Effective access (override applied). */
16700
+ access: AccessLevel,
16701
+ /** True when a file_access_override differs from the computed default. */
16702
+ isCustom: external_exports.boolean(),
16703
+ findings: external_exports.number().int().nonnegative(),
16704
+ /** When the file was auto-blocked by a detection; null when not blocked. */
16705
+ blockedAt: external_exports.iso.datetime().nullable().optional(),
16706
+ /** Why the file was blocked; null when absent. */
16707
+ note: external_exports.string().nullable().optional()
16708
+ }).meta({ id: "FileSummary" });
16709
+ var FolderSummary = external_exports.object({
16710
+ name: external_exports.string(),
16711
+ path: external_exports.string(),
16712
+ /** Rollup of effective access across all descendants. */
16713
+ accessCounts: AccessCounts
16714
+ }).meta({ id: "FolderSummary" });
16715
+ var ProjectTreeResponse = external_exports.object({
16716
+ project: external_exports.object({
16717
+ id: external_exports.string(),
16718
+ repo: external_exports.string(),
16719
+ visibility: Visibility
16720
+ }),
16721
+ path: external_exports.string(),
16722
+ /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16723
+ folders: external_exports.array(FolderSummary).optional(),
16724
+ files: external_exports.array(FileSummary)
16725
+ }).meta({ id: "ProjectTreeResponse" });
16726
+ var FileDetail = FileSummary.extend({
16727
+ project: external_exports.object({
16728
+ repo: external_exports.string(),
16729
+ visibility: Visibility,
16730
+ language: external_exports.string(),
16731
+ policyDefault: AccessLevel,
16732
+ updatedAt: external_exports.iso.datetime()
16733
+ }),
16734
+ findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16735
+ }).meta({ id: "FileDetail" });
16736
+ var SetFileAccessBody = external_exports.object({
16737
+ path: external_exports.string(),
16738
+ access: AccessLevel
16739
+ }).meta({ id: "SetFileAccessBody" });
16740
+ var SetFileAccessResponse = external_exports.object({
16741
+ file: FileSummary,
16742
+ accessCounts: AccessCounts
16743
+ }).meta({ id: "SetFileAccessResponse" });
16744
+ var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16745
+ var HarnessEventItem = external_exports.object({
16746
+ kind: HarnessEventKind,
16747
+ title: external_exports.string(),
16748
+ detail: external_exports.string(),
16749
+ occurredAt: external_exports.iso.datetime(),
16750
+ findingId: external_exports.string().nullable().optional()
16751
+ }).meta({ id: "HarnessEventItem" });
16752
+ var HarnessEventsResponse = external_exports.object({
16753
+ counts: external_exports.object({
16754
+ block: external_exports.number().int().nonnegative(),
16755
+ redact: external_exports.number().int().nonnegative(),
16756
+ warn: external_exports.number().int().nonnegative()
16757
+ }),
16758
+ items: external_exports.array(HarnessEventItem)
16759
+ }).meta({ id: "HarnessEventsResponse" });
16760
+ var RescanResponse = external_exports.object({
16761
+ jobId: external_exports.string(),
16762
+ startedAt: external_exports.iso.datetime()
16763
+ }).meta({ id: "RescanResponse" });
16764
+ var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16765
+ var ListAssetsQuery = external_exports.object({
16766
+ /** Filter by one or more AssetType values; absent means all types. */
16767
+ type: external_exports.array(AssetType).optional(),
16768
+ /** Free-text search term. */
16769
+ q: external_exports.string().optional()
16770
+ });
16771
+ var GetProjectTreeQuery = external_exports.object({
16772
+ /** Subtree root path; defaults to repository root when absent. */
16773
+ path: external_exports.string().optional(),
16774
+ /** Free-text filter applied to file paths. */
16775
+ q: external_exports.string().optional(),
16776
+ /**
16777
+ * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16778
+ * file across the whole repo (folders omitted, most-recent first), ignoring
16779
+ * `path`/`q` — powers the project-wide "recently blocked" strip.
16780
+ */
16781
+ filter: external_exports.enum(["blocked"]).optional()
16782
+ });
16783
+ var GetProjectFileQuery = external_exports.object({
16784
+ /** Repository-relative file path; absent or empty → 400. */
16785
+ path: external_exports.string()
16786
+ });
16787
+ var GetHarnessEventsQuery = external_exports.object({
16788
+ /** Maximum number of events to return. Range: 1–50; default: 7. */
16789
+ limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16790
+ });
16791
+
17035
16792
  // ../../packages/schema/src/zod/shares.ts
17036
16793
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17037
16794
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17238,6 +16995,127 @@ var EgressWriteSummary = external_exports.object({
17238
16995
  droppedFiles: external_exports.array(external_exports.string()).default([])
17239
16996
  }).meta({ id: "EgressWriteSummary" });
17240
16997
 
16998
+ // ../../packages/schema/src/zod/event.ts
16999
+ var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
17000
+ var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
17001
+ var EventMetadata = external_exports.object({
17002
+ sessionId: external_exports.string().optional(),
17003
+ repo: external_exports.string().optional(),
17004
+ filePath: external_exports.string().optional(),
17005
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
17006
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
17007
+ // arguments or output, which can carry the very value a finding masked
17008
+ // (metadata is stored unredacted). Gives findings on non-file captures a
17009
+ // display location ("via Bash") when no filePath exists.
17010
+ toolName: external_exports.string().optional(),
17011
+ // Set (true) by the worktree scanner when the file is excluded by the
17012
+ // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
17013
+ // generated code can leak real secrets — but the provenance is recorded so
17014
+ // policy/dashboards can treat those findings as informational rather than
17015
+ // blocking. Omitted (not false) for tracked files and non-scan events.
17016
+ gitignored: external_exports.boolean().optional(),
17017
+ // Set (true) ONLY when the event's `content` is the COMPLETE file at
17018
+ // capture time (a worktree scan reading from disk). Hook-captured edits
17019
+ // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
17020
+ // this. The resolver-on-ingest keys its fixed-at-source dropout
17021
+ // diff on this marker: only a whole-file snapshot can prove a previously
17022
+ // open finding is gone; a fragment's absence proves nothing (the secret
17023
+ // may live outside the hunk). Omitted (not false) for fragments and
17024
+ // non-scan events, so pre-marker clients safely default to the
17025
+ // non-authoritative path.
17026
+ wholeFile: external_exports.boolean().optional(),
17027
+ model: external_exports.string().optional(),
17028
+ turnIndex: external_exports.number().int().nonnegative().optional(),
17029
+ // Distributed-tracing correlation. `correlationId` ties a recorded event back
17030
+ // to the request that captured/ingested it (a UUID, generated independently of
17031
+ // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
17032
+ // originating span when telemetry is enabled. Both optional + backward
17033
+ // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
17034
+ correlationId: external_exports.uuid().optional(),
17035
+ traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
17036
+ // Ids of the detection exceptions that downgraded findings in this capture
17037
+ // to 'allow' — the enforcement audit trail's link back to the grant that
17038
+ // authorized the bypass. Absent on captures where no exception applied.
17039
+ exceptionIds: external_exports.array(external_exports.guid()).optional()
17040
+ }).meta({ id: "EventMetadata" });
17041
+ var Event = external_exports.object({
17042
+ id: external_exports.guid(),
17043
+ sourceTool: SourceTool,
17044
+ kind: EventKind,
17045
+ occurredAt: external_exports.iso.datetime(),
17046
+ contentHash: external_exports.string(),
17047
+ content: external_exports.string(),
17048
+ metadata: EventMetadata.optional()
17049
+ }).meta({ id: "Event" });
17050
+ var IngestEvent = Event.meta({ id: "IngestEvent" });
17051
+ var IngestBatch = external_exports.object({
17052
+ events: external_exports.array(IngestEvent).min(1).max(100),
17053
+ // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
17054
+ // additionally rejects any event whose contentHash the store has already
17055
+ // recorded — for re-runnable bulk ingest (worktree scan, transcript
17056
+ // backfill), where a re-run mints fresh event ids for identical content and
17057
+ // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
17058
+ // two genuinely separate prompts can be byte-identical and both belong on
17059
+ // the timeline.
17060
+ dedupe: external_exports.literal("content-hash").optional()
17061
+ }).meta({ id: "IngestBatch" });
17062
+
17063
+ // ../../packages/schema/src/zod/exception.ts
17064
+ var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
17065
+ var ExceptionConditions = external_exports.object({
17066
+ repo: external_exports.string().optional(),
17067
+ sourceTool: external_exports.string().optional(),
17068
+ provider: external_exports.string().optional()
17069
+ }).strict();
17070
+ var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
17071
+ var DetectionException = external_exports.object({
17072
+ id: external_exports.guid(),
17073
+ ruleId: external_exports.string(),
17074
+ // Denormalized from the rule, for reporting — never matched on.
17075
+ category: DetectionCategory,
17076
+ // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
17077
+ // fingerprint, never the raw value, and never reversible. Matching recomputes
17078
+ // the fingerprint from a fresh capture; the value itself is never stored.
17079
+ // Shape-constrained so a malformed — or accidentally raw — value is rejected
17080
+ // at the boundary rather than persisted.
17081
+ valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
17082
+ // Version of the fingerprint key the grant was written under; a rotated key
17083
+ // invalidates old grants rather than silently mismatching them.
17084
+ keyVersion: external_exports.number().int().positive(),
17085
+ // maskMatch() preview of the approved value — never the raw value.
17086
+ maskedValue: external_exports.string(),
17087
+ capability: ExceptionCapability.default("suppress"),
17088
+ scope: ExceptionScope,
17089
+ expiresAt: external_exports.iso.datetime().nullable(),
17090
+ maxUses: external_exports.number().int().positive().nullable(),
17091
+ useCount: external_exports.number().int().nonnegative(),
17092
+ lastUsedAt: external_exports.iso.datetime().nullable(),
17093
+ // Mandatory: every grant carries the human reason it exists.
17094
+ justification: external_exports.string().min(1),
17095
+ conditions: ExceptionConditions.nullable(),
17096
+ createdBy: external_exports.string(),
17097
+ createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
17098
+ createdAt: external_exports.iso.datetime(),
17099
+ updatedAt: external_exports.iso.datetime(),
17100
+ // Revocation is terminal and retained — consumed/expired/revoked rows are
17101
+ // audit evidence; nothing in the exception lifecycle hard-deletes.
17102
+ revokedAt: external_exports.iso.datetime().nullable(),
17103
+ revokedBy: external_exports.string().nullable(),
17104
+ revokeReason: external_exports.string().nullable()
17105
+ });
17106
+ var ExceptionBundleEntry = DetectionException.pick({
17107
+ id: true,
17108
+ ruleId: true,
17109
+ valueFingerprint: true,
17110
+ keyVersion: true,
17111
+ capability: true,
17112
+ expiresAt: true,
17113
+ maxUses: true,
17114
+ useCount: true,
17115
+ conditions: true
17116
+ });
17117
+ var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
17118
+
17241
17119
  // ../../packages/schema/src/zod/exception-action.ts
17242
17120
  var confirmation = external_exports.string().optional();
17243
17121
  var ApproveBlockedInput = external_exports.object({
@@ -17280,10 +17158,11 @@ function toApiAction(dbVal) {
17280
17158
  }
17281
17159
  function toApiCategory(dbVal) {
17282
17160
  if (dbVal === "code_context") return "source_code";
17283
- return dbVal;
17161
+ const parsed = FindingCategory.safeParse(dbVal);
17162
+ return parsed.success ? parsed.data : "custom";
17284
17163
  }
17285
17164
  function toApiProvider(sourceTool) {
17286
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17165
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17287
17166
  }
17288
17167
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17289
17168
  function foldGroupStatus(instanceStatuses) {
@@ -17871,7 +17750,14 @@ function isVaultConsentValid(consent) {
17871
17750
 
17872
17751
  // ../../packages/schema/src/zod/local.ts
17873
17752
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17874
- var RunMode = external_exports.enum(["standalone"]);
17753
+ var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17754
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17755
+ var ControlPlaneConnection = external_exports.object({
17756
+ endpoint: external_exports.string().min(1),
17757
+ // Display name for the deployment, shown instead of the raw endpoint.
17758
+ label: external_exports.string().min(1).optional(),
17759
+ attachedAt: external_exports.iso.datetime()
17760
+ }).meta({ id: "ControlPlaneConnection" });
17875
17761
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17876
17762
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17877
17763
  var ModelJudgeConsent = external_exports.object({
@@ -17880,12 +17766,10 @@ var ModelJudgeConsent = external_exports.object({
17880
17766
  });
17881
17767
  var WorkspaceSettings = external_exports.object({
17882
17768
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17883
- // Settings files written by earlier releases may carry the retired 'attached'
17884
- // value; it parses as 'standalone' so those files keep loading.
17885
- runMode: external_exports.preprocess(
17886
- (v) => v === "attached" ? "standalone" : v,
17887
- RunMode.default("standalone")
17888
- ),
17769
+ runMode: RunMode.default("standalone"),
17770
+ // Present only while attached; a detach clears it. Its presence is what makes
17771
+ // `runMode: 'attached'` mean anything — see isAttached.
17772
+ controlPlane: ControlPlaneConnection.optional(),
17889
17773
  policy: SimpleDetectionPolicy.default("redact"),
17890
17774
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17891
17775
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -17987,39 +17871,244 @@ function toInspectionFindingRow(input) {
17987
17871
  firstDetectedAt: input.firstDetectedAt ? isoToEpochMillis(input.firstDetectedAt) : null
17988
17872
  };
17989
17873
  }
17990
- function toCaptureAttributes(event) {
17991
- const metadata = event.metadata;
17992
- return {
17993
- source_tool: event.sourceTool,
17994
- ...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
17995
- ...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
17996
- ...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
17997
- ...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
17998
- ...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
17999
- ...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
18000
- ...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
18001
- ...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
18002
- // `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
18003
- // has ever populated either), but every legacy metadata key still rides
18004
- // the bag rather than being silently dropped — CaptureAttributes'
18005
- // `.catchall(z.unknown())` carries the long tail.
18006
- ...metadata?.model !== void 0 ? { model: metadata.model } : {},
18007
- ...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
18008
- };
17874
+ function toCaptureAttributes(event) {
17875
+ const metadata = event.metadata;
17876
+ return {
17877
+ source_tool: event.sourceTool,
17878
+ ...metadata?.repo !== void 0 ? { repo: metadata.repo } : {},
17879
+ ...metadata?.filePath !== void 0 ? { file_path: metadata.filePath } : {},
17880
+ ...metadata?.toolName !== void 0 ? { tool_name: metadata.toolName } : {},
17881
+ ...metadata?.gitignored !== void 0 ? { gitignored: metadata.gitignored } : {},
17882
+ ...metadata?.wholeFile !== void 0 ? { whole_file: metadata.wholeFile } : {},
17883
+ ...metadata?.correlationId !== void 0 ? { correlation_id: metadata.correlationId } : {},
17884
+ ...metadata?.traceId !== void 0 ? { trace_id: metadata.traceId } : {},
17885
+ ...metadata?.exceptionIds !== void 0 ? { exception_ids: metadata.exceptionIds } : {},
17886
+ // `model`/`turnIndex` have no dedicated CaptureAttributes field (no writer
17887
+ // has ever populated either), but every legacy metadata key still rides
17888
+ // the bag rather than being silently dropped — CaptureAttributes'
17889
+ // `.catchall(z.unknown())` carries the long tail.
17890
+ ...metadata?.model !== void 0 ? { model: metadata.model } : {},
17891
+ ...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
17892
+ };
17893
+ }
17894
+ function captureDefinitionVersion(finding) {
17895
+ return `capture/${finding.category}/${finding.severity}`;
17896
+ }
17897
+ function toCaptureDefinitionInput(finding) {
17898
+ return {
17899
+ ruleId: finding.ruleId,
17900
+ version: captureDefinitionVersion(finding),
17901
+ name: finding.ruleId,
17902
+ category: finding.category,
17903
+ severity: finding.severity,
17904
+ definition: JSON.stringify({ ruleId: finding.ruleId })
17905
+ };
17906
+ }
17907
+
17908
+ // ../../packages/schema/src/zod/managed.ts
17909
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
17910
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
17911
+ var ManagedSettingKey = external_exports.enum([
17912
+ "runMode",
17913
+ "historicalAccess",
17914
+ "vaultConsent",
17915
+ "vaultKeyCustody",
17916
+ "vaultInlineReveal",
17917
+ "modelJudgeConsent",
17918
+ "dataSharesInPlace"
17919
+ ]).meta({ id: "ManagedSettingKey" });
17920
+ var ManagedSettingsValues = external_exports.object({
17921
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
17922
+ controlPlane: external_exports.object({
17923
+ endpoint: external_exports.string().min(1),
17924
+ label: external_exports.string().min(1).optional()
17925
+ }).optional(),
17926
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
17927
+ vaultConsent: external_exports.boolean().optional(),
17928
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
17929
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
17930
+ modelJudgeConsent: external_exports.boolean().optional(),
17931
+ dataSharesInPlace: external_exports.boolean().optional()
17932
+ }).meta({ id: "ManagedSettingsValues" });
17933
+ var ManagedSettings = external_exports.object({
17934
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
17935
+ // Shown on every locked control, so the user can tell an administrative
17936
+ // decision from a bug. Absent renders as a generic "your organization".
17937
+ organization: external_exports.string().min(1).optional(),
17938
+ // What the administrator pinned.
17939
+ values: ManagedSettingsValues.default({}),
17940
+ // Which of those the user may not change. A key here with no matching value
17941
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
17942
+ // the user may still override. The two are separable on purpose.
17943
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
17944
+ }).meta({ id: "ManagedSettings" });
17945
+
17946
+ // ../../packages/schema/src/zod/policy.ts
17947
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
17948
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
17949
+ var Policy = external_exports.object({
17950
+ id: external_exports.guid(),
17951
+ scope: PolicyScope,
17952
+ target: PolicyTarget,
17953
+ action: ActionTaken,
17954
+ enabled: external_exports.boolean().default(true),
17955
+ customKeywords: external_exports.array(external_exports.string()).optional(),
17956
+ // Display name — optional so older policy rows without name still parse.
17957
+ // Added for the findings API (policy.name column migration).
17958
+ name: external_exports.string().optional()
17959
+ }).meta({ id: "Policy" });
17960
+ var PolicyBundle = external_exports.object({
17961
+ version: external_exports.string(),
17962
+ policies: external_exports.array(Policy),
17963
+ // Rules from the installed marketplace packs (snapshotted by the
17964
+ // control plane). The plugin registers these in addition to its bundled
17965
+ // packs. Optional so older backends — and older on-disk caches — that omit
17966
+ // the field still parse; consumers read `bundle.rules ?? []`.
17967
+ rules: external_exports.array(Rule).optional(),
17968
+ // When true, `rules` IS the complete effective ruleset and the runtime must
17969
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
17970
+ // after reading the user's installed snapshot (installed_packs, enabled
17971
+ // packs only), which is how detection updates stay manual: new bundled
17972
+ // rules run only after the user applies the pack update. Absent/false keeps
17973
+ // the historical composition (bundled packs + rules) — older caches.
17974
+ rulesComplete: external_exports.boolean().optional(),
17975
+ // Active detection exceptions, evaluation subset only (see
17976
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
17977
+ // on-disk caches — that omit the field still parse; consumers read
17978
+ // `bundle.exceptions ?? []`.
17979
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
17980
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
17981
+ // A second axis over the same `redact` action, carried beside the policies
17982
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
17983
+ // widening Policy itself would change a persisted shape to express something
17984
+ // only the in-memory bundle needs. Optional so an older producer — or an
17985
+ // older on-disk cache — still parses; consumers read `?? []` and get the
17986
+ // pre-existing one-way behaviour, which is the safe direction to default.
17987
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
17988
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
17989
+ // from a versioned installed pack. Optional so older backends — and older
17990
+ // on-disk caches — that omit the field still parse; consumers fall back to
17991
+ // the rule's own spec version. NOT the bundle version above — see
17992
+ // installedRuleset's ruleVersions for the source of truth.
17993
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
17994
+ customKeywords: external_exports.array(external_exports.string()),
17995
+ fetchedAt: external_exports.iso.datetime()
17996
+ }).meta({ id: "PolicyBundle" });
17997
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
17998
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
17999
+ var CATEGORY_PEAK_SEVERITY = {
18000
+ secret: "critical",
18001
+ financial: "critical",
18002
+ // core-financial/credit-card
18003
+ code_flaw: "critical",
18004
+ pii: "high",
18005
+ phi: "high",
18006
+ custom: "high",
18007
+ // user-defined; conservative
18008
+ code_context: "low",
18009
+ config: "low"
18010
+ // observe-only; floors to monitor regardless
18011
+ };
18012
+ function severityFloorPolicy(category) {
18013
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18014
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18015
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18016
+ }
18017
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18018
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18019
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18020
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18021
+ var BUILTIN_POLICY_SPECS = {
18022
+ monitor: {
18023
+ name: "Monitor",
18024
+ action: "log",
18025
+ reversible: false,
18026
+ description: "Log every match for audit. The request is allowed through untouched."
18027
+ },
18028
+ warn: {
18029
+ name: "Warn",
18030
+ action: "warn",
18031
+ reversible: false,
18032
+ description: "Allow the request, but warn the user inline before it is sent."
18033
+ },
18034
+ redact: {
18035
+ name: "Redact",
18036
+ action: "redact",
18037
+ reversible: false,
18038
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18039
+ },
18040
+ vault: {
18041
+ name: "Redact & Vault",
18042
+ action: "redact",
18043
+ reversible: true,
18044
+ 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."
18045
+ },
18046
+ block: {
18047
+ name: "Block",
18048
+ action: "block",
18049
+ reversible: false,
18050
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18051
+ }
18052
+ };
18053
+ function builtinPolicyToAction(id) {
18054
+ return BUILTIN_POLICY_SPECS[id].action;
18009
18055
  }
18010
- function captureDefinitionVersion(finding) {
18011
- return `capture/${finding.category}/${finding.severity}`;
18056
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18057
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18058
+ );
18059
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18060
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18061
+ );
18062
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18063
+ function builtinPolicyIsReversible(id) {
18064
+ return BUILTIN_POLICY_SPECS[id].reversible;
18012
18065
  }
18013
- function toCaptureDefinitionInput(finding) {
18014
- return {
18015
- ruleId: finding.ruleId,
18016
- version: captureDefinitionVersion(finding),
18017
- name: finding.ruleId,
18018
- category: finding.category,
18019
- severity: finding.severity,
18020
- definition: JSON.stringify({ ruleId: finding.ruleId })
18021
- };
18066
+ function policyIdIsReversible(policyId) {
18067
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18068
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18069
+ return builtinPolicyIsReversible(id);
18070
+ }
18071
+ var DEFAULT_ACTIONS = Object.fromEntries(
18072
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18073
+ );
18074
+ var BUILTIN_POLICIES = Object.fromEntries(
18075
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18076
+ );
18077
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18078
+ function policyIdToAction(policyId) {
18079
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18080
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18081
+ return BUILTIN_POLICIES[id].action;
18022
18082
  }
18083
+ var UsedByItem = external_exports.object({
18084
+ id: external_exports.string(),
18085
+ name: external_exports.string(),
18086
+ ruleCount: external_exports.number().int().nonnegative(),
18087
+ enabled: external_exports.boolean()
18088
+ }).meta({ id: "UsedByItem" });
18089
+ var PolicyListItem = external_exports.object({
18090
+ id: external_exports.string(),
18091
+ kind: PolicyKind,
18092
+ name: external_exports.string(),
18093
+ enabled: external_exports.boolean(),
18094
+ usedByCount: external_exports.number().int().nonnegative()
18095
+ }).meta({ id: "PolicyListItem" });
18096
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18097
+ var PolicyDetail = external_exports.object({
18098
+ specVersion: external_exports.literal(1),
18099
+ id: external_exports.string(),
18100
+ kind: PolicyKind,
18101
+ name: external_exports.string(),
18102
+ enabled: external_exports.boolean(),
18103
+ description: external_exports.string(),
18104
+ usedBy: external_exports.array(UsedByItem)
18105
+ }).meta({ id: "PolicyDetail" });
18106
+ var PolicyStatsResponse = external_exports.object({
18107
+ policies: external_exports.number().int().nonnegative(),
18108
+ builtin: external_exports.number().int().nonnegative(),
18109
+ custom: external_exports.number().int().nonnegative(),
18110
+ detectionsGoverned: external_exports.number().int().nonnegative()
18111
+ }).meta({ id: "PolicyStatsResponse" });
18023
18112
 
18024
18113
  // ../../packages/schema/src/zod/project-files.ts
18025
18114
  var ProjectFileInput = external_exports.object({
@@ -18099,44 +18188,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18099
18188
  NoRemediationDecision
18100
18189
  ]);
18101
18190
 
18102
- // ../../packages/schema/src/zod/rule-test.ts
18103
- var TestRulesRequest = external_exports.object({
18104
- rules: external_exports.array(Rule).min(1).max(100),
18105
- text: external_exports.string().max(5e4).optional(),
18106
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18107
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18108
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18109
- }).meta({ id: "TestRulesRequest" });
18110
- var RuleTestMatch = external_exports.object({
18111
- ruleId: external_exports.string(),
18112
- category: DetectionCategory,
18113
- severity: Severity,
18114
- span: Span,
18115
- confidence: external_exports.number().min(0).max(1),
18116
- match: external_exports.string()
18117
- }).meta({ id: "RuleTestMatch" });
18118
- var FixtureResult = external_exports.object({
18119
- label: external_exports.string(),
18120
- shouldMatch: external_exports.boolean(),
18121
- didMatch: external_exports.boolean(),
18122
- passed: external_exports.boolean(),
18123
- matches: external_exports.array(RuleTestMatch)
18124
- }).meta({ id: "FixtureResult" });
18125
- var TestRulesResponse = external_exports.object({
18126
- // Present only when the request supplied `text`.
18127
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18128
- fixtures: external_exports.array(FixtureResult),
18129
- summary: external_exports.object({
18130
- total: external_exports.number().int().nonnegative(),
18131
- passed: external_exports.number().int().nonnegative(),
18132
- failed: external_exports.number().int().nonnegative()
18133
- }),
18134
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18135
- // `validator`), so they silently never match. Surfaced so an author is not
18136
- // misled by a green run that actually skipped a rule.
18137
- unsupportedRuleIds: external_exports.array(external_exports.string())
18138
- }).meta({ id: "TestRulesResponse" });
18139
-
18140
18191
  // ../../packages/schema/src/zod/security.ts
18141
18192
  var SeveritySummaryItem = external_exports.object({
18142
18193
  severity: Severity,
@@ -18234,10 +18285,22 @@ var TopSourcesQuery = external_exports.object({
18234
18285
  // Omit for both kinds.
18235
18286
  kind: external_exports.enum(SOURCE_KINDS).optional()
18236
18287
  });
18237
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18288
+ var Provider = Harness.extract([
18289
+ "ClaudeCode",
18290
+ "Cursor",
18291
+ "Codex",
18292
+ "Antigravity",
18293
+ "ClaudeAi",
18294
+ "ChatGpt",
18295
+ "Copilot",
18296
+ "Api"
18297
+ ]).meta({ id: "Provider" });
18238
18298
  var ScanCoverageProvider = external_exports.object({
18239
18299
  provider: Provider,
18240
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18300
+ // Percent of that provider's traffic the shipped capture surface reaches.
18301
+ // A curated business fact, constant across every `range` — not a measured
18302
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18303
+ // above the block for where these numbers are decided.
18241
18304
  coverage: external_exports.number().int().min(0).max(100),
18242
18305
  supported: external_exports.boolean()
18243
18306
  }).meta({ id: "ScanCoverageProvider" });
@@ -18291,6 +18354,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18291
18354
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18292
18355
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18293
18356
 
18357
+ // ../../packages/schema/src/zod/settings-action.ts
18358
+ var SaveSettingsInput = external_exports.object({
18359
+ historicalAccess: external_exports.string(),
18360
+ modelJudgeConsent: external_exports.boolean(),
18361
+ vaultConsent: external_exports.string(),
18362
+ vaultInlineReveal: external_exports.string()
18363
+ });
18364
+ var AttachInput = external_exports.object({
18365
+ endpoint: external_exports.string(),
18366
+ label: external_exports.string().optional()
18367
+ });
18368
+
18294
18369
  // ../../packages/schema/src/zod/triage.ts
18295
18370
  var TriageHit = external_exports.object({
18296
18371
  ruleId: external_exports.string(),
@@ -18305,7 +18380,7 @@ var TriageHit = external_exports.object({
18305
18380
  valueFingerprint: external_exports.string().optional(),
18306
18381
  keyVersion: external_exports.number().int().nonnegative().optional()
18307
18382
  });
18308
- var TriagePolicy = BuiltinPolicyId;
18383
+ var TriagePolicy = CategoryPolicyId;
18309
18384
  var TriageCategoryRec = external_exports.object({
18310
18385
  category: DetectionCategory,
18311
18386
  action: TriagePolicy,
@@ -18512,8 +18587,11 @@ function chmodBestEffort(path, mode) {
18512
18587
  function tightenDir(dir) {
18513
18588
  chmodBestEffort(dir, DATA_DIR_MODE);
18514
18589
  }
18590
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18591
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18592
+ }
18515
18593
  function ensureDataDirSync(dir) {
18516
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18594
+ mkdirOwnerOnlySync(dir, true);
18517
18595
  tightenDir(dir);
18518
18596
  }
18519
18597
  function dbSidecars(file2) {
@@ -18525,6 +18603,26 @@ function tightenFile(file2) {
18525
18603
  function tightenPerms(file2) {
18526
18604
  for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
18527
18605
  }
18606
+ function writeExclusiveOwnerOnlySync(file2, data) {
18607
+ writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
18608
+ }
18609
+ function writeOwnerOnlyFileSync(file2, data) {
18610
+ const tmp = `${file2}.${String(process.pid)}.tmp`;
18611
+ try {
18612
+ rmSync(tmp, { force: true });
18613
+ } catch {
18614
+ }
18615
+ try {
18616
+ writeExclusiveOwnerOnlySync(tmp, data);
18617
+ renameSync(tmp, file2);
18618
+ } finally {
18619
+ try {
18620
+ rmSync(tmp, { force: true });
18621
+ } catch {
18622
+ }
18623
+ }
18624
+ tightenFile(file2);
18625
+ }
18528
18626
  function classifyOccupant(file2) {
18529
18627
  try {
18530
18628
  if (lstatSync(file2).isSymbolicLink()) return { kind: "symlink" };
@@ -18553,7 +18651,7 @@ function createOwnerOnlyFileSync(file2, data) {
18553
18651
  }
18554
18652
  let created;
18555
18653
  try {
18556
- writeFileSync(tmp, data, { mode: DATA_FILE_MODE, flag: "wx" });
18654
+ writeExclusiveOwnerOnlySync(tmp, data);
18557
18655
  created = publishByLink(tmp, file2, data);
18558
18656
  } finally {
18559
18657
  try {
@@ -18575,7 +18673,7 @@ function publishByLink(tmp, file2, data) {
18575
18673
  if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
18576
18674
  }
18577
18675
  try {
18578
- writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
18676
+ writeExclusiveOwnerOnlySync(file2, data);
18579
18677
  return true;
18580
18678
  } catch (err) {
18581
18679
  if (err.code === "EEXIST") return false;
@@ -18588,6 +18686,25 @@ function backupPath(file2, tag) {
18588
18686
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18589
18687
  }
18590
18688
  var STALE_PARTIAL_MS = 5 * 6e4;
18689
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18690
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18691
+ var SNAPSHOT_STAGING_COPY = "copy";
18692
+ function createSnapshotStaging(backup) {
18693
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18694
+ rmSync2(stage, { recursive: true, force: true });
18695
+ mkdirOwnerOnlySync(stage);
18696
+ tightenDir(stage);
18697
+ return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
18698
+ }
18699
+ function idleMs(entry) {
18700
+ for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
18701
+ try {
18702
+ return Date.now() - statSync(candidate).mtimeMs;
18703
+ } catch {
18704
+ }
18705
+ }
18706
+ return null;
18707
+ }
18591
18708
  function reapStalePartials(file2) {
18592
18709
  const dir = dirname(file2);
18593
18710
  const prefix = `${basename(file2)}.`;
@@ -18598,30 +18715,34 @@ function reapStalePartials(file2) {
18598
18715
  return;
18599
18716
  }
18600
18717
  for (const name of entries) {
18601
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18602
- const partial2 = join(dir, name);
18718
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18719
+ const staging = join(dir, name);
18603
18720
  try {
18604
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18605
- rmSync2(partial2, { force: true });
18721
+ const idle = idleMs(staging);
18722
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18723
+ rmSync2(staging, { recursive: true, force: true });
18606
18724
  }
18607
18725
  } catch {
18608
18726
  }
18609
18727
  }
18610
18728
  }
18611
18729
  function snapshotStore(db, backup) {
18612
- const partial2 = `${backup}.partial`;
18730
+ const { stage, copy } = createSnapshotStaging(backup);
18613
18731
  try {
18614
- rmSync2(partial2, { force: true });
18615
- db.prepare("VACUUM INTO ?").run(partial2);
18616
- tightenFile(partial2);
18617
- renameSync2(partial2, backup);
18732
+ db.prepare("VACUUM INTO ?").run(copy);
18733
+ tightenFile(copy);
18734
+ renameSync2(copy, backup);
18618
18735
  } catch (error51) {
18619
18736
  try {
18620
- rmSync2(partial2, { force: true });
18737
+ rmSync2(stage, { recursive: true, force: true });
18621
18738
  } catch {
18622
18739
  }
18623
18740
  throw error51;
18624
18741
  }
18742
+ try {
18743
+ rmSync2(stage, { recursive: true, force: true });
18744
+ } catch {
18745
+ }
18625
18746
  }
18626
18747
  function moveStoreAside(file2, backup) {
18627
18748
  const undo = [];
@@ -19349,9 +19470,10 @@ function safeParseStringArray(raw) {
19349
19470
  const parsed = safeJson(raw, null);
19350
19471
  return Array.isArray(parsed) ? parsed : [];
19351
19472
  }
19473
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19352
19474
  function toHarness(raw) {
19353
19475
  const parsed = Harness.safeParse(raw);
19354
- return parsed.success ? parsed.data : "claudecode";
19476
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19355
19477
  }
19356
19478
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19357
19479
  if (row.status) {
@@ -19482,7 +19604,7 @@ var SqliteActivityRepository = class {
19482
19604
  const params = [];
19483
19605
  if (query.harness && query.harness.length > 0) {
19484
19606
  conditions.push(
19485
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19607
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19486
19608
  );
19487
19609
  params.push(...query.harness);
19488
19610
  }
@@ -19689,13 +19811,13 @@ var SqliteActivityRepository = class {
19689
19811
  * The DISTINCT harnesses that actually have sessions (optionally within a
19690
19812
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19691
19813
  * present rather than the full enum. Each stored value is normalized through
19692
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19693
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19814
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19815
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19694
19816
  */
19695
19817
  harnessFacets(fromMs) {
19696
19818
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19697
19819
  const stmt = this.db.prepare(
19698
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19820
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19699
19821
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19700
19822
  );
19701
19823
  const rows = allRows(
@@ -19909,8 +20031,8 @@ var SqliteAuditEventsRepository = class {
19909
20031
  }
19910
20032
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19911
20033
  // (which takes a caller-supplied random id), the id here is MINTED internally
19912
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19913
- // sibling local-store ids (the local store is single-tenant). The deterministic
20034
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
20035
+ // session and message alone, like the sibling local-store ids. The deterministic
19914
20036
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19915
20037
  // AND converge a streaming partial/final split across two incremental passes:
19916
20038
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20875,6 +20997,42 @@ var SqliteFindingsRepository = class {
20875
20997
  this.db = db;
20876
20998
  }
20877
20999
  db;
21000
+ /**
21001
+ * The newest `limit` findings, newest first.
21002
+ *
21003
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
21004
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
21005
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
21006
+ * not on the driving table: SQLite sorts every finding in the store through a
21007
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
21008
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
21009
+ * rather than a constant.
21010
+ *
21011
+ * What it takes to make `started_at` order come out of an index instead:
21012
+ *
21013
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
21014
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
21015
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
21016
+ * satisfying a global `started_at` order across them needs a range merge
21017
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
21018
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
21019
+ * filters the type per row, which lets the LIMIT stop the scan early.
21020
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
21021
+ * purely to stop the tables being reordered. With plain JOINs the planner
21022
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
21023
+ * unary plus ALONE recovers almost none of the win. Both are needed.
21024
+ *
21025
+ * Neither is a micro-optimisation that a later reader should tidy away, and
21026
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
21027
+ * the temp B-tree comes back.
21028
+ *
21029
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
21030
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
21031
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
21032
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
21033
+ * a store whose recent captures carry no findings at all, where the scan walks
21034
+ * the whole index; that is still no worse than the full sort it replaced.
21035
+ */
20878
21036
  recentFindings(opts) {
20879
21037
  const limit = opts?.limit ?? 50;
20880
21038
  const rows = allRows(
@@ -20883,10 +21041,10 @@ var SqliteFindingsRepository = class {
20883
21041
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20884
21042
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20885
21043
  e.event_type AS kind
20886
- FROM inspection_findings f
20887
- JOIN audit_events e ON e.id = f.audit_event_id
20888
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20889
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
21044
+ FROM audit_events e
21045
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
21046
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
21047
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20890
21048
  ORDER BY e.started_at DESC, f.rowid DESC
20891
21049
  LIMIT :limit`
20892
21050
  ),
@@ -21530,7 +21688,8 @@ var SqliteInspectionDefinitionsRepository = class {
21530
21688
  }
21531
21689
  db;
21532
21690
  insertStmt;
21533
- // Idempotent upsert; returns the content-addressed definition id.
21691
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21692
+ // present keeps the stored row untouched — see the class doc.
21534
21693
  upsert(input) {
21535
21694
  const id = inspectionDefinitionId(input.ruleId, input.version);
21536
21695
  const row = toInspectionDefinitionRow(input, id);
@@ -21674,11 +21833,23 @@ function isParseableBinaryVersion(version2) {
21674
21833
 
21675
21834
  // ../../packages/persistence/src/repositories/installed-packs.ts
21676
21835
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21836
+ function printableRuleId(entry) {
21837
+ if (typeof entry !== "object" || entry === null) return null;
21838
+ const candidate = entry.id;
21839
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21840
+ }
21841
+ function firstIssueReason(error51) {
21842
+ const issue2 = error51.issues[0];
21843
+ if (!issue2) return "unknown";
21844
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21845
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21846
+ }
21847
+ var REJECTED_RULE_DETAIL_CAP = 10;
21677
21848
  function inventorySignature(packs2) {
21678
21849
  return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21679
21850
  }
21680
21851
  function hashRules(rulesJson) {
21681
- return createHash2("sha1").update(rulesJson).digest("hex");
21852
+ return createHash2("sha256").update(rulesJson).digest("hex");
21682
21853
  }
21683
21854
  function parseVersion(v) {
21684
21855
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21890,10 +22061,21 @@ var SqliteInstalledPacksRepository = class {
21890
22061
  * (all detection off) instead of falling back to the bundled packs. Every
21891
22062
  * JSON-level failure therefore counts as invalid.
21892
22063
  */
22064
+ /**
22065
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
22066
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
22067
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
22068
+ * winner is whatever order SQLite happens to return, which makes a collision
22069
+ * resolve differently on two machines holding identical stores. Ordering by
22070
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
22071
+ */
21893
22072
  installedRuleset() {
21894
22073
  const rows = allRows(
21895
22074
  this.db.prepare(
21896
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
22075
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
22076
+ namespace, pack_id AS packId
22077
+ FROM installed_packs
22078
+ ORDER BY namespace, pack_id`
21897
22079
  )
21898
22080
  );
21899
22081
  const out = {
@@ -21901,22 +22083,32 @@ var SqliteInstalledPacksRepository = class {
21901
22083
  enabledPacks: 0,
21902
22084
  rules: [],
21903
22085
  invalidRules: 0,
22086
+ rejectedRules: [],
21904
22087
  ruleActions: /* @__PURE__ */ new Map(),
21905
- ruleVersions: /* @__PURE__ */ new Map()
22088
+ ruleVersions: /* @__PURE__ */ new Map(),
22089
+ reversibleRules: /* @__PURE__ */ new Set()
22090
+ };
22091
+ const reject = (pack, ruleId, reason) => {
22092
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22093
+ out.rejectedRules.push({ pack, ruleId, reason });
21906
22094
  };
21907
22095
  for (const row of rows) {
21908
22096
  if (!intToBool(row.enabled)) continue;
21909
22097
  out.enabledPacks += 1;
21910
22098
  const action = policyIdToAction(row.policyId);
22099
+ const reversible = policyIdIsReversible(row.policyId);
22100
+ const pack = `${row.namespace}/${row.packId}`;
21911
22101
  let raw;
21912
22102
  try {
21913
22103
  raw = JSON.parse(row.rulesJson);
21914
22104
  } catch {
21915
22105
  out.invalidRules += 1;
22106
+ reject(pack, null, "rules_json: malformed JSON");
21916
22107
  continue;
21917
22108
  }
21918
22109
  if (!Array.isArray(raw)) {
21919
22110
  out.invalidRules += 1;
22111
+ reject(pack, null, "rules_json: not an array");
21920
22112
  continue;
21921
22113
  }
21922
22114
  for (const entry of raw) {
@@ -21925,7 +22117,12 @@ var SqliteInstalledPacksRepository = class {
21925
22117
  out.rules.push(parsed.data);
21926
22118
  out.ruleActions.set(parsed.data.id, action);
21927
22119
  out.ruleVersions.set(parsed.data.id, row.version);
21928
- } else out.invalidRules += 1;
22120
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22121
+ else out.reversibleRules.delete(parsed.data.id);
22122
+ } else {
22123
+ out.invalidRules += 1;
22124
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22125
+ }
21929
22126
  }
21930
22127
  }
21931
22128
  return out;
@@ -22119,31 +22316,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22119
22316
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22120
22317
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22121
22318
  var HARNESS_LABELS = {
22122
- claudecode: "Claude Code",
22123
- cursor: "Cursor",
22124
- codex: "Codex",
22125
- antigravity: "Antigravity"
22319
+ [HARNESS.ClaudeCode]: "Claude Code",
22320
+ [HARNESS.Cursor]: "Cursor",
22321
+ [HARNESS.Codex]: "Codex",
22322
+ [HARNESS.Antigravity]: "Antigravity"
22126
22323
  };
22127
22324
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22128
22325
  var EMPTY_PROJECT_AGG = {
22129
22326
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22130
22327
  findingsCount: 0
22131
22328
  };
22329
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22330
+ var TITLE_NEEDLES = {
22331
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22332
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22333
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22334
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22335
+ };
22132
22336
  function resolveHarnessId(attrs, row) {
22133
22337
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22134
22338
  return attrs.provider;
22135
22339
  }
22136
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22137
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22138
- if (t.includes("cursor")) return "cursor";
22139
- if (t.includes("codex")) return "codex";
22140
- if (t.includes("antigravity")) return "antigravity";
22340
+ const t = stripSeparators(row.title ?? "");
22341
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22342
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22343
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22344
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22141
22345
  return null;
22142
22346
  }
22143
22347
  function isLiveRealClaudeCode(rows) {
22144
22348
  return rows.some((r) => {
22145
22349
  const attrs = safeJson(r.attributes, {});
22146
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22350
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22147
22351
  });
22148
22352
  }
22149
22353
  function toAssetSummary(row) {
@@ -22411,7 +22615,7 @@ var SqliteInventoryAssetsRepository = class {
22411
22615
  const isRealHarness = rows.some(
22412
22616
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22413
22617
  );
22414
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22618
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22415
22619
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22416
22620
  if (q && assets.length === 0) continue;
22417
22621
  const firstRow = rows[0];
@@ -23085,9 +23289,10 @@ var SqliteProjectFilesRepository = class {
23085
23289
  // ../../packages/persistence/src/repositories/resolutions.ts
23086
23290
  import { randomUUID as randomUUID7 } from "crypto";
23087
23291
  var SqliteResolutionsRepository = class {
23088
- constructor(db, now = () => Date.now()) {
23292
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23089
23293
  this.db = db;
23090
23294
  this.now = now;
23295
+ this.newId = newId;
23091
23296
  this.insertStmt = db.prepare(
23092
23297
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23093
23298
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23120,12 +23325,14 @@ var SqliteResolutionsRepository = class {
23120
23325
  }
23121
23326
  db;
23122
23327
  now;
23328
+ newId;
23123
23329
  insertStmt;
23124
23330
  latestStmt;
23125
23331
  openAtRestStmt;
23126
23332
  resolvedAtRestStmt;
23127
23333
  /**
23128
- * Insert one disposition row. The repo mints the id and stamps created_at.
23334
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23335
+ * both through the constructor's injectable seams.
23129
23336
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23130
23337
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23131
23338
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23137,7 +23344,7 @@ var SqliteResolutionsRepository = class {
23137
23344
  */
23138
23345
  insertResolution(r) {
23139
23346
  this.insertStmt.run({
23140
- id: randomUUID7(),
23347
+ id: this.newId(),
23141
23348
  findingKey: r.findingKey,
23142
23349
  status: FindingStatus.parse(r.status),
23143
23350
  method: ResolutionMethod.parse(r.method),
@@ -23716,16 +23923,16 @@ var ACTION_TO_KIND = {
23716
23923
  warn: "warned"
23717
23924
  };
23718
23925
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23719
- var SCAN_COVERAGE = [
23720
- { provider: "claudecode", coverage: 100, supported: true },
23721
- { provider: "cursor", coverage: 0, supported: false },
23722
- { provider: "codex", coverage: 80, supported: true },
23723
- { provider: "antigravity", coverage: 60, supported: true },
23724
- { provider: "claudeai", coverage: 0, supported: false },
23725
- { provider: "chatgpt", coverage: 0, supported: false },
23726
- { provider: "copilot", coverage: 0, supported: false },
23727
- { provider: "api", coverage: 0, supported: false }
23728
- ];
23926
+ var SCAN_COVERAGE = {
23927
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
23928
+ [HARNESS.Api]: { coverage: 0, supported: false },
23929
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23930
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23931
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23932
+ [HARNESS.Codex]: { coverage: 80, supported: true },
23933
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
23934
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
23935
+ };
23729
23936
  var GRANULARITY = {
23730
23937
  "7d": "day",
23731
23938
  "30d": "day",
@@ -23824,9 +24031,22 @@ var SqliteSecurityRepository = class {
23824
24031
  return Promise.resolve({ total, needsRemediation, bySeverity });
23825
24032
  }
23826
24033
  // Range is echoed but does not change the result today — coverage is a constant
23827
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
24034
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
24035
+ // comes from Provider.options (the enum's declaration order), not from
24036
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
24037
+ // leave key order unspecified (ES2015 guarantees insertion order for these
24038
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
24039
+ // reliable too). The reason is the schema comment's promise: the returned
24040
+ // order must mirror the generated OpenAPI enum list, which is Provider's
24041
+ // contract, not this table's.
23828
24042
  scanCoverage(range) {
23829
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
24043
+ return Promise.resolve({
24044
+ range,
24045
+ providers: Provider.options.map((provider) => ({
24046
+ provider,
24047
+ ...SCAN_COVERAGE[provider]
24048
+ }))
24049
+ });
23830
24050
  }
23831
24051
  enforcementActions(range) {
23832
24052
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23884,10 +24104,10 @@ var SqliteSecurityRepository = class {
23884
24104
  // count; a superseding open/redetected row means the finding is not
23885
24105
  // remediated and is excluded, same invariant as severitySummary. Legacy
23886
24106
  // at-rest findings with finding_key IS NULL can never have a resolution row
23887
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23888
- // outright. One raw-row query (fetch every trackable finding + its latest
23889
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23890
- // mirroring this file's other methods.
24107
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24108
+ // set below. One raw-row query (fetch the findings with resolution activity in
24109
+ // the window + each one's latest resolution status/method/resolved_at) +
24110
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23891
24111
  mttrTrend(range) {
23892
24112
  const granularity = granularityFor(range);
23893
24113
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23903,29 +24123,80 @@ var SqliteSecurityRepository = class {
23903
24123
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23904
24124
  // COALESCE onto the parent event's started_at defends against any
23905
24125
  // legacy/edge row the backfill left null.
23906
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24126
+ `SELECT DISTINCT f.finding_key AS finding_key,
24127
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23907
24128
  latest.status AS latest_status,
23908
24129
  latest.method AS latest_method,
23909
24130
  latest.resolved_at AS latest_resolved_at
23910
- FROM inspection_findings f
23911
- JOIN audit_events e ON e.id = f.audit_event_id
23912
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24131
+ FROM finding_resolution fr
24132
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24133
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24134
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23913
24135
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23914
24136
  ON latest.finding_key = f.finding_key
23915
- WHERE f.finding_key IS NOT NULL
23916
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23917
- AND EXISTS (
23918
- SELECT 1 FROM finding_resolution fr
23919
- WHERE fr.finding_key = f.finding_key
23920
- AND fr.resolved_at >= :windowStart
23921
- )`
23922
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23923
- // any resolution activity at/after the window start — a row this method
24137
+ WHERE fr.resolved_at >= :windowStart
24138
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24139
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23924
24140
  // ultimately counts has its LATEST resolution inside the window, which
23925
- // implies such a row exists, so nothing wanted is dropped. The exact
23926
- // latest-wins + status/method + window gate stays in JS below,
23927
- // dialect-agnostic. Without this, a
23928
- // 7d request evaluated the store's entire trackable-findings history.
24141
+ // implies a resolution row at/after the window start exists, so nothing
24142
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24143
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24144
+ // implied rather than dropped — the join key comes from
24145
+ // finding_resolution, whose finding_key is NOT NULL.
24146
+ //
24147
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24148
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24149
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24150
+ // event_type, joined every capture event to its findings, and evaluated
24151
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24152
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24153
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24154
+ // both cases returning rows for a window holding a fraction of it.
24155
+ //
24156
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24157
+ // stating precisely, because the obvious reading (both are needed for the
24158
+ // speed) is wrong and was measured to be wrong:
24159
+ //
24160
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24161
+ // keyword is semantically identical to JOIN and exists only to stop the
24162
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24163
+ // the outside, because with no ANALYZE statistics it prices
24164
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24165
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24166
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24167
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24168
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24169
+ // range had none. It buys NO flatness in store size: remove it and the
24170
+ // ratio above does not move, because the latest-resolution derived
24171
+ // table already passes over the whole of finding_resolution, so this
24172
+ // read is O(resolutions) either way and resolutions are not the store.
24173
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24174
+ // — no hot read may pass over a table with no index — and that is the
24175
+ // guard that goes red when it is dropped. Neither test catches the
24176
+ // other's defect.
24177
+ //
24178
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24179
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24180
+ // redetected and fixed again carries several rows inside one window and
24181
+ // matches once per row — and the value below is a MEAN, so a key matched
24182
+ // three times is a key weighted three times.
24183
+ //
24184
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24185
+ // recording. Duplicate rows for ONE key are identical (every projected
24186
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24187
+ // preserved), so sums and counts scale together and that key's own mean
24188
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24189
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24190
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24191
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24192
+ // it has inside the window" — which needed a fixture built for it, since no
24193
+ // single-key case can show it.
24194
+ //
24195
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24196
+ // by value tuple. On the other columns alone, two genuinely different
24197
+ // findings sharing a severity, a first-detection event and a resolution
24198
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24199
+ // collapsing them would under-count in the other direction.
23929
24200
  ),
23930
24201
  { windowStart }
23931
24202
  );
@@ -23983,16 +24254,47 @@ var SqliteSecurityRepository = class {
23983
24254
  }
23984
24255
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23985
24256
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23986
- // same latest-resolution-wins correlated subquery as severitySummary /
23987
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23988
- // resolution row for a key rather than just its current disposition). A key
23989
- // whose latest row is a superseding 'open'/'redetected' row (the same
23990
- // secret came back) is excluded — it is not currently resolved. Legacy
23991
- // at-rest findings with finding_key IS NULL are excluded outright (the
23992
- // resolution lifecycle can never attach to them). Path comes from the
23993
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23994
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23995
- // DESC, capped at `limit`.
24257
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24258
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24259
+ // a key rather than just its current disposition). A key whose latest row is
24260
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24261
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24262
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24263
+ // never attach to them). Path comes from the finding's parent event
24264
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24265
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24266
+ //
24267
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24268
+ // the plan rather than a preference. Written the other way round — driving
24269
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24270
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24271
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24272
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24273
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24274
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24275
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24276
+ // because an empty finding_resolution table makes the inner side empty and the
24277
+ // cross product collapses to nothing — so the shape is only observable on a
24278
+ // corpus that seeds resolutions.
24279
+ //
24280
+ // Driving from `latest` instead makes every step below it a unique-index or
24281
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24282
+ // so the cost is the derived table's own — linear in resolutions, which is
24283
+ // what this feed is legitimately about.
24284
+ //
24285
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24286
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24287
+ // exists only to stop the optimizer reordering the tables. Written as plain
24288
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24289
+ // ANALYZE statistics to price the alternatives with, so it takes
24290
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24291
+ // cross product. The FROM order alone was measured to change the plan not at
24292
+ // all.
24293
+ //
24294
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24295
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24296
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24297
+ // reading as though the findings side could drive.
23996
24298
  recentlyResolved(limit = 20) {
23997
24299
  const rows = allRows(
23998
24300
  this.db.prepare(
@@ -24002,17 +24304,15 @@ var SqliteSecurityRepository = class {
24002
24304
  json_extract(e.attributes, '$.file_path') AS path,
24003
24305
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
24004
24306
  latest.resolved_at AS latest_resolved_at
24005
- FROM inspection_findings f
24006
- JOIN audit_events e ON e.id = f.audit_event_id
24007
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24008
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24009
- ON latest.finding_key = f.finding_key
24010
- WHERE e.event_type = 'code_change'
24011
- AND f.finding_key IS NOT NULL
24012
- AND latest.status = 'resolved'
24307
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24308
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24309
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24310
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24311
+ WHERE latest.status = 'resolved'
24013
24312
  AND latest.method = 'fixed-at-source'
24014
24313
  AND latest.resolved_at IS NOT NULL
24015
- ORDER BY latest_resolved_at DESC
24314
+ AND e.event_type = 'code_change'
24315
+ ORDER BY latest.resolved_at DESC
24016
24316
  LIMIT :limit`
24017
24317
  ),
24018
24318
  { limit }
@@ -24949,6 +25249,7 @@ function openAndInitialize(file2) {
24949
25249
  function openLocalDatabase(dir) {
24950
25250
  ensureDataDirSync(dir);
24951
25251
  const file2 = join2(dir, DB_FILENAME);
25252
+ reapStalePartials(file2);
24952
25253
  const {
24953
25254
  db,
24954
25255
  events,
@@ -25376,11 +25677,79 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25376
25677
  }
25377
25678
  }
25378
25679
 
25379
- // ../../packages/persistence/src/settings.ts
25680
+ // ../../packages/persistence/src/managed-settings.ts
25380
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 overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25711
+ if (!managed) return settings;
25712
+ const { values } = managed;
25713
+ const merged = { ...settings };
25714
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25715
+ if (values.controlPlane !== void 0) {
25716
+ merged.controlPlane = {
25717
+ ...values.controlPlane,
25718
+ // The administrator pinned WHICH deployment, not WHEN this machine
25719
+ // joined it. Keep the user's own attach time when the endpoint is
25720
+ // unchanged, so a managed machine does not appear to re-attach on every
25721
+ // read; stamp a fresh one when the administrator moved it.
25722
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25723
+ };
25724
+ }
25725
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25726
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25727
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25728
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25729
+ if (values.vaultConsent !== void 0) {
25730
+ merged.vaultConsent = values.vaultConsent ? (
25731
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25732
+ // at the current version otherwise.
25733
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25734
+ ) : void 0;
25735
+ }
25736
+ if (values.modelJudgeConsent !== void 0) {
25737
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25738
+ acknowledgedAt: now().toISOString(),
25739
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25740
+ } : void 0;
25741
+ }
25742
+ return merged;
25743
+ }
25744
+
25745
+ // ../../packages/persistence/src/settings.ts
25746
+ import { readFileSync as readFileSync4 } from "fs";
25381
25747
  import { join as join5 } from "path";
25382
25748
  var SETTINGS_FILENAME = "settings.json";
25383
25749
  function readWorkspaceSettings(base = defaultDataDir()) {
25750
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25751
+ }
25752
+ function readUserSettings(base) {
25384
25753
  const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25385
25754
  if (!record2) return defaultWorkspaceSettings();
25386
25755
  try {
@@ -25392,7 +25761,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25392
25761
  function readJson(file2) {
25393
25762
  let text;
25394
25763
  try {
25395
- text = readFileSync3(file2, "utf8");
25764
+ text = readFileSync4(file2, "utf8");
25396
25765
  } catch {
25397
25766
  return null;
25398
25767
  }
@@ -25510,15 +25879,7 @@ function formatPointer(category, keyVersion, pointerId, tag) {
25510
25879
  // ../../packages/persistence/src/vault/key-provider.ts
25511
25880
  import { execFileSync } from "child_process";
25512
25881
  import { randomBytes as randomBytes2 } from "crypto";
25513
- import {
25514
- chmodSync as chmodSync2,
25515
- mkdirSync as mkdirSync2,
25516
- readFileSync as readFileSync4,
25517
- renameSync as renameSync4,
25518
- rmSync as rmSync4,
25519
- statSync as statSync3,
25520
- writeFileSync as writeFileSync3
25521
- } from "fs";
25882
+ import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
25522
25883
  import { join as join6 } from "path";
25523
25884
  var VAULT_OCCUPANT_REASON = {
25524
25885
  symlink: "the path is a symlink; remove it so a keyring can be created",
@@ -25611,7 +25972,8 @@ var LOCK_OWNER_FILE = "owner";
25611
25972
  var ROTATION_IN_PROGRESS = "vault: a key rotation is already in progress";
25612
25973
  function claimRotationLock(lock, owner) {
25613
25974
  try {
25614
- mkdirSync2(lock);
25975
+ mkdirOwnerOnlySync(lock);
25976
+ tightenDir(lock);
25615
25977
  } catch (err) {
25616
25978
  if (err.code === "EEXIST") return false;
25617
25979
  throw asError(err);
@@ -25653,7 +26015,7 @@ function acquireRotationLock(keysDir2) {
25653
26015
  }
25654
26016
  function releaseRotationLock(lease) {
25655
26017
  try {
25656
- if (readFileSync4(join6(lease.lock, LOCK_OWNER_FILE), "utf8").trim() !== lease.owner) return;
26018
+ if (readFileSync5(join6(lease.lock, LOCK_OWNER_FILE), "utf8").trim() !== lease.owner) return;
25657
26019
  } catch {
25658
26020
  return;
25659
26021
  }
@@ -25704,7 +26066,7 @@ var FileKeyProvider = class {
25704
26066
  #read() {
25705
26067
  let raw;
25706
26068
  try {
25707
- raw = readFileSync4(this.filePath, "utf8");
26069
+ raw = readFileSync5(this.filePath, "utf8");
25708
26070
  } catch (err) {
25709
26071
  if (err.code === "ENOENT") return null;
25710
26072
  throw err instanceof Error ? err : new Error(String(err));
@@ -25743,15 +26105,19 @@ var FileKeyProvider = class {
25743
26105
  * Atomic tmp + rename so a crash mid-write cannot truncate the keyring.
25744
26106
  * Used only for rotation, under the rotation lock — first creation goes
25745
26107
  * through the creation-exclusive path instead.
26108
+ *
26109
+ * Delegated to the shared owner-only write rather than spelled here, so the
26110
+ * create mode this file is published at is the one paths.ts owns and tests
26111
+ * directly. A local copy of the pair was a second place the mode could be
26112
+ * dropped with the trailing tighten still repairing the end state, which is
26113
+ * the shape no assertion on a published file can see. It also picks up that
26114
+ * primitive's per-process tmp name, its stale-tmp sweep, and an exclusive
26115
+ * create that refuses to follow a symlink planted at the tmp path.
25746
26116
  */
25747
26117
  #write(keyring) {
25748
26118
  ensureDataDirSync(this.#keysDir);
25749
- const file2 = this.filePath;
25750
- const tmp = `${file2}.tmp`;
25751
- writeFileSync3(tmp, `${serializeKeyring(keyring)}
25752
- `, { mode: DATA_FILE_MODE });
25753
- renameSync4(tmp, file2);
25754
- tightenFileMode(file2);
26119
+ writeOwnerOnlyFileSync(this.filePath, `${serializeKeyring(keyring)}
26120
+ `);
25755
26121
  return keyring;
25756
26122
  }
25757
26123
  };
@@ -25761,15 +26127,73 @@ function tightenFileMode(file2) {
25761
26127
  } catch {
25762
26128
  }
25763
26129
  }
25764
- var runSecurity = (args) => execFileSync("/usr/bin/security", args, {
26130
+ var SECURITY_TIMEOUT_MS = 5e3;
26131
+ var runSecurity = (args, stdin) => execFileSync("/usr/bin/security", args, {
25765
26132
  encoding: "utf8",
25766
- stdio: ["ignore", "pipe", "ignore"]
26133
+ input: stdin,
26134
+ timeout: SECURITY_TIMEOUT_MS,
26135
+ // stderr is discarded rather than captured, and that is deliberate: a
26136
+ // captured stream rides out on an execFileSync error's `.stderr`, and the
26137
+ // write paths here carry the keyring. Exit status is the only thing any
26138
+ // branch below reads.
26139
+ stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "ignore"]
25767
26140
  });
25768
26141
  var SECURITY_ITEM_NOT_FOUND = 44;
26142
+ function corruptReason(err) {
26143
+ if (err instanceof SyntaxError) return "malformed JSON";
26144
+ return err instanceof Error ? err.message : "unknown";
26145
+ }
26146
+ function securityFailureMeta(err) {
26147
+ const e = err;
26148
+ const parts = [];
26149
+ if (typeof e.status === "number") parts.push(`exit ${String(e.status)}`);
26150
+ if (typeof e.signal === "string" && e.signal) parts.push(`signal ${e.signal}`);
26151
+ if (typeof e.code === "string" && e.code) parts.push(e.code);
26152
+ return parts.length > 0 ? parts.join(", ") : "unknown error";
26153
+ }
26154
+ function writeCommand(keyring, update, keychain) {
26155
+ const hex3 = Buffer.from(serializeKeyring(keyring), "utf8").toString("hex");
26156
+ const parts = [
26157
+ "add-generic-password",
26158
+ ...update ? ["-U"] : [],
26159
+ "-s",
26160
+ KEYCHAIN_SERVICE,
26161
+ "-a",
26162
+ KEYCHAIN_ACCOUNT,
26163
+ "-X",
26164
+ hex3
26165
+ ];
26166
+ if (keychain !== void 0) {
26167
+ if (/['\\\n\r\0]/.test(keychain)) {
26168
+ throw new Error(
26169
+ "vault: keychain path contains a quote, backslash, line break or NUL, which security -i cannot carry intact"
26170
+ );
26171
+ }
26172
+ parts.push(`'${keychain}'`);
26173
+ }
26174
+ return `${parts.join(" ")}
26175
+ `;
26176
+ }
25769
26177
  var KeychainKeyProvider = class {
25770
26178
  #keysDir;
25771
26179
  #exec;
25772
- constructor(keysDir2, exec = runSecurity) {
26180
+ #keychain;
26181
+ /**
26182
+ * The trailing keychain argument, or nothing. Every subcommand used here
26183
+ * takes it last (`add-generic-password [keychain]`,
26184
+ * `find-generic-password [keychain...]`), and omitting it means the default
26185
+ * search list. Fixed at construction, so it is built once rather than per
26186
+ * call on the capture path.
26187
+ */
26188
+ #target;
26189
+ /**
26190
+ * `keychain` names the keychain to operate on, as `security`'s trailing
26191
+ * argument. Production passes nothing and gets the user's default keychain,
26192
+ * which is the whole point of the backend. A test driving the REAL binary
26193
+ * passes a throwaway one, because the alternative is writing vault key
26194
+ * material into the developer's own login keychain and leaving it there.
26195
+ */
26196
+ constructor(keysDir2, exec = runSecurity, keychain) {
25773
26197
  if (exec === runSecurity && process.platform !== "darwin") {
25774
26198
  throw new Error(
25775
26199
  `keychain custody is not available on this platform (${process.platform}); use file custody`
@@ -25777,6 +26201,8 @@ var KeychainKeyProvider = class {
25777
26201
  }
25778
26202
  this.#keysDir = keysDir2;
25779
26203
  this.#exec = exec;
26204
+ this.#keychain = keychain;
26205
+ this.#target = keychain === void 0 ? [] : [keychain];
25780
26206
  }
25781
26207
  /** Where a fallback file provider for the same vault would keep its keyring. */
25782
26208
  get keysDir() {
@@ -25815,18 +26241,22 @@ var KeychainKeyProvider = class {
25815
26241
  KEYCHAIN_SERVICE,
25816
26242
  "-a",
25817
26243
  KEYCHAIN_ACCOUNT,
25818
- "-w"
26244
+ "-w",
26245
+ ...this.#target
25819
26246
  ]);
25820
26247
  } catch (err) {
25821
26248
  if (err.status === SECURITY_ITEM_NOT_FOUND) return null;
25822
26249
  throw new Error(
25823
- `vault: keychain read failed (${err instanceof Error ? err.message : String(err)}); refusing to treat the failure as an absent keyring`,
25824
- { cause: err }
26250
+ `vault: keychain read failed (${securityFailureMeta(err)}); refusing to treat the failure as an absent keyring`
25825
26251
  );
25826
26252
  }
25827
26253
  const body = raw.trim();
25828
26254
  if (body.length === 0) return null;
25829
- return parseKeyring(body);
26255
+ try {
26256
+ return parseKeyring(body);
26257
+ } catch (err) {
26258
+ throw new Error(`vault: keychain item is not a usable keyring (${corruptReason(err)})`);
26259
+ }
25830
26260
  }
25831
26261
  /**
25832
26262
  * First mint: a plain `add-generic-password` (no `-U`) fails when an item
@@ -25834,37 +26264,25 @@ var KeychainKeyProvider = class {
25834
26264
  * keyring — the loser re-reads and adopts it instead.
25835
26265
  */
25836
26266
  #create(keyring) {
25837
- const args = [
25838
- "add-generic-password",
25839
- "-s",
25840
- KEYCHAIN_SERVICE,
25841
- "-a",
25842
- KEYCHAIN_ACCOUNT,
25843
- "-w",
25844
- serializeKeyring(keyring)
25845
- ];
26267
+ const line = writeCommand(keyring, false, this.#keychain);
25846
26268
  try {
25847
- this.#exec(args);
26269
+ this.#exec(["-i"], line);
25848
26270
  } catch (err) {
25849
26271
  const winner = this.#read();
25850
26272
  if (winner) return winner;
25851
- throw asError(err);
26273
+ throw new Error(`vault: keychain write failed (${securityFailureMeta(err)})`);
25852
26274
  }
25853
26275
  return keyring;
25854
26276
  }
25855
26277
  // `-U` updates the item in place, deliberately replacing the stored map with
25856
26278
  // one that contains it — used only for rotation, under the rotation lock.
25857
26279
  #replace(keyring) {
25858
- this.#exec([
25859
- "add-generic-password",
25860
- "-U",
25861
- "-s",
25862
- KEYCHAIN_SERVICE,
25863
- "-a",
25864
- KEYCHAIN_ACCOUNT,
25865
- "-w",
25866
- serializeKeyring(keyring)
25867
- ]);
26280
+ const line = writeCommand(keyring, true, this.#keychain);
26281
+ try {
26282
+ this.#exec(["-i"], line);
26283
+ } catch (err) {
26284
+ throw new Error(`vault: keychain write failed (${securityFailureMeta(err)})`);
26285
+ }
25868
26286
  return keyring;
25869
26287
  }
25870
26288
  };
@@ -26354,7 +26772,7 @@ function resolveProviderSafe(resolveProviderFn) {
26354
26772
  }
26355
26773
 
26356
26774
  // ../../packages/plugin-sdk/src/config-inventory.ts
26357
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
26775
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
26358
26776
  import { homedir as homedir2 } from "os";
26359
26777
  import { basename as basename3, join as join10 } from "path";
26360
26778
 
@@ -26930,6 +27348,40 @@ function escapeRegExp2(value) {
26930
27348
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26931
27349
  }
26932
27350
 
27351
+ // ../../packages/detections/src/regex-cache.ts
27352
+ var singles = /* @__PURE__ */ new WeakMap();
27353
+ var keywordLists = /* @__PURE__ */ new WeakMap();
27354
+ var labelLists = /* @__PURE__ */ new WeakMap();
27355
+ function listCache(kind) {
27356
+ return kind === "keyword" ? keywordLists : labelLists;
27357
+ }
27358
+ function memoizedRegExp(owner, build) {
27359
+ const cached2 = singles.get(owner);
27360
+ if (cached2 !== void 0) {
27361
+ cached2.lastIndex = 0;
27362
+ return cached2;
27363
+ }
27364
+ const compiled = build();
27365
+ singles.set(owner, compiled);
27366
+ return compiled;
27367
+ }
27368
+ function memoizedRegExpList(kind, owner, build) {
27369
+ const cache = listCache(kind);
27370
+ const cached2 = cache.get(owner);
27371
+ if (cached2 !== void 0) {
27372
+ if (cached2.stateful) {
27373
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
27374
+ }
27375
+ return cached2.entries;
27376
+ }
27377
+ const entries = build();
27378
+ cache.set(owner, {
27379
+ entries,
27380
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
27381
+ });
27382
+ return entries;
27383
+ }
27384
+
26933
27385
  // ../../packages/detections/src/matchers/limits.ts
26934
27386
  var MAX_MATCHES_PER_RULE = 1e4;
26935
27387
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -26940,10 +27392,17 @@ var KeywordMatcher2 = class {
26940
27392
  if (rule.matcher.type !== "keyword") return [];
26941
27393
  const { keywords, caseSensitive } = rule.matcher;
26942
27394
  const spans = [];
26943
- for (const kw of keywords) {
26944
- if (kw.length === 0) continue;
27395
+ const compiled = memoizedRegExpList(
27396
+ "keyword",
27397
+ rule.matcher,
27398
+ () => keywords.map((kw) => {
27399
+ if (kw.length === 0) return void 0;
27400
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
27401
+ })
27402
+ );
27403
+ for (const re of compiled) {
27404
+ if (re === void 0) continue;
26945
27405
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
26946
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26947
27406
  let m;
26948
27407
  while ((m = re.exec(text)) !== null) {
26949
27408
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -26959,7 +27418,10 @@ var RegexMatcher2 = class {
26959
27418
  match(text, rule) {
26960
27419
  if (rule.matcher.type !== "regex") return [];
26961
27420
  const { pattern, flags, captureGroup } = rule.matcher;
26962
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
27421
+ const re = memoizedRegExp(
27422
+ rule.matcher,
27423
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
27424
+ );
26963
27425
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
26964
27426
  const spans = [];
26965
27427
  let m;
@@ -27018,6 +27480,10 @@ function luhnCheck(digits) {
27018
27480
  // ../../packages/detections/src/engine.ts
27019
27481
  var keywordMatcher = new KeywordMatcher2();
27020
27482
  var regexMatcher = new RegexMatcher2();
27483
+ var MATCHERS = {
27484
+ keyword: (text, rule) => keywordMatcher.match(text, rule),
27485
+ regex: (text, rule) => regexMatcher.match(text, rule)
27486
+ };
27021
27487
  var packs = /* @__PURE__ */ new Map();
27022
27488
  var POST_VALIDATORS = {
27023
27489
  entropy: (value, config2) => isHighEntropy(value, numberOption(config2, "threshold"), numberOption(config2, "minLength")),
@@ -27033,8 +27499,7 @@ function passesPostValidators(rule, value) {
27033
27499
  for (const ref of validators) {
27034
27500
  const name = typeof ref === "string" ? ref : ref.name;
27035
27501
  const config2 = typeof ref === "string" ? void 0 : ref.config;
27036
- const validate = POST_VALIDATORS[name];
27037
- if (validate && !validate(value, config2)) return false;
27502
+ if (!POST_VALIDATORS[name](value, config2)) return false;
27038
27503
  }
27039
27504
  return true;
27040
27505
  }
@@ -27067,11 +27532,15 @@ function isCorroborated(candidate, candidates, text) {
27067
27532
  const labels = req.labels;
27068
27533
  if (labels && labels.length > 0) {
27069
27534
  const haystack = text.slice(Math.max(0, winStart), winEnd);
27070
- for (const label of labels) {
27071
- const trimmed = label.trim();
27072
- if (trimmed.length === 0) continue;
27073
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27074
- if (re.test(haystack)) return true;
27535
+ for (const re of memoizedRegExpList(
27536
+ "label",
27537
+ req,
27538
+ () => labels.map((label) => {
27539
+ const trimmed = label.trim();
27540
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
27541
+ })
27542
+ )) {
27543
+ if (re?.test(haystack)) return true;
27075
27544
  }
27076
27545
  }
27077
27546
  return false;
@@ -27091,14 +27560,7 @@ function scan(text, rules, context) {
27091
27560
  const candidates = [];
27092
27561
  for (const rule of ruleset) {
27093
27562
  if (!ruleApplies(rule, extension)) continue;
27094
- let spans;
27095
- if (rule.matcher.type === "keyword") {
27096
- spans = keywordMatcher.match(text, rule);
27097
- } else if (rule.matcher.type === "regex") {
27098
- spans = regexMatcher.match(text, rule);
27099
- } else {
27100
- continue;
27101
- }
27563
+ const spans = MATCHERS[rule.matcher.type](text, rule);
27102
27564
  for (const span of spans) {
27103
27565
  const rawMatch = text.slice(span.start, span.end);
27104
27566
  if (!passesPostValidators(rule, rawMatch)) continue;
@@ -27222,6 +27684,7 @@ var CONFIG_POSTURE_RULES = [
27222
27684
  ];
27223
27685
 
27224
27686
  // ../../packages/detections/src/security/redos-probe.ts
27687
+ var BUDGET_MS = 100;
27225
27688
  var EXPONENTIAL_UNITS = [
27226
27689
  "a",
27227
27690
  "0",
@@ -27245,6 +27708,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
27245
27708
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
27246
27709
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
27247
27710
  );
27711
+ var CPU_CORROBORATION_SHARE = 0.2;
27712
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
27248
27713
 
27249
27714
  // ../../rules/code-flaws/auth-jwt-no-verify.json
27250
27715
  var auth_jwt_no_verify_default = {
@@ -29267,7 +29732,7 @@ function registerBundledPacks() {
29267
29732
  }
29268
29733
 
29269
29734
  // ../../packages/plugin-sdk/src/repo.ts
29270
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
29735
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
29271
29736
  import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
29272
29737
 
29273
29738
  // ../../packages/plugin-sdk/src/events.ts
@@ -29278,21 +29743,25 @@ import { existsSync as existsSync7 } from "fs";
29278
29743
  import { fileURLToPath } from "url";
29279
29744
  import { Worker } from "worker_threads";
29280
29745
 
29746
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
29747
+ var import_ignore = __toESM(require_ignore(), 1);
29748
+ import { readFileSync as readFileSync8 } from "fs";
29749
+ import { join as join11 } from "path";
29750
+
29281
29751
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
29282
29752
  import { arch, hostname as hostname4, platform, release } from "os";
29283
29753
 
29284
29754
  // ../../packages/plugin-sdk/src/nudge.ts
29285
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
29286
- import { join as join11 } from "path";
29755
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
29756
+ import { join as join12 } from "path";
29287
29757
 
29288
29758
  // ../../packages/plugin-sdk/src/paths.ts
29289
29759
  import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
29290
29760
  import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
29291
29761
 
29292
29762
  // ../../packages/plugin-sdk/src/project-files.ts
29293
- var import_ignore = __toESM(require_ignore(), 1);
29294
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
29295
- import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
29763
+ import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
29764
+ import { basename as basename5, join as join13 } from "path";
29296
29765
 
29297
29766
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
29298
29767
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -29327,8 +29796,8 @@ import { randomUUID as randomUUID14 } from "crypto";
29327
29796
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
29328
29797
 
29329
29798
  // ../../packages/plugin-sdk/src/throttle.ts
29330
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
29331
- import { join as join13 } from "path";
29799
+ import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
29800
+ import { join as join14 } from "path";
29332
29801
 
29333
29802
  // ../../packages/plugin-sdk/src/tokenize.ts
29334
29803
  function redactedPlaceholder(category) {
@@ -29391,6 +29860,8 @@ var SecretVaultGlue = class {
29391
29860
  async tokenizeText(text, opts) {
29392
29861
  try {
29393
29862
  const findings = opts?.findings ?? this.#selfScan(text);
29863
+ const reversible = opts?.reversible;
29864
+ const keeps = (finding) => reversible === void 0 || reversible.has(finding);
29394
29865
  if (findings === null) return { text: "[REDACTED]", pointers: [], degraded: [] };
29395
29866
  if (findings.length === 0) return { text, pointers: [], degraded: [] };
29396
29867
  const groups = groupSpans(text, findings);
@@ -29407,6 +29878,8 @@ var SecretVaultGlue = class {
29407
29878
  } else if (original !== finding.rawMatch) {
29408
29879
  replacement = redactedPlaceholder(group.category);
29409
29880
  degraded.unshift({ category: group.category });
29881
+ } else if (!keeps(finding)) {
29882
+ replacement = redactedPlaceholder(finding.category);
29410
29883
  } else {
29411
29884
  replacement = await this.tokenizeValue(finding.rawMatch, {
29412
29885
  ruleId: finding.ruleId,
@@ -29639,15 +30112,15 @@ function describePointerSafe(token) {
29639
30112
 
29640
30113
  // src/hooks/message-display-transform.ts
29641
30114
  import {
29642
- mkdirSync as mkdirSync5,
30115
+ mkdirSync as mkdirSync4,
29643
30116
  readdirSync as readdirSync5,
29644
- readFileSync as readFileSync9,
30117
+ readFileSync as readFileSync10,
29645
30118
  renameSync as renameSync5,
29646
30119
  rmSync as rmSync5,
29647
30120
  statSync as statSync7,
29648
30121
  writeFileSync as writeFileSync7
29649
30122
  } from "fs";
29650
- import { dirname as dirname4, join as join14 } from "path";
30123
+ import { dirname as dirname4, join as join15 } from "path";
29651
30124
  var EMPTY_CARRY = Object.freeze({
29652
30125
  tail: "",
29653
30126
  fence: null,
@@ -29844,7 +30317,7 @@ var CARRY_FILE_PREFIX = "display-carry";
29844
30317
  var STALE_CARRY_MS = 15 * 60 * 1e3;
29845
30318
  function carryFilePath(dataDir2, sessionId) {
29846
30319
  const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 80);
29847
- return join14(dataDir2, `${CARRY_FILE_PREFIX}-${safe === "" ? "session" : safe}.json`);
30320
+ return join15(dataDir2, `${CARRY_FILE_PREFIX}-${safe === "" ? "session" : safe}.json`);
29848
30321
  }
29849
30322
  function parseFence(value) {
29850
30323
  if (typeof value !== "object" || value === null) return null;
@@ -29855,7 +30328,7 @@ function parseFence(value) {
29855
30328
  }
29856
30329
  function loadCarry(file2, keys) {
29857
30330
  try {
29858
- const parsed = JSON.parse(readFileSync9(file2, "utf8"));
30331
+ const parsed = JSON.parse(readFileSync10(file2, "utf8"));
29859
30332
  if (typeof parsed !== "object" || parsed === null) return EMPTY_CARRY;
29860
30333
  const record2 = parsed;
29861
30334
  const revealedCount = record2.messageKey === keys.messageKey && typeof record2.revealedCount === "number" && Number.isFinite(record2.revealedCount) ? record2.revealedCount : 0;
@@ -29908,7 +30381,7 @@ function removeStaleCarryFiles(dir, keep) {
29908
30381
  const cutoff = Date.now() - STALE_CARRY_MS;
29909
30382
  for (const name of readdirSync5(dir)) {
29910
30383
  if (!name.startsWith(CARRY_FILE_PREFIX) || !name.endsWith(".json")) continue;
29911
- const path = join14(dir, name);
30384
+ const path = join15(dir, name);
29912
30385
  if (path === keep) continue;
29913
30386
  try {
29914
30387
  if (statSync7(path).mtimeMs < cutoff) rmSync5(path, { force: true });
@@ -29921,7 +30394,7 @@ function removeStaleCarryFiles(dir, keep) {
29921
30394
  function saveCarry(file2, keys, carry) {
29922
30395
  try {
29923
30396
  const dir = dirname4(file2);
29924
- mkdirSync5(dir, { recursive: true });
30397
+ mkdirSync4(dir, { recursive: true });
29925
30398
  removeStaleCarryFiles(dir, file2);
29926
30399
  writeCarryRecord(file2, keys.blockKey, keys.messageKey, carry);
29927
30400
  } catch {
@@ -29930,7 +30403,7 @@ function saveCarry(file2, keys, carry) {
29930
30403
  function finalizeCarry(file2, keys, carry) {
29931
30404
  try {
29932
30405
  if (carry.revealedCount > 0) {
29933
- mkdirSync5(dirname4(file2), { recursive: true });
30406
+ mkdirSync4(dirname4(file2), { recursive: true });
29934
30407
  writeCarryRecord(file2, null, keys.messageKey, {
29935
30408
  ...EMPTY_CARRY,
29936
30409
  revealedCount: carry.revealedCount
@@ -29939,7 +30412,7 @@ function finalizeCarry(file2, keys, carry) {
29939
30412
  }
29940
30413
  let owned = true;
29941
30414
  try {
29942
- const parsed = JSON.parse(readFileSync9(file2, "utf8"));
30415
+ const parsed = JSON.parse(readFileSync10(file2, "utf8"));
29943
30416
  if (typeof parsed === "object" && parsed !== null) {
29944
30417
  const record2 = parsed;
29945
30418
  owned = record2.blockKey === keys.blockKey || record2.messageKey === keys.messageKey;