@akasecurity/cli 0.9.9 → 0.9.10

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.
Files changed (110) hide show
  1. package/dist/cli.js +2923 -1482
  2. package/extension/manifest.json +1 -1
  3. package/native-host/host.js +2092 -1286
  4. package/native-host/scan-worker.js +278 -176
  5. package/native-host/sync.js +1182 -918
  6. package/package.json +4 -4
  7. package/web-ui/package.json +1 -1
  8. package/web-ui/packages/dashboard-ui/package.json +1 -0
  9. package/web-ui/web-ui/.next/BUILD_ID +1 -1
  10. package/web-ui/web-ui/.next/app-build-manifest.json +56 -56
  11. package/web-ui/web-ui/.next/app-path-routes-manifest.json +6 -6
  12. package/web-ui/web-ui/.next/build-manifest.json +2 -2
  13. package/web-ui/web-ui/.next/prerender-manifest.json +10 -10
  14. package/web-ui/web-ui/.next/server/app/(app)/activity/page.js +2 -2
  15. package/web-ui/web-ui/.next/server/app/(app)/activity/page.js.nft.json +1 -1
  16. package/web-ui/web-ui/.next/server/app/(app)/activity/page_client-reference-manifest.js +1 -1
  17. package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js +2 -2
  18. package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js.nft.json +1 -1
  19. package/web-ui/web-ui/.next/server/app/(app)/data-shares/page_client-reference-manifest.js +1 -1
  20. package/web-ui/web-ui/.next/server/app/(app)/detections/page.js +2 -2
  21. package/web-ui/web-ui/.next/server/app/(app)/detections/page.js.nft.json +1 -1
  22. package/web-ui/web-ui/.next/server/app/(app)/detections/page_client-reference-manifest.js +1 -1
  23. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js +2 -2
  24. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js.nft.json +1 -1
  25. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page_client-reference-manifest.js +1 -1
  26. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js +1 -1
  27. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js.nft.json +1 -1
  28. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page_client-reference-manifest.js +1 -1
  29. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js +1 -1
  30. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js.nft.json +1 -1
  31. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page_client-reference-manifest.js +1 -1
  32. package/web-ui/web-ui/.next/server/app/(app)/findings/page.js +2 -2
  33. package/web-ui/web-ui/.next/server/app/(app)/findings/page.js.nft.json +1 -1
  34. package/web-ui/web-ui/.next/server/app/(app)/findings/page_client-reference-manifest.js +1 -1
  35. package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js +2 -2
  36. package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js.nft.json +1 -1
  37. package/web-ui/web-ui/.next/server/app/(app)/inventory/page_client-reference-manifest.js +1 -1
  38. package/web-ui/web-ui/.next/server/app/(app)/policies/page.js +2 -2
  39. package/web-ui/web-ui/.next/server/app/(app)/policies/page.js.nft.json +1 -1
  40. package/web-ui/web-ui/.next/server/app/(app)/policies/page_client-reference-manifest.js +1 -1
  41. package/web-ui/web-ui/.next/server/app/(app)/scan/page.js +3 -3
  42. package/web-ui/web-ui/.next/server/app/(app)/scan/page.js.nft.json +1 -1
  43. package/web-ui/web-ui/.next/server/app/(app)/scan/page_client-reference-manifest.js +1 -1
  44. package/web-ui/web-ui/.next/server/app/(app)/security/page.js +2 -2
  45. package/web-ui/web-ui/.next/server/app/(app)/security/page.js.nft.json +1 -1
  46. package/web-ui/web-ui/.next/server/app/(app)/security/page_client-reference-manifest.js +1 -1
  47. package/web-ui/web-ui/.next/server/app/(app)/settings/page.js +1 -1
  48. package/web-ui/web-ui/.next/server/app/(app)/settings/page.js.nft.json +1 -1
  49. package/web-ui/web-ui/.next/server/app/(app)/settings/page_client-reference-manifest.js +1 -1
  50. package/web-ui/web-ui/.next/server/app/(app)/updates/page.js +2 -2
  51. package/web-ui/web-ui/.next/server/app/(app)/updates/page.js.nft.json +1 -1
  52. package/web-ui/web-ui/.next/server/app/(app)/updates/page_client-reference-manifest.js +1 -1
  53. package/web-ui/web-ui/.next/server/app/(app)/vault/page.js +2 -2
  54. package/web-ui/web-ui/.next/server/app/(app)/vault/page.js.nft.json +1 -1
  55. package/web-ui/web-ui/.next/server/app/(app)/vault/page_client-reference-manifest.js +1 -1
  56. package/web-ui/web-ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  57. package/web-ui/web-ui/.next/server/app/_not-found.html +1 -1
  58. package/web-ui/web-ui/.next/server/app/_not-found.rsc +1 -1
  59. package/web-ui/web-ui/.next/server/app/index.html +1 -1
  60. package/web-ui/web-ui/.next/server/app/index.rsc +1 -1
  61. package/web-ui/web-ui/.next/server/app/page_client-reference-manifest.js +1 -1
  62. package/web-ui/web-ui/.next/server/app-paths-manifest.json +6 -6
  63. package/web-ui/web-ui/.next/server/chunks/389.js +903 -0
  64. package/web-ui/web-ui/.next/server/chunks/475.js +1 -1
  65. package/web-ui/web-ui/.next/server/chunks/667.js +894 -0
  66. package/web-ui/web-ui/.next/server/chunks/851.js +1 -0
  67. package/web-ui/web-ui/.next/server/chunks/{318.js → 950.js} +3 -3
  68. package/web-ui/web-ui/.next/server/functions-config-manifest.json +2 -2
  69. package/web-ui/web-ui/.next/server/middleware-manifest.json +5 -5
  70. package/web-ui/web-ui/.next/server/pages/404.html +1 -1
  71. package/web-ui/web-ui/.next/server/pages/500.html +1 -1
  72. package/web-ui/web-ui/.next/server/server-reference-manifest.js +1 -1
  73. package/web-ui/web-ui/.next/server/server-reference-manifest.json +1 -1
  74. package/web-ui/web-ui/.next/static/chunks/907-207847c57011ba44.js +1 -0
  75. package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/page-e4f94a75c631ce86.js +1 -0
  76. package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/page-d70fcc5612f380b7.js +1 -0
  77. package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-e1823ce15bb6277d.js +1 -0
  78. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-0fcffe4b9dff201f.js +1 -0
  79. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-c95079a2acd284fd.js +1 -0
  80. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-4b9f034bd078f5d3.js +1 -0
  81. package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/page-b97fc083884ef341.js +1 -0
  82. package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-e96e7e947ed5f279.js +1 -0
  83. package/web-ui/web-ui/.next/static/chunks/app/(app)/policies/page-b90748d139a546c9.js +1 -0
  84. package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-7a46abe149271870.js +1 -0
  85. package/web-ui/web-ui/.next/static/chunks/app/(app)/security/page-f4a1ee202d6e60a4.js +1 -0
  86. package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-2ec6d863f1a31792.js +1 -0
  87. package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-b388003fb0d3efd1.js +1 -0
  88. package/web-ui/web-ui/.next/static/chunks/app/(app)/vault/page-6bda74d234a1376a.js +1 -0
  89. package/web-ui/web-ui/dist/scan-worker.js +278 -176
  90. package/web-ui/web-ui/package.json +1 -0
  91. package/web-ui/web-ui/.next/server/chunks/237.js +0 -884
  92. package/web-ui/web-ui/.next/server/chunks/793.js +0 -893
  93. package/web-ui/web-ui/.next/server/chunks/83.js +0 -1
  94. package/web-ui/web-ui/.next/static/chunks/907-b6ee7720ac5bf516.js +0 -1
  95. package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/page-0f1263f7b62606c9.js +0 -1
  96. package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/page-39fd8cad4b6b709f.js +0 -1
  97. package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-b8386e543b783552.js +0 -1
  98. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-62c9e95207311f3d.js +0 -1
  99. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-9792760d5acc2de1.js +0 -1
  100. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-807c39bb900256bd.js +0 -1
  101. package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/page-e3a531d9590a623b.js +0 -1
  102. package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-11d47cdf3619126f.js +0 -1
  103. package/web-ui/web-ui/.next/static/chunks/app/(app)/policies/page-15e6d747c4e794c7.js +0 -1
  104. package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-b390a81c67dd6d32.js +0 -1
  105. package/web-ui/web-ui/.next/static/chunks/app/(app)/security/page-07913f82a7e29f67.js +0 -1
  106. package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-0898d0d22c14bb45.js +0 -1
  107. package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-ad9483e25680d8b4.js +0 -1
  108. package/web-ui/web-ui/.next/static/chunks/app/(app)/vault/page-04450aa587f0d5ef.js +0 -1
  109. /package/web-ui/web-ui/.next/static/{MbmJUh_qusg2UF-ZJLjuR → k7tk0MLkPITbi_x6JL01X}/_buildManifest.js +0 -0
  110. /package/web-ui/web-ui/.next/static/{MbmJUh_qusg2UF-ZJLjuR → k7tk0MLkPITbi_x6JL01X}/_ssgManifest.js +0 -0
@@ -21799,6 +21799,26 @@ var AttachTokenResponse = external_exports.union([
21799
21799
  AttachTokenExpired,
21800
21800
  external_exports.object({ status: printable(64) })
21801
21801
  ]);
21802
+ var DeviceCommandKind = external_exports.enum(["shares_rescan"]);
21803
+ var DeviceCommand = external_exports.object({
21804
+ id: printable(128).min(1),
21805
+ kind: DeviceCommandKind,
21806
+ issuedAt: printable(64).min(1),
21807
+ expiresAt: printable(64).min(1)
21808
+ }).strict();
21809
+ var DeviceCommandPollResponse = external_exports.object({ command: DeviceCommand.nullable() });
21810
+ var DeviceCommandFailureReason = external_exports.enum(["scan_failed", "no_projects", "expired"]);
21811
+ var DeviceCommandAckBody = external_exports.discriminatedUnion("outcome", [
21812
+ external_exports.object({
21813
+ outcome: external_exports.literal("reported"),
21814
+ projectsScanned: external_exports.number().int().nonnegative()
21815
+ }).strict(),
21816
+ external_exports.object({
21817
+ outcome: external_exports.literal("failed"),
21818
+ reason: DeviceCommandFailureReason,
21819
+ projectsScanned: external_exports.number().int().nonnegative()
21820
+ }).strict()
21821
+ ]);
21802
21822
 
21803
21823
  // ../../packages/schema/src/zod/registry.ts
21804
21824
  var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
@@ -21965,7 +21985,7 @@ var PackManifest = external_exports.object({
21965
21985
  }).meta({ id: "PackManifest" });
21966
21986
 
21967
21987
  // ../../packages/schema/src/zod/detection.ts
21968
- var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
21988
+ var OriginEnum = external_exports.enum(["library", "custom"]).meta({ id: "OriginEnum" });
21969
21989
  var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
21970
21990
  var LibraryStateEnum = external_exports.enum(["new", "imported", "update"]).meta({ id: "LibraryStateEnum" });
