@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.
@@ -492,15 +492,14 @@ var require_ignore = __commonJS({
492
492
  });
493
493
 
494
494
  // src/apply-suppressions.ts
495
- import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
495
+ import { existsSync as existsSync10, readFileSync as readFileSync13 } from "fs";
496
496
  import { userInfo } from "os";
497
- import { dirname as dirname6, join as join17 } from "path";
497
+ import { dirname as dirname7, join as join20 } from "path";
498
498
  import { fileURLToPath as fileURLToPath4 } from "url";
499
499
 
500
- // ../../packages/persistence/src/database.ts
501
- import { randomUUID as randomUUID10 } from "crypto";
502
- import { join as join2, sep } from "path";
503
- import { DatabaseSync } from "node:sqlite";
500
+ // ../../packages/persistence/src/control-plane-credential.ts
501
+ import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
502
+ import { join } from "path";
504
503
 
505
504
  // ../../packages/schema/src/drizzle/sqlite-ddl.ts
506
505
  var SQLITE_MIGRATIONS = [
@@ -587,6 +586,10 @@ var SQLITE_MIGRATIONS = [
587
586
  {
588
587
  tag: "0020_secret_vault_pagination_indexes",
589
588
  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');"
589
+ },
590
+ {
591
+ tag: "0021_finding_resolution_resolved_at_index",
592
+ sql: "CREATE INDEX `idx_finding_resolution_resolved_at` ON `finding_resolution` (`resolved_at`);\n"
590
593
  }
591
594
  ];
592
595
 
@@ -15302,6 +15305,47 @@ function date4(params) {
15302
15305
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15303
15306
  config(en_default());
15304
15307
 
15308
+ // ../../packages/schema/src/zod/harness-map.ts
15309
+ var HARNESS = {
15310
+ ClaudeCode: "claudecode",
15311
+ Cursor: "cursor",
15312
+ Copilot: "copilot",
15313
+ Codex: "codex",
15314
+ Antigravity: "antigravity",
15315
+ Windsurf: "windsurf",
15316
+ ClaudeDesktop: "claudedesktop",
15317
+ ChatGpt: "chatgpt",
15318
+ ClaudeAi: "claudeai",
15319
+ Api: "api"
15320
+ };
15321
+ var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
15322
+ var SOURCE_TOOL = {
15323
+ ClaudeCode: "claude-code",
15324
+ ClaudeDesktop: "claude-desktop",
15325
+ Cursor: "cursor",
15326
+ ChatGpt: "chatgpt",
15327
+ ClaudeAi: "claude-ai",
15328
+ Copilot: "github-copilot",
15329
+ Codex: "codex",
15330
+ Antigravity: "antigravity",
15331
+ // No harness counterpart, deliberately: the CLI's own captures and a capture
15332
+ // whose tool could not be identified both render through the read side's
15333
+ // miss path rather than as a harness of their own.
15334
+ Cli: "cli",
15335
+ Unknown: "unknown"
15336
+ };
15337
+ var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
15338
+ var TOOL_TO_HARNESS = {
15339
+ [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
15340
+ [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
15341
+ [SOURCE_TOOL.Copilot]: HARNESS.Copilot,
15342
+ [SOURCE_TOOL.Cursor]: HARNESS.Cursor,
15343
+ [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
15344
+ [SOURCE_TOOL.Codex]: HARNESS.Codex,
15345
+ [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
15346
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
15347
+ };
15348
+
15305
15349
  // ../../packages/schema/src/zod/finding.ts
15306
15350
  var DetectionCategory = external_exports.enum(["pii", "financial", "secret", "phi", "code_context", "code_flaw", "custom", "config"]).meta({ id: "DetectionCategory" });
15307
15351
  var Severity = external_exports.enum(["critical", "high", "medium", "low"]).meta({ id: "Severity" });
@@ -15324,21 +15368,22 @@ var Finding = external_exports.object({
15324
15368
  }).meta({ id: "Finding" });
15325
15369
  var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
15326
15370
  var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
15327
- var FindingProvider = external_exports.enum([
15328
- "claudecode",
15329
- "claudedesktop",
15330
- "cursor",
15331
- "copilot",
15332
- "chatgpt",
15333
- "claudeai",
15334
- "codex",
15335
- "antigravity",
15336
- "api"
15371
+ var FindingProvider = Harness.extract([
15372
+ "ClaudeCode",
15373
+ "ClaudeDesktop",
15374
+ "Cursor",
15375
+ "Copilot",
15376
+ "ChatGpt",
15377
+ "ClaudeAi",
15378
+ "Codex",
15379
+ "Antigravity",
15380
+ "Api"
15337
15381
  ]).meta({ id: "FindingProvider" });
15338
15382
  var FindingCategory = external_exports.enum([
15339
15383
  "secret",
15340
15384
  "pii",
15341
15385
  "source_code",
15386
+ "code_flaw",
15342
15387
  "external_share",
15343
15388
  "mcp_server",
15344
15389
  "customer_data",
@@ -15518,6 +15563,7 @@ var FindingInstanceDetail = FindingInstance.extend({
15518
15563
  policy: FindingPolicyRef
15519
15564
  }).meta({ id: "FindingInstanceDetail" });
15520
15565
  var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
15566
+ var MAX_FLAT_FINDINGS_LIMIT = 200;
15521
15567
  var ListFindingInstancesQuery = external_exports.object({
15522
15568
  severity: external_exports.array(Severity).optional(),
15523
15569
  // Rule ids, the same vocabulary the grouped list's `subtype` carries.
@@ -15537,7 +15583,7 @@ var ListFindingInstancesQuery = external_exports.object({
15537
15583
  q: external_exports.string().optional(),
15538
15584
  sessionId: external_exports.string().optional(),
15539
15585
  from: external_exports.iso.datetime().optional(),
15540
- limit: external_exports.coerce.number().int().min(1).max(200).optional(),
15586
+ limit: external_exports.coerce.number().int().min(1).max(MAX_FLAT_FINDINGS_LIMIT).optional(),
15541
15587
  cursor: external_exports.string().optional()
15542
15588
  });
15543
15589
  var ListFindingInstancesResponse = external_exports.object({
@@ -15599,30 +15645,6 @@ var ListFindingLocationsResponse = external_exports.object({
15599
15645
  hasMore: external_exports.boolean()
15600
15646
  }).meta({ id: "ListFindingLocationsResponse" });
15601
15647
 
15602
- // ../../packages/schema/src/zod/harness-map.ts
15603
- var Harness = external_exports.enum([
15604
- "claudecode",
15605
- "cursor",
15606
- "copilot",
15607
- "codex",
15608
- "antigravity",
15609
- "windsurf",
15610
- "claudedesktop",
15611
- "chatgpt",
15612
- "claudeai",
15613
- "api"
15614
- ]).meta({ id: "Harness" });
15615
- var TOOL_TO_HARNESS = {
15616
- "claude-code": "claudecode",
15617
- "claude-desktop": "claudedesktop",
15618
- "github-copilot": "copilot",
15619
- cursor: "cursor",
15620
- chatgpt: "chatgpt",
15621
- codex: "codex",
15622
- antigravity: "antigravity",
15623
- "claude-ai": "claudeai"
15624
- };
15625
-
15626
15648
  // ../../packages/schema/src/zod/meta.ts
15627
15649
  var InventoryObjectType = external_exports.enum(["host", "harness", "user", "skill", "hook", "mcp_server", "config_file"]).meta({ id: "InventoryObjectType" });
15628
15650
  var AuditEventType = external_exports.enum([
@@ -16075,366 +16097,240 @@ var ActivityOverviewResponse = external_exports.object({
16075
16097
  sessions: ListActivitySessionsResponse
16076
16098
  }).meta({ id: "ActivityOverviewResponse" });
16077
16099
 
16078
- // ../../packages/schema/src/zod/event.ts
16079
- var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
16080
- var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
16081
- var SourceTool = external_exports.enum([
16082
- "claude-code",
16083
- "claude-desktop",
16084
- "cursor",
16085
- "chatgpt",
16086
- "claude-ai",
16087
- "github-copilot",
16088
- "codex",
16089
- "antigravity",
16090
- "cli",
16091
- "unknown"
16092
- ]).meta({ id: "SourceTool" });
16093
- var EventMetadata = external_exports.object({
16094
- sessionId: external_exports.string().optional(),
16095
- repo: external_exports.string().optional(),
16096
- filePath: external_exports.string().optional(),
16097
- // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
16098
- // set by the tool-scanning hooks. The tool NAME only — never the tool's
16099
- // arguments or output, which can carry the very value a finding masked
16100
- // (metadata is stored unredacted). Gives findings on non-file captures a
16101
- // display location ("via Bash") when no filePath exists.
16102
- toolName: external_exports.string().optional(),
16103
- // Set (true) by the worktree scanner when the file is excluded by the
16104
- // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
16105
- // generated code can leak real secrets — but the provenance is recorded so
16106
- // policy/dashboards can treat those findings as informational rather than
16107
- // blocking. Omitted (not false) for tracked files and non-scan events.
16108
- gitignored: external_exports.boolean().optional(),
16109
- // Set (true) ONLY when the event's `content` is the COMPLETE file at
16110
- // capture time (a worktree scan reading from disk). Hook-captured edits
16111
- // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
16112
- // this. The resolver-on-ingest keys its fixed-at-source dropout
16113
- // diff on this marker: only a whole-file snapshot can prove a previously
16114
- // open finding is gone; a fragment's absence proves nothing (the secret
16115
- // may live outside the hunk). Omitted (not false) for fragments and
16116
- // non-scan events, so pre-marker clients safely default to the
16117
- // non-authoritative path.
16118
- wholeFile: external_exports.boolean().optional(),
16119
- model: external_exports.string().optional(),
16120
- turnIndex: external_exports.number().int().nonnegative().optional(),
16121
- // Distributed-tracing correlation. `correlationId` ties a recorded event back
16122
- // to the request that captured/ingested it (a UUID, generated independently of
16123
- // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
16124
- // originating span when telemetry is enabled. Both optional + backward
16125
- // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
16126
- correlationId: external_exports.uuid().optional(),
16127
- traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
16128
- // Ids of the detection exceptions that downgraded findings in this capture
16129
- // to 'allow' — the enforcement audit trail's link back to the grant that
16130
- // authorized the bypass. Absent on captures where no exception applied.
16131
- exceptionIds: external_exports.array(external_exports.guid()).optional()
16132
- }).meta({ id: "EventMetadata" });
16133
- var Event = external_exports.object({
16134
- id: external_exports.guid(),
16135
- sourceTool: SourceTool,
16136
- kind: EventKind,
16137
- occurredAt: external_exports.iso.datetime(),
16138
- contentHash: external_exports.string(),
16139
- content: external_exports.string(),
16140
- metadata: EventMetadata.optional()
16141
- }).meta({ id: "Event" });
16142
- var IngestEvent = Event.meta({ id: "IngestEvent" });
16143
- var IngestBatch = external_exports.object({
16144
- events: external_exports.array(IngestEvent).min(1).max(100),
16145
- // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
16146
- // additionally rejects any event whose contentHash the store has already
16147
- // recorded — for re-runnable bulk ingest (worktree scan, transcript
16148
- // backfill), where a re-run mints fresh event ids for identical content and
16149
- // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
16150
- // two genuinely separate prompts can be byte-identical and both belong on
16151
- // the timeline.
16152
- dedupe: external_exports.literal("content-hash").optional()
16153
- }).meta({ id: "IngestBatch" });
16154
-
16155
- // ../../packages/schema/src/zod/inventory.ts
16156
- var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16157
- var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16158
- var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16159
- var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16160
- var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16161
- var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16162
- var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16163
- var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
16164
- var AccessCounts = external_exports.object({
16165
- open: external_exports.number().int().nonnegative(),
16166
- approved: external_exports.number().int().nonnegative(),
16167
- blocked: external_exports.number().int().nonnegative(),
16168
- total: external_exports.number().int().nonnegative()
16169
- }).meta({ id: "AccessCounts" });
16170
- var AssetSummary = external_exports.object({
16171
- id: external_exports.string(),
16172
- type: AssetType,
16173
- name: external_exports.string(),
16174
- sub: external_exports.string(),
16175
- flags: external_exports.array(Flag),
16176
- /** MCP servers only — omitted for all other types. */
16177
- trust: TrustLevel.optional()
16178
- }).meta({ id: "AssetSummary" });
16179
- var ProjectSummary = external_exports.object({
16180
- id: external_exports.string(),
16181
- name: external_exports.string(),
16182
- repo: external_exports.string(),
16183
- visibility: Visibility,
16184
- language: external_exports.string(),
16185
- policyDefault: AccessLevel,
16186
- updatedAt: external_exports.iso.datetime(),
16187
- accessCounts: AccessCounts,
16188
- findingsCount: external_exports.number().int().nonnegative()
16189
- }).meta({ id: "ProjectSummary" });
16190
- var HarnessCategory = external_exports.object({
16191
- /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16192
- type: AssetType,
16193
- assets: external_exports.array(AssetSummary)
16100
+ // ../../packages/schema/src/zod/config-inventory.ts
16101
+ var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16102
+ var SkillScanEntry = external_exports.object({
16103
+ name: external_exports.string().min(1),
16104
+ // The identity source: a marketplace repo for plugin skills (e.g.
16105
+ // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16106
+ // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16107
+ // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16108
+ source: external_exports.string().min(1),
16109
+ scope: ConfigScope,
16110
+ pluginName: external_exports.string().optional(),
16111
+ // Volatile — rides the attribute bag, never the identity hash.
16112
+ version: external_exports.string().optional(),
16113
+ description: external_exports.string().optional(),
16114
+ // Skill directory mtime (ISO) the "updated Nd ago" freshness signal.
16115
+ updatedAt: external_exports.iso.datetime().optional(),
16116
+ // Filesystem path — the promoted inventory `location` column.
16117
+ location: external_exports.string().optional()
16194
16118
  });
16195
- var HarnessSummary = external_exports.object({
16196
- id: HarnessId,
16197
- label: external_exports.string(),
16198
- kind: external_exports.string(),
16199
- version: external_exports.string(),
16200
- sessions: external_exports.number().int().nonnegative(),
16201
- assetCount: external_exports.number().int().nonnegative(),
16202
- flagCount: external_exports.number().int().nonnegative(),
16203
- projects: external_exports.array(ProjectSummary),
16204
- categories: external_exports.array(HarnessCategory)
16205
- }).meta({ id: "HarnessSummary" });
16206
- var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16207
- var AssetGroup = external_exports.object({
16208
- /** Group key — never project (enforced at service layer). */
16209
- type: AssetType,
16210
- total: external_exports.number().int().nonnegative(),
16211
- /**
16212
- * MCP group only — omitted for all other types.
16213
- * Partial: only TrustLevel keys with non-zero counts are included.
16214
- * Strict: unknown keys are rejected only TrustLevel values are valid keys.
16215
- */
16216
- trustRollup: external_exports.object({
16217
- "known-good": external_exports.number().int().nonnegative(),
16218
- risky: external_exports.number().int().nonnegative(),
16219
- unapproved: external_exports.number().int().nonnegative()
16220
- }).partial().strict().optional(),
16119
+ var HookScanEntry = external_exports.object({
16120
+ // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16121
+ // set is harness-defined and grows without a schema change.
16122
+ event: external_exports.string().min(1),
16123
+ // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16124
+ matcher: external_exports.string().optional(),
16125
+ command: external_exports.string().min(1),
16126
+ timeout: external_exports.number().optional(),
16127
+ scope: ConfigScope,
16128
+ pluginName: external_exports.string().optional(),
16129
+ // The settings file / hooks.json the entry came from.
16130
+ location: external_exports.string().optional()
16131
+ });
16132
+ var McpServerScanEntry = external_exports.object({
16133
+ // The server's config key ("github", "filesystem", …) — identity, with the
16134
+ // qualified scope (see mcpServerIdentityKey).
16135
+ name: external_exports.string().min(1),
16136
+ scope: ConfigScope,
16137
+ pluginName: external_exports.string().optional(),
16138
+ // The owning plugin's marketplacepart of PLUGIN-scope identity: two
16139
+ // marketplaces can each ship a plugin named `guard`, and without this their
16140
+ // same-named servers would collapse to one row (the second silently dropped,
16141
+ // inheriting the first's trust).
16142
+ marketplace: external_exports.string().optional(),
16143
+ // The repo identity (remote url, or the cwd for un-remoted repos) — part of
16144
+ // PROJECT/LOCAL-scope identity: a server named `github` in repo A and one in
16145
+ // repo B are different servers with different commands, and MUST NOT share a
16146
+ // row — a shared row would let a cloned repo's .mcp.json inherit the trust
16147
+ // the user granted elsewhere.
16148
+ project: external_exports.string().optional(),
16149
+ // 'stdio' when the entry carries a command; otherwise the config's `type`
16150
+ // ('http' / 'sse' / …). Open string — the transport set is harness-defined.
16151
+ transport: external_exports.string().min(1),
16152
+ // Volatile on purpose (unlike hook `command`): a changed command/url is drift
16153
+ // on a stable row — visible across config_scan snapshots and preserving the
16154
+ // user's trust decision — never a quiet new row. One of the two is present.
16155
+ // Secret-masked at collection time (the scanner runs the bundled detection
16156
+ // packs over both — tokens routinely ride command args and URLs).
16157
+ command: external_exports.string().optional(),
16158
+ url: external_exports.string().optional(),
16159
+ // Env var NAMES only, never values (the no-secrets rule).
16160
+ envKeys: external_exports.array(external_exports.string()).optional(),
16161
+ // The config file the entry came from.
16162
+ location: external_exports.string().optional()
16163
+ });
16164
+ var ConfigFileScanEntry = external_exports.object({
16165
+ // Basename (settings.json, CLAUDE.md) or dir name (commands/, agents/).
16166
+ name: external_exports.string().min(1),
16167
+ // The absolute path — identity (with scope) and the promoted `location`.
16168
+ path: external_exports.string().min(1),
16169
+ scope: ConfigScope,
16170
+ // Human label: "User settings", "Project memory", "Slash commands", …
16171
+ kind: external_exports.string().min(1),
16172
+ // Derived SHAPE summary — top-level key names, entry counts, line counts.
16173
+ // Never file content or values (memory files can carry sensitive detail).
16174
+ detail: external_exports.string().optional(),
16175
+ // Dir configs (commands/, agents/) and .mcp.json: how many entries.
16176
+ entryCount: external_exports.number().optional(),
16177
+ // File mtime (ISO) — the freshness signal.
16178
+ updatedAt: external_exports.iso.datetime().optional()
16179
+ });
16180
+ var ConfigScanResult = external_exports.object({
16181
+ scannedAt: external_exports.iso.datetime(),
16182
+ skills: external_exports.array(SkillScanEntry),
16183
+ hooks: external_exports.array(HookScanEntry),
16184
+ mcpServers: external_exports.array(McpServerScanEntry),
16185
+ configFiles: external_exports.array(ConfigFileScanEntry),
16186
+ errors: external_exports.array(external_exports.object({ source: external_exports.string(), reason: external_exports.string() }))
16187
+ });
16188
+ var ConfigPostureFindingInput = external_exports.object({
16189
+ ruleId: external_exports.string().min(1),
16190
+ version: external_exports.string().min(1),
16191
+ span: Span,
16192
+ // For posture rules this is the offending COMMAND (config the user already
16193
+ // holds locally, not captured secret content) — it is also the correlation
16194
+ // key the read surface matches back to a hook row.
16195
+ maskedMatch: external_exports.string(),
16196
+ actionTaken: ActionTaken,
16197
+ confidence: external_exports.number().min(0).max(1)
16198
+ });
16199
+ var ConfigScanRecord = external_exports.object({
16200
+ items: external_exports.array(InventoryInput),
16201
+ scanEvent: AuditEventInput,
16202
+ definitions: external_exports.array(InspectionDefinitionInput).optional(),
16203
+ findings: external_exports.array(ConfigPostureFindingInput).optional()
16204
+ });
16205
+
16206
+ // ../../packages/schema/src/zod/control-plane.ts
16207
+ var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
16208
+ var AttachedCredential = external_exports.object({
16209
+ specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
16210
+ // The control-plane endpoint this credential was minted against.
16211
+ endpoint: external_exports.string().min(1),
16212
+ // The bearer credential itself. Never logged, never rendered — status
16213
+ // surfaces show `keyPrefix` and nothing else.
16214
+ apiKey: external_exports.string().min(1),
16215
+ // First few characters of the key, safe to display so a user can match the
16216
+ // credential against their organization's key list.
16217
+ keyPrefix: external_exports.string().min(1).max(16).optional(),
16218
+ mintedAt: external_exports.iso.datetime().optional()
16219
+ });
16220
+ var MAX_DATE_MS = 253402300799999;
16221
+ var MAX_INT4 = 2147483647;
16222
+ var StorePosturePack = external_exports.object({
16223
+ packId: external_exports.string().min(1),
16224
+ // 'namespace/packId'
16225
+ version: external_exports.string().min(1),
16226
+ enabled: external_exports.boolean(),
16227
+ // Stringified pass-through of the local store's `installed_packs.updated_at`
16228
+ // — the column format is store-version-dependent (epoch millis vs ISO), so
16229
+ // the wire shape assumes neither.
16230
+ updatedAt: external_exports.string().nullable()
16231
+ }).meta({ id: "StorePosturePack" });
16232
+ var StorePosturePolicyCounts = external_exports.object({
16233
+ total: external_exports.number().int().min(0),
16234
+ disabled: external_exports.number().int().min(0),
16235
+ // Exhaustive per-action map; the builder pre-fills every action with 0.
16236
+ //
16237
+ // Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
16238
+ // enforces exhaustiveness either way, but z.record emits `propertyNames` +
16239
+ // `additionalProperties` into a generated schema document, and a type
16240
+ // generator renders THAT with every key optional — a sender built against
16241
+ // the generated type would typecheck and still be rejected at runtime. An
16242
+ // explicit object emits `properties` + `required`, so generated types
16243
+ // demand all five.
16244
+ //
16245
+ // `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
16246
+ // ActionTaken member is a COMPILE error here instead of silent drift.
16247
+ // `.strict()` is load-bearing — it rejects an unknown action key, which a
16248
+ // bare object would silently STRIP, accepting a miscounted map as valid.
16249
+ byAction: external_exports.object({
16250
+ warn: external_exports.number().int().min(0),
16251
+ redact: external_exports.number().int().min(0),
16252
+ block: external_exports.number().int().min(0),
16253
+ allow: external_exports.number().int().min(0),
16254
+ log: external_exports.number().int().min(0)
16255
+ }).strict()
16256
+ }).meta({ id: "StorePosturePolicyCounts" });
16257
+ var StorePosturePlugin = external_exports.object({
16258
+ /** Package name of the reporting plugin. */
16259
+ package: external_exports.string().min(1).max(200),
16260
+ version: external_exports.string().min(1).max(64),
16261
+ /** Version of the bundled core, when the build records one separately. */
16262
+ ossVersion: external_exports.string().max(64).nullable(),
16221
16263
  /**
16222
- * Partial: only Flag keys with non-zero counts are included.
16223
- * Strict: unknown keys are rejected only Flag values are valid keys.
16264
+ * `version` of the policy bundle this machine last fetched. Bounded at 200
16265
+ * rather than the 64 a bare sha256 hex digest needs today, so a later
16266
+ * format with an algorithm prefix does not start rejecting the channel.
16224
16267
  */
16225
- flagRollup: external_exports.object({
16226
- update: external_exports.number().int().nonnegative(),
16227
- stale: external_exports.number().int().nonnegative(),
16228
- conflict: external_exports.number().int().nonnegative(),
16229
- unknown: external_exports.number().int().nonnegative(),
16230
- change: external_exports.number().int().nonnegative(),
16231
- untracked: external_exports.number().int().nonnegative(),
16232
- risk: external_exports.number().int().nonnegative(),
16233
- findings: external_exports.number().int().nonnegative()
16234
- }).partial().strict(),
16235
- items: external_exports.array(AssetSummary)
16236
- }).meta({ id: "AssetGroup" });
16237
- var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16238
- var McpTool = external_exports.object({
16239
- name: external_exports.string(),
16240
- signature: external_exports.string(),
16241
- description: external_exports.string(),
16242
- write: external_exports.boolean(),
16243
- /** Non-null string when tool is dangerous / blocked; null otherwise. */
16244
- risk: external_exports.string().nullable()
16245
- }).meta({ id: "McpTool" });
16246
- var AssetFindingRef = external_exports.object({
16247
- id: external_exports.string(),
16248
- title: external_exports.string(),
16249
- note: external_exports.string()
16250
- });
16251
- var AssetDetail = AssetSummary.extend({
16252
- /** string | null — null when no description is available. */
16253
- description: external_exports.string().nullable(),
16254
- /** trustLevel | null — null for non-MCP assets. */
16255
- trust: TrustLevel.nullable(),
16256
- /** Type-specific raw key/values — FE renders the grid. */
16257
- meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16258
- /** always present object when there is an active finding, null when absent. */
16259
- finding: AssetFindingRef.nullable(),
16260
- /** MCP exposed-tools list — omitted for non-mcp. */
16261
- tools: external_exports.array(McpTool).optional()
16262
- }).meta({ id: "AssetDetail" });
16263
- var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16264
- var InventoryStats = external_exports.object({
16265
- /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16266
- attention: external_exports.number().int().nonnegative(),
16267
- byType: external_exports.object({
16268
- project: external_exports.number().int().nonnegative(),
16269
- skill: external_exports.number().int().nonnegative(),
16270
- mcp: external_exports.number().int().nonnegative(),
16271
- hook: external_exports.number().int().nonnegative(),
16272
- config: external_exports.number().int().nonnegative()
16273
- }),
16274
- harnesses: external_exports.number().int().nonnegative(),
16275
- mcpTrust: external_exports.object({
16276
- "known-good": external_exports.number().int().nonnegative(),
16277
- risky: external_exports.number().int().nonnegative(),
16278
- unapproved: external_exports.number().int().nonnegative()
16279
- })
16280
- }).meta({ id: "InventoryStats" });
16281
- var FileSummary = external_exports.object({
16282
- path: external_exports.string(),
16283
- name: external_exports.string(),
16284
- origin: Origin,
16285
- /** Effective access (override applied). */
16286
- access: AccessLevel,
16287
- /** True when a file_access_override differs from the computed default. */
16288
- isCustom: external_exports.boolean(),
16289
- findings: external_exports.number().int().nonnegative(),
16290
- /** When the file was auto-blocked by a detection; null when not blocked. */
16291
- blockedAt: external_exports.iso.datetime().nullable().optional(),
16292
- /** Why the file was blocked; null when absent. */
16293
- note: external_exports.string().nullable().optional()
16294
- }).meta({ id: "FileSummary" });
16295
- var FolderSummary = external_exports.object({
16296
- name: external_exports.string(),
16297
- path: external_exports.string(),
16298
- /** Rollup of effective access across all descendants. */
16299
- accessCounts: AccessCounts
16300
- }).meta({ id: "FolderSummary" });
16301
- var ProjectTreeResponse = external_exports.object({
16302
- project: external_exports.object({
16303
- id: external_exports.string(),
16304
- repo: external_exports.string(),
16305
- visibility: Visibility
16306
- }),
16307
- path: external_exports.string(),
16308
- /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16309
- folders: external_exports.array(FolderSummary).optional(),
16310
- files: external_exports.array(FileSummary)
16311
- }).meta({ id: "ProjectTreeResponse" });
16312
- var FileDetail = FileSummary.extend({
16313
- project: external_exports.object({
16314
- repo: external_exports.string(),
16315
- visibility: Visibility,
16316
- language: external_exports.string(),
16317
- policyDefault: AccessLevel,
16318
- updatedAt: external_exports.iso.datetime()
16319
- }),
16320
- findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16321
- }).meta({ id: "FileDetail" });
16322
- var SetFileAccessBody = external_exports.object({
16323
- path: external_exports.string(),
16324
- access: AccessLevel
16325
- }).meta({ id: "SetFileAccessBody" });
16326
- var SetFileAccessResponse = external_exports.object({
16327
- file: FileSummary,
16328
- accessCounts: AccessCounts
16329
- }).meta({ id: "SetFileAccessResponse" });
16330
- var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16331
- var HarnessEventItem = external_exports.object({
16332
- kind: HarnessEventKind,
16333
- title: external_exports.string(),
16334
- detail: external_exports.string(),
16335
- occurredAt: external_exports.iso.datetime(),
16336
- findingId: external_exports.string().nullable().optional()
16337
- }).meta({ id: "HarnessEventItem" });
16338
- var HarnessEventsResponse = external_exports.object({
16339
- counts: external_exports.object({
16340
- block: external_exports.number().int().nonnegative(),
16341
- redact: external_exports.number().int().nonnegative(),
16342
- warn: external_exports.number().int().nonnegative()
16343
- }),
16344
- items: external_exports.array(HarnessEventItem)
16345
- }).meta({ id: "HarnessEventsResponse" });
16346
- var RescanResponse = external_exports.object({
16347
- jobId: external_exports.string(),
16348
- startedAt: external_exports.iso.datetime()
16349
- }).meta({ id: "RescanResponse" });
16350
- var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16351
- var ListAssetsQuery = external_exports.object({
16352
- /** Filter by one or more AssetType values; absent means all types. */
16353
- type: external_exports.array(AssetType).optional(),
16354
- /** Free-text search term. */
16355
- q: external_exports.string().optional()
16356
- });
16357
- var GetProjectTreeQuery = external_exports.object({
16358
- /** Subtree root path; defaults to repository root when absent. */
16359
- path: external_exports.string().optional(),
16360
- /** Free-text filter applied to file paths. */
16361
- q: external_exports.string().optional(),
16362
- /**
16363
- * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16364
- * file across the whole repo (folders omitted, most-recent first), ignoring
16365
- * `path`/`q` — powers the project-wide "recently blocked" strip.
16366
- */
16367
- filter: external_exports.enum(["blocked"]).optional()
16268
+ policyBundleVersion: external_exports.string().max(200).nullable(),
16269
+ /** Epoch millis, on the CLIENT clock, of that fetch. */
16270
+ policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
16271
+ }).meta({ id: "StorePosturePlugin" });
16272
+ var StorePostureSnapshot = external_exports.object({
16273
+ deviceId: external_exports.guid(),
16274
+ hostname: external_exports.string().min(1).max(253),
16275
+ // Epoch millis on the CLIENT clock. Bounded by what a receiving store
16276
+ // accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
16277
+ capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
16278
+ // False is a measurement, not an error state: "no local store exists on
16279
+ // this machine".
16280
+ storePresent: external_exports.boolean(),
16281
+ schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
16282
+ // PRAGMA user_version
16283
+ findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
16284
+ // Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
16285
+ // bound does and does not do. Worth stating for these two specifically:
16286
+ // they are read from the local store's own ROWS rather than from this
16287
+ // machine's clock, so a damaged or hand-edited store is enough to produce
16288
+ // an out-of-range value with no clock skew involved.
16289
+ findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16290
+ findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16291
+ packs: external_exports.array(StorePosturePack).max(500),
16292
+ policyCounts: StorePosturePolicyCounts,
16293
+ // OPTIONAL, not nullable: a reporter that predates this member keeps
16294
+ // getting its 200 without a payload change.
16295
+ plugin: StorePosturePlugin.optional()
16296
+ }).meta({ id: "StorePostureSnapshot" });
16297
+ var CAPTURE_VERSION_PREFIX = "capture/";
16298
+ var RecordAuditEventRequest = AuditEventInput.extend({
16299
+ inspections: external_exports.array(ToolCallInspection).default([])
16300
+ }).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
16301
+ message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
16302
+ path: ["inspections"]
16303
+ }).meta({ id: "RecordAuditEventRequest" });
16304
+ var IngestAck = external_exports.object({
16305
+ accepted: external_exports.number().int().nonnegative(),
16306
+ duplicates: external_exports.number().int().nonnegative()
16368
16307
  });
16369
- var GetProjectFileQuery = external_exports.object({
16370
- /** Repository-relative file path; absent or empty 400. */
16371
- path: external_exports.string()
16308
+ var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
16309
+ var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
16310
+ var PluginWhoami = external_exports.object({
16311
+ tenantName: printable(200),
16312
+ userEmail: printable(320),
16313
+ role: printable(64),
16314
+ keyKind: printable(64),
16315
+ serverTime: printable(64)
16372
16316
  });
16373
- var GetHarnessEventsQuery = external_exports.object({
16374
- /** Maximum number of events to return. Range: 1–50; default: 7. */
16375
- limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16317
+ var ControlPlaneErrorBody = external_exports.object({
16318
+ error: external_exports.object({
16319
+ code: external_exports.string().optional(),
16320
+ message: external_exports.string().optional()
16321
+ }).optional()
16376
16322
  });
16377
16323
 
16378
- // ../../packages/schema/src/zod/exception.ts
16379
- var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
16380
- var ExceptionConditions = external_exports.object({
16381
- repo: external_exports.string().optional(),
16382
- sourceTool: external_exports.string().optional(),
16383
- provider: external_exports.string().optional()
16384
- }).strict();
16385
- var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
16386
- var DetectionException = external_exports.object({
16387
- id: external_exports.guid(),
16388
- ruleId: external_exports.string(),
16389
- // Denormalized from the rule, for reporting — never matched on.
16390
- category: DetectionCategory,
16391
- // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
16392
- // fingerprint, never the raw value, and never reversible. Matching recomputes
16393
- // the fingerprint from a fresh capture; the value itself is never stored.
16394
- // Shape-constrained so a malformed — or accidentally raw — value is rejected
16395
- // at the boundary rather than persisted.
16396
- valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
16397
- // Version of the fingerprint key the grant was written under; a rotated key
16398
- // invalidates old grants rather than silently mismatching them.
16399
- keyVersion: external_exports.number().int().positive(),
16400
- // maskMatch() preview of the approved value — never the raw value.
16401
- maskedValue: external_exports.string(),
16402
- capability: ExceptionCapability.default("suppress"),
16403
- scope: ExceptionScope,
16404
- expiresAt: external_exports.iso.datetime().nullable(),
16405
- maxUses: external_exports.number().int().positive().nullable(),
16406
- useCount: external_exports.number().int().nonnegative(),
16407
- lastUsedAt: external_exports.iso.datetime().nullable(),
16408
- // Mandatory: every grant carries the human reason it exists.
16409
- justification: external_exports.string().min(1),
16410
- conditions: ExceptionConditions.nullable(),
16411
- createdBy: external_exports.string(),
16412
- createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
16413
- createdAt: external_exports.iso.datetime(),
16414
- updatedAt: external_exports.iso.datetime(),
16415
- // Revocation is terminal and retained — consumed/expired/revoked rows are
16416
- // audit evidence; nothing in the exception lifecycle hard-deletes.
16417
- revokedAt: external_exports.iso.datetime().nullable(),
16418
- revokedBy: external_exports.string().nullable(),
16419
- revokeReason: external_exports.string().nullable()
16420
- });
16421
- var ExceptionBundleEntry = DetectionException.pick({
16422
- id: true,
16423
- ruleId: true,
16424
- valueFingerprint: true,
16425
- keyVersion: true,
16426
- capability: true,
16427
- expiresAt: true,
16428
- maxUses: true,
16429
- useCount: true,
16430
- conditions: true
16431
- });
16432
- var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
16324
+ // ../../packages/schema/src/zod/registry.ts
16325
+ var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16326
+ var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16327
+ var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16328
+ var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16433
16329
 
16434
16330
  // ../../packages/schema/src/zod/rule.ts
16435
- var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
16331
+ var MatcherType = external_exports.enum(["keyword", "regex"]).meta({ id: "MatcherType" });
16436
16332
  var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
16437
- var KeywordMatcher = external_exports.object({
16333
+ var KeywordMatcher = external_exports.strictObject({
16438
16334
  type: external_exports.literal("keyword"),
16439
16335
  // An empty keyword matches at every position, yielding one zero-length span
16440
16336
  // per character. Rejected here because a keyword that matches everything is
@@ -16450,16 +16346,31 @@ function isValidRegex(pattern, flags) {
16450
16346
  return false;
16451
16347
  }
16452
16348
  }
16349
+ function probeFlags(flags) {
16350
+ return flags.replace(/[gy]/g, "");
16351
+ }
16453
16352
  function matchesEmptyString(pattern, flags) {
16454
16353
  try {
16455
- const re = new RegExp(pattern, flags.replace(/[gy]/g, ""));
16354
+ const re = new RegExp(pattern, probeFlags(flags));
16456
16355
  return re.exec("")?.[0].length === 0;
16457
16356
  } catch {
16458
16357
  return false;
16459
16358
  }
16460
16359
  }
16360
+ function spansWholeMatch(captureGroup) {
16361
+ return captureGroup === void 0 || captureGroup === 0;
16362
+ }
16363
+ function captureGroupCount(pattern, flags) {
16364
+ try {
16365
+ const probe = new RegExp(`${pattern}|`, probeFlags(flags));
16366
+ const result = probe.exec("");
16367
+ return result ? result.length - 1 : void 0;
16368
+ } catch {
16369
+ return void 0;
16370
+ }
16371
+ }
16461
16372
  var MAX_PATTERN_LENGTH = 2e3;
16462
- var RegexMatcher = external_exports.object({
16373
+ var RegexMatcher = external_exports.strictObject({
16463
16374
  type: external_exports.literal("regex"),
16464
16375
  pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
16465
16376
  flags: external_exports.string().default("gi"),
@@ -16467,379 +16378,112 @@ var RegexMatcher = external_exports.object({
16467
16378
  }).refine((v) => isValidRegex(v.pattern, v.flags), {
16468
16379
  message: "pattern/flags do not form a valid JavaScript regular expression",
16469
16380
  path: ["pattern"]
16470
- }).refine((v) => v.captureGroup !== void 0 || !matchesEmptyString(v.pattern, v.flags), {
16381
+ }).refine((v) => !spansWholeMatch(v.captureGroup) || !matchesEmptyString(v.pattern, v.flags), {
16471
16382
  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',
16472
16383
  path: ["pattern"]
16384
+ }).superRefine((v, ctx) => {
16385
+ if (v.captureGroup === void 0) return;
16386
+ const groups = captureGroupCount(v.pattern, v.flags);
16387
+ if (groups === void 0 || v.captureGroup <= groups) return;
16388
+ ctx.addIssue({
16389
+ code: "custom",
16390
+ path: ["captureGroup"],
16391
+ 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.`
16392
+ });
16473
16393
  });
16474
- var ValidatorMatcher = external_exports.object({
16475
- type: external_exports.literal("validator"),
16476
- name: external_exports.enum(["luhn", "entropy", "ssn-checksum"]),
16477
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16478
- });
16479
- var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher, ValidatorMatcher]).meta({ id: "Matcher" });
16480
- var AppliesTo = external_exports.object({
16394
+ var Matcher = external_exports.discriminatedUnion("type", [KeywordMatcher, RegexMatcher]).meta({ id: "Matcher" });
16395
+ var MATCHER_TYPES = MatcherType.options;
16396
+ var AppliesTo = external_exports.strictObject({
16481
16397
  // Dot-prefixed, e.g. ".py" — matches the scanner's SOURCE_EXTENSIONS shape.
16482
16398
  extensions: external_exports.array(external_exports.string().regex(/^\.[A-Za-z0-9]+$/)).min(1)
16483
16399
  }).meta({ id: "AppliesTo" });
16484
- var PostValidatorRef = external_exports.union([
16485
- external_exports.string(),
16486
- external_exports.object({
16487
- name: external_exports.string(),
16488
- config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16489
- })
16490
- ]).meta({ id: "PostValidatorRef" });
16491
- var RequiresNearby = external_exports.object({
16492
- // Each array, when present, must be non-empty and contain non-empty strings —
16493
- // an empty/blank criterion would either never fire or (for labels) match
16494
- // everything.
16495
- categories: external_exports.array(DetectionCategory).min(1).optional(),
16496
- ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16497
- labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16498
- windowChars: external_exports.number().int().positive().default(160),
16499
- // Optional confidence bump applied when a gated match is corroborated. Capped
16500
- // small: it nudges confidence, it does not assert certainty.
16501
- confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16502
- }).refine(
16503
- (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16504
- { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16505
- ).meta({ id: "RequiresNearby" });
16506
- var RuleFixture = external_exports.object({
16507
- label: external_exports.string(),
16508
- text: external_exports.string().max(5e4),
16509
- shouldMatch: external_exports.boolean(),
16510
- // Simulated file context for the scan, so fixtures can assert `appliesTo`
16511
- // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16512
- filePath: external_exports.string().optional(),
16513
- expectedSpans: external_exports.array(external_exports.object({ start: external_exports.number(), end: external_exports.number() })).optional()
16514
- }).meta({ id: "RuleFixture" });
16515
- var Rule = external_exports.object({
16516
- specVersion: external_exports.literal(1),
16517
- // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16518
- // the PACK id, NOT a namespace this is a DIFFERENT slug space from a
16519
- // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16520
- // therefore carries no namespace and is not globally unique across publishers;
16521
- // never feed one to splitDetectionId. `category` below (per-rule) is the
16522
- // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16523
- id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16524
- name: external_exports.string(),
16525
- category: DetectionCategory,
16526
- severity: Severity,
16527
- matcher: Matcher,
16528
- appliesTo: AppliesTo.optional(),
16529
- postValidators: external_exports.array(PostValidatorRef).optional(),
16530
- requiresNearby: RequiresNearby.optional(),
16531
- examples: external_exports.array(external_exports.string()).optional()
16532
- }).meta({ id: "Rule" });
16533
- var Author = external_exports.object({
16534
- name: external_exports.string(),
16535
- email: external_exports.email().optional(),
16536
- url: external_exports.url().optional()
16537
- }).meta({ id: "Author" });
16538
- var PackManifest = external_exports.object({
16539
- specVersion: external_exports.literal(1),
16540
- id: external_exports.string(),
16541
- name: external_exports.string(),
16542
- version: external_exports.string(),
16543
- rules: external_exports.array(external_exports.string()),
16544
- // Optional attribution/provenance — consumed by the rule marketplace.
16545
- description: external_exports.string().optional(),
16546
- author: Author.optional(),
16547
- license: external_exports.string().optional(),
16548
- sourceUrl: external_exports.url().optional()
16549
- }).meta({ id: "PackManifest" });
16550
-
16551
- // ../../packages/schema/src/zod/policy.ts
16552
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
16553
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
16554
- var Policy = external_exports.object({
16555
- id: external_exports.guid(),
16556
- scope: PolicyScope,
16557
- target: PolicyTarget,
16558
- action: ActionTaken,
16559
- enabled: external_exports.boolean().default(true),
16560
- customKeywords: external_exports.array(external_exports.string()).optional(),
16561
- // Display name — optional so older policy rows without name still parse.
16562
- // Added for the findings API (policy.name column migration).
16563
- name: external_exports.string().optional()
16564
- }).meta({ id: "Policy" });
16565
- var PolicyBundle = external_exports.object({
16566
- version: external_exports.string(),
16567
- policies: external_exports.array(Policy),
16568
- // Rules from the installed marketplace packs (snapshotted by the
16569
- // control plane). The plugin registers these in addition to its bundled
16570
- // packs. Optional so older backends — and older on-disk caches — that omit
16571
- // the field still parse; consumers read `bundle.rules ?? []`.
16572
- rules: external_exports.array(Rule).optional(),
16573
- // When true, `rules` IS the complete effective ruleset and the runtime must
16574
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
16575
- // after reading the user's installed snapshot (installed_packs, enabled
16576
- // packs only), which is how detection updates stay manual: new bundled
16577
- // rules run only after the user applies the pack update. Absent/false keeps
16578
- // the historical composition (bundled packs + rules) — older caches.
16579
- rulesComplete: external_exports.boolean().optional(),
16580
- // Active detection exceptions, evaluation subset only (see
16581
- // ExceptionBundleEntry). Optional so older bundle producers — and older
16582
- // on-disk caches — that omit the field still parse; consumers read
16583
- // `bundle.exceptions ?? []`.
16584
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
16585
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
16586
- // from a versioned installed pack. Optional so older backends — and older
16587
- // on-disk caches — that omit the field still parse; consumers fall back to
16588
- // the rule's own spec version. NOT the bundle version above — see
16589
- // installedRuleset's ruleVersions for the source of truth.
16590
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
16591
- customKeywords: external_exports.array(external_exports.string()),
16592
- fetchedAt: external_exports.iso.datetime()
16593
- }).meta({ id: "PolicyBundle" });
16594
- var OBSERVE_ONLY_CATEGORIES = ["config"];
16595
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
16596
- var CATEGORY_PEAK_SEVERITY = {
16597
- secret: "critical",
16598
- financial: "critical",
16599
- // core-financial/credit-card
16600
- code_flaw: "critical",
16601
- pii: "high",
16602
- phi: "high",
16603
- custom: "high",
16604
- // user-defined; conservative
16605
- code_context: "low",
16606
- config: "low"
16607
- // observe-only; floors to monitor regardless
16608
- };
16609
- function severityFloorPolicy(category) {
16610
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
16611
- const peak = CATEGORY_PEAK_SEVERITY[category];
16612
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
16613
- }
16614
- function severityFloorPosture() {
16615
- const out = {};
16616
- for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
16617
- return out;
16618
- }
16619
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
16620
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "block"];
16621
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
16622
- var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
16623
- var BUILTIN_POLICY_SPECS = {
16624
- monitor: {
16625
- name: "Monitor",
16626
- action: "log",
16627
- description: "Log every match for audit. The request is allowed through untouched."
16628
- },
16629
- warn: {
16630
- name: "Warn",
16631
- action: "warn",
16632
- description: "Allow the request, but warn the user inline before it is sent."
16633
- },
16634
- redact: {
16635
- name: "Redact",
16636
- action: "redact",
16637
- description: "Automatically strip the matched value from the request, then continue."
16638
- },
16639
- block: {
16640
- name: "Block",
16641
- action: "block",
16642
- description: "Refuse the request entirely whenever any rule in this detection matches."
16643
- }
16644
- };
16645
- function builtinPolicyToAction(id) {
16646
- return BUILTIN_POLICY_SPECS[id].action;
16647
- }
16648
- var DEFAULT_ACTIONS = Object.fromEntries(
16649
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
16650
- );
16651
- var BUILTIN_POLICIES = Object.fromEntries(
16652
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
16653
- );
16654
- var DEFAULT_PACK_POLICY_ID = "monitor";
16655
- function policyIdToAction(policyId) {
16656
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
16657
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
16658
- return BUILTIN_POLICIES[id].action;
16659
- }
16660
- var UsedByItem = external_exports.object({
16661
- id: external_exports.string(),
16662
- name: external_exports.string(),
16663
- ruleCount: external_exports.number().int().nonnegative(),
16664
- enabled: external_exports.boolean()
16665
- }).meta({ id: "UsedByItem" });
16666
- var PolicyListItem = external_exports.object({
16667
- id: external_exports.string(),
16668
- kind: PolicyKind,
16669
- name: external_exports.string(),
16670
- enabled: external_exports.boolean(),
16671
- usedByCount: external_exports.number().int().nonnegative()
16672
- }).meta({ id: "PolicyListItem" });
16673
- var PolicyDetail = external_exports.object({
16674
- specVersion: external_exports.literal(1),
16675
- id: external_exports.string(),
16676
- kind: PolicyKind,
16677
- name: external_exports.string(),
16678
- enabled: external_exports.boolean(),
16679
- description: external_exports.string(),
16680
- usedBy: external_exports.array(UsedByItem)
16681
- }).meta({ id: "PolicyDetail" });
16682
- var PolicyStatsResponse = external_exports.object({
16683
- policies: external_exports.number().int().nonnegative(),
16684
- builtin: external_exports.number().int().nonnegative(),
16685
- custom: external_exports.number().int().nonnegative(),
16686
- detectionsGoverned: external_exports.number().int().nonnegative()
16687
- }).meta({ id: "PolicyStatsResponse" });
16688
-
16689
- // ../../packages/schema/src/zod/api.ts
16690
- var LIST_QUERY_MAX_LIMIT = 200;
16691
- var ListEventsQuery = external_exports.object({
16692
- cursor: external_exports.string().optional(),
16693
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16694
- sourceTool: external_exports.string().optional(),
16695
- kind: external_exports.string().optional(),
16696
- from: external_exports.iso.datetime().optional(),
16697
- to: external_exports.iso.datetime().optional()
16698
- });
16699
- var ListEventsResponse = external_exports.object({
16700
- items: external_exports.array(Event),
16701
- nextCursor: external_exports.string().nullable()
16702
- }).meta({ id: "ListEventsResponse" });
16703
- var IngestResponse = external_exports.object({
16704
- accepted: external_exports.number().int().nonnegative(),
16705
- duplicates: external_exports.number().int().nonnegative()
16706
- }).meta({ id: "IngestResponse" });
16707
- var ListFindingsQuery = external_exports.object({
16708
- cursor: external_exports.string().optional(),
16709
- limit: external_exports.coerce.number().int().min(1).max(LIST_QUERY_MAX_LIMIT).default(50),
16710
- severity: external_exports.string().optional(),
16711
- category: external_exports.string().optional(),
16712
- eventId: external_exports.guid().optional()
16713
- });
16714
- var ListFindingsResponse = external_exports.object({
16715
- items: external_exports.array(Finding),
16716
- nextCursor: external_exports.string().nullable()
16717
- }).meta({ id: "ListFindingsResponse" });
16718
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
16719
- var CreatePolicyRequest = Policy.omit({ id: true }).meta({
16720
- id: "CreatePolicyRequest"
16721
- });
16722
- var UpdatePolicyRequest = Policy.partial().required({ id: true }).meta({ id: "UpdatePolicyRequest" });
16723
- var RecordAuditEventResponse = external_exports.object({ accepted: external_exports.boolean() }).meta({ id: "RecordAuditEventResponse" });
16724
- var ErrorResponse = external_exports.object({
16725
- error: external_exports.object({
16726
- code: external_exports.string(),
16727
- message: external_exports.string(),
16728
- details: external_exports.unknown().optional()
16729
- })
16730
- }).meta({ id: "ErrorResponse" });
16731
-
16732
- // ../../packages/schema/src/zod/config-inventory.ts
16733
- var ConfigScope = external_exports.enum(["user", "project", "local", "plugin"]);
16734
- var SkillScanEntry = external_exports.object({
16735
- name: external_exports.string().min(1),
16736
- // The identity source: a marketplace repo for plugin skills (e.g.
16737
- // 'anthropics/skills'), 'local' for personal ~/.claude/skills, or
16738
- // 'project:<repo-identity>' for checked-in project skills. The surrogate keeps
16739
- // a personal skill named 'pdf' from colliding with the marketplace 'pdf'.
16740
- source: external_exports.string().min(1),
16741
- scope: ConfigScope,
16742
- pluginName: external_exports.string().optional(),
16743
- // Volatile — rides the attribute bag, never the identity hash.
16744
- version: external_exports.string().optional(),
16745
- description: external_exports.string().optional(),
16746
- // Skill directory mtime (ISO) — the "updated Nd ago" freshness signal.
16747
- updatedAt: external_exports.iso.datetime().optional(),
16748
- // Filesystem path — the promoted inventory `location` column.
16749
- location: external_exports.string().optional()
16750
- });
16751
- var HookScanEntry = external_exports.object({
16752
- // Hook event name (PreToolUse, PostToolUse, …). Open string, not an enum: the
16753
- // set is harness-defined and grows without a schema change.
16754
- event: external_exports.string().min(1),
16755
- // The tool matcher ('Bash', 'Edit|Write', …). Absent = matches all tools.
16756
- matcher: external_exports.string().optional(),
16757
- command: external_exports.string().min(1),
16758
- timeout: external_exports.number().optional(),
16759
- scope: ConfigScope,
16760
- pluginName: external_exports.string().optional(),
16761
- // The settings file / hooks.json the entry came from.
16762
- location: external_exports.string().optional()
16763
- });
16764
- var McpServerScanEntry = external_exports.object({
16765
- // The server's config key ("github", "filesystem", …) — identity, with the
16766
- // qualified scope (see mcpServerIdentityKey).
16767
- name: external_exports.string().min(1),
16768
- scope: ConfigScope,
16769
- pluginName: external_exports.string().optional(),
16770
- // The owning plugin's marketplace — part of PLUGIN-scope identity: two
16771
- // marketplaces can each ship a plugin named `guard`, and without this their
16772
- // same-named servers would collapse to one row (the second silently dropped,
16773
- // inheriting the first's trust).
16774
- marketplace: external_exports.string().optional(),
16775
- // The repo identity (remote url, or the cwd for un-remoted repos) — part of
16776
- // PROJECT/LOCAL-scope identity: a server named `github` in repo A and one in
16777
- // repo B are different servers with different commands, and MUST NOT share a
16778
- // row — a shared row would let a cloned repo's .mcp.json inherit the trust
16779
- // the user granted elsewhere.
16780
- project: external_exports.string().optional(),
16781
- // 'stdio' when the entry carries a command; otherwise the config's `type`
16782
- // ('http' / 'sse' / …). Open string — the transport set is harness-defined.
16783
- transport: external_exports.string().min(1),
16784
- // Volatile on purpose (unlike hook `command`): a changed command/url is drift
16785
- // on a stable row — visible across config_scan snapshots and preserving the
16786
- // user's trust decision — never a quiet new row. One of the two is present.
16787
- // Secret-masked at collection time (the scanner runs the bundled detection
16788
- // packs over both — tokens routinely ride command args and URLs).
16789
- command: external_exports.string().optional(),
16790
- url: external_exports.string().optional(),
16791
- // Env var NAMES only, never values (the no-secrets rule).
16792
- envKeys: external_exports.array(external_exports.string()).optional(),
16793
- // The config file the entry came from.
16794
- location: external_exports.string().optional()
16795
- });
16796
- var ConfigFileScanEntry = external_exports.object({
16797
- // Basename (settings.json, CLAUDE.md) or dir name (commands/, agents/).
16798
- name: external_exports.string().min(1),
16799
- // The absolute path — identity (with scope) and the promoted `location`.
16800
- path: external_exports.string().min(1),
16801
- scope: ConfigScope,
16802
- // Human label: "User settings", "Project memory", "Slash commands", …
16803
- kind: external_exports.string().min(1),
16804
- // Derived SHAPE summary — top-level key names, entry counts, line counts.
16805
- // Never file content or values (memory files can carry sensitive detail).
16806
- detail: external_exports.string().optional(),
16807
- // Dir configs (commands/, agents/) and .mcp.json: how many entries.
16808
- entryCount: external_exports.number().optional(),
16809
- // File mtime (ISO) — the freshness signal.
16810
- updatedAt: external_exports.iso.datetime().optional()
16811
- });
16812
- var ConfigScanResult = external_exports.object({
16813
- scannedAt: external_exports.iso.datetime(),
16814
- skills: external_exports.array(SkillScanEntry),
16815
- hooks: external_exports.array(HookScanEntry),
16816
- mcpServers: external_exports.array(McpServerScanEntry),
16817
- configFiles: external_exports.array(ConfigFileScanEntry),
16818
- errors: external_exports.array(external_exports.object({ source: external_exports.string(), reason: external_exports.string() }))
16819
- });
16820
- var ConfigPostureFindingInput = external_exports.object({
16821
- ruleId: external_exports.string().min(1),
16822
- version: external_exports.string().min(1),
16823
- span: Span,
16824
- // For posture rules this is the offending COMMAND (config the user already
16825
- // holds locally, not captured secret content) — it is also the correlation
16826
- // key the read surface matches back to a hook row.
16827
- maskedMatch: external_exports.string(),
16828
- actionTaken: ActionTaken,
16829
- confidence: external_exports.number().min(0).max(1)
16830
- });
16831
- var ConfigScanRecord = external_exports.object({
16832
- items: external_exports.array(InventoryInput),
16833
- scanEvent: AuditEventInput,
16834
- definitions: external_exports.array(InspectionDefinitionInput).optional(),
16835
- findings: external_exports.array(ConfigPostureFindingInput).optional()
16836
- });
16837
-
16838
- // ../../packages/schema/src/zod/registry.ts
16839
- var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16840
- var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16841
- var SemVer = external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/);
16842
- var PublisherKind = external_exports.enum(["labs", "user", "org"]);
16400
+ var PostValidatorName = external_exports.enum(["entropy", "luhn"]).meta({ id: "PostValidatorName" });
16401
+ var PostValidatorRef = external_exports.union(
16402
+ [
16403
+ PostValidatorName,
16404
+ external_exports.strictObject({
16405
+ name: PostValidatorName,
16406
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
16407
+ })
16408
+ ],
16409
+ {
16410
+ // A union reports one collapsed issue for every way its arms can fail, so
16411
+ // this has to describe the whole shape rather than just the name — it is
16412
+ // what an author sees for a misspelled name AND for a stray key in the
16413
+ // object form. The names come from the enum so the message cannot go
16414
+ // stale. Without it Zod says only "Invalid input", which is precisely the
16415
+ // no-feedback outcome this schema exists to remove.
16416
+ error: () => `not a valid post-validator: use a bare name (${PostValidatorName.options.map((name) => JSON.stringify(name)).join(
16417
+ " or "
16418
+ )}) or { "name": ..., "config": { ... } }. An unrecognized name would be a false-positive guard that never runs.`
16419
+ }
16420
+ ).meta({ id: "PostValidatorRef" });
16421
+ var RequiresNearby = external_exports.strictObject({
16422
+ // Each array, when present, must be non-empty and contain non-empty strings —
16423
+ // an empty/blank criterion would either never fire or (for labels) match
16424
+ // everything.
16425
+ categories: external_exports.array(DetectionCategory).min(1).optional(),
16426
+ ruleIds: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16427
+ labels: external_exports.array(external_exports.string().min(1)).min(1).optional(),
16428
+ windowChars: external_exports.number().int().positive().default(160),
16429
+ // Optional confidence bump applied when a gated match is corroborated. Capped
16430
+ // small: it nudges confidence, it does not assert certainty.
16431
+ confidenceBoost: external_exports.number().min(0).max(0.3).optional()
16432
+ }).refine(
16433
+ (v) => (v.categories?.length ?? 0) + (v.ruleIds?.length ?? 0) + (v.labels?.length ?? 0) > 0,
16434
+ { message: "requiresNearby needs at least one of categories, ruleIds, or labels" }
16435
+ ).meta({ id: "RequiresNearby" });
16436
+ var RuleFixture = external_exports.strictObject({
16437
+ label: external_exports.string(),
16438
+ text: external_exports.string().max(5e4),
16439
+ shouldMatch: external_exports.boolean(),
16440
+ // Simulated file context for the scan, so fixtures can assert `appliesTo`
16441
+ // gating (e.g. a Python-only pattern must NOT fire in a .ts file).
16442
+ filePath: external_exports.string().optional(),
16443
+ expectedSpans: external_exports.array(external_exports.strictObject({ start: external_exports.number(), end: external_exports.number() })).optional()
16444
+ }).meta({ id: "RuleFixture" });
16445
+ var Rule = external_exports.strictObject({
16446
+ // A pinned literal over a STRICT object, and the two together decide how this
16447
+ // format may grow. A rule carrying a key not listed below is refused with
16448
+ // `unrecognized_keys`; a rule declaring `specVersion: 2` is refused with
16449
+ // `invalid_value`. So the only additive path is adding an OPTIONAL field here
16450
+ // — that keeps every rule authored before it valid — and a rule author has no
16451
+ // way to introduce a field of their own or to opt into a later version.
16452
+ // Widening the format means changing this literal and every consumer of it.
16453
+ specVersion: external_exports.literal(1),
16454
+ // `packId/ruleName` (e.g. `secrets/aws-access-key`). NOTE the first segment is
16455
+ // the PACK id, NOT a namespace — this is a DIFFERENT slug space from a
16456
+ // detection id (`namespace/packId`, decoded by splitDetectionId). A Rule.id
16457
+ // therefore carries no namespace and is not globally unique across publishers;
16458
+ // never feed one to splitDetectionId. `category` below (per-rule) is the
16459
+ // taxonomy axis; the pack's enforcement policy is installed_packs.policy_id.
16460
+ id: external_exports.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/),
16461
+ name: external_exports.string(),
16462
+ category: DetectionCategory,
16463
+ severity: Severity,
16464
+ matcher: Matcher,
16465
+ appliesTo: AppliesTo.optional(),
16466
+ postValidators: external_exports.array(PostValidatorRef).optional(),
16467
+ requiresNearby: RequiresNearby.optional(),
16468
+ examples: external_exports.array(external_exports.string()).optional()
16469
+ }).meta({ id: "Rule" });
16470
+ var Author = external_exports.object({
16471
+ name: external_exports.string(),
16472
+ email: external_exports.email().optional(),
16473
+ url: external_exports.url().optional()
16474
+ }).meta({ id: "Author" });
16475
+ var PackManifest = external_exports.object({
16476
+ specVersion: external_exports.literal(1),
16477
+ id: external_exports.string(),
16478
+ name: external_exports.string(),
16479
+ version: external_exports.string(),
16480
+ rules: external_exports.array(external_exports.string()),
16481
+ // Optional attribution/provenance — consumed by the rule marketplace.
16482
+ description: external_exports.string().optional(),
16483
+ author: Author.optional(),
16484
+ license: external_exports.string().optional(),
16485
+ sourceUrl: external_exports.url().optional()
16486
+ }).meta({ id: "PackManifest" });
16843
16487
 
16844
16488
  // ../../packages/schema/src/zod/detection.ts
16845
16489
  var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
@@ -17039,6 +16683,231 @@ function buildDetectionsList(summaries, query) {
17039
16683
  return { counts, items: filtered.map(summaryToDetectionListItem) };
17040
16684
  }
17041
16685
 
16686
+ // ../../packages/schema/src/zod/inventory.ts
16687
+ var AssetType = external_exports.enum(["project", "skill", "mcp", "hook", "config"]).meta({ id: "AssetType" });
16688
+ var AccessLevel = external_exports.enum(["open", "approved", "blocked"]).meta({ id: "AccessLevel" });
16689
+ var Origin = external_exports.enum(["source", "public-dep", "vendored", "config", "data", "docs", "generated"]).meta({ id: "Origin" });
16690
+ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).meta({ id: "TrustLevel" });
16691
+ var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
16692
+ var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
16693
+ var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
16694
+ var HarnessId = Harness.extract(["ClaudeCode", "Cursor", "Codex", "Antigravity"]).meta({
16695
+ id: "HarnessId"
16696
+ });
16697
+ var AccessCounts = external_exports.object({
16698
+ open: external_exports.number().int().nonnegative(),
16699
+ approved: external_exports.number().int().nonnegative(),
16700
+ blocked: external_exports.number().int().nonnegative(),
16701
+ total: external_exports.number().int().nonnegative()
16702
+ }).meta({ id: "AccessCounts" });
16703
+ var AssetSummary = external_exports.object({
16704
+ id: external_exports.string(),
16705
+ type: AssetType,
16706
+ name: external_exports.string(),
16707
+ sub: external_exports.string(),
16708
+ flags: external_exports.array(Flag),
16709
+ /** MCP servers only — omitted for all other types. */
16710
+ trust: TrustLevel.optional()
16711
+ }).meta({ id: "AssetSummary" });
16712
+ var ProjectSummary = external_exports.object({
16713
+ id: external_exports.string(),
16714
+ name: external_exports.string(),
16715
+ repo: external_exports.string(),
16716
+ visibility: Visibility,
16717
+ language: external_exports.string(),
16718
+ policyDefault: AccessLevel,
16719
+ updatedAt: external_exports.iso.datetime(),
16720
+ accessCounts: AccessCounts,
16721
+ findingsCount: external_exports.number().int().nonnegative()
16722
+ }).meta({ id: "ProjectSummary" });
16723
+ var HarnessCategory = external_exports.object({
16724
+ /** One of config/skill/mcp/hook — never project (enforced at service layer). */
16725
+ type: AssetType,
16726
+ assets: external_exports.array(AssetSummary)
16727
+ });
16728
+ var HarnessSummary = external_exports.object({
16729
+ id: HarnessId,
16730
+ label: external_exports.string(),
16731
+ kind: external_exports.string(),
16732
+ version: external_exports.string(),
16733
+ sessions: external_exports.number().int().nonnegative(),
16734
+ assetCount: external_exports.number().int().nonnegative(),
16735
+ flagCount: external_exports.number().int().nonnegative(),
16736
+ projects: external_exports.array(ProjectSummary),
16737
+ categories: external_exports.array(HarnessCategory)
16738
+ }).meta({ id: "HarnessSummary" });
16739
+ var ListHarnessesResponse = external_exports.object({ items: external_exports.array(HarnessSummary) }).meta({ id: "ListHarnessesResponse" });
16740
+ var AssetGroup = external_exports.object({
16741
+ /** Group key — never project (enforced at service layer). */
16742
+ type: AssetType,
16743
+ total: external_exports.number().int().nonnegative(),
16744
+ /**
16745
+ * MCP group only — omitted for all other types.
16746
+ * Partial: only TrustLevel keys with non-zero counts are included.
16747
+ * Strict: unknown keys are rejected — only TrustLevel values are valid keys.
16748
+ */
16749
+ trustRollup: external_exports.object({
16750
+ "known-good": external_exports.number().int().nonnegative(),
16751
+ risky: external_exports.number().int().nonnegative(),
16752
+ unapproved: external_exports.number().int().nonnegative()
16753
+ }).partial().strict().optional(),
16754
+ /**
16755
+ * Partial: only Flag keys with non-zero counts are included.
16756
+ * Strict: unknown keys are rejected — only Flag values are valid keys.
16757
+ */
16758
+ flagRollup: external_exports.object({
16759
+ update: external_exports.number().int().nonnegative(),
16760
+ stale: external_exports.number().int().nonnegative(),
16761
+ conflict: external_exports.number().int().nonnegative(),
16762
+ unknown: external_exports.number().int().nonnegative(),
16763
+ change: external_exports.number().int().nonnegative(),
16764
+ untracked: external_exports.number().int().nonnegative(),
16765
+ risk: external_exports.number().int().nonnegative(),
16766
+ findings: external_exports.number().int().nonnegative()
16767
+ }).partial().strict(),
16768
+ items: external_exports.array(AssetSummary)
16769
+ }).meta({ id: "AssetGroup" });
16770
+ var ListAssetsResponse = external_exports.object({ groups: external_exports.array(AssetGroup) }).meta({ id: "ListAssetsResponse" });
16771
+ var McpTool = external_exports.object({
16772
+ name: external_exports.string(),
16773
+ signature: external_exports.string(),
16774
+ description: external_exports.string(),
16775
+ write: external_exports.boolean(),
16776
+ /** Non-null string when tool is dangerous / blocked; null otherwise. */
16777
+ risk: external_exports.string().nullable()
16778
+ }).meta({ id: "McpTool" });
16779
+ var AssetFindingRef = external_exports.object({
16780
+ id: external_exports.string(),
16781
+ title: external_exports.string(),
16782
+ note: external_exports.string()
16783
+ });
16784
+ var AssetDetail = AssetSummary.extend({
16785
+ /** string | null — null when no description is available. */
16786
+ description: external_exports.string().nullable(),
16787
+ /** trustLevel | null — null for non-MCP assets. */
16788
+ trust: TrustLevel.nullable(),
16789
+ /** Type-specific raw key/values — FE renders the grid. */
16790
+ meta: external_exports.record(external_exports.string(), external_exports.unknown()),
16791
+ /** always present — object when there is an active finding, null when absent. */
16792
+ finding: AssetFindingRef.nullable(),
16793
+ /** MCP exposed-tools list — omitted for non-mcp. */
16794
+ tools: external_exports.array(McpTool).optional()
16795
+ }).meta({ id: "AssetDetail" });
16796
+ var ListProjectsResponse = external_exports.object({ items: external_exports.array(ProjectSummary) }).meta({ id: "ListProjectsResponse" });
16797
+ var InventoryStats = external_exports.object({
16798
+ /** Total assets/projects with ≥1 flag or finding (drives the attention header chip). */
16799
+ attention: external_exports.number().int().nonnegative(),
16800
+ byType: external_exports.object({
16801
+ project: external_exports.number().int().nonnegative(),
16802
+ skill: external_exports.number().int().nonnegative(),
16803
+ mcp: external_exports.number().int().nonnegative(),
16804
+ hook: external_exports.number().int().nonnegative(),
16805
+ config: external_exports.number().int().nonnegative()
16806
+ }),
16807
+ harnesses: external_exports.number().int().nonnegative(),
16808
+ mcpTrust: external_exports.object({
16809
+ "known-good": external_exports.number().int().nonnegative(),
16810
+ risky: external_exports.number().int().nonnegative(),
16811
+ unapproved: external_exports.number().int().nonnegative()
16812
+ })
16813
+ }).meta({ id: "InventoryStats" });
16814
+ var FileSummary = external_exports.object({
16815
+ path: external_exports.string(),
16816
+ name: external_exports.string(),
16817
+ origin: Origin,
16818
+ /** Effective access (override applied). */
16819
+ access: AccessLevel,
16820
+ /** True when a file_access_override differs from the computed default. */
16821
+ isCustom: external_exports.boolean(),
16822
+ findings: external_exports.number().int().nonnegative(),
16823
+ /** When the file was auto-blocked by a detection; null when not blocked. */
16824
+ blockedAt: external_exports.iso.datetime().nullable().optional(),
16825
+ /** Why the file was blocked; null when absent. */
16826
+ note: external_exports.string().nullable().optional()
16827
+ }).meta({ id: "FileSummary" });
16828
+ var FolderSummary = external_exports.object({
16829
+ name: external_exports.string(),
16830
+ path: external_exports.string(),
16831
+ /** Rollup of effective access across all descendants. */
16832
+ accessCounts: AccessCounts
16833
+ }).meta({ id: "FolderSummary" });
16834
+ var ProjectTreeResponse = external_exports.object({
16835
+ project: external_exports.object({
16836
+ id: external_exports.string(),
16837
+ repo: external_exports.string(),
16838
+ visibility: Visibility
16839
+ }),
16840
+ path: external_exports.string(),
16841
+ /** Browse mode: one-level folders at the current path. Omitted in search mode. */
16842
+ folders: external_exports.array(FolderSummary).optional(),
16843
+ files: external_exports.array(FileSummary)
16844
+ }).meta({ id: "ProjectTreeResponse" });
16845
+ var FileDetail = FileSummary.extend({
16846
+ project: external_exports.object({
16847
+ repo: external_exports.string(),
16848
+ visibility: Visibility,
16849
+ language: external_exports.string(),
16850
+ policyDefault: AccessLevel,
16851
+ updatedAt: external_exports.iso.datetime()
16852
+ }),
16853
+ findingsRefs: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() }))
16854
+ }).meta({ id: "FileDetail" });
16855
+ var SetFileAccessBody = external_exports.object({
16856
+ path: external_exports.string(),
16857
+ access: AccessLevel
16858
+ }).meta({ id: "SetFileAccessBody" });
16859
+ var SetFileAccessResponse = external_exports.object({
16860
+ file: FileSummary,
16861
+ accessCounts: AccessCounts
16862
+ }).meta({ id: "SetFileAccessResponse" });
16863
+ var SetMcpTrustBody = external_exports.object({ trust: TrustLevel }).meta({ id: "SetMcpTrustBody" });
16864
+ var HarnessEventItem = external_exports.object({
16865
+ kind: HarnessEventKind,
16866
+ title: external_exports.string(),
16867
+ detail: external_exports.string(),
16868
+ occurredAt: external_exports.iso.datetime(),
16869
+ findingId: external_exports.string().nullable().optional()
16870
+ }).meta({ id: "HarnessEventItem" });
16871
+ var HarnessEventsResponse = external_exports.object({
16872
+ counts: external_exports.object({
16873
+ block: external_exports.number().int().nonnegative(),
16874
+ redact: external_exports.number().int().nonnegative(),
16875
+ warn: external_exports.number().int().nonnegative()
16876
+ }),
16877
+ items: external_exports.array(HarnessEventItem)
16878
+ }).meta({ id: "HarnessEventsResponse" });
16879
+ var RescanResponse = external_exports.object({
16880
+ jobId: external_exports.string(),
16881
+ startedAt: external_exports.iso.datetime()
16882
+ }).meta({ id: "RescanResponse" });
16883
+ var ConnectProjectBody = external_exports.object({ repo: external_exports.string() }).meta({ id: "ConnectProjectBody" });
16884
+ var ListAssetsQuery = external_exports.object({
16885
+ /** Filter by one or more AssetType values; absent means all types. */
16886
+ type: external_exports.array(AssetType).optional(),
16887
+ /** Free-text search term. */
16888
+ q: external_exports.string().optional()
16889
+ });
16890
+ var GetProjectTreeQuery = external_exports.object({
16891
+ /** Subtree root path; defaults to repository root when absent. */
16892
+ path: external_exports.string().optional(),
16893
+ /** Free-text filter applied to file paths. */
16894
+ q: external_exports.string().optional(),
16895
+ /**
16896
+ * Special listing mode. `blocked` returns every effectively-blocked, auto-blocked
16897
+ * file across the whole repo (folders omitted, most-recent first), ignoring
16898
+ * `path`/`q` — powers the project-wide "recently blocked" strip.
16899
+ */
16900
+ filter: external_exports.enum(["blocked"]).optional()
16901
+ });
16902
+ var GetProjectFileQuery = external_exports.object({
16903
+ /** Repository-relative file path; absent or empty → 400. */
16904
+ path: external_exports.string()
16905
+ });
16906
+ var GetHarnessEventsQuery = external_exports.object({
16907
+ /** Maximum number of events to return. Range: 1–50; default: 7. */
16908
+ limit: external_exports.coerce.number().int().min(1).max(50).default(7)
16909
+ });
16910
+
17042
16911
  // ../../packages/schema/src/zod/shares.ts
17043
16912
  var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
17044
16913
  var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
@@ -17245,6 +17114,127 @@ var EgressWriteSummary = external_exports.object({
17245
17114
  droppedFiles: external_exports.array(external_exports.string()).default([])
17246
17115
  }).meta({ id: "EgressWriteSummary" });
17247
17116
 
17117
+ // ../../packages/schema/src/zod/event.ts
17118
+ var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
17119
+ var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
17120
+ var EventMetadata = external_exports.object({
17121
+ sessionId: external_exports.string().optional(),
17122
+ repo: external_exports.string().optional(),
17123
+ filePath: external_exports.string().optional(),
17124
+ // The host tool whose input/output was scanned (e.g. 'Bash', 'WebFetch'),
17125
+ // set by the tool-scanning hooks. The tool NAME only — never the tool's
17126
+ // arguments or output, which can carry the very value a finding masked
17127
+ // (metadata is stored unredacted). Gives findings on non-file captures a
17128
+ // display location ("via Bash") when no filePath exists.
17129
+ toolName: external_exports.string().optional(),
17130
+ // Set (true) by the worktree scanner when the file is excluded by the
17131
+ // repo's .gitignore. Gitignored files ARE still scanned — local scratch and
17132
+ // generated code can leak real secrets — but the provenance is recorded so
17133
+ // policy/dashboards can treat those findings as informational rather than
17134
+ // blocking. Omitted (not false) for tracked files and non-scan events.
17135
+ gitignored: external_exports.boolean().optional(),
17136
+ // Set (true) ONLY when the event's `content` is the COMPLETE file at
17137
+ // capture time (a worktree scan reading from disk). Hook-captured edits
17138
+ // (e.g. an Edit tool's new_string) are partial fragments and MUST NOT set
17139
+ // this. The resolver-on-ingest keys its fixed-at-source dropout
17140
+ // diff on this marker: only a whole-file snapshot can prove a previously
17141
+ // open finding is gone; a fragment's absence proves nothing (the secret
17142
+ // may live outside the hunk). Omitted (not false) for fragments and
17143
+ // non-scan events, so pre-marker clients safely default to the
17144
+ // non-authoritative path.
17145
+ wholeFile: external_exports.boolean().optional(),
17146
+ model: external_exports.string().optional(),
17147
+ turnIndex: external_exports.number().int().nonnegative().optional(),
17148
+ // Distributed-tracing correlation. `correlationId` ties a recorded event back
17149
+ // to the request that captured/ingested it (a UUID, generated independently of
17150
+ // the trace id); `traceId` is the W3C trace id (32 lowercase hex chars) of the
17151
+ // originating span when telemetry is enabled. Both optional + backward
17152
+ // compatible — populated by the plugin (see @akasecurity/plugin-sdk).
17153
+ correlationId: external_exports.uuid().optional(),
17154
+ traceId: external_exports.string().regex(/^[0-9a-f]{32}$/).optional(),
17155
+ // Ids of the detection exceptions that downgraded findings in this capture
17156
+ // to 'allow' — the enforcement audit trail's link back to the grant that
17157
+ // authorized the bypass. Absent on captures where no exception applied.
17158
+ exceptionIds: external_exports.array(external_exports.guid()).optional()
17159
+ }).meta({ id: "EventMetadata" });
17160
+ var Event = external_exports.object({
17161
+ id: external_exports.guid(),
17162
+ sourceTool: SourceTool,
17163
+ kind: EventKind,
17164
+ occurredAt: external_exports.iso.datetime(),
17165
+ contentHash: external_exports.string(),
17166
+ content: external_exports.string(),
17167
+ metadata: EventMetadata.optional()
17168
+ }).meta({ id: "Event" });
17169
+ var IngestEvent = Event.meta({ id: "IngestEvent" });
17170
+ var IngestBatch = external_exports.object({
17171
+ events: external_exports.array(IngestEvent).min(1).max(100),
17172
+ // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
17173
+ // additionally rejects any event whose contentHash the store has already
17174
+ // recorded — for re-runnable bulk ingest (worktree scan, transcript
17175
+ // backfill), where a re-run mints fresh event ids for identical content and
17176
+ // would otherwise accumulate duplicates. Live hook traffic must NOT set it:
17177
+ // two genuinely separate prompts can be byte-identical and both belong on
17178
+ // the timeline.
17179
+ dedupe: external_exports.literal("content-hash").optional()
17180
+ }).meta({ id: "IngestBatch" });
17181
+
17182
+ // ../../packages/schema/src/zod/exception.ts
17183
+ var ExceptionScope = external_exports.enum(["once", "temporary", "permanent"]);
17184
+ var ExceptionConditions = external_exports.object({
17185
+ repo: external_exports.string().optional(),
17186
+ sourceTool: external_exports.string().optional(),
17187
+ provider: external_exports.string().optional()
17188
+ }).strict();
17189
+ var ExceptionCapability = external_exports.enum(["suppress", "reveal_to_model"]);
17190
+ var DetectionException = external_exports.object({
17191
+ id: external_exports.guid(),
17192
+ ruleId: external_exports.string(),
17193
+ // Denormalized from the rule, for reporting — never matched on.
17194
+ category: DetectionCategory,
17195
+ // HMAC-SHA256 hex of the raw match under a machine-local key: a KEYED
17196
+ // fingerprint, never the raw value, and never reversible. Matching recomputes
17197
+ // the fingerprint from a fresh capture; the value itself is never stored.
17198
+ // Shape-constrained so a malformed — or accidentally raw — value is rejected
17199
+ // at the boundary rather than persisted.
17200
+ valueFingerprint: external_exports.string().regex(/^[0-9a-f]{64}$/),
17201
+ // Version of the fingerprint key the grant was written under; a rotated key
17202
+ // invalidates old grants rather than silently mismatching them.
17203
+ keyVersion: external_exports.number().int().positive(),
17204
+ // maskMatch() preview of the approved value — never the raw value.
17205
+ maskedValue: external_exports.string(),
17206
+ capability: ExceptionCapability.default("suppress"),
17207
+ scope: ExceptionScope,
17208
+ expiresAt: external_exports.iso.datetime().nullable(),
17209
+ maxUses: external_exports.number().int().positive().nullable(),
17210
+ useCount: external_exports.number().int().nonnegative(),
17211
+ lastUsedAt: external_exports.iso.datetime().nullable(),
17212
+ // Mandatory: every grant carries the human reason it exists.
17213
+ justification: external_exports.string().min(1),
17214
+ conditions: ExceptionConditions.nullable(),
17215
+ createdBy: external_exports.string(),
17216
+ createdVia: external_exports.enum(["cli-approve", "cli-add", "web-approve", "web-add", "api", "setup-triage"]),
17217
+ createdAt: external_exports.iso.datetime(),
17218
+ updatedAt: external_exports.iso.datetime(),
17219
+ // Revocation is terminal and retained — consumed/expired/revoked rows are
17220
+ // audit evidence; nothing in the exception lifecycle hard-deletes.
17221
+ revokedAt: external_exports.iso.datetime().nullable(),
17222
+ revokedBy: external_exports.string().nullable(),
17223
+ revokeReason: external_exports.string().nullable()
17224
+ });
17225
+ var ExceptionBundleEntry = DetectionException.pick({
17226
+ id: true,
17227
+ ruleId: true,
17228
+ valueFingerprint: true,
17229
+ keyVersion: true,
17230
+ capability: true,
17231
+ expiresAt: true,
17232
+ maxUses: true,
17233
+ useCount: true,
17234
+ conditions: true
17235
+ });
17236
+ var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
17237
+
17248
17238
  // ../../packages/schema/src/zod/exception-action.ts
17249
17239
  var confirmation = external_exports.string().optional();
17250
17240
  var ApproveBlockedInput = external_exports.object({
@@ -17287,10 +17277,11 @@ function toApiAction(dbVal) {
17287
17277
  }
17288
17278
  function toApiCategory(dbVal) {
17289
17279
  if (dbVal === "code_context") return "source_code";
17290
- return dbVal;
17280
+ const parsed = FindingCategory.safeParse(dbVal);
17281
+ return parsed.success ? parsed.data : "custom";
17291
17282
  }
17292
17283
  function toApiProvider(sourceTool) {
17293
- return TOOL_TO_HARNESS[sourceTool] ?? "api";
17284
+ return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
17294
17285
  }
17295
17286
  var STATUS_PRECEDENCE = ["open", "handled", "dismissed", "resolved"];
17296
17287
  function foldGroupStatus(instanceStatuses) {
@@ -17862,6 +17853,7 @@ var ListVaultDerefsResponse = external_exports.object({
17862
17853
  });
17863
17854
  var VaultKeyCustody = external_exports.string();
17864
17855
  var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
17856
+ var VAULT_CONSENT_VERSION = 1;
17865
17857
  var VaultConsent = external_exports.object({
17866
17858
  acknowledgedAt: external_exports.iso.datetime(),
17867
17859
  version: external_exports.number().int().positive()
@@ -17870,7 +17862,13 @@ var VaultConsent = external_exports.object({
17870
17862
  // ../../packages/schema/src/zod/local.ts
17871
17863
  var WORKSPACE_SETTINGS_SPEC_VERSION = 5;
17872
17864
  var MODEL_JUDGE_PAYLOAD_VERSION = 1;
17873
- var RunMode = external_exports.enum(["standalone"]);
17865
+ var RunMode = external_exports.enum(["standalone", "attached"]);
17866
+ var ControlPlaneConnection = external_exports.object({
17867
+ endpoint: external_exports.string().min(1),
17868
+ // Display name for the deployment, shown instead of the raw endpoint.
17869
+ label: external_exports.string().min(1).optional(),
17870
+ attachedAt: external_exports.iso.datetime()
17871
+ }).meta({ id: "ControlPlaneConnection" });
17874
17872
  var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
17875
17873
  var HistoricalAccess = external_exports.enum(["full", "session-only"]);
17876
17874
  var ModelJudgeConsent = external_exports.object({
@@ -17882,12 +17880,10 @@ function isModelJudgeConsentValid(consent) {
17882
17880
  }
17883
17881
  var WorkspaceSettings = external_exports.object({
17884
17882
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
17885
- // Settings files written by earlier releases may carry the retired 'attached'
17886
- // value; it parses as 'standalone' so those files keep loading.
17887
- runMode: external_exports.preprocess(
17888
- (v) => v === "attached" ? "standalone" : v,
17889
- RunMode.default("standalone")
17890
- ),
17883
+ runMode: RunMode.default("standalone"),
17884
+ // Present only while attached; a detach clears it. Its presence is what makes
17885
+ // `runMode: 'attached'` mean anything — see isAttached.
17886
+ controlPlane: ControlPlaneConnection.optional(),
17891
17887
  policy: SimpleDetectionPolicy.default("redact"),
17892
17888
  // Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
17893
17889
  historicalAccess: HistoricalAccess.default("session-only"),
@@ -18023,6 +18019,216 @@ function toCaptureDefinitionInput(finding) {
18023
18019
  };
18024
18020
  }
18025
18021
 
18022
+ // ../../packages/schema/src/zod/managed.ts
18023
+ var MANAGED_SETTINGS_FILENAME = "managed-settings.json";
18024
+ var MANAGED_SETTINGS_SPEC_VERSION = 1;
18025
+ var ManagedSettingKey = external_exports.enum([
18026
+ "runMode",
18027
+ "historicalAccess",
18028
+ "vaultConsent",
18029
+ "vaultKeyCustody",
18030
+ "vaultInlineReveal",
18031
+ "modelJudgeConsent",
18032
+ "dataSharesInPlace"
18033
+ ]).meta({ id: "ManagedSettingKey" });
18034
+ var ManagedSettingsValues = external_exports.object({
18035
+ runMode: external_exports.enum(["standalone", "attached"]).optional(),
18036
+ controlPlane: external_exports.object({
18037
+ endpoint: external_exports.string().min(1),
18038
+ label: external_exports.string().min(1).optional()
18039
+ }).optional(),
18040
+ historicalAccess: external_exports.enum(["full", "session-only"]).optional(),
18041
+ vaultConsent: external_exports.boolean().optional(),
18042
+ vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
18043
+ vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
18044
+ modelJudgeConsent: external_exports.boolean().optional(),
18045
+ dataSharesInPlace: external_exports.boolean().optional()
18046
+ }).meta({ id: "ManagedSettingsValues" });
18047
+ var ManagedSettings = external_exports.object({
18048
+ specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
18049
+ // Shown on every locked control, so the user can tell an administrative
18050
+ // decision from a bug. Absent renders as a generic "your organization".
18051
+ organization: external_exports.string().min(1).optional(),
18052
+ // What the administrator pinned.
18053
+ values: ManagedSettingsValues.default({}),
18054
+ // Which of those the user may not change. A key here with no matching value
18055
+ // freezes whatever the user last chose; a value with no lock is a DEFAULT
18056
+ // the user may still override. The two are separable on purpose.
18057
+ lockedFields: external_exports.array(ManagedSettingKey).default([])
18058
+ }).meta({ id: "ManagedSettings" });
18059
+
18060
+ // ../../packages/schema/src/zod/policy.ts
18061
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
18062
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
18063
+ var Policy = external_exports.object({
18064
+ id: external_exports.guid(),
18065
+ scope: PolicyScope,
18066
+ target: PolicyTarget,
18067
+ action: ActionTaken,
18068
+ enabled: external_exports.boolean().default(true),
18069
+ customKeywords: external_exports.array(external_exports.string()).optional(),
18070
+ // Display name — optional so older policy rows without name still parse.
18071
+ // Added for the findings API (policy.name column migration).
18072
+ name: external_exports.string().optional()
18073
+ }).meta({ id: "Policy" });
18074
+ var PolicyBundle = external_exports.object({
18075
+ version: external_exports.string(),
18076
+ policies: external_exports.array(Policy),
18077
+ // Rules from the installed marketplace packs (snapshotted by the
18078
+ // control plane). The plugin registers these in addition to its bundled
18079
+ // packs. Optional so older backends — and older on-disk caches — that omit
18080
+ // the field still parse; consumers read `bundle.rules ?? []`.
18081
+ rules: external_exports.array(Rule).optional(),
18082
+ // When true, `rules` IS the complete effective ruleset and the runtime must
18083
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
18084
+ // after reading the user's installed snapshot (installed_packs, enabled
18085
+ // packs only), which is how detection updates stay manual: new bundled
18086
+ // rules run only after the user applies the pack update. Absent/false keeps
18087
+ // the historical composition (bundled packs + rules) — older caches.
18088
+ rulesComplete: external_exports.boolean().optional(),
18089
+ // Active detection exceptions, evaluation subset only (see
18090
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
18091
+ // on-disk caches — that omit the field still parse; consumers read
18092
+ // `bundle.exceptions ?? []`.
18093
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
18094
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
18095
+ // A second axis over the same `redact` action, carried beside the policies
18096
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
18097
+ // widening Policy itself would change a persisted shape to express something
18098
+ // only the in-memory bundle needs. Optional so an older producer — or an
18099
+ // older on-disk cache — still parses; consumers read `?? []` and get the
18100
+ // pre-existing one-way behaviour, which is the safe direction to default.
18101
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
18102
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
18103
+ // from a versioned installed pack. Optional so older backends — and older
18104
+ // on-disk caches — that omit the field still parse; consumers fall back to
18105
+ // the rule's own spec version. NOT the bundle version above — see
18106
+ // installedRuleset's ruleVersions for the source of truth.
18107
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
18108
+ customKeywords: external_exports.array(external_exports.string()),
18109
+ fetchedAt: external_exports.iso.datetime()
18110
+ }).meta({ id: "PolicyBundle" });
18111
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
18112
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
18113
+ var CATEGORY_PEAK_SEVERITY = {
18114
+ secret: "critical",
18115
+ financial: "critical",
18116
+ // core-financial/credit-card
18117
+ code_flaw: "critical",
18118
+ pii: "high",
18119
+ phi: "high",
18120
+ custom: "high",
18121
+ // user-defined; conservative
18122
+ code_context: "low",
18123
+ config: "low"
18124
+ // observe-only; floors to monitor regardless
18125
+ };
18126
+ function severityFloorPolicy(category) {
18127
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
18128
+ const peak = CATEGORY_PEAK_SEVERITY[category];
18129
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
18130
+ }
18131
+ function severityFloorPosture() {
18132
+ const out = {};
18133
+ for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
18134
+ return out;
18135
+ }
18136
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
18137
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
18138
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
18139
+ var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
18140
+ var BUILTIN_POLICY_SPECS = {
18141
+ monitor: {
18142
+ name: "Monitor",
18143
+ action: "log",
18144
+ reversible: false,
18145
+ description: "Log every match for audit. The request is allowed through untouched."
18146
+ },
18147
+ warn: {
18148
+ name: "Warn",
18149
+ action: "warn",
18150
+ reversible: false,
18151
+ description: "Allow the request, but warn the user inline before it is sent."
18152
+ },
18153
+ redact: {
18154
+ name: "Redact",
18155
+ action: "redact",
18156
+ reversible: false,
18157
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
18158
+ },
18159
+ vault: {
18160
+ name: "Redact & Vault",
18161
+ action: "redact",
18162
+ reversible: true,
18163
+ 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."
18164
+ },
18165
+ block: {
18166
+ name: "Block",
18167
+ action: "block",
18168
+ reversible: false,
18169
+ description: "Refuse the request entirely whenever any rule in this detection matches."
18170
+ }
18171
+ };
18172
+ function builtinPolicyToAction(id) {
18173
+ return BUILTIN_POLICY_SPECS[id].action;
18174
+ }
18175
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18176
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
18177
+ );
18178
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
18179
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
18180
+ );
18181
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
18182
+ function builtinPolicyIsReversible(id) {
18183
+ return BUILTIN_POLICY_SPECS[id].reversible;
18184
+ }
18185
+ function policyIdIsReversible(policyId) {
18186
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18187
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18188
+ return builtinPolicyIsReversible(id);
18189
+ }
18190
+ var DEFAULT_ACTIONS = Object.fromEntries(
18191
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
18192
+ );
18193
+ var BUILTIN_POLICIES = Object.fromEntries(
18194
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
18195
+ );
18196
+ var DEFAULT_PACK_POLICY_ID = "monitor";
18197
+ function policyIdToAction(policyId) {
18198
+ const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18199
+ const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18200
+ return BUILTIN_POLICIES[id].action;
18201
+ }
18202
+ var UsedByItem = external_exports.object({
18203
+ id: external_exports.string(),
18204
+ name: external_exports.string(),
18205
+ ruleCount: external_exports.number().int().nonnegative(),
18206
+ enabled: external_exports.boolean()
18207
+ }).meta({ id: "UsedByItem" });
18208
+ var PolicyListItem = external_exports.object({
18209
+ id: external_exports.string(),
18210
+ kind: PolicyKind,
18211
+ name: external_exports.string(),
18212
+ enabled: external_exports.boolean(),
18213
+ usedByCount: external_exports.number().int().nonnegative()
18214
+ }).meta({ id: "PolicyListItem" });
18215
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
18216
+ var PolicyDetail = external_exports.object({
18217
+ specVersion: external_exports.literal(1),
18218
+ id: external_exports.string(),
18219
+ kind: PolicyKind,
18220
+ name: external_exports.string(),
18221
+ enabled: external_exports.boolean(),
18222
+ description: external_exports.string(),
18223
+ usedBy: external_exports.array(UsedByItem)
18224
+ }).meta({ id: "PolicyDetail" });
18225
+ var PolicyStatsResponse = external_exports.object({
18226
+ policies: external_exports.number().int().nonnegative(),
18227
+ builtin: external_exports.number().int().nonnegative(),
18228
+ custom: external_exports.number().int().nonnegative(),
18229
+ detectionsGoverned: external_exports.number().int().nonnegative()
18230
+ }).meta({ id: "PolicyStatsResponse" });
18231
+
18026
18232
  // ../../packages/schema/src/zod/project-files.ts
18027
18233
  var ProjectFileInput = external_exports.object({
18028
18234
  path: external_exports.string().min(1),
@@ -18101,44 +18307,6 @@ var BatchedRemediation = external_exports.discriminatedUnion("kind", [
18101
18307
  NoRemediationDecision
18102
18308
  ]);
18103
18309
 
18104
- // ../../packages/schema/src/zod/rule-test.ts
18105
- var TestRulesRequest = external_exports.object({
18106
- rules: external_exports.array(Rule).min(1).max(100),
18107
- text: external_exports.string().max(5e4).optional(),
18108
- fixtures: external_exports.array(RuleFixture).max(200).optional()
18109
- }).refine((v) => v.text !== void 0 || (v.fixtures?.length ?? 0) > 0, {
18110
- message: "Provide `text`, `fixtures`, or both \u2014 there must be something to test"
18111
- }).meta({ id: "TestRulesRequest" });
18112
- var RuleTestMatch = external_exports.object({
18113
- ruleId: external_exports.string(),
18114
- category: DetectionCategory,
18115
- severity: Severity,
18116
- span: Span,
18117
- confidence: external_exports.number().min(0).max(1),
18118
- match: external_exports.string()
18119
- }).meta({ id: "RuleTestMatch" });
18120
- var FixtureResult = external_exports.object({
18121
- label: external_exports.string(),
18122
- shouldMatch: external_exports.boolean(),
18123
- didMatch: external_exports.boolean(),
18124
- passed: external_exports.boolean(),
18125
- matches: external_exports.array(RuleTestMatch)
18126
- }).meta({ id: "FixtureResult" });
18127
- var TestRulesResponse = external_exports.object({
18128
- // Present only when the request supplied `text`.
18129
- adhoc: external_exports.object({ matches: external_exports.array(RuleTestMatch) }).optional(),
18130
- fixtures: external_exports.array(FixtureResult),
18131
- summary: external_exports.object({
18132
- total: external_exports.number().int().nonnegative(),
18133
- passed: external_exports.number().int().nonnegative(),
18134
- failed: external_exports.number().int().nonnegative()
18135
- }),
18136
- // Ids of rules whose matcher type the engine cannot evaluate today (e.g.
18137
- // `validator`), so they silently never match. Surfaced so an author is not
18138
- // misled by a green run that actually skipped a rule.
18139
- unsupportedRuleIds: external_exports.array(external_exports.string())
18140
- }).meta({ id: "TestRulesResponse" });
18141
-
18142
18310
  // ../../packages/schema/src/zod/security.ts
18143
18311
  var SeveritySummaryItem = external_exports.object({
18144
18312
  severity: Severity,
@@ -18236,10 +18404,22 @@ var TopSourcesQuery = external_exports.object({
18236
18404
  // Omit for both kinds.
18237
18405
  kind: external_exports.enum(SOURCE_KINDS).optional()
18238
18406
  });
18239
- var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
18407
+ var Provider = Harness.extract([
18408
+ "ClaudeCode",
18409
+ "Cursor",
18410
+ "Codex",
18411
+ "Antigravity",
18412
+ "ClaudeAi",
18413
+ "ChatGpt",
18414
+ "Copilot",
18415
+ "Api"
18416
+ ]).meta({ id: "Provider" });
18240
18417
  var ScanCoverageProvider = external_exports.object({
18241
18418
  provider: Provider,
18242
- // Percent of that provider's traffic scanned in the window. 0 when unsupported.
18419
+ // Percent of that provider's traffic the shipped capture surface reaches.
18420
+ // A curated business fact, constant across every `range` — not a measured
18421
+ // per-window metric. 0 exactly when `supported` is false. See the comment
18422
+ // above the block for where these numbers are decided.
18243
18423
  coverage: external_exports.number().int().min(0).max(100),
18244
18424
  supported: external_exports.boolean()
18245
18425
  }).meta({ id: "ScanCoverageProvider" });
@@ -18293,6 +18473,18 @@ var ApplyRecommendedActionResponse = external_exports.object({
18293
18473
  var DismissRecommendedActionResponse = external_exports.object({ id: external_exports.string(), status: external_exports.literal("dismissed") }).meta({ id: "DismissRecommendedActionResponse" });
18294
18474
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
18295
18475
 
18476
+ // ../../packages/schema/src/zod/settings-action.ts
18477
+ var SaveSettingsInput = external_exports.object({
18478
+ historicalAccess: external_exports.string(),
18479
+ modelJudgeConsent: external_exports.boolean(),
18480
+ vaultConsent: external_exports.string(),
18481
+ vaultInlineReveal: external_exports.string()
18482
+ });
18483
+ var AttachInput = external_exports.object({
18484
+ endpoint: external_exports.string(),
18485
+ label: external_exports.string().optional()
18486
+ });
18487
+
18296
18488
  // ../../packages/schema/src/zod/triage.ts
18297
18489
  var TriageHit = external_exports.object({
18298
18490
  ruleId: external_exports.string(),
@@ -18307,7 +18499,7 @@ var TriageHit = external_exports.object({
18307
18499
  valueFingerprint: external_exports.string().optional(),
18308
18500
  keyVersion: external_exports.number().int().nonnegative().optional()
18309
18501
  });
18310
- var TriagePolicy = BuiltinPolicyId;
18502
+ var TriagePolicy = CategoryPolicyId;
18311
18503
  var TriageCategoryRec = external_exports.object({
18312
18504
  category: DetectionCategory,
18313
18505
  action: TriagePolicy,
@@ -18447,42 +18639,6 @@ function reviewSeverityRank(reasons) {
18447
18639
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
18448
18640
  }
18449
18641
 
18450
- // ../../packages/persistence/src/ids.ts
18451
- import { createHash } from "crypto";
18452
- function sha256Hex(input) {
18453
- return createHash("sha256").update(input).digest("hex");
18454
- }
18455
- function inventoryId(objectType, identityKey) {
18456
- return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18457
- }
18458
- function sourceProjectId(url2) {
18459
- return sha256Hex(canonicalIdentity(["source_project", url2]));
18460
- }
18461
- function classifiedDataId(cls) {
18462
- return sha256Hex(canonicalIdentity(["classified_data", cls]));
18463
- }
18464
- function inspectionDefinitionId(ruleId, version2) {
18465
- return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18466
- }
18467
- function llmCallId(sessionId, messageId) {
18468
- return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18469
- }
18470
- function toolCallId(sessionId, toolUseId) {
18471
- return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18472
- }
18473
- var NO_SESSION = "no_session";
18474
- var NO_PATH = "no_path";
18475
- function captureId(sessionId, contentHash, filePath = null) {
18476
- return sha256Hex(
18477
- canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18478
- );
18479
- }
18480
-
18481
- // ../../packages/persistence/src/internal/snapshot.ts
18482
- import { randomUUID } from "crypto";
18483
- import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
18484
- import { basename, dirname, join } from "path";
18485
-
18486
18642
  // ../../packages/persistence/src/paths.ts
18487
18643
  import {
18488
18644
  chmodSync,
@@ -18514,8 +18670,11 @@ function chmodBestEffort(path, mode) {
18514
18670
  function tightenDir(dir) {
18515
18671
  chmodBestEffort(dir, DATA_DIR_MODE);
18516
18672
  }
18673
+ function mkdirOwnerOnlySync(dir, recursive = false) {
18674
+ mkdirSync(dir, { recursive, mode: DATA_DIR_MODE });
18675
+ }
18517
18676
  function ensureDataDirSync(dir) {
18518
- mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
18677
+ mkdirOwnerOnlySync(dir, true);
18519
18678
  tightenDir(dir);
18520
18679
  }
18521
18680
  function dbSidecars(file2) {
@@ -18528,11 +18687,69 @@ function tightenPerms(file2) {
18528
18687
  for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
18529
18688
  }
18530
18689
 
18690
+ // ../../packages/persistence/src/database.ts
18691
+ import { randomUUID as randomUUID10 } from "crypto";
18692
+ import { join as join3, sep } from "path";
18693
+ import { DatabaseSync } from "node:sqlite";
18694
+
18695
+ // ../../packages/persistence/src/ids.ts
18696
+ import { createHash } from "crypto";
18697
+ function sha256Hex(input) {
18698
+ return createHash("sha256").update(input).digest("hex");
18699
+ }
18700
+ function inventoryId(objectType, identityKey) {
18701
+ return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18702
+ }
18703
+ function sourceProjectId(url2) {
18704
+ return sha256Hex(canonicalIdentity(["source_project", url2]));
18705
+ }
18706
+ function classifiedDataId(cls) {
18707
+ return sha256Hex(canonicalIdentity(["classified_data", cls]));
18708
+ }
18709
+ function inspectionDefinitionId(ruleId, version2) {
18710
+ return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18711
+ }
18712
+ function llmCallId(sessionId, messageId) {
18713
+ return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18714
+ }
18715
+ function toolCallId(sessionId, toolUseId) {
18716
+ return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18717
+ }
18718
+ var NO_SESSION = "no_session";
18719
+ var NO_PATH = "no_path";
18720
+ function captureId(sessionId, contentHash, filePath = null) {
18721
+ return sha256Hex(
18722
+ canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18723
+ );
18724
+ }
18725
+
18531
18726
  // ../../packages/persistence/src/internal/snapshot.ts
18727
+ import { randomUUID } from "crypto";
18728
+ import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
18729
+ import { basename, dirname, join as join2 } from "path";
18532
18730
  function backupPath(file2, tag) {
18533
18731
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18534
18732
  }
18535
18733
  var STALE_PARTIAL_MS = 5 * 6e4;
18734
+ var SNAPSHOT_STAGING_SUFFIX = ".partial";
18735
+ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18736
+ var SNAPSHOT_STAGING_COPY = "copy";
18737
+ function createSnapshotStaging(backup) {
18738
+ const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18739
+ rmSync3(stage, { recursive: true, force: true });
18740
+ mkdirOwnerOnlySync(stage);
18741
+ tightenDir(stage);
18742
+ return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
18743
+ }
18744
+ function idleMs(entry) {
18745
+ for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
18746
+ try {
18747
+ return Date.now() - statSync2(candidate).mtimeMs;
18748
+ } catch {
18749
+ }
18750
+ }
18751
+ return null;
18752
+ }
18536
18753
  function reapStalePartials(file2) {
18537
18754
  const dir = dirname(file2);
18538
18755
  const prefix = `${basename(file2)}.`;
@@ -18543,30 +18760,34 @@ function reapStalePartials(file2) {
18543
18760
  return;
18544
18761
  }
18545
18762
  for (const name of entries) {
18546
- if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
18547
- const partial2 = join(dir, name);
18763
+ if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18764
+ const staging = join2(dir, name);
18548
18765
  try {
18549
- if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
18550
- rmSync2(partial2, { force: true });
18766
+ const idle = idleMs(staging);
18767
+ if (idle !== null && idle > STALE_PARTIAL_MS) {
18768
+ rmSync3(staging, { recursive: true, force: true });
18551
18769
  }
18552
18770
  } catch {
18553
18771
  }
18554
18772
  }
18555
18773
  }
18556
18774
  function snapshotStore(db, backup) {
18557
- const partial2 = `${backup}.partial`;
18775
+ const { stage, copy } = createSnapshotStaging(backup);
18558
18776
  try {
18559
- rmSync2(partial2, { force: true });
18560
- db.prepare("VACUUM INTO ?").run(partial2);
18561
- tightenFile(partial2);
18562
- renameSync2(partial2, backup);
18777
+ db.prepare("VACUUM INTO ?").run(copy);
18778
+ tightenFile(copy);
18779
+ renameSync2(copy, backup);
18563
18780
  } catch (error51) {
18564
18781
  try {
18565
- rmSync2(partial2, { force: true });
18782
+ rmSync3(stage, { recursive: true, force: true });
18566
18783
  } catch {
18567
18784
  }
18568
18785
  throw error51;
18569
18786
  }
18787
+ try {
18788
+ rmSync3(stage, { recursive: true, force: true });
18789
+ } catch {
18790
+ }
18570
18791
  }
18571
18792
  function moveStoreAside(file2, backup) {
18572
18793
  const undo = [];
@@ -18579,7 +18800,7 @@ function moveStoreAside(file2, backup) {
18579
18800
  renameSync2(sidecar, moved);
18580
18801
  undo.push([moved, sidecar]);
18581
18802
  } catch {
18582
- rmSync2(sidecar, { force: true });
18803
+ rmSync3(sidecar, { force: true });
18583
18804
  }
18584
18805
  }
18585
18806
  } catch (error51) {
@@ -18595,14 +18816,14 @@ function moveStoreAside(file2, backup) {
18595
18816
  }
18596
18817
  function discardStore(file2, backup) {
18597
18818
  try {
18598
- rmSync2(file2, { force: true });
18819
+ rmSync3(file2, { force: true });
18599
18820
  for (const sidecar of dbSidecars(file2)) {
18600
- rmSync2(sidecar, { force: true });
18821
+ rmSync3(sidecar, { force: true });
18601
18822
  }
18602
18823
  } catch (error51) {
18603
18824
  if (existsSync(file2)) {
18604
18825
  try {
18605
- rmSync2(backup, { force: true });
18826
+ rmSync3(backup, { force: true });
18606
18827
  } catch {
18607
18828
  }
18608
18829
  }
@@ -18834,10 +19055,31 @@ function applyMigrations(db, file2) {
18834
19055
  if (drained) applyLegacyDropMigration(db, file2);
18835
19056
  }
18836
19057
  }
19058
+ function readLegacyTables(db) {
19059
+ let holdsRows = false;
19060
+ const marks = [];
19061
+ for (const table2 of ["events", "findings"]) {
19062
+ try {
19063
+ const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
19064
+ if (row === void 0) {
19065
+ holdsRows = true;
19066
+ marks.push(`${table2}:unreadable`);
19067
+ continue;
19068
+ }
19069
+ if (row.n > 0) holdsRows = true;
19070
+ marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
19071
+ } catch {
19072
+ holdsRows = true;
19073
+ marks.push(`${table2}:unreadable`);
19074
+ }
19075
+ }
19076
+ return { holdsRows, mark: marks.join("|") };
19077
+ }
18837
19078
  function applyLegacyDropMigration(db, file2) {
18838
19079
  const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
18839
19080
  if (!migration) return;
18840
- if (file2) {
19081
+ const before = file2 === void 0 ? void 0 : readLegacyTables(db);
19082
+ if (file2 !== void 0 && before?.holdsRows === true) {
18841
19083
  try {
18842
19084
  backupBeforeLegacyDrop(db, file2);
18843
19085
  } catch (error51) {
@@ -18851,6 +19093,12 @@ function applyLegacyDropMigration(db, file2) {
18851
19093
  () => {
18852
19094
  const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
18853
19095
  if (alreadyDropped) return;
19096
+ if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
19097
+ akaWarn(
19098
+ "legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
19099
+ );
19100
+ return;
19101
+ }
18854
19102
  for (const statement of splitStatements(migration.sql)) {
18855
19103
  db.exec(statement);
18856
19104
  }
@@ -19294,9 +19542,10 @@ function safeParseStringArray(raw) {
19294
19542
  const parsed = safeJson(raw, null);
19295
19543
  return Array.isArray(parsed) ? parsed : [];
19296
19544
  }
19545
+ var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19297
19546
  function toHarness(raw) {
19298
19547
  const parsed = Harness.safeParse(raw);
19299
- return parsed.success ? parsed.data : "claudecode";
19548
+ return parsed.success ? parsed.data : DEFAULT_HARNESS;
19300
19549
  }
19301
19550
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19302
19551
  if (row.status) {
@@ -19427,7 +19676,7 @@ var SqliteActivityRepository = class {
19427
19676
  const params = [];
19428
19677
  if (query.harness && query.harness.length > 0) {
19429
19678
  conditions.push(
19430
- `coalesce(json_extract(attributes, '$.harness'), 'claudecode') IN (${placeholders(query.harness.length)})`
19679
+ `coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') IN (${placeholders(query.harness.length)})`
19431
19680
  );
19432
19681
  params.push(...query.harness);
19433
19682
  }
@@ -19634,13 +19883,13 @@ var SqliteActivityRepository = class {
19634
19883
  * The DISTINCT harnesses that actually have sessions (optionally within a
19635
19884
  * `started_at >= fromMs` window), so the filter can offer only the harnesses
19636
19885
  * present rather than the full enum. Each stored value is normalized through
19637
- * the SAME `toHarness` default the list uses (missing → 'claudecode'), so a
19638
- * store of bare (harness-less) roots surfaces exactly `['claudecode']`.
19886
+ * the SAME `toHarness` default the list uses (missing → DEFAULT_HARNESS), so
19887
+ * a store of bare (harness-less) roots surfaces exactly that one harness.
19639
19888
  */
19640
19889
  harnessFacets(fromMs) {
19641
19890
  const where = fromMs === void 0 ? "" : " AND started_at >= ?";
19642
19891
  const stmt = this.db.prepare(
19643
- `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), 'claudecode') AS harness
19892
+ `SELECT DISTINCT coalesce(json_extract(attributes, '$.harness'), '${DEFAULT_HARNESS}') AS harness
19644
19893
  FROM audit_events WHERE ${SESSION_ROOT}${where}`
19645
19894
  );
19646
19895
  const rows = allRows(
@@ -19854,8 +20103,8 @@ var SqliteAuditEventsRepository = class {
19854
20103
  }
19855
20104
  // Insert one transcript-derived `llm_call` leaf. Unlike `insertAuditEvent`
19856
20105
  // (which takes a caller-supplied random id), the id here is MINTED internally
19857
- // from the natural key — `llmCallId(sessionId, messageId)` — tenant-free like the
19858
- // sibling local-store ids (the local store is single-tenant). The deterministic
20106
+ // from the natural key — `llmCallId(sessionId, messageId)` — derived from the
20107
+ // session and message alone, like the sibling local-store ids. The deterministic
19859
20108
  // id + the UPSERT-take-MAX(output_tokens) statement make every re-read idempotent
19860
20109
  // AND converge a streaming partial/final split across two incremental passes:
19861
20110
  // a whole-file re-read no-ops (equal output), a lagging final replaces a
@@ -20820,6 +21069,42 @@ var SqliteFindingsRepository = class {
20820
21069
  this.db = db;
20821
21070
  }
20822
21071
  db;
21072
+ /**
21073
+ * The newest `limit` findings, newest first.
21074
+ *
21075
+ * THE PLAN IS THE POINT HERE, and two things in the SQL below exist only to
21076
+ * pin it. The natural spelling — drive from `inspection_findings`, order by the
21077
+ * JOINED `e.started_at` — cannot push the LIMIT down, because the sort key is
21078
+ * not on the driving table: SQLite sorts every finding in the store through a
21079
+ * temp B-tree to return 500 rows. Measured at 35.0 ms on a 40,000-event corpus
21080
+ * against 0.9 ms for the form below, and the gap is a ratio of the store size
21081
+ * rather than a constant.
21082
+ *
21083
+ * What it takes to make `started_at` order come out of an index instead:
21084
+ *
21085
+ * - **`+e.event_type`** — the unary plus makes that term non-indexable, so the
21086
+ * planner stops choosing `idx_audit_type_t` (`event_type, started_at`). That
21087
+ * index cannot serve the ORDER BY: the predicate spans four event types, so
21088
+ * satisfying a global `started_at` order across them needs a range merge
21089
+ * SQLite will not do, and it sorts instead. Freed of it, the planner scans
21090
+ * `idx_audit_started_at` — a bare `started_at` index — in DESC order and
21091
+ * filters the type per row, which lets the LIMIT stop the scan early.
21092
+ * - **`CROSS JOIN`** — semantically identical to JOIN in SQLite, and there
21093
+ * purely to stop the tables being reordered. With plain JOINs the planner
21094
+ * drives from `f` and sorts everything again: measured at 23.6 ms, i.e. the
21095
+ * unary plus ALONE recovers almost none of the win. Both are needed.
21096
+ *
21097
+ * Neither is a micro-optimisation that a later reader should tidy away, and
21098
+ * `packages/persistence/test/performance/hot-read-query-plans.test.ts` fails if
21099
+ * the temp B-tree comes back.
21100
+ *
21101
+ * Degrading gracefully was the reason for `+` over `INDEXED BY`, which measured
21102
+ * identically (0.9 ms): `INDEXED BY` is a hard requirement, so dropping or
21103
+ * renaming the index turns this read into an ERROR, where `+` turns it into a
21104
+ * scan-and-sort — slower, still correct. The worst case for the chosen form is
21105
+ * a store whose recent captures carry no findings at all, where the scan walks
21106
+ * the whole index; that is still no worse than the full sort it replaced.
21107
+ */
20823
21108
  recentFindings(opts) {
20824
21109
  const limit = opts?.limit ?? 50;
20825
21110
  const rows = allRows(
@@ -20828,10 +21113,10 @@ var SqliteFindingsRepository = class {
20828
21113
  f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
20829
21114
  json_extract(e.attributes, '$.source_tool') AS source_tool,
20830
21115
  e.event_type AS kind
20831
- FROM inspection_findings f
20832
- JOIN audit_events e ON e.id = f.audit_event_id
20833
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
20834
- WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
21116
+ FROM audit_events e
21117
+ CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
21118
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
21119
+ WHERE +e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
20835
21120
  ORDER BY e.started_at DESC, f.rowid DESC
20836
21121
  LIMIT :limit`
20837
21122
  ),
@@ -21475,7 +21760,8 @@ var SqliteInspectionDefinitionsRepository = class {
21475
21760
  }
21476
21761
  db;
21477
21762
  insertStmt;
21478
- // Idempotent upsert; returns the content-addressed definition id.
21763
+ // Insert-if-absent; returns the content-addressed definition id. An id already
21764
+ // present keeps the stored row untouched — see the class doc.
21479
21765
  upsert(input) {
21480
21766
  const id = inspectionDefinitionId(input.ruleId, input.version);
21481
21767
  const row = toInspectionDefinitionRow(input, id);
@@ -21619,11 +21905,23 @@ function isParseableBinaryVersion(version2) {
21619
21905
 
21620
21906
  // ../../packages/persistence/src/repositories/installed-packs.ts
21621
21907
  var DEFAULT_POLICY_ID = DEFAULT_PACK_POLICY_ID;
21908
+ function printableRuleId(entry) {
21909
+ if (typeof entry !== "object" || entry === null) return null;
21910
+ const candidate = entry.id;
21911
+ return Rule.shape.id.safeParse(candidate).success ? candidate : null;
21912
+ }
21913
+ function firstIssueReason(error51) {
21914
+ const issue2 = error51.issues[0];
21915
+ if (!issue2) return "unknown";
21916
+ const path = issue2.path.map((segment) => String(segment)).join(".");
21917
+ return path ? `${path}: ${issue2.code}` : issue2.code;
21918
+ }
21919
+ var REJECTED_RULE_DETAIL_CAP = 10;
21622
21920
  function inventorySignature(packs2) {
21623
21921
  return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
21624
21922
  }
21625
21923
  function hashRules(rulesJson) {
21626
- return createHash2("sha1").update(rulesJson).digest("hex");
21924
+ return createHash2("sha256").update(rulesJson).digest("hex");
21627
21925
  }
21628
21926
  function parseVersion(v) {
21629
21927
  const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
@@ -21835,10 +22133,21 @@ var SqliteInstalledPacksRepository = class {
21835
22133
  * (all detection off) instead of falling back to the bundled packs. Every
21836
22134
  * JSON-level failure therefore counts as invalid.
21837
22135
  */
22136
+ /**
22137
+ * ORDERED, because a rule id is unique only WITHIN a pack — the sole unique
22138
+ * index is (namespace, pack_id) — so two enabled packs may contribute the same
22139
+ * id, and the per-rule maps below are last-write-wins. Without an ORDER BY the
22140
+ * winner is whatever order SQLite happens to return, which makes a collision
22141
+ * resolve differently on two machines holding identical stores. Ordering by
22142
+ * (namespace, pack_id) makes the loser deterministic and therefore testable.
22143
+ */
21838
22144
  installedRuleset() {
21839
22145
  const rows = allRows(
21840
22146
  this.db.prepare(
21841
- `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version FROM installed_packs`
22147
+ `SELECT enabled, policy_id AS policyId, rules_json AS rulesJson, version,
22148
+ namespace, pack_id AS packId
22149
+ FROM installed_packs
22150
+ ORDER BY namespace, pack_id`
21842
22151
  )
21843
22152
  );
21844
22153
  const out = {
@@ -21846,22 +22155,32 @@ var SqliteInstalledPacksRepository = class {
21846
22155
  enabledPacks: 0,
21847
22156
  rules: [],
21848
22157
  invalidRules: 0,
22158
+ rejectedRules: [],
21849
22159
  ruleActions: /* @__PURE__ */ new Map(),
21850
- ruleVersions: /* @__PURE__ */ new Map()
22160
+ ruleVersions: /* @__PURE__ */ new Map(),
22161
+ reversibleRules: /* @__PURE__ */ new Set()
22162
+ };
22163
+ const reject = (pack, ruleId, reason) => {
22164
+ if (out.rejectedRules.length >= REJECTED_RULE_DETAIL_CAP) return;
22165
+ out.rejectedRules.push({ pack, ruleId, reason });
21851
22166
  };
21852
22167
  for (const row of rows) {
21853
22168
  if (!intToBool(row.enabled)) continue;
21854
22169
  out.enabledPacks += 1;
21855
22170
  const action = policyIdToAction(row.policyId);
22171
+ const reversible = policyIdIsReversible(row.policyId);
22172
+ const pack = `${row.namespace}/${row.packId}`;
21856
22173
  let raw;
21857
22174
  try {
21858
22175
  raw = JSON.parse(row.rulesJson);
21859
22176
  } catch {
21860
22177
  out.invalidRules += 1;
22178
+ reject(pack, null, "rules_json: malformed JSON");
21861
22179
  continue;
21862
22180
  }
21863
22181
  if (!Array.isArray(raw)) {
21864
22182
  out.invalidRules += 1;
22183
+ reject(pack, null, "rules_json: not an array");
21865
22184
  continue;
21866
22185
  }
21867
22186
  for (const entry of raw) {
@@ -21870,7 +22189,12 @@ var SqliteInstalledPacksRepository = class {
21870
22189
  out.rules.push(parsed.data);
21871
22190
  out.ruleActions.set(parsed.data.id, action);
21872
22191
  out.ruleVersions.set(parsed.data.id, row.version);
21873
- } else out.invalidRules += 1;
22192
+ if (reversible) out.reversibleRules.add(parsed.data.id);
22193
+ else out.reversibleRules.delete(parsed.data.id);
22194
+ } else {
22195
+ out.invalidRules += 1;
22196
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22197
+ }
21874
22198
  }
21875
22199
  }
21876
22200
  return out;
@@ -22064,31 +22388,38 @@ var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
22064
22388
  var VALID_HARNESS_IDS = new Set(HarnessId.options);
22065
22389
  var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
22066
22390
  var HARNESS_LABELS = {
22067
- claudecode: "Claude Code",
22068
- cursor: "Cursor",
22069
- codex: "Codex",
22070
- antigravity: "Antigravity"
22391
+ [HARNESS.ClaudeCode]: "Claude Code",
22392
+ [HARNESS.Cursor]: "Cursor",
22393
+ [HARNESS.Codex]: "Codex",
22394
+ [HARNESS.Antigravity]: "Antigravity"
22071
22395
  };
22072
22396
  var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
22073
22397
  var EMPTY_PROJECT_AGG = {
22074
22398
  accessCounts: { open: 0, approved: 0, blocked: 0, total: 0 },
22075
22399
  findingsCount: 0
22076
22400
  };
22401
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
22402
+ var TITLE_NEEDLES = {
22403
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
22404
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
22405
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
22406
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
22407
+ };
22077
22408
  function resolveHarnessId(attrs, row) {
22078
22409
  if (attrs.provider && VALID_HARNESS_IDS.has(attrs.provider)) {
22079
22410
  return attrs.provider;
22080
22411
  }
22081
- const t = (row.title ?? "").toLowerCase().replace(/[\s-]/g, "");
22082
- if (t.includes("claudecode") || t === "claude") return "claudecode";
22083
- if (t.includes("cursor")) return "cursor";
22084
- if (t.includes("codex")) return "codex";
22085
- if (t.includes("antigravity")) return "antigravity";
22412
+ const t = stripSeparators(row.title ?? "");
22413
+ if (t.includes(TITLE_NEEDLES.ClaudeCode) || t === "claude") return HARNESS.ClaudeCode;
22414
+ if (t.includes(TITLE_NEEDLES.Cursor)) return HARNESS.Cursor;
22415
+ if (t.includes(TITLE_NEEDLES.Codex)) return HARNESS.Codex;
22416
+ if (t.includes(TITLE_NEEDLES.Antigravity)) return HARNESS.Antigravity;
22086
22417
  return null;
22087
22418
  }
22088
22419
  function isLiveRealClaudeCode(rows) {
22089
22420
  return rows.some((r) => {
22090
22421
  const attrs = safeJson(r.attributes, {});
22091
- return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === "claudecode";
22422
+ return attrs.provenance !== "sample" && resolveHarnessId(attrs, r) === HARNESS.ClaudeCode;
22092
22423
  });
22093
22424
  }
22094
22425
  function toAssetSummary(row) {
@@ -22356,7 +22687,7 @@ var SqliteInventoryAssetsRepository = class {
22356
22687
  const isRealHarness = rows.some(
22357
22688
  (r) => safeJson(r.attributes, {}).provenance !== "sample"
22358
22689
  );
22359
- const attachConfig = isRealHarness && harnessId === "claudecode" && configAssets.length > 0;
22690
+ const attachConfig = isRealHarness && harnessId === HARNESS.ClaudeCode && configAssets.length > 0;
22360
22691
  const assets = attachConfig ? [...harnessAssets, ...configAssets].sort((a, b) => a.name.localeCompare(b.name)) : harnessAssets;
22361
22692
  if (q && assets.length === 0) continue;
22362
22693
  const firstRow = rows[0];
@@ -23030,9 +23361,10 @@ var SqliteProjectFilesRepository = class {
23030
23361
  // ../../packages/persistence/src/repositories/resolutions.ts
23031
23362
  import { randomUUID as randomUUID7 } from "crypto";
23032
23363
  var SqliteResolutionsRepository = class {
23033
- constructor(db, now = () => Date.now()) {
23364
+ constructor(db, now = () => Date.now(), newId = () => randomUUID7()) {
23034
23365
  this.db = db;
23035
23366
  this.now = now;
23367
+ this.newId = newId;
23036
23368
  this.insertStmt = db.prepare(
23037
23369
  `INSERT INTO finding_resolution (id, finding_key, status, method, resolved_at, evidence, created_at)
23038
23370
  VALUES (:id, :findingKey, :status, :method, :resolvedAt, :evidence, :createdAt)`
@@ -23065,12 +23397,14 @@ var SqliteResolutionsRepository = class {
23065
23397
  }
23066
23398
  db;
23067
23399
  now;
23400
+ newId;
23068
23401
  insertStmt;
23069
23402
  latestStmt;
23070
23403
  openAtRestStmt;
23071
23404
  resolvedAtRestStmt;
23072
23405
  /**
23073
- * Insert one disposition row. The repo mints the id and stamps created_at.
23406
+ * Insert one disposition row. The repo mints the id and stamps created_at,
23407
+ * both through the constructor's injectable seams.
23074
23408
  * `status`/`method` are typed AND re-parsed here against @akasecurity/schema's
23075
23409
  * FindingStatus/ResolutionMethod, so the persisted vocabulary can never drift
23076
23410
  * from the schema enums. NOTE for future manual-resolution writers: this is
@@ -23082,7 +23416,7 @@ var SqliteResolutionsRepository = class {
23082
23416
  */
23083
23417
  insertResolution(r) {
23084
23418
  this.insertStmt.run({
23085
- id: randomUUID7(),
23419
+ id: this.newId(),
23086
23420
  findingKey: r.findingKey,
23087
23421
  status: FindingStatus.parse(r.status),
23088
23422
  method: ResolutionMethod.parse(r.method),
@@ -23661,16 +23995,16 @@ var ACTION_TO_KIND = {
23661
23995
  warn: "warned"
23662
23996
  };
23663
23997
  var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
23664
- var SCAN_COVERAGE = [
23665
- { provider: "claudecode", coverage: 100, supported: true },
23666
- { provider: "cursor", coverage: 0, supported: false },
23667
- { provider: "codex", coverage: 80, supported: true },
23668
- { provider: "antigravity", coverage: 60, supported: true },
23669
- { provider: "claudeai", coverage: 0, supported: false },
23670
- { provider: "chatgpt", coverage: 0, supported: false },
23671
- { provider: "copilot", coverage: 0, supported: false },
23672
- { provider: "api", coverage: 0, supported: false }
23673
- ];
23998
+ var SCAN_COVERAGE = {
23999
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
24000
+ [HARNESS.Api]: { coverage: 0, supported: false },
24001
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
24002
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
24003
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
24004
+ [HARNESS.Codex]: { coverage: 80, supported: true },
24005
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
24006
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
24007
+ };
23674
24008
  var GRANULARITY = {
23675
24009
  "7d": "day",
23676
24010
  "30d": "day",
@@ -23769,9 +24103,22 @@ var SqliteSecurityRepository = class {
23769
24103
  return Promise.resolve({ total, needsRemediation, bySeverity });
23770
24104
  }
23771
24105
  // Range is echoed but does not change the result today — coverage is a constant
23772
- // business fact (see SCAN_COVERAGE), not a measured per-window metric.
24106
+ // business fact (see SCAN_COVERAGE), not a measured per-window metric. Order
24107
+ // comes from Provider.options (the enum's declaration order), not from
24108
+ // SCAN_COVERAGE's own key order — deliberately, not because object literals
24109
+ // leave key order unspecified (ES2015 guarantees insertion order for these
24110
+ // non-integer string keys, so iterating SCAN_COVERAGE directly would be
24111
+ // reliable too). The reason is the schema comment's promise: the returned
24112
+ // order must mirror the generated OpenAPI enum list, which is Provider's
24113
+ // contract, not this table's.
23773
24114
  scanCoverage(range) {
23774
- return Promise.resolve({ range, providers: SCAN_COVERAGE.map((p) => ({ ...p })) });
24115
+ return Promise.resolve({
24116
+ range,
24117
+ providers: Provider.options.map((provider) => ({
24118
+ provider,
24119
+ ...SCAN_COVERAGE[provider]
24120
+ }))
24121
+ });
23775
24122
  }
23776
24123
  enforcementActions(range) {
23777
24124
  const lenMs = RANGE_DAYS[range] * DAY_MS4;
@@ -23829,10 +24176,10 @@ var SqliteSecurityRepository = class {
23829
24176
  // count; a superseding open/redetected row means the finding is not
23830
24177
  // remediated and is excluded, same invariant as severitySummary. Legacy
23831
24178
  // at-rest findings with finding_key IS NULL can never have a resolution row
23832
- // (the lifecycle is keyed by finding_key), so the SQL guard excludes them
23833
- // outright. One raw-row query (fetch every trackable finding + its latest
23834
- // resolution's status/method/resolved_at) + pure-JS filter/bucket/mean,
23835
- // mirroring this file's other methods.
24179
+ // (the lifecycle is keyed by finding_key), so they cannot reach the driving
24180
+ // set below. One raw-row query (fetch the findings with resolution activity in
24181
+ // the window + each one's latest resolution status/method/resolved_at) +
24182
+ // pure-JS filter/bucket/mean, mirroring this file's other methods.
23836
24183
  mttrTrend(range) {
23837
24184
  const granularity = granularityFor(range);
23838
24185
  const bucketMs = (granularity === "day" ? 1 : 7) * DAY_MS4;
@@ -23848,29 +24195,80 @@ var SqliteSecurityRepository = class {
23848
24195
  // started_at the upsert overwrites onto inspection_findings.audit_event_id.
23849
24196
  // COALESCE onto the parent event's started_at defends against any
23850
24197
  // legacy/edge row the backfill left null.
23851
- `SELECT COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
24198
+ `SELECT DISTINCT f.finding_key AS finding_key,
24199
+ COALESCE(f.first_detected_at, e.started_at) AS first_detected_at, d.severity AS severity,
23852
24200
  latest.status AS latest_status,
23853
24201
  latest.method AS latest_method,
23854
24202
  latest.resolved_at AS latest_resolved_at
23855
- FROM inspection_findings f
23856
- JOIN audit_events e ON e.id = f.audit_event_id
23857
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24203
+ FROM finding_resolution fr
24204
+ CROSS JOIN inspection_findings f ON f.finding_key = fr.finding_key
24205
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24206
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23858
24207
  LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23859
24208
  ON latest.finding_key = f.finding_key
23860
- WHERE f.finding_key IS NOT NULL
23861
- AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
23862
- AND EXISTS (
23863
- SELECT 1 FROM finding_resolution fr
23864
- WHERE fr.finding_key = f.finding_key
23865
- AND fr.resolved_at >= :windowStart
23866
- )`
23867
- // The EXISTS is a SUPERSET prefilter that bounds the scan to keys with
23868
- // any resolution activity at/after the window start — a row this method
24209
+ WHERE fr.resolved_at >= :windowStart
24210
+ AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`
24211
+ // `fr` is a SUPERSET prefilter, not the answer: a finding this method
23869
24212
  // ultimately counts has its LATEST resolution inside the window, which
23870
- // implies such a row exists, so nothing wanted is dropped. The exact
23871
- // latest-wins + status/method + window gate stays in JS below,
23872
- // dialect-agnostic. Without this, a
23873
- // 7d request evaluated the store's entire trackable-findings history.
24213
+ // implies a resolution row at/after the window start exists, so nothing
24214
+ // wanted is dropped. The exact latest-wins + status/method + window gate
24215
+ // stays in JS below, dialect-agnostic. `f.finding_key IS NOT NULL` is
24216
+ // implied rather than dropped — the join key comes from
24217
+ // finding_resolution, whose finding_key is NOT NULL.
24218
+ //
24219
+ // IT IS THE DRIVING TABLE THAT MAKES THAT PREFILTER A BOUND, which is
24220
+ // the correction this replaced. Spelled as an `EXISTS` in the WHERE it
24221
+ // READ as a bound and was not one: SQLite drove from `audit_events` on
24222
+ // event_type, joined every capture event to its findings, and evaluated
24223
+ // the EXISTS last — bounding the RESULT and not the scan, so a 7d request
24224
+ // still cost the store's whole trackable history. Measured at 44.6 ms on
24225
+ // 50,000 events and 171.3 ms on 150,000 — linear in the STORE, and in
24226
+ // both cases returning rows for a window holding a fraction of it.
24227
+ //
24228
+ // Two things carry it, and they answer DIFFERENT halves — which is worth
24229
+ // stating precisely, because the obvious reading (both are needed for the
24230
+ // speed) is wrong and was measured to be wrong:
24231
+ //
24232
+ // - **`CROSS JOIN`** is the whole of the store-size fix. In SQLite the
24233
+ // keyword is semantically identical to JOIN and exists only to stop the
24234
+ // tables being reordered; with plain JOINs the planner puts `e` back on
24235
+ // the outside, because with no ANALYZE statistics it prices
24236
+ // `event_type IN (...)` as a selective probe. Reverting it alone takes
24237
+ // the 2k->20k flatness ratio from 1.32 to 16.87.
24238
+ // - **`idx_finding_resolution_resolved_at`** (migration 0021) makes
24239
+ // `resolved_at >= :windowStart` a range SEARCH instead of a bare
24240
+ // `SCAN fr` — finding_key was this table's only index before it, so the
24241
+ // range had none. It buys NO flatness in store size: remove it and the
24242
+ // ratio above does not move, because the latest-resolution derived
24243
+ // table already passes over the whole of finding_resolution, so this
24244
+ // read is O(resolutions) either way and resolutions are not the store.
24245
+ // What it buys is the criterion `hot-read-query-plans.test.ts` enforces
24246
+ // — no hot read may pass over a table with no index — and that is the
24247
+ // guard that goes red when it is dropped. Neither test catches the
24248
+ // other's defect.
24249
+ //
24250
+ // SELECT DISTINCT is a CORRECTNESS requirement of driving from `fr`, not a
24251
+ // tidy-up. finding_resolution is append-only, so a key that was fixed,
24252
+ // redetected and fixed again carries several rows inside one window and
24253
+ // matches once per row — and the value below is a MEAN, so a key matched
24254
+ // three times is a key weighted three times.
24255
+ //
24256
+ // The skew is easy to argue away and the argument is wrong, so it is worth
24257
+ // recording. Duplicate rows for ONE key are identical (every projected
24258
+ // column is per-key: `latest.*` is latest-wins, `first_detected_at` is
24259
+ // preserved), so sums and counts scale together and that key's own mean
24260
+ // does not move. What moves is a bucket holding TWO findings that duplicate
24261
+ // UNEQUALLY: three rows for a 5.9-day fix and one for a 1.9-day fix average
24262
+ // 4.9 days weighted against 3.9 unweighted. Measured, and pinned by
24263
+ // `security.test.ts`'s "weights a finding ONCE however many resolution rows
24264
+ // it has inside the window" — which needed a fixture built for it, since no
24265
+ // single-key case can show it.
24266
+ //
24267
+ // `finding_key` is selected to make the DISTINCT dedup by KEY rather than
24268
+ // by value tuple. On the other columns alone, two genuinely different
24269
+ // findings sharing a severity, a first-detection event and a resolution
24270
+ // instant — one commit fixing two secrets in one file — are one tuple, and
24271
+ // collapsing them would under-count in the other direction.
23874
24272
  ),
23875
24273
  { windowStart }
23876
24274
  );
@@ -23928,16 +24326,47 @@ var SqliteSecurityRepository = class {
23928
24326
  }
23929
24327
  // Recently-resolved activity feed: findings whose finding_key's LATEST
23930
24328
  // finding_resolution row is status:'resolved'/method:'fixed-at-source' —
23931
- // same latest-resolution-wins correlated subquery as severitySummary /
23932
- // mttrTrend (NOT a plain JOIN, which would surface every historical
23933
- // resolution row for a key rather than just its current disposition). A key
23934
- // whose latest row is a superseding 'open'/'redetected' row (the same
23935
- // secret came back) is excluded — it is not currently resolved. Legacy
23936
- // at-rest findings with finding_key IS NULL are excluded outright (the
23937
- // resolution lifecycle can never attach to them). Path comes from the
23938
- // finding's parent event (event_type 'code_change', attributes.file_path) —
23939
- // mirrors resolutions.ts's openAtRestStmt accessor. Ordered by resolved_at
23940
- // DESC, capped at `limit`.
24329
+ // same latest-resolution-wins derived table as severitySummary / mttrTrend
24330
+ // (NOT a plain JOIN, which would surface every historical resolution row for
24331
+ // a key rather than just its current disposition). A key whose latest row is
24332
+ // a superseding 'open'/'redetected' row (the same secret came back) is
24333
+ // excluded — it is not currently resolved. Legacy at-rest findings with
24334
+ // finding_key IS NULL are excluded outright (the resolution lifecycle can
24335
+ // never attach to them). Path comes from the finding's parent event
24336
+ // (event_type 'code_change', attributes.file_path) — mirrors resolutions.ts's
24337
+ // openAtRestStmt accessor. Ordered by resolved_at DESC, capped at `limit`.
24338
+ //
24339
+ // THE RESOLUTION SET DRIVES THIS QUERY, and that is a correctness property of
24340
+ // the plan rather than a preference. Written the other way round — driving
24341
+ // from inspection_findings/audit_events with `latest` LEFT JOINed on — SQLite
24342
+ // cannot use the join key: `f` is reached FROM `latest` by finding_key, so
24343
+ // `latest` gets probed on (rn, status, method) instead and the plan enumerates
24344
+ // every (code_change event x resolved key) pair before `f` can reject it. That
24345
+ // is a cross product, and it is quadratic in the store: measured at 10,966 ms
24346
+ // on a corpus of 50,000 events carrying 2,051 resolutions, against 20 rows
24347
+ // returned. It was invisible for as long as it was, and reported at 8 ms,
24348
+ // because an empty finding_resolution table makes the inner side empty and the
24349
+ // cross product collapses to nothing — so the shape is only observable on a
24350
+ // corpus that seeds resolutions.
24351
+ //
24352
+ // Driving from `latest` instead makes every step below it a unique-index or
24353
+ // primary-key lookup (uq_inspection_findings_key, then audit_events' own PK),
24354
+ // so the cost is the derived table's own — linear in resolutions, which is
24355
+ // what this feed is legitimately about.
24356
+ //
24357
+ // CROSS JOIN is what actually pins that, and it is load-bearing rather than
24358
+ // decorative: in SQLite the keyword is semantically identical to JOIN and
24359
+ // exists only to stop the optimizer reordering the tables. Written as plain
24360
+ // JOINs in this order the planner puts `e` back on the outside — it has no
24361
+ // ANALYZE statistics to price the alternatives with, so it takes
24362
+ // `event_type = 'code_change'` for a selective index probe and rebuilds the
24363
+ // cross product. The FROM order alone was measured to change the plan not at
24364
+ // all.
24365
+ //
24366
+ // The LEFT JOIN it replaced was already an inner join in effect: three
24367
+ // `latest.*` predicates sit in the WHERE, and each of them is false for a
24368
+ // null-extended row. Spelling it JOIN changes no row and stops the plan
24369
+ // reading as though the findings side could drive.
23941
24370
  recentlyResolved(limit = 20) {
23942
24371
  const rows = allRows(
23943
24372
  this.db.prepare(
@@ -23947,17 +24376,15 @@ var SqliteSecurityRepository = class {
23947
24376
  json_extract(e.attributes, '$.file_path') AS path,
23948
24377
  COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
23949
24378
  latest.resolved_at AS latest_resolved_at
23950
- FROM inspection_findings f
23951
- JOIN audit_events e ON e.id = f.audit_event_id
23952
- JOIN inspection_definitions d ON d.id = f.inspection_definition_id
23953
- LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
23954
- ON latest.finding_key = f.finding_key
23955
- WHERE e.event_type = 'code_change'
23956
- AND f.finding_key IS NOT NULL
23957
- AND latest.status = 'resolved'
24379
+ FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
24380
+ CROSS JOIN inspection_findings f ON f.finding_key = latest.finding_key
24381
+ CROSS JOIN audit_events e ON e.id = f.audit_event_id
24382
+ CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
24383
+ WHERE latest.status = 'resolved'
23958
24384
  AND latest.method = 'fixed-at-source'
23959
24385
  AND latest.resolved_at IS NOT NULL
23960
- ORDER BY latest_resolved_at DESC
24386
+ AND e.event_type = 'code_change'
24387
+ ORDER BY latest.resolved_at DESC
23961
24388
  LIMIT :limit`
23962
24389
  ),
23963
24390
  { limit }
@@ -24893,7 +25320,8 @@ function openAndInitialize(file2) {
24893
25320
  }
24894
25321
  function openLocalDatabase(dir) {
24895
25322
  ensureDataDirSync(dir);
24896
- const file2 = join2(dir, DB_FILENAME);
25323
+ const file2 = join3(dir, DB_FILENAME);
25324
+ reapStalePartials(file2);
24897
25325
  const {
24898
25326
  db,
24899
25327
  events,
@@ -25128,9 +25556,9 @@ import {
25128
25556
  closeSync,
25129
25557
  existsSync as existsSync2,
25130
25558
  openSync,
25131
- readFileSync,
25132
- rmSync as rmSync3,
25133
- statSync as statSync2,
25559
+ readFileSync as readFileSync2,
25560
+ rmSync as rmSync4,
25561
+ statSync as statSync3,
25134
25562
  writeFileSync as writeFileSync2
25135
25563
  } from "fs";
25136
25564
  import { hostname as hostname3 } from "os";
@@ -25141,26 +25569,26 @@ import { createHash as createHash3 } from "crypto";
25141
25569
 
25142
25570
  // ../../packages/persistence/src/fingerprint.ts
25143
25571
  import { createHmac, randomBytes } from "crypto";
25144
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
25145
- import { join as join3 } from "path";
25572
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
25573
+ import { join as join4 } from "path";
25146
25574
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
25147
25575
 
25148
25576
  // ../../packages/persistence/src/local-layout.ts
25149
25577
  import { renameSync as renameSync3 } from "fs";
25150
25578
  import { mkdir } from "fs/promises";
25151
25579
  import { homedir } from "os";
25152
- import { join as join4 } from "path";
25580
+ import { join as join5 } from "path";
25153
25581
  function defaultDataDir() {
25154
- return join4(homedir(), ".aka");
25582
+ return join5(homedir(), ".aka");
25155
25583
  }
25156
25584
  function settingsDir(base = defaultDataDir()) {
25157
- return join4(base, "settings");
25585
+ return join5(base, "settings");
25158
25586
  }
25159
25587
  function dataDir(base = defaultDataDir()) {
25160
- return join4(base, "data");
25588
+ return join5(base, "data");
25161
25589
  }
25162
25590
  function dbPath(base = defaultDataDir()) {
25163
- return join4(dataDir(base), "aka.db");
25591
+ return join5(dataDir(base), "aka.db");
25164
25592
  }
25165
25593
  function ensureLayoutDirSync(dir = defaultDataDir()) {
25166
25594
  ensureDataDirSync(dir);
@@ -25173,20 +25601,88 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25173
25601
  for (const { name, dest } of moves) {
25174
25602
  try {
25175
25603
  ensureDataDirSync(dest);
25176
- const moved = join4(dest, name);
25177
- renameSync3(join4(base, name), moved);
25604
+ const moved = join5(dest, name);
25605
+ renameSync3(join5(base, name), moved);
25178
25606
  tightenFile(moved);
25179
25607
  } catch {
25180
25608
  }
25181
25609
  }
25182
25610
  }
25183
25611
 
25612
+ // ../../packages/persistence/src/managed-settings.ts
25613
+ import { readFileSync as readFileSync4 } from "fs";
25614
+ import { posix, win32 } from "path";
25615
+ function managedSettingsPaths(platform2 = process.platform) {
25616
+ if (platform2 === "darwin") {
25617
+ return [
25618
+ posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
25619
+ posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
25620
+ ];
25621
+ }
25622
+ if (platform2 === "win32") {
25623
+ return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
25624
+ }
25625
+ return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
25626
+ }
25627
+ function readManagedSettings(paths = managedSettingsPaths()) {
25628
+ for (const path of paths) {
25629
+ let text;
25630
+ try {
25631
+ text = readFileSync4(path, "utf8");
25632
+ } catch {
25633
+ continue;
25634
+ }
25635
+ const record2 = parseJsonObject(text);
25636
+ if (!record2) continue;
25637
+ const parsed = ManagedSettings.safeParse(record2);
25638
+ if (parsed.success) return parsed.data;
25639
+ }
25640
+ return null;
25641
+ }
25642
+ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
25643
+ if (!managed) return settings;
25644
+ const { values } = managed;
25645
+ const merged = { ...settings };
25646
+ if (values.runMode !== void 0) merged.runMode = values.runMode;
25647
+ if (values.controlPlane !== void 0) {
25648
+ merged.controlPlane = {
25649
+ ...values.controlPlane,
25650
+ // The administrator pinned WHICH deployment, not WHEN this machine
25651
+ // joined it. Keep the user's own attach time when the endpoint is
25652
+ // unchanged, so a managed machine does not appear to re-attach on every
25653
+ // read; stamp a fresh one when the administrator moved it.
25654
+ attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
25655
+ };
25656
+ }
25657
+ if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
25658
+ if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
25659
+ if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
25660
+ if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
25661
+ if (values.vaultConsent !== void 0) {
25662
+ merged.vaultConsent = values.vaultConsent ? (
25663
+ // Keep an existing valid grant so its acknowledgedAt survives; mint one
25664
+ // at the current version otherwise.
25665
+ settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
25666
+ ) : void 0;
25667
+ }
25668
+ if (values.modelJudgeConsent !== void 0) {
25669
+ merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
25670
+ acknowledgedAt: now().toISOString(),
25671
+ payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
25672
+ } : void 0;
25673
+ }
25674
+ return merged;
25675
+ }
25676
+
25184
25677
  // ../../packages/persistence/src/settings.ts
25185
- import { readFileSync as readFileSync3 } from "fs";
25186
- import { join as join5 } from "path";
25678
+ import { readFileSync as readFileSync5 } from "fs";
25679
+ import { join as join6 } from "path";
25187
25680
  var SETTINGS_FILENAME = "settings.json";
25188
25681
  function readWorkspaceSettings(base = defaultDataDir()) {
25189
- const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25682
+ return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25683
+ }
25684
+ function readUserSettings(base) {
25685
+ const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
25190
25686
  if (!record2) return defaultWorkspaceSettings();
25191
25687
  try {
25192
25688
  return WorkspaceSettings.parse(record2);
@@ -25197,13 +25693,17 @@ function readWorkspaceSettings(base = defaultDataDir()) {
25197
25693
  function readJson(file2) {
25198
25694
  let text;
25199
25695
  try {
25200
- text = readFileSync3(file2, "utf8");
25696
+ text = readFileSync5(file2, "utf8");
25201
25697
  } catch {
25202
25698
  return null;
25203
25699
  }
25204
25700
  return parseJsonObject(text) ?? null;
25205
25701
  }
25206
25702
 
25703
+ // ../../packages/persistence/src/store-symlinks.ts
25704
+ import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
25705
+ import { dirname as dirname2, join as join7, resolve } from "path";
25706
+
25207
25707
  // ../../packages/persistence/src/vault/crypto.ts
25208
25708
  import {
25209
25709
  createCipheriv,
@@ -25216,27 +25716,19 @@ import {
25216
25716
  // ../../packages/persistence/src/vault/key-provider.ts
25217
25717
  import { execFileSync } from "child_process";
25218
25718
  import { randomBytes as randomBytes2 } from "crypto";
25219
- import {
25220
- chmodSync as chmodSync2,
25221
- mkdirSync as mkdirSync2,
25222
- readFileSync as readFileSync4,
25223
- renameSync as renameSync4,
25224
- rmSync as rmSync4,
25225
- statSync as statSync3,
25226
- writeFileSync as writeFileSync3
25227
- } from "fs";
25228
- import { join as join6 } from "path";
25719
+ import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
25720
+ import { join as join8 } from "path";
25229
25721
 
25230
25722
  // ../../packages/persistence/src/vault/vault.ts
25231
25723
  import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
25232
25724
 
25233
25725
  // ../../packages/persistence/src/warn-era-cap.ts
25234
- import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
25235
- import { join as join7 } from "path";
25726
+ import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
25727
+ import { join as join9 } from "path";
25236
25728
 
25237
25729
  // ../../packages/plugin-sdk/src/config.ts
25238
- import { existsSync as existsSync5 } from "fs";
25239
- import { join as join8 } from "path";
25730
+ import { existsSync as existsSync6 } from "fs";
25731
+ import { join as join10 } from "path";
25240
25732
 
25241
25733
  // ../../packages/plugin-sdk/src/provider-env.ts
25242
25734
  var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
@@ -25290,8 +25782,8 @@ function resolveProvider() {
25290
25782
  function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
25291
25783
  try {
25292
25784
  ensureLayoutDirSync(base);
25293
- const settingsFile = join8(settingsDir(base), "settings.json");
25294
- if (existsSync5(settingsFile)) tightenFile(settingsFile);
25785
+ const settingsFile = join10(settingsDir(base), "settings.json");
25786
+ if (existsSync6(settingsFile)) tightenFile(settingsFile);
25295
25787
  } catch {
25296
25788
  }
25297
25789
  migrateLegacyLayout(base);
@@ -25314,9 +25806,9 @@ function resolveProviderSafe(resolveProviderFn) {
25314
25806
  }
25315
25807
 
25316
25808
  // ../../packages/plugin-sdk/src/config-inventory.ts
25317
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
25809
+ import { readdirSync as readdirSync2, readFileSync as readFileSync8, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
25318
25810
  import { homedir as homedir2 } from "os";
25319
- import { basename as basename3, join as join10 } from "path";
25811
+ import { basename as basename3, join as join12 } from "path";
25320
25812
 
25321
25813
  // ../../packages/detections/src/egress/registry.ts
25322
25814
  var EXTRACTOR_VERSION = "1";
@@ -26022,6 +26514,10 @@ function luhnCheck(digits) {
26022
26514
  // ../../packages/detections/src/engine.ts
26023
26515
  var keywordMatcher = new KeywordMatcher2();
26024
26516
  var regexMatcher = new RegexMatcher2();
26517
+ var MATCHERS = {
26518
+ keyword: (text, rule) => keywordMatcher.match(text, rule),
26519
+ regex: (text, rule) => regexMatcher.match(text, rule)
26520
+ };
26025
26521
  var packs = /* @__PURE__ */ new Map();
26026
26522
  var POST_VALIDATORS = {
26027
26523
  entropy: (value, config2) => isHighEntropy(value, numberOption(config2, "threshold"), numberOption(config2, "minLength")),
@@ -26037,8 +26533,7 @@ function passesPostValidators(rule, value) {
26037
26533
  for (const ref of validators) {
26038
26534
  const name = typeof ref === "string" ? ref : ref.name;
26039
26535
  const config2 = typeof ref === "string" ? void 0 : ref.config;
26040
- const validate = POST_VALIDATORS[name];
26041
- if (validate && !validate(value, config2)) return false;
26536
+ if (!POST_VALIDATORS[name](value, config2)) return false;
26042
26537
  }
26043
26538
  return true;
26044
26539
  }
@@ -26099,14 +26594,7 @@ function scan(text, rules, context) {
26099
26594
  const candidates = [];
26100
26595
  for (const rule of ruleset) {
26101
26596
  if (!ruleApplies(rule, extension)) continue;
26102
- let spans;
26103
- if (rule.matcher.type === "keyword") {
26104
- spans = keywordMatcher.match(text, rule);
26105
- } else if (rule.matcher.type === "regex") {
26106
- spans = regexMatcher.match(text, rule);
26107
- } else {
26108
- continue;
26109
- }
26597
+ const spans = MATCHERS[rule.matcher.type](text, rule);
26110
26598
  for (const span of spans) {
26111
26599
  const rawMatch = text.slice(span.start, span.end);
26112
26600
  if (!passesPostValidators(rule, rawMatch)) continue;
@@ -26262,6 +26750,7 @@ var CONFIG_POSTURE_RULES = [
26262
26750
  ];
26263
26751
 
26264
26752
  // ../../packages/detections/src/security/redos-probe.ts
26753
+ var BUDGET_MS = 100;
26265
26754
  var EXPONENTIAL_UNITS = [
26266
26755
  "a",
26267
26756
  "0",
@@ -26285,6 +26774,8 @@ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
26285
26774
  var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
26286
26775
  (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
26287
26776
  );
26777
+ var CPU_CORROBORATION_SHARE = 0.2;
26778
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
26288
26779
 
26289
26780
  // ../../rules/code-flaws/auth-jwt-no-verify.json
26290
26781
  var auth_jwt_no_verify_default = {
@@ -27007,10 +27498,10 @@ var localhost_ref_default = {
27007
27498
  severity: "low",
27008
27499
  matcher: {
27009
27500
  type: "regex",
27010
- pattern: "\\b(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|::1)\\b",
27501
+ pattern: "(?<![A-Za-z0-9_])(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|(?<![A-Za-z0-9_]\\[)::1(?!:|\\.[0-9]))(?![A-Za-z0-9_])",
27011
27502
  flags: "g"
27012
27503
  },
27013
- examples: ["localhost", "127.0.0.1"]
27504
+ examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
27014
27505
  };
27015
27506
 
27016
27507
  // ../../rules/core-code-context/stack-trace.json
@@ -28352,27 +28843,32 @@ function maskText(text) {
28352
28843
  }
28353
28844
 
28354
28845
  // ../../packages/plugin-sdk/src/repo.ts
28355
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
28356
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
28846
+ import { existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
28847
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join11, sep as sep2 } from "path";
28357
28848
 
28358
28849
  // ../../packages/plugin-sdk/src/events.ts
28359
28850
  import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
28360
28851
 
28361
28852
  // ../../packages/plugin-sdk/src/isolated-scan.ts
28362
- import { existsSync as existsSync7 } from "fs";
28853
+ import { existsSync as existsSync8 } from "fs";
28363
28854
  import { fileURLToPath } from "url";
28364
28855
  import { Worker } from "worker_threads";
28365
28856
 
28857
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
28858
+ var import_ignore = __toESM(require_ignore(), 1);
28859
+ import { readFileSync as readFileSync9 } from "fs";
28860
+ import { join as join13 } from "path";
28861
+
28366
28862
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
28367
28863
  import { arch, hostname as hostname4, platform, release } from "os";
28368
28864
 
28369
28865
  // ../../packages/plugin-sdk/src/nudge.ts
28370
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
28371
- import { join as join11 } from "path";
28866
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
28867
+ import { join as join14 } from "path";
28372
28868
 
28373
28869
  // ../../packages/plugin-sdk/src/paths.ts
28374
- import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
28375
- import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
28870
+ import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
28871
+ import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
28376
28872
 
28377
28873
  // ../../packages/plugin-sdk/src/posture.ts
28378
28874
  function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
@@ -28385,9 +28881,8 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
28385
28881
  }
28386
28882
 
28387
28883
  // ../../packages/plugin-sdk/src/project-files.ts
28388
- var import_ignore = __toESM(require_ignore(), 1);
28389
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
28390
- import { basename as basename5, join as join12 } from "path";
28884
+ import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
28885
+ import { basename as basename5, join as join15 } from "path";
28391
28886
 
28392
28887
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28393
28888
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -28504,12 +28999,12 @@ async function applySetupTriageSuppressions(entries, writer, opts) {
28504
28999
  }
28505
29000
 
28506
29001
  // ../../packages/plugin-sdk/src/throttle.ts
28507
- import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28508
- import { join as join13 } from "path";
29002
+ import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
29003
+ import { join as join16 } from "path";
28509
29004
 
28510
29005
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
28511
29006
  import { writeFileSync as writeFileSync7 } from "fs";
28512
- import { join as join14 } from "path";
29007
+ import { join as join17 } from "path";
28513
29008
 
28514
29009
  // ../../packages/setup-wizard/src/triage/dedupe.ts
28515
29010
  function dedupeKey(hit) {
@@ -28564,12 +29059,12 @@ function deriveFalsePositivePatterns(hits, rec, plan) {
28564
29059
  }
28565
29060
 
28566
29061
  // ../../packages/setup-wizard/src/triage/gate-display.ts
28567
- function findContext(entry, join18) {
28568
- const byFingerprint = join18.find(
29062
+ function findContext(entry, join21) {
29063
+ const byFingerprint = join21.find(
28569
29064
  (j) => j.valueFingerprint !== void 0 && j.valueFingerprint === entry.valueFingerprint
28570
29065
  );
28571
29066
  if (byFingerprint) return byFingerprint.maskedContext;
28572
- const byRuleAndMask = join18.find(
29067
+ const byRuleAndMask = join21.find(
28573
29068
  (j) => j.ruleId === entry.ruleId && j.maskedMatch === entry.maskedValue
28574
29069
  );
28575
29070
  return byRuleAndMask?.maskedContext;
@@ -28640,13 +29135,13 @@ function renderShowcase(showcase) {
28640
29135
 
28641
29136
  ${blocks.join("\n\n")}`;
28642
29137
  }
28643
- function renderSuppressionGate(entries, join18) {
29138
+ function renderSuppressionGate(entries, join21) {
28644
29139
  if (entries.length === 0) {
28645
29140
  return "No false-positive suppressions to confirm \u2014 nothing will be written.";
28646
29141
  }
28647
29142
  const header = entries.length === 1 ? "This looks like a false positive \u2014 take a look before I suppress it:" : `These ${String(entries.length)} look like false positives \u2014 take a look before I suppress them:`;
28648
29143
  const blocks = entries.map((entry, i) => {
28649
- const context = findContext(entry, join18);
29144
+ const context = findContext(entry, join21);
28650
29145
  const lines = [
28651
29146
  `${String(i + 1)}. ${entry.ruleId} [${entry.category}]`,
28652
29147
  ` value: ${entry.maskedValue}`,
@@ -28661,7 +29156,9 @@ ${blocks.join("\n\n")}`;
28661
29156
  }
28662
29157
 
28663
29158
  // ../../packages/setup-wizard/src/triage/merge.ts
28664
- var RANK = { monitor: 0, warn: 1, redact: 2, block: 3 };
29159
+ var RANK = Object.fromEntries(
29160
+ KNOWN_BUILTIN_IDS.map((id, i) => [id, i])
29161
+ );
28665
29162
  function chunkForJudge(hits, maxBytes = 262144) {
28666
29163
  const chunks = [];
28667
29164
  let current = [];
@@ -28730,9 +29227,9 @@ function mergeRecommendations(verdicts) {
28730
29227
  }
28731
29228
 
28732
29229
  // ../../packages/setup-wizard/src/triage/plan-file.ts
28733
- import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
29230
+ import { mkdtempSync, readFileSync as readFileSync11, rmdirSync, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
28734
29231
  import { tmpdir } from "os";
28735
- import { basename as basename6, dirname as dirname4, join as join15 } from "path";
29232
+ import { basename as basename6, dirname as dirname5, join as join18 } from "path";
28736
29233
  var SuppressionEntrySchema = external_exports.object({
28737
29234
  ruleId: external_exports.string(),
28738
29235
  category: DetectionCategory,
@@ -28787,19 +29284,19 @@ function serializePlan(plan, current) {
28787
29284
  function writePlanFile(plan, current, rawValues, deps = {}) {
28788
29285
  const serialized = serializePlan(plan, current);
28789
29286
  assertRawFree(serialized, rawValues);
28790
- const dir = (deps.mkTempDir ?? (() => mkdtempSync(join15(tmpdir(), "aka-plan-"))))();
28791
- const path = join15(dir, "setup-plan.json");
29287
+ const dir = (deps.mkTempDir ?? (() => mkdtempSync(join18(tmpdir(), "aka-plan-"))))();
29288
+ const path = join18(dir, "setup-plan.json");
28792
29289
  writeFileSync8(path, serialized, { encoding: "utf8", mode: 384 });
28793
29290
  return path;
28794
29291
  }
28795
29292
  function readPlanFile(path) {
28796
- const text = readFileSync9(path, "utf8");
29293
+ const text = readFileSync11(path, "utf8");
28797
29294
  const json2 = JSON.parse(text);
28798
29295
  return PersistedPlanSchema.parse(json2);
28799
29296
  }
28800
29297
  function deletePlanFile(path) {
28801
- rmSync5(path, { force: true });
28802
- const dir = dirname4(path);
29298
+ rmSync6(path, { force: true });
29299
+ const dir = dirname5(path);
28803
29300
  if (!basename6(dir).startsWith("aka-plan-")) return;
28804
29301
  try {
28805
29302
  rmdirSync(dir);
@@ -28867,8 +29364,8 @@ function buildJoinEntries(hits) {
28867
29364
  }
28868
29365
 
28869
29366
  // ../../packages/setup-wizard/src/triage/resolve.ts
28870
- function resolveSuppressions(rec, join18) {
28871
- const byId = new Map(join18.map((e) => [e.id, e]));
29367
+ function resolveSuppressions(rec, join21) {
29368
+ const byId = new Map(join21.map((e) => [e.id, e]));
28872
29369
  const entries = [];
28873
29370
  const skipped = [];
28874
29371
  for (const cat of rec.perCategory) {
@@ -28970,7 +29467,7 @@ function parseTriageStream(text) {
28970
29467
  return { hits, status: "complete" };
28971
29468
  }
28972
29469
  function planTriageWriteback(hits, rec) {
28973
- const join18 = buildJoinEntries(hits);
29470
+ const join21 = buildJoinEntries(hits);
28974
29471
  const rawValues = hits.map((h) => h.rawMatch);
28975
29472
  const skipped = [];
28976
29473
  const posture = {};
@@ -29010,7 +29507,7 @@ function planTriageWriteback(hits, rec) {
29010
29507
  }
29011
29508
  const { entries, skipped: resolveSkips } = resolveSuppressions(
29012
29509
  { perCategory: safeCategories, notes: rec.notes },
29013
- join18
29510
+ join21
29014
29511
  );
29015
29512
  skipped.push(...resolveSkips);
29016
29513
  let notes = rec.notes;
@@ -29020,7 +29517,7 @@ function planTriageWriteback(hits, rec) {
29020
29517
  if (err instanceof RawEgressError) notes = SCRUBBED_NOTES;
29021
29518
  else throw err;
29022
29519
  }
29023
- return { entries, posture, showcase, join: join18, notes, skipped };
29520
+ return { entries, posture, showcase, join: join21, notes, skipped };
29024
29521
  }
29025
29522
  function recommendedPosture(evidence) {
29026
29523
  return { ...severityFloorPosture(), ...evidence };
@@ -29289,12 +29786,121 @@ function parseRecommendation(text) {
29289
29786
 
29290
29787
  // src/triage/judge.ts
29291
29788
  import { execFileSync as execFileSync2 } from "child_process";
29292
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync10, rmSync as rmSync6 } from "fs";
29789
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync12, rmSync as rmSync7 } from "fs";
29293
29790
  import { tmpdir as tmpdir2 } from "os";
29294
- import { dirname as dirname5, join as join16 } from "path";
29791
+ import { dirname as dirname6, join as join19 } from "path";
29295
29792
  import { fileURLToPath as fileURLToPath2 } from "url";
29296
- var TRIAGE_DIR = dirname5(fileURLToPath2(import.meta.url));
29297
- var DEFAULT_RUBRIC_PATH = join16(
29793
+
29794
+ // ../../packages/plugin-sdk/src/bare-command.ts
29795
+ import { spawnSync } from "child_process";
29796
+ import { homedir as homedir3 } from "os";
29797
+ import { win32 as win322 } from "path";
29798
+ var CMD_LINE_MAX = 8191;
29799
+ var CMD_WRAPPER_ALLOWANCE = 64;
29800
+ var DIRECT_EXTENSIONS = /* @__PURE__ */ new Set([".exe", ".com"]);
29801
+ var CMD_HAZARDS = [
29802
+ ['"', "a double quote, which would close the quoted argument"],
29803
+ ["%", "a percent sign, which cmd.exe expands inside quotes and re-parses"],
29804
+ ["!", "an exclamation mark, which cmd.exe expands when delayed expansion is on"],
29805
+ ["\r", "a carriage return, which a Windows command line cannot carry"],
29806
+ ["\n", "a line break, which a Windows command line cannot carry"],
29807
+ ["\0", "a NUL byte"]
29808
+ ];
29809
+ var BACKSLASH = 92;
29810
+ var BARE_COMMAND_ERROR_CODE = "ERR_AKA_WINDOWS_ARGV";
29811
+ var BareCommandUnsupportedError = class extends Error {
29812
+ code = BARE_COMMAND_ERROR_CODE;
29813
+ reason;
29814
+ constructor(reason) {
29815
+ super(`cannot spawn this command on Windows: ${reason}`);
29816
+ this.name = "BareCommandUnsupportedError";
29817
+ this.reason = reason;
29818
+ }
29819
+ };
29820
+ function isBareCommandUnsupported(err) {
29821
+ return typeof err === "object" && err !== null && err.code === BARE_COMMAND_ERROR_CODE && typeof err.reason === "string";
29822
+ }
29823
+ function cmdLineHazard(command, args) {
29824
+ for (const [index, arg] of [command, ...args].entries()) {
29825
+ for (const [char, why] of CMD_HAZARDS) {
29826
+ if (arg.includes(char)) {
29827
+ const where = index === 0 ? "the command name" : `argument ${String(index)}`;
29828
+ return `${where} contains ${why}`;
29829
+ }
29830
+ }
29831
+ }
29832
+ const length = quoteCommandLine(command, args).length + CMD_WRAPPER_ALLOWANCE;
29833
+ if (length > CMD_LINE_MAX) {
29834
+ return `the command line is ${String(length)} characters once cmd.exe's own prefix is counted, over its ${String(CMD_LINE_MAX)}`;
29835
+ }
29836
+ return void 0;
29837
+ }
29838
+ function quoteForCmd(value) {
29839
+ let runStart = value.length;
29840
+ while (runStart > 0 && value.charCodeAt(runStart - 1) === BACKSLASH) runStart -= 1;
29841
+ const trailingBackslashes = value.slice(runStart);
29842
+ const body = value.slice(0, runStart);
29843
+ return `"${body}${trailingBackslashes}${trailingBackslashes}"`;
29844
+ }
29845
+ function quoteCommandLine(command, args) {
29846
+ return [command, ...args].map(quoteForCmd).join(" ");
29847
+ }
29848
+ var RESOLVE_TIMEOUT_MS = 5e3;
29849
+ function systemWhere(env) {
29850
+ for (const [key, value] of Object.entries(env ?? process.env)) {
29851
+ if (key.toLowerCase() !== "systemroot") continue;
29852
+ if (typeof value !== "string" || value.trim() === "") return void 0;
29853
+ return win322.join(value, "System32", "where.exe");
29854
+ }
29855
+ return void 0;
29856
+ }
29857
+ function resolveWindowsCommand(command, env, home) {
29858
+ try {
29859
+ const probe = spawnSync(systemWhere(env) ?? "where", [command], {
29860
+ encoding: "utf8",
29861
+ cwd: home,
29862
+ windowsHide: true,
29863
+ timeout: RESOLVE_TIMEOUT_MS,
29864
+ ...env === void 0 ? {} : { env }
29865
+ });
29866
+ if (probe.status !== 0 || typeof probe.stdout !== "string") return void 0;
29867
+ const first = probe.stdout.split(/\r?\n/).find((line) => line.trim() !== "");
29868
+ return first?.trim();
29869
+ } catch {
29870
+ return void 0;
29871
+ }
29872
+ }
29873
+ function isDirectlyExecutable(file2) {
29874
+ return DIRECT_EXTENSIONS.has(win322.extname(file2).toLowerCase());
29875
+ }
29876
+ function planBareCommand(command, args, deps = {}) {
29877
+ const platform2 = deps.platform ?? process.platform;
29878
+ if (platform2 !== "win32") {
29879
+ return { file: command, args, options: {}, viaShell: false, resolved: void 0 };
29880
+ }
29881
+ const home = deps.home ?? homedir3();
29882
+ const resolve2 = deps.resolve ?? resolveWindowsCommand;
29883
+ const resolved = resolve2(command, deps.env, home);
29884
+ if (resolved !== void 0 && isDirectlyExecutable(resolved)) {
29885
+ return { file: resolved, args, options: { cwd: home }, viaShell: false, resolved };
29886
+ }
29887
+ const hazard = cmdLineHazard(command, args);
29888
+ if (hazard !== void 0) {
29889
+ const why = resolved === void 0 ? `${command} did not resolve to an executable, so it could only be run through cmd.exe` : `${command} resolves only to a batch shim, which must be run through cmd.exe`;
29890
+ throw new BareCommandUnsupportedError(`${why}, and ${hazard}`);
29891
+ }
29892
+ return {
29893
+ file: quoteCommandLine(command, args),
29894
+ args: [],
29895
+ options: { shell: true, cwd: home },
29896
+ viaShell: true,
29897
+ resolved
29898
+ };
29899
+ }
29900
+
29901
+ // src/triage/judge.ts
29902
+ var TRIAGE_DIR = dirname6(fileURLToPath2(import.meta.url));
29903
+ var DEFAULT_RUBRIC_PATH = join19(
29298
29904
  TRIAGE_DIR,
29299
29905
  "..",
29300
29906
  "..",
@@ -29331,20 +29937,23 @@ function judgeEnv(platform2 = process.platform) {
29331
29937
  CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
29332
29938
  };
29333
29939
  if (platform2 === "darwin") {
29334
- env.CLAUDE_CONFIG_DIR = mkdtempSync2(join16(tmpdir2(), "aka-judge-cfg-"));
29940
+ env.CLAUDE_CONFIG_DIR = mkdtempSync2(join19(tmpdir2(), "aka-judge-cfg-"));
29335
29941
  }
29336
29942
  return env;
29337
29943
  }
29338
- function spawnClaude(argv, env, stdin) {
29339
- return execFileSync2("claude", [...argv], {
29944
+ function spawnClaude(argv, env, stdin, platform2 = process.platform) {
29945
+ const plan = planBareCommand("claude", argv, { env, platform: platform2 });
29946
+ return execFileSync2(plan.file, [...plan.args], {
29340
29947
  env,
29341
29948
  input: stdin,
29342
29949
  encoding: "utf8",
29343
29950
  timeout: 18e4,
29344
- maxBuffer: 32 * 1024 * 1024
29951
+ maxBuffer: 32 * 1024 * 1024,
29952
+ ...plan.options
29345
29953
  });
29346
29954
  }
29347
29955
  function spawnFailureMeta(err) {
29956
+ if (isBareCommandUnsupported(err)) return err.reason;
29348
29957
  const e = err;
29349
29958
  const parts = [];
29350
29959
  if (typeof e.status === "number") parts.push(`exit ${String(e.status)}`);
@@ -29363,7 +29972,7 @@ function runJudge(hits, deps) {
29363
29972
  if (typeof deps.spawn !== "function") {
29364
29973
  throw new TypeError("runJudge requires deps.spawn \u2014 there is no live-spawn fallback");
29365
29974
  }
29366
- const rubric = deps.loadRubric?.() ?? readFileSync10(DEFAULT_RUBRIC_PATH, "utf8");
29975
+ const rubric = deps.loadRubric?.() ?? readFileSync12(DEFAULT_RUBRIC_PATH, "utf8");
29367
29976
  const hitsJsonl = hits.map((h) => JSON.stringify(toJudgePayload(h))).join("\n");
29368
29977
  const fullPrompt = `${rubric}
29369
29978
 
@@ -29387,7 +29996,7 @@ ${hitsJsonl}
29387
29996
  } finally {
29388
29997
  if (platform2 === "darwin" && env.CLAUDE_CONFIG_DIR) {
29389
29998
  try {
29390
- rmSync6(env.CLAUDE_CONFIG_DIR, { recursive: true, force: true });
29999
+ rmSync7(env.CLAUDE_CONFIG_DIR, { recursive: true, force: true });
29391
30000
  } catch {
29392
30001
  }
29393
30002
  }
@@ -29534,9 +30143,9 @@ function renderPostureGrid(posture) {
29534
30143
  );
29535
30144
  const rows = packs2.map((category) => [
29536
30145
  category,
29537
- ...BUILTIN_ORDER.map((level) => posture[category] === level ? GRID_MARK : "")
30146
+ ...CATEGORY_EXPRESSIBLE_IDS.map((level) => posture[category] === level ? GRID_MARK : "")
29538
30147
  ]);
29539
- return indent(table(["Category", ...BUILTIN_ORDER], rows));
30148
+ return indent(table(["Category", ...CATEGORY_EXPRESSIBLE_IDS], rows));
29540
30149
  }
29541
30150
  var READY_COMMANDS = ["/aka:health", "/aka:findings", "/aka:recommend"];
29542
30151
  function renderCategoriesTuned(categoriesTuned) {
@@ -29632,11 +30241,11 @@ function resolveCreatedBy() {
29632
30241
  }
29633
30242
  }
29634
30243
  function loadRubric() {
29635
- const here = dirname6(fileURLToPath4(import.meta.url));
29636
- const shipped = join17(here, "triage-rubric.md");
29637
- if (existsSync9(shipped)) return readFileSync11(shipped, "utf8");
29638
- return readFileSync11(
29639
- join17(here, "..", "..", "..", "packages", "setup-wizard", "assets", "triage-rubric.md"),
30244
+ const here = dirname7(fileURLToPath4(import.meta.url));
30245
+ const shipped = join20(here, "triage-rubric.md");
30246
+ if (existsSync10(shipped)) return readFileSync13(shipped, "utf8");
30247
+ return readFileSync13(
30248
+ join20(here, "..", "..", "..", "packages", "setup-wizard", "assets", "triage-rubric.md"),
29640
30249
  "utf8"
29641
30250
  );
29642
30251
  }
@@ -29646,7 +30255,7 @@ async function main() {
29646
30255
  argv,
29647
30256
  // fd 0 = stdin; the wizard pipes `backfill.js --triage` into this on preview.
29648
30257
  // Called only on the preview path — the confirm path never reads a stream.
29649
- readStream: (streamPath) => streamPath !== void 0 ? readFileSync11(streamPath, "utf8") : readFileSync11(0, "utf8"),
30258
+ readStream: (streamPath) => streamPath !== void 0 ? readFileSync13(streamPath, "utf8") : readFileSync13(0, "utf8"),
29650
30259
  runJudge: (hits) => runJudge(hits, { spawn: spawnClaude, loadRubric }),
29651
30260
  // The distinct model-judge egress consent, read from settings.json. When it
29652
30261
  // is absent or stale the preview skips the judge instead of sending findings