@akasecurity/ai-tc-claude-code 0.9.6 → 0.9.8

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.
@@ -491,10 +491,9 @@ var require_ignore = __commonJS({
491
491
  }
492
492
  });
493
493
 
494
- // ../../packages/persistence/src/database.ts
495
- import { randomUUID as randomUUID10 } from "crypto";
496
- import { join as join2, sep } from "path";
497
- import { DatabaseSync } from "node:sqlite";
494
+ // ../../packages/persistence/src/control-plane-credential.ts
495
+ import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
496
+ import { join } from "path";
498
497
 
499
498
  // ../../packages/schema/src/drizzle/sqlite-ddl.ts
500
499
  var SQLITE_MIGRATIONS = [
@@ -581,6 +580,10 @@ var SQLITE_MIGRATIONS = [
581
580
  {
582
581
  tag: "0020_secret_vault_pagination_indexes",
583
582
  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');"
583
+ },
584
+ {
585
+ tag: "0021_finding_resolution_resolved_at_index",
586
+ sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
584
587
  }
585
588
  ];
586
589
 
@@ -15296,6 +15299,47 @@ function date4(params) {
15296
15299
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15297
15300
  config(en_default());
15298
15301
 
15302
+ // ../../packages/schema/src/zod/harness-map.ts
15303
+ var HARNESS = {
15304
+ ClaudeCode: "claudecode",
15305
+ Cursor: "cursor",
15306
+ Copilot: "copilot",
15307
+ Codex: "codex",
15308
+ Antigravity: "antigravity",
15309
+ Windsurf: "windsurf",
15310
+ ClaudeDesktop: "claudedesktop",
15311
+ ChatGpt: "chatgpt",
15312
+ ClaudeAi: "claudeai",
15313
+ Api: "api"
15314
+ };
15315
+ var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
15316
+ var SOURCE_TOOL = {
15317
+ ClaudeCode: "claude-code",
15318
+ ClaudeDesktop: "claude-desktop",
15319
+ Cursor: "cursor",
15320
+ ChatGpt: "chatgpt",
15321
+ ClaudeAi: "claude-ai",
15322
+ Copilot: "github-copilot",
15323
+ Codex: "codex",
15324
+ Antigravity: "antigravity",
15325
+ // No harness counterpart, deliberately: the CLI's own captures and a capture
15326
+ // whose tool could not be identified both render through the read side's
15327
+ // miss path rather than as a harness of their own.
15328
+ Cli: "cli",
15329
+ Unknown: "unknown"
15330
+ };
15331
+ var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
15332
+ var TOOL_TO_HARNESS = {
15333
+ [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
15334
+ [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
15335
+ [SOURCE_TOOL.Copilot]: HARNESS.Copilot,
15336
+ [SOURCE_TOOL.Cursor]: HARNESS.Cursor,
15337
+ [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
15338
+ [SOURCE_TOOL.Codex]: HARNESS.Codex,
15339
+ [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
15340
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
15341
+ };
15342
+
15299
15343
  // ../../packages/schema/src/zod/finding.ts
15300
15344
  var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
15301
15345
  var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
@@ -15318,21 +15362,22 @@ var Finding = external_exports.object({
15318
15362
  }).meta({ id: "Finding" });
15319
15363
  var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
15320
15364
  var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
15321
- var FindingProvider = external_exports.enum([
15322
- "claudecode",
15323
- "claudedesktop",
15324
- "cursor",
15325
- "copilot",
15326
- "chatgpt",
15327
- "claudeai",
15328
- "codex",
15329
- "antigravity",
15330
- "api"
15365
+ var FindingProvider = Harness.extract([
15366
+ "ClaudeCode",
15367
+ "ClaudeDesktop",
15368
+ "Cursor",
15369
+ "Copilot",
15370
+ "ChatGpt",
15371
+ "ClaudeAi",
15372
+ "Codex",
15373
+ "Antigravity",
15374
+ "Api"
15331
15375
  ]).meta({ id: "FindingProvider" });
15332
15376
  var FindingCategory = external_exports.enum([
15333
15377
  "secret",
15334
15378
  "pii",
15335
15379
  "source_code",
15380
+ "code_flaw",
15336
15381
  "external_share",
15337
15382
  "mcp_server",
15338
15383
  "customer_data",
@@ -15512,6 +15557,7 @@ var FindingInstanceDetail = FindingInstance.extend({
15512
15557
  policy: FindingPolicyRef
15513
15558
  }).meta({ id: "FindingInstanceDetail" });
15514
15559
  var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
15560
+ var MAX_FLAT_FINDINGS_LIMIT = 200;
15515
15561
  var ListFindingInstancesQuery = external_exports.object({
15516
15562
  severity: external_exports.array(Severity).optional(),
15517
15563
  // Rule ids, the same vocabulary the grouped list's `subtype` carries.
@@ -15531,7 +15577,7 @@ var ListFindingInstancesQuery = external_exports.object({
15531
15577
  q: external_exports.string().optional(),
15532
15578
  sessionId: external_exports.string().optional(),
15533
15579
  from: external_exports.iso.datetime().optional(),
15534
- limit: external_exports.coerce.number().int().min(1).max(200).optional(),
15580
+ limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
15535
15581
  cursor: external_exports.string().optional()
15536
15582
  });
15537
15583
  var ListFindingInstancesResponse = external_exports.object({
@@ -15593,30 +15639,6 @@ var ListFindingLocationsResponse = external_exports.object({
15593
15639
  hasMore: external_exports.boolean()
15594
15640
  }).meta({ id: "ListFindingLocationsResponse" });
15595
15641
 
15596
- // ../../packages/schema/src/zod/harness-map.ts
15597
- var Harness = external_exports.enum([
15598
- "claudecode",
15599
- "cursor",
15600
- "copilot",
15601
- "codex",
15602
- "antigravity",
15603
- "windsurf",
15604
- "claudedesktop",
15605
- "chatgpt",
15606
- "claudeai",
15607
- "api"
15608
- ]).meta({ id: "Harness" });
15609
- var TOOL_TO_HARNESS = {
15610
- "claude-code": "claudecode",
15611
- "claude-desktop": "claudedesktop",
15612
- "github-copilot": "copilot",
15613
- cursor: "cursor",
15614
- chatgpt: "chatgpt",
15615
- codex: "codex",
15616
- antigravity: "antigravity",
15617
- "claude-ai": "claudeai"
15618
- };
15619
-
15620
15642
  // ../../packages/schema/src/zod/meta.ts
15621
15643
  var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
15622
15644
  var AuditEventType = external_exports.enum([
@@ -16069,366 +16091,240 @@ var ActivityOverviewResponse = external_exports.object({
16069
16091
  sessions: ListActivitySessionsResponse
16070
16092
  }).meta({ id: "ActivityOverviewResponse" });
16071
16093
 
16072
- // ../../packages/schema/src/zod/event.ts
16073
- var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
16074
- var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
16075
- var SourceTool = external_exports.enum([
16076
- "claude-code",
16077
- "claude-desktop",
16078
- "cursor",
16079
- "chatgpt",
16080
- "claude-ai",
16081
- "github-copilot",
16082
- "codex",
16083
- "antigravity",
16084
- "cli",
16085
- "unknown"
16086
- ]).meta({ id: "SourceTool" });
16087
- var EventMetadata = external_exports.object({
16088
- sessionId: external_exports.string().optional(),
16089
- repo: external_exports.string().optional(),
16090
- filePath: external_exports.string().optional(),
16091
- // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
16092
- // set by the tool-scanning hooks. The tool NAME only — never the tool's
16093
- // arguments or output, which can carry the very value a finding masked
16094
- // (metadata is stored unredacted). Gives findings on non-file captures a
16095
- // display location ("via Bash") when no filePath exists.
16096
- toolName: external_exports.string().optional(),
16097
- // Set (true) by the worktree scanner when the file is excluded by the
16098
- // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
16099
- // generated code can leak real secrets — but the provenance is recorded so
16100
- // policy/dashboards can treat those findings as informational rather than
16101
- // blocking. Omitted (not false) for tracked files and non-scan events.
16102
- gitignored: external_exports.boolean().optional(),
16103
- // Set (true) ONLY when the event's `content` is the COMPLETE file at
16104
- // capture time (a worktree scan reading from disk). Hook-captured edits
16105
- // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
16106
- // this. The resolver-on-ingest keys its fixed-at-source dropout
16107
- // diff on this marker: only a whole-file snapshot can prove a previously
16108
- // open finding is gone; a fragment's absence proves nothing (the secret
16109
- // may live outside the hunk). Omitted (not false) for fragments and
16110
- // non-scan events, so pre-marker clients safely default to the
16111
- // non-authoritative path.
16112
- wholeFile: external_exports.boolean().optional(),
16113
- model: external_exports.string().optional(),
16114
- turnIndex: external_exports.number().int().nonnegative().optional(),
16115
- // Distributed-tracing correlation. `correlationId` ties a recorded event back
16116
- // to the request that captured/ingested it (a UUID, generated independently of
16117
- // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
16118
- // originating span when telemetry is enabled. Both optional + backward
16119
- // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
16120
- correlationId: external_exports.uuid().optional(),
16121
- traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
16122
- // Ids of the detection exceptions that downgraded findings in this capture
16123
- // to 'allow' — the enforcement audit trail's link back to the grant that
16124
- // authorized the bypass. Absent on captures where no exception applied.
16125
- exceptionIds: external_exports.array(external_exports.guid()).optional()
16126
- }).meta({ id: "EventMetadata" });
16127
- var Event = external_exports.object({
16128
- id: external_exports.guid(),
16129
- sourceTool: SourceTool,
16130
- kind: EventKind,
16131
- occurredAt: external_exports.iso.datetime(),
16132
- contentHash: external_exports.string(),
16133
- content: external_exports.string(),
16134
- metadata: EventMetadata.optional()
16135
- }).meta({ id: "Event" });
16136
- var IngestEvent = Event.meta({ id: "IngestEvent" });
16137
- var IngestBatch = external_exports.object({
16138
- events: external_exports.array(IngestEvent).min(1).max(100),
16139
- // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
16140
- // additionally rejects any event whose contentHash the store has already
16141
- // recorded — for re-runnable bulk ingest (worktree scan, transcript
16142
- // backfill), where a re-run mints fresh event ids for identical content and
16143
- // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
16144
- // two genuinely separate prompts can be byte-identical and both belong on
16145
- // the timeline.
16146
- dedupe: external_exports.literal("content-hash").optional()
16147
- }).meta({ id: "IngestBatch" });
16148
-
16149
- // ../../packages/schema/src/zod/inventory.ts
16150
- var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16151
- var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16152
- var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16153
- var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16154
- var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16155
- var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16156
- var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16157
- var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
16158
- var AccessCounts = external_exports.object({
16159
- open: external_exports.number().int().nonnegative(),
16160
- approved: external_exports.number().int().nonnegative(),
16161
- blocked: external_exports.number().int().nonnegative(),
16162
- total: external_exports.number().int().nonnegative()
16163
- }).meta({ id: "AccessCounts" });
16164
- var AssetSummary = external_exports.object({
16165
- id: external_exports.string(),
16166
- type: AssetType,
16167
- name: external_exports.string(),
16168
- sub: external_exports.string(),
16169
- flags: external_exports.array(Flag),
16170
- /** MCP servers only — omitted for all other types. */
16171
- trust: TrustLevel.optional()
16172
- }).meta({ id: "AssetSummary" });
16173
- var ProjectSummary = external_exports.object({
16174
- id: external_exports.string(),
16175
- name: external_exports.string(),
16176
- repo: external_exports.string(),
16177
- visibility: Visibility,
16178
- language: external_exports.string(),
16179
- policyDefault: AccessLevel,
16180
- updatedAt: external_exports.iso.datetime(),
16181
- accessCounts: AccessCounts,
16182
- findingsCount: external_exports.number().int().nonnegative()
16183
- }).meta({ id: "ProjectSummary" });
16184
- var HarnessCategory = external_exports.object({
16185
- /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16186
- type: AssetType,
16187
- assets: external_exports.array(AssetSummary)
16094
+ // ../../packages/schema/src/zod/config-inventory.ts
16095
+ var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16096
+ var SkillScanEntry = external_exports.object({
16097
+ name: external_exports.string().min(1),
16098
+ // The identity source: a marketplace repo for plugin skills (e.g.
16099
+ // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16100
+ // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16101
+ // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16102
+ source: external_exports.string().min(1),
16103
+ scope: ConfigScope,
16104
+ pluginName: external_exports.string().optional(),
16105
+ // Volatile — rides the attribute bag, never the identity hash.
16106
+ version: external_exports.string().optional(),
16107
+ description: external_exports.string().optional(),
16108
+ // Skill directory mtime (ISO) the "updated Nd ago" freshness signal.
16109
+ updatedAt: external_exports.iso.datetime().optional(),
16110
+ // Filesystem path — the promoted inventory `location` column.
16111
+ location: external_exports.string().optional()
16188
16112
  });
16189
- var HarnessSummary = external_exports.object({
16190
- id: HarnessId,
16191
- label: external_exports.string(),
16192
- kind: external_exports.string(),
16193
- version: external_exports.string(),
16194
- sessions: external_exports.number().int().nonnegative(),
16195
- assetCount: external_exports.number().int().nonnegative(),
16196
- flagCount: external_exports.number().int().nonnegative(),
16197
- projects: external_exports.array(ProjectSummary),
16198
- categories: external_exports.array(HarnessCategory)
16199
- }).meta({ id: "HarnessSummary" });
16200
- var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16201
- var AssetGroup = external_exports.object({
16202
- /** Group key — never project (enforced at service layer). */
16203
- type: AssetType,
16204
- total: external_exports.number().int().nonnegative(),
16205
- /**
16206
- * MCP group only — omitted for all other types.
16207
- * Partial: only TrustLevel keys with non-zero counts are included.
16208
- * Strict: unknown keys are rejected only TrustLevel values are valid keys.
16209
- */
16210
- trustRollup: external_exports.object({
16211
- "known-good": external_exports.number().int().nonnegative(),
16212
- risky: external_exports.number().int().nonnegative(),
16213
- unapproved: external_exports.number().int().nonnegative()
16214
- }).partial().strict().optional(),
16113
+ var HookScanEntry = external_exports.object({
16114
+ // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16115
+ // set is harness-defined and grows without a schema change.
16116
+ event: external_exports.string().min(1),
16117
+ // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16118
+ matcher: external_exports.string().optional(),
16119
+ command: external_exports.string().min(1),
16120
+ timeout: external_exports.number().optional(),
16121
+ scope: ConfigScope,
16122
+ pluginName: external_exports.string().optional(),
16123
+ // The settings file / hooks.json the entry came from.
16124
+ location: external_exports.string().optional()
16125
+ });
16126
+ var McpServerScanEntry = external_exports.object({
16127
+ // The server's config key ("github", "filesystem", …) — identity, with the
16128
+ // qualified scope (see mcpServerIdentityKey).
16129
+ name: external_exports.string().min(1),
16130
+ scope: ConfigScope,
16131
+ pluginName: external_exports.string().optional(),
16132
+ // The owning plugin's marketplacepart of PLUGIN-scope identity: two
16133
+ // marketplaces can each ship a plugin named `guard`, and without this their
16134
+ // same-named servers would collapse to one row (the second silently dropped,
16135
+ // inheriting the first's trust).
16136
+ marketplace: external_exports.string().optional(),
16137
+ // The repo identity (remote url, or the cwd for un-remoted repos) — part of
16138
+ // PROJECT/LOCAL-scope identity: a server named `github` in repo A and one in
16139
+ // repo B are different servers with different commands, and MUST NOT share a
16140
+ // row — a shared row would let a cloned repo's .mcp.json inherit the trust
16141
+ // the user granted elsewhere.
16142
+ project: external_exports.string().optional(),
16143
+ // 'stdio' when the entry carries a command; otherwise the config's `type`
16144
+ // ('http' / 'sse' / …). Open string — the transport set is harness-defined.
16145
+ transport: external_exports.string().min(1),
16146
+ // Volatile on purpose (unlike hook `command`): a changed command/url is drift
16147
+ // on a stable row — visible across config_scan snapshots and preserving the
16148
+ // user's trust decision — never a quiet new row. One of the two is present.
16149
+ // Secret-masked at collection time (the scanner runs the bundled detection
16150
+ // packs over both — tokens routinely ride command args and URLs).
16151
+ command: external_exports.string().optional(),
16152
+ url: external_exports.string().optional(),
16153
+ // Env var NAMES only, never values (the no-secrets rule).
16154
+ envKeys: external_exports.array(external_exports.string()).optional(),
16155
+ // The config file the entry came from.
16156
+ location: external_exports.string().optional()
16157
+ });
16158
+ var ConfigFileScanEntry = external_exports.object({
16159
+ // Basename (settings.json, CLAUDE.md) or dir name (commands/, agents/).
16160
+ name: external_exports.string().min(1),
16161
+ // The absolute path — identity (with scope) and the promoted `location`.
16162
+ path: external_exports.string().min(1),
16163
+ scope: ConfigScope,
16164
+ // Human label: "User settings", "Project memory", "Slash commands", …
16165
+ kind: external_exports.string().min(1),
16166
+ // Derived SHAPE summary — top-level key names, entry counts, line counts.
16167
+ // Never file content or values (memory files can carry sensitive detail).
16168
+ detail: external_exports.string().optional(),
16169
+ // Dir configs (commands/, agents/) and .mcp.json: how many entries.
16170
+ entryCount: external_exports.number().optional(),
16171
+ // File mtime (ISO) — the freshness signal.
16172
+ updatedAt: external_exports.iso.datetime().optional()
16173
+ });
16174
+ var ConfigScanResult = external_exports.object({
16175
+ scannedAt: external_exports.iso.datetime(),
16176
+ skills: external_exports.array(SkillScanEntry),
16177
+ hooks: external_exports.array(HookScanEntry),
16178
+ mcpServers: external_exports.array(McpServerScanEntry),
16179
+ configFiles: external_exports.array(ConfigFileScanEntry),
16180
+ errors: external_exports.array(external_exports.object({ source: external_exports.string(), reason: external_exports.string() }))
16181
+ });
16182
+ var ConfigPostureFindingInput = external_exports.object({
16183
+ ruleId: external_exports.string().min(1),
16184
+ version: external_exports.string().min(1),
16185
+ span: Span,
16186
+ // For posture rules this is the offending COMMAND (config the user already
16187
+ // holds locally, not captured secret content) — it is also the correlation
16188
+ // key the read surface matches back to a hook row.
16189
+ maskedMatch: external_exports.string(),
16190
+ actionTaken: ActionTaken,
16191
+ confidence: external_exports.number().min(0).max(1)
16192
+ });
16193
+ var ConfigScanRecord = external_exports.object({
16194
+ items: external_exports.array(InventoryInput),
16195
+ scanEvent: AuditEventInput,
16196
+ definitions: external_exports.array(InspectionDefinitionInput).optional(),
16197
+ findings: external_exports.array(ConfigPostureFindingInput).optional()
16198
+ });
16199
+
16200
+ // ../../packages/schema/src/zod/control-plane.ts
16201
+ var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
16202
+ var AttachedCredential = external_exports.object({
16203
+ specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
16204
+ // The control-plane endpoint this credential was minted against.
16205
+ endpoint: external_exports.string().min(1),
16206
+ // The bearer credential itself. Never logged, never rendered — status
16207
+ // surfaces show `keyPrefix` and nothing else.
16208
+ apiKey: external_exports.string().min(1),
16209
+ // First few characters of the key, safe to display so a user can match the
16210
+ // credential against their organization's key list.
16211
+ keyPrefix: external_exports.string().min(1).max(16).optional(),
16212
+ mintedAt: external_exports.iso.datetime().optional()
16213
+ });
16214
+ var MAX_DATE_MS = 253402300799999;
16215
+ var MAX_INT4 = 2147483647;
16216
+ var StorePosturePack = external_exports.object({
16217
+ packId: external_exports.string().min(1),
16218
+ // 'namespace/packId'
16219
+ version: external_exports.string().min(1),
16220
+ enabled: external_exports.boolean(),
16221
+ // Stringified pass-through of the local store's `installed_packs.updated_at`
16222
+ // — the column format is store-version-dependent (epoch millis vs ISO), so
16223
+ // the wire shape assumes neither.
16224
+ updatedAt: external_exports.string().nullable()
16225
+ }).meta({ id: "StorePosturePack" });
16226
+ var StorePosturePolicyCounts = external_exports.object({
16227
+ total: external_exports.number().int().min(0),
16228
+ disabled: external_exports.number().int().min(0),
16229
+ // Exhaustive per-action map; the builder pre-fills every action with 0.
16230
+ //
16231
+ // Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
16232
+ // enforces exhaustiveness either way, but z.record emits `propertyNames` +
16233
+ // `additionalProperties` into a generated schema document, and a type
16234
+ // generator renders THAT with every key optional — a sender built against
16235
+ // the generated type would typecheck and still be rejected at runtime. An
16236
+ // explicit object emits `properties` + `required`, so generated types
16237
+ // demand all five.
16238
+ //
16239
+ // `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
16240
+ // ActionTaken member is a COMPILE error here instead of silent drift.
16241
+ // `.strict()` is load-bearing — it rejects an unknown action key, which a
16242
+ // bare object would silently STRIP, accepting a miscounted map as valid.
16243
+ byAction: external_exports.object({
16244
+ warn: external_exports.number().int().min(0),
16245
+ redact: external_exports.number().int().min(0),
16246
+ block: external_exports.number().int().min(0),
16247
+ allow: external_exports.number().int().min(0),
16248
+ log: external_exports.number().int().min(0)
16249
+ }).strict()
16250
+ }).meta({ id: "StorePosturePolicyCounts" });
16251
+ var StorePosturePlugin = external_exports.object({
16252
+ /** Package name of the reporting plugin. */
16253
+ package: external_exports.string().min(1).max(200),
16254
+ version: external_exports.string().min(1).max(64),
16255
+ /** Version of the bundled core, when the build records one separately. */
16256
+ ossVersion: external_exports.string().max(64).nullable(),
16215
16257
  /**
16216
- * Partial: only Flag keys with non-zero counts are included.
16217
- * Strict: unknown keys are rejected only Flag values are valid keys.
16258
+ * `version` of the policy bundle this machine last fetched. Bounded at 200
16259
+ * rather than the 64 a bare sha256 hex digest needs today, so a later
16260
+ * format with an algorithm prefix does not start rejecting the channel.
16218
16261
  */
16219
- flagRollup: external_exports.object({
16220
- update: external_exports.number().int().nonnegative(),
16221
- stale: external_exports.number().int().nonnegative(),
16222
- conflict: external_exports.number().int().nonnegative(),
16223
- unknown: external_exports.number().int().nonnegative(),
16224
- change: external_exports.number().int().nonnegative(),
16225
- untracked: external_exports.number().int().nonnegative(),
16226
- risk: external_exports.number().int().nonnegative(),
16227
- findings: external_exports.number().int().nonnegative()
16228
- }).partial().strict(),
16229
- items: external_exports.array(AssetSummary)
16230
- }).meta({ id: "AssetGroup" });
16231
- var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16232
- var McpTool = external_exports.object({
16233
- name: external_exports.string(),
16234
- signature: external_exports.string(),
16235
- description: external_exports.string(),
16236
- write: external_exports.boolean(),
16237
- /** Non-null string when tool is dangerous / blocked; null otherwise. */
16238
- risk: external_exports.string().nullable()
16239
- }).meta({ id: "McpTool" });
16240
- var AssetFindingRef = external_exports.object({
16241
- id: external_exports.string(),
16242
- title: external_exports.string(),
16243
- note: external_exports.string()
16244
- });
16245
- var AssetDetail = AssetSummary.extend({
16246
- /** string | null — null when no description is available. */
16247
- description: external_exports.string().nullable(),
16248
- /** trustLevel | null — null for non-MCP assets. */
16249
- trust: TrustLevel.nullable(),
16250
- /** Type-specific raw key/values — FE renders the grid. */
16251
- meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16252
- /** always present object when there is an active finding, null when absent. */
16253
- finding: AssetFindingRef.nullable(),
16254
- /** MCP exposed-tools list — omitted for non-mcp. */
16255
- tools: external_exports.array(McpTool).optional()
16256
- }).meta({ id: "AssetDetail" });
16257
- var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16258
- var InventoryStats = external_exports.object({
16259
- /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16260
- attention: external_exports.number().int().nonnegative(),
16261
- byType: external_exports.object({
16262
- project: external_exports.number().int().nonnegative(),
16263
- skill: external_exports.number().int().nonnegative(),
16264
- mcp: external_exports.number().int().nonnegative(),
16265
- hook: external_exports.number().int().nonnegative(),
16266
- config: external_exports.number().int().nonnegative()
16267
- }),
16268
- harnesses: external_exports.number().int().nonnegative(),
16269
- mcpTrust: external_exports.object({
16270
- "known-good": external_exports.number().int().nonnegative(),
16271
- risky: external_exports.number().int().nonnegative(),
16272
- unapproved: external_exports.number().int().nonnegative()
16273
- })
16274
- }).meta({ id: "InventoryStats" });
16275
- var FileSummary = external_exports.object({
16276
- path: external_exports.string(),
16277
- name: external_exports.string(),
16278
- origin: Origin,
16279
- /** Effective access (override applied). */
16280
- access: AccessLevel,
16281
- /** True when a file_access_override differs from the computed default. */
16282
- isCustom: external_exports.boolean(),
16283
- findings: external_exports.number().int().nonnegative(),
16284
- /** When the file was auto-blocked by a detection; null when not blocked. */
16285
- blockedAt: external_exports.iso.datetime().nullable().optional(),
16286
- /** Why the file was blocked; null when absent. */
16287
- note: external_exports.string().nullable().optional()
16288
- }).meta({ id: "FileSummary" });
16289
- var FolderSummary = external_exports.object({
16290
- name: external_exports.string(),
16291
- path: external_exports.string(),
16292
- /** Rollup of effective access across all descendants. */
16293
- accessCounts: AccessCounts
16294
- }).meta({ id: "FolderSummary" });
16295
- var ProjectTreeResponse = external_exports.object({
16296
- project: external_exports.object({
16297
- id: external_exports.string(),
16298
- repo: external_exports.string(),
16299
- visibility: Visibility
16300
- }),
16301
- path: external_exports.string(),
16302
- /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16303
- folders: external_exports.array(FolderSummary).optional(),
16304
- files: external_exports.array(FileSummary)
16305
- }).meta({ id: "ProjectTreeResponse" });
16306
- var FileDetail = FileSummary.extend({
16307
- project: external_exports.object({
16308
- repo: external_exports.string(),
16309
- visibility: Visibility,
16310
- language: external_exports.string(),
16311
- policyDefault: AccessLevel,
16312
- updatedAt: external_exports.iso.datetime()
16313
- }),
16314
- findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16315
- }).meta({ id: "FileDetail" });
16316
- var SetFileAccessBody = external_exports.object({
16317
- path: external_exports.string(),
16318
- access: AccessLevel
16319
- }).meta({ id: "SetFileAccessBody" });
16320
- var SetFileAccessResponse = external_exports.object({
16321
- file: FileSummary,
16322
- accessCounts: AccessCounts
16323
- }).meta({ id: "SetFileAccessResponse" });
16324
- var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16325
- var HarnessEventItem = external_exports.object({
16326
- kind: HarnessEventKind,
16327
- title: external_exports.string(),
16328
- detail: external_exports.string(),
16329
- occurredAt: external_exports.iso.datetime(),
16330
- findingId: external_exports.string().nullable().optional()
16331
- }).meta({ id: "HarnessEventItem" });
16332
- var HarnessEventsResponse = external_exports.object({
16333
- counts: external_exports.object({
16334
- block: external_exports.number().int().nonnegative(),
16335
- redact: external_exports.number().int().nonnegative(),
16336
- warn: external_exports.number().int().nonnegative()
16337
- }),
16338
- items: external_exports.array(HarnessEventItem)
16339
- }).meta({ id: "HarnessEventsResponse" });
16340
- var RescanResponse = external_exports.object({
16341
- jobId: external_exports.string(),
16342
- startedAt: external_exports.iso.datetime()
16343
- }).meta({ id: "RescanResponse" });
16344
- var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16345
- var ListAssetsQuery = external_exports.object({
16346
- /** Filter by one or more AssetType values; absent means all types. */
16347
- type: external_exports.array(AssetType).optional(),
16348
- /** Free-text search term. */
16349
- q: external_exports.string().optional()
16350
- });
16351
- var GetProjectTreeQuery = external_exports.object({
16352
- /** Subtree root path; defaults to repository root when absent. */
16353
- path: external_exports.string().optional(),
16354
- /** Free-text filter applied to file paths. */
16355
- q: external_exports.string().optional(),
16356
- /**
16357
- * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16358
- * file across the whole repo (folders omitted, most-recent first), ignoring
16359
- * `path`/`q` — powers the project-wide "recently blocked" strip.
16360
- */
16361
- filter: external_exports.enum(["blocked"]).optional()
16262
+ policyBundleVersion: external_exports.string().max(200).nullable(),
16263
+ /** Epoch millis, on the CLIENT clock, of that fetch. */
16264
+ policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
16265
+ }).meta({ id: "StorePosturePlugin" });
16266
+ var StorePostureSnapshot = external_exports.object({
16267
+ deviceId: external_exports.guid(),
16268
+ hostname: external_exports.string().min(1).max(253),
16269
+ // Epoch millis on the CLIENT clock. Bounded by what a receiving store
16270
+ // accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
16271
+ capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
16272
+ // False is a measurement, not an error state: "no local store exists on
16273
+ // this machine".
16274
+ storePresent: external_exports.boolean(),
16275
+ schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
16276
+ // PRAGMA user_version
16277
+ findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
16278
+ // Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
16279
+ // bound does and does not do. Worth stating for these two specifically:
16280
+ // they are read from the local store's own ROWS rather than from this
16281
+ // machine's clock, so a damaged or hand-edited store is enough to produce
16282
+ // an out-of-range value with no clock skew involved.
16283
+ findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16284
+ findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16285
+ packs: external_exports.array(StorePosturePack).max(500),
16286
+ policyCounts: StorePosturePolicyCounts,
16287
+ // OPTIONAL, not nullable: a reporter that predates this member keeps
16288
+ // getting its 200 without a payload change.
16289
+ plugin: StorePosturePlugin.optional()
16290
+ }).meta({ id: "StorePostureSnapshot" });
16291
+ var CAPTURE_VERSION_PREFIX = "capture/";
16292
+ var RecordAuditEventRequest = AuditEventInput.extend({
16293
+ inspections: external_exports.array(ToolCallInspection).default([])
16294
+ }).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
16295
+ message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
16296
+ path: ["inspections"]
16297
+ }).meta({ id: "RecordAuditEventRequest" });
16298
+ var IngestAck = external_exports.object({
16299
+ accepted: external_exports.number().int().nonnegative(),
16300
+ duplicates: external_exports.number().int().nonnegative()
16362
16301
  });
16363
- var GetProjectFileQuery = external_exports.object({
16364
- /** Repository-relative file path; absent or empty 400. */
16365
- path: external_exports.string()
16302
+ var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
16303
+ var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
16304
+ var PluginWhoami = external_exports.object({
16305
+ tenantName: printable(200),
16306
+ userEmail: printable(320),
16307
+ role: printable(64),
16308
+ keyKind: printable(64),
16309
+ serverTime: printable(64)
16366
16310
  });
16367
- var GetHarnessEventsQuery = external_exports.object({
16368
- /** Maximum number of events to return. Range: 1–50; default: 7. */
16369
- limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16311
+ var ControlPlaneErrorBody = external_exports.object({
16312
+ error: external_exports.object({
16313
+ code: external_exports.string().optional(),
16314
+ message: external_exports.string().optional()
16315
+ }).optional()
16370
16316
  });
16371
16317
 
16372
- // ../../packages/schema/src/zod/exception.ts
16373
- var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
16374
- var ExceptionConditions = external_exports.object({
16375
- repo: external_exports.string().optional(),
16376
- sourceTool: external_exports.string().optional(),
16377
- provider: external_exports.string().optional()
16378
- }).strict();
16379
- var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
16380
- var DetectionException = external_exports.object({
16381
- id: external_exports.guid(),
16382
- ruleId: external_exports.string(),
16383
- // Denormalized from the rule, for reporting — never matched on.
16384
- category: DetectionCategory,
16385
- // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
16386
- // fingerprint, never the raw value, and never reversible. Matching recomputes
16387
- // the fingerprint from a fresh capture; the value itself is never stored.
16388
- // Shape-constrained so a malformed — or accidentally raw — value is rejected
16389
- // at the boundary rather than persisted.
16390
- valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
16391
- // Version of the fingerprint key the grant was written under; a rotated key
16392
- // invalidates old grants rather than silently mismatching them.
16393
- keyVersion: external_exports.number().int().positive(),
16394
- // maskMatch() preview of the approved value — never the raw value.
16395
- maskedValue: external_exports.string(),
16396
- capability: ExceptionCapability.default("suppress"),
16397
- scope: ExceptionScope,
16398
- expiresAt: external_exports.iso.datetime().nullable(),
16399
- maxUses: external_exports.number().int().positive().nullable(),
16400
- useCount: external_exports.number().int().nonnegative(),
16401
- lastUsedAt: external_exports.iso.datetime().nullable(),
16402
- // Mandatory: every grant carries the human reason it exists.
16403
- justification: external_exports.string().min(1),
16404
- conditions: ExceptionConditions.nullable(),
16405
- createdBy: external_exports.string(),
16406
- createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
16407
- createdAt: external_exports.iso.datetime(),
16408
- updatedAt: external_exports.iso.datetime(),
16409
- // Revocation is terminal and retained — consumed/expired/revoked rows are
16410
- // audit evidence; nothing in the exception lifecycle hard-deletes.
16411
- revokedAt: external_exports.iso.datetime().nullable(),
16412
- revokedBy: external_exports.string().nullable(),
16413
- revokeReason: external_exports.string().nullable()
16414
- });
16415
- var ExceptionBundleEntry = DetectionException.pick({
16416
- id: true,
16417
- ruleId: true,
16418
- valueFingerprint: true,
16419
- keyVersion: true,
16420
- capability: true,
16421
- expiresAt: true,
16422
- maxUses: true,
16423
- useCount: true,
16424
- conditions: true
16425
- });
16426
- var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
16318
+ // ../../packages/schema/src/zod/registry.ts
16319
+ var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16320
+ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16321
+ var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16322
+ var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16427
16323
 
16428
16324
  // ../../packages/schema/src/zod/rule.ts
16429
- var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16325
+ var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
16430
16326
  var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16431
- var KeywordMatcher = external_exports.object({
16327
+ var KeywordMatcher = external_exports.strictObject({
16432
16328
  type: external_exports.literal("keyword"),
16433
16329
  // An empty keyword matches at every position, yielding one zero-length span
16434
16330
  // per character. Rejected here because a keyword that matches everything is
@@ -16444,396 +16340,144 @@ function isValidRegex(pattern, flags2) {
16444
16340
  return false;
16445
16341
  }
16446
16342
  }
16343
+ function probeFlags(flags2) {
16344
+ return flags2.replace(/[gy]/g, "");
16345
+ }
16447
16346
  function matchesEmptyString(pattern, flags2) {
16448
16347
  try {
16449
- const re = new RegExp(pattern, flags2.replace(/[gy]/g, ""));
16348
+ const re = new RegExp(pattern, probeFlags(flags2));
16450
16349
  return re.exec("")?.[0].length === 0;
16451
16350
  } catch {
16452
16351
  return false;
16453
16352
  }
16454
16353
  }
16354
+ function spansWholeMatch(captureGroup) {
16355
+ return captureGroup === void 0 || captureGroup === 0;
16356
+ }
16357
+ function captureGroupCount(pattern, flags2) {
16358
+ try {
16359
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags2));
16360
+ const result = probe.exec("");
16361
+ return result ? result.length - 1 : void 0;
16362
+ } catch {
16363
+ return void 0;
16364
+ }
16365
+ }
16455
16366
  var MAX_PATTERN_LENGTH = 2e3;
16456
- var RegexMatcher = external_exports.object({
16367
+ var RegexMatcher = external_exports.strictObject({
16457
16368
  type: external_exports.literal("regex"),
16458
16369
  pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16459
16370
  flags: external_exports.string().default("gi"),
16460
16371
  captureGroup: external_exports.number().int().nonnegative().optional()
16461
16372
  }).refine((v) => isValidRegex(v.pattern, v.flags), {
16462
16373
  message: "pattern/flags do not form a valid JavaScript regular expression",
16463
- path: ["pattern"]
16464
- }).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
16465
- message: 'a whole-match regex that can match the empty string (e.g. "\\d*", "a?", "(?:)") can hang the matcher \u2014 scope the quantifier to a captureGroup, or require at least one character',
16466
- path: ["pattern"]
16467
- });
16468
- var ValidatorMatcher = external_exports.object({
16469
- type: external_exports.literal("validator"),
16470
- name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
16471
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16472
- });
16473
- var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
16474
- var AppliesTo = external_exports.object({
16475
- // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16476
- extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16477
- }).meta({ id: "AppliesTo" });
16478
- var PostValidatorRef = external_exports.union([
16479
- external_exports.string(),
16480
- external_exports.object({
16481
- name: external_exports.string(),
16482
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16483
- })
16484
- ]).meta({ id: "PostValidatorRef" });
16485
- var RequiresNearby = external_exports.object({
16486
- // Each array, when present, must be non-empty and contain non-empty strings —
16487
- // an empty/blank criterion would either never fire or (for labels) match
16488
- // everything.
16489
- categories: external_exports.array(DetectionCategory).min(1).optional(),
16490
- ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16491
- labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16492
- windowChars: external_exports.number().int().positive().default(160),
16493
- // Optional confidence bump applied when a gated match is corroborated. Capped
16494
- // small: it nudges confidence, it does not assert certainty.
16495
- confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16496
- }).refine(
16497
- (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16498
- { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16499
- ).meta({ id: "RequiresNearby" });
16500
- var RuleFixture = external_exports.object({
16501
- label: external_exports.string(),
16502
- text: external_exports.string().max(5e4),
16503
- shouldMatch: external_exports.boolean(),
16504
- // Simulated file context for the scan, so fixtures can assert `appliesTo`
16505
- // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16506
- filePath: external_exports.string().optional(),
16507
- expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
16508
- }).meta({ id: "RuleFixture" });
16509
- var Rule = external_exports.object({
16510
- specVersion: external_exports.literal(1),
16511
- // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16512
- // the PACK id, NOT a namespace this is a DIFFERENT slug space from a
16513
- // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16514
- // therefore carries no namespace and is not globally unique across publishers;
16515
- // never feed one to splitDetectionId. `category` below (per-rule) is the
16516
- // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16517
- id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16518
- name: external_exports.string(),
16519
- category: DetectionCategory,
16520
- severity: Severity,
16521
- matcher: Matcher,
16522
- appliesTo: AppliesTo.optional(),
16523
- postValidators: external_exports.array(PostValidatorRef).optional(),
16524
- requiresNearby: RequiresNearby.optional(),
16525
- examples: external_exports.array(external_exports.string()).optional()
16526
- }).meta({ id: "Rule" });
16527
- var Author = external_exports.object({
16528
- name: external_exports.string(),
16529
- email: external_exports.email().optional(),
16530
- url: external_exports.url().optional()
16531
- }).meta({ id: "Author" });
16532
- var PackManifest = external_exports.object({
16533
- specVersion: external_exports.literal(1),
16534
- id: external_exports.string(),
16535
- name: external_exports.string(),
16536
- version: external_exports.string(),
16537
- rules: external_exports.array(external_exports.string()),
16538
- // Optional attribution/provenanceconsumed by the rule marketplace.
16539
- description: external_exports.string().optional(),
16540
- author: Author.optional(),
16541
- license: external_exports.string().optional(),
16542
- sourceUrl: external_exports.url().optional()
16543
- }).meta({ id: "PackManifest" });
16544
-
16545
- // ../../packages/schema/src/zod/policy.ts
16546
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
16547
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
16548
- var Policy = external_exports.object({
16549
- id: external_exports.guid(),
16550
- scope: PolicyScope,
16551
- target: PolicyTarget,
16552
- action: ActionTaken,
16553
- enabled: external_exports.boolean().default(true),
16554
- customKeywords: external_exports.array(external_exports.string()).optional(),
16555
- // Display name — optional so older policy rows without name still parse.
16556
- // Added for the findings API (policy.name column migration).
16557
- name: external_exports.string().optional()
16558
- }).meta({ id: "Policy" });
16559
- var PolicyBundle = external_exports.object({
16560
- version: external_exports.string(),
16561
- policies: external_exports.array(Policy),
16562
- // Rules from the installed marketplace packs (snapshotted by the
16563
- // control plane). The plugin registers these in addition to its bundled
16564
- // packs. Optional so older backends and older on-disk caches — that omit
16565
- // the field still parse; consumers read `bundle.rules ?? []`.
16566
- rules: external_exports.array(Rule).optional(),
16567
- // When true, `rules` IS the complete effective ruleset and the runtime must
16568
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
16569
- // after reading the user's installed snapshot (installed_packs, enabled
16570
- // packs only), which is how detection updates stay manual: new bundled
16571
- // rules run only after the user applies the pack update. Absent/false keeps
16572
- // the historical composition (bundled packs + rules) — older caches.
16573
- rulesComplete: external_exports.boolean().optional(),
16574
- // Active detection exceptions, evaluation subset only (see
16575
- // ExceptionBundleEntry). Optional so older bundle producers — and older
16576
- // on-disk caches — that omit the field still parse; consumers read
16577
- // `bundle.exceptions ?? []`.
16578
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
16579
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
16580
- // from a versioned installed pack. Optional so older backends — and older
16581
- // on-disk caches — that omit the field still parse; consumers fall back to
16582
- // the rule's own spec version. NOT the bundle version above — see
16583
- // installedRuleset's ruleVersions for the source of truth.
16584
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
16585
- customKeywords: external_exports.array(external_exports.string()),
16586
- fetchedAt: external_exports.iso.datetime()
16587
- }).meta({ id: "PolicyBundle" });
16588
- var OBSERVE_ONLY_CATEGORIES = ["config"];
16589
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
16590
- var CATEGORY_PEAK_SEVERITY = {
16591
- secret: "critical",
16592
- financial: "critical",
16593
- // core-financial/credit-card
16594
- code_flaw: "critical",
16595
- pii: "high",
16596
- phi: "high",
16597
- custom: "high",
16598
- // user-defined; conservative
16599
- code_context: "low",
16600
- config: "low"
16601
- // observe-only; floors to monitor regardless
16602
- };
16603
- function severityFloorPolicy(category) {
16604
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
16605
- const peak = CATEGORY_PEAK_SEVERITY[category];
16606
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
16607
- }
16608
- function severityFloorPosture() {
16609
- const out = {};
16610
- for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
16611
- return out;
16612
- }
16613
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16614
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16615
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16616
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16617
- var BUILTIN_POLICY_SPECS = {
16618
- monitor: {
16619
- name: "Monitor",
16620
- action: "log",
16621
- description: "Log every match for audit. The request is allowed through untouched."
16622
- },
16623
- warn: {
16624
- name: "Warn",
16625
- action: "warn",
16626
- description: "Allow the request, but warn the user inline before it is sent."
16627
- },
16628
- redact: {
16629
- name: "Redact",
16630
- action: "redact",
16631
- description: "Automatically strip the matched value from the request, then continue."
16632
- },
16633
- block: {
16634
- name: "Block",
16635
- action: "block",
16636
- description: "Refuse the request entirely whenever any rule in this detection matches."
16637
- }
16638
- };
16639
- function builtinPolicyToAction(id) {
16640
- return BUILTIN_POLICY_SPECS[id].action;
16641
- }
16642
- var DEFAULT_ACTIONS = Object.fromEntries(
16643
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16644
- );
16645
- var BUILTIN_POLICIES = Object.fromEntries(
16646
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16647
- );
16648
- var DEFAULT_PACK_POLICY_ID = "monitor";
16649
- function policyIdToAction(policyId) {
16650
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16651
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16652
- return BUILTIN_POLICIES[id].action;
16653
- }
16654
- var UsedByItem = external_exports.object({
16655
- id: external_exports.string(),
16656
- name: external_exports.string(),
16657
- ruleCount: external_exports.number().int().nonnegative(),
16658
- enabled: external_exports.boolean()
16659
- }).meta({ id: "UsedByItem" });
16660
- var PolicyListItem = external_exports.object({
16661
- id: external_exports.string(),
16662
- kind: PolicyKind,
16663
- name: external_exports.string(),
16664
- enabled: external_exports.boolean(),
16665
- usedByCount: external_exports.number().int().nonnegative()
16666
- }).meta({ id: "PolicyListItem" });
16667
- var PolicyDetail = external_exports.object({
16668
- specVersion: external_exports.literal(1),
16669
- id: external_exports.string(),
16670
- kind: PolicyKind,
16671
- name: external_exports.string(),
16672
- enabled: external_exports.boolean(),
16673
- description: external_exports.string(),
16674
- usedBy: external_exports.array(UsedByItem)
16675
- }).meta({ id: "PolicyDetail" });
16676
- var PolicyStatsResponse = external_exports.object({
16677
- policies: external_exports.number().int().nonnegative(),
16678
- builtin: external_exports.number().int().nonnegative(),
16679
- custom: external_exports.number().int().nonnegative(),
16680
- detectionsGoverned: external_exports.number().int().nonnegative()
16681
- }).meta({ id: "PolicyStatsResponse" });
16682
-
16683
- // ../../packages/schema/src/zod/api.ts
16684
- var LIST_QUERY_MAX_LIMIT = 200;
16685
- var ListEventsQuery = external_exports.object({
16686
- cursor: external_exports.string().optional(),
16687
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16688
- sourceTool: external_exports.string().optional(),
16689
- kind: external_exports.string().optional(),
16690
- from: external_exports.iso.datetime().optional(),
16691
- to: external_exports.iso.datetime().optional()
16692
- });
16693
- var ListEventsResponse = external_exports.object({
16694
- items: external_exports.array(Event),
16695
- nextCursor: external_exports.string().nullable()
16696
- }).meta({ id: "ListEventsResponse" });
16697
- var IngestResponse = external_exports.object({
16698
- accepted: external_exports.number().int().nonnegative(),
16699
- duplicates: external_exports.number().int().nonnegative()
16700
- }).meta({ id: "IngestResponse" });
16701
- var ListFindingsQuery = external_exports.object({
16702
- cursor: external_exports.string().optional(),
16703
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16704
- severity: external_exports.string().optional(),
16705
- category: external_exports.string().optional(),
16706
- eventId: external_exports.guid().optional()
16707
- });
16708
- var ListFindingsResponse = external_exports.object({
16709
- items: external_exports.array(Finding),
16710
- nextCursor: external_exports.string().nullable()
16711
- }).meta({ id: "ListFindingsResponse" });
16712
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16713
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16714
- id: "CreatePolicyRequest"
16715
- });
16716
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16717
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16718
- var ErrorResponse = external_exports.object({
16719
- error: external_exports.object({
16720
- code: external_exports.string(),
16721
- message: external_exports.string(),
16722
- details: external_exports.unknown().optional()
16723
- })
16724
- }).meta({ id: "ErrorResponse" });
16725
-
16726
- // ../../packages/schema/src/zod/config-inventory.ts
16727
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16728
- var SkillScanEntry = external_exports.object({
16729
- name: external_exports.string().min(1),
16730
- // The identity source: a marketplace repo for plugin skills (e.g.
16731
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16732
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16733
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16734
- source: external_exports.string().min(1),
16735
- scope: ConfigScope,
16736
- pluginName: external_exports.string().optional(),
16737
- // Volatile — rides the attribute bag, never the identity hash.
16738
- version: external_exports.string().optional(),
16739
- description: external_exports.string().optional(),
16740
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16741
- updatedAt: external_exports.iso.datetime().optional(),
16742
- // Filesystem path — the promoted inventory `location` column.
16743
- location: external_exports.string().optional()
16744
- });
16745
- var HookScanEntry = external_exports.object({
16746
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16747
- // set is harness-defined and grows without a schema change.
16748
- event: external_exports.string().min(1),
16749
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16750
- matcher: external_exports.string().optional(),
16751
- command: external_exports.string().min(1),
16752
- timeout: external_exports.number().optional(),
16753
- scope: ConfigScope,
16754
- pluginName: external_exports.string().optional(),
16755
- // The settings file / hooks.json the entry came from.
16756
- location: external_exports.string().optional()
16757
- });
16758
- var McpServerScanEntry = external_exports.object({
16759
- // The server's config key ("github", "filesystem", …) — identity, with the
16760
- // qualified scope (see mcpServerIdentityKey).
16761
- name: external_exports.string().min(1),
16762
- scope: ConfigScope,
16763
- pluginName: external_exports.string().optional(),
16764
- // The owning plugin's marketplace — part of PLUGIN-scope identity: two
16765
- // marketplaces can each ship a plugin named `guard`, and without this their
16766
- // same-named servers would collapse to one row (the second silently dropped,
16767
- // inheriting the first's trust).
16768
- marketplace: external_exports.string().optional(),
16769
- // The repo identity (remote url, or the cwd for un-remoted repos) — part of
16770
- // PROJECT/LOCAL-scope identity: a server named `github` in repo A and one in
16771
- // repo B are different servers with different commands, and MUST NOT share a
16772
- // row — a shared row would let a cloned repo's .mcp.json inherit the trust
16773
- // the user granted elsewhere.
16774
- project: external_exports.string().optional(),
16775
- // 'stdio' when the entry carries a command; otherwise the config's `type`
16776
- // ('http' / 'sse' / …). Open string — the transport set is harness-defined.
16777
- transport: external_exports.string().min(1),
16778
- // Volatile on purpose (unlike hook `command`): a changed command/url is drift
16779
- // on a stable row — visible across config_scan snapshots and preserving the
16780
- // user's trust decision — never a quiet new row. One of the two is present.
16781
- // Secret-masked at collection time (the scanner runs the bundled detection
16782
- // packs over both — tokens routinely ride command args and URLs).
16783
- command: external_exports.string().optional(),
16784
- url: external_exports.string().optional(),
16785
- // Env var NAMES only, never values (the no-secrets rule).
16786
- envKeys: external_exports.array(external_exports.string()).optional(),
16787
- // The config file the entry came from.
16788
- location: external_exports.string().optional()
16789
- });
16790
- var ConfigFileScanEntry = external_exports.object({
16791
- // Basename (settings.json, CLAUDE.md) or dir name (commands/, agents/).
16792
- name: external_exports.string().min(1),
16793
- // The absolute path — identity (with scope) and the promoted `location`.
16794
- path: external_exports.string().min(1),
16795
- scope: ConfigScope,
16796
- // Human label: "User settings", "Project memory", "Slash commands", …
16797
- kind: external_exports.string().min(1),
16798
- // Derived SHAPE summary — top-level key names, entry counts, line counts.
16799
- // Never file content or values (memory files can carry sensitive detail).
16800
- detail: external_exports.string().optional(),
16801
- // Dir configs (commands/, agents/) and .mcp.json: how many entries.
16802
- entryCount: external_exports.number().optional(),
16803
- // File mtime (ISO) — the freshness signal.
16804
- updatedAt: external_exports.iso.datetime().optional()
16805
- });
16806
- var ConfigScanResult = external_exports.object({
16807
- scannedAt: external_exports.iso.datetime(),
16808
- skills: external_exports.array(SkillScanEntry),
16809
- hooks: external_exports.array(HookScanEntry),
16810
- mcpServers: external_exports.array(McpServerScanEntry),
16811
- configFiles: external_exports.array(ConfigFileScanEntry),
16812
- errors: external_exports.array(external_exports.object({ source: external_exports.string(), reason: external_exports.string() }))
16813
- });
16814
- var ConfigPostureFindingInput = external_exports.object({
16815
- ruleId: external_exports.string().min(1),
16816
- version: external_exports.string().min(1),
16817
- span: Span,
16818
- // For posture rules this is the offending COMMAND (config the user already
16819
- // holds locally, not captured secret content) — it is also the correlation
16820
- // key the read surface matches back to a hook row.
16821
- maskedMatch: external_exports.string(),
16822
- actionTaken: ActionTaken,
16823
- confidence: external_exports.number().min(0).max(1)
16824
- });
16825
- var ConfigScanRecord = external_exports.object({
16826
- items: external_exports.array(InventoryInput),
16827
- scanEvent: AuditEventInput,
16828
- definitions: external_exports.array(InspectionDefinitionInput).optional(),
16829
- findings: external_exports.array(ConfigPostureFindingInput).optional()
16830
- });
16831
-
16832
- // ../../packages/schema/src/zod/registry.ts
16833
- var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16834
- var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16835
- var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16836
- var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16374
+ path: ["pattern"]
16375
+ }).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
16376
+ 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',
16377
+ path: ["pattern"]
16378
+ }).superRefine((v, ctx) => {
16379
+ if (v.captureGroup === void 0) return;
16380
+ const groups = captureGroupCount(v.pattern, v.flags);
16381
+ if (groups === void 0 || v.captureGroup <= groups) return;
16382
+ ctx.addIssue({
16383
+ code: "custom",
16384
+ path: ["captureGroup"],
16385
+ 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.`
16386
+ });
16387
+ });
16388
+ var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
16389
+ var MATCHER_TYPES = MatcherType.options;
16390
+ var AppliesTo = external_exports.strictObject({
16391
+ // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16392
+ extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16393
+ }).meta({ id: "AppliesTo" });
16394
+ var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
16395
+ var PostValidatorRef = external_exports.union(
16396
+ [
16397
+ PostValidatorName,
16398
+ external_exports.strictObject({
16399
+ name: PostValidatorName,
16400
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16401
+ })
16402
+ ],
16403
+ {
16404
+ // A union reports one collapsed issue for every way its arms can fail, so
16405
+ // this has to describe the whole shape rather than just the name — it is
16406
+ // what an author sees for a misspelled name AND for a stray key in the
16407
+ // object form. The names come from the enum so the message cannot go
16408
+ // stale. Without it Zod says only "Invalid input", which is precisely the
16409
+ // no-feedback outcome this schema exists to remove.
16410
+ error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
16411
+ " or "
16412
+ )}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
16413
+ }
16414
+ ).meta({ id: "PostValidatorRef" });
16415
+ var RequiresNearby = external_exports.strictObject({
16416
+ // Each array, when present, must be non-empty and contain non-empty strings —
16417
+ // an empty/blank criterion would either never fire or (for labels) match
16418
+ // everything.
16419
+ categories: external_exports.array(DetectionCategory).min(1).optional(),
16420
+ ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16421
+ labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16422
+ windowChars: external_exports.number().int().positive().default(160),
16423
+ // Optional confidence bump applied when a gated match is corroborated. Capped
16424
+ // small: it nudges confidence, it does not assert certainty.
16425
+ confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16426
+ }).refine(
16427
+ (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16428
+ { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16429
+ ).meta({ id: "RequiresNearby" });
16430
+ var RuleFixture = external_exports.strictObject({
16431
+ label: external_exports.string(),
16432
+ text: external_exports.string().max(5e4),
16433
+ shouldMatch: external_exports.boolean(),
16434
+ // Simulated file context for the scan, so fixtures can assert `appliesTo`
16435
+ // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16436
+ filePath: external_exports.string().optional(),
16437
+ expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
16438
+ }).meta({ id: "RuleFixture" });
16439
+ var Rule = external_exports.strictObject({
16440
+ // A pinned literal over a STRICT object, and the two together decide how this
16441
+ // format may grow. A rule carrying a key not listed below is refused with
16442
+ // `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
16443
+ // `invalid_value`. So the only additive path is adding an OPTIONAL field here
16444
+ // — that keeps every rule authored before it valid — and a rule author has no
16445
+ // way to introduce a field of their own or to opt into a later version.
16446
+ // Widening the format means changing this literal and every consumer of it.
16447
+ specVersion: external_exports.literal(1),
16448
+ // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16449
+ // the PACK id, NOT a namespace this is a DIFFERENT slug space from a
16450
+ // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16451
+ // therefore carries no namespace and is not globally unique across publishers;
16452
+ // never feed one to splitDetectionId. `category` below (per-rule) is the
16453
+ // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16454
+ id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16455
+ name: external_exports.string(),
16456
+ category: DetectionCategory,
16457
+ severity: Severity,
16458
+ matcher: Matcher,
16459
+ appliesTo: AppliesTo.optional(),
16460
+ postValidators: external_exports.array(PostValidatorRef).optional(),
16461
+ requiresNearby: RequiresNearby.optional(),
16462
+ examples: external_exports.array(external_exports.string()).optional()
16463
+ }).meta({ id: "Rule" });
16464
+ var Author = external_exports.object({
16465
+ name: external_exports.string(),
16466
+ email: external_exports.email().optional(),
16467
+ url: external_exports.url().optional()
16468
+ }).meta({ id: "Author" });
16469
+ var PackManifest = external_exports.object({
16470
+ specVersion: external_exports.literal(1),
16471
+ id: external_exports.string(),
16472
+ name: external_exports.string(),
16473
+ version: external_exports.string(),
16474
+ rules: external_exports.array(external_exports.string()),
16475
+ // Optional attribution/provenanceconsumed by the rule marketplace.
16476
+ description: external_exports.string().optional(),
16477
+ author: Author.optional(),
16478
+ license: external_exports.string().optional(),
16479
+ sourceUrl: external_exports.url().optional()
16480
+ }).meta({ id: "PackManifest" });
16837
16481
 
16838
16482
  // ../../packages/schema/src/zod/detection.ts
16839
16483
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
@@ -17033,6 +16677,231 @@ function buildDetectionsList(summaries, query) {
17033
16677
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17034
16678
  }
17035
16679
 
16680
+ // ../../packages/schema/src/zod/inventory.ts
16681
+ var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16682
+ var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16683
+ var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16684
+ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16685
+ var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16686
+ var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16687
+ var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16688
+ var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
16689
+ id: "HarnessId"
16690
+ });
16691
+ var AccessCounts = external_exports.object({
16692
+ open: external_exports.number().int().nonnegative(),
16693
+ approved: external_exports.number().int().nonnegative(),
16694
+ blocked: external_exports.number().int().nonnegative(),
16695
+ total: external_exports.number().int().nonnegative()
16696
+ }).meta({ id: "AccessCounts" });
16697
+ var AssetSummary = external_exports.object({
16698
+ id: external_exports.string(),
16699
+ type: AssetType,
16700
+ name: external_exports.string(),
16701
+ sub: external_exports.string(),
16702
+ flags: external_exports.array(Flag),
16703
+ /** MCP servers only — omitted for all other types. */
16704
+ trust: TrustLevel.optional()
16705
+ }).meta({ id: "AssetSummary" });
16706
+ var ProjectSummary = external_exports.object({
16707
+ id: external_exports.string(),
16708
+ name: external_exports.string(),
16709
+ repo: external_exports.string(),
16710
+ visibility: Visibility,
16711
+ language: external_exports.string(),
16712
+ policyDefault: AccessLevel,
16713
+ updatedAt: external_exports.iso.datetime(),
16714
+ accessCounts: AccessCounts,
16715
+ findingsCount: external_exports.number().int().nonnegative()
16716
+ }).meta({ id: "ProjectSummary" });
16717
+ var HarnessCategory = external_exports.object({
16718
+ /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16719
+ type: AssetType,
16720
+ assets: external_exports.array(AssetSummary)
16721
+ });
16722
+ var HarnessSummary = external_exports.object({
16723
+ id: HarnessId,
16724
+ label: external_exports.string(),
16725
+ kind: external_exports.string(),
16726
+ version: external_exports.string(),
16727
+ sessions: external_exports.number().int().nonnegative(),
16728
+ assetCount: external_exports.number().int().nonnegative(),
16729
+ flagCount: external_exports.number().int().nonnegative(),
16730
+ projects: external_exports.array(ProjectSummary),
16731
+ categories: external_exports.array(HarnessCategory)
16732
+ }).meta({ id: "HarnessSummary" });
16733
+ var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16734
+ var AssetGroup = external_exports.object({
16735
+ /** Group key — never project (enforced at service layer). */
16736
+ type: AssetType,
16737
+ total: external_exports.number().int().nonnegative(),
16738
+ /**
16739
+ * MCP group only — omitted for all other types.
16740
+ * Partial: only TrustLevel keys with non-zero counts are included.
16741
+ * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16742
+ */
16743
+ trustRollup: external_exports.object({
16744
+ "known-good": external_exports.number().int().nonnegative(),
16745
+ risky: external_exports.number().int().nonnegative(),
16746
+ unapproved: external_exports.number().int().nonnegative()
16747
+ }).partial().strict().optional(),
16748
+ /**
16749
+ * Partial: only Flag keys with non-zero counts are included.
16750
+ * Strict: unknown keys are rejected — only Flag values are valid keys.
16751
+ */
16752
+ flagRollup: external_exports.object({
16753
+ update: external_exports.number().int().nonnegative(),
16754
+ stale: external_exports.number().int().nonnegative(),
16755
+ conflict: external_exports.number().int().nonnegative(),
16756
+ unknown: external_exports.number().int().nonnegative(),
16757
+ change: external_exports.number().int().nonnegative(),
16758
+ untracked: external_exports.number().int().nonnegative(),
16759
+ risk: external_exports.number().int().nonnegative(),
16760
+ findings: external_exports.number().int().nonnegative()
16761
+ }).partial().strict(),
16762
+ items: external_exports.array(AssetSummary)
16763
+ }).meta({ id: "AssetGroup" });
16764
+ var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16765
+ var McpTool = external_exports.object({
16766
+ name: external_exports.string(),
16767
+ signature: external_exports.string(),
16768
+ description: external_exports.string(),
16769
+ write: external_exports.boolean(),
16770
+ /** Non-null string when tool is dangerous / blocked; null otherwise. */
16771
+ risk: external_exports.string().nullable()
16772
+ }).meta({ id: "McpTool" });
16773
+ var AssetFindingRef = external_exports.object({
16774
+ id: external_exports.string(),
16775
+ title: external_exports.string(),
16776
+ note: external_exports.string()
16777
+ });
16778
+ var AssetDetail = AssetSummary.extend({
16779
+ /** string | null — null when no description is available. */
16780
+ description: external_exports.string().nullable(),
16781
+ /** trustLevel | null — null for non-MCP assets. */
16782
+ trust: TrustLevel.nullable(),
16783
+ /** Type-specific raw key/values — FE renders the grid. */
16784
+ meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16785
+ /** always present — object when there is an active finding, null when absent. */
16786
+ finding: AssetFindingRef.nullable(),
16787
+ /** MCP exposed-tools list — omitted for non-mcp. */
16788
+ tools: external_exports.array(McpTool).optional()
16789
+ }).meta({ id: "AssetDetail" });
16790
+ var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16791
+ var InventoryStats = external_exports.object({
16792
+ /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16793
+ attention: external_exports.number().int().nonnegative(),
16794
+ byType: external_exports.object({
16795
+ project: external_exports.number().int().nonnegative(),
16796
+ skill: external_exports.number().int().nonnegative(),
16797
+ mcp: external_exports.number().int().nonnegative(),
16798
+ hook: external_exports.number().int().nonnegative(),
16799
+ config: external_exports.number().int().nonnegative()
16800
+ }),
16801
+ harnesses: external_exports.number().int().nonnegative(),
16802
+ mcpTrust: external_exports.object({
16803
+ "known-good": external_exports.number().int().nonnegative(),
16804
+ risky: external_exports.number().int().nonnegative(),
16805
+ unapproved: external_exports.number().int().nonnegative()
16806
+ })
16807
+ }).meta({ id: "InventoryStats" });
16808
+ var FileSummary = external_exports.object({
16809
+ path: external_exports.string(),
16810
+ name: external_exports.string(),
16811
+ origin: Origin,
16812
+ /** Effective access (override applied). */
16813
+ access: AccessLevel,
16814
+ /** True when a file_access_override differs from the computed default. */
16815
+ isCustom: external_exports.boolean(),
16816
+ findings: external_exports.number().int().nonnegative(),
16817
+ /** When the file was auto-blocked by a detection; null when not blocked. */
16818
+ blockedAt: external_exports.iso.datetime().nullable().optional(),
16819
+ /** Why the file was blocked; null when absent. */
16820
+ note: external_exports.string().nullable().optional()
16821
+ }).meta({ id: "FileSummary" });
16822
+ var FolderSummary = external_exports.object({
16823
+ name: external_exports.string(),
16824
+ path: external_exports.string(),
16825
+ /** Rollup of effective access across all descendants. */
16826
+ accessCounts: AccessCounts
16827
+ }).meta({ id: "FolderSummary" });
16828
+ var ProjectTreeResponse = external_exports.object({
16829
+ project: external_exports.object({
16830
+ id: external_exports.string(),
16831
+ repo: external_exports.string(),
16832
+ visibility: Visibility
16833
+ }),
16834
+ path: external_exports.string(),
16835
+ /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16836
+ folders: external_exports.array(FolderSummary).optional(),
16837
+ files: external_exports.array(FileSummary)
16838
+ }).meta({ id: "ProjectTreeResponse" });
16839
+ var FileDetail = FileSummary.extend({
16840
+ project: external_exports.object({
16841
+ repo: external_exports.string(),
16842
+ visibility: Visibility,
16843
+ language: external_exports.string(),
16844
+ policyDefault: AccessLevel,
16845
+ updatedAt: external_exports.iso.datetime()
16846
+ }),
16847
+ findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16848
+ }).meta({ id: "FileDetail" });
16849
+ var SetFileAccessBody = external_exports.object({
16850
+ path: external_exports.string(),
16851
+ access: AccessLevel
16852
+ }).meta({ id: "SetFileAccessBody" });
16853
+ var SetFileAccessResponse = external_exports.object({
16854
+ file: FileSummary,
16855
+ accessCounts: AccessCounts
16856
+ }).meta({ id: "SetFileAccessResponse" });
16857
+ var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16858
+ var HarnessEventItem = external_exports.object({
16859
+ kind: HarnessEventKind,
16860
+ title: external_exports.string(),
16861
+ detail: external_exports.string(),
16862
+ occurredAt: external_exports.iso.datetime(),
16863
+ findingId: external_exports.string().nullable().optional()
16864
+ }).meta({ id: "HarnessEventItem" });
16865
+ var HarnessEventsResponse = external_exports.object({
16866
+ counts: external_exports.object({
16867
+ block: external_exports.number().int().nonnegative(),
16868
+ redact: external_exports.number().int().nonnegative(),
16869
+ warn: external_exports.number().int().nonnegative()
16870
+ }),
16871
+ items: external_exports.array(HarnessEventItem)
16872
+ }).meta({ id: "HarnessEventsResponse" });
16873
+ var RescanResponse = external_exports.object({
16874
+ jobId: external_exports.string(),
16875
+ startedAt: external_exports.iso.datetime()
16876
+ }).meta({ id: "RescanResponse" });
16877
+ var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16878
+ var ListAssetsQuery = external_exports.object({
16879
+ /** Filter by one or more AssetType values; absent means all types. */
16880
+ type: external_exports.array(AssetType).optional(),
16881
+ /** Free-text search term. */
16882
+ q: external_exports.string().optional()
16883
+ });
16884
+ var GetProjectTreeQuery = external_exports.object({
16885
+ /** Subtree root path; defaults to repository root when absent. */
16886
+ path: external_exports.string().optional(),
16887
+ /** Free-text filter applied to file paths. */
16888
+ q: external_exports.string().optional(),
16889
+ /**
16890
+ * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16891
+ * file across the whole repo (folders omitted, most-recent first), ignoring
16892
+ * `path`/`q` — powers the project-wide "recently blocked" strip.
16893
+ */
16894
+ filter: external_exports.enum(["blocked"]).optional()
16895
+ });
16896
+ var GetProjectFileQuery = external_exports.object({
16897
+ /** Repository-relative file path; absent or empty → 400. */
16898
+ path: external_exports.string()
16899
+ });
16900
+ var GetHarnessEventsQuery = external_exports.object({
16901
+ /** Maximum number of events to return. Range: 1–50; default: 7. */
16902
+ limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16903
+ });
16904
+
17036
16905
  // ../../packages/schema/src/zod/shares.ts
17037
16906
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17038
16907
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17239,6 +17108,127 @@ var EgressWriteSummary = external_exports.object({
17239
17108
  droppedFiles: external_exports.array(external_exports.string()).default([])
17240
17109
  }).meta({ id: "EgressWriteSummary" });
17241
17110
 
17111
+ // ../../packages/schema/src/zod/event.ts
17112
+ var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
17113
+ var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
17114
+ var EventMetadata = external_exports.object({
17115
+ sessionId: external_exports.string().optional(),
17116
+ repo: external_exports.string().optional(),
17117
+ filePath: external_exports.string().optional(),
17118
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
17119
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
17120
+ // arguments or output, which can carry the very value a finding masked
17121
+ // (metadata is stored unredacted). Gives findings on non-file captures a
17122
+ // display location ("via Bash") when no filePath exists.
17123
+ toolName: external_exports.string().optional(),
17124
+ // Set (true) by the worktree scanner when the file is excluded by the
17125
+ // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
17126
+ // generated code can leak real secrets — but the provenance is recorded so
17127
+ // policy/dashboards can treat those findings as informational rather than
17128
+ // blocking. Omitted (not false) for tracked files and non-scan events.
17129
+ gitignored: external_exports.boolean().optional(),
17130
+ // Set (true) ONLY when the event's `content` is the COMPLETE file at
17131
+ // capture time (a worktree scan reading from disk). Hook-captured edits
17132
+ // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
17133
+ // this. The resolver-on-ingest keys its fixed-at-source dropout
17134
+ // diff on this marker: only a whole-file snapshot can prove a previously
17135
+ // open finding is gone; a fragment's absence proves nothing (the secret
17136
+ // may live outside the hunk). Omitted (not false) for fragments and
17137
+ // non-scan events, so pre-marker clients safely default to the
17138
+ // non-authoritative path.
17139
+ wholeFile: external_exports.boolean().optional(),
17140
+ model: external_exports.string().optional(),
17141
+ turnIndex: external_exports.number().int().nonnegative().optional(),
17142
+ // Distributed-tracing correlation. `correlationId` ties a recorded event back
17143
+ // to the request that captured/ingested it (a UUID, generated independently of
17144
+ // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
17145
+ // originating span when telemetry is enabled. Both optional + backward
17146
+ // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
17147
+ correlationId: external_exports.uuid().optional(),
17148
+ traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
17149
+ // Ids of the detection exceptions that downgraded findings in this capture
17150
+ // to 'allow' — the enforcement audit trail's link back to the grant that
17151
+ // authorized the bypass. Absent on captures where no exception applied.
17152
+ exceptionIds: external_exports.array(external_exports.guid()).optional()
17153
+ }).meta({ id: "EventMetadata" });
17154
+ var Event = external_exports.object({
17155
+ id: external_exports.guid(),
17156
+ sourceTool: SourceTool,
17157
+ kind: EventKind,
17158
+ occurredAt: external_exports.iso.datetime(),
17159
+ contentHash: external_exports.string(),
17160
+ content: external_exports.string(),
17161
+ metadata: EventMetadata.optional()
17162
+ }).meta({ id: "Event" });
17163
+ var IngestEvent = Event.meta({ id: "IngestEvent" });
17164
+ var IngestBatch = external_exports.object({
17165
+ events: external_exports.array(IngestEvent).min(1).max(100),
17166
+ // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
17167
+ // additionally rejects any event whose contentHash the store has already
17168
+ // recorded — for re-runnable bulk ingest (worktree scan, transcript
17169
+ // backfill), where a re-run mints fresh event ids for identical content and
17170
+ // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
17171
+ // two genuinely separate prompts can be byte-identical and both belong on
17172
+ // the timeline.
17173
+ dedupe: external_exports.literal("content-hash").optional()
17174
+ }).meta({ id: "IngestBatch" });
17175
+
17176
+ // ../../packages/schema/src/zod/exception.ts
17177
+ var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
17178
+ var ExceptionConditions = external_exports.object({
17179
+ repo: external_exports.string().optional(),
17180
+ sourceTool: external_exports.string().optional(),
17181
+ provider: external_exports.string().optional()
17182
+ }).strict();
17183
+ var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
17184
+ var DetectionException = external_exports.object({
17185
+ id: external_exports.guid(),
17186
+ ruleId: external_exports.string(),
17187
+ // Denormalized from the rule, for reporting — never matched on.
17188
+ category: DetectionCategory,
17189
+ // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
17190
+ // fingerprint, never the raw value, and never reversible. Matching recomputes
17191
+ // the fingerprint from a fresh capture; the value itself is never stored.
17192
+ // Shape-constrained so a malformed — or accidentally raw — value is rejected
17193
+ // at the boundary rather than persisted.
17194
+ valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
17195
+ // Version of the fingerprint key the grant was written under; a rotated key
17196
+ // invalidates old grants rather than silently mismatching them.
17197
+ keyVersion: external_exports.number().int().positive(),
17198
+ // maskMatch() preview of the approved value — never the raw value.
17199
+ maskedValue: external_exports.string(),
17200
+ capability: ExceptionCapability.default("suppress"),
17201
+ scope: ExceptionScope,
17202
+ expiresAt: external_exports.iso.datetime().nullable(),
17203
+ maxUses: external_exports.number().int().positive().nullable(),
17204
+ useCount: external_exports.number().int().nonnegative(),
17205
+ lastUsedAt: external_exports.iso.datetime().nullable(),
17206
+ // Mandatory: every grant carries the human reason it exists.
17207
+ justification: external_exports.string().min(1),
17208
+ conditions: ExceptionConditions.nullable(),
17209
+ createdBy: external_exports.string(),
17210
+ createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
17211
+ createdAt: external_exports.iso.datetime(),
17212
+ updatedAt: external_exports.iso.datetime(),
17213
+ // Revocation is terminal and retained — consumed/expired/revoked rows are
17214
+ // audit evidence; nothing in the exception lifecycle hard-deletes.
17215
+ revokedAt: external_exports.iso.datetime().nullable(),
17216
+ revokedBy: external_exports.string().nullable(),
17217
+ revokeReason: external_exports.string().nullable()
17218
+ });
17219
+ var ExceptionBundleEntry = DetectionException.pick({
17220
+ id: true,
17221
+ ruleId: true,
17222
+ valueFingerprint: true,
17223
+ keyVersion: true,
17224
+ capability: true,
17225
+ expiresAt: true,
17226
+ maxUses: true,
17227
+ useCount: true,
17228
+ conditions: true
17229
+ });
17230
+ var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
17231
+
17242
17232
  // ../../packages/schema/src/zod/exception-action.ts
17243
17233
  var confirmation = external_exports.string().optional();
17244
17234
  var ApproveBlockedInput = external_exports.object({
@@ -17281,10 +17271,11 @@ function toApiAction(dbVal) {
17281
17271
  }
17282
17272
  function toApiCategory(dbVal) {
17283
17273
  if (dbVal === "code_context") return "source_code";
17284
- return dbVal;
17274
+ const parsed = FindingCategory.safeParse(dbVal);
17275
+ return parsed.success ? parsed.data : "custom";
17285
17276
  }
17286
17277
  function toApiProvider(sourceTool) {
17287
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17278
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17288
17279
  }
17289
17280
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17290
17281
  function foldGroupStatus(instanceStatuses) {
@@ -17858,25 +17849,35 @@ var VaultConsent = external_exports.object({
17858
17849
  acknowledgedAt: external_exports.iso.datetime(),
17859
17850
  version: external_exports.number().int().positive()
17860
17851
  });
17852
+ function isVaultConsentValid(consent) {
17853
+ return consent?.version === VAULT_CONSENT_VERSION;
17854
+ }
17861
17855
 
17862
17856
  // ../../packages/schema/src/zod/local.ts
17863
17857
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17864
17858
  var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17865
- var RunMode = external_exports.enum(["standalone"]);
17859
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17860
+ var ControlPlaneConnection = external_exports.object({
17861
+ endpoint: external_exports.string().min(1),
17862
+ // Display name for the deployment, shown instead of the raw endpoint.
17863
+ label: external_exports.string().min(1).optional(),
17864
+ attachedAt: external_exports.iso.datetime()
17865
+ }).meta({ id: "ControlPlaneConnection" });
17866
17866
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17867
17867
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17868
17868
  var ModelJudgeConsent = external_exports.object({
17869
17869
  acknowledgedAt: external_exports.iso.datetime(),
17870
17870
  payloadVersion: external_exports.number().int().positive()
17871
17871
  });
17872
+ function isModelJudgeConsentValid(consent) {
17873
+ return consent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION;
17874
+ }
17872
17875
  var WorkspaceSettings = external_exports.object({
17873
17876
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17874
- // Settings files written by earlier releases may carry the retired 'attached'
17875
- // value; it parses as 'standalone' so those files keep loading.
17876
- runMode: external_exports.preprocess(
17877
- (v) => v === "attached" ? "standalone" : v,
17878
- RunMode.default("standalone")
17879
- ),
17877
+ runMode: RunMode.default("standalone"),
17878
+ // Present only while attached; a detach clears it. Its presence is what makes
17879
+ // `runMode: 'attached'` mean anything — see isAttached.
17880
+ controlPlane: ControlPlaneConnection.optional(),
17880
17881
  policy: SimpleDetectionPolicy.default("redact"),
17881
17882
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17882
17883
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -17998,19 +17999,230 @@ function toCaptureAttributes(event) {
17998
17999
  ...metadata?.turnIndex !== void 0 ? { turn_index: metadata.turnIndex } : {}
17999
18000
  };
18000
18001
  }
18001
- function captureDefinitionVersion(finding) {
18002
- return `capture/${finding.category}/${finding.severity}`;
18002
+ function captureDefinitionVersion(finding) {
18003
+ return `capture/${finding.category}/${finding.severity}`;
18004
+ }
18005
+ function toCaptureDefinitionInput(finding) {
18006
+ return {
18007
+ ruleId: finding.ruleId,
18008
+ version: captureDefinitionVersion(finding),
18009
+ name: finding.ruleId,
18010
+ category: finding.category,
18011
+ severity: finding.severity,
18012
+ definition: JSON.stringify({ ruleId: finding.ruleId })
18013
+ };
18014
+ }
18015
+
18016
+ // ../../packages/schema/src/zod/managed.ts
18017
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
18018
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
18019
+ var ManagedSettingKey = external_exports.enum([
18020
+ "runMode",
18021
+ "historicalAccess",
18022
+ "vaultConsent",
18023
+ "vaultKeyCustody",
18024
+ "vaultInlineReveal",
18025
+ "modelJudgeConsent",
18026
+ "dataSharesInPlace"
18027
+ ]).meta({ id: "ManagedSettingKey" });
18028
+ var ManagedSettingsValues = external_exports.object({
18029
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
18030
+ controlPlane: external_exports.object({
18031
+ endpoint: external_exports.string().min(1),
18032
+ label: external_exports.string().min(1).optional()
18033
+ }).optional(),
18034
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
18035
+ vaultConsent: external_exports.boolean().optional(),
18036
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
18037
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
18038
+ modelJudgeConsent: external_exports.boolean().optional(),
18039
+ dataSharesInPlace: external_exports.boolean().optional()
18040
+ }).meta({ id: "ManagedSettingsValues" });
18041
+ var ManagedSettings = external_exports.object({
18042
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
18043
+ // Shown on every locked control, so the user can tell an administrative
18044
+ // decision from a bug. Absent renders as a generic "your organization".
18045
+ organization: external_exports.string().min(1).optional(),
18046
+ // What the administrator pinned.
18047
+ values: ManagedSettingsValues.default({}),
18048
+ // Which of those the user may not change. A key here with no matching value
18049
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
18050
+ // the user may still override. The two are separable on purpose.
18051
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
18052
+ }).meta({ id: "ManagedSettings" });
18053
+ var NO_MANAGED_CONTEXT = { present: false, lockedFields: [] };
18054
+
18055
+ // ../../packages/schema/src/zod/policy.ts
18056
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
18057
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
18058
+ var Policy = external_exports.object({
18059
+ id: external_exports.guid(),
18060
+ scope: PolicyScope,
18061
+ target: PolicyTarget,
18062
+ action: ActionTaken,
18063
+ enabled: external_exports.boolean().default(true),
18064
+ customKeywords: external_exports.array(external_exports.string()).optional(),
18065
+ // Display name — optional so older policy rows without name still parse.
18066
+ // Added for the findings API (policy.name column migration).
18067
+ name: external_exports.string().optional()
18068
+ }).meta({ id: "Policy" });
18069
+ var PolicyBundle = external_exports.object({
18070
+ version: external_exports.string(),
18071
+ policies: external_exports.array(Policy),
18072
+ // Rules from the installed marketplace packs (snapshotted by the
18073
+ // control plane). The plugin registers these in addition to its bundled
18074
+ // packs. Optional so older backends — and older on-disk caches — that omit
18075
+ // the field still parse; consumers read `bundle.rules ?? []`.
18076
+ rules: external_exports.array(Rule).optional(),
18077
+ // When true, `rules` IS the complete effective ruleset and the runtime must
18078
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
18079
+ // after reading the user's installed snapshot (installed_packs, enabled
18080
+ // packs only), which is how detection updates stay manual: new bundled
18081
+ // rules run only after the user applies the pack update. Absent/false keeps
18082
+ // the historical composition (bundled packs + rules) — older caches.
18083
+ rulesComplete: external_exports.boolean().optional(),
18084
+ // Active detection exceptions, evaluation subset only (see
18085
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
18086
+ // on-disk caches — that omit the field still parse; consumers read
18087
+ // `bundle.exceptions ?? []`.
18088
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
18089
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
18090
+ // A second axis over the same `redact` action, carried beside the policies
18091
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
18092
+ // widening Policy itself would change a persisted shape to express something
18093
+ // only the in-memory bundle needs. Optional so an older producer — or an
18094
+ // older on-disk cache — still parses; consumers read `?? []` and get the
18095
+ // pre-existing one-way behaviour, which is the safe direction to default.
18096
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
18097
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
18098
+ // from a versioned installed pack. Optional so older backends — and older
18099
+ // on-disk caches — that omit the field still parse; consumers fall back to
18100
+ // the rule's own spec version. NOT the bundle version above — see
18101
+ // installedRuleset's ruleVersions for the source of truth.
18102
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
18103
+ customKeywords: external_exports.array(external_exports.string()),
18104
+ fetchedAt: external_exports.iso.datetime()
18105
+ }).meta({ id: "PolicyBundle" });
18106
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
18107
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
18108
+ var CATEGORY_PEAK_SEVERITY = {
18109
+ secret: "critical",
18110
+ financial: "critical",
18111
+ // core-financial/credit-card
18112
+ code_flaw: "critical",
18113
+ pii: "high",
18114
+ phi: "high",
18115
+ custom: "high",
18116
+ // user-defined; conservative
18117
+ code_context: "low",
18118
+ config: "low"
18119
+ // observe-only; floors to monitor regardless
18120
+ };
18121
+ function severityFloorPolicy(category) {
18122
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18123
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18124
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18125
+ }
18126
+ function severityFloorPosture() {
18127
+ const out = {};
18128
+ for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
18129
+ return out;
18130
+ }
18131
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18132
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18133
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18134
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18135
+ var BUILTIN_POLICY_SPECS = {
18136
+ monitor: {
18137
+ name: "Monitor",
18138
+ action: "log",
18139
+ reversible: false,
18140
+ description: "Log every match for audit. The request is allowed through untouched."
18141
+ },
18142
+ warn: {
18143
+ name: "Warn",
18144
+ action: "warn",
18145
+ reversible: false,
18146
+ description: "Allow the request, but warn the user inline before it is sent."
18147
+ },
18148
+ redact: {
18149
+ name: "Redact",
18150
+ action: "redact",
18151
+ reversible: false,
18152
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18153
+ },
18154
+ vault: {
18155
+ name: "Redact & Vault",
18156
+ action: "redact",
18157
+ reversible: true,
18158
+ 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."
18159
+ },
18160
+ block: {
18161
+ name: "Block",
18162
+ action: "block",
18163
+ reversible: false,
18164
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18165
+ }
18166
+ };
18167
+ function builtinPolicyToAction(id) {
18168
+ return BUILTIN_POLICY_SPECS[id].action;
18169
+ }
18170
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18171
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18172
+ );
18173
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18174
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18175
+ );
18176
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18177
+ function builtinPolicyIsReversible(id) {
18178
+ return BUILTIN_POLICY_SPECS[id].reversible;
18003
18179
  }
18004
- function toCaptureDefinitionInput(finding) {
18005
- return {
18006
- ruleId: finding.ruleId,
18007
- version: captureDefinitionVersion(finding),
18008
- name: finding.ruleId,
18009
- category: finding.category,
18010
- severity: finding.severity,
18011
- definition: JSON.stringify({ ruleId: finding.ruleId })
18012
- };
18180
+ function policyIdIsReversible(policyId) {
18181
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18182
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18183
+ return builtinPolicyIsReversible(id);
18184
+ }
18185
+ var DEFAULT_ACTIONS = Object.fromEntries(
18186
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18187
+ );
18188
+ var BUILTIN_POLICIES = Object.fromEntries(
18189
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18190
+ );
18191
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18192
+ function policyIdToAction(policyId) {
18193
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18194
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18195
+ return BUILTIN_POLICIES[id].action;
18013
18196
  }
18197
+ var UsedByItem = external_exports.object({
18198
+ id: external_exports.string(),
18199
+ name: external_exports.string(),
18200
+ ruleCount: external_exports.number().int().nonnegative(),
18201
+ enabled: external_exports.boolean()
18202
+ }).meta({ id: "UsedByItem" });
18203
+ var PolicyListItem = external_exports.object({
18204
+ id: external_exports.string(),
18205
+ kind: PolicyKind,
18206
+ name: external_exports.string(),
18207
+ enabled: external_exports.boolean(),
18208
+ usedByCount: external_exports.number().int().nonnegative()
18209
+ }).meta({ id: "PolicyListItem" });
18210
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18211
+ var PolicyDetail = external_exports.object({
18212
+ specVersion: external_exports.literal(1),
18213
+ id: external_exports.string(),
18214
+ kind: PolicyKind,
18215
+ name: external_exports.string(),
18216
+ enabled: external_exports.boolean(),
18217
+ description: external_exports.string(),
18218
+ usedBy: external_exports.array(UsedByItem)
18219
+ }).meta({ id: "PolicyDetail" });
18220
+ var PolicyStatsResponse = external_exports.object({
18221
+ policies: external_exports.number().int().nonnegative(),
18222
+ builtin: external_exports.number().int().nonnegative(),
18223
+ custom: external_exports.number().int().nonnegative(),
18224
+ detectionsGoverned: external_exports.number().int().nonnegative()
18225
+ }).meta({ id: "PolicyStatsResponse" });
18014
18226
 
18015
18227
  // ../../packages/schema/src/zod/project-files.ts
18016
18228
  var ProjectFileInput = external_exports.object({
@@ -18090,44 +18302,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18090
18302
  NoRemediationDecision
18091
18303
  ]);
18092
18304
 
18093
- // ../../packages/schema/src/zod/rule-test.ts
18094
- var TestRulesRequest = external_exports.object({
18095
- rules: external_exports.array(Rule).min(1).max(100),
18096
- text: external_exports.string().max(5e4).optional(),
18097
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18098
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18099
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18100
- }).meta({ id: "TestRulesRequest" });
18101
- var RuleTestMatch = external_exports.object({
18102
- ruleId: external_exports.string(),
18103
- category: DetectionCategory,
18104
- severity: Severity,
18105
- span: Span,
18106
- confidence: external_exports.number().min(0).max(1),
18107
- match: external_exports.string()
18108
- }).meta({ id: "RuleTestMatch" });
18109
- var FixtureResult = external_exports.object({
18110
- label: external_exports.string(),
18111
- shouldMatch: external_exports.boolean(),
18112
- didMatch: external_exports.boolean(),
18113
- passed: external_exports.boolean(),
18114
- matches: external_exports.array(RuleTestMatch)
18115
- }).meta({ id: "FixtureResult" });
18116
- var TestRulesResponse = external_exports.object({
18117
- // Present only when the request supplied `text`.
18118
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18119
- fixtures: external_exports.array(FixtureResult),
18120
- summary: external_exports.object({
18121
- total: external_exports.number().int().nonnegative(),
18122
- passed: external_exports.number().int().nonnegative(),
18123
- failed: external_exports.number().int().nonnegative()
18124
- }),
18125
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18126
- // `validator`), so they silently never match. Surfaced so an author is not
18127
- // misled by a green run that actually skipped a rule.
18128
- unsupportedRuleIds: external_exports.array(external_exports.string())
18129
- }).meta({ id: "TestRulesResponse" });
18130
-
18131
18305
  // ../../packages/schema/src/zod/security.ts
18132
18306
  var SeveritySummaryItem = external_exports.object({
18133
18307
  severity: Severity,
@@ -18225,10 +18399,22 @@ var TopSourcesQuery = external_exports.object({
18225
18399
  // Omit for both kinds.
18226
18400
  kind: external_exports.enum(SOURCE_KINDS).optional()
18227
18401
  });
18228
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18402
+ var Provider = Harness.extract([
18403
+ "ClaudeCode",
18404
+ "Cursor",
18405
+ "Codex",
18406
+ "Antigravity",
18407
+ "ClaudeAi",
18408
+ "ChatGpt",
18409
+ "Copilot",
18410
+ "Api"
18411
+ ]).meta({ id: "Provider" });
18229
18412
  var ScanCoverageProvider = external_exports.object({
18230
18413
  provider: Provider,
18231
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18414
+ // Percent of that provider's traffic the shipped capture surface reaches.
18415
+ // A curated business fact, constant across every `range` — not a measured
18416
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18417
+ // above the block for where these numbers are decided.
18232
18418
  coverage: external_exports.number().int().min(0).max(100),
18233
18419
  supported: external_exports.boolean()
18234
18420
  }).meta({ id: "ScanCoverageProvider" });
@@ -18282,6 +18468,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18282
18468
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18283
18469
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18284
18470
 
18471
+ // ../../packages/schema/src/zod/settings-action.ts
18472
+ var SaveSettingsInput = external_exports.object({
18473
+ historicalAccess: external_exports.string(),
18474
+ modelJudgeConsent: external_exports.boolean(),
18475
+ vaultConsent: external_exports.string(),
18476
+ vaultInlineReveal: external_exports.string()
18477
+ });
18478
+ var AttachInput = external_exports.object({
18479
+ endpoint: external_exports.string(),
18480
+ label: external_exports.string().optional()
18481
+ });
18482
+
18285
18483
  // ../../packages/schema/src/zod/triage.ts
18286
18484
  var TriageHit = external_exports.object({
18287
18485
  ruleId: external_exports.string(),
@@ -18296,7 +18494,7 @@ var TriageHit = external_exports.object({
18296
18494
  valueFingerprint: external_exports.string().optional(),
18297
18495
  keyVersion: external_exports.number().int().nonnegative().optional()
18298
18496
  });
18299
- var TriagePolicy = BuiltinPolicyId;
18497
+ var TriagePolicy = CategoryPolicyId;
18300
18498
  var TriageCategoryRec = external_exports.object({
18301
18499
  category: DetectionCategory,
18302
18500
  action: TriagePolicy,
@@ -18436,42 +18634,6 @@ function reviewSeverityRank(reasons) {
18436
18634
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
18437
18635
  }
18438
18636
 
18439
- // ../../packages/persistence/src/ids.ts
18440
- import { createHash } from "crypto";
18441
- function sha256Hex(input) {
18442
- return createHash("sha256").update(input).digest("hex");
18443
- }
18444
- function inventoryId(objectType, identityKey) {
18445
- return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18446
- }
18447
- function sourceProjectId(url2) {
18448
- return sha256Hex(canonicalIdentity(["source_project", url2]));
18449
- }
18450
- function classifiedDataId(cls) {
18451
- return sha256Hex(canonicalIdentity(["classified_data", cls]));
18452
- }
18453
- function inspectionDefinitionId(ruleId, version2) {
18454
- return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18455
- }
18456
- function llmCallId(sessionId, messageId) {
18457
- return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18458
- }
18459
- function toolCallId(sessionId, toolUseId) {
18460
- return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18461
- }
18462
- var NO_SESSION = "no_session";
18463
- var NO_PATH = "no_path";
18464
- function captureId(sessionId, contentHash, filePath = null) {
18465
- return sha256Hex(
18466
- canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18467
- );
18468
- }
18469
-
18470
- // ../../packages/persistence/src/internal/snapshot.ts
18471
- import { randomUUID } from "crypto";
18472
- import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
18473
- import { basename, dirname, join } from "path";
18474
-
18475
18637
  // ../../packages/persistence/src/paths.ts
18476
18638
  import {
18477
18639
  chmodSync,
@@ -18503,8 +18665,11 @@ function chmodBestEffort(path, mode) {
18503
18665
  function tightenDir(dir) {
18504
18666
  chmodBestEffort(dir, DATA_DIR_MODE);
18505
18667
  }
18668
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18669
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18670
+ }
18506
18671
  function ensureDataDirSync(dir) {
18507
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18672
+ mkdirOwnerOnlySync(dir, true);
18508
18673
  tightenDir(dir);
18509
18674
  }
18510
18675
  function dbSidecars(file2) {
@@ -18516,6 +18681,9 @@ function tightenFile(file2) {
18516
18681
  function tightenPerms(file2) {
18517
18682
  for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
18518
18683
  }
18684
+ function writeExclusiveOwnerOnlySync(file2, data) {
18685
+ writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
18686
+ }
18519
18687
  function writeOwnerOnlyFileSync(file2, data) {
18520
18688
  const tmp = `${file2}.${String(process.pid)}.tmp`;
18521
18689
  try {
@@ -18523,7 +18691,7 @@ function writeOwnerOnlyFileSync(file2, data) {
18523
18691
  } catch {
18524
18692
  }
18525
18693
  try {
18526
- writeFileSync(tmp, data, { mode: DATA_FILE_MODE, flag: "wx" });
18694
+ writeExclusiveOwnerOnlySync(tmp, data);
18527
18695
  renameSync(tmp, file2);
18528
18696
  } finally {
18529
18697
  try {
@@ -18534,11 +18702,69 @@ function writeOwnerOnlyFileSync(file2, data) {
18534
18702
  tightenFile(file2);
18535
18703
  }
18536
18704
 
18705
+ // ../../packages/persistence/src/database.ts
18706
+ import { randomUUID as randomUUID10 } from "crypto";
18707
+ import { join as join3, sep } from "path";
18708
+ import { DatabaseSync } from "node:sqlite";
18709
+
18710
+ // ../../packages/persistence/src/ids.ts
18711
+ import { createHash } from "crypto";
18712
+ function sha256Hex(input) {
18713
+ return createHash("sha256").update(input).digest("hex");
18714
+ }
18715
+ function inventoryId(objectType, identityKey) {
18716
+ return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18717
+ }
18718
+ function sourceProjectId(url2) {
18719
+ return sha256Hex(canonicalIdentity(["source_project", url2]));
18720
+ }
18721
+ function classifiedDataId(cls) {
18722
+ return sha256Hex(canonicalIdentity(["classified_data", cls]));
18723
+ }
18724
+ function inspectionDefinitionId(ruleId, version2) {
18725
+ return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18726
+ }
18727
+ function llmCallId(sessionId, messageId) {
18728
+ return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18729
+ }
18730
+ function toolCallId(sessionId, toolUseId) {
18731
+ return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18732
+ }
18733
+ var NO_SESSION = "no_session";
18734
+ var NO_PATH = "no_path";
18735
+ function captureId(sessionId, contentHash, filePath = null) {
18736
+ return sha256Hex(
18737
+ canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18738
+ );
18739
+ }
18740
+
18537
18741
  // ../../packages/persistence/src/internal/snapshot.ts
18742
+ import { randomUUID } from "crypto";
18743
+ import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
18744
+ import { basename, dirname, join as join2 } from "path";
18538
18745
  function backupPath(file2, tag) {
18539
18746
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18540
18747
  }
18541
18748
  var STALE_PARTIAL_MS = 5 * 6e4;
18749
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18750
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18751
+ var SNAPSHOT_STAGING_COPY = "copy";
18752
+ function createSnapshotStaging(backup) {
18753
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18754
+ rmSync3(stage, { recursive: true, force: true });
18755
+ mkdirOwnerOnlySync(stage);
18756
+ tightenDir(stage);
18757
+ return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
18758
+ }
18759
+ function idleMs(entry) {
18760
+ for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
18761
+ try {
18762
+ return Date.now() - statSync2(candidate).mtimeMs;
18763
+ } catch {
18764
+ }
18765
+ }
18766
+ return null;
18767
+ }
18542
18768
  function reapStalePartials(file2) {
18543
18769
  const dir = dirname(file2);
18544
18770
  const prefix = `${basename(file2)}.`;
@@ -18549,30 +18775,34 @@ function reapStalePartials(file2) {
18549
18775
  return;
18550
18776
  }
18551
18777
  for (const name of entries) {
18552
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18553
- const partial2 = join(dir, name);
18778
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18779
+ const staging = join2(dir, name);
18554
18780
  try {
18555
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18556
- rmSync2(partial2, { force: true });
18781
+ const idle = idleMs(staging);
18782
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18783
+ rmSync3(staging, { recursive: true, force: true });
18557
18784
  }
18558
18785
  } catch {
18559
18786
  }
18560
18787
  }
18561
18788
  }
18562
18789
  function snapshotStore(db, backup) {
18563
- const partial2 = `${backup}.partial`;
18790
+ const { stage, copy } = createSnapshotStaging(backup);
18564
18791
  try {
18565
- rmSync2(partial2, { force: true });
18566
- db.prepare("VACUUM INTO ?").run(partial2);
18567
- tightenFile(partial2);
18568
- renameSync2(partial2, backup);
18792
+ db.prepare("VACUUM INTO ?").run(copy);
18793
+ tightenFile(copy);
18794
+ renameSync2(copy, backup);
18569
18795
  } catch (error51) {
18570
18796
  try {
18571
- rmSync2(partial2, { force: true });
18797
+ rmSync3(stage, { recursive: true, force: true });
18572
18798
  } catch {
18573
18799
  }
18574
18800
  throw error51;
18575
18801
  }
18802
+ try {
18803
+ rmSync3(stage, { recursive: true, force: true });
18804
+ } catch {
18805
+ }
18576
18806
  }
18577
18807
  function moveStoreAside(file2, backup) {
18578
18808
  const undo = [];
@@ -18585,7 +18815,7 @@ function moveStoreAside(file2, backup) {
18585
18815
  renameSync2(sidecar, moved);
18586
18816
  undo.push([moved, sidecar]);
18587
18817
  } catch {
18588
- rmSync2(sidecar, { force: true });
18818
+ rmSync3(sidecar, { force: true });
18589
18819
  }
18590
18820
  }
18591
18821
  } catch (error51) {
@@ -18601,14 +18831,14 @@ function moveStoreAside(file2, backup) {
18601
18831
  }
18602
18832
  function discardStore(file2, backup) {
18603
18833
  try {
18604
- rmSync2(file2, { force: true });
18834
+ rmSync3(file2, { force: true });
18605
18835
  for (const sidecar of dbSidecars(file2)) {
18606
- rmSync2(sidecar, { force: true });
18836
+ rmSync3(sidecar, { force: true });
18607
18837
  }
18608
18838
  } catch (error51) {
18609
18839
  if (existsSync(file2)) {
18610
18840
  try {
18611
- rmSync2(backup, { force: true });
18841
+ rmSync3(backup, { force: true });
18612
18842
  } catch {
18613
18843
  }
18614
18844
  }
@@ -18840,10 +19070,31 @@ function applyMigrations(db, file2) {
18840
19070
  if (drained) applyLegacyDropMigration(db, file2);
18841
19071
  }
18842
19072
  }
19073
+ function readLegacyTables(db) {
19074
+ let holdsRows = false;
19075
+ const marks = [];
19076
+ for (const table2 of ["events", "findings"]) {
19077
+ try {
19078
+ const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
19079
+ if (row === void 0) {
19080
+ holdsRows = true;
19081
+ marks.push(`${table2}:unreadable`);
19082
+ continue;
19083
+ }
19084
+ if (row.n > 0) holdsRows = true;
19085
+ marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
19086
+ } catch {
19087
+ holdsRows = true;
19088
+ marks.push(`${table2}:unreadable`);
19089
+ }
19090
+ }
19091
+ return { holdsRows, mark: marks.join("|") };
19092
+ }
18843
19093
  function applyLegacyDropMigration(db, file2) {
18844
19094
  const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
18845
19095
  if (!migration) return;
18846
- if (file2) {
19096
+ const before = file2 === void 0 ? void 0 : readLegacyTables(db);
19097
+ if (file2 !== void 0 && before?.holdsRows === true) {
18847
19098
  try {
18848
19099
  backupBeforeLegacyDrop(db, file2);
18849
19100
  } catch (error51) {
@@ -18857,6 +19108,12 @@ function applyLegacyDropMigration(db, file2) {
18857
19108
  () => {
18858
19109
  const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
18859
19110
  if (alreadyDropped) return;
19111
+ if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
19112
+ akaWarn(
19113
+ "legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
19114
+ );
19115
+ return;
19116
+ }
18860
19117
  for (const statement of splitStatements(migration.sql)) {
18861
19118
  db.exec(statement);
18862
19119
  }
@@ -19300,9 +19557,10 @@ function safeParseStringArray(raw) {
19300
19557
  const parsed = safeJson(raw, null);
19301
19558
  return Array.isArray(parsed) ? parsed : [];
19302
19559
  }
19560
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19303
19561
  function toHarness(raw) {
19304
19562
  const parsed = Harness.safeParse(raw);
19305
- return parsed.success ? parsed.data : "claudecode";
19563
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19306
19564
  }
19307
19565
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19308
19566
  if (row.status) {
@@ -19433,7 +19691,7 @@ var SqliteActivityRepository = class {
19433
19691
  const params = [];
19434
19692
  if (query.harness && query.harness.length > 0) {
19435
19693
  conditions.push(
19436
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19694
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19437
19695
  );
19438
19696
  params.push(...query.harness);
19439
19697
  }
@@ -19640,13 +19898,13 @@ var SqliteActivityRepository = class {
19640
19898
  * The DISTINCT harnesses that actually have sessions (optionally within a
19641
19899
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19642
19900
  * present rather than the full enum. Each stored value is normalized through
19643
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19644
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19901
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19902
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19645
19903
  */
19646
19904
  harnessFacets(fromMs) {
19647
19905
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19648
19906
  const stmt = this.db.prepare(
19649
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19907
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19650
19908
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19651
19909
  );
19652
19910
  const rows = allRows(
@@ -19860,8 +20118,8 @@ var SqliteAuditEventsRepository = class {
19860
20118
  }
19861
20119
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19862
20120
  // (which takes a caller-supplied random id), the id here is MINTED internally
19863
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19864
- // sibling local-store ids (the local store is single-tenant). The deterministic
20121
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
20122
+ // session and message alone, like the sibling local-store ids. The deterministic
19865
20123
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19866
20124
  // AND converge a streaming partial/final split across two incremental passes:
19867
20125
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20826,6 +21084,42 @@ var SqliteFindingsRepository = class {
20826
21084
  this.db = db;
20827
21085
  }
20828
21086
  db;
21087
+ /**
21088
+ * The newest `limit` findings, newest first.
21089
+ *
21090
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
21091
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
21092
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
21093
+ * not on the driving table: SQLite sorts every finding in the store through a
21094
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
21095
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
21096
+ * rather than a constant.
21097
+ *
21098
+ * What it takes to make `started_at` order come out of an index instead:
21099
+ *
21100
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
21101
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
21102
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
21103
+ * satisfying a global `started_at` order across them needs a range merge
21104
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
21105
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
21106
+ * filters the type per row, which lets the LIMIT stop the scan early.
21107
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
21108
+ * purely to stop the tables being reordered. With plain JOINs the planner
21109
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
21110
+ * unary plus ALONE recovers almost none of the win. Both are needed.
21111
+ *
21112
+ * Neither is a micro-optimisation that a later reader should tidy away, and
21113
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
21114
+ * the temp B-tree comes back.
21115
+ *
21116
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
21117
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
21118
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
21119
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
21120
+ * a store whose recent captures carry no findings at all, where the scan walks
21121
+ * the whole index; that is still no worse than the full sort it replaced.
21122
+ */
20829
21123
  recentFindings(opts) {
20830
21124
  const limit = opts?.limit ?? 50;
20831
21125
  const rows = allRows(
@@ -20834,10 +21128,10 @@ var SqliteFindingsRepository = class {
20834
21128
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20835
21129
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20836
21130
  e.event_type AS kind
20837
- FROM inspection_findings f
20838
- JOIN audit_events e ON e.id = f.audit_event_id
20839
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20840
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
21131
+ FROM audit_events e
21132
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
21133
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
21134
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20841
21135
  ORDER BY e.started_at DESC, f.rowid DESC
20842
21136
  LIMIT :limit`
20843
21137
  ),
@@ -21481,7 +21775,8 @@ var SqliteInspectionDefinitionsRepository = class {
21481
21775
  }
21482
21776
  db;
21483
21777
  insertStmt;
21484
- // Idempotent upsert; returns the content-addressed definition id.
21778
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21779
+ // present keeps the stored row untouched — see the class doc.
21485
21780
  upsert(input) {
21486
21781
  const id = inspectionDefinitionId(input.ruleId, input.version);
21487
21782
  const row = toInspectionDefinitionRow(input, id);
@@ -21625,11 +21920,23 @@ function isParseableBinaryVersion(version2) {
21625
21920
 
21626
21921
  // ../../packages/persistence/src/repositories/installed-packs.ts
21627
21922
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21923
+ function printableRuleId(entry) {
21924
+ if (typeof entry !== "object" || entry === null) return null;
21925
+ const candidate = entry.id;
21926
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21927
+ }
21928
+ function firstIssueReason(error51) {
21929
+ const issue2 = error51.issues[0];
21930
+ if (!issue2) return "unknown";
21931
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21932
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21933
+ }
21934
+ var REJECTED_RULE_DETAIL_CAP = 10;
21628
21935
  function inventorySignature(packs) {
21629
21936
  return packs.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21630
21937
  }
21631
21938
  function hashRules(rulesJson) {
21632
- return createHash2("sha1").update(rulesJson).digest("hex");
21939
+ return createHash2("sha256").update(rulesJson).digest("hex");
21633
21940
  }
21634
21941
  function parseVersion(v) {
21635
21942
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21841,10 +22148,21 @@ var SqliteInstalledPacksRepository = class {
21841
22148
  * (all detection off) instead of falling back to the bundled packs. Every
21842
22149
  * JSON-level failure therefore counts as invalid.
21843
22150
  */
22151
+ /**
22152
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
22153
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
22154
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
22155
+ * winner is whatever order SQLite happens to return, which makes a collision
22156
+ * resolve differently on two machines holding identical stores. Ordering by
22157
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
22158
+ */
21844
22159
  installedRuleset() {
21845
22160
  const rows = allRows(
21846
22161
  this.db.prepare(
21847
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
22162
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
22163
+ namespace, pack_id AS packId
22164
+ FROM installed_packs
22165
+ ORDER BY namespace, pack_id`
21848
22166
  )
21849
22167
  );
21850
22168
  const out = {
@@ -21852,22 +22170,32 @@ var SqliteInstalledPacksRepository = class {
21852
22170
  enabledPacks: 0,
21853
22171
  rules: [],
21854
22172
  invalidRules: 0,
22173
+ rejectedRules: [],
21855
22174
  ruleActions: /* @__PURE__ */ new Map(),
21856
- ruleVersions: /* @__PURE__ */ new Map()
22175
+ ruleVersions: /* @__PURE__ */ new Map(),
22176
+ reversibleRules: /* @__PURE__ */ new Set()
22177
+ };
22178
+ const reject = (pack, ruleId, reason) => {
22179
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22180
+ out.rejectedRules.push({ pack, ruleId, reason });
21857
22181
  };
21858
22182
  for (const row of rows) {
21859
22183
  if (!intToBool(row.enabled)) continue;
21860
22184
  out.enabledPacks += 1;
21861
22185
  const action = policyIdToAction(row.policyId);
22186
+ const reversible = policyIdIsReversible(row.policyId);
22187
+ const pack = `${row.namespace}/${row.packId}`;
21862
22188
  let raw;
21863
22189
  try {
21864
22190
  raw = JSON.parse(row.rulesJson);
21865
22191
  } catch {
21866
22192
  out.invalidRules += 1;
22193
+ reject(pack, null, "rules_json: malformed JSON");
21867
22194
  continue;
21868
22195
  }
21869
22196
  if (!Array.isArray(raw)) {
21870
22197
  out.invalidRules += 1;
22198
+ reject(pack, null, "rules_json: not an array");
21871
22199
  continue;
21872
22200
  }
21873
22201
  for (const entry of raw) {
@@ -21876,7 +22204,12 @@ var SqliteInstalledPacksRepository = class {
21876
22204
  out.rules.push(parsed.data);
21877
22205
  out.ruleActions.set(parsed.data.id, action);
21878
22206
  out.ruleVersions.set(parsed.data.id, row.version);
21879
- } else out.invalidRules += 1;
22207
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22208
+ else out.reversibleRules.delete(parsed.data.id);
22209
+ } else {
22210
+ out.invalidRules += 1;
22211
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22212
+ }
21880
22213
  }
21881
22214
  }
21882
22215
  return out;
@@ -22070,31 +22403,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22070
22403
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22071
22404
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22072
22405
  var HARNESS_LABELS = {
22073
- claudecode: "Claude Code",
22074
- cursor: "Cursor",
22075
- codex: "Codex",
22076
- antigravity: "Antigravity"
22406
+ [HARNESS.ClaudeCode]: "Claude Code",
22407
+ [HARNESS.Cursor]: "Cursor",
22408
+ [HARNESS.Codex]: "Codex",
22409
+ [HARNESS.Antigravity]: "Antigravity"
22077
22410
  };
22078
22411
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22079
22412
  var EMPTY_PROJECT_AGG = {
22080
22413
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22081
22414
  findingsCount: 0
22082
22415
  };
22416
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22417
+ var TITLE_NEEDLES = {
22418
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22419
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22420
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22421
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22422
+ };
22083
22423
  function resolveHarnessId(attrs, row) {
22084
22424
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22085
22425
  return attrs.provider;
22086
22426
  }
22087
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22088
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22089
- if (t.includes("cursor")) return "cursor";
22090
- if (t.includes("codex")) return "codex";
22091
- if (t.includes("antigravity")) return "antigravity";
22427
+ const t = stripSeparators(row.title ?? "");
22428
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22429
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22430
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22431
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22092
22432
  return null;
22093
22433
  }
22094
22434
  function isLiveRealClaudeCode(rows) {
22095
22435
  return rows.some((r) => {
22096
22436
  const attrs = safeJson(r.attributes, {});
22097
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22437
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22098
22438
  });
22099
22439
  }
22100
22440
  function toAssetSummary(row) {
@@ -22362,7 +22702,7 @@ var SqliteInventoryAssetsRepository = class {
22362
22702
  const isRealHarness = rows.some(
22363
22703
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22364
22704
  );
22365
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22705
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22366
22706
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22367
22707
  if (q && assets.length === 0) continue;
22368
22708
  const firstRow = rows[0];
@@ -23036,9 +23376,10 @@ var SqliteProjectFilesRepository = class {
23036
23376
  // ../../packages/persistence/src/repositories/resolutions.ts
23037
23377
  import { randomUUID as randomUUID7 } from "crypto";
23038
23378
  var SqliteResolutionsRepository = class {
23039
- constructor(db, now = () => Date.now()) {
23379
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23040
23380
  this.db = db;
23041
23381
  this.now = now;
23382
+ this.newId = newId;
23042
23383
  this.insertStmt = db.prepare(
23043
23384
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23044
23385
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23071,12 +23412,14 @@ var SqliteResolutionsRepository = class {
23071
23412
  }
23072
23413
  db;
23073
23414
  now;
23415
+ newId;
23074
23416
  insertStmt;
23075
23417
  latestStmt;
23076
23418
  openAtRestStmt;
23077
23419
  resolvedAtRestStmt;
23078
23420
  /**
23079
- * Insert one disposition row. The repo mints the id and stamps created_at.
23421
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23422
+ * both through the constructor's injectable seams.
23080
23423
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23081
23424
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23082
23425
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23088,7 +23431,7 @@ var SqliteResolutionsRepository = class {
23088
23431
  */
23089
23432
  insertResolution(r) {
23090
23433
  this.insertStmt.run({
23091
- id: randomUUID7(),
23434
+ id: this.newId(),
23092
23435
  findingKey: r.findingKey,
23093
23436
  status: FindingStatus.parse(r.status),
23094
23437
  method: ResolutionMethod.parse(r.method),
@@ -23667,16 +24010,16 @@ var ACTION_TO_KIND = {
23667
24010
  warn: "warned"
23668
24011
  };
23669
24012
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23670
- var SCAN_COVERAGE = [
23671
- { provider: "claudecode", coverage: 100, supported: true },
23672
- { provider: "cursor", coverage: 0, supported: false },
23673
- { provider: "codex", coverage: 80, supported: true },
23674
- { provider: "antigravity", coverage: 60, supported: true },
23675
- { provider: "claudeai", coverage: 0, supported: false },
23676
- { provider: "chatgpt", coverage: 0, supported: false },
23677
- { provider: "copilot", coverage: 0, supported: false },
23678
- { provider: "api", coverage: 0, supported: false }
23679
- ];
24013
+ var SCAN_COVERAGE = {
24014
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
24015
+ [HARNESS.Api]: { coverage: 0, supported: false },
24016
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
24017
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
24018
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
24019
+ [HARNESS.Codex]: { coverage: 80, supported: true },
24020
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
24021
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
24022
+ };
23680
24023
  var GRANULARITY = {
23681
24024
  "7d": "day",
23682
24025
  "30d": "day",
@@ -23775,9 +24118,22 @@ var SqliteSecurityRepository = class {
23775
24118
  return Promise.resolve({ total, needsRemediation, bySeverity });
23776
24119
  }
23777
24120
  // Range is echoed but does not change the result today — coverage is a constant
23778
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
24121
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
24122
+ // comes from Provider.options (the enum's declaration order), not from
24123
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
24124
+ // leave key order unspecified (ES2015 guarantees insertion order for these
24125
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
24126
+ // reliable too). The reason is the schema comment's promise: the returned
24127
+ // order must mirror the generated OpenAPI enum list, which is Provider's
24128
+ // contract, not this table's.
23779
24129
  scanCoverage(range) {
23780
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
24130
+ return Promise.resolve({
24131
+ range,
24132
+ providers: Provider.options.map((provider) => ({
24133
+ provider,
24134
+ ...SCAN_COVERAGE[provider]
24135
+ }))
24136
+ });
23781
24137
  }
23782
24138
  enforcementActions(range) {
23783
24139
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23835,10 +24191,10 @@ var SqliteSecurityRepository = class {
23835
24191
  // count; a superseding open/redetected row means the finding is not
23836
24192
  // remediated and is excluded, same invariant as severitySummary. Legacy
23837
24193
  // at-rest findings with finding_key IS NULL can never have a resolution row
23838
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23839
- // outright. One raw-row query (fetch every trackable finding + its latest
23840
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23841
- // mirroring this file's other methods.
24194
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24195
+ // set below. One raw-row query (fetch the findings with resolution activity in
24196
+ // the window + each one's latest resolution status/method/resolved_at) +
24197
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23842
24198
  mttrTrend(range) {
23843
24199
  const granularity = granularityFor(range);
23844
24200
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23854,29 +24210,80 @@ var SqliteSecurityRepository = class {
23854
24210
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23855
24211
  // COALESCE onto the parent event's started_at defends against any
23856
24212
  // legacy/edge row the backfill left null.
23857
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24213
+ `SELECT DISTINCT f.finding_key AS finding_key,
24214
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23858
24215
  latest.status AS latest_status,
23859
24216
  latest.method AS latest_method,
23860
24217
  latest.resolved_at AS latest_resolved_at
23861
- FROM inspection_findings f
23862
- JOIN audit_events e ON e.id = f.audit_event_id
23863
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24218
+ FROM finding_resolution fr
24219
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24220
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24221
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23864
24222
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23865
24223
  ON latest.finding_key = f.finding_key
23866
- WHERE f.finding_key IS NOT NULL
23867
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23868
- AND EXISTS (
23869
- SELECT 1 FROM finding_resolution fr
23870
- WHERE fr.finding_key = f.finding_key
23871
- AND fr.resolved_at >= :windowStart
23872
- )`
23873
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23874
- // any resolution activity at/after the window start — a row this method
24224
+ WHERE fr.resolved_at >= :windowStart
24225
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24226
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23875
24227
  // ultimately counts has its LATEST resolution inside the window, which
23876
- // implies such a row exists, so nothing wanted is dropped. The exact
23877
- // latest-wins + status/method + window gate stays in JS below,
23878
- // dialect-agnostic. Without this, a
23879
- // 7d request evaluated the store's entire trackable-findings history.
24228
+ // implies a resolution row at/after the window start exists, so nothing
24229
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24230
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24231
+ // implied rather than dropped — the join key comes from
24232
+ // finding_resolution, whose finding_key is NOT NULL.
24233
+ //
24234
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24235
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24236
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24237
+ // event_type, joined every capture event to its findings, and evaluated
24238
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24239
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24240
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24241
+ // both cases returning rows for a window holding a fraction of it.
24242
+ //
24243
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24244
+ // stating precisely, because the obvious reading (both are needed for the
24245
+ // speed) is wrong and was measured to be wrong:
24246
+ //
24247
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24248
+ // keyword is semantically identical to JOIN and exists only to stop the
24249
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24250
+ // the outside, because with no ANALYZE statistics it prices
24251
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24252
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24253
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24254
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24255
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24256
+ // range had none. It buys NO flatness in store size: remove it and the
24257
+ // ratio above does not move, because the latest-resolution derived
24258
+ // table already passes over the whole of finding_resolution, so this
24259
+ // read is O(resolutions) either way and resolutions are not the store.
24260
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24261
+ // — no hot read may pass over a table with no index — and that is the
24262
+ // guard that goes red when it is dropped. Neither test catches the
24263
+ // other's defect.
24264
+ //
24265
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24266
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24267
+ // redetected and fixed again carries several rows inside one window and
24268
+ // matches once per row — and the value below is a MEAN, so a key matched
24269
+ // three times is a key weighted three times.
24270
+ //
24271
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24272
+ // recording. Duplicate rows for ONE key are identical (every projected
24273
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24274
+ // preserved), so sums and counts scale together and that key's own mean
24275
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24276
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24277
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24278
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24279
+ // it has inside the window" — which needed a fixture built for it, since no
24280
+ // single-key case can show it.
24281
+ //
24282
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24283
+ // by value tuple. On the other columns alone, two genuinely different
24284
+ // findings sharing a severity, a first-detection event and a resolution
24285
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24286
+ // collapsing them would under-count in the other direction.
23880
24287
  ),
23881
24288
  { windowStart }
23882
24289
  );
@@ -23934,16 +24341,47 @@ var SqliteSecurityRepository = class {
23934
24341
  }
23935
24342
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23936
24343
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23937
- // same latest-resolution-wins correlated subquery as severitySummary /
23938
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23939
- // resolution row for a key rather than just its current disposition). A key
23940
- // whose latest row is a superseding 'open'/'redetected' row (the same
23941
- // secret came back) is excluded — it is not currently resolved. Legacy
23942
- // at-rest findings with finding_key IS NULL are excluded outright (the
23943
- // resolution lifecycle can never attach to them). Path comes from the
23944
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23945
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23946
- // DESC, capped at `limit`.
24344
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24345
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24346
+ // a key rather than just its current disposition). A key whose latest row is
24347
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24348
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24349
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24350
+ // never attach to them). Path comes from the finding's parent event
24351
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24352
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24353
+ //
24354
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24355
+ // the plan rather than a preference. Written the other way round — driving
24356
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24357
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24358
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24359
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24360
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24361
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24362
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24363
+ // because an empty finding_resolution table makes the inner side empty and the
24364
+ // cross product collapses to nothing — so the shape is only observable on a
24365
+ // corpus that seeds resolutions.
24366
+ //
24367
+ // Driving from `latest` instead makes every step below it a unique-index or
24368
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24369
+ // so the cost is the derived table's own — linear in resolutions, which is
24370
+ // what this feed is legitimately about.
24371
+ //
24372
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24373
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24374
+ // exists only to stop the optimizer reordering the tables. Written as plain
24375
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24376
+ // ANALYZE statistics to price the alternatives with, so it takes
24377
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24378
+ // cross product. The FROM order alone was measured to change the plan not at
24379
+ // all.
24380
+ //
24381
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24382
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24383
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24384
+ // reading as though the findings side could drive.
23947
24385
  recentlyResolved(limit = 20) {
23948
24386
  const rows = allRows(
23949
24387
  this.db.prepare(
@@ -23953,17 +24391,15 @@ var SqliteSecurityRepository = class {
23953
24391
  json_extract(e.attributes, '$.file_path') AS path,
23954
24392
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
23955
24393
  latest.resolved_at AS latest_resolved_at
23956
- FROM inspection_findings f
23957
- JOIN audit_events e ON e.id = f.audit_event_id
23958
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23959
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23960
- ON latest.finding_key = f.finding_key
23961
- WHERE e.event_type = 'code_change'
23962
- AND f.finding_key IS NOT NULL
23963
- AND latest.status = 'resolved'
24394
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24395
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24396
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24397
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24398
+ WHERE latest.status = 'resolved'
23964
24399
  AND latest.method = 'fixed-at-source'
23965
24400
  AND latest.resolved_at IS NOT NULL
23966
- ORDER BY latest_resolved_at DESC
24401
+ AND e.event_type = 'code_change'
24402
+ ORDER BY latest.resolved_at DESC
23967
24403
  LIMIT :limit`
23968
24404
  ),
23969
24405
  { limit }
@@ -24899,7 +25335,8 @@ function openAndInitialize(file2) {
24899
25335
  }
24900
25336
  function openLocalDatabase(dir) {
24901
25337
  ensureDataDirSync(dir);
24902
- const file2 = join2(dir, DB_FILENAME);
25338
+ const file2 = join3(dir, DB_FILENAME);
25339
+ reapStalePartials(file2);
24903
25340
  const {
24904
25341
  db,
24905
25342
  events,
@@ -25134,9 +25571,9 @@ import {
25134
25571
  closeSync,
25135
25572
  existsSync as existsSync2,
25136
25573
  openSync,
25137
- readFileSync,
25138
- rmSync as rmSync3,
25139
- statSync as statSync2,
25574
+ readFileSync as readFileSync2,
25575
+ rmSync as rmSync4,
25576
+ statSync as statSync3,
25140
25577
  writeFileSync as writeFileSync2
25141
25578
  } from "fs";
25142
25579
  import { hostname as hostname3 } from "os";
@@ -25170,7 +25607,7 @@ function lockPathFor(file2) {
25170
25607
  function readLockBody(lock) {
25171
25608
  let raw;
25172
25609
  try {
25173
- raw = readFileSync(lock, "utf8");
25610
+ raw = readFileSync2(lock, "utf8");
25174
25611
  } catch {
25175
25612
  return null;
25176
25613
  }
@@ -25201,7 +25638,7 @@ function directoryAcceptsCreates(lock) {
25201
25638
  return false;
25202
25639
  } finally {
25203
25640
  try {
25204
- rmSync3(probe, { force: true });
25641
+ rmSync4(probe, { force: true });
25205
25642
  } catch {
25206
25643
  }
25207
25644
  }
@@ -25234,7 +25671,7 @@ function tryAcquire(lock, file2) {
25234
25671
  closeSync(fd);
25235
25672
  } catch {
25236
25673
  }
25237
- rmSync3(lock, { force: true });
25674
+ rmSync4(lock, { force: true });
25238
25675
  return null;
25239
25676
  }
25240
25677
  try {
@@ -25246,7 +25683,7 @@ function tryAcquire(lock, file2) {
25246
25683
  function isAbandoned(body, lock, staleMs) {
25247
25684
  if (!body) {
25248
25685
  try {
25249
- return Date.now() - statSync2(lock).mtimeMs >= staleMs;
25686
+ return Date.now() - statSync3(lock).mtimeMs >= staleMs;
25250
25687
  } catch {
25251
25688
  return false;
25252
25689
  }
@@ -25270,13 +25707,13 @@ function breakIfStale(lock, staleMs) {
25270
25707
  }
25271
25708
  try {
25272
25709
  if (!existsSync2(lock) || !isAbandoned(readLockBody(lock), lock, staleMs)) return false;
25273
- rmSync3(lock, { force: true });
25710
+ rmSync4(lock, { force: true });
25274
25711
  return true;
25275
25712
  } catch {
25276
25713
  return false;
25277
25714
  } finally {
25278
25715
  try {
25279
- rmSync3(breaker, { force: true });
25716
+ rmSync4(breaker, { force: true });
25280
25717
  } catch {
25281
25718
  }
25282
25719
  }
@@ -25284,8 +25721,8 @@ function breakIfStale(lock, staleMs) {
25284
25721
  var BREAKER_ABANDONED_MS = 1e4;
25285
25722
  function reapAbandonedBreaker(breaker) {
25286
25723
  try {
25287
- if (Date.now() - statSync2(breaker).mtimeMs >= BREAKER_ABANDONED_MS) {
25288
- rmSync3(breaker, { force: true });
25724
+ if (Date.now() - statSync3(breaker).mtimeMs >= BREAKER_ABANDONED_MS) {
25725
+ rmSync4(breaker, { force: true });
25289
25726
  }
25290
25727
  } catch {
25291
25728
  }
@@ -25296,7 +25733,7 @@ function abandonWindow(staleMs) {
25296
25733
  function release(lock, token) {
25297
25734
  try {
25298
25735
  if (readLockBody(lock)?.token !== token) return;
25299
- rmSync3(lock, { force: true });
25736
+ rmSync4(lock, { force: true });
25300
25737
  } catch {
25301
25738
  }
25302
25739
  }
@@ -25347,26 +25784,26 @@ import { createHash as createHash3 } from "crypto";
25347
25784
 
25348
25785
  // ../../packages/persistence/src/fingerprint.ts
25349
25786
  import { createHmac, randomBytes } from "crypto";
25350
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
25351
- import { join as join3 } from "path";
25787
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
25788
+ import { join as join4 } from "path";
25352
25789
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
25353
25790
 
25354
25791
  // ../../packages/persistence/src/local-layout.ts
25355
25792
  import { renameSync as renameSync3 } from "fs";
25356
25793
  import { mkdir } from "fs/promises";
25357
25794
  import { homedir } from "os";
25358
- import { join as join4 } from "path";
25795
+ import { join as join5 } from "path";
25359
25796
  function defaultDataDir() {
25360
- return join4(homedir(), ".aka");
25797
+ return join5(homedir(), ".aka");
25361
25798
  }
25362
25799
  function settingsDir(base = defaultDataDir()) {
25363
- return join4(base, "settings");
25800
+ return join5(base, "settings");
25364
25801
  }
25365
25802
  function dataDir(base = defaultDataDir()) {
25366
- return join4(base, "data");
25803
+ return join5(base, "data");
25367
25804
  }
25368
25805
  function dbPath(base = defaultDataDir()) {
25369
- return join4(dataDir(base), "aka.db");
25806
+ return join5(dataDir(base), "aka.db");
25370
25807
  }
25371
25808
  function ensureLayoutDirSync(dir = defaultDataDir()) {
25372
25809
  ensureDataDirSync(dir);
@@ -25379,20 +25816,100 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25379
25816
  for (const { name, dest } of moves) {
25380
25817
  try {
25381
25818
  ensureDataDirSync(dest);
25382
- const moved = join4(dest, name);
25383
- renameSync3(join4(base, name), moved);
25819
+ const moved = join5(dest, name);
25820
+ renameSync3(join5(base, name), moved);
25384
25821
  tightenFile(moved);
25385
25822
  } catch {
25386
25823
  }
25387
25824
  }
25388
25825
  }
25389
25826
 
25827
+ // ../../packages/persistence/src/managed-settings.ts
25828
+ import { readFileSync as readFileSync4 } from "fs";
25829
+ import { posix, win32 } from "path";
25830
+ function managedSettingsPaths(platform2 = process.platform) {
25831
+ if (platform2 === "darwin") {
25832
+ return [
25833
+ posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
25834
+ posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
25835
+ ];
25836
+ }
25837
+ if (platform2 === "win32") {
25838
+ return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
25839
+ }
25840
+ return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
25841
+ }
25842
+ function readManagedSettings(paths = managedSettingsPaths()) {
25843
+ for (const path of paths) {
25844
+ let text;
25845
+ try {
25846
+ text = readFileSync4(path, "utf8");
25847
+ } catch {
25848
+ continue;
25849
+ }
25850
+ const record2 = parseJsonObject(text);
25851
+ if (!record2) continue;
25852
+ const parsed = ManagedSettings.safeParse(record2);
25853
+ if (parsed.success) return parsed.data;
25854
+ }
25855
+ return null;
25856
+ }
25857
+ function managedContextOf(managed) {
25858
+ if (!managed) return NO_MANAGED_CONTEXT;
25859
+ return {
25860
+ present: true,
25861
+ ...managed.organization === void 0 ? {} : { organization: managed.organization },
25862
+ lockedFields: managed.lockedFields
25863
+ };
25864
+ }
25865
+ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25866
+ if (!managed) return settings;
25867
+ const { values } = managed;
25868
+ const merged = { ...settings };
25869
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25870
+ if (values.controlPlane !== void 0) {
25871
+ merged.controlPlane = {
25872
+ ...values.controlPlane,
25873
+ // The administrator pinned WHICH deployment, not WHEN this machine
25874
+ // joined it. Keep the user's own attach time when the endpoint is
25875
+ // unchanged, so a managed machine does not appear to re-attach on every
25876
+ // read; stamp a fresh one when the administrator moved it.
25877
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25878
+ };
25879
+ }
25880
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25881
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25882
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25883
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25884
+ if (values.vaultConsent !== void 0) {
25885
+ merged.vaultConsent = values.vaultConsent ? (
25886
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25887
+ // at the current version otherwise.
25888
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25889
+ ) : void 0;
25890
+ }
25891
+ if (values.modelJudgeConsent !== void 0) {
25892
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25893
+ acknowledgedAt: now().toISOString(),
25894
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25895
+ } : void 0;
25896
+ }
25897
+ return merged;
25898
+ }
25899
+ function lockedAmong(context, requested) {
25900
+ if (!context.present) return [];
25901
+ return requested.filter((key) => context.lockedFields.includes(key));
25902
+ }
25903
+
25390
25904
  // ../../packages/persistence/src/settings.ts
25391
- import { readFileSync as readFileSync3 } from "fs";
25392
- import { join as join5 } from "path";
25905
+ import { readFileSync as readFileSync5 } from "fs";
25906
+ import { join as join6 } from "path";
25393
25907
  var SETTINGS_FILENAME = "settings.json";
25394
25908
  function readWorkspaceSettings(base = defaultDataDir()) {
25395
- const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25909
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25910
+ }
25911
+ function readUserSettings(base) {
25912
+ const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
25396
25913
  if (!record2) return defaultWorkspaceSettings();
25397
25914
  try {
25398
25915
  return WorkspaceSettings.parse(record2);
@@ -25400,16 +25917,81 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25400
25917
  return defaultWorkspaceSettings();
25401
25918
  }
25402
25919
  }
25403
- function applyOnboarding(answers2, base = defaultDataDir()) {
25920
+ var ManagedFieldError = class extends Error {
25921
+ fields;
25922
+ constructor(fields) {
25923
+ super(`refusing to write administratively locked settings: ${fields.join(", ")}`);
25924
+ this.name = "ManagedFieldError";
25925
+ this.fields = fields;
25926
+ }
25927
+ };
25928
+ function lockableKeysTouched(current, applied) {
25929
+ const keys = [];
25930
+ const changed = (key) => key in applied && applied[key] !== current[key];
25931
+ const descriptorChanged = "controlPlane" in applied && (applied.controlPlane?.endpoint !== current.controlPlane?.endpoint || applied.controlPlane?.label !== current.controlPlane?.label);
25932
+ if (changed("runMode") || descriptorChanged) keys.push("runMode");
25933
+ if (changed("historicalAccess")) keys.push("historicalAccess");
25934
+ if (changed("vaultKeyCustody")) keys.push("vaultKeyCustody");
25935
+ if (changed("vaultInlineReveal")) keys.push("vaultInlineReveal");
25936
+ if (changed("dataSharesInPlace")) keys.push("dataSharesInPlace");
25937
+ if ("vaultConsent" in applied && isVaultConsentValid(applied.vaultConsent) !== isVaultConsentValid(current.vaultConsent)) {
25938
+ keys.push("vaultConsent");
25939
+ }
25940
+ if ("modelJudgeConsent" in applied && isModelJudgeConsentValid(applied.modelJudgeConsent) !== isModelJudgeConsentValid(current.modelJudgeConsent)) {
25941
+ keys.push("modelJudgeConsent");
25942
+ }
25943
+ return keys;
25944
+ }
25945
+ function pinnedKeys(managed) {
25946
+ if (!managed) return [];
25947
+ const { values } = managed;
25948
+ const keys = [];
25949
+ if (values.runMode !== void 0 || values.controlPlane !== void 0) keys.push("runMode");
25950
+ if (values.historicalAccess !== void 0) keys.push("historicalAccess");
25951
+ if (values.vaultConsent !== void 0) keys.push("vaultConsent");
25952
+ if (values.vaultKeyCustody !== void 0) keys.push("vaultKeyCustody");
25953
+ if (values.vaultInlineReveal !== void 0) keys.push("vaultInlineReveal");
25954
+ if (values.modelJudgeConsent !== void 0) keys.push("modelJudgeConsent");
25955
+ if (values.dataSharesInPlace !== void 0) keys.push("dataSharesInPlace");
25956
+ return keys;
25957
+ }
25958
+ function withoutManagedKeys(applied, managed, pinned, touched) {
25959
+ if (!managed.present) return applied;
25960
+ const strip = (key) => (managed.lockedFields.includes(key) || pinned.includes(key)) && !touched.includes(key);
25961
+ const out = { ...applied };
25962
+ if (strip("runMode")) {
25963
+ delete out.runMode;
25964
+ delete out.controlPlane;
25965
+ }
25966
+ if (strip("historicalAccess")) delete out.historicalAccess;
25967
+ if (strip("vaultConsent")) delete out.vaultConsent;
25968
+ if (strip("vaultKeyCustody")) delete out.vaultKeyCustody;
25969
+ if (strip("vaultInlineReveal")) delete out.vaultInlineReveal;
25970
+ if (strip("modelJudgeConsent")) delete out.modelJudgeConsent;
25971
+ if (strip("dataSharesInPlace")) delete out.dataSharesInPlace;
25972
+ return out;
25973
+ }
25974
+ function applyOnboarding(answers2, base = defaultDataDir(), managedOverride) {
25404
25975
  const dir = settingsDir(base);
25405
25976
  ensureDataDirSync(dir);
25406
- const file2 = join5(dir, SETTINGS_FILENAME);
25977
+ const file2 = join6(dir, SETTINGS_FILENAME);
25978
+ const managedSettings = managedOverride === void 0 ? readManagedSettings() : managedOverride;
25979
+ const managed = managedContextOf(managedSettings);
25407
25980
  return withFileLock(file2, () => {
25408
- const current = readWorkspaceSettings(base);
25981
+ const current = readUserSettings(base);
25409
25982
  const applied = typeof answers2 === "function" ? answers2(current) : answers2;
25983
+ const effective = overlayManagedSettings(current, managedSettings);
25984
+ const touched = lockableKeysTouched(effective, applied);
25985
+ const refused = lockedAmong(managed, touched);
25986
+ if (refused.length > 0) throw new ManagedFieldError(refused);
25410
25987
  const merged = WorkspaceSettings.parse({
25411
25988
  ...current,
25412
- ...applied,
25989
+ // Locked keys are stripped rather than merged. Everything still here is,
25990
+ // by the refusal above, an unchanged ECHO of the administrator's value —
25991
+ // so dropping it discards no answer of the user's, and writing it would
25992
+ // persist the pin into their file, where it would outlive the managed
25993
+ // file and read as their own choice once the lock was gone.
25994
+ ...withoutManagedKeys(applied, managed, pinnedKeys(managedSettings), touched),
25413
25995
  // First setup stamps the time; later edits keep the original completion mark.
25414
25996
  onboardedAt: applied.onboardedAt ?? current.onboardedAt ?? (/* @__PURE__ */ new Date()).toISOString()
25415
25997
  });
@@ -25421,13 +26003,17 @@ function applyOnboarding(answers2, base = defaultDataDir()) {
25421
26003
  function readJson(file2) {
25422
26004
  let text;
25423
26005
  try {
25424
- text = readFileSync3(file2, "utf8");
26006
+ text = readFileSync5(file2, "utf8");
25425
26007
  } catch {
25426
26008
  return null;
25427
26009
  }
25428
26010
  return parseJsonObject(text) ?? null;
25429
26011
  }
25430
26012
 
26013
+ // ../../packages/persistence/src/store-symlinks.ts
26014
+ import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
26015
+ import { dirname as dirname2, join as join7, resolve } from "path";
26016
+
25431
26017
  // ../../packages/persistence/src/vault/crypto.ts
25432
26018
  import {
25433
26019
  createCipheriv,
@@ -25440,28 +26026,20 @@ import {
25440
26026
  // ../../packages/persistence/src/vault/key-provider.ts
25441
26027
  import { execFileSync } from "child_process";
25442
26028
  import { randomBytes as randomBytes2 } from "crypto";
25443
- import {
25444
- chmodSync as chmodSync2,
25445
- mkdirSync as mkdirSync2,
25446
- readFileSync as readFileSync4,
25447
- renameSync as renameSync4,
25448
- rmSync as rmSync4,
25449
- statSync as statSync3,
25450
- writeFileSync as writeFileSync3
25451
- } from "fs";
25452
- import { join as join6 } from "path";
26029
+ import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
26030
+ import { join as join8 } from "path";
25453
26031
 
25454
26032
  // ../../packages/persistence/src/vault/vault.ts
25455
26033
  import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
25456
26034
 
25457
26035
  // ../../packages/persistence/src/warn-era-cap.ts
25458
- import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
25459
- import { join as join7 } from "path";
26036
+ import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
26037
+ import { join as join9 } from "path";
25460
26038
  var MARKER = "warn-era-capped";
25461
26039
  function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
25462
26040
  if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
25463
- const marker = join7(dataDir2, MARKER);
25464
- if (existsSync4(marker)) return { capped: 0, skipped: "already-run" };
26041
+ const marker = join9(dataDir2, MARKER);
26042
+ if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
25465
26043
  const capped = db.policies.capCategoryActions();
25466
26044
  writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
25467
26045
  `, { mode: DATA_FILE_MODE });
@@ -25469,8 +26047,8 @@ function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
25469
26047
  }
25470
26048
 
25471
26049
  // ../../packages/plugin-sdk/src/config.ts
25472
- import { existsSync as existsSync5 } from "fs";
25473
- import { join as join8 } from "path";
26050
+ import { existsSync as existsSync6 } from "fs";
26051
+ import { join as join10 } from "path";
25474
26052
 
25475
26053
  // ../../packages/plugin-sdk/src/provider-env.ts
25476
26054
  var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
@@ -25524,8 +26102,8 @@ function resolveProvider() {
25524
26102
  function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
25525
26103
  try {
25526
26104
  ensureLayoutDirSync(base);
25527
- const settingsFile = join8(settingsDir(base), "settings.json");
25528
- if (existsSync5(settingsFile)) tightenFile(settingsFile);
26105
+ const settingsFile = join10(settingsDir(base), "settings.json");
26106
+ if (existsSync6(settingsFile)) tightenFile(settingsFile);
25529
26107
  } catch {
25530
26108
  }
25531
26109
  migrateLegacyLayout(base);
@@ -25548,9 +26126,9 @@ function resolveProviderSafe(resolveProviderFn) {
25548
26126
  }
25549
26127
 
25550
26128
  // ../../packages/plugin-sdk/src/config-inventory.ts
25551
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
26129
+ import { readdirSync as readdirSync2, readFileSync as readFileSync8, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
25552
26130
  import { homedir as homedir2 } from "os";
25553
- import { basename as basename3, join as join10 } from "path";
26131
+ import { basename as basename3, join as join12 } from "path";
25554
26132
 
25555
26133
  // ../../packages/detections/src/egress/registry.ts
25556
26134
  var EXTRACTOR_VERSION = "1";
@@ -26280,6 +26858,7 @@ var CONFIG_POSTURE_RULES = [
26280
26858
  ];
26281
26859
 
26282
26860
  // ../../packages/detections/src/security/redos-probe.ts
26861
+ var BUDGET_MS = 100;
26283
26862
  var EXPONENTIAL_UNITS = [
26284
26863
  "a",
26285
26864
  "0",
@@ -26303,29 +26882,36 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
26303
26882
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
26304
26883
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
26305
26884
  );
26885
+ var CPU_CORROBORATION_SHARE = 0.2;
26886
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
26306
26887
 
26307
26888
  // ../../packages/plugin-sdk/src/repo.ts
26308
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
26309
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
26889
+ import { existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
26890
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join11, sep as sep2 } from "path";
26310
26891
 
26311
26892
  // ../../packages/plugin-sdk/src/events.ts
26312
26893
  import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
26313
26894
 
26314
26895
  // ../../packages/plugin-sdk/src/isolated-scan.ts
26315
- import { existsSync as existsSync7 } from "fs";
26896
+ import { existsSync as existsSync8 } from "fs";
26316
26897
  import { fileURLToPath } from "url";
26317
26898
  import { Worker } from "worker_threads";
26318
26899
 
26900
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
26901
+ var import_ignore = __toESM(require_ignore(), 1);
26902
+ import { readFileSync as readFileSync9 } from "fs";
26903
+ import { join as join13 } from "path";
26904
+
26319
26905
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
26320
26906
  import { arch, hostname as hostname4, platform, release as release2 } from "os";
26321
26907
 
26322
26908
  // ../../packages/plugin-sdk/src/nudge.ts
26323
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
26324
- import { join as join11 } from "path";
26909
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
26910
+ import { join as join14 } from "path";
26325
26911
 
26326
26912
  // ../../packages/plugin-sdk/src/paths.ts
26327
- import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
26328
- import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
26913
+ import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
26914
+ import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
26329
26915
 
26330
26916
  // ../../packages/plugin-sdk/src/posture.ts
26331
26917
  function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
@@ -26338,9 +26924,8 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
26338
26924
  }
26339
26925
 
26340
26926
  // ../../packages/plugin-sdk/src/project-files.ts
26341
- var import_ignore = __toESM(require_ignore(), 1);
26342
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
26343
- import { basename as basename5, join as join12 } from "path";
26927
+ import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
26928
+ import { basename as basename5, join as join15 } from "path";
26344
26929
 
26345
26930
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
26346
26931
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -26375,8 +26960,8 @@ import { randomUUID as randomUUID14 } from "crypto";
26375
26960
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
26376
26961
 
26377
26962
  // ../../packages/plugin-sdk/src/throttle.ts
26378
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
26379
- import { join as join13 } from "path";
26963
+ import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
26964
+ import { join as join16 } from "path";
26380
26965
 
26381
26966
  // ../../packages/setup-wizard/src/onboard-posture.ts
26382
26967
  function parsePosture(json2) {
@@ -26386,10 +26971,10 @@ function parsePosture(json2) {
26386
26971
  for (const [key, value] of Object.entries(raw)) {
26387
26972
  const cat = DetectionCategory.safeParse(key);
26388
26973
  if (!cat.success) throw new Error(`unknown category "${key}"`);
26389
- const act = BuiltinPolicyId.safeParse(value);
26974
+ const act = CategoryPolicyId.safeParse(value);
26390
26975
  if (!act.success)
26391
26976
  throw new Error(
26392
- `invalid action "${String(value)}" for ${key} (expected monitor/warn/redact/block)`
26977
+ `invalid action "${String(value)}" for ${key} (expected ${CATEGORY_EXPRESSIBLE_IDS.join("/")})`
26393
26978
  );
26394
26979
  out[cat.data] = act.data;
26395
26980
  }
@@ -26399,12 +26984,17 @@ function parsePosture(json2) {
26399
26984
 
26400
26985
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
26401
26986
  import { writeFileSync as writeFileSync7 } from "fs";
26402
- import { join as join14 } from "path";
26987
+ import { join as join17 } from "path";
26988
+
26989
+ // ../../packages/setup-wizard/src/triage/merge.ts
26990
+ var RANK = Object.fromEntries(
26991
+ KNOWN_BUILTIN_IDS.map((id, i) => [id, i])
26992
+ );
26403
26993
 
26404
26994
  // ../../packages/setup-wizard/src/triage/plan-file.ts
26405
- import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
26995
+ import { mkdtempSync, readFileSync as readFileSync11, rmdirSync, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
26406
26996
  import { tmpdir } from "os";
26407
- import { basename as basename6, dirname as dirname4, join as join15 } from "path";
26997
+ import { basename as basename6, dirname as dirname5, join as join18 } from "path";
26408
26998
  var SuppressionEntrySchema = external_exports.object({
26409
26999
  ruleId: external_exports.string(),
26410
27000
  category: DetectionCategory,