21971
21991
  var DetectionCounts = external_exports.object({
@@ -22160,8 +22180,9 @@ var Event = external_exports.object({
22160
22180
  metadata: EventMetadata.optional()
22161
22181
  }).meta({ id: "Event" });
22162
22182
  var IngestEvent = Event.meta({ id: "IngestEvent" });
22183
+ var INGEST_BATCH_MAX = 100;
22163
22184
  var IngestBatch = external_exports.object({
22164
- events: external_exports.array(IngestEvent).min(1).max(100),
22185
+ events: external_exports.array(IngestEvent).min(1).max(INGEST_BATCH_MAX),
22165
22186
  // Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
22166
22187
  // additionally rejects any event whose contentHash the store has already
22167
22188
  // recorded — for re-runnable bulk ingest (worktree scan, transcript
@@ -22287,6 +22308,225 @@ var PatchInstalledPackRequest = external_exports.object({
22287
22308
  message: "At least one field must be provided"
22288
22309
  }).meta({ id: "PatchInstalledPackRequest" });
22289
22310
 
22311
+ // ../../packages/schema/src/zod/policy.ts
22312
+ var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
22313
+ var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
22314
+ var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
22315
+ var PolicyProvenance = external_exports.enum(["builtin", "authored"]).meta({ id: "PolicyProvenance" });
22316
+ var Policy = external_exports.object({
22317
+ id: external_exports.guid(),
22318
+ scope: PolicyScope,
22319
+ target: PolicyTarget,
22320
+ action: ActionTaken,
22321
+ enabled: external_exports.boolean().default(true),
22322
+ customKeywords: external_exports.array(external_exports.string()).optional(),
22323
+ // Display name — optional so older policy rows without name still parse.
22324
+ // Added for the findings API (policy.name column migration).
22325
+ name: external_exports.string().optional(),
22326
+ // Whether an AUTHORED policy governs this row's target — not a claim about
22327
+ // which row this is. A producer that collapses several rows onto one target
22328
+ // must carry the marker onto whichever row survives, or the collapse decides
22329
+ // the answer; a survivor may therefore be a built-in expansion still marked
22330
+ // 'authored' because an authored sibling targeted the same thing.
22331
+ // Optional so an older producer — and an older on-disk cache — still parses;
22332
+ // absent reads as 'builtin', which is the behaviour that predates the field.
22333
+ //
22334
+ // Deliberately NOT `kind`/PolicyKind: that name and that enum answer which
22335
+ // built-in archetype catalog entry a policy is, which every catalog surface
22336
+ // reads and which a caller may state. This one is a statement the PRODUCER
22337
+ // of a bundle makes about a row, and only the bundle builder ever stamps it
22338
+ // — the CRUD routes neither accept nor set it.
22339
+ //
22340
+ // A device consumes this in exactly one direction: an 'authored' policy
22341
+ // arriving from a control plane marks the rules it targets as not
22342
+ // locally re-assignable. That can only ever ADD a refusal, never relax one,
22343
+ // which is what makes it safe to honour from an unsigned cache — the same
22344
+ // test `prohibitedModels` passes and `reversibleRuleIds` fails.
22345
+ provenance: PolicyProvenance.optional()
22346
+ }).meta({ id: "Policy" });
22347
+ var PolicyBundle = external_exports.object({
22348
+ version: external_exports.string(),
22349
+ policies: external_exports.array(Policy),
22350
+ // Rules from the installed marketplace packs (snapshotted by the
22351
+ // control plane). The plugin registers these in addition to its bundled
22352
+ // packs. Optional so older backends — and older on-disk caches — that omit
22353
+ // the field still parse; consumers read `bundle.rules ?? []`.
22354
+ rules: external_exports.array(Rule).optional(),
22355
+ // When true, `rules` IS the complete effective ruleset and the runtime must
22356
+ // NOT merge its compiled-in bundled packs — the standalone gateway sets this
22357
+ // after reading the user's installed snapshot (installed_packs, enabled
22358
+ // packs only), which is how detection updates stay manual: new bundled
22359
+ // rules run only after the user applies the pack update. Absent/false keeps
22360
+ // the historical composition (bundled packs + rules) — older caches.
22361
+ rulesComplete: external_exports.boolean().optional(),
22362
+ // Active detection exceptions, evaluation subset only (see
22363
+ // ExceptionBundleEntry). Optional so older bundle producers — and older
22364
+ // on-disk caches — that omit the field still parse; consumers read
22365
+ // `bundle.exceptions ?? []`.
22366
+ exceptions: external_exports.array(ExceptionBundleEntry).optional(),
22367
+ // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
22368
+ // A second axis over the same `redact` action, carried beside the policies
22369
+ // rather than on them: nothing writes ruleId-targeted policies to disk, so
22370
+ // widening Policy itself would change a persisted shape to express something
22371
+ // only the in-memory bundle needs. Optional so an older producer — or an
22372
+ // older on-disk cache — still parses; consumers read `?? []` and get the
22373
+ // pre-existing one-way behaviour, which is the safe direction to default.
22374
+ reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
22375
+ // Installed pack version, keyed by ruleId, for rules in `rules` that came
22376
+ // from a versioned installed pack. Optional so older backends — and older
22377
+ // on-disk caches — that omit the field still parse; consumers fall back to
22378
+ // the rule's own spec version. NOT the bundle version above — see
22379
+ // installedRuleset's ruleVersions for the source of truth.
22380
+ ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
22381
+ // Model ids (the raw `model` string a harness reports, e.g.
22382
+ // `claude-opus-4-1`) the tenant has PROHIBITED. The plugin refuses to switch
22383
+ // a session onto one (PreModelSwitch) and refuses a turn that would run on
22384
+ // one (UserPromptSubmit). Optional so an older backend — and an older
22385
+ // on-disk cache — still parses; consumers read `?? []`, which is the
22386
+ // unenforced behaviour that predates this field and the safe direction to
22387
+ // default.
22388
+ //
22389
+ // Ids, not display names: the governance decision is keyed on the exact
22390
+ // string the harness reports (`model_status_override.versionId` in the
22391
+ // control plane), so no name resolution stands between the decision and the
22392
+ // comparison.
22393
+ prohibitedModels: external_exports.array(external_exports.string()).optional(),
22394
+ customKeywords: external_exports.array(external_exports.string()),
22395
+ fetchedAt: external_exports.iso.datetime()
22396
+ }).meta({ id: "PolicyBundle" });
22397
+ var POLICY_BUNDLE_SHAPE_ID = [
22398
+ ...Object.keys(PolicyBundle.shape),
22399
+ ...Object.keys(Policy.shape).map((key) => `policies.${key}`),
22400
+ ...PolicyTarget.options.flatMap((member) => "shape" in member ? Object.keys(member.shape) : []).map((key) => `policies.target.${key}`),
22401
+ ...Object.keys(ExceptionBundleEntry.shape).map((key) => `exceptions.${key}`)
22402
+ ].sort().join(",");
22403
+ var OBSERVE_ONLY_CATEGORIES = ["config"];
22404
+ var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
22405
+ var CATEGORY_PEAK_SEVERITY = {
22406
+ secret: "critical",
22407
+ financial: "critical",
22408
+ // core-financial/credit-card
22409
+ code_flaw: "critical",
22410
+ pii: "high",
22411
+ phi: "high",
22412
+ custom: "high",
22413
+ // user-defined; conservative
22414
+ code_context: "low",
22415
+ config: "low"
22416
+ // observe-only; floors to monitor regardless
22417
+ };
22418
+ function severityFloorPolicy(category) {
22419
+ if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
22420
+ const peak = CATEGORY_PEAK_SEVERITY[category];
22421
+ return peak === "critical" || peak === "high" ? "warn" : "monitor";
22422
+ }
22423
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
22424
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
22425
+ var RedactFallback = BuiltinPolicyId.extract(["monitor", "warn", "block"]).meta({
22426
+ id: "RedactFallback"
22427
+ });
22428
+ var BUILTIN_POLICY_SPECS = {
22429
+ monitor: {
22430
+ name: "Monitor",
22431
+ action: "log",
22432
+ reversible: false,
22433
+ description: "Log every match for audit. The request is allowed through untouched."
22434
+ },
22435
+ warn: {
22436
+ name: "Warn",
22437
+ action: "warn",
22438
+ reversible: false,
22439
+ description: "Allow the request, but warn the user inline before it is sent."
22440
+ },
22441
+ redact: {
22442
+ name: "Redact",
22443
+ action: "redact",
22444
+ reversible: false,
22445
+ description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
22446
+ },
22447
+ vault: {
22448
+ name: "Redact & Vault",
22449
+ action: "redact",
22450
+ reversible: true,
22451
+ 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."
22452
+ },
22453
+ block: {
22454
+ name: "Block",
22455
+ action: "block",
22456
+ reversible: false,
22457
+ description: "Refuse the request entirely whenever any rule in this detection matches."
22458
+ }
22459
+ };
22460
+ function builtinPolicyToAction(id) {
22461
+ return BUILTIN_POLICY_SPECS[id].action;
22462
+ }
22463
+ var PALETTE_WEAKEST_FIRST = [
22464
+ ...new Set(KNOWN_BUILTIN_IDS.map(builtinPolicyToAction))
22465
+ ];
22466
+ var BELOW_PALETTE = ACTION_TAKEN_KEYS.filter(
22467
+ (action) => !PALETTE_WEAKEST_FIRST.includes(action)
22468
+ );
22469
+ var ACTION_STRENGTH_ORDER = [
22470
+ ...BELOW_PALETTE,
22471
+ ...PALETTE_WEAKEST_FIRST
22472
+ ];
22473
+ var PackPolicyFloor = external_exports.object({
22474
+ /**
22475
+ * The weakest archetype the device may assign. Stated as a BuiltinPolicyId
22476
+ * rather than a raw ActionTaken because that is the vocabulary the user
22477
+ * picks from — a floor a UI cannot name is one it cannot explain.
22478
+ */
22479
+ floor: BuiltinPolicyId,
22480
+ /**
22481
+ * True when the organization AUTHORED a policy governing this pack rather
22482
+ * than stating a minimum: it gave the answer, so the pack is not
22483
+ * re-assignable locally in either direction.
22484
+ */
22485
+ locked: external_exports.boolean()
22486
+ }).describe("PackPolicyFloor");
22487
+ var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
22488
+ (id) => !BUILTIN_POLICY_SPECS[id].reversible
22489
+ );
22490
+ var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
22491
+ (id) => BUILTIN_POLICY_SPECS[id].reversible
22492
+ );
22493
+ var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
22494
+ var DEFAULT_ACTIONS = Object.fromEntries(
22495
+ DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
22496
+ );
22497
+ var BUILTIN_POLICIES = Object.fromEntries(
22498
+ KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
22499
+ );
22500
+ var UsedByItem = external_exports.object({
22501
+ id: external_exports.string(),
22502
+ name: external_exports.string(),
22503
+ ruleCount: external_exports.number().int().nonnegative(),
22504
+ enabled: external_exports.boolean()
22505
+ }).meta({ id: "UsedByItem" });
22506
+ var PolicyListItem = external_exports.object({
22507
+ id: external_exports.string(),
22508
+ kind: PolicyKind,
22509
+ name: external_exports.string(),
22510
+ enabled: external_exports.boolean(),
22511
+ usedByCount: external_exports.number().int().nonnegative()
22512
+ }).meta({ id: "PolicyListItem" });
22513
+ var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
22514
+ var PolicyDetail = external_exports.object({
22515
+ specVersion: external_exports.literal(1),
22516
+ id: external_exports.string(),
22517
+ kind: PolicyKind,
22518
+ name: external_exports.string(),
22519
+ enabled: external_exports.boolean(),
22520
+ description: external_exports.string(),
22521
+ usedBy: external_exports.array(UsedByItem)
22522
+ }).meta({ id: "PolicyDetail" });
22523
+ var PolicyStatsResponse = external_exports.object({
22524
+ policies: external_exports.number().int().nonnegative(),
22525
+ builtin: external_exports.number().int().nonnegative(),
22526
+ custom: external_exports.number().int().nonnegative(),
22527
+ detectionsGoverned: external_exports.number().int().nonnegative()
22528
+ }).meta({ id: "PolicyStatsResponse" });
22529
+
22290
22530
  // ../../packages/schema/src/zod/vault.ts
22291
22531
  var CATEGORY_ALTERNATION = DetectionCategory.options.join("|");
22292
22532
  var POINTER_TOKEN_PATTERN = new RegExp(
@@ -22323,6 +22563,14 @@ var VaultEntry = external_exports.object({
22323
22563
  // How many times this value has been detected on this machine — the reuse
22324
22564
  // signal the vault dashboard reads. Distinct from VaultDeref.pointerCount.
22325
22565
  occurrenceCount: external_exports.number().int().nonnegative(),
22566
+ // True when a PERSON asked for this value to be replaced — the surfaced-
22567
+ // secrets strike — rather than a pack enforcing its assignment. One value is
22568
+ // one row however many paths vault it, so this is what tells a policy sweep
22569
+ // that the row carries somebody's own instruction and not just an assignment
22570
+ // that has since been lowered. STICKY and MONOTONIC: a later automatic
22571
+ // vaulting of the same value must never clear it — what the user said about
22572
+ // the value does not expire.
22573
+ userAuthorized: external_exports.boolean(),
22326
22574
  firstSeen: external_exports.string(),
22327
22575
  lastSeen: external_exports.string()
22328
22576
  });
@@ -22439,7 +22687,7 @@ var VaultConsent = external_exports.object({
22439
22687
  });
22440
22688
 
22441
22689
  // ../../packages/schema/src/zod/local.ts
22442
- var WORKSPACE_SETTINGS_SPEC_VERSION = 6;
22690
+ var WORKSPACE_SETTINGS_SPEC_VERSION = 7;
22443
22691
  var MODEL_JUDGE_PAYLOAD_VERSION = 1;
22444
22692
  var RunMode = external_exports.enum(["standalone", "attached"]);
22445
22693
  var ControlPlaneConnection = external_exports.object({
@@ -22481,6 +22729,19 @@ var WorkspaceSettings = external_exports.object({
22481
22729
  vaultKeyCustody: VaultKeyCustody.default("file"),
22482
22730
  // How a pointer renders in assistant prose on screen (see VaultInlineReveal).
22483
22731
  vaultInlineReveal: VaultInlineReveal.default("masked"),
22732
+ // What a `redact` policy degrades to on a FIELD the host cannot rewrite in
22733
+ // place. Not a handling policy: the policy has already resolved to redact,
22734
+ // and this only says what happens when the host offers no channel to carry it
22735
+ // out — Antigravity's PreToolUse has no updatedInput at all, and Codex and
22736
+ // Claude Code decline to mask a field that EXECUTES because masking would
22737
+ // change what runs. Per FIELD rather than per host, so a host that can
22738
+ // rewrite some inputs keeps true redaction on those.
22739
+ //
22740
+ // Spelled in the built-in policy vocabulary rather than as a fresh enum, so
22741
+ // an attached machine's merge is `strongerAction` over the one action ladder
22742
+ // and no second rank order exists to drift from it. 'deny' is a host wire
22743
+ // word and stays out of the stored value.
22744
+ redactFallback: RedactFallback.default("warn"),
22484
22745
  // Absent until /aka:setup completes; its presence is what "onboarded" means.
22485
22746
  onboardedAt: external_exports.iso.datetime().optional(),
22486
22747
  // Records that the user consented to sending findings to the model API for
@@ -22488,10 +22749,12 @@ var WorkspaceSettings = external_exports.object({
22488
22749
  // Absent until granted; a stale payloadVersion means the consent no longer
22489
22750
  // covers the current payload and must be re-granted.
22490
22751
  modelJudgeConsent: ModelJudgeConsent.optional(),
22491
- // Records that the user consented to sending the activity already recorded on
22492
- // this machine to the deployment it is attached to, along with the payload
22493
- // shape and the endpoint they agreed to. Absent until granted, and a grant for
22494
- // a different endpoint or an older payload no longer counts.
22752
+ // Records that the user consented to the DEFERRED send — the outbox — along
22753
+ // with the payload shape and the endpoint they agreed to. Since payload v2
22754
+ // that covers both the pre-attach backlog and undelivered captures (which
22755
+ // carry prompt/reply text in `content`); the key name predates the widening.
22756
+ // Absent until granted, and a grant for a different endpoint or an older
22757
+ // payload no longer counts.
22495
22758
  historySyncConsent: HistorySyncConsent.optional()
22496
22759
  });
22497
22760
  function defaultWorkspaceSettings() {
@@ -22511,7 +22774,8 @@ var ManagedSettingKey = external_exports.enum([
22511
22774
  "vaultKeyCustody",
22512
22775
  "vaultInlineReveal",
22513
22776
  "modelJudgeConsent",
22514
- "dataSharesInPlace"
22777
+ "dataSharesInPlace",
22778
+ "redactFallback"
22515
22779
  ]).meta({ id: "ManagedSettingKey" });
22516
22780
  var ManagedSettingsValues = external_exports.object({
22517
22781
  runMode: external_exports.enum(["standalone", "attached"]).optional(),
@@ -22524,7 +22788,8 @@ var ManagedSettingsValues = external_exports.object({
22524
22788
  vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
22525
22789
  vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
22526
22790
  modelJudgeConsent: external_exports.boolean().optional(),
22527
- dataSharesInPlace: external_exports.boolean().optional()
22791
+ dataSharesInPlace: external_exports.boolean().optional(),
22792
+ redactFallback: RedactFallback.optional()
22528
22793
  }).meta({ id: "ManagedSettingsValues" });
22529
22794
  var ManagedSettings = external_exports.object({
22530
22795
  specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
@@ -22539,171 +22804,6 @@ var ManagedSettings = external_exports.object({
22539
22804
  lockedFields: external_exports.array(ManagedSettingKey).default([])
22540
22805
  }).meta({ id: "ManagedSettings" });
22541
22806
 
22542
- // ../../packages/schema/src/zod/policy.ts
22543
- var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
22544
- var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
22545
- var Policy = external_exports.object({
22546
- id: external_exports.guid(),
22547
- scope: PolicyScope,
22548
- target: PolicyTarget,
22549
- action: ActionTaken,
22550
- enabled: external_exports.boolean().default(true),
22551
- customKeywords: external_exports.array(external_exports.string()).optional(),
22552
- // Display name — optional so older policy rows without name still parse.
22553
- // Added for the findings API (policy.name column migration).
22554
- name: external_exports.string().optional()
22555
- }).meta({ id: "Policy" });
22556
- var PolicyBundle = external_exports.object({
22557
- version: external_exports.string(),
22558
- policies: external_exports.array(Policy),
22559
- // Rules from the installed marketplace packs (snapshotted by the
22560
- // control plane). The plugin registers these in addition to its bundled
22561
- // packs. Optional so older backends — and older on-disk caches — that omit
22562
- // the field still parse; consumers read `bundle.rules ?? []`.
22563
- rules: external_exports.array(Rule).optional(),
22564
- // When true, `rules` IS the complete effective ruleset and the runtime must
22565
- // NOT merge its compiled-in bundled packs — the standalone gateway sets this
22566
- // after reading the user's installed snapshot (installed_packs, enabled
22567
- // packs only), which is how detection updates stay manual: new bundled
22568
- // rules run only after the user applies the pack update. Absent/false keeps
22569
- // the historical composition (bundled packs + rules) — older caches.
22570
- rulesComplete: external_exports.boolean().optional(),
22571
- // Active detection exceptions, evaluation subset only (see
22572
- // ExceptionBundleEntry). Optional so older bundle producers — and older
22573
- // on-disk caches — that omit the field still parse; consumers read
22574
- // `bundle.exceptions ?? []`.
22575
- exceptions: external_exports.array(ExceptionBundleEntry).optional(),
22576
- // Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
22577
- // A second axis over the same `redact` action, carried beside the policies
22578
- // rather than on them: nothing writes ruleId-targeted policies to disk, so
22579
- // widening Policy itself would change a persisted shape to express something
22580
- // only the in-memory bundle needs. Optional so an older producer — or an
22581
- // older on-disk cache — still parses; consumers read `?? []` and get the
22582
- // pre-existing one-way behaviour, which is the safe direction to default.
22583
- reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
22584
- // Installed pack version, keyed by ruleId, for rules in `rules` that came
22585
- // from a versioned installed pack. Optional so older backends — and older
22586
- // on-disk caches — that omit the field still parse; consumers fall back to
22587
- // the rule's own spec version. NOT the bundle version above — see
22588
- // installedRuleset's ruleVersions for the source of truth.
22589
- ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
22590
- // Model ids (the raw `model` string a harness reports, e.g.
22591
- // `claude-opus-4-1`) the tenant has PROHIBITED. The plugin refuses to switch
22592
- // a session onto one (PreModelSwitch) and refuses a turn that would run on
22593
- // one (UserPromptSubmit). Optional so an older backend — and an older
22594
- // on-disk cache — still parses; consumers read `?? []`, which is the
22595
- // unenforced behaviour that predates this field and the safe direction to
22596
- // default.
22597
- //
22598
- // Ids, not display names: the governance decision is keyed on the exact
22599
- // string the harness reports (`model_status_override.versionId` in the
22600
- // control plane), so no name resolution stands between the decision and the
22601
- // comparison.
22602
- prohibitedModels: external_exports.array(external_exports.string()).optional(),
22603
- customKeywords: external_exports.array(external_exports.string()),
22604
- fetchedAt: external_exports.iso.datetime()
22605
- }).meta({ id: "PolicyBundle" });
22606
- var OBSERVE_ONLY_CATEGORIES = ["config"];
22607
- var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
22608
- var CATEGORY_PEAK_SEVERITY = {
22609
- secret: "critical",
22610
- financial: "critical",
22611
- // core-financial/credit-card
22612
- code_flaw: "critical",
22613
- pii: "high",
22614
- phi: "high",
22615
- custom: "high",
22616
- // user-defined; conservative
22617
- code_context: "low",
22618
- config: "low"
22619
- // observe-only; floors to monitor regardless
22620
- };
22621
- function severityFloorPolicy(category) {
22622
- if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
22623
- const peak = CATEGORY_PEAK_SEVERITY[category];
22624
- return peak === "critical" || peak === "high" ? "warn" : "monitor";
22625
- }
22626
- var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
22627
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
22628
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
22629
- var BUILTIN_POLICY_SPECS = {
22630
- monitor: {
22631
- name: "Monitor",
22632
- action: "log",
22633
- reversible: false,
22634
- description: "Log every match for audit. The request is allowed through untouched."
22635
- },
22636
- warn: {
22637
- name: "Warn",
22638
- action: "warn",
22639
- reversible: false,
22640
- description: "Allow the request, but warn the user inline before it is sent."
22641
- },
22642
- redact: {
22643
- name: "Redact",
22644
- action: "redact",
22645
- reversible: false,
22646
- description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
22647
- },
22648
- vault: {
22649
- name: "Redact & Vault",
22650
- action: "redact",
22651
- reversible: true,
22652
- 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."
22653
- },
22654
- block: {
22655
- name: "Block",
22656
- action: "block",
22657
- reversible: false,
22658
- description: "Refuse the request entirely whenever any rule in this detection matches."
22659
- }
22660
- };
22661
- function builtinPolicyToAction(id) {
22662
- return BUILTIN_POLICY_SPECS[id].action;
22663
- }
22664
- var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
22665
- (id) => !BUILTIN_POLICY_SPECS[id].reversible
22666
- );
22667
- var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
22668
- (id) => BUILTIN_POLICY_SPECS[id].reversible
22669
- );
22670
- var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
22671
- var DEFAULT_ACTIONS = Object.fromEntries(
22672
- DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
22673
- );
22674
- var BUILTIN_POLICIES = Object.fromEntries(
22675
- KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
22676
- );
22677
- var UsedByItem = external_exports.object({
22678
- id: external_exports.string(),
22679
- name: external_exports.string(),
22680
- ruleCount: external_exports.number().int().nonnegative(),
22681
- enabled: external_exports.boolean()
22682
- }).meta({ id: "UsedByItem" });
22683
- var PolicyListItem = external_exports.object({
22684
- id: external_exports.string(),
22685
- kind: PolicyKind,
22686
- name: external_exports.string(),
22687
- enabled: external_exports.boolean(),
22688
- usedByCount: external_exports.number().int().nonnegative()
22689
- }).meta({ id: "PolicyListItem" });
22690
- var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
22691
- var PolicyDetail = external_exports.object({
22692
- specVersion: external_exports.literal(1),
22693
- id: external_exports.string(),
22694
- kind: PolicyKind,
22695
- name: external_exports.string(),
22696
- enabled: external_exports.boolean(),
22697
- description: external_exports.string(),
22698
- usedBy: external_exports.array(UsedByItem)
22699
- }).meta({ id: "PolicyDetail" });
22700
- var PolicyStatsResponse = external_exports.object({
22701
- policies: external_exports.number().int().nonnegative(),
22702
- builtin: external_exports.number().int().nonnegative(),
22703
- custom: external_exports.number().int().nonnegative(),
22704
- detectionsGoverned: external_exports.number().int().nonnegative()
22705
- }).meta({ id: "PolicyStatsResponse" });
22706
-
22707
22807
  // ../../packages/schema/src/zod/project-files.ts
22708
22808
  var ProjectFileInput = external_exports.object({
22709
22809
  path: external_exports.string().min(1),
@@ -22943,10 +23043,12 @@ var DismissRecommendedActionResponse = external_exports.object({ id: external_ex
22943
23043
  var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
22944
23044
 
22945
23045
  // ../../packages/schema/src/zod/settings-action.ts
23046
+ var HistorySyncConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "HistorySyncConsentChoice" });
23047
+ var ModelJudgeConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "ModelJudgeConsentChoice" });
22946
23048
  var SaveSettingsInput = external_exports.object({
22947
23049
  historicalAccess: external_exports.string(),
22948
- modelJudgeConsent: external_exports.boolean(),
22949
- historySyncConsent: external_exports.boolean(),
23050
+ modelJudgeConsent: ModelJudgeConsentChoice,
23051
+ historySyncConsent: HistorySyncConsentChoice,
22950
23052
  vaultConsent: external_exports.string(),
22951
23053
  vaultInlineReveal: external_exports.string()
22952
23054
  });
@@ -23202,8 +23304,8 @@ function readControlPlaneCredentialFile(settingsDir2, connection) {
23202
23304
  }
23203
23305
 
23204
23306
  // ../../packages/persistence/src/database.ts
23205
- import { randomUUID as randomUUID10 } from "crypto";
23206
- import { join as join4, sep } from "path";
23307
+ import { randomUUID as randomUUID11 } from "crypto";
23308
+ import { dirname as dirname2, join as join7, sep } from "path";
23207
23309
  import { DatabaseSync } from "node:sqlite";
23208
23310
 
23209
23311
  // ../../packages/persistence/src/ids.ts
@@ -23230,6 +23332,20 @@ function parseJsonObject(s) {
23230
23332
 
23231
23333
  // ../../packages/persistence/src/repositories/activity.ts
23232
23334
  var LIVE_ACTIVITY_WINDOW_MS = 30 * 6e4;
23335
+ var DB_EVENT_TYPE_TO_KIND = {
23336
+ session: "session",
23337
+ prompt: "prompt",
23338
+ response: "response",
23339
+ tool_call: "tool",
23340
+ hook: "hook",
23341
+ detection: "detection",
23342
+ share: "share",
23343
+ permission: "permission",
23344
+ commit: "commit",
23345
+ error: "error",
23346
+ active: "active"
23347
+ };
23348
+ var TIMELINE_EVENT_TYPES = Object.keys(DB_EVENT_TYPE_TO_KIND).map((kind) => `'${kind}'`).join(", ");
23233
23349
  var DEFAULT_HARNESS = HARNESS.ClaudeCode;
23234
23350
 
23235
23351
  // ../../packages/persistence/src/repositories/exceptions.ts
@@ -23246,104 +23362,55 @@ var ACTIVE_REVEAL_GRANT_PREDICATE = `capability = 'reveal_to_model'
23246
23362
  // ../../packages/persistence/src/repositories/history-sync.ts
23247
23363
  var STRUCTURAL_EVENT_TYPES = ["session", "llm_call", "tool_call"];
23248
23364
  var TYPE_LIST = STRUCTURAL_EVENT_TYPES.map((t) => `'${t}'`).join(", ");
23365
+ var OUTBOX_CAPTURE_EVENT_TYPES = ["prompt", "response", "tool_use"];
23366
+ var CAPTURE_TYPE_LIST = OUTBOX_CAPTURE_EVENT_TYPES.map((t) => `'${t}'`).join(", ");
23249
23367
 
23250
23368
  // ../../packages/persistence/src/repositories/installed-packs.ts
23251
- import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
23252
-
23253
- // ../../packages/persistence/src/repositories/inventory-assets.ts
23254
- import { randomUUID as randomUUID4 } from "crypto";
23255
- var VALID_HARNESS_IDS = new Set(HarnessId.options);
23256
- var HARNESS_LABELS = {
23257
- [HARNESS.ClaudeCode]: "Claude Code",
23258
- [HARNESS.Cursor]: "Cursor",
23259
- [HARNESS.Codex]: "Codex",
23260
- [HARNESS.Antigravity]: "Antigravity"
23261
- };
23262
- var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
23263
- var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
23264
- var TITLE_NEEDLES = {
23265
- ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
23266
- Cursor: stripSeparators(SOURCE_TOOL.Cursor),
23267
- Codex: stripSeparators(SOURCE_TOOL.Codex),
23268
- Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
23269
- };
23270
-
23271
- // ../../packages/persistence/src/repositories/policies.ts
23272
- import { randomUUID as randomUUID5 } from "crypto";
23273
-
23274
- // ../../packages/persistence/src/repositories/project-files.ts
23275
- import { randomUUID as randomUUID6 } from "crypto";
23276
-
23277
- // ../../packages/persistence/src/repositories/resolutions.ts
23278
- import { randomUUID as randomUUID7 } from "crypto";
23279
-
23280
- // ../../packages/persistence/src/repositories/secret-vault.ts
23281
- import { randomUUID as randomUUID8 } from "crypto";
23282
-
23283
- // ../../packages/persistence/src/repositories/security.ts
23284
- var SCAN_COVERAGE = {
23285
- [HARNESS.Antigravity]: { coverage: 60, supported: true },
23286
- [HARNESS.Api]: { coverage: 0, supported: false },
23287
- [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23288
- [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23289
- [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23290
- [HARNESS.Codex]: { coverage: 80, supported: true },
23291
- [HARNESS.Copilot]: { coverage: 0, supported: false },
23292
- [HARNESS.Cursor]: { coverage: 0, supported: false }
23293
- };
23294
-
23295
- // ../../packages/persistence/src/repositories/shares.ts
23296
- import { randomUUID as randomUUID9 } from "crypto";
23297
-
23298
- // ../../packages/persistence/src/file-lock.ts
23299
- import { randomUUID as randomUUID11 } from "crypto";
23300
- import {
23301
- closeSync,
23302
- existsSync as existsSync2,
23303
- openSync,
23304
- readFileSync as readFileSync2,
23305
- rmSync as rmSync5,
23306
- statSync as statSync3,
23307
- writeFileSync as writeFileSync2
23308
- } from "fs";
23309
- import { hostname as hostname3 } from "os";
23310
- var PARK = new Int32Array(new SharedArrayBuffer(4));
23311
-
23312
- // ../../packages/persistence/src/finding-key.ts
23313
- import { createHash as createHash3 } from "crypto";
23314
-
23315
- // ../../packages/persistence/src/fingerprint.ts
23316
- import { createHmac, randomBytes } from "crypto";
23317
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
23318
- import { join as join5 } from "path";
23319
- import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
23369
+ import { createHash as createHash2, randomUUID as randomUUID4 } from "crypto";
23320
23370
 
23321
- // ../../packages/persistence/src/history-preview.ts
23322
- import { existsSync as existsSync4 } from "fs";
23371
+ // ../../packages/persistence/src/policy-floor.ts
23372
+ import { readFileSync as readFileSync5 } from "fs";
23323
23373
  import { join as join6 } from "path";
23324
- import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
23325
23374
 
23326
23375
  // ../../packages/persistence/src/local-layout.ts
23327
23376
  import { renameSync as renameSync3 } from "fs";
23328
23377
  import { mkdir } from "fs/promises";
23329
23378
  import { homedir } from "os";
23330
- import { join as join7 } from "path";
23379
+ import { join as join4 } from "path";
23331
23380
  function defaultDataDir() {
23332
- return join7(homedir(), ".aka");
23381
+ return join4(homedir(), ".aka");
23333
23382
  }
23334
23383
  function settingsDir(base = defaultDataDir()) {
23335
- return join7(base, "settings");
23384
+ return join4(base, "settings");
23336
23385
  }
23337
23386
  function dataDir(base = defaultDataDir()) {
23338
- return join7(base, "data");
23387
+ return join4(base, "data");
23339
23388
  }
23340
23389
  async function ensureDataDir(dir = defaultDataDir()) {
23341
23390
  await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
23342
23391
  tightenDir(dir);
23343
23392
  }
23344
23393
 
23345
- // ../../packages/persistence/src/managed-settings.ts
23394
+ // ../../packages/persistence/src/settings.ts
23346
23395
  import { readFileSync as readFileSync4 } from "fs";
23396
+ import { join as join5 } from "path";
23397
+
23398
+ // ../../packages/persistence/src/file-lock.ts
23399
+ import { randomUUID as randomUUID3 } from "crypto";
23400
+ import {
23401
+ closeSync,
23402
+ existsSync as existsSync2,
23403
+ openSync,
23404
+ readFileSync as readFileSync2,
23405
+ rmSync as rmSync5,
23406
+ statSync as statSync3,
23407
+ writeFileSync as writeFileSync2
23408
+ } from "fs";
23409
+ import { hostname as hostname3 } from "os";
23410
+ var PARK = new Int32Array(new SharedArrayBuffer(4));
23411
+
23412
+ // ../../packages/persistence/src/managed-settings.ts
23413
+ import { readFileSync as readFileSync3 } from "fs";
23347
23414
  import { posix, win32 } from "path";
23348
23415
  function managedSettingsPaths(platform2 = process.platform) {
23349
23416
  if (platform2 === "darwin") {
@@ -23361,7 +23428,7 @@ function readManagedSettings(paths = managedSettingsPaths()) {
23361
23428
  for (const path of paths) {
23362
23429
  let text;
23363
23430
  try {
23364
- text = readFileSync4(path, "utf8");
23431
+ text = readFileSync3(path, "utf8");
23365
23432
  } catch {
23366
23433
  continue;
23367
23434
  }
@@ -23391,6 +23458,7 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
23391
23458
  if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
23392
23459
  if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
23393
23460
  if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
23461
+ if (values.redactFallback !== void 0) merged.redactFallback = values.redactFallback;
23394
23462
  if (values.vaultConsent !== void 0) {
23395
23463
  merged.vaultConsent = values.vaultConsent ? (
23396
23464
  // Keep an existing valid grant so its acknowledgedAt survives; mint one
@@ -23408,14 +23476,12 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
23408
23476
  }
23409
23477
 
23410
23478
  // ../../packages/persistence/src/settings.ts
23411
- import { readFileSync as readFileSync5 } from "fs";
23412
- import { join as join8 } from "path";
23413
23479
  var SETTINGS_FILENAME = "settings.json";
23414
23480
  function readWorkspaceSettings(base = defaultDataDir()) {
23415
23481
  return overlayManagedSettings(readUserSettings(base), readManagedSettings());
23416
23482
  }
23417
23483
  function readUserSettings(base) {
23418
- const record2 = readJson(join8(settingsDir(base), SETTINGS_FILENAME));
23484
+ const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
23419
23485
  if (!record2) return defaultWorkspaceSettings();
23420
23486
  try {
23421
23487
  return WorkspaceSettings.parse(record2);
@@ -23426,16 +23492,78 @@ function readUserSettings(base) {
23426
23492
  function readJson(file2) {
23427
23493
  let text;
23428
23494
  try {
23429
- text = readFileSync5(file2, "utf8");
23495
+ text = readFileSync4(file2, "utf8");
23430
23496
  } catch {
23431
23497
  return null;
23432
23498
  }
23433
23499
  return parseJsonObject(text) ?? null;
23434
23500
  }
23435
23501
 
23502
+ // ../../packages/persistence/src/repositories/inventory-assets.ts
23503
+ import { randomUUID as randomUUID5 } from "crypto";
23504
+ var VALID_HARNESS_IDS = new Set(HarnessId.options);
23505
+ var HARNESS_LABELS = {
23506
+ [HARNESS.ClaudeCode]: "Claude Code",
23507
+ [HARNESS.Cursor]: "Cursor",
23508
+ [HARNESS.Codex]: "Codex",
23509
+ [HARNESS.Antigravity]: "Antigravity"
23510
+ };
23511
+ var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
23512
+ var stripSeparators = (value) => value.toLowerCase().replace(/[\s-]/g, "");
23513
+ var TITLE_NEEDLES = {
23514
+ ClaudeCode: stripSeparators(SOURCE_TOOL.ClaudeCode),
23515
+ Cursor: stripSeparators(SOURCE_TOOL.Cursor),
23516
+ Codex: stripSeparators(SOURCE_TOOL.Codex),
23517
+ Antigravity: stripSeparators(SOURCE_TOOL.Antigravity)
23518
+ };
23519
+
23520
+ // ../../packages/persistence/src/repositories/policies.ts
23521
+ import { randomUUID as randomUUID6 } from "crypto";
23522
+
23523
+ // ../../packages/persistence/src/repositories/project-files.ts
23524
+ import { randomUUID as randomUUID7 } from "crypto";
23525
+
23526
+ // ../../packages/persistence/src/repositories/resolutions.ts
23527
+ import { randomUUID as randomUUID8 } from "crypto";
23528
+
23529
+ // ../../packages/persistence/src/repositories/secret-vault.ts
23530
+ import { randomUUID as randomUUID9 } from "crypto";
23531
+
23532
+ // ../../packages/persistence/src/repositories/security.ts
23533
+ var SCAN_COVERAGE = {
23534
+ [HARNESS.Antigravity]: { coverage: 60, supported: true },
23535
+ [HARNESS.Api]: { coverage: 0, supported: false },
23536
+ [HARNESS.ChatGpt]: { coverage: 40, supported: true },
23537
+ [HARNESS.ClaudeAi]: { coverage: 40, supported: true },
23538
+ [HARNESS.ClaudeCode]: { coverage: 100, supported: true },
23539
+ [HARNESS.Codex]: { coverage: 80, supported: true },
23540
+ [HARNESS.Copilot]: { coverage: 0, supported: false },
23541
+ [HARNESS.Cursor]: { coverage: 0, supported: false }
23542
+ };
23543
+
23544
+ // ../../packages/persistence/src/repositories/shares.ts
23545
+ import { randomUUID as randomUUID10 } from "crypto";
23546
+
23547
+ // ../../packages/persistence/src/database.ts
23548
+ var CAPTURE_GRAIN = new Set(EventKind.options);
23549
+
23550
+ // ../../packages/persistence/src/finding-key.ts
23551
+ import { createHash as createHash3 } from "crypto";
23552
+
23553
+ // ../../packages/persistence/src/fingerprint.ts
23554
+ import { createHmac, randomBytes } from "crypto";
23555
+ import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
23556
+ import { join as join8 } from "path";
23557
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
23558
+
23559
+ // ../../packages/persistence/src/history-preview.ts
23560
+ import { existsSync as existsSync4 } from "fs";
23561
+ import { join as join9 } from "path";
23562
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
23563
+
23436
23564
  // ../../packages/persistence/src/store-symlinks.ts
23437
23565
  import { existsSync as existsSync5, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
23438
- import { dirname as dirname2, join as join9, resolve } from "path";
23566
+ import { dirname as dirname3, join as join10, resolve } from "path";
23439
23567
 
23440
23568
  // ../../packages/persistence/src/vault/crypto.ts
23441
23569
  import {
@@ -23449,47 +23577,421 @@ import {
23449
23577
  // ../../packages/persistence/src/vault/key-provider.ts
23450
23578
  import { execFileSync } from "child_process";
23451
23579
  import { randomBytes as randomBytes2 } from "crypto";
23452
- import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
23453
- import { join as join10 } from "path";
23580
+ import { chmodSync as chmodSync3, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
23581
+ import { join as join11 } from "path";
23454
23582
 
23455
23583
  // ../../packages/persistence/src/vault/vault.ts
23456
23584
  import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
23457
23585
 
23458
23586
  // ../../packages/persistence/src/warn-era-cap.ts
23459
23587
  import { existsSync as existsSync6, writeFileSync as writeFileSync4 } from "fs";
23460
- import { join as join11 } from "path";
23588
+ import { join as join12 } from "path";
23461
23589
 
23462
- // ../../packages/plugin-runtime/src/attached/failure.ts
23463
- function statusOf(err) {
23464
- if (typeof err !== "object" || err === null || !("status" in err)) return null;
23465
- const { status } = err;
23466
- if (typeof status !== "number" || !Number.isInteger(status)) return null;
23467
- return status >= 100 && status <= 599 ? status : null;
23468
- }
23469
- function classifyFailure(err) {
23470
- switch (statusOf(err)) {
23471
- case 401:
23472
- return "unauthorized";
23473
- case 403:
23590
+ // ../../packages/remote/src/http.ts
23591
+ import { request as httpRequest } from "http";
23592
+ import { request as httpsRequest } from "https";
23593
+ var DEFAULT_TIMEOUT_MS = 1e4;
23594
+ var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
23595
+ var RemoteRequestError = class extends Error {
23596
+ constructor(status) {
23597
+ super(`control-plane request failed with status ${String(status)}`);
23598
+ this.status = status;
23599
+ this.name = "RemoteRequestError";
23600
+ }
23601
+ status;
23602
+ };
23603
+ var RemoteRouteAbsent = class extends Error {
23604
+ constructor(route) {
23605
+ super(`control plane does not serve ${route}`);
23606
+ this.route = route;
23607
+ this.name = "RemoteRouteAbsent";
23608
+ }
23609
+ route;
23610
+ };
23611
+ var RemoteRequestInvalid = class extends Error {
23612
+ constructor(route, cause) {
23613
+ super(`refusing to send a malformed body to ${route}`);
23614
+ this.cause = cause;
23615
+ this.name = "RemoteRequestInvalid";
23616
+ }
23617
+ cause;
23618
+ };
23619
+ var RemoteResponseInvalid = class extends Error {
23620
+ constructor(route, detail) {
23621
+ super(`control plane answered ${route} with ${detail}`);
23622
+ this.name = "RemoteResponseInvalid";
23623
+ }
23624
+ };
23625
+ var RemoteTransportError = class extends Error {
23626
+ /**
23627
+ * The status the peer sent, when headers arrived and only the BODY was
23628
+ * refused.
23629
+ *
23630
+ * Undefined for the ordinary case this class was written for — no answer at
23631
+ * all. It exists because two paths reject after a status has already been
23632
+ * delivered: an oversized body and an aborted response. Discarding it there
23633
+ * reported a deployment answering 401 with a verbose body as a network
23634
+ * outage, which sends the reader to look at their network instead of their
23635
+ * credential.
23636
+ */
23637
+ constructor(reason, status) {
23638
+ super(`control-plane request did not complete: ${reason}`);
23639
+ this.status = status;
23640
+ this.name = "RemoteTransportError";
23641
+ }
23642
+ status;
23643
+ };
23644
+ async function send(options) {
23645
+ const url2 = new URL(options.url);
23646
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
23647
+ const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
23648
+ const requestOptions = {
23649
+ method: options.method,
23650
+ headers: {
23651
+ // CALLER HEADERS FIRST, so this module's own are not overridable. Spread
23652
+ // last they win, and two of the values below are ones no caller may
23653
+ // replace: `x-api-key` is the credential, and `content-length` is the
23654
+ // byte count that stops a multi-byte body being truncated by the
23655
+ // receiver. `SendOptions.headers` is a free-form record on an exported
23656
+ // function, so "no caller does that today" is not the guarantee to rely
23657
+ // on. The one header any caller actually passes — `if-none-match` on the
23658
+ // conditional GET — is untouched by this order.
23659
+ ...options.headers,
23660
+ // The credential. One header, matching what the deployment authenticates
23661
+ // on; a second copy in an `Authorization` header would be one more place
23662
+ // it can be logged by an intermediary for no gain.
23663
+ //
23664
+ // Spread conditionally rather than assigned as `undefined`: Node's header
23665
+ // handling and `content-length` bookkeeping treat a present-but-undefined
23666
+ // key differently from an absent one, and "the header is not there" is
23667
+ // the property the attach flow needs.
23668
+ ...options.apiKey === void 0 ? {} : { "x-api-key": options.apiKey },
23669
+ accept: "application/json",
23670
+ ...options.body === void 0 ? {} : {
23671
+ "content-type": "application/json",
23672
+ // Byte length, not string length: a multi-byte body sent with a
23673
+ // character count is truncated by the receiver.
23674
+ "content-length": String(Buffer.byteLength(options.body))
23675
+ }
23676
+ }
23677
+ };
23678
+ return new Promise((resolve2, reject) => {
23679
+ let settled = false;
23680
+ const fail = (reason, status) => {
23681
+ if (settled) return;
23682
+ settled = true;
23683
+ reject(new RemoteTransportError(reason, status));
23684
+ };
23685
+ const req = send_(url2, requestOptions, (res) => {
23686
+ const chunks = [];
23687
+ let size = 0;
23688
+ res.on("data", (chunk) => {
23689
+ size += chunk.length;
23690
+ if (size > MAX_RESPONSE_BYTES) {
23691
+ fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
23692
+ res.destroy();
23693
+ req.destroy();
23694
+ return;
23695
+ }
23696
+ chunks.push(chunk);
23697
+ });
23698
+ res.on("aborted", () => {
23699
+ fail("the response was aborted", res.statusCode);
23700
+ });
23701
+ res.on("end", () => {
23702
+ if (settled) return;
23703
+ settled = true;
23704
+ resolve2({
23705
+ status: res.statusCode ?? 0,
23706
+ headers: res.headers,
23707
+ body: Buffer.concat(chunks).toString("utf8")
23708
+ });
23709
+ });
23710
+ });
23711
+ const deadline = setTimeout(() => {
23712
+ fail(`no response within ${String(timeoutMs)}ms`);
23713
+ req.destroy();
23714
+ }, timeoutMs);
23715
+ deadline.unref();
23716
+ req.on("upgrade", (_res, socket) => {
23717
+ fail("the deployment answered with a protocol upgrade");
23718
+ socket.destroy();
23719
+ });
23720
+ req.on("close", () => {
23721
+ fail("the connection closed before a response was read");
23722
+ clearTimeout(deadline);
23723
+ });
23724
+ req.on("error", (err) => {
23725
+ fail(err.message);
23726
+ });
23727
+ if (options.body !== void 0) req.write(options.body);
23728
+ req.end();
23729
+ });
23730
+ }
23731
+
23732
+ // ../../packages/remote/src/client.ts
23733
+ var ROUTES = {
23734
+ events: "/v1/events",
23735
+ auditEvents: "/v1/audit-events",
23736
+ auditEventsBatch: "/v1/audit-events/batch",
23737
+ inventory: "/v1/inventory",
23738
+ storePosture: "/v1/store-posture",
23739
+ policyBundle: "/v1/policy-bundle",
23740
+ whoami: "/v1/plugin/whoami",
23741
+ shares: "/v1/shares",
23742
+ commands: "/v1/plugin/commands"
23743
+ };
23744
+ function ackRoute(id) {
23745
+ return `${ROUTES.commands}/${encodeURIComponent(id)}/ack`;
23746
+ }
23747
+ function headerValue(response, name) {
23748
+ const raw = response.headers[name];
23749
+ if (raw === void 0) return void 0;
23750
+ return Array.isArray(raw) ? raw[0] : raw;
23751
+ }
23752
+ function okBody(response) {
23753
+ if (response.status < 200 || response.status >= 300) {
23754
+ throw new RemoteRequestError(response.status);
23755
+ }
23756
+ return response.body;
23757
+ }
23758
+ function parsed(schema, body, route) {
23759
+ let json2;
23760
+ try {
23761
+ json2 = JSON.parse(body);
23762
+ } catch {
23763
+ throw new RemoteResponseInvalid(route, "a body that is not JSON");
23764
+ }
23765
+ const result = schema.safeParse(json2);
23766
+ if (!result.success) {
23767
+ throw new RemoteResponseInvalid(route, "a body this client cannot read");
23768
+ }
23769
+ return result.data;
23770
+ }
23771
+ function withoutTrailingSlashes(endpoint) {
23772
+ let end = endpoint.length;
23773
+ while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
23774
+ return endpoint.slice(0, end);
23775
+ }
23776
+ var SLASH = "/".charCodeAt(0);
23777
+ function createRemoteClient(options) {
23778
+ const base = withoutTrailingSlashes(options.endpoint);
23779
+ const url2 = (route) => `${base}${route}`;
23780
+ const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
23781
+ const sendOne = async (event) => {
23782
+ const validated = RecordAuditEventRequest.safeParse(event);
23783
+ if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
23784
+ const response = await send({
23785
+ ...common,
23786
+ method: "POST",
23787
+ url: url2(ROUTES.auditEvents),
23788
+ body: JSON.stringify(validated.data)
23789
+ });
23790
+ okBody(response);
23791
+ };
23792
+ return {
23793
+ async ingestEvents(batch) {
23794
+ const response = await send({
23795
+ ...common,
23796
+ method: "POST",
23797
+ url: url2(ROUTES.events),
23798
+ body: JSON.stringify(batch)
23799
+ });
23800
+ return parsed(IngestAck, okBody(response), ROUTES.events);
23801
+ },
23802
+ async ingestInventory(context) {
23803
+ const response = await send({
23804
+ ...common,
23805
+ method: "POST",
23806
+ url: url2(ROUTES.inventory),
23807
+ body: JSON.stringify(context)
23808
+ });
23809
+ return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
23810
+ },
23811
+ async recordAuditEvent(event) {
23812
+ await sendOne(event);
23813
+ },
23814
+ async recordAuditEvents(events, opts) {
23815
+ const validated = RecordAuditEventBatch.safeParse({ events });
23816
+ if (!validated.success) {
23817
+ throw new RemoteRequestInvalid(ROUTES.auditEventsBatch, validated.error);
23818
+ }
23819
+ const response = await send({
23820
+ ...common,
23821
+ method: "POST",
23822
+ url: url2(ROUTES.auditEventsBatch),
23823
+ body: JSON.stringify(validated.data)
23824
+ });
23825
+ if (response.status === 404) {
23826
+ if (opts?.fallbackToSingleEvents !== true) {
23827
+ throw new RemoteRouteAbsent(ROUTES.auditEventsBatch);
23828
+ }
23829
+ for (const event of validated.data.events) await sendOne(event);
23830
+ return { accepted: validated.data.events.length };
23831
+ }
23832
+ return parsed(AuditEventBatchAck, okBody(response), ROUTES.auditEventsBatch);
23833
+ },
23834
+ async reportStorePosture(snapshot) {
23835
+ const response = await send({
23836
+ ...common,
23837
+ method: "POST",
23838
+ url: url2(ROUTES.storePosture),
23839
+ body: JSON.stringify(snapshot)
23840
+ });
23841
+ okBody(response);
23842
+ },
23843
+ async getPolicyBundle(etag) {
23844
+ const response = await send({
23845
+ ...common,
23846
+ method: "GET",
23847
+ url: url2(ROUTES.policyBundle),
23848
+ ...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
23849
+ });
23850
+ if (response.status === 304) {
23851
+ return { changed: false, etag: headerValue(response, "etag") ?? etag };
23852
+ }
23853
+ const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
23854
+ return { changed: true, bundle, etag: headerValue(response, "etag") };
23855
+ },
23856
+ async whoami() {
23857
+ const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
23858
+ return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
23859
+ },
23860
+ async recordProjectEgress(request) {
23861
+ const validated = EgressIngestRequest.safeParse(request);
23862
+ if (!validated.success) throw new RemoteRequestInvalid(ROUTES.shares, validated.error);
23863
+ const response = await send({
23864
+ ...common,
23865
+ method: "POST",
23866
+ url: url2(ROUTES.shares),
23867
+ body: JSON.stringify(validated.data)
23868
+ });
23869
+ okBody(response);
23870
+ },
23871
+ async pollCommand() {
23872
+ const response = await send({ ...common, method: "GET", url: url2(ROUTES.commands) });
23873
+ if (response.status === 404) return null;
23874
+ return parsed(DeviceCommandPollResponse, okBody(response), ROUTES.commands).command;
23875
+ },
23876
+ async ackCommand(id, body) {
23877
+ const validated = DeviceCommandAckBody.safeParse(body);
23878
+ const route = ackRoute(id);
23879
+ if (!validated.success) throw new RemoteRequestInvalid(route, validated.error);
23880
+ const response = await send({
23881
+ ...common,
23882
+ method: "POST",
23883
+ url: url2(route),
23884
+ body: JSON.stringify(validated.data)
23885
+ });
23886
+ okBody(response);
23887
+ }
23888
+ };
23889
+ }
23890
+
23891
+ // ../../packages/plugin-runtime/src/attached/failure.ts
23892
+ function statusOf(err) {
23893
+ if (typeof err !== "object" || err === null || !("status" in err)) return null;
23894
+ const { status } = err;
23895
+ if (typeof status !== "number" || !Number.isInteger(status)) return null;
23896
+ return status >= 100 && status <= 599 ? status : null;
23897
+ }
23898
+ function classifyFailure(err) {
23899
+ switch (statusOf(err)) {
23900
+ case 401:
23901
+ return "unauthorized";
23902
+ case 403:
23474
23903
  return "forbidden";
23475
23904
  default:
23476
23905
  return "unreachable";
23477
23906
  }
23478
23907
  }
23479
23908
 
23909
+ // ../../packages/plugin-runtime/src/attached/with-timeout.ts
23910
+ function withTimeout(promise2, ms) {
23911
+ let timer;
23912
+ const timeout = new Promise((_, reject) => {
23913
+ timer = setTimeout(() => {
23914
+ reject(new Error("attached gateway request timed out"));
23915
+ }, ms);
23916
+ });
23917
+ promise2.catch(() => void 0);
23918
+ return Promise.race([promise2, timeout]).finally(() => {
23919
+ clearTimeout(timer);
23920
+ });
23921
+ }
23922
+
23923
+ // ../../packages/plugin-runtime/src/attached/command-sync.ts
23924
+ var COMMAND_REQUEST_TIMEOUT_MS = 3e4;
23925
+ var SCAN_THREW = "scan_failed";
23926
+ var SCAN_FOUND_NOTHING = "no_projects";
23927
+ var COMMAND_EXPIRED = "expired";
23928
+ function hasExpired(expiresAt, atMs) {
23929
+ const deadlineMs = Date.parse(expiresAt);
23930
+ return Number.isFinite(deadlineMs) && deadlineMs <= atMs;
23931
+ }
23932
+ var SERVICE = {
23933
+ shares_rescan: (scan2) => scan2()
23934
+ };
23935
+ async function runCommandSync(deps) {
23936
+ if (deps.scan === void 0) return null;
23937
+ const settings = readWorkspaceSettings(deps.base);
23938
+ const connection = isAttached(settings) ? settings.controlPlane : void 0;
23939
+ const state = connection === void 0 ? void 0 : readControlPlaneCredentialFile(deps.settingsDir, connection);
23940
+ if (connection === void 0 || !state?.usable) return null;
23941
+ const client = createRemoteClient({
23942
+ endpoint: connection.endpoint,
23943
+ apiKey: state.credential.apiKey,
23944
+ timeoutMs: COMMAND_REQUEST_TIMEOUT_MS
23945
+ });
23946
+ let command;
23947
+ try {
23948
+ command = await withTimeout(client.pollCommand(), COMMAND_REQUEST_TIMEOUT_MS);
23949
+ } catch (err) {
23950
+ const failure = classifyFailure(err);
23951
+ return failure === "unreachable" ? "unreachable" : failure;
23952
+ }
23953
+ if (command === null) return "none";
23954
+ let projects = 0;
23955
+ let reason = null;
23956
+ if (hasExpired(command.expiresAt, (deps.now ?? Date.now)())) {
23957
+ reason = COMMAND_EXPIRED;
23958
+ } else {
23959
+ try {
23960
+ projects = (await SERVICE[command.kind](deps.scan)).projects;
23961
+ if (projects === 0) reason = SCAN_FOUND_NOTHING;
23962
+ } catch {
23963
+ reason = SCAN_THREW;
23964
+ }
23965
+ }
23966
+ try {
23967
+ await withTimeout(
23968
+ reason === null ? client.ackCommand(command.id, { outcome: "reported", projectsScanned: projects }) : client.ackCommand(command.id, {
23969
+ outcome: "failed",
23970
+ reason,
23971
+ projectsScanned: projects
23972
+ }),
23973
+ COMMAND_REQUEST_TIMEOUT_MS
23974
+ );
23975
+ } catch (err) {
23976
+ const failure = classifyFailure(err);
23977
+ return failure === "unreachable" ? "unreachable" : failure;
23978
+ }
23979
+ return reason === null ? "reported" : "failed";
23980
+ }
23981
+
23480
23982
  // ../../packages/plugin-runtime/src/attached/forward-drops.ts
23481
- import { readFileSync as readFileSync7 } from "fs";
23482
- import { join as join12 } from "path";
23983
+ import { readFileSync as readFileSync8 } from "fs";
23984
+ import { join as join13 } from "path";
23483
23985
 
23484
23986
  // ../../packages/plugin-runtime/src/attached/forward-policy.ts
23485
23987
  import { randomUUID as randomUUID15 } from "crypto";
23486
- import { readFileSync as readFileSync13 } from "fs";
23988
+ import { readFileSync as readFileSync14 } from "fs";
23487
23989
  import { readFile, rename, writeFile } from "fs/promises";
23488
- import { join as join21 } from "path";
23990
+ import { join as join22 } from "path";
23489
23991
 
23490
23992
  // ../../packages/plugin-sdk/src/config.ts
23491
23993
  import { existsSync as existsSync7 } from "fs";
23492
- import { join as join13 } from "path";
23994
+ import { join as join14 } from "path";
23493
23995
 
23494
23996
  // ../../packages/plugin-sdk/src/provider-env.ts
23495
23997
  var booleanish = external_exports.string().optional().transform((v) => {
@@ -23510,9 +24012,9 @@ var providerEnvShape = {
23510
24012
  var ProviderEnvSchema = external_exports.object(providerEnvShape);
23511
24013
 
23512
24014
  // ../../packages/plugin-sdk/src/config-inventory.ts
23513
- import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
24015
+ import { readdirSync as readdirSync2, readFileSync as readFileSync10, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
23514
24016
  import { homedir as homedir2 } from "os";
23515
- import { basename as basename3, join as join15 } from "path";
24017
+ import { basename as basename3, join as join16 } from "path";
23516
24018
 
23517
24019
  // ../../packages/detections/src/egress/registry.ts
23518
24020
  var EXTRACTOR_VERSION = "1";
@@ -24009,637 +24511,359 @@ var PROVIDER_REGISTRY = [
24009
24511
  }
24010
24512
  },
24011
24513
  {
24012
- id: "huggingface",
24013
- name: "Hugging Face",
24014
- category: "LLM provider",
24015
- hostSuffixes: ["huggingface.co"],
24016
- apiBase: "https://api-inference.huggingface.co",
24017
- defaultDataClasses: ["source"],
24018
- sdks: {
24019
- npm: ["@huggingface/inference"],
24020
- pypi: ["huggingface-hub", "transformers"],
24021
- rubygems: ["hugging-face"]
24022
- }
24023
- },
24024
- {
24025
- id: "cohere",
24026
- name: "Cohere",
24027
- category: "LLM provider",
24028
- hostSuffixes: ["cohere.com", "cohere.ai"],
24029
- apiBase: "https://api.cohere.com",
24030
- defaultDataClasses: ["pii", "source"],
24031
- sdks: {
24032
- npm: ["cohere-ai"],
24033
- pypi: ["cohere"],
24034
- go: ["github.com/cohere-ai/cohere-go"]
24035
- }
24036
- },
24037
- {
24038
- id: "mistral",
24039
- name: "Mistral AI",
24040
- category: "LLM provider",
24041
- hostSuffixes: ["mistral.ai"],
24042
- apiBase: "https://api.mistral.ai",
24043
- defaultDataClasses: ["pii", "source"],
24044
- sdks: {
24045
- npm: ["@mistralai/mistralai"],
24046
- pypi: ["mistralai"],
24047
- go: ["github.com/gage-technologies/mistral-go"]
24048
- }
24049
- }
24050
- ];
24051
- var EGRESS_VERSION_MATERIAL = `${EXTRACTOR_VERSION}
24052
- ${JSON.stringify(PROVIDER_REGISTRY)}`;
24053
-
24054
- // ../../packages/detections/src/egress/extract.ts
24055
- var SECRET_KEY_NAMES = "api[_-]?key|apikey|private[_-]?key|access[_-]?key|access[_-]?token|token|secret|credentials?|password|passwd|pwd|authorization|sig|signature|sas|assertion";
24056
- var AUTH_SCHEMES = "Bearer|Basic|Token|Digest|ApiKey|SSWS|AWS4-HMAC-SHA256";
24057
- var SECRET_VALUE = new RegExp(
24058
- `((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(?!(?:${AUTH_SCHEMES})[\\s'"\`])[^\\s'"\`&]+`,
24059
- "gi"
24060
- );
24061
- var AUTH_SCHEME_VALUE = new RegExp(
24062
- `((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(${AUTH_SCHEMES})\\s+[^\\s'"\`]+`,
24063
- "gi"
24064
- );
24065
- var WEBHOOK_SECRET_PATHS = [
24066
- { hosts: ["hooks.slack.com"], prefix: "/services/" },
24067
- {
24068
- hosts: ["discord.com", "discordapp.com", "ptb.discord.com", "canary.discord.com"],
24069
- prefix: "/api/webhooks/"
24070
- },
24071
- { hosts: ["hooks.zapier.com"], prefix: "/hooks/" },
24072
- { hosts: ["outlook.office.com", "outlook.office365.com"], prefix: "/webhook/" }
24073
- ];
24074
- function escapeRegExp(literal2) {
24075
- return literal2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24076
- }
24077
- var WEBHOOK_URL = new RegExp(
24078
- `(https?://(?:${WEBHOOK_SECRET_PATHS.flatMap(
24079
- (entry) => entry.hosts.map((host) => `${escapeRegExp(host)}${escapeRegExp(entry.prefix)}`)
24080
- ).join("|")}))[^\\s'"\`<>()[\\]{},;]+`,
24081
- "gi"
24082
- );
24083
-
24084
- // ../../packages/detections/src/escape-regexp.ts
24085
- function escapeRegExp2(value) {
24086
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24087
- }
24088
-
24089
- // ../../packages/detections/src/regex-cache.ts
24090
- var singles = /* @__PURE__ */ new WeakMap();
24091
- var keywordLists = /* @__PURE__ */ new WeakMap();
24092
- var labelLists = /* @__PURE__ */ new WeakMap();
24093
- function listCache(kind) {
24094
- return kind === "keyword" ? keywordLists : labelLists;
24095
- }
24096
- function memoizedRegExp(owner, build) {
24097
- const cached2 = singles.get(owner);
24098
- if (cached2 !== void 0) {
24099
- cached2.lastIndex = 0;
24100
- return cached2;
24101
- }
24102
- const compiled = build();
24103
- singles.set(owner, compiled);
24104
- return compiled;
24105
- }
24106
- function memoizedRegExpList(kind, owner, build) {
24107
- const cache = listCache(kind);
24108
- const cached2 = cache.get(owner);
24109
- if (cached2 !== void 0) {
24110
- if (cached2.stateful) {
24111
- for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
24112
- }
24113
- return cached2.entries;
24114
- }
24115
- const entries = build();
24116
- cache.set(owner, {
24117
- entries,
24118
- stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
24119
- });
24120
- return entries;
24121
- }
24122
-
24123
- // ../../packages/detections/src/matchers/limits.ts
24124
- var MAX_MATCHES_PER_RULE = 1e4;
24125
- var MAX_REGEX_INPUT_LENGTH = 2e5;
24126
-
24127
- // ../../packages/detections/src/matchers/keyword.ts
24128
- var KeywordMatcher2 = class {
24129
- match(text, rule) {
24130
- if (rule.matcher.type !== "keyword") return [];
24131
- const { keywords, caseSensitive } = rule.matcher;
24132
- const spans = [];
24133
- const compiled = memoizedRegExpList(
24134
- "keyword",
24135
- rule.matcher,
24136
- () => keywords.map((kw) => {
24137
- if (kw.length === 0) return void 0;
24138
- return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
24139
- })
24140
- );
24141
- for (const re of compiled) {
24142
- if (re === void 0) continue;
24143
- if (spans.length >= MAX_MATCHES_PER_RULE) break;
24144
- let m;
24145
- while ((m = re.exec(text)) !== null) {
24146
- spans.push({ start: m.index, end: m.index + m[0].length });
24147
- if (spans.length >= MAX_MATCHES_PER_RULE) break;
24148
- }
24149
- }
24150
- return spans;
24151
- }
24152
- };
24153
-
24154
- // ../../packages/detections/src/matchers/regex.ts
24155
- var RegexMatcher2 = class {
24156
- match(text, rule) {
24157
- if (rule.matcher.type !== "regex") return [];
24158
- const { pattern, flags, captureGroup } = rule.matcher;
24159
- const re = memoizedRegExp(
24160
- rule.matcher,
24161
- () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
24162
- );
24163
- const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
24164
- const spans = [];
24165
- let m;
24166
- const maxIterations = scanText2.length + 1;
24167
- let iterations = 0;
24168
- while ((m = re.exec(scanText2)) !== null) {
24169
- if (++iterations > maxIterations) break;
24170
- const group = captureGroup != null ? m[captureGroup] : m[0];
24171
- if (m[0].length === 0) re.lastIndex++;
24172
- if (group && spans.length < MAX_MATCHES_PER_RULE) {
24173
- const groupIndices = captureGroup != null ? m.indices?.[captureGroup] : void 0;
24174
- const start = groupIndices ? groupIndices[0] : m.index;
24175
- spans.push({ start, end: start + group.length });
24176
- }
24177
- if (!flags.includes("g") || spans.length >= MAX_MATCHES_PER_RULE) break;
24178
- }
24179
- return spans;
24180
- }
24181
- };
24182
-
24183
- // ../../packages/detections/src/engine.ts
24184
- var keywordMatcher = new KeywordMatcher2();
24185
- var regexMatcher = new RegexMatcher2();
24186
-
24187
- // ../../packages/detections/src/posture/config-posture.ts
24188
- var RULE_VERSION = "1";
24189
- var KNOWN_TOOLS = /* @__PURE__ */ new Set([
24190
- "prettier",
24191
- "eslint",
24192
- "biome",
24193
- "black",
24194
- "ruff",
24195
- "gofmt",
24196
- "rustfmt",
24197
- "node",
24198
- "npx",
24199
- "npm",
24200
- "pnpm",
24201
- "yarn",
24202
- "make",
24203
- "git",
24204
- "jq",
24205
- "echo"
24206
- ]);
24207
- var EGRESS_RE = /\b(curl|wget|nc|ncat|netcat|ssh|scp|rsync)\b|https?:\/\//;
24208
- var MUTATION_RE = /(--write|--fix|\s-w\b|\s-i\b|(?<![\d&>])>\s?\S)/;
24209
- var CONFIG_POSTURE_RULES = [
24210
- {
24211
- ruleId: "hook-conflict",
24212
- version: RULE_VERSION,
24213
- name: "Overlapping hooks \u2014 run order is undefined",
24214
- category: "config",
24215
- severity: "medium",
24216
- definition: JSON.stringify({
24217
- kind: "hook-conflict",
24218
- mutation: MUTATION_RE.source,
24219
- note: "two hooks on the same event with overlapping matchers, both mutating the tool target"
24220
- })
24514
+ id: "huggingface",
24515
+ name: "Hugging Face",
24516
+ category: "LLM provider",
24517
+ hostSuffixes: ["huggingface.co"],
24518
+ apiBase: "https://api-inference.huggingface.co",
24519
+ defaultDataClasses: ["source"],
24520
+ sdks: {
24521
+ npm: ["@huggingface/inference"],
24522
+ pypi: ["huggingface-hub", "transformers"],
24523
+ rubygems: ["hugging-face"]
24524
+ }
24221
24525
  },
24222
24526
  {
24223
- ruleId: "hook-unknown",
24224
- version: RULE_VERSION,
24225
- name: "Unknown hook \u2014 not attributable to a plugin or known tool",
24226
- category: "config",
24227
- // medium, not high: with a deliberately tiny allowlist this v1 heuristic
24228
- // fires on ordinary interpreter hooks (python3 x.py) and compound commands
24229
- // (cd … && tool); 'high' is reserved for egress, which is genuinely
24230
- // dangerous. Revisit when the known-hooks registry replaces the allowlist.
24231
- severity: "medium",
24232
- definition: JSON.stringify({ kind: "hook-unknown", knownTools: [...KNOWN_TOOLS] })
24527
+ id: "cohere",
24528
+ name: "Cohere",
24529
+ category: "LLM provider",
24530
+ hostSuffixes: ["cohere.com", "cohere.ai"],
24531
+ apiBase: "https://api.cohere.com",
24532
+ defaultDataClasses: ["pii", "source"],
24533
+ sdks: {
24534
+ npm: ["cohere-ai"],
24535
+ pypi: ["cohere"],
24536
+ go: ["github.com/cohere-ai/cohere-go"]
24537
+ }
24233
24538
  },
24234
24539
  {
24235
- ruleId: "hook-external-egress",
24236
- version: RULE_VERSION,
24237
- name: "Hook command can send data externally",
24238
- category: "config",
24239
- severity: "high",
24240
- definition: JSON.stringify({ kind: "hook-external-egress", pattern: EGRESS_RE.source })
24241
- }
24242
- ];
24243
-
24244
- // ../../packages/detections/src/security/redos-probe.ts
24245
- var BUDGET_MS = 100;
24246
- var EXPONENTIAL_UNITS = [
24247
- "a",
24248
- "0",
24249
- " ",
24250
- "x",
24251
- "ab",
24252
- "a.",
24253
- "a-",
24254
- "a_",
24255
- "a@",
24256
- "a/",
24257
- "a:",
24258
- "a=",
24259
- "a;",
24260
- "aA0",
24261
- " "
24262
- ];
24263
- var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
24264
- (unit) => [23, 25].map((len) => unit.repeat(Math.ceil(len / unit.length)).slice(0, len) + "!")
24265
- );
24266
- var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
24267
- (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
24268
- );
24269
- var CPU_CORROBORATION_SHARE = 0.2;
24270
- var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
24271
-
24272
- // ../../packages/plugin-sdk/src/repo.ts
24273
- import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
24274
- import { basename as basename2, dirname as dirname3, isAbsolute, join as join14, sep as sep2 } from "path";
24275
-
24276
- // ../../packages/plugin-sdk/src/events.ts
24277
- import { createHash as createHash5, randomUUID as randomUUID13 } from "crypto";
24278
-
24279
- // ../../packages/plugin-sdk/src/isolated-scan.ts
24280
- import { existsSync as existsSync9 } from "fs";
24281
- import { fileURLToPath } from "url";
24282
- import { Worker } from "worker_threads";
24283
-
24284
- // ../../packages/plugin-sdk/src/ignore-layers.ts
24285
- var import_ignore = __toESM(require_ignore(), 1);
24286
- import { readFileSync as readFileSync10 } from "fs";
24287
- import { join as join16 } from "path";
24288
-
24289
- // ../../packages/plugin-sdk/src/inventory-resolver.ts
24290
- import { arch, hostname as hostname4, platform, release } from "os";
24291
-
24292
- // ../../packages/plugin-sdk/src/model-governance.ts
24293
- import {
24294
- closeSync as closeSync2,
24295
- fstatSync,
24296
- mkdirSync as mkdirSync2,
24297
- openSync as openSync2,
24298
- readFileSync as readFileSync11,
24299
- readSync,
24300
- writeFileSync as writeFileSync5
24301
- } from "fs";
24302
- import { join as join17 } from "path";
24303
- var TAIL_BYTES = 256 * 1024;
24304
-
24305
- // ../../packages/plugin-sdk/src/nudge.ts
24306
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
24307
- import { join as join18 } from "path";
24308
-
24309
- // ../../packages/plugin-sdk/src/paths.ts
24310
- import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
24311
- import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
24312
-
24313
- // ../../packages/plugin-sdk/src/project-files.ts
24314
- import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
24315
- import { basename as basename5, join as join19 } from "path";
24316
-
24317
- // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
24318
- var optionalBaseUrl2 = external_exports.preprocess((v) => {
24319
- if (typeof v === "string" && v.trim() === "") return void 0;
24320
- return v;
24321
- }, external_exports.string().optional()).catch(void 0);
24322
- var optionalFlag = external_exports.preprocess((v) => {
24323
- if (typeof v !== "string") return false;
24324
- const normalized = v.trim().toLowerCase();
24325
- return normalized !== "" && normalized !== "0" && normalized !== "false";
24326
- }, external_exports.boolean()).catch(false);
24327
- var antigravityProviderEnvShape = {
24328
- GOOGLE_GENAI_USE_VERTEXAI: optionalFlag,
24329
- GOOGLE_GEMINI_BASE_URL: optionalBaseUrl2
24330
- };
24331
- var AntigravityProviderEnvSchema = external_exports.object(antigravityProviderEnvShape);
24332
-
24333
- // ../../packages/plugin-sdk/src/provider-env-codex.ts
24334
- var optionalBaseUrl3 = external_exports.preprocess((v) => {
24335
- if (typeof v === "string" && v.trim() === "") return void 0;
24336
- return v;
24337
- }, external_exports.string().optional()).catch(void 0);
24338
- var codexProviderEnvShape = {
24339
- OPENAI_BASE_URL: optionalBaseUrl3
24340
- };
24341
- var CodexProviderEnvSchema = external_exports.object(codexProviderEnvShape);
24342
-
24343
- // ../../packages/plugin-sdk/src/runtime.ts
24344
- import { randomUUID as randomUUID14 } from "crypto";
24345
-
24346
- // ../../packages/plugin-sdk/src/suppressions.ts
24347
- var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
24348
-
24349
- // ../../packages/plugin-sdk/src/throttle.ts
24350
- import { mkdirSync as mkdirSync4, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
24351
- import { join as join20 } from "path";
24352
-
24353
- // ../../packages/plugin-runtime/src/attached/with-timeout.ts
24354
- function withTimeout(promise2, ms) {
24355
- let timer;
24356
- const timeout = new Promise((_, reject) => {
24357
- timer = setTimeout(() => {
24358
- reject(new Error("attached gateway request timed out"));
24359
- }, ms);
24360
- });
24361
- promise2.catch(() => void 0);
24362
- return Promise.race([promise2, timeout]).finally(() => {
24363
- clearTimeout(timer);
24364
- });
24365
- }
24366
-
24367
- // ../../packages/plugin-runtime/src/attached/history-state.ts
24368
- import { readFileSync as readFileSync14 } from "fs";
24369
- import { join as join22 } from "path";
24370
-
24371
- // ../../packages/plugin-runtime/src/attached/history-sync.ts
24372
- import { createHash as createHash6 } from "crypto";
24373
- import { hostname as hostname5 } from "os";
24374
-
24375
- // ../../packages/remote/src/http.ts
24376
- import { request as httpRequest } from "http";
24377
- import { request as httpsRequest } from "https";
24378
- var DEFAULT_TIMEOUT_MS = 1e4;
24379
- var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
24380
- var RemoteRequestError = class extends Error {
24381
- constructor(status) {
24382
- super(`control-plane request failed with status ${String(status)}`);
24383
- this.status = status;
24384
- this.name = "RemoteRequestError";
24385
- }
24386
- status;
24387
- };
24388
- var RemoteRequestInvalid = class extends Error {
24389
- constructor(route, cause) {
24390
- super(`refusing to send a malformed body to ${route}`);
24391
- this.cause = cause;
24392
- this.name = "RemoteRequestInvalid";
24393
- }
24394
- cause;
24395
- };
24396
- var RemoteResponseInvalid = class extends Error {
24397
- constructor(route, detail) {
24398
- super(`control plane answered ${route} with ${detail}`);
24399
- this.name = "RemoteResponseInvalid";
24400
- }
24401
- };
24402
- var RemoteTransportError = class extends Error {
24403
- /**
24404
- * The status the peer sent, when headers arrived and only the BODY was
24405
- * refused.
24406
- *
24407
- * Undefined for the ordinary case this class was written for — no answer at
24408
- * all. It exists because two paths reject after a status has already been
24409
- * delivered: an oversized body and an aborted response. Discarding it there
24410
- * reported a deployment answering 401 with a verbose body as a network
24411
- * outage, which sends the reader to look at their network instead of their
24412
- * credential.
24413
- */
24414
- constructor(reason, status) {
24415
- super(`control-plane request did not complete: ${reason}`);
24416
- this.status = status;
24417
- this.name = "RemoteTransportError";
24418
- }
24419
- status;
24420
- };
24421
- async function send(options) {
24422
- const url2 = new URL(options.url);
24423
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
24424
- const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
24425
- const requestOptions = {
24426
- method: options.method,
24427
- headers: {
24428
- // CALLER HEADERS FIRST, so this module's own are not overridable. Spread
24429
- // last they win, and two of the values below are ones no caller may
24430
- // replace: `x-api-key` is the credential, and `content-length` is the
24431
- // byte count that stops a multi-byte body being truncated by the
24432
- // receiver. `SendOptions.headers` is a free-form record on an exported
24433
- // function, so "no caller does that today" is not the guarantee to rely
24434
- // on. The one header any caller actually passes — `if-none-match` on the
24435
- // conditional GET — is untouched by this order.
24436
- ...options.headers,
24437
- // The credential. One header, matching what the deployment authenticates
24438
- // on; a second copy in an `Authorization` header would be one more place
24439
- // it can be logged by an intermediary for no gain.
24440
- //
24441
- // Spread conditionally rather than assigned as `undefined`: Node's header
24442
- // handling and `content-length` bookkeeping treat a present-but-undefined
24443
- // key differently from an absent one, and "the header is not there" is
24444
- // the property the attach flow needs.
24445
- ...options.apiKey === void 0 ? {} : { "x-api-key": options.apiKey },
24446
- accept: "application/json",
24447
- ...options.body === void 0 ? {} : {
24448
- "content-type": "application/json",
24449
- // Byte length, not string length: a multi-byte body sent with a
24450
- // character count is truncated by the receiver.
24451
- "content-length": String(Buffer.byteLength(options.body))
24452
- }
24540
+ id: "mistral",
24541
+ name: "Mistral AI",
24542
+ category: "LLM provider",
24543
+ hostSuffixes: ["mistral.ai"],
24544
+ apiBase: "https://api.mistral.ai",
24545
+ defaultDataClasses: ["pii", "source"],
24546
+ sdks: {
24547
+ npm: ["@mistralai/mistralai"],
24548
+ pypi: ["mistralai"],
24549
+ go: ["github.com/gage-technologies/mistral-go"]
24453
24550
  }
24454
- };
24455
- return new Promise((resolve2, reject) => {
24456
- let settled = false;
24457
- const fail = (reason, status) => {
24458
- if (settled) return;
24459
- settled = true;
24460
- reject(new RemoteTransportError(reason, status));
24461
- };
24462
- const req = send_(url2, requestOptions, (res) => {
24463
- const chunks = [];
24464
- let size = 0;
24465
- res.on("data", (chunk) => {
24466
- size += chunk.length;
24467
- if (size > MAX_RESPONSE_BYTES) {
24468
- fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
24469
- res.destroy();
24470
- req.destroy();
24471
- return;
24472
- }
24473
- chunks.push(chunk);
24474
- });
24475
- res.on("aborted", () => {
24476
- fail("the response was aborted", res.statusCode);
24477
- });
24478
- res.on("end", () => {
24479
- if (settled) return;
24480
- settled = true;
24481
- resolve2({
24482
- status: res.statusCode ?? 0,
24483
- headers: res.headers,
24484
- body: Buffer.concat(chunks).toString("utf8")
24485
- });
24486
- });
24487
- });
24488
- const deadline = setTimeout(() => {
24489
- fail(`no response within ${String(timeoutMs)}ms`);
24490
- req.destroy();
24491
- }, timeoutMs);
24492
- deadline.unref();
24493
- req.on("upgrade", (_res, socket) => {
24494
- fail("the deployment answered with a protocol upgrade");
24495
- socket.destroy();
24496
- });
24497
- req.on("close", () => {
24498
- fail("the connection closed before a response was read");
24499
- clearTimeout(deadline);
24500
- });
24501
- req.on("error", (err) => {
24502
- fail(err.message);
24503
- });
24504
- if (options.body !== void 0) req.write(options.body);
24505
- req.end();
24506
- });
24551
+ }
24552
+ ];
24553
+ var EGRESS_VERSION_MATERIAL = `${EXTRACTOR_VERSION}
24554
+ ${JSON.stringify(PROVIDER_REGISTRY)}`;
24555
+
24556
+ // ../../packages/detections/src/egress/extract.ts
24557
+ var SECRET_KEY_NAMES = "api[_-]?key|apikey|private[_-]?key|access[_-]?key|access[_-]?token|token|secret|credentials?|password|passwd|pwd|authorization|sig|signature|sas|assertion";
24558
+ var AUTH_SCHEMES = "Bearer|Basic|Token|Digest|ApiKey|SSWS|AWS4-HMAC-SHA256";
24559
+ var SECRET_VALUE = new RegExp(
24560
+ `((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(?!(?:${AUTH_SCHEMES})[\\s'"\`])[^\\s'"\`&]+`,
24561
+ "gi"
24562
+ );
24563
+ var AUTH_SCHEME_VALUE = new RegExp(
24564
+ `((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(${AUTH_SCHEMES})\\s+[^\\s'"\`]+`,
24565
+ "gi"
24566
+ );
24567
+ var WEBHOOK_SECRET_PATHS = [
24568
+ { hosts: ["hooks.slack.com"], prefix: "/services/" },
24569
+ {
24570
+ hosts: ["discord.com", "discordapp.com", "ptb.discord.com", "canary.discord.com"],
24571
+ prefix: "/api/webhooks/"
24572
+ },
24573
+ { hosts: ["hooks.zapier.com"], prefix: "/hooks/" },
24574
+ { hosts: ["outlook.office.com", "outlook.office365.com"], prefix: "/webhook/" }
24575
+ ];
24576
+ function escapeRegExp(literal2) {
24577
+ return literal2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24507
24578
  }
24579
+ var WEBHOOK_URL = new RegExp(
24580
+ `(https?://(?:${WEBHOOK_SECRET_PATHS.flatMap(
24581
+ (entry) => entry.hosts.map((host) => `${escapeRegExp(host)}${escapeRegExp(entry.prefix)}`)
24582
+ ).join("|")}))[^\\s'"\`<>()[\\]{},;]+`,
24583
+ "gi"
24584
+ );
24508
24585
 
24509
- // ../../packages/remote/src/client.ts
24510
- var ROUTES = {
24511
- events: "/v1/events",
24512
- auditEvents: "/v1/audit-events",
24513
- auditEventsBatch: "/v1/audit-events/batch",
24514
- inventory: "/v1/inventory",
24515
- storePosture: "/v1/store-posture",
24516
- policyBundle: "/v1/policy-bundle",
24517
- whoami: "/v1/plugin/whoami",
24518
- shares: "/v1/shares"
24519
- };
24520
- function headerValue(response, name) {
24521
- const raw = response.headers[name];
24522
- if (raw === void 0) return void 0;
24523
- return Array.isArray(raw) ? raw[0] : raw;
24586
+ // ../../packages/detections/src/escape-regexp.ts
24587
+ function escapeRegExp2(value) {
24588
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24524
24589
  }
24525
- function okBody(response) {
24526
- if (response.status < 200 || response.status >= 300) {
24527
- throw new RemoteRequestError(response.status);
24528
- }
24529
- return response.body;
24590
+
24591
+ // ../../packages/detections/src/regex-cache.ts
24592
+ var singles = /* @__PURE__ */ new WeakMap();
24593
+ var keywordLists = /* @__PURE__ */ new WeakMap();
24594
+ var labelLists = /* @__PURE__ */ new WeakMap();
24595
+ function listCache(kind) {
24596
+ return kind === "keyword" ? keywordLists : labelLists;
24530
24597
  }
24531
- function parsed(schema, body, route) {
24532
- let json2;
24533
- try {
24534
- json2 = JSON.parse(body);
24535
- } catch {
24536
- throw new RemoteResponseInvalid(route, "a body that is not JSON");
24537
- }
24538
- const result = schema.safeParse(json2);
24539
- if (!result.success) {
24540
- throw new RemoteResponseInvalid(route, "a body this client cannot read");
24598
+ function memoizedRegExp(owner, build) {
24599
+ const cached2 = singles.get(owner);
24600
+ if (cached2 !== void 0) {
24601
+ cached2.lastIndex = 0;
24602
+ return cached2;
24541
24603
  }
24542
- return result.data;
24604
+ const compiled = build();
24605
+ singles.set(owner, compiled);
24606
+ return compiled;
24543
24607
  }
24544
- function withoutTrailingSlashes(endpoint) {
24545
- let end = endpoint.length;
24546
- while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
24547
- return endpoint.slice(0, end);
24608
+ function memoizedRegExpList(kind, owner, build) {
24609
+ const cache = listCache(kind);
24610
+ const cached2 = cache.get(owner);
24611
+ if (cached2 !== void 0) {
24612
+ if (cached2.stateful) {
24613
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
24614
+ }
24615
+ return cached2.entries;
24616
+ }
24617
+ const entries = build();
24618
+ cache.set(owner, {
24619
+ entries,
24620
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
24621
+ });
24622
+ return entries;
24548
24623
  }
24549
- var SLASH = "/".charCodeAt(0);
24550
- function createRemoteClient(options) {
24551
- const base = withoutTrailingSlashes(options.endpoint);
24552
- const url2 = (route) => `${base}${route}`;
24553
- const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
24554
- const sendOne = async (event) => {
24555
- const validated = RecordAuditEventRequest.safeParse(event);
24556
- if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
24557
- const response = await send({
24558
- ...common,
24559
- method: "POST",
24560
- url: url2(ROUTES.auditEvents),
24561
- body: JSON.stringify(validated.data)
24562
- });
24563
- okBody(response);
24564
- };
24565
- return {
24566
- async ingestEvents(batch) {
24567
- const response = await send({
24568
- ...common,
24569
- method: "POST",
24570
- url: url2(ROUTES.events),
24571
- body: JSON.stringify(batch)
24572
- });
24573
- return parsed(IngestAck, okBody(response), ROUTES.events);
24574
- },
24575
- async ingestInventory(context) {
24576
- const response = await send({
24577
- ...common,
24578
- method: "POST",
24579
- url: url2(ROUTES.inventory),
24580
- body: JSON.stringify(context)
24581
- });
24582
- return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
24583
- },
24584
- async recordAuditEvent(event) {
24585
- await sendOne(event);
24586
- },
24587
- async recordAuditEvents(events) {
24588
- const validated = RecordAuditEventBatch.safeParse({ events });
24589
- if (!validated.success) {
24590
- throw new RemoteRequestInvalid(ROUTES.auditEventsBatch, validated.error);
24591
- }
24592
- const response = await send({
24593
- ...common,
24594
- method: "POST",
24595
- url: url2(ROUTES.auditEventsBatch),
24596
- body: JSON.stringify(validated.data)
24597
- });
24598
- if (response.status === 404) {
24599
- for (const event of validated.data.events) await sendOne(event);
24600
- return { accepted: validated.data.events.length };
24601
- }
24602
- return parsed(AuditEventBatchAck, okBody(response), ROUTES.auditEventsBatch);
24603
- },
24604
- async reportStorePosture(snapshot) {
24605
- const response = await send({
24606
- ...common,
24607
- method: "POST",
24608
- url: url2(ROUTES.storePosture),
24609
- body: JSON.stringify(snapshot)
24610
- });
24611
- okBody(response);
24612
- },
24613
- async getPolicyBundle(etag) {
24614
- const response = await send({
24615
- ...common,
24616
- method: "GET",
24617
- url: url2(ROUTES.policyBundle),
24618
- ...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
24619
- });
24620
- if (response.status === 304) {
24621
- return { changed: false, etag: headerValue(response, "etag") ?? etag };
24624
+
24625
+ // ../../packages/detections/src/matchers/limits.ts
24626
+ var MAX_MATCHES_PER_RULE = 1e4;
24627
+ var MAX_REGEX_INPUT_LENGTH = 2e5;
24628
+
24629
+ // ../../packages/detections/src/matchers/keyword.ts
24630
+ var KeywordMatcher2 = class {
24631
+ match(text, rule) {
24632
+ if (rule.matcher.type !== "keyword") return [];
24633
+ const { keywords, caseSensitive } = rule.matcher;
24634
+ const spans = [];
24635
+ const compiled = memoizedRegExpList(
24636
+ "keyword",
24637
+ rule.matcher,
24638
+ () => keywords.map((kw) => {
24639
+ if (kw.length === 0) return void 0;
24640
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
24641
+ })
24642
+ );
24643
+ for (const re of compiled) {
24644
+ if (re === void 0) continue;
24645
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
24646
+ let m;
24647
+ while ((m = re.exec(text)) !== null) {
24648
+ spans.push({ start: m.index, end: m.index + m[0].length });
24649
+ if (spans.length >= MAX_MATCHES_PER_RULE) break;
24622
24650
  }
24623
- const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
24624
- return { changed: true, bundle, etag: headerValue(response, "etag") };
24625
- },
24626
- async whoami() {
24627
- const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
24628
- return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
24629
- },
24630
- async recordProjectEgress(request) {
24631
- const validated = EgressIngestRequest.safeParse(request);
24632
- if (!validated.success) throw new RemoteRequestInvalid(ROUTES.shares, validated.error);
24633
- const response = await send({
24634
- ...common,
24635
- method: "POST",
24636
- url: url2(ROUTES.shares),
24637
- body: JSON.stringify(validated.data)
24638
- });
24639
- okBody(response);
24640
24651
  }
24641
- };
24642
- }
24652
+ return spans;
24653
+ }
24654
+ };
24655
+
24656
+ // ../../packages/detections/src/matchers/regex.ts
24657
+ var RegexMatcher2 = class {
24658
+ match(text, rule) {
24659
+ if (rule.matcher.type !== "regex") return [];
24660
+ const { pattern, flags, captureGroup } = rule.matcher;
24661
+ const re = memoizedRegExp(
24662
+ rule.matcher,
24663
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
24664
+ );
24665
+ const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
24666
+ const spans = [];
24667
+ let m;
24668
+ const maxIterations = scanText2.length + 1;
24669
+ let iterations = 0;
24670
+ while ((m = re.exec(scanText2)) !== null) {
24671
+ if (++iterations > maxIterations) break;
24672
+ const group = captureGroup != null ? m[captureGroup] : m[0];
24673
+ if (m[0].length === 0) re.lastIndex++;
24674
+ if (group && spans.length < MAX_MATCHES_PER_RULE) {
24675
+ const groupIndices = captureGroup != null ? m.indices?.[captureGroup] : void 0;
24676
+ const start = groupIndices ? groupIndices[0] : m.index;
24677
+ spans.push({ start, end: start + group.length });
24678
+ }
24679
+ if (!flags.includes("g") || spans.length >= MAX_MATCHES_PER_RULE) break;
24680
+ }
24681
+ return spans;
24682
+ }
24683
+ };
24684
+
24685
+ // ../../packages/detections/src/engine.ts
24686
+ var keywordMatcher = new KeywordMatcher2();
24687
+ var regexMatcher = new RegexMatcher2();
24688
+
24689
+ // ../../packages/detections/src/posture/config-posture.ts
24690
+ var RULE_VERSION = "1";
24691
+ var KNOWN_TOOLS = /* @__PURE__ */ new Set([
24692
+ "prettier",
24693
+ "eslint",
24694
+ "biome",
24695
+ "black",
24696
+ "ruff",
24697
+ "gofmt",
24698
+ "rustfmt",
24699
+ "node",
24700
+ "npx",
24701
+ "npm",
24702
+ "pnpm",
24703
+ "yarn",
24704
+ "make",
24705
+ "git",
24706
+ "jq",
24707
+ "echo"
24708
+ ]);
24709
+ var EGRESS_RE = /\b(curl|wget|nc|ncat|netcat|ssh|scp|rsync)\b|https?:\/\//;
24710
+ var MUTATION_RE = /(--write|--fix|\s-w\b|\s-i\b|(?<![\d&>])>\s?\S)/;
24711
+ var CONFIG_POSTURE_RULES = [
24712
+ {
24713
+ ruleId: "hook-conflict",
24714
+ version: RULE_VERSION,
24715
+ name: "Overlapping hooks \u2014 run order is undefined",
24716
+ category: "config",
24717
+ severity: "medium",
24718
+ definition: JSON.stringify({
24719
+ kind: "hook-conflict",
24720
+ mutation: MUTATION_RE.source,
24721
+ note: "two hooks on the same event with overlapping matchers, both mutating the tool target"
24722
+ })
24723
+ },
24724
+ {
24725
+ ruleId: "hook-unknown",
24726
+ version: RULE_VERSION,
24727
+ name: "Unknown hook \u2014 not attributable to a plugin or known tool",
24728
+ category: "config",
24729
+ // medium, not high: with a deliberately tiny allowlist this v1 heuristic
24730
+ // fires on ordinary interpreter hooks (python3 x.py) and compound commands
24731
+ // (cd … && tool); 'high' is reserved for egress, which is genuinely
24732
+ // dangerous. Revisit when the known-hooks registry replaces the allowlist.
24733
+ severity: "medium",
24734
+ definition: JSON.stringify({ kind: "hook-unknown", knownTools: [...KNOWN_TOOLS] })
24735
+ },
24736
+ {
24737
+ ruleId: "hook-external-egress",
24738
+ version: RULE_VERSION,
24739
+ name: "Hook command can send data externally",
24740
+ category: "config",
24741
+ severity: "high",
24742
+ definition: JSON.stringify({ kind: "hook-external-egress", pattern: EGRESS_RE.source })
24743
+ }
24744
+ ];
24745
+
24746
+ // ../../packages/detections/src/security/redos-probe.ts
24747
+ var BUDGET_MS = 100;
24748
+ var EXPONENTIAL_UNITS = [
24749
+ "a",
24750
+ "0",
24751
+ " ",
24752
+ "x",
24753
+ "ab",
24754
+ "a.",
24755
+ "a-",
24756
+ "a_",
24757
+ "a@",
24758
+ "a/",
24759
+ "a:",
24760
+ "a=",
24761
+ "a;",
24762
+ "aA0",
24763
+ " "
24764
+ ];
24765
+ var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
24766
+ (unit) => [23, 25].map((len) => unit.repeat(Math.ceil(len / unit.length)).slice(0, len) + "!")
24767
+ );
24768
+ var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
24769
+ (unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
24770
+ );
24771
+ var CPU_CORROBORATION_SHARE = 0.2;
24772
+ var CORROBORATION_FLOOR_MS = BUDGET_MS * CPU_CORROBORATION_SHARE;
24773
+
24774
+ // ../../packages/plugin-sdk/src/repo.ts
24775
+ import { existsSync as existsSync8, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
24776
+ import { basename as basename2, dirname as dirname4, isAbsolute, join as join15, sep as sep2 } from "path";
24777
+
24778
+ // ../../packages/plugin-sdk/src/events.ts
24779
+ import { createHash as createHash5, randomUUID as randomUUID13 } from "crypto";
24780
+
24781
+ // ../../packages/plugin-sdk/src/isolated-scan.ts
24782
+ import { existsSync as existsSync9 } from "fs";
24783
+ import { fileURLToPath } from "url";
24784
+ import { Worker } from "worker_threads";
24785
+
24786
+ // ../../packages/plugin-sdk/src/ignore-layers.ts
24787
+ var import_ignore = __toESM(require_ignore(), 1);
24788
+ import { readFileSync as readFileSync11 } from "fs";
24789
+ import { join as join17 } from "path";
24790
+
24791
+ // ../../packages/plugin-sdk/src/inventory-resolver.ts
24792
+ import { arch, hostname as hostname4, platform, release } from "os";
24793
+
24794
+ // ../../packages/plugin-sdk/src/model-governance.ts
24795
+ import {
24796
+ closeSync as closeSync2,
24797
+ fstatSync,
24798
+ mkdirSync as mkdirSync2,
24799
+ openSync as openSync2,
24800
+ readFileSync as readFileSync12,
24801
+ readSync,
24802
+ writeFileSync as writeFileSync5
24803
+ } from "fs";
24804
+ import { join as join18 } from "path";
24805
+ var TAIL_BYTES = 256 * 1024;
24806
+
24807
+ // ../../packages/plugin-sdk/src/nudge.ts
24808
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
24809
+ import { join as join19 } from "path";
24810
+
24811
+ // ../../packages/plugin-sdk/src/paths.ts
24812
+ import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
24813
+ import { basename as basename4, dirname as dirname5, sep as sep3 } from "path";
24814
+
24815
+ // ../../packages/plugin-sdk/src/project-files.ts
24816
+ import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
24817
+ import { basename as basename5, join as join20 } from "path";
24818
+
24819
+ // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
24820
+ var optionalBaseUrl2 = external_exports.preprocess((v) => {
24821
+ if (typeof v === "string" && v.trim() === "") return void 0;
24822
+ return v;
24823
+ }, external_exports.string().optional()).catch(void 0);
24824
+ var optionalFlag = external_exports.preprocess((v) => {
24825
+ if (typeof v !== "string") return false;
24826
+ const normalized = v.trim().toLowerCase();
24827
+ return normalized !== "" && normalized !== "0" && normalized !== "false";
24828
+ }, external_exports.boolean()).catch(false);
24829
+ var antigravityProviderEnvShape = {
24830
+ GOOGLE_GENAI_USE_VERTEXAI: optionalFlag,
24831
+ GOOGLE_GEMINI_BASE_URL: optionalBaseUrl2
24832
+ };
24833
+ var AntigravityProviderEnvSchema = external_exports.object(antigravityProviderEnvShape);
24834
+
24835
+ // ../../packages/plugin-sdk/src/provider-env-codex.ts
24836
+ var optionalBaseUrl3 = external_exports.preprocess((v) => {
24837
+ if (typeof v === "string" && v.trim() === "") return void 0;
24838
+ return v;
24839
+ }, external_exports.string().optional()).catch(void 0);
24840
+ var codexProviderEnvShape = {
24841
+ OPENAI_BASE_URL: optionalBaseUrl3
24842
+ };
24843
+ var CodexProviderEnvSchema = external_exports.object(codexProviderEnvShape);
24844
+
24845
+ // ../../packages/plugin-sdk/src/runtime.ts
24846
+ import { randomUUID as randomUUID14 } from "crypto";
24847
+
24848
+ // ../../packages/plugin-sdk/src/suppressions.ts
24849
+ var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
24850
+
24851
+ // ../../packages/plugin-sdk/src/throttle.ts
24852
+ import { mkdirSync as mkdirSync4, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
24853
+ import { join as join21 } from "path";
24854
+
24855
+ // ../../packages/plugin-runtime/src/attached/history-state.ts
24856
+ import { readFileSync as readFileSync15 } from "fs";
24857
+ import { join as join23 } from "path";
24858
+
24859
+ // ../../packages/plugin-runtime/src/attached/history-sync.ts
24860
+ import { createHash as createHash6 } from "crypto";
24861
+ import { hostname as hostname5 } from "os";
24862
+
24863
+ // ../../packages/plugin-runtime/src/attached/capture-rebuild.ts
24864
+ var CORRELATION_ID = EventMetadata.shape.correlationId;
24865
+ var TRACE_ID = EventMetadata.shape.traceId;
24866
+ var EXCEPTION_ID = EventMetadata.shape.exceptionIds.unwrap().element;
24643
24867
 
24644
24868
  // ../../packages/plugin-runtime/src/attached/history-sync-trigger.ts
24645
24869
  import { spawn } from "child_process";
@@ -24647,12 +24871,12 @@ import { fileURLToPath as fileURLToPath2 } from "url";
24647
24871
  var HISTORY_SYNC_THROTTLE_MS = 5 * 60 * 1e3;
24648
24872
 
24649
24873
  // ../../packages/plugin-runtime/src/attached/plugin-block.ts
24650
- import { readFileSync as readFileSync15 } from "fs";
24874
+ import { readFileSync as readFileSync16 } from "fs";
24651
24875
 
24652
24876
  // ../../packages/plugin-runtime/src/attached/policy-store.ts
24653
24877
  import { randomUUID as randomUUID16 } from "crypto";
24654
24878
  import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
24655
- import { join as join23 } from "path";
24879
+ import { join as join24 } from "path";
24656
24880
 
24657
24881
  // ../../packages/plugin-runtime/src/attached/atomic-publish.ts
24658
24882
  import { rename as rename2 } from "fs/promises";
@@ -24676,7 +24900,7 @@ async function publishByRename(tmp, file2, move = rename2) {
24676
24900
 
24677
24901
  // ../../packages/plugin-runtime/src/attached/policy-store.ts
24678
24902
  function createPolicyStore(dir = dataDir()) {
24679
- const file2 = join23(dir, "policy-cache.json");
24903
+ const file2 = join24(dir, "policy-cache.json");
24680
24904
  async function read() {
24681
24905
  try {
24682
24906
  const raw = await readFile2(file2, "utf8");
@@ -24685,22 +24909,32 @@ function createPolicyStore(dir = dataDir()) {
24685
24909
  const record2 = parsed2;
24686
24910
  const bundle = PolicyBundle.parse(record2.bundle);
24687
24911
  const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
24688
- const etag = typeof record2.etag === "string" ? record2.etag : void 0;
24912
+ const stored = typeof record2.etag === "string" ? record2.etag : void 0;
24913
+ const replayable = record2.shapeId === POLICY_BUNDLE_SHAPE_ID || knowsMoreThanThisBuild(record2.shapeId);
24914
+ const etag = replayable ? stored : void 0;
24689
24915
  return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
24690
24916
  } catch {
24691
24917
  return null;
24692
24918
  }
24693
24919
  }
24694
- async function write(bundle, etag) {
24695
- await ensureDataDir(dir);
24696
- const stored = {
24697
- bundle,
24698
- fetchedAtMs: Date.now(),
24699
- ...etag === void 0 ? {} : { etag }
24700
- };
24920
+ function knowsMoreThanThisBuild(shapeId) {
24921
+ if (typeof shapeId !== "string" || shapeId === "") return false;
24922
+ const theirs = new Set(shapeId.split(","));
24923
+ const ours = new Set(POLICY_BUNDLE_SHAPE_ID.split(","));
24924
+ return theirs.size > ours.size && [...ours].every((key) => theirs.has(key));
24925
+ }
24926
+ async function priorRecord() {
24927
+ try {
24928
+ const parsed2 = JSON.parse(await readFile2(file2, "utf8"));
24929
+ return typeof parsed2 === "object" && parsed2 !== null ? parsed2 : null;
24930
+ } catch {
24931
+ return null;
24932
+ }
24933
+ }
24934
+ async function publishRecord(record2) {
24701
24935
  const tmp = `${file2}.${randomUUID16()}.tmp`;
24702
24936
  try {
24703
- await writeFile2(tmp, JSON.stringify(stored), {
24937
+ await writeFile2(tmp, JSON.stringify(record2), {
24704
24938
  encoding: "utf8",
24705
24939
  mode: DATA_FILE_MODE,
24706
24940
  flag: "wx"
@@ -24711,6 +24945,27 @@ function createPolicyStore(dir = dataDir()) {
24711
24945
  throw err;
24712
24946
  }
24713
24947
  }
24948
+ async function write(bundle, etag) {
24949
+ await ensureDataDir(dir);
24950
+ const prior = await priorRecord();
24951
+ const priorVersion = prior?.bundle?.version;
24952
+ if (prior !== null && knowsMoreThanThisBuild(prior.shapeId) && priorVersion === bundle.version) {
24953
+ await publishRecord({
24954
+ ...prior,
24955
+ fetchedAtMs: Date.now()
24956
+ });
24957
+ return;
24958
+ }
24959
+ await publishRecord({
24960
+ bundle,
24961
+ fetchedAtMs: Date.now(),
24962
+ // Stamped on EVERY write, the 304 arm's included: that arm hands back the
24963
+ // bundle it already holds, and the point of the stamp is to describe the
24964
+ // build that last narrowed those bytes, which is this one.
24965
+ shapeId: POLICY_BUNDLE_SHAPE_ID,
24966
+ ...etag === void 0 ? {} : { etag }
24967
+ });
24968
+ }
24714
24969
  return { read, write, file: file2 };
24715
24970
  }
24716
24971
 
@@ -24773,14 +25028,14 @@ import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
24773
25028
  // ../../packages/plugin-runtime/src/attached/posture-store.ts
24774
25029
  import { randomUUID as randomUUID17 } from "crypto";
24775
25030
  import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
24776
- import { join as join24 } from "path";
25031
+ import { join as join25 } from "path";
24777
25032
 
24778
25033
  // ../../packages/plugin-runtime/src/attached/sync-state.ts
24779
- import { readFileSync as readFileSync16 } from "fs";
24780
- import { join as join25 } from "path";
25034
+ import { readFileSync as readFileSync17 } from "fs";
25035
+ import { join as join26 } from "path";
24781
25036
  var SYNC_STATE_FILENAME = ATTACHED_SYNC_STATE_FILENAME;
24782
25037
  function syncStatePath(dataDir2) {
24783
- return join25(dataDir2, SYNC_STATE_FILENAME);
25038
+ return join26(dataDir2, SYNC_STATE_FILENAME);
24784
25039
  }
24785
25040
  function writeSyncState(dataDir2, result) {
24786
25041
  try {
@@ -24807,14 +25062,23 @@ var OUTCOME_LINES = {
24807
25062
  };
24808
25063
 
24809
25064
  // ../../packages/plugin-runtime/src/attached/sync-entry.ts
24810
- async function runAttachedSync(base = defaultDataDir()) {
25065
+ async function runAttachedSync(base = defaultDataDir(), deps = {}) {
25066
+ let policyOutcome = null;
24811
25067
  try {
24812
25068
  const result = await runPolicySync({
24813
25069
  base,
24814
25070
  settingsDir: settingsDir(base),
24815
25071
  dataDir: dataDir(base)
24816
25072
  });
24817
- if (result !== null) writeSyncState(dataDir(base), result);
25073
+ if (result !== null) {
25074
+ policyOutcome = result.outcome;
25075
+ writeSyncState(dataDir(base), result);
25076
+ }
25077
+ } catch {
25078
+ }
25079
+ if (policyOutcome === "unauthorized") return;
25080
+ try {
25081
+ await runCommandSync({ base, settingsDir: settingsDir(base), scan: deps.scan });
24818
25082
  } catch {
24819
25083
  }
24820
25084
  }