@akasecurity/ai-tc-claude-code 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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/hooks/hooks.json +1 -1
- package/package.json +5 -5
- package/scripts/apply-suppressions.js +1200 -671
- package/scripts/backfill.js +2299 -1434
- package/scripts/filescan.js +2053 -1249
- package/scripts/firstrun.js +1961 -1188
- package/scripts/history-sync.js +1882 -1029
- package/scripts/intro.js +363 -238
- package/scripts/message-display.js +1212 -662
- package/scripts/onboard.js +1598 -1066
- package/scripts/post-model-switch.js +447 -311
- package/scripts/post-tool-use.js +2101 -1274
- package/scripts/pre-model-switch.js +1981 -1204
- package/scripts/pre-tool-use.js +2113 -1102
- package/scripts/query.js +1961 -1188
- package/scripts/reconcile.js +2082 -1242
- package/scripts/remediate.js +2152 -1279
- package/scripts/scan-worker.js +278 -176
- package/scripts/session-start.js +2050 -1277
- package/scripts/start-light.js +366 -241
- package/scripts/statusline.js +1961 -1188
- package/scripts/stop.js +448 -315
- package/scripts/sync.js +17422 -2018
- package/scripts/user-prompt-submit.js +2110 -1279
|
@@ -492,14 +492,15 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// src/apply-suppressions.ts
|
|
495
|
-
import { existsSync as existsSync11, readFileSync as
|
|
495
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
496
496
|
import { userInfo } from "os";
|
|
497
|
-
import { dirname as
|
|
497
|
+
import { dirname as dirname8, join as join24 } from "path";
|
|
498
498
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
499
499
|
|
|
500
500
|
// ../../packages/persistence/src/attached-derived.ts
|
|
501
501
|
import { rmSync } from "fs";
|
|
502
502
|
import { join } from "path";
|
|
503
|
+
var POLICY_CACHE_FILENAME = "policy-cache.json";
|
|
503
504
|
|
|
504
505
|
// ../../packages/persistence/src/control-plane-credential.ts
|
|
505
506
|
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync3, statSync } from "fs";
|
|
@@ -598,6 +599,30 @@ var SQLITE_MIGRATIONS = [
|
|
|
598
599
|
{
|
|
599
600
|
tag: "0022_audit_inspection_ms",
|
|
600
601
|
sql: "ALTER TABLE `audit_events` ADD `inspection_ms` integer GENERATED ALWAYS AS (json_extract(attributes, '$.inspection_ms')) VIRTUAL;"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
tag: "0023_secret_vault_user_authorized",
|
|
605
|
+
sql: "ALTER TABLE `secret_vault` ADD `user_authorized` integer DEFAULT 0 NOT NULL;"
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
tag: "0024_finding_resolution_key_created_index",
|
|
609
|
+
sql: "DROP INDEX IF EXISTS `idx_finding_resolution_key`;--> statement-breakpoint\nCREATE INDEX `idx_finding_resolution_key_created` ON `finding_resolution` (`finding_key`,`created_at`);"
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
tag: "0025_audit_capture_attribute_columns",
|
|
613
|
+
sql: "ALTER TABLE `audit_events` ADD `source_tool` text GENERATED ALWAYS AS (json_extract(attributes, '$.source_tool')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `repo` text GENERATED ALWAYS AS (json_extract(attributes, '$.repo')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `file_path` text GENERATED ALWAYS AS (json_extract(attributes, '$.file_path')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `tool_name` text GENERATED ALWAYS AS (json_extract(attributes, '$.tool_name')) VIRTUAL;"
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
tag: "0026_audit_llm_call_usage_columns",
|
|
617
|
+
sql: "ALTER TABLE `audit_events` ADD `service_tier` text GENERATED ALWAYS AS (json_extract(attributes, '$.service_tier')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `ephemeral_1h_input_tokens` integer GENERATED ALWAYS AS (json_extract(attributes, '$.ephemeral_1h_input_tokens')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `ephemeral_5m_input_tokens` integer GENERATED ALWAYS AS (json_extract(attributes, '$.ephemeral_5m_input_tokens')) VIRTUAL;--> statement-breakpoint\nALTER TABLE `audit_events` ADD `web_search_requests` integer GENERATED ALWAYS AS (json_extract(attributes, '$.web_search_requests')) VIRTUAL;"
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
tag: "0027_audit_llm_usage_index",
|
|
621
|
+
sql: "CREATE INDEX `idx_audit_llm_usage` ON `audit_events` (`started_at`,`root_session_id`,`provider`,`model`,`service_tier`,`input_tokens`,`output_tokens`,`cache_creation_input_tokens`,`cache_read_input_tokens`,`ephemeral_1h_input_tokens`,`ephemeral_5m_input_tokens`,`web_search_requests`) WHERE event_type = 'llm_call' AND attributes IS NOT NULL;"
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
tag: "0028_activity_session_probe_indexes",
|
|
625
|
+
sql: "CREATE INDEX `idx_audit_session_prompt` ON `audit_events` (`root_session_id`) WHERE event_type = 'prompt';--> statement-breakpoint\nCREATE INDEX `idx_audit_session_share` ON `audit_events` (`root_session_id`) WHERE event_type = 'share';--> statement-breakpoint\nCREATE INDEX `idx_audit_ended_at` ON `audit_events` (`ended_at`,`root_session_id`) WHERE ended_at IS NOT NULL;--> statement-breakpoint\nCREATE INDEX `idx_audit_session_ended` ON `audit_events` (`root_session_id`,`ended_at`) WHERE ended_at IS NOT NULL;--> statement-breakpoint\n-- Expression index for the activity list's turns rollup: a live-captured\n-- session's turns are the DISTINCT `run_key` across its `llm_call` leaves, and\n-- carrying the extracted key in the index answers that count from the index\n-- alone instead of parsing every leaf's attribute bag. Written by hand, as\n-- 0013's `idx_audit_code_change_path` was: drizzle-kit cannot emit an\n-- expression containing a comma, so this index is not declared in sqlite.ts.\nCREATE INDEX `idx_audit_session_run_key` ON `audit_events` (`root_session_id`, json_extract(`attributes`, '$.run_key')) WHERE `event_type` = 'llm_call';\n"
|
|
601
626
|
}
|
|
602
627
|
];
|
|
603
628
|
|
|
@@ -22136,6 +22161,26 @@ var AttachTokenResponse = external_exports.union([
|
|
|
22136
22161
|
AttachTokenExpired,
|
|
22137
22162
|
external_exports.object({ status: printable(64) })
|
|
22138
22163
|
]);
|
|
22164
|
+
var DeviceCommandKind = external_exports.enum(["shares_rescan"]);
|
|
22165
|
+
var DeviceCommand = external_exports.object({
|
|
22166
|
+
id: printable(128).min(1),
|
|
22167
|
+
kind: DeviceCommandKind,
|
|
22168
|
+
issuedAt: printable(64).min(1),
|
|
22169
|
+
expiresAt: printable(64).min(1)
|
|
22170
|
+
}).strict();
|
|
22171
|
+
var DeviceCommandPollResponse = external_exports.object({ command: DeviceCommand.nullable() });
|
|
22172
|
+
var DeviceCommandFailureReason = external_exports.enum(["scan_failed", "no_projects", "expired"]);
|
|
22173
|
+
var DeviceCommandAckBody = external_exports.discriminatedUnion("outcome", [
|
|
22174
|
+
external_exports.object({
|
|
22175
|
+
outcome: external_exports.literal("reported"),
|
|
22176
|
+
projectsScanned: external_exports.number().int().nonnegative()
|
|
22177
|
+
}).strict(),
|
|
22178
|
+
external_exports.object({
|
|
22179
|
+
outcome: external_exports.literal("failed"),
|
|
22180
|
+
reason: DeviceCommandFailureReason,
|
|
22181
|
+
projectsScanned: external_exports.number().int().nonnegative()
|
|
22182
|
+
}).strict()
|
|
22183
|
+
]);
|
|
22139
22184
|
|
|
22140
22185
|
// ../../packages/schema/src/zod/registry.ts
|
|
22141
22186
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -22302,7 +22347,7 @@ var PackManifest = external_exports.object({
|
|
|
22302
22347
|
}).meta({ id: "PackManifest" });
|
|
22303
22348
|
|
|
22304
22349
|
// ../../packages/schema/src/zod/detection.ts
|
|
22305
|
-
var OriginEnum = external_exports.enum(["library"]).meta({ id: "OriginEnum" });
|
|
22350
|
+
var OriginEnum = external_exports.enum(["library", "custom"]).meta({ id: "OriginEnum" });
|
|
22306
22351
|
var DetectionFilterEnum = external_exports.enum(["all", "library", "custom", "customized", "updates"]);
|
|
22307
22352
|
var LibraryStateEnum = external_exports.enum(["new", "imported", "update"]).meta({ id: "LibraryStateEnum" });
|
|
22308
22353
|
var DetectionCounts = external_exports.object({
|
|
@@ -22439,14 +22484,17 @@ function optional2(key, parsed, raw) {
|
|
|
22439
22484
|
function isStringArray(value) {
|
|
22440
22485
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
22441
22486
|
}
|
|
22487
|
+
var ORIGIN_VALUES = { library: true, custom: true };
|
|
22488
|
+
function resolveOrigin(origin) {
|
|
22489
|
+
return origin != null && Object.hasOwn(ORIGIN_VALUES, origin) ? origin : "library";
|
|
22490
|
+
}
|
|
22442
22491
|
function summaryToDetectionListItem(s) {
|
|
22443
22492
|
return {
|
|
22444
22493
|
id: `${s.namespace}/${s.packId}`,
|
|
22445
22494
|
name: s.name,
|
|
22446
22495
|
version: s.version,
|
|
22447
22496
|
enabled: s.enabled,
|
|
22448
|
-
origin:
|
|
22449
|
-
// v1: every installed pack is library origin
|
|
22497
|
+
origin: resolveOrigin(s.origin),
|
|
22450
22498
|
namespace: s.namespace,
|
|
22451
22499
|
packId: s.packId,
|
|
22452
22500
|
ruleCount: s.ruleCount,
|
|
@@ -22498,7 +22546,7 @@ function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
|
22498
22546
|
name: row.name,
|
|
22499
22547
|
version: row.version,
|
|
22500
22548
|
enabled: row.enabled,
|
|
22501
|
-
origin:
|
|
22549
|
+
origin: resolveOrigin(row.origin),
|
|
22502
22550
|
namespace: row.namespace,
|
|
22503
22551
|
packId: row.packId,
|
|
22504
22552
|
ruleCount: row.rules.length,
|
|
@@ -22518,16 +22566,20 @@ function splitDetectionId(id) {
|
|
|
22518
22566
|
}
|
|
22519
22567
|
function buildDetectionsList(summaries, query) {
|
|
22520
22568
|
const withUpdate = summaries.filter((s) => s.latestVersion != null);
|
|
22569
|
+
const originOf = (s) => resolveOrigin(s.origin);
|
|
22521
22570
|
const counts = {
|
|
22522
22571
|
all: summaries.length,
|
|
22523
|
-
library: summaries.length,
|
|
22524
|
-
|
|
22525
|
-
|
|
22572
|
+
library: summaries.filter((s) => originOf(s) === "library").length,
|
|
22573
|
+
custom: summaries.filter((s) => originOf(s) === "custom").length,
|
|
22574
|
+
// No origin member produces this, so it is 0 BY CONSTRUCTION rather than by
|
|
22575
|
+
// omission: `customized` would mean a LIBRARY pack whose rules were edited in
|
|
22576
|
+
// place, and that state does not exist — editing a library pack forks it. See
|
|
22577
|
+
// OriginEnum.
|
|
22526
22578
|
customized: 0,
|
|
22527
22579
|
updates: withUpdate.length
|
|
22528
22580
|
};
|
|
22529
22581
|
const filter = query.filter;
|
|
22530
|
-
let filtered = filter === "custom"
|
|
22582
|
+
let filtered = filter === "customized" ? [] : filter === "custom" ? summaries.filter((s) => originOf(s) === "custom") : filter === "library" ? summaries.filter((s) => originOf(s) === "library") : filter === "updates" ? [...withUpdate] : [...summaries];
|
|
22531
22583
|
if (query.q) {
|
|
22532
22584
|
const q = query.q.toLowerCase();
|
|
22533
22585
|
filtered = filtered.filter(
|
|
@@ -22607,8 +22659,9 @@ var Event = external_exports.object({
|
|
|
22607
22659
|
metadata: EventMetadata.optional()
|
|
22608
22660
|
}).meta({ id: "Event" });
|
|
22609
22661
|
var IngestEvent = Event.meta({ id: "IngestEvent" });
|
|
22662
|
+
var INGEST_BATCH_MAX = 100;
|
|
22610
22663
|
var IngestBatch = external_exports.object({
|
|
22611
|
-
events: external_exports.array(IngestEvent).min(1).max(
|
|
22664
|
+
events: external_exports.array(IngestEvent).min(1).max(INGEST_BATCH_MAX),
|
|
22612
22665
|
// Dedup policy for this batch. Id-dedup ALWAYS applies. 'content-hash'
|
|
22613
22666
|
// additionally rejects any event whose contentHash the store has already
|
|
22614
22667
|
// recorded — for re-runnable bulk ingest (worktree scan, transcript
|
|
@@ -23164,6 +23217,257 @@ var PatchInstalledPackRequest = external_exports.object({
|
|
|
23164
23217
|
message: "At least one field must be provided"
|
|
23165
23218
|
}).meta({ id: "PatchInstalledPackRequest" });
|
|
23166
23219
|
|
|
23220
|
+
// ../../packages/schema/src/zod/policy.ts
|
|
23221
|
+
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
23222
|
+
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
23223
|
+
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
23224
|
+
var PolicyProvenance = external_exports.enum(["builtin", "authored"]).meta({ id: "PolicyProvenance" });
|
|
23225
|
+
var Policy = external_exports.object({
|
|
23226
|
+
id: external_exports.guid(),
|
|
23227
|
+
scope: PolicyScope,
|
|
23228
|
+
target: PolicyTarget,
|
|
23229
|
+
action: ActionTaken,
|
|
23230
|
+
enabled: external_exports.boolean().default(true),
|
|
23231
|
+
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
23232
|
+
// Display name — optional so older policy rows without name still parse.
|
|
23233
|
+
// Added for the findings API (policy.name column migration).
|
|
23234
|
+
name: external_exports.string().optional(),
|
|
23235
|
+
// Whether an AUTHORED policy governs this row's target — not a claim about
|
|
23236
|
+
// which row this is. A producer that collapses several rows onto one target
|
|
23237
|
+
// must carry the marker onto whichever row survives, or the collapse decides
|
|
23238
|
+
// the answer; a survivor may therefore be a built-in expansion still marked
|
|
23239
|
+
// 'authored' because an authored sibling targeted the same thing.
|
|
23240
|
+
// Optional so an older producer — and an older on-disk cache — still parses;
|
|
23241
|
+
// absent reads as 'builtin', which is the behaviour that predates the field.
|
|
23242
|
+
//
|
|
23243
|
+
// Deliberately NOT `kind`/PolicyKind: that name and that enum answer which
|
|
23244
|
+
// built-in archetype catalog entry a policy is, which every catalog surface
|
|
23245
|
+
// reads and which a caller may state. This one is a statement the PRODUCER
|
|
23246
|
+
// of a bundle makes about a row, and only the bundle builder ever stamps it
|
|
23247
|
+
// — the CRUD routes neither accept nor set it.
|
|
23248
|
+
//
|
|
23249
|
+
// A device consumes this in exactly one direction: an 'authored' policy
|
|
23250
|
+
// arriving from a control plane marks the rules it targets as not
|
|
23251
|
+
// locally re-assignable. That can only ever ADD a refusal, never relax one,
|
|
23252
|
+
// which is what makes it safe to honour from an unsigned cache — the same
|
|
23253
|
+
// test `prohibitedModels` passes and `reversibleRuleIds` fails.
|
|
23254
|
+
provenance: PolicyProvenance.optional()
|
|
23255
|
+
}).meta({ id: "Policy" });
|
|
23256
|
+
var PolicyBundle = external_exports.object({
|
|
23257
|
+
version: external_exports.string(),
|
|
23258
|
+
policies: external_exports.array(Policy),
|
|
23259
|
+
// Rules from the installed marketplace packs (snapshotted by the
|
|
23260
|
+
// control plane). The plugin registers these in addition to its bundled
|
|
23261
|
+
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
23262
|
+
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
23263
|
+
rules: external_exports.array(Rule).optional(),
|
|
23264
|
+
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
23265
|
+
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
23266
|
+
// after reading the user's installed snapshot (installed_packs, enabled
|
|
23267
|
+
// packs only), which is how detection updates stay manual: new bundled
|
|
23268
|
+
// rules run only after the user applies the pack update. Absent/false keeps
|
|
23269
|
+
// the historical composition (bundled packs + rules) — older caches.
|
|
23270
|
+
rulesComplete: external_exports.boolean().optional(),
|
|
23271
|
+
// Active detection exceptions, evaluation subset only (see
|
|
23272
|
+
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
23273
|
+
// on-disk caches — that omit the field still parse; consumers read
|
|
23274
|
+
// `bundle.exceptions ?? []`.
|
|
23275
|
+
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
23276
|
+
// Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
|
|
23277
|
+
// A second axis over the same `redact` action, carried beside the policies
|
|
23278
|
+
// rather than on them: nothing writes ruleId-targeted policies to disk, so
|
|
23279
|
+
// widening Policy itself would change a persisted shape to express something
|
|
23280
|
+
// only the in-memory bundle needs. Optional so an older producer — or an
|
|
23281
|
+
// older on-disk cache — still parses; consumers read `?? []` and get the
|
|
23282
|
+
// pre-existing one-way behaviour, which is the safe direction to default.
|
|
23283
|
+
reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
|
|
23284
|
+
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
23285
|
+
// from a versioned installed pack. Optional so older backends — and older
|
|
23286
|
+
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
23287
|
+
// the rule's own spec version. NOT the bundle version above — see
|
|
23288
|
+
// installedRuleset's ruleVersions for the source of truth.
|
|
23289
|
+
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
23290
|
+
// Model ids (the raw `model` string a harness reports, e.g.
|
|
23291
|
+
// `claude-opus-4-1`) the tenant has PROHIBITED. The plugin refuses to switch
|
|
23292
|
+
// a session onto one (PreModelSwitch) and refuses a turn that would run on
|
|
23293
|
+
// one (UserPromptSubmit). Optional so an older backend — and an older
|
|
23294
|
+
// on-disk cache — still parses; consumers read `?? []`, which is the
|
|
23295
|
+
// unenforced behaviour that predates this field and the safe direction to
|
|
23296
|
+
// default.
|
|
23297
|
+
//
|
|
23298
|
+
// Ids, not display names: the governance decision is keyed on the exact
|
|
23299
|
+
// string the harness reports (`model_status_override.versionId` in the
|
|
23300
|
+
// control plane), so no name resolution stands between the decision and the
|
|
23301
|
+
// comparison.
|
|
23302
|
+
prohibitedModels: external_exports.array(external_exports.string()).optional(),
|
|
23303
|
+
customKeywords: external_exports.array(external_exports.string()),
|
|
23304
|
+
fetchedAt: external_exports.iso.datetime()
|
|
23305
|
+
}).meta({ id: "PolicyBundle" });
|
|
23306
|
+
var POLICY_BUNDLE_SHAPE_ID = [
|
|
23307
|
+
...Object.keys(PolicyBundle.shape),
|
|
23308
|
+
...Object.keys(Policy.shape).map((key) => `policies.${key}`),
|
|
23309
|
+
...PolicyTarget.options.flatMap((member) => "shape" in member ? Object.keys(member.shape) : []).map((key) => `policies.target.${key}`),
|
|
23310
|
+
...Object.keys(ExceptionBundleEntry.shape).map((key) => `exceptions.${key}`)
|
|
23311
|
+
].sort().join(",");
|
|
23312
|
+
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
23313
|
+
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
23314
|
+
var CATEGORY_PEAK_SEVERITY = {
|
|
23315
|
+
secret: "critical",
|
|
23316
|
+
financial: "critical",
|
|
23317
|
+
// core-financial/credit-card
|
|
23318
|
+
code_flaw: "critical",
|
|
23319
|
+
pii: "high",
|
|
23320
|
+
phi: "high",
|
|
23321
|
+
custom: "high",
|
|
23322
|
+
// user-defined; conservative
|
|
23323
|
+
code_context: "low",
|
|
23324
|
+
config: "low"
|
|
23325
|
+
// observe-only; floors to monitor regardless
|
|
23326
|
+
};
|
|
23327
|
+
function severityFloorPolicy(category) {
|
|
23328
|
+
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
23329
|
+
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
23330
|
+
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
23331
|
+
}
|
|
23332
|
+
function severityFloorPosture() {
|
|
23333
|
+
const out = {};
|
|
23334
|
+
for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
|
|
23335
|
+
return out;
|
|
23336
|
+
}
|
|
23337
|
+
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
|
|
23338
|
+
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
23339
|
+
var RedactFallback = BuiltinPolicyId.extract(["monitor", "warn", "block"]).meta({
|
|
23340
|
+
id: "RedactFallback"
|
|
23341
|
+
});
|
|
23342
|
+
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
23343
|
+
var BUILTIN_POLICY_SPECS = {
|
|
23344
|
+
monitor: {
|
|
23345
|
+
name: "Monitor",
|
|
23346
|
+
action: "log",
|
|
23347
|
+
reversible: false,
|
|
23348
|
+
description: "Log every match for audit. The request is allowed through untouched."
|
|
23349
|
+
},
|
|
23350
|
+
warn: {
|
|
23351
|
+
name: "Warn",
|
|
23352
|
+
action: "warn",
|
|
23353
|
+
reversible: false,
|
|
23354
|
+
description: "Allow the request, but warn the user inline before it is sent."
|
|
23355
|
+
},
|
|
23356
|
+
redact: {
|
|
23357
|
+
name: "Redact",
|
|
23358
|
+
action: "redact",
|
|
23359
|
+
reversible: false,
|
|
23360
|
+
description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
|
|
23361
|
+
},
|
|
23362
|
+
vault: {
|
|
23363
|
+
name: "Redact & Vault",
|
|
23364
|
+
action: "redact",
|
|
23365
|
+
reversible: true,
|
|
23366
|
+
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."
|
|
23367
|
+
},
|
|
23368
|
+
block: {
|
|
23369
|
+
name: "Block",
|
|
23370
|
+
action: "block",
|
|
23371
|
+
reversible: false,
|
|
23372
|
+
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
23373
|
+
}
|
|
23374
|
+
};
|
|
23375
|
+
function builtinPolicyToAction(id) {
|
|
23376
|
+
return BUILTIN_POLICY_SPECS[id].action;
|
|
23377
|
+
}
|
|
23378
|
+
var PALETTE_WEAKEST_FIRST = [
|
|
23379
|
+
...new Set(KNOWN_BUILTIN_IDS.map(builtinPolicyToAction))
|
|
23380
|
+
];
|
|
23381
|
+
var BELOW_PALETTE = ACTION_TAKEN_KEYS.filter(
|
|
23382
|
+
(action) => !PALETTE_WEAKEST_FIRST.includes(action)
|
|
23383
|
+
);
|
|
23384
|
+
var ACTION_STRENGTH_ORDER = [
|
|
23385
|
+
...BELOW_PALETTE,
|
|
23386
|
+
...PALETTE_WEAKEST_FIRST
|
|
23387
|
+
];
|
|
23388
|
+
function actionRank(action) {
|
|
23389
|
+
return ACTION_STRENGTH_ORDER.indexOf(action);
|
|
23390
|
+
}
|
|
23391
|
+
function isActionAtLeast(action, floor) {
|
|
23392
|
+
return actionRank(action) >= actionRank(floor);
|
|
23393
|
+
}
|
|
23394
|
+
function strongerAction(a, b) {
|
|
23395
|
+
return actionRank(a) >= actionRank(b) ? a : b;
|
|
23396
|
+
}
|
|
23397
|
+
function weakestBuiltinAtLeast(floor) {
|
|
23398
|
+
return KNOWN_BUILTIN_IDS.find((id) => isActionAtLeast(builtinPolicyToAction(id), floor)) ?? "block";
|
|
23399
|
+
}
|
|
23400
|
+
var PackPolicyFloor = external_exports.object({
|
|
23401
|
+
/**
|
|
23402
|
+
* The weakest archetype the device may assign. Stated as a BuiltinPolicyId
|
|
23403
|
+
* rather than a raw ActionTaken because that is the vocabulary the user
|
|
23404
|
+
* picks from — a floor a UI cannot name is one it cannot explain.
|
|
23405
|
+
*/
|
|
23406
|
+
floor: BuiltinPolicyId,
|
|
23407
|
+
/**
|
|
23408
|
+
* True when the organization AUTHORED a policy governing this pack rather
|
|
23409
|
+
* than stating a minimum: it gave the answer, so the pack is not
|
|
23410
|
+
* re-assignable locally in either direction.
|
|
23411
|
+
*/
|
|
23412
|
+
locked: external_exports.boolean()
|
|
23413
|
+
}).describe("PackPolicyFloor");
|
|
23414
|
+
var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
23415
|
+
(id) => !BUILTIN_POLICY_SPECS[id].reversible
|
|
23416
|
+
);
|
|
23417
|
+
var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
23418
|
+
(id) => BUILTIN_POLICY_SPECS[id].reversible
|
|
23419
|
+
);
|
|
23420
|
+
var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
|
|
23421
|
+
function builtinPolicyIsReversible(id) {
|
|
23422
|
+
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
23423
|
+
}
|
|
23424
|
+
function policyIdIsReversible(policyId) {
|
|
23425
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
23426
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
23427
|
+
return builtinPolicyIsReversible(id);
|
|
23428
|
+
}
|
|
23429
|
+
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
23430
|
+
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
23431
|
+
);
|
|
23432
|
+
var BUILTIN_POLICIES = Object.fromEntries(
|
|
23433
|
+
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
23434
|
+
);
|
|
23435
|
+
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
23436
|
+
function policyIdToAction(policyId) {
|
|
23437
|
+
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
23438
|
+
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
23439
|
+
return BUILTIN_POLICIES[id].action;
|
|
23440
|
+
}
|
|
23441
|
+
var UsedByItem = external_exports.object({
|
|
23442
|
+
id: external_exports.string(),
|
|
23443
|
+
name: external_exports.string(),
|
|
23444
|
+
ruleCount: external_exports.number().int().nonnegative(),
|
|
23445
|
+
enabled: external_exports.boolean()
|
|
23446
|
+
}).meta({ id: "UsedByItem" });
|
|
23447
|
+
var PolicyListItem = external_exports.object({
|
|
23448
|
+
id: external_exports.string(),
|
|
23449
|
+
kind: PolicyKind,
|
|
23450
|
+
name: external_exports.string(),
|
|
23451
|
+
enabled: external_exports.boolean(),
|
|
23452
|
+
usedByCount: external_exports.number().int().nonnegative()
|
|
23453
|
+
}).meta({ id: "PolicyListItem" });
|
|
23454
|
+
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
23455
|
+
var PolicyDetail = external_exports.object({
|
|
23456
|
+
specVersion: external_exports.literal(1),
|
|
23457
|
+
id: external_exports.string(),
|
|
23458
|
+
kind: PolicyKind,
|
|
23459
|
+
name: external_exports.string(),
|
|
23460
|
+
enabled: external_exports.boolean(),
|
|
23461
|
+
description: external_exports.string(),
|
|
23462
|
+
usedBy: external_exports.array(UsedByItem)
|
|
23463
|
+
}).meta({ id: "PolicyDetail" });
|
|
23464
|
+
var PolicyStatsResponse = external_exports.object({
|
|
23465
|
+
policies: external_exports.number().int().nonnegative(),
|
|
23466
|
+
builtin: external_exports.number().int().nonnegative(),
|
|
23467
|
+
custom: external_exports.number().int().nonnegative(),
|
|
23468
|
+
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
23469
|
+
}).meta({ id: "PolicyStatsResponse" });
|
|
23470
|
+
|
|
23167
23471
|
// ../../packages/schema/src/zod/vault.ts
|
|
23168
23472
|
var POINTER_FORMAT_VERSION = 2;
|
|
23169
23473
|
var CATEGORY_ALTERNATION = DetectionCategory.options.join("|");
|
|
@@ -23204,6 +23508,14 @@ var VaultEntry = external_exports.object({
|
|
|
23204
23508
|
// How many times this value has been detected on this machine — the reuse
|
|
23205
23509
|
// signal the vault dashboard reads. Distinct from VaultDeref.pointerCount.
|
|
23206
23510
|
occurrenceCount: external_exports.number().int().nonnegative(),
|
|
23511
|
+
// True when a PERSON asked for this value to be replaced — the surfaced-
|
|
23512
|
+
// secrets strike — rather than a pack enforcing its assignment. One value is
|
|
23513
|
+
// one row however many paths vault it, so this is what tells a policy sweep
|
|
23514
|
+
// that the row carries somebody's own instruction and not just an assignment
|
|
23515
|
+
// that has since been lowered. STICKY and MONOTONIC: a later automatic
|
|
23516
|
+
// vaulting of the same value must never clear it — what the user said about
|
|
23517
|
+
// the value does not expire.
|
|
23518
|
+
userAuthorized: external_exports.boolean(),
|
|
23207
23519
|
firstSeen: external_exports.string(),
|
|
23208
23520
|
lastSeen: external_exports.string()
|
|
23209
23521
|
});
|
|
@@ -23322,7 +23634,7 @@ var VaultConsent = external_exports.object({
|
|
|
23322
23634
|
});
|
|
23323
23635
|
|
|
23324
23636
|
// ../../packages/schema/src/zod/local.ts
|
|
23325
|
-
var WORKSPACE_SETTINGS_SPEC_VERSION =
|
|
23637
|
+
var WORKSPACE_SETTINGS_SPEC_VERSION = 7;
|
|
23326
23638
|
var MODEL_JUDGE_PAYLOAD_VERSION = 1;
|
|
23327
23639
|
var RunMode = external_exports.enum(["standalone", "attached"]);
|
|
23328
23640
|
var ControlPlaneConnection = external_exports.object({
|
|
@@ -23367,6 +23679,19 @@ var WorkspaceSettings = external_exports.object({
|
|
|
23367
23679
|
vaultKeyCustody: VaultKeyCustody.default("file"),
|
|
23368
23680
|
// How a pointer renders in assistant prose on screen (see VaultInlineReveal).
|
|
23369
23681
|
vaultInlineReveal: VaultInlineReveal.default("masked"),
|
|
23682
|
+
// What a `redact` policy degrades to on a FIELD the host cannot rewrite in
|
|
23683
|
+
// place. Not a handling policy: the policy has already resolved to redact,
|
|
23684
|
+
// and this only says what happens when the host offers no channel to carry it
|
|
23685
|
+
// out — Antigravity's PreToolUse has no updatedInput at all, and Codex and
|
|
23686
|
+
// Claude Code decline to mask a field that EXECUTES because masking would
|
|
23687
|
+
// change what runs. Per FIELD rather than per host, so a host that can
|
|
23688
|
+
// rewrite some inputs keeps true redaction on those.
|
|
23689
|
+
//
|
|
23690
|
+
// Spelled in the built-in policy vocabulary rather than as a fresh enum, so
|
|
23691
|
+
// an attached machine's merge is `strongerAction` over the one action ladder
|
|
23692
|
+
// and no second rank order exists to drift from it. 'deny' is a host wire
|
|
23693
|
+
// word and stays out of the stored value.
|
|
23694
|
+
redactFallback: RedactFallback.default("warn"),
|
|
23370
23695
|
// Absent until /aka:setup completes; its presence is what "onboarded" means.
|
|
23371
23696
|
onboardedAt: external_exports.iso.datetime().optional(),
|
|
23372
23697
|
// Records that the user consented to sending findings to the model API for
|
|
@@ -23374,15 +23699,20 @@ var WorkspaceSettings = external_exports.object({
|
|
|
23374
23699
|
// Absent until granted; a stale payloadVersion means the consent no longer
|
|
23375
23700
|
// covers the current payload and must be re-granted.
|
|
23376
23701
|
modelJudgeConsent: ModelJudgeConsent.optional(),
|
|
23377
|
-
// Records that the user consented to
|
|
23378
|
-
//
|
|
23379
|
-
//
|
|
23380
|
-
//
|
|
23702
|
+
// Records that the user consented to the DEFERRED send — the outbox — along
|
|
23703
|
+
// with the payload shape and the endpoint they agreed to. Since payload v2
|
|
23704
|
+
// that covers both the pre-attach backlog and undelivered captures (which
|
|
23705
|
+
// carry prompt/reply text in `content`); the key name predates the widening.
|
|
23706
|
+
// Absent until granted, and a grant for a different endpoint or an older
|
|
23707
|
+
// payload no longer counts.
|
|
23381
23708
|
historySyncConsent: HistorySyncConsent.optional()
|
|
23382
23709
|
});
|
|
23383
23710
|
function defaultWorkspaceSettings() {
|
|
23384
23711
|
return WorkspaceSettings.parse({});
|
|
23385
23712
|
}
|
|
23713
|
+
function isAttached(settings) {
|
|
23714
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
23715
|
+
}
|
|
23386
23716
|
function toInventoryRow(input2, id, now) {
|
|
23387
23717
|
return {
|
|
23388
23718
|
id,
|
|
@@ -23502,7 +23832,8 @@ var ManagedSettingKey = external_exports.enum([
|
|
|
23502
23832
|
"vaultKeyCustody",
|
|
23503
23833
|
"vaultInlineReveal",
|
|
23504
23834
|
"modelJudgeConsent",
|
|
23505
|
-
"dataSharesInPlace"
|
|
23835
|
+
"dataSharesInPlace",
|
|
23836
|
+
"redactFallback"
|
|
23506
23837
|
]).meta({ id: "ManagedSettingKey" });
|
|
23507
23838
|
var ManagedSettingsValues = external_exports.object({
|
|
23508
23839
|
runMode: external_exports.enum(["standalone", "attached"]).optional(),
|
|
@@ -23515,7 +23846,8 @@ var ManagedSettingsValues = external_exports.object({
|
|
|
23515
23846
|
vaultKeyCustody: external_exports.enum(["file", "keychain"]).optional(),
|
|
23516
23847
|
vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
|
|
23517
23848
|
modelJudgeConsent: external_exports.boolean().optional(),
|
|
23518
|
-
dataSharesInPlace: external_exports.boolean().optional()
|
|
23849
|
+
dataSharesInPlace: external_exports.boolean().optional(),
|
|
23850
|
+
redactFallback: RedactFallback.optional()
|
|
23519
23851
|
}).meta({ id: "ManagedSettingsValues" });
|
|
23520
23852
|
var ManagedSettings = external_exports.object({
|
|
23521
23853
|
specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
|
|
@@ -23530,191 +23862,6 @@ var ManagedSettings = external_exports.object({
|
|
|
23530
23862
|
lockedFields: external_exports.array(ManagedSettingKey).default([])
|
|
23531
23863
|
}).meta({ id: "ManagedSettings" });
|
|
23532
23864
|
|
|
23533
|
-
// ../../packages/schema/src/zod/policy.ts
|
|
23534
|
-
var PolicyScope = external_exports.enum(["global", "repo", "user"]).meta({ id: "PolicyScope" });
|
|
23535
|
-
var PolicyTarget = external_exports.union([external_exports.object({ ruleId: external_exports.string() }), external_exports.object({ category: DetectionCategory })]).meta({ id: "PolicyTarget" });
|
|
23536
|
-
var Policy = external_exports.object({
|
|
23537
|
-
id: external_exports.guid(),
|
|
23538
|
-
scope: PolicyScope,
|
|
23539
|
-
target: PolicyTarget,
|
|
23540
|
-
action: ActionTaken,
|
|
23541
|
-
enabled: external_exports.boolean().default(true),
|
|
23542
|
-
customKeywords: external_exports.array(external_exports.string()).optional(),
|
|
23543
|
-
// Display name — optional so older policy rows without name still parse.
|
|
23544
|
-
// Added for the findings API (policy.name column migration).
|
|
23545
|
-
name: external_exports.string().optional()
|
|
23546
|
-
}).meta({ id: "Policy" });
|
|
23547
|
-
var PolicyBundle = external_exports.object({
|
|
23548
|
-
version: external_exports.string(),
|
|
23549
|
-
policies: external_exports.array(Policy),
|
|
23550
|
-
// Rules from the installed marketplace packs (snapshotted by the
|
|
23551
|
-
// control plane). The plugin registers these in addition to its bundled
|
|
23552
|
-
// packs. Optional so older backends — and older on-disk caches — that omit
|
|
23553
|
-
// the field still parse; consumers read `bundle.rules ?? []`.
|
|
23554
|
-
rules: external_exports.array(Rule).optional(),
|
|
23555
|
-
// When true, `rules` IS the complete effective ruleset and the runtime must
|
|
23556
|
-
// NOT merge its compiled-in bundled packs — the standalone gateway sets this
|
|
23557
|
-
// after reading the user's installed snapshot (installed_packs, enabled
|
|
23558
|
-
// packs only), which is how detection updates stay manual: new bundled
|
|
23559
|
-
// rules run only after the user applies the pack update. Absent/false keeps
|
|
23560
|
-
// the historical composition (bundled packs + rules) — older caches.
|
|
23561
|
-
rulesComplete: external_exports.boolean().optional(),
|
|
23562
|
-
// Active detection exceptions, evaluation subset only (see
|
|
23563
|
-
// ExceptionBundleEntry). Optional so older bundle producers — and older
|
|
23564
|
-
// on-disk caches — that omit the field still parse; consumers read
|
|
23565
|
-
// `bundle.exceptions ?? []`.
|
|
23566
|
-
exceptions: external_exports.array(ExceptionBundleEntry).optional(),
|
|
23567
|
-
// Rule ids whose pack is assigned a REVERSIBLE archetype (Redact & Vault).
|
|
23568
|
-
// A second axis over the same `redact` action, carried beside the policies
|
|
23569
|
-
// rather than on them: nothing writes ruleId-targeted policies to disk, so
|
|
23570
|
-
// widening Policy itself would change a persisted shape to express something
|
|
23571
|
-
// only the in-memory bundle needs. Optional so an older producer — or an
|
|
23572
|
-
// older on-disk cache — still parses; consumers read `?? []` and get the
|
|
23573
|
-
// pre-existing one-way behaviour, which is the safe direction to default.
|
|
23574
|
-
reversibleRuleIds: external_exports.array(external_exports.string()).optional(),
|
|
23575
|
-
// Installed pack version, keyed by ruleId, for rules in `rules` that came
|
|
23576
|
-
// from a versioned installed pack. Optional so older backends — and older
|
|
23577
|
-
// on-disk caches — that omit the field still parse; consumers fall back to
|
|
23578
|
-
// the rule's own spec version. NOT the bundle version above — see
|
|
23579
|
-
// installedRuleset's ruleVersions for the source of truth.
|
|
23580
|
-
ruleVersions: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
23581
|
-
// Model ids (the raw `model` string a harness reports, e.g.
|
|
23582
|
-
// `claude-opus-4-1`) the tenant has PROHIBITED. The plugin refuses to switch
|
|
23583
|
-
// a session onto one (PreModelSwitch) and refuses a turn that would run on
|
|
23584
|
-
// one (UserPromptSubmit). Optional so an older backend — and an older
|
|
23585
|
-
// on-disk cache — still parses; consumers read `?? []`, which is the
|
|
23586
|
-
// unenforced behaviour that predates this field and the safe direction to
|
|
23587
|
-
// default.
|
|
23588
|
-
//
|
|
23589
|
-
// Ids, not display names: the governance decision is keyed on the exact
|
|
23590
|
-
// string the harness reports (`model_status_override.versionId` in the
|
|
23591
|
-
// control plane), so no name resolution stands between the decision and the
|
|
23592
|
-
// comparison.
|
|
23593
|
-
prohibitedModels: external_exports.array(external_exports.string()).optional(),
|
|
23594
|
-
customKeywords: external_exports.array(external_exports.string()),
|
|
23595
|
-
fetchedAt: external_exports.iso.datetime()
|
|
23596
|
-
}).meta({ id: "PolicyBundle" });
|
|
23597
|
-
var OBSERVE_ONLY_CATEGORIES = ["config"];
|
|
23598
|
-
var ENFORCEABLE_CATEGORIES = DetectionCategory.options.filter((c) => !OBSERVE_ONLY_CATEGORIES.includes(c));
|
|
23599
|
-
var CATEGORY_PEAK_SEVERITY = {
|
|
23600
|
-
secret: "critical",
|
|
23601
|
-
financial: "critical",
|
|
23602
|
-
// core-financial/credit-card
|
|
23603
|
-
code_flaw: "critical",
|
|
23604
|
-
pii: "high",
|
|
23605
|
-
phi: "high",
|
|
23606
|
-
custom: "high",
|
|
23607
|
-
// user-defined; conservative
|
|
23608
|
-
code_context: "low",
|
|
23609
|
-
config: "low"
|
|
23610
|
-
// observe-only; floors to monitor regardless
|
|
23611
|
-
};
|
|
23612
|
-
function severityFloorPolicy(category) {
|
|
23613
|
-
if (OBSERVE_ONLY_CATEGORIES.includes(category)) return "monitor";
|
|
23614
|
-
const peak = CATEGORY_PEAK_SEVERITY[category];
|
|
23615
|
-
return peak === "critical" || peak === "high" ? "warn" : "monitor";
|
|
23616
|
-
}
|
|
23617
|
-
function severityFloorPosture() {
|
|
23618
|
-
const out = {};
|
|
23619
|
-
for (const c of DetectionCategory.options) out[c] = severityFloorPolicy(c);
|
|
23620
|
-
return out;
|
|
23621
|
-
}
|
|
23622
|
-
var PolicyKind = external_exports.enum(["builtin", "custom"]).meta({ id: "PolicyKind" });
|
|
23623
|
-
var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
|
|
23624
|
-
var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
|
|
23625
|
-
var BUILTIN_ORDER = KNOWN_BUILTIN_IDS;
|
|
23626
|
-
var BUILTIN_POLICY_SPECS = {
|
|
23627
|
-
monitor: {
|
|
23628
|
-
name: "Monitor",
|
|
23629
|
-
action: "log",
|
|
23630
|
-
reversible: false,
|
|
23631
|
-
description: "Log every match for audit. The request is allowed through untouched."
|
|
23632
|
-
},
|
|
23633
|
-
warn: {
|
|
23634
|
-
name: "Warn",
|
|
23635
|
-
action: "warn",
|
|
23636
|
-
reversible: false,
|
|
23637
|
-
description: "Allow the request, but warn the user inline before it is sent."
|
|
23638
|
-
},
|
|
23639
|
-
redact: {
|
|
23640
|
-
name: "Redact",
|
|
23641
|
-
action: "redact",
|
|
23642
|
-
reversible: false,
|
|
23643
|
-
description: "Strip the matched value from the request and destroy it, then continue. What was removed cannot be recovered."
|
|
23644
|
-
},
|
|
23645
|
-
vault: {
|
|
23646
|
-
name: "Redact & Vault",
|
|
23647
|
-
action: "redact",
|
|
23648
|
-
reversible: true,
|
|
23649
|
-
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."
|
|
23650
|
-
},
|
|
23651
|
-
block: {
|
|
23652
|
-
name: "Block",
|
|
23653
|
-
action: "block",
|
|
23654
|
-
reversible: false,
|
|
23655
|
-
description: "Refuse the request entirely whenever any rule in this detection matches."
|
|
23656
|
-
}
|
|
23657
|
-
};
|
|
23658
|
-
function builtinPolicyToAction(id) {
|
|
23659
|
-
return BUILTIN_POLICY_SPECS[id].action;
|
|
23660
|
-
}
|
|
23661
|
-
var CATEGORY_EXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
23662
|
-
(id) => !BUILTIN_POLICY_SPECS[id].reversible
|
|
23663
|
-
);
|
|
23664
|
-
var CATEGORY_INEXPRESSIBLE_IDS = KNOWN_BUILTIN_IDS.filter(
|
|
23665
|
-
(id) => BUILTIN_POLICY_SPECS[id].reversible
|
|
23666
|
-
);
|
|
23667
|
-
var CategoryPolicyId = external_exports.enum(CATEGORY_EXPRESSIBLE_IDS).meta({ id: "CategoryPolicyId" });
|
|
23668
|
-
function builtinPolicyIsReversible(id) {
|
|
23669
|
-
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
23670
|
-
}
|
|
23671
|
-
function policyIdIsReversible(policyId) {
|
|
23672
|
-
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
23673
|
-
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
23674
|
-
return builtinPolicyIsReversible(id);
|
|
23675
|
-
}
|
|
23676
|
-
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
23677
|
-
DetectionCategory.options.map((c) => [c, builtinPolicyToAction(severityFloorPolicy(c))])
|
|
23678
|
-
);
|
|
23679
|
-
var BUILTIN_POLICIES = Object.fromEntries(
|
|
23680
|
-
KNOWN_BUILTIN_IDS.map((id) => [id, { id, ...BUILTIN_POLICY_SPECS[id] }])
|
|
23681
|
-
);
|
|
23682
|
-
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
23683
|
-
function policyIdToAction(policyId) {
|
|
23684
|
-
const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
23685
|
-
const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
|
|
23686
|
-
return BUILTIN_POLICIES[id].action;
|
|
23687
|
-
}
|
|
23688
|
-
var UsedByItem = external_exports.object({
|
|
23689
|
-
id: external_exports.string(),
|
|
23690
|
-
name: external_exports.string(),
|
|
23691
|
-
ruleCount: external_exports.number().int().nonnegative(),
|
|
23692
|
-
enabled: external_exports.boolean()
|
|
23693
|
-
}).meta({ id: "UsedByItem" });
|
|
23694
|
-
var PolicyListItem = external_exports.object({
|
|
23695
|
-
id: external_exports.string(),
|
|
23696
|
-
kind: PolicyKind,
|
|
23697
|
-
name: external_exports.string(),
|
|
23698
|
-
enabled: external_exports.boolean(),
|
|
23699
|
-
usedByCount: external_exports.number().int().nonnegative()
|
|
23700
|
-
}).meta({ id: "PolicyListItem" });
|
|
23701
|
-
var ListPoliciesResponse = external_exports.object({ items: external_exports.array(PolicyListItem) }).meta({ id: "ListPoliciesResponse" });
|
|
23702
|
-
var PolicyDetail = external_exports.object({
|
|
23703
|
-
specVersion: external_exports.literal(1),
|
|
23704
|
-
id: external_exports.string(),
|
|
23705
|
-
kind: PolicyKind,
|
|
23706
|
-
name: external_exports.string(),
|
|
23707
|
-
enabled: external_exports.boolean(),
|
|
23708
|
-
description: external_exports.string(),
|
|
23709
|
-
usedBy: external_exports.array(UsedByItem)
|
|
23710
|
-
}).meta({ id: "PolicyDetail" });
|
|
23711
|
-
var PolicyStatsResponse = external_exports.object({
|
|
23712
|
-
policies: external_exports.number().int().nonnegative(),
|
|
23713
|
-
builtin: external_exports.number().int().nonnegative(),
|
|
23714
|
-
custom: external_exports.number().int().nonnegative(),
|
|
23715
|
-
detectionsGoverned: external_exports.number().int().nonnegative()
|
|
23716
|
-
}).meta({ id: "PolicyStatsResponse" });
|
|
23717
|
-
|
|
23718
23865
|
// ../../packages/schema/src/zod/project-files.ts
|
|
23719
23866
|
var ProjectFileInput = external_exports.object({
|
|
23720
23867
|
path: external_exports.string().min(1),
|
|
@@ -23960,10 +24107,12 @@ var DismissRecommendedActionResponse = external_exports.object({ id: external_ex
|
|
|
23960
24107
|
var RecommendedActionIdParam = external_exports.object({ id: external_exports.string() });
|
|
23961
24108
|
|
|
23962
24109
|
// ../../packages/schema/src/zod/settings-action.ts
|
|
24110
|
+
var HistorySyncConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "HistorySyncConsentChoice" });
|
|
24111
|
+
var ModelJudgeConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "ModelJudgeConsentChoice" });
|
|
23963
24112
|
var SaveSettingsInput = external_exports.object({
|
|
23964
24113
|
historicalAccess: external_exports.string(),
|
|
23965
|
-
modelJudgeConsent:
|
|
23966
|
-
historySyncConsent:
|
|
24114
|
+
modelJudgeConsent: ModelJudgeConsentChoice,
|
|
24115
|
+
historySyncConsent: HistorySyncConsentChoice,
|
|
23967
24116
|
vaultConsent: external_exports.string(),
|
|
23968
24117
|
vaultInlineReveal: external_exports.string()
|
|
23969
24118
|
});
|
|
@@ -24113,9 +24262,9 @@ function deriveReviewReasons(trust, transports) {
|
|
|
24113
24262
|
if (transports.includes("http") || transports.includes("ws")) reasons.push("plaintext_transport");
|
|
24114
24263
|
return reasons;
|
|
24115
24264
|
}
|
|
24116
|
-
function buildReviewInfo(trust, transports) {
|
|
24265
|
+
function buildReviewInfo(trust, transports, decided) {
|
|
24117
24266
|
const reasons = deriveReviewReasons(trust, transports);
|
|
24118
|
-
return { needsReview: reasons.length > 0, reasons };
|
|
24267
|
+
return { needsReview: reasons.length > 0 && !decided, reasons };
|
|
24119
24268
|
}
|
|
24120
24269
|
function distinctTransports(transports) {
|
|
24121
24270
|
return Array.from(new Set(transports));
|
|
@@ -24186,8 +24335,8 @@ function tightenPerms(file2) {
|
|
|
24186
24335
|
}
|
|
24187
24336
|
|
|
24188
24337
|
// ../../packages/persistence/src/database.ts
|
|
24189
|
-
import { randomUUID as
|
|
24190
|
-
import { join as
|
|
24338
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
24339
|
+
import { dirname as dirname2, join as join7, sep } from "path";
|
|
24191
24340
|
import { DatabaseSync } from "node:sqlite";
|
|
24192
24341
|
|
|
24193
24342
|
// ../../packages/persistence/src/ids.ts
|
|
@@ -24431,6 +24580,10 @@ function allRows(stmt, params) {
|
|
|
24431
24580
|
if (Array.isArray(params)) return stmt.all(...params);
|
|
24432
24581
|
return stmt.all(params);
|
|
24433
24582
|
}
|
|
24583
|
+
function* iterateRows(stmt, params) {
|
|
24584
|
+
const rows = params === void 0 ? stmt.iterate() : Array.isArray(params) ? stmt.iterate(...params) : stmt.iterate(params);
|
|
24585
|
+
for (const row of rows) yield row;
|
|
24586
|
+
}
|
|
24434
24587
|
function getRow(stmt, params) {
|
|
24435
24588
|
if (params === void 0) return stmt.get();
|
|
24436
24589
|
if (Array.isArray(params)) return stmt.get(...params);
|
|
@@ -24899,10 +25052,17 @@ function ensureSyncedAtColumn(db, table2) {
|
|
|
24899
25052
|
if (!columns.includes("sync_claimed_at")) {
|
|
24900
25053
|
db.exec(`ALTER TABLE ${table2} ADD COLUMN sync_claimed_at integer`);
|
|
24901
25054
|
}
|
|
25055
|
+
if (!columns.includes("outbox_owed")) {
|
|
25056
|
+
db.exec(`ALTER TABLE ${table2} ADD COLUMN outbox_owed integer`);
|
|
25057
|
+
}
|
|
24902
25058
|
db.exec(
|
|
24903
25059
|
`CREATE INDEX IF NOT EXISTS idx_audit_events_sync
|
|
24904
25060
|
ON audit_events (event_type, synced_at, sync_claimed_at, started_at)`
|
|
24905
25061
|
);
|
|
25062
|
+
db.exec(
|
|
25063
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_outbox_owed
|
|
25064
|
+
ON audit_events (event_type, synced_at, sync_claimed_at, started_at) WHERE outbox_owed = 1`
|
|
25065
|
+
);
|
|
24906
25066
|
db.exec(
|
|
24907
25067
|
`CREATE INDEX IF NOT EXISTS idx_audit_claimed
|
|
24908
25068
|
ON audit_events (sync_claimed_at) WHERE sync_claimed_at IS NOT NULL`
|
|
@@ -25007,7 +25167,6 @@ function decodeKeysetCursor(cursor) {
|
|
|
25007
25167
|
// ../../packages/persistence/src/repositories/activity.ts
|
|
25008
25168
|
var DAY_MS = 864e5;
|
|
25009
25169
|
var LIVE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
25010
|
-
var LAST_ACTIVITY_EXPR = `max(started_at, coalesce(ended_at, started_at))`;
|
|
25011
25170
|
function defaultTimeZone() {
|
|
25012
25171
|
try {
|
|
25013
25172
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
@@ -25062,6 +25221,7 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
25062
25221
|
error: "error",
|
|
25063
25222
|
active: "active"
|
|
25064
25223
|
};
|
|
25224
|
+
var TIMELINE_EVENT_TYPES = Object.keys(DB_EVENT_TYPE_TO_KIND).map((kind) => `'${kind}'`).join(", ");
|
|
25065
25225
|
function safeParseStringArray(raw) {
|
|
25066
25226
|
if (!raw) return [];
|
|
25067
25227
|
const parsed = safeJson(raw, null);
|
|
@@ -25135,6 +25295,37 @@ var TIMELINE_COLUMNS = `
|
|
|
25135
25295
|
json_extract(attributes, '$.targetId') AS target_id,
|
|
25136
25296
|
json_extract(attributes, '$.internal') AS internal,
|
|
25137
25297
|
json_extract(attributes, '$.flagged') AS flagged`;
|
|
25298
|
+
var LLM_USAGE_SELECT = `
|
|
25299
|
+
SELECT root_session_id AS sessionId,
|
|
25300
|
+
provider,
|
|
25301
|
+
model,
|
|
25302
|
+
service_tier AS serviceTier,
|
|
25303
|
+
coalesce(sum(input_tokens), 0) AS inputTokens,
|
|
25304
|
+
coalesce(sum(output_tokens), 0) AS outputTokens,
|
|
25305
|
+
coalesce(sum(cache_creation_input_tokens), 0) AS cacheCreationTokens,
|
|
25306
|
+
coalesce(sum(cache_read_input_tokens), 0) AS cacheReadTokens,
|
|
25307
|
+
coalesce(sum(ephemeral_1h_input_tokens), 0) AS ephemeral1hTokens,
|
|
25308
|
+
coalesce(sum(ephemeral_5m_input_tokens), 0) AS ephemeral5mTokens,
|
|
25309
|
+
coalesce(sum(web_search_requests), 0) AS webSearchRequests`;
|
|
25310
|
+
var LLM_USAGE_SCOPE = `event_type = 'llm_call' AND attributes IS NOT NULL AND root_session_id IS NOT NULL`;
|
|
25311
|
+
var LLM_USAGE_GROUP = `GROUP BY root_session_id, provider, model, service_tier`;
|
|
25312
|
+
function usageLeaves(rows) {
|
|
25313
|
+
return rows.map((row) => {
|
|
25314
|
+
const attributes = {
|
|
25315
|
+
input_tokens: row.inputTokens,
|
|
25316
|
+
output_tokens: row.outputTokens,
|
|
25317
|
+
cache_creation_input_tokens: row.cacheCreationTokens,
|
|
25318
|
+
cache_read_input_tokens: row.cacheReadTokens,
|
|
25319
|
+
ephemeral_1h_input_tokens: row.ephemeral1hTokens,
|
|
25320
|
+
ephemeral_5m_input_tokens: row.ephemeral5mTokens,
|
|
25321
|
+
web_search_requests: row.webSearchRequests
|
|
25322
|
+
};
|
|
25323
|
+
if (row.provider !== null) attributes.provider = row.provider;
|
|
25324
|
+
if (row.model !== null) attributes.model = row.model;
|
|
25325
|
+
if (row.serviceTier !== null) attributes.service_tier = row.serviceTier;
|
|
25326
|
+
return { sessionId: row.sessionId, attributes };
|
|
25327
|
+
});
|
|
25328
|
+
}
|
|
25138
25329
|
var SESSION_ROOT = `event_type = 'session'`;
|
|
25139
25330
|
var HAS_ACTIVITY = `EXISTS (
|
|
25140
25331
|
SELECT 1 FROM audit_events c
|
|
@@ -25160,16 +25351,17 @@ var SqliteActivityRepository = class {
|
|
|
25160
25351
|
const liveThreshold = this.now() - LIVE_ACTIVITY_WINDOW_MS;
|
|
25161
25352
|
const liveNow = countScalar(
|
|
25162
25353
|
this.db,
|
|
25163
|
-
`SELECT count(*) AS n FROM audit_events s
|
|
25354
|
+
`SELECT count(*) AS n FROM audit_events s INDEXED BY sqlite_autoindex_audit_events_1
|
|
25164
25355
|
WHERE s.event_type = 'session' AND s.ended_at IS NULL
|
|
25165
|
-
AND
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25356
|
+
AND s.id IN (
|
|
25357
|
+
SELECT id FROM audit_events WHERE event_type = 'session' AND started_at >= ?
|
|
25358
|
+
UNION
|
|
25359
|
+
SELECT root_session_id FROM audit_events INDEXED BY idx_audit_started_at
|
|
25360
|
+
WHERE started_at >= ?
|
|
25361
|
+
UNION
|
|
25362
|
+
SELECT root_session_id FROM audit_events INDEXED BY idx_audit_ended_at
|
|
25363
|
+
WHERE ended_at >= ?)`,
|
|
25364
|
+
[liveThreshold, liveThreshold, liveThreshold]
|
|
25173
25365
|
);
|
|
25174
25366
|
const toolCallsToday = countScalar(
|
|
25175
25367
|
this.db,
|
|
@@ -25299,7 +25491,7 @@ var SqliteActivityRepository = class {
|
|
|
25299
25491
|
this.db.prepare(
|
|
25300
25492
|
`SELECT ${TIMELINE_COLUMNS}
|
|
25301
25493
|
FROM audit_events
|
|
25302
|
-
WHERE id = ? OR root_session_id = ?
|
|
25494
|
+
WHERE (id = ? OR root_session_id = ?) AND event_type IN (${TIMELINE_EVENT_TYPES})
|
|
25303
25495
|
ORDER BY started_at ASC, id ASC`
|
|
25304
25496
|
),
|
|
25305
25497
|
[sessionId, sessionId]
|
|
@@ -25312,14 +25504,14 @@ var SqliteActivityRepository = class {
|
|
|
25312
25504
|
coalesce(sum(output_tokens), 0) AS output,
|
|
25313
25505
|
coalesce(sum(cache_creation_input_tokens), 0) AS cache_creation,
|
|
25314
25506
|
coalesce(sum(cache_read_input_tokens), 0) AS cache_read
|
|
25315
|
-
FROM audit_events
|
|
25507
|
+
FROM audit_events INDEXED BY idx_audit_session_type
|
|
25316
25508
|
WHERE root_session_id = ? AND event_type = 'llm_call'`
|
|
25317
25509
|
),
|
|
25318
25510
|
[sessionId]
|
|
25319
25511
|
) ?? { input: 0, output: 0, cache_creation: 0, cache_read: 0 };
|
|
25320
25512
|
const primaryModel = getRow(
|
|
25321
25513
|
this.db.prepare(
|
|
25322
|
-
`SELECT model, provider FROM audit_events
|
|
25514
|
+
`SELECT model, provider FROM audit_events INDEXED BY idx_audit_session_type
|
|
25323
25515
|
WHERE root_session_id = ? AND event_type = 'llm_call'
|
|
25324
25516
|
ORDER BY started_at ASC, id ASC
|
|
25325
25517
|
LIMIT 1`
|
|
@@ -25330,7 +25522,7 @@ var SqliteActivityRepository = class {
|
|
|
25330
25522
|
this.db.prepare(
|
|
25331
25523
|
`SELECT coalesce(json_extract(attributes, '$.tool_name'), json_extract(attributes, '$.tool')) AS tool,
|
|
25332
25524
|
count(*) AS n
|
|
25333
|
-
FROM audit_events
|
|
25525
|
+
FROM audit_events INDEXED BY idx_audit_session
|
|
25334
25526
|
WHERE root_session_id = ? AND event_type = 'tool_call'
|
|
25335
25527
|
GROUP BY coalesce(json_extract(attributes, '$.tool_name'), json_extract(attributes, '$.tool'))`
|
|
25336
25528
|
),
|
|
@@ -25338,7 +25530,7 @@ var SqliteActivityRepository = class {
|
|
|
25338
25530
|
);
|
|
25339
25531
|
const modelRows = allRows(
|
|
25340
25532
|
this.db.prepare(
|
|
25341
|
-
`SELECT DISTINCT model FROM audit_events
|
|
25533
|
+
`SELECT DISTINCT model FROM audit_events INDEXED BY idx_audit_session_type
|
|
25342
25534
|
WHERE root_session_id = ? AND event_type = 'llm_call' AND model IS NOT NULL AND model <> ''
|
|
25343
25535
|
ORDER BY model`
|
|
25344
25536
|
),
|
|
@@ -25347,7 +25539,7 @@ var SqliteActivityRepository = class {
|
|
|
25347
25539
|
const derivedModels = modelRows.map((r) => r.model);
|
|
25348
25540
|
const commits = countScalar(
|
|
25349
25541
|
this.db,
|
|
25350
|
-
`SELECT count(*) AS n FROM audit_events
|
|
25542
|
+
`SELECT count(*) AS n FROM audit_events INDEXED BY idx_audit_session
|
|
25351
25543
|
WHERE root_session_id = ? AND event_type = 'commit'`,
|
|
25352
25544
|
[sessionId]
|
|
25353
25545
|
);
|
|
@@ -25383,25 +25575,57 @@ var SqliteActivityRepository = class {
|
|
|
25383
25575
|
return Promise.resolve(session);
|
|
25384
25576
|
}
|
|
25385
25577
|
/**
|
|
25386
|
-
* Cross-session token report — every `llm_call`
|
|
25387
|
-
* `started_at >= fromMs`
|
|
25388
|
-
* USD cost DERIVED at read time via the shared
|
|
25389
|
-
*
|
|
25390
|
-
*
|
|
25391
|
-
*
|
|
25578
|
+
* Cross-session token report — every `llm_call` in the store (or in a
|
|
25579
|
+
* `started_at >= fromMs` window, the Activity page's range) grouped per
|
|
25580
|
+
* session, with USD cost DERIVED at read time via the shared
|
|
25581
|
+
* `defaultCostModel` (never stored). The caller collapses these onto
|
|
25582
|
+
* per-model rows with `aggregateTokenUsage`.
|
|
25583
|
+
*
|
|
25584
|
+
* Grouped in SQL over `idx_audit_llm_usage` — one entry per call carrying
|
|
25585
|
+
* the members the rollup sums — and priced once per group, which is exact
|
|
25586
|
+
* (see LlmUsageRow). Reading the bags and folding them in JS measured 31 ms
|
|
25587
|
+
* for a seven-day window at 50k calls, and naming the VIRTUAL columns
|
|
25588
|
+
* against the table 40 ms, since each is a json_extract recomputed per row;
|
|
25589
|
+
* the index stores the values once, at write, and answers the same window in
|
|
25590
|
+
* 8.5 ms. `INDEXED BY` is deliberate: with or without ANALYZE statistics the
|
|
25591
|
+
* planner prefers the general event-type index and fetches every row to
|
|
25592
|
+
* recompute the columns it could have read. The index is one every open
|
|
25593
|
+
* store carries, since opening runs the migrations, so the hard requirement
|
|
25594
|
+
* `INDEXED BY` introduces is already met; token-rollup-plans.test.ts pins
|
|
25595
|
+
* the plan. All-time is a scan of the whole index — still one narrow entry
|
|
25596
|
+
* per call, no bag parsed.
|
|
25392
25597
|
*/
|
|
25393
25598
|
tokenReports(fromMs) {
|
|
25394
|
-
const
|
|
25395
|
-
|
|
25599
|
+
const rows = allRows(
|
|
25600
|
+
this.db.prepare(
|
|
25601
|
+
`${LLM_USAGE_SELECT}
|
|
25602
|
+
FROM audit_events INDEXED BY idx_audit_llm_usage
|
|
25603
|
+
WHERE ${LLM_USAGE_SCOPE}${fromMs === void 0 ? "" : " AND started_at >= ?"}
|
|
25604
|
+
${LLM_USAGE_GROUP}`
|
|
25605
|
+
),
|
|
25606
|
+
fromMs === void 0 ? void 0 : [fromMs]
|
|
25607
|
+
);
|
|
25608
|
+
return Promise.resolve(buildTokenReports(usageLeaves(rows), defaultCostModel));
|
|
25396
25609
|
}
|
|
25397
25610
|
/**
|
|
25398
|
-
* One session's token report — its `llm_call`
|
|
25399
|
-
* model) with derived cost, or `null` when the session made no
|
|
25400
|
-
* (an empty/tool-only session). Feeds the session-detail pane's
|
|
25401
|
-
* breakdown + estimated cost.
|
|
25611
|
+
* One session's token report — its `llm_call`s grouped per (provider,
|
|
25612
|
+
* model, tier) with derived cost, or `null` when the session made no
|
|
25613
|
+
* `llm_call`s (an empty/tool-only session). Feeds the session-detail pane's
|
|
25614
|
+
* per-model breakdown + estimated cost. The same rollup as `tokenReports`,
|
|
25615
|
+
* seeking one root through a root-led `llm_call` index; the bag-reading fold
|
|
25616
|
+
* it replaces walked every `llm_call` in the store to find one session's.
|
|
25402
25617
|
*/
|
|
25403
25618
|
tokenReportForSession(sessionId) {
|
|
25404
|
-
const
|
|
25619
|
+
const rows = allRows(
|
|
25620
|
+
this.db.prepare(
|
|
25621
|
+
`${LLM_USAGE_SELECT}
|
|
25622
|
+
FROM audit_events
|
|
25623
|
+
WHERE ${LLM_USAGE_SCOPE} AND root_session_id = ?
|
|
25624
|
+
${LLM_USAGE_GROUP}`
|
|
25625
|
+
),
|
|
25626
|
+
[sessionId]
|
|
25627
|
+
);
|
|
25628
|
+
const reports = buildTokenReports(usageLeaves(rows), defaultCostModel);
|
|
25405
25629
|
return Promise.resolve(reports[0] ?? null);
|
|
25406
25630
|
}
|
|
25407
25631
|
/**
|
|
@@ -25425,42 +25649,6 @@ var SqliteActivityRepository = class {
|
|
|
25425
25649
|
for (const row of rows) seen.add(toHarness(row.harness));
|
|
25426
25650
|
return Promise.resolve([...seen]);
|
|
25427
25651
|
}
|
|
25428
|
-
/**
|
|
25429
|
-
* The raw `llm_call` leaves (session id + parsed attribute bag) for the token
|
|
25430
|
-
* rollups, optionally narrowed to one session and/or a `started_at >= fromMs`
|
|
25431
|
-
* window. A leaf whose attributes blob is NULL or unparseable is skipped
|
|
25432
|
-
* (best-effort read — a corrupt bag never breaks the report). `root_session_id`
|
|
25433
|
-
* is the leaf's session (the reconciler sets parent_id = root_session_id).
|
|
25434
|
-
*/
|
|
25435
|
-
readLlmCallLeaves(opts = {}) {
|
|
25436
|
-
const conditions = ["event_type = 'llm_call'", "attributes IS NOT NULL"];
|
|
25437
|
-
const params = [];
|
|
25438
|
-
if (opts.sessionId !== void 0) {
|
|
25439
|
-
conditions.push("root_session_id = ?");
|
|
25440
|
-
params.push(opts.sessionId);
|
|
25441
|
-
}
|
|
25442
|
-
if (opts.fromMs !== void 0) {
|
|
25443
|
-
conditions.push("started_at >= ?");
|
|
25444
|
-
params.push(opts.fromMs);
|
|
25445
|
-
}
|
|
25446
|
-
const rows = allRows(
|
|
25447
|
-
this.db.prepare(
|
|
25448
|
-
`SELECT root_session_id AS sessionId, attributes
|
|
25449
|
-
FROM audit_events
|
|
25450
|
-
WHERE ${conditions.join(" AND ")}`
|
|
25451
|
-
),
|
|
25452
|
-
params
|
|
25453
|
-
);
|
|
25454
|
-
return mapRowsTolerant(
|
|
25455
|
-
rows.filter(
|
|
25456
|
-
(row) => row.sessionId !== null
|
|
25457
|
-
),
|
|
25458
|
-
(row) => ({
|
|
25459
|
-
sessionId: row.sessionId,
|
|
25460
|
-
attributes: JSON.parse(row.attributes)
|
|
25461
|
-
})
|
|
25462
|
-
);
|
|
25463
|
-
}
|
|
25464
25652
|
/**
|
|
25465
25653
|
* Per-session turns/findings/shares + last-activity for a page of session ids,
|
|
25466
25654
|
* in grouped queries (not one per row). An id with no matching rows still
|
|
@@ -25475,20 +25663,23 @@ var SqliteActivityRepository = class {
|
|
|
25475
25663
|
const inClause = placeholders(sessionIds.length);
|
|
25476
25664
|
const lastActivityRows = allRows(
|
|
25477
25665
|
this.db.prepare(
|
|
25478
|
-
`SELECT
|
|
25479
|
-
|
|
25480
|
-
|
|
25666
|
+
`SELECT ids.value AS id,
|
|
25667
|
+
(SELECT max(started_at) FROM audit_events e WHERE e.root_session_id = ids.value) AS ms,
|
|
25668
|
+
(SELECT max(ended_at) FROM audit_events e
|
|
25669
|
+
WHERE e.root_session_id = ids.value AND e.ended_at IS NOT NULL) AS me
|
|
25670
|
+
FROM json_each(?) AS ids`
|
|
25481
25671
|
),
|
|
25482
|
-
sessionIds
|
|
25672
|
+
[JSON.stringify(sessionIds)]
|
|
25483
25673
|
);
|
|
25484
25674
|
for (const row of lastActivityRows) {
|
|
25485
|
-
if (row.id === null) continue;
|
|
25486
25675
|
const entry = result.get(row.id);
|
|
25487
|
-
|
|
25676
|
+
const last = Math.max(row.ms ?? 0, row.me ?? 0);
|
|
25677
|
+
if (entry && last > 0) entry.lastActivityMs = last;
|
|
25488
25678
|
}
|
|
25489
25679
|
const turnsRows = allRows(
|
|
25490
25680
|
this.db.prepare(
|
|
25491
|
-
`SELECT root_session_id AS id, count(*) AS n
|
|
25681
|
+
`SELECT root_session_id AS id, count(*) AS n
|
|
25682
|
+
FROM audit_events INDEXED BY idx_audit_session_prompt
|
|
25492
25683
|
WHERE root_session_id IN (${inClause}) AND event_type = 'prompt'
|
|
25493
25684
|
GROUP BY root_session_id`
|
|
25494
25685
|
),
|
|
@@ -25503,7 +25694,7 @@ var SqliteActivityRepository = class {
|
|
|
25503
25694
|
this.db.prepare(
|
|
25504
25695
|
`SELECT root_session_id AS id,
|
|
25505
25696
|
count(DISTINCT json_extract(attributes, '$.run_key')) AS n
|
|
25506
|
-
FROM audit_events
|
|
25697
|
+
FROM audit_events INDEXED BY idx_audit_session_run_key
|
|
25507
25698
|
WHERE root_session_id IN (${inClause}) AND event_type = 'llm_call'
|
|
25508
25699
|
AND json_extract(attributes, '$.run_key') IS NOT NULL
|
|
25509
25700
|
GROUP BY root_session_id`
|
|
@@ -25533,7 +25724,7 @@ var SqliteActivityRepository = class {
|
|
|
25533
25724
|
this.db.prepare(
|
|
25534
25725
|
`SELECT root_session_id AS id,
|
|
25535
25726
|
count(DISTINCT json_extract(attributes, '$.destination')) AS n
|
|
25536
|
-
FROM audit_events
|
|
25727
|
+
FROM audit_events INDEXED BY idx_audit_session_share
|
|
25537
25728
|
WHERE root_session_id IN (${inClause}) AND event_type = 'share'
|
|
25538
25729
|
GROUP BY root_session_id`
|
|
25539
25730
|
),
|
|
@@ -26562,7 +26753,6 @@ var LATEST_RESOLUTION_BY_KEY_SQL = `(
|
|
|
26562
26753
|
|
|
26563
26754
|
// ../../packages/persistence/src/repositories/findings.ts
|
|
26564
26755
|
var PREVIEW_INSTANCES_PER_GROUP = 200;
|
|
26565
|
-
var SCAN_BATCH_ROWS = 1e3;
|
|
26566
26756
|
var DEFAULT_LOCATIONS_LIMIT = 100;
|
|
26567
26757
|
var LOCATION_RULE_IDS_CAP = 20;
|
|
26568
26758
|
function compareLocationOrder(a, b) {
|
|
@@ -26591,6 +26781,25 @@ function deriveInstanceStatus(row) {
|
|
|
26591
26781
|
latestResolutionStatus: row.latest_status
|
|
26592
26782
|
});
|
|
26593
26783
|
}
|
|
26784
|
+
function toFlatFindingRow(r) {
|
|
26785
|
+
return {
|
|
26786
|
+
id: r.id,
|
|
26787
|
+
ruleId: r.rule_id,
|
|
26788
|
+
category: r.category,
|
|
26789
|
+
severity: r.severity,
|
|
26790
|
+
maskedMatch: r.masked_match,
|
|
26791
|
+
actionTaken: r.action_taken,
|
|
26792
|
+
confidence: r.confidence,
|
|
26793
|
+
occurredAt: epochMillisToIso(r.occurred_at),
|
|
26794
|
+
sourceTool: r.source_tool,
|
|
26795
|
+
repo: r.repo ?? "",
|
|
26796
|
+
file: r.file ?? "",
|
|
26797
|
+
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
26798
|
+
eventId: r.event_id,
|
|
26799
|
+
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
26800
|
+
status: deriveInstanceStatus(r)
|
|
26801
|
+
};
|
|
26802
|
+
}
|
|
26594
26803
|
function encodeGroupCursor(group) {
|
|
26595
26804
|
const payload = {
|
|
26596
26805
|
sev: group.severity,
|
|
@@ -26666,7 +26875,7 @@ var SqliteFindingsRepository = class {
|
|
|
26666
26875
|
this.db.prepare(
|
|
26667
26876
|
`SELECT f.id, f.audit_event_id AS event_id, d.rule_id, d.category, d.severity,
|
|
26668
26877
|
f.masked_match, f.action_taken, f.confidence, e.started_at AS occurred_at,
|
|
26669
|
-
|
|
26878
|
+
e.source_tool AS source_tool,
|
|
26670
26879
|
e.event_type AS kind
|
|
26671
26880
|
FROM audit_events e
|
|
26672
26881
|
CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
|
|
@@ -26774,56 +26983,11 @@ var SqliteFindingsRepository = class {
|
|
|
26774
26983
|
predicate,
|
|
26775
26984
|
params: sessionParams
|
|
26776
26985
|
});
|
|
26777
|
-
const rows =
|
|
26778
|
-
|
|
26779
|
-
|
|
26780
|
-
|
|
26781
|
-
|
|
26782
|
-
FROM (
|
|
26783
|
-
SELECT f.id AS id, d.rule_id AS rule_id, d.category AS category,
|
|
26784
|
-
d.severity AS severity, f.masked_match AS masked_match,
|
|
26785
|
-
f.action_taken AS action_taken, f.confidence AS confidence,
|
|
26786
|
-
e.started_at AS occurred_at,
|
|
26787
|
-
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
26788
|
-
json_extract(e.attributes, '$.repo') AS repo,
|
|
26789
|
-
json_extract(e.attributes, '$.file_path') AS file,
|
|
26790
|
-
json_extract(e.attributes, '$.tool_name') AS tool_name,
|
|
26791
|
-
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
26792
|
-
e.event_type AS kind, f.finding_key AS finding_key,
|
|
26793
|
-
latest.status AS latest_status,
|
|
26794
|
-
ROW_NUMBER() OVER (
|
|
26795
|
-
PARTITION BY d.rule_id
|
|
26796
|
-
ORDER BY e.started_at DESC, f.id DESC
|
|
26797
|
-
) AS rn
|
|
26798
|
-
FROM inspection_findings f
|
|
26799
|
-
JOIN audit_events e ON e.id = f.audit_event_id
|
|
26800
|
-
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
26801
|
-
LEFT JOIN ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
26802
|
-
ON latest.finding_key = f.finding_key
|
|
26803
|
-
${predicate}
|
|
26804
|
-
)
|
|
26805
|
-
WHERE rn <= :cap
|
|
26806
|
-
ORDER BY occurred_at DESC, id DESC`
|
|
26807
|
-
),
|
|
26808
|
-
{ cap: PREVIEW_INSTANCES_PER_GROUP, ...sessionParams }
|
|
26809
|
-
);
|
|
26810
|
-
const groupable = rows.map((r) => ({
|
|
26811
|
-
id: r.id,
|
|
26812
|
-
ruleId: r.rule_id,
|
|
26813
|
-
category: r.category,
|
|
26814
|
-
severity: r.severity,
|
|
26815
|
-
maskedMatch: r.masked_match,
|
|
26816
|
-
actionTaken: r.action_taken,
|
|
26817
|
-
confidence: r.confidence,
|
|
26818
|
-
occurredAt: epochMillisToIso(r.occurred_at),
|
|
26819
|
-
sourceTool: r.source_tool,
|
|
26820
|
-
repo: r.repo ?? "",
|
|
26821
|
-
file: r.file ?? "",
|
|
26822
|
-
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
26823
|
-
eventId: r.event_id,
|
|
26824
|
-
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
26825
|
-
status: deriveInstanceStatus(r)
|
|
26826
|
-
}));
|
|
26986
|
+
const rows = this.previewRows(aggregates, {
|
|
26987
|
+
sessionId: query.sessionId,
|
|
26988
|
+
from: query.from
|
|
26989
|
+
});
|
|
26990
|
+
const groupable = rows.map(toFlatFindingRow);
|
|
26827
26991
|
const allGroups = buildFindingGroups(groupable, { aggregates });
|
|
26828
26992
|
const filterOpts = {
|
|
26829
26993
|
severity: query.severity,
|
|
@@ -26909,8 +27073,10 @@ var SqliteFindingsRepository = class {
|
|
|
26909
27073
|
*
|
|
26910
27074
|
* The scan runs from the top of the scope on every request, not from the
|
|
26911
27075
|
* cursor: `totals` and `facets` describe the whole filtered scope and must not
|
|
26912
|
-
* move as the caller pages. Rows
|
|
26913
|
-
*
|
|
27076
|
+
* move as the caller pages. Rows come off ONE statement, iterated rather
|
|
27077
|
+
* than materialized (`scanFindingRows`), so memory stays flat while the
|
|
27078
|
+
* counting runs — a generator streaming the index order, not a sequence of
|
|
27079
|
+
* fetched batches; only the page itself is retained.
|
|
26914
27080
|
*/
|
|
26915
27081
|
listFindingInstances(query) {
|
|
26916
27082
|
const opts = {
|
|
@@ -26926,6 +27092,10 @@ var SqliteFindingsRepository = class {
|
|
|
26926
27092
|
};
|
|
26927
27093
|
const limit = query.limit ?? DEFAULT_FLAT_FINDINGS_LIMIT;
|
|
26928
27094
|
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
27095
|
+
const isPastCursor = cursor === null ? () => true : (row) => {
|
|
27096
|
+
const rowMs = isoToEpochMillis(row.occurredAt);
|
|
27097
|
+
return rowMs <= cursor.startedAtMs && (rowMs < cursor.startedAtMs || row.id < cursor.id);
|
|
27098
|
+
};
|
|
26929
27099
|
const accumulator = createInstanceFacetAccumulator(opts);
|
|
26930
27100
|
const items = [];
|
|
26931
27101
|
let total = 0;
|
|
@@ -26938,6 +27108,7 @@ var SqliteFindingsRepository = class {
|
|
|
26938
27108
|
accumulator.add(row);
|
|
26939
27109
|
if (!matchesInstanceFilters(row, opts)) continue;
|
|
26940
27110
|
total += 1;
|
|
27111
|
+
if (!isPastCursor(row)) continue;
|
|
26941
27112
|
if (items.length < limit) {
|
|
26942
27113
|
items.push(toInstanceDetail(row));
|
|
26943
27114
|
last = row;
|
|
@@ -26946,15 +27117,6 @@ var SqliteFindingsRepository = class {
|
|
|
26946
27117
|
}
|
|
26947
27118
|
}
|
|
26948
27119
|
const nextCursor = hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null;
|
|
26949
|
-
if (cursor !== null) {
|
|
26950
|
-
const resumed = this.pageAfter(cursor, opts, limit, query);
|
|
26951
|
-
return Promise.resolve({
|
|
26952
|
-
totals: { findings: total },
|
|
26953
|
-
facets: accumulator.facets(),
|
|
26954
|
-
items: resumed.items,
|
|
26955
|
-
nextCursor: resumed.nextCursor
|
|
26956
|
-
});
|
|
26957
|
-
}
|
|
26958
27120
|
return Promise.resolve({
|
|
26959
27121
|
totals: { findings: total },
|
|
26960
27122
|
facets: accumulator.facets(),
|
|
@@ -26962,35 +27124,6 @@ var SqliteFindingsRepository = class {
|
|
|
26962
27124
|
nextCursor
|
|
26963
27125
|
});
|
|
26964
27126
|
}
|
|
26965
|
-
/**
|
|
26966
|
-
* The page of matching rows strictly after `cursor`. Separate from the
|
|
26967
|
-
* counting pass because that one starts at the top of the scope by design;
|
|
26968
|
-
* this one narrows the scan with the same keyset predicate the activity list
|
|
26969
|
-
* uses, so a later page costs less than the first rather than more.
|
|
26970
|
-
*/
|
|
26971
|
-
pageAfter(cursor, opts, limit, query) {
|
|
26972
|
-
const items = [];
|
|
26973
|
-
let last;
|
|
26974
|
-
let hasMore = false;
|
|
26975
|
-
for (const row of this.scanFindingRows({
|
|
26976
|
-
sessionId: query.sessionId,
|
|
26977
|
-
from: query.from,
|
|
26978
|
-
after: cursor
|
|
26979
|
-
})) {
|
|
26980
|
-
if (!matchesInstanceFilters(row, opts)) continue;
|
|
26981
|
-
if (items.length < limit) {
|
|
26982
|
-
items.push(toInstanceDetail(row));
|
|
26983
|
-
last = row;
|
|
26984
|
-
} else {
|
|
26985
|
-
hasMore = true;
|
|
26986
|
-
break;
|
|
26987
|
-
}
|
|
26988
|
-
}
|
|
26989
|
-
return {
|
|
26990
|
-
items,
|
|
26991
|
-
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null
|
|
26992
|
-
};
|
|
26993
|
-
}
|
|
26994
27127
|
/**
|
|
26995
27128
|
* The same findings folded by location: repository, then file within it.
|
|
26996
27129
|
*
|
|
@@ -27073,25 +27206,111 @@ var SqliteFindingsRepository = class {
|
|
|
27073
27206
|
});
|
|
27074
27207
|
}
|
|
27075
27208
|
/**
|
|
27076
|
-
*
|
|
27209
|
+
* Each group's newest instances, for the table's expanded rows.
|
|
27210
|
+
*
|
|
27211
|
+
* ONE index-ordered scan with early termination, and the shape is the point.
|
|
27212
|
+
* The natural spelling — `ROW_NUMBER() OVER (PARTITION BY rule_id ORDER BY
|
|
27213
|
+
* started_at DESC)` then `WHERE rn <= cap` — sorts EVERY finding in scope
|
|
27214
|
+
* through a temp B-tree to keep a bounded preview of each group, and then
|
|
27215
|
+
* sorts the survivors again for the page order. Both sorts grow with the
|
|
27216
|
+
* store while the answer does not.
|
|
27217
|
+
*
|
|
27218
|
+
* Instead the scan walks `audit_events` newest-first off `idx_audit_started_at`
|
|
27219
|
+
* (or the session or window index the scope names — see `findingScanSql`),
|
|
27220
|
+
* which is already the order the page wants, and keeps rows per rule until
|
|
27221
|
+
* each rule has as many as it can show. The aggregate the caller already holds
|
|
27222
|
+
* says how many that is: `min(instanceCount, PREVIEW_INSTANCES_PER_GROUP)`
|
|
27223
|
+
* per rule, summed, is the number of rows this scan has to find, and it stops
|
|
27224
|
+
* on the last one. That sum is bounded by `rules * PREVIEW_INSTANCES_PER_GROUP`
|
|
27225
|
+
* (8,000 at this repo's 40-rule bench corpus), not by a fixed row count — a
|
|
27226
|
+
* store with many firing rules widens it. The bound that DOES hold
|
|
27227
|
+
* unconditionally is the sorted form's floor: this scan visits at most as
|
|
27228
|
+
* many rows as `ROW_NUMBER() OVER (PARTITION BY rule_id …)` would have
|
|
27229
|
+
* sorted, and stops the moment every rule has its cap, where the sorted form
|
|
27230
|
+
* sorts the whole scope regardless. The true worst case — the rarest rule's
|
|
27231
|
+
* wanted instances sitting at the tail of the scope — is one pass over
|
|
27232
|
+
* everything in scope with a block sort of the id tie-break only, never a
|
|
27233
|
+
* sort of the scope, which is still that floor.
|
|
27234
|
+
*
|
|
27235
|
+
* A row whose rule the aggregate did not see is skipped: the two statements
|
|
27236
|
+
* run without a shared snapshot, so a capture landing between them can add a
|
|
27237
|
+
* rule here that has no counts there, and the counts are what the group is
|
|
27238
|
+
* built from.
|
|
27239
|
+
*/
|
|
27240
|
+
previewRows(aggregates, scope) {
|
|
27241
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
27242
|
+
let remaining = 0;
|
|
27243
|
+
for (const [ruleId, agg] of aggregates) {
|
|
27244
|
+
const n = Math.min(agg.instanceCount, PREVIEW_INSTANCES_PER_GROUP);
|
|
27245
|
+
wanted.set(ruleId, n);
|
|
27246
|
+
remaining += n;
|
|
27247
|
+
}
|
|
27248
|
+
const rows = [];
|
|
27249
|
+
if (remaining === 0) return rows;
|
|
27250
|
+
const { sql, params } = this.findingScanSql(scope);
|
|
27251
|
+
const taken = /* @__PURE__ */ new Map();
|
|
27252
|
+
for (const r of iterateRows(this.db.prepare(sql), params)) {
|
|
27253
|
+
const want = wanted.get(r.rule_id);
|
|
27254
|
+
if (want === void 0) continue;
|
|
27255
|
+
const have = taken.get(r.rule_id) ?? 0;
|
|
27256
|
+
if (have >= want) continue;
|
|
27257
|
+
taken.set(r.rule_id, have + 1);
|
|
27258
|
+
rows.push(r);
|
|
27259
|
+
remaining -= 1;
|
|
27260
|
+
if (remaining === 0) break;
|
|
27261
|
+
}
|
|
27262
|
+
return rows;
|
|
27263
|
+
}
|
|
27264
|
+
/**
|
|
27265
|
+
* Every finding in scope as a FlatFindingRow, newest first, streamed.
|
|
27077
27266
|
*
|
|
27078
27267
|
* A generator so a caller streams the scope without it ever being an array:
|
|
27079
27268
|
* the flat list counts and facets the whole filtered scope, which on a large
|
|
27080
|
-
* store is far more rows than any page.
|
|
27081
|
-
*
|
|
27082
|
-
*
|
|
27269
|
+
* store is far more rows than any page. The rows come off ONE statement,
|
|
27270
|
+
* iterated rather than materialized, in the index order `findingScanSql`
|
|
27271
|
+
* arranges — so the scan is a single pass with a block sort of the id
|
|
27272
|
+
* tie-break only, never a sort of the scope, where a sequence of
|
|
27273
|
+
* keyset-bounded batches re-sorted everything below the cursor on every
|
|
27274
|
+
* batch and cost the square of the scope.
|
|
27083
27275
|
*
|
|
27084
|
-
*
|
|
27085
|
-
*
|
|
27086
|
-
*
|
|
27087
|
-
*
|
|
27088
|
-
*
|
|
27089
|
-
*
|
|
27090
|
-
*
|
|
27091
|
-
* dimension — see listFindingInstances.
|
|
27276
|
+
* `sessionId` and `from` carry ONLY what no facet counts — a filter
|
|
27277
|
+
* dimension narrowed here would be missing from its own facet, which is
|
|
27278
|
+
* computed by excluding that dimension (see listFindingInstances). There is
|
|
27279
|
+
* no `after`/cursor parameter: a keyset page is collected inline from this
|
|
27280
|
+
* same pass (`listFindingInstances`' `isPastCursor`) rather than by a second,
|
|
27281
|
+
* narrower statement, since the counting pass already visits every row a
|
|
27282
|
+
* page-2+ request would otherwise re-seek for.
|
|
27092
27283
|
*/
|
|
27093
27284
|
*scanFindingRows(scope) {
|
|
27094
|
-
const
|
|
27285
|
+
const { sql, params } = this.findingScanSql(scope);
|
|
27286
|
+
for (const r of iterateRows(this.db.prepare(sql), params)) {
|
|
27287
|
+
yield toFlatFindingRow(r);
|
|
27288
|
+
}
|
|
27289
|
+
}
|
|
27290
|
+
/**
|
|
27291
|
+
* The one statement both instance-level scans run: every finding in scope,
|
|
27292
|
+
* joined to its event and definition, newest first.
|
|
27293
|
+
*
|
|
27294
|
+
* THE PLAN IS THE POINT, and two things in the SQL exist only to pin it —
|
|
27295
|
+
* the same two `recentFindings` documents at length, for the same reason:
|
|
27296
|
+
*
|
|
27297
|
+
* - **`+e.event_type`** makes the capture-kind predicate non-indexable, so
|
|
27298
|
+
* the planner cannot pick `idx_audit_type_t` and then sort. That index
|
|
27299
|
+
* yields `started_at` order per event type, not across the four, so
|
|
27300
|
+
* satisfying the ORDER BY from it would need a merge SQLite does not do.
|
|
27301
|
+
* Freed of it, the planner walks `idx_audit_started_at` backwards — or
|
|
27302
|
+
* `idx_audit_session` for a session scope, which is also `started_at`
|
|
27303
|
+
* ordered within the session — and the order falls out of the index.
|
|
27304
|
+
* - **`CROSS JOIN`** pins `audit_events` as the driving table. With plain
|
|
27305
|
+
* JOINs the planner drives from the findings and sorts everything.
|
|
27306
|
+
*
|
|
27307
|
+
* The latest-resolution lookup is the CORRELATED form: only `status` is
|
|
27308
|
+
* needed, `idx_finding_resolution_key_created` answers it with one backward
|
|
27309
|
+
* index probe per keyed row, and a derived table over the whole resolution
|
|
27310
|
+
* table would be materialized before the first row streamed.
|
|
27311
|
+
*/
|
|
27312
|
+
findingScanSql(scope) {
|
|
27313
|
+
const conditions = [`+e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`];
|
|
27095
27314
|
const params = [];
|
|
27096
27315
|
if (scope.sessionId !== void 0 && scope.sessionId !== "") {
|
|
27097
27316
|
conditions.push("e.root_session_id = ?");
|
|
@@ -27105,58 +27324,24 @@ var SqliteFindingsRepository = class {
|
|
|
27105
27324
|
d.severity AS severity, f.masked_match AS masked_match,
|
|
27106
27325
|
f.action_taken AS action_taken, f.confidence AS confidence,
|
|
27107
27326
|
e.started_at AS occurred_at,
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
|
|
27327
|
+
e.source_tool AS source_tool,
|
|
27328
|
+
e.repo AS repo,
|
|
27329
|
+
e.file_path AS file,
|
|
27330
|
+
e.tool_name AS tool_name,
|
|
27112
27331
|
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
27113
27332
|
e.event_type AS kind, f.finding_key AS finding_key,
|
|
27114
27333
|
${latestResolutionStatusSql("f")} AS latest_status
|
|
27115
|
-
FROM
|
|
27116
|
-
JOIN
|
|
27117
|
-
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
27334
|
+
FROM audit_events e
|
|
27335
|
+
CROSS JOIN inspection_findings f ON f.audit_event_id = e.id
|
|
27336
|
+
CROSS JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
27118
27337
|
WHERE ${conditions.join(" AND ")}
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
LIMIT ?`;
|
|
27122
|
-
let after = scope.after ?? { startedAtMs: Number.MAX_SAFE_INTEGER, id: "\uFFFF" };
|
|
27123
|
-
for (; ; ) {
|
|
27124
|
-
const rows = allRows(this.db.prepare(sql), [
|
|
27125
|
-
...params,
|
|
27126
|
-
after.startedAtMs,
|
|
27127
|
-
after.startedAtMs,
|
|
27128
|
-
after.id,
|
|
27129
|
-
SCAN_BATCH_ROWS
|
|
27130
|
-
]);
|
|
27131
|
-
for (const r of rows) {
|
|
27132
|
-
yield {
|
|
27133
|
-
id: r.id,
|
|
27134
|
-
ruleId: r.rule_id,
|
|
27135
|
-
category: r.category,
|
|
27136
|
-
severity: r.severity,
|
|
27137
|
-
maskedMatch: r.masked_match,
|
|
27138
|
-
actionTaken: r.action_taken,
|
|
27139
|
-
confidence: r.confidence,
|
|
27140
|
-
occurredAt: epochMillisToIso(r.occurred_at),
|
|
27141
|
-
sourceTool: r.source_tool,
|
|
27142
|
-
repo: r.repo ?? "",
|
|
27143
|
-
file: r.file ?? "",
|
|
27144
|
-
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
27145
|
-
eventId: r.event_id,
|
|
27146
|
-
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
27147
|
-
status: deriveInstanceStatus(r)
|
|
27148
|
-
};
|
|
27149
|
-
}
|
|
27150
|
-
if (rows.length < SCAN_BATCH_ROWS) return;
|
|
27151
|
-
const lastRow = rows[rows.length - 1];
|
|
27152
|
-
if (lastRow === void 0) return;
|
|
27153
|
-
after = { startedAtMs: lastRow.occurred_at, id: lastRow.id };
|
|
27154
|
-
}
|
|
27338
|
+
ORDER BY e.started_at DESC, f.id DESC`;
|
|
27339
|
+
return { sql, params };
|
|
27155
27340
|
}
|
|
27156
27341
|
groupAggregates(withSearchText, scope) {
|
|
27157
|
-
const innerSearchColumns = withSearchText ? `, group_concat(DISTINCT
|
|
27158
|
-
group_concat(DISTINCT
|
|
27159
|
-
group_concat(DISTINCT 'via ' ||
|
|
27342
|
+
const innerSearchColumns = withSearchText ? `, group_concat(DISTINCT e.repo) AS repos,
|
|
27343
|
+
group_concat(DISTINCT e.file_path) AS files,
|
|
27344
|
+
group_concat(DISTINCT 'via ' || e.tool_name) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
|
|
27160
27345
|
const rows = this.db.prepare(
|
|
27161
27346
|
`SELECT rule_id,
|
|
27162
27347
|
sum(tuple_count) AS instance_count,
|
|
@@ -27174,7 +27359,7 @@ var SqliteFindingsRepository = class {
|
|
|
27174
27359
|
coalesce(latest.status, '') AS status_tuple,
|
|
27175
27360
|
count(*) AS tuple_count,
|
|
27176
27361
|
max(e.started_at) AS latest_at,
|
|
27177
|
-
group_concat(DISTINCT
|
|
27362
|
+
group_concat(DISTINCT e.source_tool) AS source_tools,
|
|
27178
27363
|
group_concat(DISTINCT f.action_taken) AS actions_taken
|
|
27179
27364
|
${innerSearchColumns}
|
|
27180
27365
|
FROM inspection_findings f
|
|
@@ -27305,6 +27490,8 @@ function isoDay(ms) {
|
|
|
27305
27490
|
// ../../packages/persistence/src/repositories/history-sync.ts
|
|
27306
27491
|
var STRUCTURAL_EVENT_TYPES = ["session", "llm_call", "tool_call"];
|
|
27307
27492
|
var TYPE_LIST = STRUCTURAL_EVENT_TYPES.map((t) => `'${t}'`).join(", ");
|
|
27493
|
+
var OUTBOX_CAPTURE_EVENT_TYPES = ["prompt", "response", "tool_use"];
|
|
27494
|
+
var CAPTURE_TYPE_LIST = OUTBOX_CAPTURE_EVENT_TYPES.map((t) => `'${t}'`).join(", ");
|
|
27308
27495
|
var SKIPPED = -1;
|
|
27309
27496
|
var ROW_COLUMNS = `id,
|
|
27310
27497
|
parent_id AS parentId,
|
|
@@ -27344,6 +27531,20 @@ var SqliteHistorySyncRepository = class {
|
|
|
27344
27531
|
ORDER BY (event_type = 'session') DESC, started_at
|
|
27345
27532
|
LIMIT :limit`
|
|
27346
27533
|
);
|
|
27534
|
+
this.captureRowsStmt = db.prepare(
|
|
27535
|
+
`SELECT ${ROW_COLUMNS}
|
|
27536
|
+
FROM audit_events
|
|
27537
|
+
WHERE synced_at IS NULL
|
|
27538
|
+
AND sync_claimed_at IS NULL
|
|
27539
|
+
AND outbox_owed = 1
|
|
27540
|
+
AND event_type IN (${CAPTURE_TYPE_LIST})
|
|
27541
|
+
AND started_at < :before
|
|
27542
|
+
ORDER BY started_at
|
|
27543
|
+
LIMIT :limit`
|
|
27544
|
+
);
|
|
27545
|
+
this.markOwedStmt = db.prepare(
|
|
27546
|
+
`UPDATE audit_events SET outbox_owed = 1 WHERE id = :id AND synced_at IS NULL`
|
|
27547
|
+
);
|
|
27347
27548
|
this.stampStmt = db.prepare(
|
|
27348
27549
|
`UPDATE audit_events SET synced_at = :at, sync_claimed_at = NULL WHERE id = :id`
|
|
27349
27550
|
);
|
|
@@ -27375,6 +27576,12 @@ var SqliteHistorySyncRepository = class {
|
|
|
27375
27576
|
FROM audit_events
|
|
27376
27577
|
WHERE event_type IN (${TYPE_LIST})`
|
|
27377
27578
|
);
|
|
27579
|
+
this.captureSkipCountStmt = db.prepare(
|
|
27580
|
+
`SELECT COUNT(*) AS skipped
|
|
27581
|
+
FROM audit_events
|
|
27582
|
+
WHERE synced_at = ${String(SKIPPED)}
|
|
27583
|
+
AND event_type IN (${CAPTURE_TYPE_LIST})`
|
|
27584
|
+
);
|
|
27378
27585
|
this.fingerprintStmt = db.prepare(
|
|
27379
27586
|
`SELECT endpoint_fingerprint AS fingerprint, backlog_before AS backlogBefore
|
|
27380
27587
|
FROM history_sync WHERE id = 1`
|
|
@@ -27384,6 +27591,10 @@ var SqliteHistorySyncRepository = class {
|
|
|
27384
27591
|
SET endpoint_fingerprint = :fingerprint, backlog_before = :backlogBefore
|
|
27385
27592
|
WHERE id = 1`
|
|
27386
27593
|
);
|
|
27594
|
+
this.disownCapturesStmt = db.prepare(
|
|
27595
|
+
`UPDATE audit_events SET outbox_owed = NULL
|
|
27596
|
+
WHERE outbox_owed IS NOT NULL AND event_type IN (${CAPTURE_TYPE_LIST})`
|
|
27597
|
+
);
|
|
27387
27598
|
this.rearmStmt = db.prepare(
|
|
27388
27599
|
`UPDATE audit_events SET synced_at = NULL
|
|
27389
27600
|
WHERE synced_at > 0 AND event_type IN (${TYPE_LIST})`
|
|
@@ -27456,6 +27667,10 @@ var SqliteHistorySyncRepository = class {
|
|
|
27456
27667
|
closeWindowStmt;
|
|
27457
27668
|
releaseBoundaryStmt;
|
|
27458
27669
|
freezeBoundaryStmt;
|
|
27670
|
+
captureRowsStmt;
|
|
27671
|
+
markOwedStmt;
|
|
27672
|
+
captureSkipCountStmt;
|
|
27673
|
+
disownCapturesStmt;
|
|
27459
27674
|
partitionStmt;
|
|
27460
27675
|
claimRowStmt;
|
|
27461
27676
|
releaseRowStmt;
|
|
@@ -27489,6 +27704,34 @@ var SqliteHistorySyncRepository = class {
|
|
|
27489
27704
|
pendingRows(sessionId, limit, before) {
|
|
27490
27705
|
return allRows(this.rowsStmt, { sessionId, limit, before });
|
|
27491
27706
|
}
|
|
27707
|
+
/**
|
|
27708
|
+
* Captures this machine still owes the deployment, oldest first.
|
|
27709
|
+
*
|
|
27710
|
+
* Selected by the `outbox_owed` marker the attached forward path writes, not
|
|
27711
|
+
* by a time window — see captureRowsStmt for why a window could not express
|
|
27712
|
+
* this. `before` is the grace window that leaves a just-recorded capture to
|
|
27713
|
+
* the live path.
|
|
27714
|
+
*/
|
|
27715
|
+
pendingCaptureRows(limit, before) {
|
|
27716
|
+
return allRows(this.captureRowsStmt, { limit, before });
|
|
27717
|
+
}
|
|
27718
|
+
/**
|
|
27719
|
+
* Record that a capture is OWED to the deployment.
|
|
27720
|
+
*
|
|
27721
|
+
* Written by the attached forward path when a live send did not confirm
|
|
27722
|
+
* delivery, and read by the drain as the whole of its eligibility test. It is
|
|
27723
|
+
* a fact rather than an inference: the machine was attached, the send did not
|
|
27724
|
+
* land, so the row is owed — which no time window can state, because the same
|
|
27725
|
+
* window that holds the rows a past attachment left owed also holds every
|
|
27726
|
+
* capture recorded while the machine was DETACHED, and those were never
|
|
27727
|
+
* offered to anyone.
|
|
27728
|
+
*
|
|
27729
|
+
* Idempotent, and never un-set: `markSynced` settling the row is what takes it
|
|
27730
|
+
* out of the drain's read.
|
|
27731
|
+
*/
|
|
27732
|
+
markCaptureOwed(id) {
|
|
27733
|
+
this.markOwedStmt.run({ id });
|
|
27734
|
+
}
|
|
27492
27735
|
/** Record delivery. Called only AFTER the far side has accepted the rows. */
|
|
27493
27736
|
markSynced(ids, atMs) {
|
|
27494
27737
|
this.stampAll(ids, atMs);
|
|
@@ -27572,10 +27815,12 @@ var SqliteHistorySyncRepository = class {
|
|
|
27572
27815
|
this.countsStmt,
|
|
27573
27816
|
{ before }
|
|
27574
27817
|
);
|
|
27818
|
+
const captures = getRow(this.captureSkipCountStmt);
|
|
27575
27819
|
return {
|
|
27576
27820
|
pending: row?.pending ?? 0,
|
|
27577
27821
|
sent: row?.sent ?? 0,
|
|
27578
|
-
skipped: row?.skipped ?? 0
|
|
27822
|
+
skipped: row?.skipped ?? 0,
|
|
27823
|
+
capturesSkipped: captures?.skipped ?? 0
|
|
27579
27824
|
};
|
|
27580
27825
|
}
|
|
27581
27826
|
/**
|
|
@@ -27616,7 +27861,11 @@ var SqliteHistorySyncRepository = class {
|
|
|
27616
27861
|
withTransaction(
|
|
27617
27862
|
this.db,
|
|
27618
27863
|
() => {
|
|
27864
|
+
const previous = getRow(this.fingerprintStmt)?.fingerprint;
|
|
27619
27865
|
this.rearmStmt.run();
|
|
27866
|
+
if (previous !== null && previous !== void 0 && previous !== fingerprint) {
|
|
27867
|
+
this.disownCapturesStmt.run();
|
|
27868
|
+
}
|
|
27620
27869
|
this.setFingerprintStmt.run({ fingerprint, backlogBefore });
|
|
27621
27870
|
},
|
|
27622
27871
|
"IMMEDIATE"
|
|
@@ -27813,7 +28062,252 @@ var SqliteInspectionFindingsRepository = class {
|
|
|
27813
28062
|
};
|
|
27814
28063
|
|
|
27815
28064
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
27816
|
-
import { createHash as createHash2, randomUUID as
|
|
28065
|
+
import { createHash as createHash2, randomUUID as randomUUID4 } from "crypto";
|
|
28066
|
+
|
|
28067
|
+
// ../../packages/persistence/src/policy-floor.ts
|
|
28068
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
28069
|
+
import { join as join6 } from "path";
|
|
28070
|
+
|
|
28071
|
+
// ../../packages/persistence/src/local-layout.ts
|
|
28072
|
+
import { renameSync as renameSync3 } from "fs";
|
|
28073
|
+
import { mkdir } from "fs/promises";
|
|
28074
|
+
import { homedir } from "os";
|
|
28075
|
+
import { join as join4 } from "path";
|
|
28076
|
+
function defaultDataDir() {
|
|
28077
|
+
return join4(homedir(), ".aka");
|
|
28078
|
+
}
|
|
28079
|
+
function settingsDir(base = defaultDataDir()) {
|
|
28080
|
+
return join4(base, "settings");
|
|
28081
|
+
}
|
|
28082
|
+
function dataDir(base = defaultDataDir()) {
|
|
28083
|
+
return join4(base, "data");
|
|
28084
|
+
}
|
|
28085
|
+
function dbPath(base = defaultDataDir()) {
|
|
28086
|
+
return join4(dataDir(base), "aka.db");
|
|
28087
|
+
}
|
|
28088
|
+
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
28089
|
+
ensureDataDirSync(dir);
|
|
28090
|
+
}
|
|
28091
|
+
function migrateLegacyLayout(base = defaultDataDir()) {
|
|
28092
|
+
const moves = [
|
|
28093
|
+
{ name: "config.json", dest: settingsDir(base) },
|
|
28094
|
+
{ name: "policy-cache.json", dest: dataDir(base) }
|
|
28095
|
+
];
|
|
28096
|
+
for (const { name, dest } of moves) {
|
|
28097
|
+
try {
|
|
28098
|
+
ensureDataDirSync(dest);
|
|
28099
|
+
const moved = join4(dest, name);
|
|
28100
|
+
renameSync3(join4(base, name), moved);
|
|
28101
|
+
tightenFile(moved);
|
|
28102
|
+
} catch {
|
|
28103
|
+
}
|
|
28104
|
+
}
|
|
28105
|
+
}
|
|
28106
|
+
|
|
28107
|
+
// ../../packages/persistence/src/settings.ts
|
|
28108
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
28109
|
+
import { join as join5 } from "path";
|
|
28110
|
+
|
|
28111
|
+
// ../../packages/persistence/src/file-lock.ts
|
|
28112
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
28113
|
+
import {
|
|
28114
|
+
closeSync,
|
|
28115
|
+
existsSync as existsSync2,
|
|
28116
|
+
openSync,
|
|
28117
|
+
readFileSync as readFileSync2,
|
|
28118
|
+
rmSync as rmSync5,
|
|
28119
|
+
statSync as statSync3,
|
|
28120
|
+
writeFileSync as writeFileSync2
|
|
28121
|
+
} from "fs";
|
|
28122
|
+
import { hostname as hostname3 } from "os";
|
|
28123
|
+
var PARK = new Int32Array(new SharedArrayBuffer(4));
|
|
28124
|
+
|
|
28125
|
+
// ../../packages/persistence/src/managed-settings.ts
|
|
28126
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
28127
|
+
import { posix, win32 } from "path";
|
|
28128
|
+
function managedSettingsPaths(platform2 = process.platform) {
|
|
28129
|
+
if (platform2 === "darwin") {
|
|
28130
|
+
return [
|
|
28131
|
+
posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
|
|
28132
|
+
posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
|
|
28133
|
+
];
|
|
28134
|
+
}
|
|
28135
|
+
if (platform2 === "win32") {
|
|
28136
|
+
return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
|
|
28137
|
+
}
|
|
28138
|
+
return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
|
|
28139
|
+
}
|
|
28140
|
+
function readManagedSettings(paths = managedSettingsPaths()) {
|
|
28141
|
+
for (const path of paths) {
|
|
28142
|
+
let text;
|
|
28143
|
+
try {
|
|
28144
|
+
text = readFileSync3(path, "utf8");
|
|
28145
|
+
} catch {
|
|
28146
|
+
continue;
|
|
28147
|
+
}
|
|
28148
|
+
const record2 = parseJsonObject(text);
|
|
28149
|
+
if (!record2) continue;
|
|
28150
|
+
const parsed = ManagedSettings.safeParse(record2);
|
|
28151
|
+
if (parsed.success) return parsed.data;
|
|
28152
|
+
}
|
|
28153
|
+
return null;
|
|
28154
|
+
}
|
|
28155
|
+
function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
|
|
28156
|
+
if (!managed) return settings;
|
|
28157
|
+
const { values } = managed;
|
|
28158
|
+
const merged = { ...settings };
|
|
28159
|
+
if (values.runMode !== void 0) merged.runMode = values.runMode;
|
|
28160
|
+
if (values.controlPlane !== void 0) {
|
|
28161
|
+
merged.controlPlane = {
|
|
28162
|
+
...values.controlPlane,
|
|
28163
|
+
// The administrator pinned WHICH deployment, not WHEN this machine
|
|
28164
|
+
// joined it. Keep the user's own attach time when the endpoint is
|
|
28165
|
+
// unchanged, so a managed machine does not appear to re-attach on every
|
|
28166
|
+
// read; stamp a fresh one when the administrator moved it.
|
|
28167
|
+
attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
|
|
28168
|
+
};
|
|
28169
|
+
}
|
|
28170
|
+
if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
|
|
28171
|
+
if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
|
|
28172
|
+
if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
|
|
28173
|
+
if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
|
|
28174
|
+
if (values.redactFallback !== void 0) merged.redactFallback = values.redactFallback;
|
|
28175
|
+
if (values.vaultConsent !== void 0) {
|
|
28176
|
+
merged.vaultConsent = values.vaultConsent ? (
|
|
28177
|
+
// Keep an existing valid grant so its acknowledgedAt survives; mint one
|
|
28178
|
+
// at the current version otherwise.
|
|
28179
|
+
settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
|
|
28180
|
+
) : void 0;
|
|
28181
|
+
}
|
|
28182
|
+
if (values.modelJudgeConsent !== void 0) {
|
|
28183
|
+
merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
|
|
28184
|
+
acknowledgedAt: now().toISOString(),
|
|
28185
|
+
payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
|
|
28186
|
+
} : void 0;
|
|
28187
|
+
}
|
|
28188
|
+
return merged;
|
|
28189
|
+
}
|
|
28190
|
+
|
|
28191
|
+
// ../../packages/persistence/src/settings.ts
|
|
28192
|
+
var SETTINGS_FILENAME = "settings.json";
|
|
28193
|
+
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
28194
|
+
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
28195
|
+
}
|
|
28196
|
+
function readUserSettings(base) {
|
|
28197
|
+
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
28198
|
+
if (!record2) return defaultWorkspaceSettings();
|
|
28199
|
+
try {
|
|
28200
|
+
return WorkspaceSettings.parse(record2);
|
|
28201
|
+
} catch {
|
|
28202
|
+
return defaultWorkspaceSettings();
|
|
28203
|
+
}
|
|
28204
|
+
}
|
|
28205
|
+
function readJson(file2) {
|
|
28206
|
+
let text;
|
|
28207
|
+
try {
|
|
28208
|
+
text = readFileSync4(file2, "utf8");
|
|
28209
|
+
} catch {
|
|
28210
|
+
return null;
|
|
28211
|
+
}
|
|
28212
|
+
return parseJsonObject(text) ?? null;
|
|
28213
|
+
}
|
|
28214
|
+
|
|
28215
|
+
// ../../packages/persistence/src/policy-floor.ts
|
|
28216
|
+
function refusalMessage(pack, attempted, floor, refusal) {
|
|
28217
|
+
switch (refusal) {
|
|
28218
|
+
case "lock":
|
|
28219
|
+
return `refusing to re-assign '${pack}': its policy is set by the connected control plane`;
|
|
28220
|
+
case "disable":
|
|
28221
|
+
return `refusing to disable '${pack}': it is governed by the connected control plane`;
|
|
28222
|
+
case "floor":
|
|
28223
|
+
return `refusing to set '${pack}' to '${attempted ?? "unassigned"}': the connected control plane requires at least '${floor}'`;
|
|
28224
|
+
}
|
|
28225
|
+
}
|
|
28226
|
+
var PolicyFloorError = class extends Error {
|
|
28227
|
+
/** `namespace/packId` of the detection whose write was refused. */
|
|
28228
|
+
pack;
|
|
28229
|
+
/**
|
|
28230
|
+
* The archetype the caller asked for, or null when the write named none —
|
|
28231
|
+
* clearing the assignment, or switching the detection off.
|
|
28232
|
+
*/
|
|
28233
|
+
attempted;
|
|
28234
|
+
/** The weakest archetype the control plane permits for this pack. */
|
|
28235
|
+
floor;
|
|
28236
|
+
refusal;
|
|
28237
|
+
constructor(pack, attempted, floor, refusal) {
|
|
28238
|
+
super(refusalMessage(pack, attempted, floor, refusal));
|
|
28239
|
+
this.name = "PolicyFloorError";
|
|
28240
|
+
this.pack = pack;
|
|
28241
|
+
this.attempted = attempted;
|
|
28242
|
+
this.floor = floor;
|
|
28243
|
+
this.refusal = refusal;
|
|
28244
|
+
}
|
|
28245
|
+
};
|
|
28246
|
+
function readCachedPolicyBundle(base = defaultDataDir()) {
|
|
28247
|
+
try {
|
|
28248
|
+
const raw = readFileSync5(join6(dataDir(base), POLICY_CACHE_FILENAME), "utf8");
|
|
28249
|
+
const parsed = JSON.parse(raw);
|
|
28250
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
28251
|
+
return PolicyBundle.parse(parsed.bundle);
|
|
28252
|
+
} catch {
|
|
28253
|
+
return null;
|
|
28254
|
+
}
|
|
28255
|
+
}
|
|
28256
|
+
function indexEnabled(policies) {
|
|
28257
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
28258
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
28259
|
+
for (const policy of policies) {
|
|
28260
|
+
if (!policy.enabled) continue;
|
|
28261
|
+
if ("ruleId" in policy.target) {
|
|
28262
|
+
if (!byRuleId.has(policy.target.ruleId)) byRuleId.set(policy.target.ruleId, policy.action);
|
|
28263
|
+
} else if (!byCategory.has(policy.target.category)) {
|
|
28264
|
+
byCategory.set(policy.target.category, policy.action);
|
|
28265
|
+
}
|
|
28266
|
+
}
|
|
28267
|
+
return { byRuleId, byCategory };
|
|
28268
|
+
}
|
|
28269
|
+
function hasAuthoredPolicy(policies, rules, byRuleId) {
|
|
28270
|
+
const ruleIds = new Set(rules.map((rule) => rule.id));
|
|
28271
|
+
const categories = new Set(rules.map((rule) => rule.category));
|
|
28272
|
+
const bundleNamesPack = rules.some((rule) => byRuleId.has(rule.id));
|
|
28273
|
+
return policies.some((policy) => {
|
|
28274
|
+
if (!policy.enabled || policy.provenance !== "authored") return false;
|
|
28275
|
+
return "ruleId" in policy.target ? ruleIds.has(policy.target.ruleId) : bundleNamesPack && categories.has(policy.target.category);
|
|
28276
|
+
});
|
|
28277
|
+
}
|
|
28278
|
+
function controlPlanePolicyFloor(rules, base = defaultDataDir()) {
|
|
28279
|
+
const floors = openControlPlaneFloors(base);
|
|
28280
|
+
return floors === null ? null : floors.floorFor(rules);
|
|
28281
|
+
}
|
|
28282
|
+
function openControlPlaneFloors(base = defaultDataDir()) {
|
|
28283
|
+
if (!isAttached(readWorkspaceSettings(base))) return null;
|
|
28284
|
+
const bundle = readCachedPolicyBundle(base);
|
|
28285
|
+
if (bundle === null) return null;
|
|
28286
|
+
const indexes = indexEnabled(bundle.policies);
|
|
28287
|
+
return { floorFor: (rules) => resolveFloor(rules, bundle.policies, indexes) };
|
|
28288
|
+
}
|
|
28289
|
+
function resolveFloor(rules, policies, { byRuleId, byCategory }) {
|
|
28290
|
+
let action = null;
|
|
28291
|
+
for (const rule of rules) {
|
|
28292
|
+
const resolved = byRuleId.get(rule.id) ?? byCategory.get(rule.category);
|
|
28293
|
+
if (resolved === void 0) continue;
|
|
28294
|
+
action = action === null ? resolved : strongerAction(action, resolved);
|
|
28295
|
+
}
|
|
28296
|
+
if (action === null) return null;
|
|
28297
|
+
return {
|
|
28298
|
+
floor: weakestBuiltinAtLeast(action),
|
|
28299
|
+
locked: hasAuthoredPolicy(policies, rules, byRuleId)
|
|
28300
|
+
};
|
|
28301
|
+
}
|
|
28302
|
+
function policyAssignmentRefusal(policyId, floor) {
|
|
28303
|
+
if (floor.locked) return "lock";
|
|
28304
|
+
const effective = policyId ?? DEFAULT_PACK_POLICY_ID;
|
|
28305
|
+
return isActionAtLeast(builtinPolicyToAction(effective), builtinPolicyToAction(floor.floor)) ? null : "floor";
|
|
28306
|
+
}
|
|
28307
|
+
function packEnablementRefusal(enabled, floor) {
|
|
28308
|
+
if (floor === null || enabled) return null;
|
|
28309
|
+
return "disable";
|
|
28310
|
+
}
|
|
27817
28311
|
|
|
27818
28312
|
// ../../packages/persistence/src/semver.ts
|
|
27819
28313
|
function parse3(version2) {
|
|
@@ -27907,8 +28401,19 @@ function ruleIdsOf(rulesJson) {
|
|
|
27907
28401
|
return ids;
|
|
27908
28402
|
}
|
|
27909
28403
|
var SqliteInstalledPacksRepository = class {
|
|
27910
|
-
|
|
28404
|
+
/**
|
|
28405
|
+
* `baseDir` is the `~/.aka` LAYOUT BASE, not the data dir — the control-plane
|
|
28406
|
+
* floor needs both halves of it (settings/ says whether this machine is
|
|
28407
|
+
* attached, data/ holds the cached bundle). It is optional because a caller
|
|
28408
|
+
* holding only a DatabaseSync — every test construction site, and any embedder
|
|
28409
|
+
* that opens the store itself — has no layout to point at, and such a caller
|
|
28410
|
+
* gets the pre-existing behaviour: no floor, no lock. Production threads it in
|
|
28411
|
+
* from `openLocalDatabase`, which is the single construction site that owns a
|
|
28412
|
+
* real `~/.aka`.
|
|
28413
|
+
*/
|
|
28414
|
+
constructor(db, baseDir) {
|
|
27911
28415
|
this.db = db;
|
|
28416
|
+
this.baseDir = baseDir;
|
|
27912
28417
|
this.insertMissingStmt = db.prepare(
|
|
27913
28418
|
`INSERT INTO installed_packs (id, namespace, pack_id, version, name, rules_json, enabled, created_at, updated_at)
|
|
27914
28419
|
VALUES (:id, :namespace, :packId, :version, :name, :rulesJson, 1, :now, :now)
|
|
@@ -27930,11 +28435,17 @@ var SqliteInstalledPacksRepository = class {
|
|
|
27930
28435
|
this.signatureStmt = db.prepare(
|
|
27931
28436
|
`SELECT namespace, pack_id AS packId, version, rules_json AS rulesJson FROM available_packs`
|
|
27932
28437
|
);
|
|
28438
|
+
this.packRulesStmt = db.prepare(
|
|
28439
|
+
`SELECT rules_json AS rulesJson FROM installed_packs
|
|
28440
|
+
WHERE namespace = ? AND pack_id = ?`
|
|
28441
|
+
);
|
|
27933
28442
|
}
|
|
27934
28443
|
db;
|
|
28444
|
+
baseDir;
|
|
27935
28445
|
insertMissingStmt;
|
|
27936
28446
|
upsertAvailableStmt;
|
|
27937
28447
|
signatureStmt;
|
|
28448
|
+
packRulesStmt;
|
|
27938
28449
|
/**
|
|
27939
28450
|
* Record the running binary's detection inventory. Refreshes the
|
|
27940
28451
|
* available_packs mirror (pruning packs the binary no longer ships) and
|
|
@@ -27976,7 +28487,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
27976
28487
|
let behind = false;
|
|
27977
28488
|
for (const row of rows) {
|
|
27978
28489
|
const params = {
|
|
27979
|
-
id:
|
|
28490
|
+
id: randomUUID4(),
|
|
27980
28491
|
namespace: row.namespace,
|
|
27981
28492
|
packId: row.packId,
|
|
27982
28493
|
version: row.version,
|
|
@@ -27988,7 +28499,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
27988
28499
|
if (stored === void 0 || !isMirrorDowngrade(row, stored)) {
|
|
27989
28500
|
this.upsertAvailableStmt.run({
|
|
27990
28501
|
...params,
|
|
27991
|
-
id:
|
|
28502
|
+
id: randomUUID4(),
|
|
27992
28503
|
recordedBy: meta4?.recordedBy ?? null
|
|
27993
28504
|
});
|
|
27994
28505
|
} else {
|
|
@@ -28234,9 +28745,65 @@ var SqliteInstalledPacksRepository = class {
|
|
|
28234
28745
|
// NOT on the hook path — so, unlike recordInventory, these surface errors to the
|
|
28235
28746
|
// caller rather than swallowing them. Each returns whether a row matched, so the
|
|
28236
28747
|
// caller can tell an edit from a no-such-detection.
|
|
28748
|
+
/**
|
|
28749
|
+
* The rules one installed pack owns, reduced to what a floor computation
|
|
28750
|
+
* reads. Display-tolerant parsing on purpose: a pack whose snapshot is
|
|
28751
|
+
* unreadable contributes no rules to a scan either, so it is not a detection
|
|
28752
|
+
* the control plane can be governing, and an empty list correctly imposes no
|
|
28753
|
+
* floor. Enabled state is deliberately not filtered — a disabled pack is one
|
|
28754
|
+
* the user can re-enable, and its assignment stays governed meanwhile.
|
|
28755
|
+
*/
|
|
28756
|
+
packFloorRules(namespace, packId) {
|
|
28757
|
+
const row = getRow(this.packRulesStmt, [namespace, packId]);
|
|
28758
|
+
if (!row) return [];
|
|
28759
|
+
return parseRules(row.rulesJson).map((rule) => ({ id: rule.id, category: rule.category }));
|
|
28760
|
+
}
|
|
28761
|
+
/**
|
|
28762
|
+
* What the connected control plane imposes on one installed pack, or null on a
|
|
28763
|
+
* machine that is its own authority (standalone, no cached bundle, or a
|
|
28764
|
+
* repository constructed without a layout base).
|
|
28765
|
+
*
|
|
28766
|
+
* Exposed as a READ so a surface can render the constraint — grey out the
|
|
28767
|
+
* choices below the floor, mark a locked detection as locked — rather than
|
|
28768
|
+
* offer the user a picker whose selections it will then be told it may not
|
|
28769
|
+
* make. The refusal in `setPolicy` does not depend on any surface calling this.
|
|
28770
|
+
*/
|
|
28771
|
+
policyFloor(namespace, packId) {
|
|
28772
|
+
if (this.baseDir === void 0) return null;
|
|
28773
|
+
return controlPlanePolicyFloor(this.packFloorRules(namespace, packId), this.baseDir);
|
|
28774
|
+
}
|
|
28775
|
+
/**
|
|
28776
|
+
* The same answer for several packs, keyed `namespace/packId` and carrying an
|
|
28777
|
+
* entry only for a pack the control plane actually governs.
|
|
28778
|
+
*
|
|
28779
|
+
* A surface listing every detection asks per pack, and asking through
|
|
28780
|
+
* `policyFloor` re-reads the settings, re-reads and re-parses the whole cached
|
|
28781
|
+
* bundle and rebuilds its indexes once per pack — the entire cost of one
|
|
28782
|
+
* answer, repeated for each row, on every render. This reads all of that once.
|
|
28783
|
+
* Packs whose rules the snapshot cannot produce simply contribute no entry,
|
|
28784
|
+
* exactly as the single-pack read returns null for them.
|
|
28785
|
+
*/
|
|
28786
|
+
policyFloors(packs2) {
|
|
28787
|
+
const floors = /* @__PURE__ */ new Map();
|
|
28788
|
+
if (this.baseDir === void 0) return floors;
|
|
28789
|
+
const source = openControlPlaneFloors(this.baseDir);
|
|
28790
|
+
if (source === null) return floors;
|
|
28791
|
+
for (const pack of packs2) {
|
|
28792
|
+
const floor = source.floorFor(this.packFloorRules(pack.namespace, pack.packId));
|
|
28793
|
+
if (floor !== null) floors.set(`${pack.namespace}/${pack.packId}`, floor);
|
|
28794
|
+
}
|
|
28795
|
+
return floors;
|
|
28796
|
+
}
|
|
28237
28797
|
/**
|
|
28238
28798
|
* Assign (or clear, with null) the enforcement policy for one installed pack.
|
|
28239
|
-
* `policyId` must be a known built-in id (monitor/warn/redact/block).
|
|
28799
|
+
* `policyId` must be a known built-in id (monitor/warn/redact/block/vault).
|
|
28800
|
+
*
|
|
28801
|
+
* On an ATTACHED machine the organization's bundle is a floor this refuses to
|
|
28802
|
+
* write below, and a detection the organization has authored a policy for is
|
|
28803
|
+
* refused outright — see policy-floor.ts for both, and for why the refusal is
|
|
28804
|
+
* a throw rather than a silently substituted value. This is the one device-local
|
|
28805
|
+
* write path for the assignment, so the check belongs here rather than on any
|
|
28806
|
+
* surface that offers the choice.
|
|
28240
28807
|
*/
|
|
28241
28808
|
setPolicy(namespace, packId, policyId) {
|
|
28242
28809
|
if (policyId !== null && !BuiltinPolicyId.safeParse(policyId).success) {
|
|
@@ -28244,14 +28811,38 @@ var SqliteInstalledPacksRepository = class {
|
|
|
28244
28811
|
`Unknown policy '${policyId}'. Must be one of: ${KNOWN_BUILTIN_IDS.join(", ")}.`
|
|
28245
28812
|
);
|
|
28246
28813
|
}
|
|
28814
|
+
const requested = policyId;
|
|
28815
|
+
const floor = this.policyFloor(namespace, packId);
|
|
28816
|
+
if (floor !== null) {
|
|
28817
|
+
const refusal = policyAssignmentRefusal(requested, floor);
|
|
28818
|
+
if (refusal !== null) {
|
|
28819
|
+
throw new PolicyFloorError(`${namespace}/${packId}`, requested, floor.floor, refusal);
|
|
28820
|
+
}
|
|
28821
|
+
}
|
|
28247
28822
|
const res = this.db.prepare(
|
|
28248
28823
|
`UPDATE installed_packs SET policy_id = :policyId, updated_at = :now
|
|
28249
28824
|
WHERE namespace = :namespace AND pack_id = :packId`
|
|
28250
28825
|
).run({ policyId, now: Date.now(), namespace, packId });
|
|
28251
28826
|
return Number(res.changes) > 0;
|
|
28252
28827
|
}
|
|
28253
|
-
/**
|
|
28828
|
+
/**
|
|
28829
|
+
* Enable or disable one installed pack.
|
|
28830
|
+
*
|
|
28831
|
+
* On an ATTACHED machine a detection the organization's bundle governs at all
|
|
28832
|
+
* may not be switched OFF here — see packEnablementRefusal for why that is not
|
|
28833
|
+
* merely another point below the floor, and why re-enabling stays open. Like
|
|
28834
|
+
* the assignment above, the check belongs at this write path rather than on a
|
|
28835
|
+
* surface: this is the one device-local writer of the column, and a refusal
|
|
28836
|
+
* that lived in a page would leave the CLI free.
|
|
28837
|
+
*/
|
|
28254
28838
|
setEnabled(namespace, packId, enabled) {
|
|
28839
|
+
const floor = this.policyFloor(namespace, packId);
|
|
28840
|
+
if (floor !== null) {
|
|
28841
|
+
const refusal = packEnablementRefusal(enabled, floor);
|
|
28842
|
+
if (refusal !== null) {
|
|
28843
|
+
throw new PolicyFloorError(`${namespace}/${packId}`, null, floor.floor, refusal);
|
|
28844
|
+
}
|
|
28845
|
+
}
|
|
28255
28846
|
const res = this.db.prepare(
|
|
28256
28847
|
`UPDATE installed_packs SET enabled = :enabled, updated_at = :now
|
|
28257
28848
|
WHERE namespace = :namespace AND pack_id = :packId`
|
|
@@ -28337,7 +28928,7 @@ var SqliteInventoryRepository = class {
|
|
|
28337
28928
|
};
|
|
28338
28929
|
|
|
28339
28930
|
// ../../packages/persistence/src/repositories/inventory-assets.ts
|
|
28340
|
-
import { randomUUID as
|
|
28931
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
28341
28932
|
var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
28342
28933
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
28343
28934
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
@@ -28826,7 +29417,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
28826
29417
|
`INSERT INTO file_access_override (id, project_id, path, access, created_at, updated_at)
|
|
28827
29418
|
VALUES (:id, :projectId, :path, :access, :now, :now)
|
|
28828
29419
|
ON CONFLICT (project_id, path) DO UPDATE SET access = excluded.access, updated_at = excluded.updated_at`
|
|
28829
|
-
).run({ id:
|
|
29420
|
+
).run({ id: randomUUID5(), projectId, path, access, now: Date.now() });
|
|
28830
29421
|
}
|
|
28831
29422
|
return true;
|
|
28832
29423
|
}
|
|
@@ -28847,7 +29438,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
28847
29438
|
`INSERT INTO mcp_trust_override (id, asset_id, trust, created_at, updated_at)
|
|
28848
29439
|
VALUES (:id, :assetId, :trust, :now, :now)
|
|
28849
29440
|
ON CONFLICT (asset_id) DO UPDATE SET trust = excluded.trust, updated_at = excluded.updated_at`
|
|
28850
|
-
).run({ id:
|
|
29441
|
+
).run({ id: randomUUID5(), assetId, trust, now: Date.now() });
|
|
28851
29442
|
}
|
|
28852
29443
|
this.configRowsCache = void 0;
|
|
28853
29444
|
return "ok";
|
|
@@ -29144,7 +29735,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
29144
29735
|
};
|
|
29145
29736
|
|
|
29146
29737
|
// ../../packages/persistence/src/repositories/policies.ts
|
|
29147
|
-
import { randomUUID as
|
|
29738
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
29148
29739
|
var SqlitePoliciesRepository = class {
|
|
29149
29740
|
constructor(db) {
|
|
29150
29741
|
this.db = db;
|
|
@@ -29179,7 +29770,7 @@ var SqlitePoliciesRepository = class {
|
|
|
29179
29770
|
failOpenTransaction(this.db, () => {
|
|
29180
29771
|
for (const [category, action] of Object.entries(DEFAULT_ACTIONS)) {
|
|
29181
29772
|
stmt.run({
|
|
29182
|
-
id:
|
|
29773
|
+
id: randomUUID6(),
|
|
29183
29774
|
target: JSON.stringify({ category }),
|
|
29184
29775
|
action,
|
|
29185
29776
|
now: Date.now()
|
|
@@ -29199,7 +29790,7 @@ var SqlitePoliciesRepository = class {
|
|
|
29199
29790
|
`INSERT INTO policies (id, scope, target, action, enabled, created_at, updated_at)
|
|
29200
29791
|
VALUES (:id, 'global', :target, :action, 1, :now, :now)
|
|
29201
29792
|
ON CONFLICT(scope, target) DO UPDATE SET action = excluded.action, enabled = 1, updated_at = excluded.updated_at`
|
|
29202
|
-
).run({ id:
|
|
29793
|
+
).run({ id: randomUUID6(), target: JSON.stringify({ category }), action, now });
|
|
29203
29794
|
}
|
|
29204
29795
|
// Caps every global per-category policy currently set to block/redact down
|
|
29205
29796
|
// to warn (see warn-era-cap.ts). Rule-targeted policies are untouched.
|
|
@@ -29267,7 +29858,7 @@ var SqlitePolicyCatalogRepository = class {
|
|
|
29267
29858
|
};
|
|
29268
29859
|
|
|
29269
29860
|
// ../../packages/persistence/src/repositories/project-files.ts
|
|
29270
|
-
import { randomUUID as
|
|
29861
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
29271
29862
|
var SqliteProjectFilesRepository = class {
|
|
29272
29863
|
constructor(db) {
|
|
29273
29864
|
this.db = db;
|
|
@@ -29299,7 +29890,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
29299
29890
|
const stamp = Math.max(now, maxStamp + 1);
|
|
29300
29891
|
for (const file2 of scan2.files) {
|
|
29301
29892
|
this.upsertStmt.run({
|
|
29302
|
-
id:
|
|
29893
|
+
id: randomUUID7(),
|
|
29303
29894
|
projectId,
|
|
29304
29895
|
path: file2.path,
|
|
29305
29896
|
name: file2.name,
|
|
@@ -29313,9 +29904,9 @@ var SqliteProjectFilesRepository = class {
|
|
|
29313
29904
|
};
|
|
29314
29905
|
|
|
29315
29906
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
29316
|
-
import { randomUUID as
|
|
29907
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
29317
29908
|
var SqliteResolutionsRepository = class {
|
|
29318
|
-
constructor(db, now = () => Date.now(), newId = () =>
|
|
29909
|
+
constructor(db, now = () => Date.now(), newId = () => randomUUID8()) {
|
|
29319
29910
|
this.db = db;
|
|
29320
29911
|
this.now = now;
|
|
29321
29912
|
this.newId = newId;
|
|
@@ -29528,7 +30119,7 @@ var SqliteScanLedgerRepository = class {
|
|
|
29528
30119
|
};
|
|
29529
30120
|
|
|
29530
30121
|
// ../../packages/persistence/src/repositories/secret-vault.ts
|
|
29531
|
-
import { randomUUID as
|
|
30122
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
29532
30123
|
function pageLimit(requested, fallback) {
|
|
29533
30124
|
if (requested === void 0) return fallback;
|
|
29534
30125
|
return Math.min(Math.max(1, Math.trunc(requested)), MAX_VAULT_PAGE_LIMIT);
|
|
@@ -29574,12 +30165,14 @@ var SELECT_COLUMNS = `
|
|
|
29574
30165
|
ciphertext,
|
|
29575
30166
|
nonce,
|
|
29576
30167
|
auth_tag AS authTag,
|
|
30168
|
+
user_authorized AS userAuthorized,
|
|
29577
30169
|
occurrence_count AS occurrenceCount,
|
|
29578
30170
|
first_seen AS firstSeen,
|
|
29579
30171
|
last_seen AS lastSeen`;
|
|
29580
30172
|
function toRow(raw) {
|
|
29581
|
-
const { provider, ...rest } = raw;
|
|
29582
|
-
|
|
30173
|
+
const { provider, userAuthorized, ...rest } = raw;
|
|
30174
|
+
const row = { ...rest, userAuthorized: userAuthorized !== 0 };
|
|
30175
|
+
return provider === null ? row : { ...row, provider };
|
|
29583
30176
|
}
|
|
29584
30177
|
var SqliteSecretVaultRepository = class {
|
|
29585
30178
|
constructor(db) {
|
|
@@ -29589,17 +30182,18 @@ var SqliteSecretVaultRepository = class {
|
|
|
29589
30182
|
pointer_id, value_fingerprint, fingerprint_key_version, key_version,
|
|
29590
30183
|
format_version, category, rule_id, masked_match, provider,
|
|
29591
30184
|
ciphertext, nonce, auth_tag,
|
|
29592
|
-
occurrence_count, first_seen, last_seen
|
|
30185
|
+
user_authorized, occurrence_count, first_seen, last_seen
|
|
29593
30186
|
) VALUES (
|
|
29594
30187
|
:pointerId, :valueFingerprint, :fingerprintKeyVersion, :keyVersion,
|
|
29595
30188
|
:formatVersion, :category, :ruleId, :maskedMatch, :provider,
|
|
29596
30189
|
:ciphertext, :nonce, :authTag,
|
|
29597
|
-
1, :now, :now
|
|
30190
|
+
:userAuthorized, 1, :now, :now
|
|
29598
30191
|
)`
|
|
29599
30192
|
);
|
|
29600
30193
|
this.bumpStmt = db.prepare(
|
|
29601
30194
|
`UPDATE secret_vault
|
|
29602
|
-
SET occurrence_count = occurrence_count + 1, last_seen = :now
|
|
30195
|
+
SET occurrence_count = occurrence_count + 1, last_seen = :now,
|
|
30196
|
+
user_authorized = max(user_authorized, :userAuthorized)
|
|
29603
30197
|
WHERE value_fingerprint = :valueFingerprint`
|
|
29604
30198
|
);
|
|
29605
30199
|
this.byPointerStmt = db.prepare(
|
|
@@ -29619,6 +30213,7 @@ var SqliteSecretVaultRepository = class {
|
|
|
29619
30213
|
SET value_fingerprint = :valueFingerprint, fingerprint_key_version = :fingerprintKeyVersion
|
|
29620
30214
|
WHERE pointer_id = :pointerId`
|
|
29621
30215
|
);
|
|
30216
|
+
this.deleteByPointerStmt = db.prepare(`DELETE FROM secret_vault WHERE pointer_id = :pointerId`);
|
|
29622
30217
|
this.derefStmt = db.prepare(
|
|
29623
30218
|
`INSERT INTO secret_vault_deref (id, pointer_id, at, target, reason, outcome, grant_id, pointer_count)
|
|
29624
30219
|
VALUES (:id, :pointerId, :at, :target, :reason, :outcome, :grantId, :pointerCount)`
|
|
@@ -29632,6 +30227,7 @@ var SqliteSecretVaultRepository = class {
|
|
|
29632
30227
|
listStmt;
|
|
29633
30228
|
replaceCiphertextStmt;
|
|
29634
30229
|
refreshFingerprintStmt;
|
|
30230
|
+
deleteByPointerStmt;
|
|
29635
30231
|
derefStmt;
|
|
29636
30232
|
/**
|
|
29637
30233
|
* Vault a value, or record another sighting of one already vaulted. Keyed on
|
|
@@ -29640,6 +30236,11 @@ var SqliteSecretVaultRepository = class {
|
|
|
29640
30236
|
* pointer, category and ciphertext, so the same secret always resolves to one
|
|
29641
30237
|
* wire token. `minted` is true only when this call created the row.
|
|
29642
30238
|
*
|
|
30239
|
+
* `userAuthorized` is the one field a repeat call may still change, and only
|
|
30240
|
+
* upwards: it records that a PERSON asked for this value to be replaced, and
|
|
30241
|
+
* the row is shared with every automatic path that vaults the same value. See
|
|
30242
|
+
* `bumpStmt` for why clearing it is the defect this shape exists to refuse.
|
|
30243
|
+
*
|
|
29643
30244
|
* The read-then-write runs in one IMMEDIATE transaction so two concurrent
|
|
29644
30245
|
* writers cannot both decide they are minting.
|
|
29645
30246
|
*/
|
|
@@ -29666,13 +30267,18 @@ var SqliteSecretVaultRepository = class {
|
|
|
29666
30267
|
ciphertext: input2.ciphertext,
|
|
29667
30268
|
nonce: input2.nonce,
|
|
29668
30269
|
authTag: input2.authTag,
|
|
30270
|
+
userAuthorized: input2.userAuthorized === true ? 1 : 0,
|
|
29669
30271
|
now
|
|
29670
30272
|
})
|
|
29671
30273
|
);
|
|
29672
30274
|
minted = true;
|
|
29673
30275
|
return;
|
|
29674
30276
|
}
|
|
29675
|
-
this.bumpStmt.run({
|
|
30277
|
+
this.bumpStmt.run({
|
|
30278
|
+
valueFingerprint: input2.valueFingerprint,
|
|
30279
|
+
userAuthorized: input2.userAuthorized === true ? 1 : 0,
|
|
30280
|
+
now
|
|
30281
|
+
});
|
|
29676
30282
|
},
|
|
29677
30283
|
"IMMEDIATE"
|
|
29678
30284
|
);
|
|
@@ -29732,6 +30338,42 @@ var SqliteSecretVaultRepository = class {
|
|
|
29732
30338
|
);
|
|
29733
30339
|
return destroyed;
|
|
29734
30340
|
}
|
|
30341
|
+
/**
|
|
30342
|
+
* Destroy the named entries and report WHICH ones went — the scoped
|
|
30343
|
+
* counterpart to `purgeAll`, for a caller that has already put those specific
|
|
30344
|
+
* values back where they came from. Ids the store does not hold are absent
|
|
30345
|
+
* from the answer rather than an error, so a set assembled from a stale read
|
|
30346
|
+
* is not a fault. The deref audit is left alone, exactly as the purge leaves
|
|
30347
|
+
* it.
|
|
30348
|
+
*
|
|
30349
|
+
* The ids come back rather than a count because the caller's next act is to
|
|
30350
|
+
* write a purge row per destroyed entry, and a record of destruction has to
|
|
30351
|
+
* be a record of what was really destroyed: a selection is a claim about a
|
|
30352
|
+
* read that has since gone stale, and auditing from it invents a purge for an
|
|
30353
|
+
* entry still sitting in the vault.
|
|
30354
|
+
*
|
|
30355
|
+
* One transaction over the whole set rather than a statement per id: the
|
|
30356
|
+
* caller hands this the result of a restore pass it has completed, and a
|
|
30357
|
+
* fault partway through must leave the vault as it was found rather than
|
|
30358
|
+
* destroying a prefix of it. The vault holds the only copy of what a pointer
|
|
30359
|
+
* stands for, so half a delete is not a state anything can recover from.
|
|
30360
|
+
*/
|
|
30361
|
+
deleteByPointerIds(pointerIds) {
|
|
30362
|
+
if (pointerIds.length === 0) return [];
|
|
30363
|
+
const deleted = [];
|
|
30364
|
+
withTransaction(
|
|
30365
|
+
this.db,
|
|
30366
|
+
() => {
|
|
30367
|
+
for (const pointerId of pointerIds) {
|
|
30368
|
+
if (Number(this.deleteByPointerStmt.run({ pointerId }).changes) > 0) {
|
|
30369
|
+
deleted.push(pointerId);
|
|
30370
|
+
}
|
|
30371
|
+
}
|
|
30372
|
+
},
|
|
30373
|
+
"IMMEDIATE"
|
|
30374
|
+
);
|
|
30375
|
+
return deleted;
|
|
30376
|
+
}
|
|
29735
30377
|
/**
|
|
29736
30378
|
* Record (or re-stamp) one place a pointer has been written. One row per
|
|
29737
30379
|
* (pointer, location); a re-sighting bumps last_seen. Best-effort bookkeeping
|
|
@@ -29744,7 +30386,7 @@ var SqliteSecretVaultRepository = class {
|
|
|
29744
30386
|
VALUES (:id, :pointerId, :location, :kind, :now, :now)
|
|
29745
30387
|
ON CONFLICT (pointer_id, location) DO UPDATE SET last_seen = :now`
|
|
29746
30388
|
).run({
|
|
29747
|
-
id:
|
|
30389
|
+
id: randomUUID9(),
|
|
29748
30390
|
pointerId: entry.pointerId,
|
|
29749
30391
|
location: entry.location,
|
|
29750
30392
|
kind: entry.kind,
|
|
@@ -30257,15 +30899,15 @@ var SqliteSecurityRepository = class {
|
|
|
30257
30899
|
const from = now - RANGE_DAYS[range] * DAY_MS4;
|
|
30258
30900
|
const rows = allRows(
|
|
30259
30901
|
this.db.prepare(
|
|
30260
|
-
`SELECT
|
|
30902
|
+
`SELECT e.repo AS repo, count(*) AS c
|
|
30261
30903
|
FROM inspection_findings f
|
|
30262
30904
|
JOIN audit_events e ON e.id = f.audit_event_id
|
|
30263
30905
|
WHERE e.started_at >= :from AND e.started_at < :to
|
|
30264
30906
|
AND e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})
|
|
30265
|
-
AND
|
|
30266
|
-
AND
|
|
30267
|
-
GROUP BY repo
|
|
30268
|
-
ORDER BY c DESC, repo
|
|
30907
|
+
AND e.repo IS NOT NULL
|
|
30908
|
+
AND e.repo != ''
|
|
30909
|
+
GROUP BY e.repo
|
|
30910
|
+
ORDER BY c DESC, e.repo
|
|
30269
30911
|
LIMIT :limit`
|
|
30270
30912
|
),
|
|
30271
30913
|
{ from, to: now, limit }
|
|
@@ -30327,7 +30969,7 @@ var SqliteSecurityRepository = class {
|
|
|
30327
30969
|
`SELECT f.finding_key AS finding_key,
|
|
30328
30970
|
d.rule_id AS rule_id,
|
|
30329
30971
|
d.severity AS severity,
|
|
30330
|
-
|
|
30972
|
+
e.file_path AS path,
|
|
30331
30973
|
COALESCE(f.first_detected_at, e.started_at) AS first_detected_at,
|
|
30332
30974
|
latest.resolved_at AS latest_resolved_at
|
|
30333
30975
|
FROM ${LATEST_RESOLUTION_BY_KEY_SQL} latest
|
|
@@ -30380,7 +31022,7 @@ var SqliteSecurityRepository = class {
|
|
|
30380
31022
|
};
|
|
30381
31023
|
|
|
30382
31024
|
// ../../packages/persistence/src/repositories/shares.ts
|
|
30383
|
-
import { randomUUID as
|
|
31025
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
30384
31026
|
var MAX_EGRESS_CALL_SITES_PER_PROJECT = 5e3;
|
|
30385
31027
|
var IN_CHUNK = 500;
|
|
30386
31028
|
var KIND_ORDER = ["provider", "internal", "external", "ip"];
|
|
@@ -30468,7 +31110,7 @@ function buildSummary(dest, endpoints) {
|
|
|
30468
31110
|
callSiteCount,
|
|
30469
31111
|
transports: distinctTransports(transports),
|
|
30470
31112
|
dataClasses: distinctDataClasses(dataClasses),
|
|
30471
|
-
review: buildReviewInfo(dest.trust, transports),
|
|
31113
|
+
review: buildReviewInfo(dest.trust, transports, dest.overrideDecision !== null),
|
|
30472
31114
|
network: dest.kind === "provider" ? null : parseNetwork(dest.networkJson),
|
|
30473
31115
|
endpoints: endpoints.map(toEndpointSummary)
|
|
30474
31116
|
};
|
|
@@ -30495,7 +31137,7 @@ function buildDetail(dest, endpoints, callSites) {
|
|
|
30495
31137
|
lastSeen: new Date(lastSeenMs).toISOString(),
|
|
30496
31138
|
transports: distinctTransports(transports),
|
|
30497
31139
|
dataClasses: distinctDataClasses(endpoints.map((e) => e.dataClass)),
|
|
30498
|
-
review: buildReviewInfo(dest.trust, transports),
|
|
31140
|
+
review: buildReviewInfo(dest.trust, transports, dest.overrideDecision !== null),
|
|
30499
31141
|
network: dest.kind === "provider" ? null : parseNetwork(dest.networkJson),
|
|
30500
31142
|
note: dest.note,
|
|
30501
31143
|
endpoints: endpoints.map((ep) => ({
|
|
@@ -30524,7 +31166,11 @@ var SqliteSharesRepository = class {
|
|
|
30524
31166
|
FROM share_destination d
|
|
30525
31167
|
LEFT JOIN share_endpoint e ON e.destination_id = d.id
|
|
30526
31168
|
AND e.transport IN ${PLAINTEXT_TRANSPORT_SQL}
|
|
30527
|
-
WHERE d.trust IN ('unverified', 'ip') OR e.id IS NOT NULL
|
|
31169
|
+
WHERE (d.trust IN ('unverified', 'ip') OR e.id IS NOT NULL)
|
|
31170
|
+
AND NOT EXISTS (
|
|
31171
|
+
SELECT 1 FROM egress_decision_override o
|
|
31172
|
+
WHERE o.host = d.host OR (o.destination_id = d.id AND o.host IS NULL)
|
|
31173
|
+
)`
|
|
30528
31174
|
);
|
|
30529
31175
|
const kindCounts = countBy(
|
|
30530
31176
|
this.db,
|
|
@@ -30636,7 +31282,7 @@ var SqliteSharesRepository = class {
|
|
|
30636
31282
|
(id, destination_id, host, decision, created_at, updated_at)
|
|
30637
31283
|
VALUES (:id, :destinationId, :host, :decision, :now, :now)`
|
|
30638
31284
|
).run({
|
|
30639
|
-
id:
|
|
31285
|
+
id: randomUUID10(),
|
|
30640
31286
|
destinationId,
|
|
30641
31287
|
host: dest.host,
|
|
30642
31288
|
decision,
|
|
@@ -30785,7 +31431,7 @@ var SqliteSharesRepository = class {
|
|
|
30785
31431
|
let destinationId = destIds.get(hit.host);
|
|
30786
31432
|
if (destinationId === void 0) {
|
|
30787
31433
|
destStmt.run({
|
|
30788
|
-
id:
|
|
31434
|
+
id: randomUUID10(),
|
|
30789
31435
|
kind: hit.kind,
|
|
30790
31436
|
name: hit.name,
|
|
30791
31437
|
host: hit.host,
|
|
@@ -30801,7 +31447,7 @@ var SqliteSharesRepository = class {
|
|
|
30801
31447
|
let endpointId = endpointIds.get(endpointKey);
|
|
30802
31448
|
if (endpointId === void 0) {
|
|
30803
31449
|
endpointStmt.run({
|
|
30804
|
-
id:
|
|
31450
|
+
id: randomUUID10(),
|
|
30805
31451
|
destinationId,
|
|
30806
31452
|
method: hit.method,
|
|
30807
31453
|
transport: hit.transport,
|
|
@@ -30814,7 +31460,7 @@ var SqliteSharesRepository = class {
|
|
|
30814
31460
|
endpointIds.set(endpointKey, endpointId);
|
|
30815
31461
|
}
|
|
30816
31462
|
siteStmt.run({
|
|
30817
|
-
id:
|
|
31463
|
+
id: randomUUID10(),
|
|
30818
31464
|
endpointId,
|
|
30819
31465
|
project: input2.project,
|
|
30820
31466
|
projectKey: input2.projectKey,
|
|
@@ -31179,6 +31825,7 @@ function purgeSampleData(db) {
|
|
|
31179
31825
|
}
|
|
31180
31826
|
|
|
31181
31827
|
// ../../packages/persistence/src/database.ts
|
|
31828
|
+
var CAPTURE_GRAIN = new Set(EventKind.options);
|
|
31182
31829
|
var UNSAFE_TEST_ONLY_RAW_HANDLE = /* @__PURE__ */ Symbol(
|
|
31183
31830
|
"aka.persistence.unsafeTestOnlyRawHandle"
|
|
31184
31831
|
);
|
|
@@ -31226,7 +31873,7 @@ function backupLegacyStore(db, file2) {
|
|
|
31226
31873
|
discardStore(file2, backup);
|
|
31227
31874
|
return backup;
|
|
31228
31875
|
}
|
|
31229
|
-
function openAndInitialize(file2) {
|
|
31876
|
+
function openAndInitialize(file2, base) {
|
|
31230
31877
|
let db = openWithPragmas(file2);
|
|
31231
31878
|
try {
|
|
31232
31879
|
if (isForeignSqliteLineage(db)) {
|
|
@@ -31239,7 +31886,7 @@ function openAndInitialize(file2) {
|
|
|
31239
31886
|
applyMigrations(db, file2);
|
|
31240
31887
|
tightenPerms(file2);
|
|
31241
31888
|
const policies = new SqlitePoliciesRepository(db);
|
|
31242
|
-
const installedPacks = new SqliteInstalledPacksRepository(db);
|
|
31889
|
+
const installedPacks = new SqliteInstalledPacksRepository(db, base);
|
|
31243
31890
|
const repositories = {
|
|
31244
31891
|
events: new SqliteEventsRepository(db),
|
|
31245
31892
|
findings: new SqliteFindingsRepository(db),
|
|
@@ -31275,7 +31922,7 @@ function openAndInitialize(file2) {
|
|
|
31275
31922
|
}
|
|
31276
31923
|
function openLocalDatabase(dir) {
|
|
31277
31924
|
ensureDataDirSync(dir);
|
|
31278
|
-
const file2 =
|
|
31925
|
+
const file2 = join7(dir, DB_FILENAME);
|
|
31279
31926
|
reapStalePartials(file2);
|
|
31280
31927
|
const {
|
|
31281
31928
|
db,
|
|
@@ -31303,7 +31950,13 @@ function openLocalDatabase(dir) {
|
|
|
31303
31950
|
inspectionDefinitions,
|
|
31304
31951
|
inspectionFindings,
|
|
31305
31952
|
configInventory
|
|
31306
|
-
} = openAndInitialize(
|
|
31953
|
+
} = openAndInitialize(
|
|
31954
|
+
file2,
|
|
31955
|
+
// `dir` is always `<base>/data` — every caller resolves it through
|
|
31956
|
+
// `dataDir()` — so its parent is the `~/.aka` base the layout splits into
|
|
31957
|
+
// settings/ and data/, and the pack-policy floor needs both halves.
|
|
31958
|
+
dirname2(dir)
|
|
31959
|
+
);
|
|
31307
31960
|
function captureRowId(event) {
|
|
31308
31961
|
return captureId(
|
|
31309
31962
|
event.metadata?.sessionId ?? null,
|
|
@@ -31316,6 +31969,21 @@ function openLocalDatabase(dir) {
|
|
|
31316
31969
|
historySync.markSynced([captureRowId(event)], atMs);
|
|
31317
31970
|
});
|
|
31318
31971
|
}
|
|
31972
|
+
function markCaptureOwed(event) {
|
|
31973
|
+
failOpenTransaction(db, () => {
|
|
31974
|
+
historySync.markCaptureOwed(captureRowId(event));
|
|
31975
|
+
});
|
|
31976
|
+
}
|
|
31977
|
+
function markAuditEventsDelivered(events2, atMs) {
|
|
31978
|
+
const stampable = events2.filter((event) => !CAPTURE_GRAIN.has(event.eventType));
|
|
31979
|
+
if (stampable.length === 0) return;
|
|
31980
|
+
failOpenTransaction(db, () => {
|
|
31981
|
+
historySync.markSynced(
|
|
31982
|
+
stampable.map((event) => event.id),
|
|
31983
|
+
atMs
|
|
31984
|
+
);
|
|
31985
|
+
});
|
|
31986
|
+
}
|
|
31319
31987
|
function recordCapture(event, detected) {
|
|
31320
31988
|
failOpenTransaction(db, () => {
|
|
31321
31989
|
const sessionId = event.metadata?.sessionId;
|
|
@@ -31402,7 +32070,7 @@ function openLocalDatabase(dir) {
|
|
|
31402
32070
|
const definitionId = definitionIds.get(`${finding.ruleId}@${finding.version}`);
|
|
31403
32071
|
if (!definitionId) continue;
|
|
31404
32072
|
inspectionFindings.insertFinding({
|
|
31405
|
-
id:
|
|
32073
|
+
id: randomUUID11(),
|
|
31406
32074
|
auditEventId: record2.scanEvent.id,
|
|
31407
32075
|
inspectionDefinitionId: definitionId,
|
|
31408
32076
|
span: finding.span,
|
|
@@ -31498,6 +32166,8 @@ function openLocalDatabase(dir) {
|
|
|
31498
32166
|
inspectionFindings,
|
|
31499
32167
|
recordCapture,
|
|
31500
32168
|
markCaptureDelivered,
|
|
32169
|
+
markCaptureOwed,
|
|
32170
|
+
markAuditEventsDelivered,
|
|
31501
32171
|
ensureInventory,
|
|
31502
32172
|
recordConfigScan,
|
|
31503
32173
|
recordProjectFiles,
|
|
@@ -31516,164 +32186,23 @@ function openLocalDatabase(dir) {
|
|
|
31516
32186
|
};
|
|
31517
32187
|
}
|
|
31518
32188
|
|
|
31519
|
-
// ../../packages/persistence/src/file-lock.ts
|
|
31520
|
-
import { randomUUID as randomUUID11 } from "crypto";
|
|
31521
|
-
import {
|
|
31522
|
-
closeSync,
|
|
31523
|
-
existsSync as existsSync2,
|
|
31524
|
-
openSync,
|
|
31525
|
-
readFileSync as readFileSync2,
|
|
31526
|
-
rmSync as rmSync5,
|
|
31527
|
-
statSync as statSync3,
|
|
31528
|
-
writeFileSync as writeFileSync2
|
|
31529
|
-
} from "fs";
|
|
31530
|
-
import { hostname as hostname3 } from "os";
|
|
31531
|
-
var PARK = new Int32Array(new SharedArrayBuffer(4));
|
|
31532
|
-
|
|
31533
32189
|
// ../../packages/persistence/src/finding-key.ts
|
|
31534
32190
|
import { createHash as createHash3 } from "crypto";
|
|
31535
32191
|
|
|
31536
32192
|
// ../../packages/persistence/src/fingerprint.ts
|
|
31537
32193
|
import { createHmac, randomBytes } from "crypto";
|
|
31538
|
-
import { existsSync as existsSync3, readFileSync as
|
|
31539
|
-
import { join as
|
|
32194
|
+
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
|
|
32195
|
+
import { join as join8 } from "path";
|
|
31540
32196
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
31541
32197
|
|
|
31542
32198
|
// ../../packages/persistence/src/history-preview.ts
|
|
31543
32199
|
import { existsSync as existsSync4 } from "fs";
|
|
31544
|
-
import { join as
|
|
32200
|
+
import { join as join9 } from "path";
|
|
31545
32201
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
31546
32202
|
|
|
31547
|
-
// ../../packages/persistence/src/local-layout.ts
|
|
31548
|
-
import { renameSync as renameSync3 } from "fs";
|
|
31549
|
-
import { mkdir } from "fs/promises";
|
|
31550
|
-
import { homedir } from "os";
|
|
31551
|
-
import { join as join7 } from "path";
|
|
31552
|
-
function defaultDataDir() {
|
|
31553
|
-
return join7(homedir(), ".aka");
|
|
31554
|
-
}
|
|
31555
|
-
function settingsDir(base = defaultDataDir()) {
|
|
31556
|
-
return join7(base, "settings");
|
|
31557
|
-
}
|
|
31558
|
-
function dataDir(base = defaultDataDir()) {
|
|
31559
|
-
return join7(base, "data");
|
|
31560
|
-
}
|
|
31561
|
-
function dbPath(base = defaultDataDir()) {
|
|
31562
|
-
return join7(dataDir(base), "aka.db");
|
|
31563
|
-
}
|
|
31564
|
-
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
31565
|
-
ensureDataDirSync(dir);
|
|
31566
|
-
}
|
|
31567
|
-
function migrateLegacyLayout(base = defaultDataDir()) {
|
|
31568
|
-
const moves = [
|
|
31569
|
-
{ name: "config.json", dest: settingsDir(base) },
|
|
31570
|
-
{ name: "policy-cache.json", dest: dataDir(base) }
|
|
31571
|
-
];
|
|
31572
|
-
for (const { name, dest } of moves) {
|
|
31573
|
-
try {
|
|
31574
|
-
ensureDataDirSync(dest);
|
|
31575
|
-
const moved = join7(dest, name);
|
|
31576
|
-
renameSync3(join7(base, name), moved);
|
|
31577
|
-
tightenFile(moved);
|
|
31578
|
-
} catch {
|
|
31579
|
-
}
|
|
31580
|
-
}
|
|
31581
|
-
}
|
|
31582
|
-
|
|
31583
|
-
// ../../packages/persistence/src/managed-settings.ts
|
|
31584
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
31585
|
-
import { posix, win32 } from "path";
|
|
31586
|
-
function managedSettingsPaths(platform2 = process.platform) {
|
|
31587
|
-
if (platform2 === "darwin") {
|
|
31588
|
-
return [
|
|
31589
|
-
posix.join("/Library", "Application Support", "AKASecurity", MANAGED_SETTINGS_FILENAME),
|
|
31590
|
-
posix.join("/Library", "Managed Preferences", MANAGED_SETTINGS_FILENAME)
|
|
31591
|
-
];
|
|
31592
|
-
}
|
|
31593
|
-
if (platform2 === "win32") {
|
|
31594
|
-
return [win32.join("C:\\", "ProgramData", "AKASecurity", MANAGED_SETTINGS_FILENAME)];
|
|
31595
|
-
}
|
|
31596
|
-
return [posix.join("/etc", "aka", MANAGED_SETTINGS_FILENAME)];
|
|
31597
|
-
}
|
|
31598
|
-
function readManagedSettings(paths = managedSettingsPaths()) {
|
|
31599
|
-
for (const path of paths) {
|
|
31600
|
-
let text;
|
|
31601
|
-
try {
|
|
31602
|
-
text = readFileSync4(path, "utf8");
|
|
31603
|
-
} catch {
|
|
31604
|
-
continue;
|
|
31605
|
-
}
|
|
31606
|
-
const record2 = parseJsonObject(text);
|
|
31607
|
-
if (!record2) continue;
|
|
31608
|
-
const parsed = ManagedSettings.safeParse(record2);
|
|
31609
|
-
if (parsed.success) return parsed.data;
|
|
31610
|
-
}
|
|
31611
|
-
return null;
|
|
31612
|
-
}
|
|
31613
|
-
function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ new Date()) {
|
|
31614
|
-
if (!managed) return settings;
|
|
31615
|
-
const { values } = managed;
|
|
31616
|
-
const merged = { ...settings };
|
|
31617
|
-
if (values.runMode !== void 0) merged.runMode = values.runMode;
|
|
31618
|
-
if (values.controlPlane !== void 0) {
|
|
31619
|
-
merged.controlPlane = {
|
|
31620
|
-
...values.controlPlane,
|
|
31621
|
-
// The administrator pinned WHICH deployment, not WHEN this machine
|
|
31622
|
-
// joined it. Keep the user's own attach time when the endpoint is
|
|
31623
|
-
// unchanged, so a managed machine does not appear to re-attach on every
|
|
31624
|
-
// read; stamp a fresh one when the administrator moved it.
|
|
31625
|
-
attachedAt: settings.controlPlane?.endpoint === values.controlPlane.endpoint ? settings.controlPlane.attachedAt : now().toISOString()
|
|
31626
|
-
};
|
|
31627
|
-
}
|
|
31628
|
-
if (values.historicalAccess !== void 0) merged.historicalAccess = values.historicalAccess;
|
|
31629
|
-
if (values.vaultKeyCustody !== void 0) merged.vaultKeyCustody = values.vaultKeyCustody;
|
|
31630
|
-
if (values.vaultInlineReveal !== void 0) merged.vaultInlineReveal = values.vaultInlineReveal;
|
|
31631
|
-
if (values.dataSharesInPlace !== void 0) merged.dataSharesInPlace = values.dataSharesInPlace;
|
|
31632
|
-
if (values.vaultConsent !== void 0) {
|
|
31633
|
-
merged.vaultConsent = values.vaultConsent ? (
|
|
31634
|
-
// Keep an existing valid grant so its acknowledgedAt survives; mint one
|
|
31635
|
-
// at the current version otherwise.
|
|
31636
|
-
settings.vaultConsent?.version === VAULT_CONSENT_VERSION ? settings.vaultConsent : { acknowledgedAt: now().toISOString(), version: VAULT_CONSENT_VERSION }
|
|
31637
|
-
) : void 0;
|
|
31638
|
-
}
|
|
31639
|
-
if (values.modelJudgeConsent !== void 0) {
|
|
31640
|
-
merged.modelJudgeConsent = values.modelJudgeConsent ? settings.modelJudgeConsent?.payloadVersion === MODEL_JUDGE_PAYLOAD_VERSION ? settings.modelJudgeConsent : {
|
|
31641
|
-
acknowledgedAt: now().toISOString(),
|
|
31642
|
-
payloadVersion: MODEL_JUDGE_PAYLOAD_VERSION
|
|
31643
|
-
} : void 0;
|
|
31644
|
-
}
|
|
31645
|
-
return merged;
|
|
31646
|
-
}
|
|
31647
|
-
|
|
31648
|
-
// ../../packages/persistence/src/settings.ts
|
|
31649
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
31650
|
-
import { join as join8 } from "path";
|
|
31651
|
-
var SETTINGS_FILENAME = "settings.json";
|
|
31652
|
-
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
31653
|
-
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
31654
|
-
}
|
|
31655
|
-
function readUserSettings(base) {
|
|
31656
|
-
const record2 = readJson(join8(settingsDir(base), SETTINGS_FILENAME));
|
|
31657
|
-
if (!record2) return defaultWorkspaceSettings();
|
|
31658
|
-
try {
|
|
31659
|
-
return WorkspaceSettings.parse(record2);
|
|
31660
|
-
} catch {
|
|
31661
|
-
return defaultWorkspaceSettings();
|
|
31662
|
-
}
|
|
31663
|
-
}
|
|
31664
|
-
function readJson(file2) {
|
|
31665
|
-
let text;
|
|
31666
|
-
try {
|
|
31667
|
-
text = readFileSync5(file2, "utf8");
|
|
31668
|
-
} catch {
|
|
31669
|
-
return null;
|
|
31670
|
-
}
|
|
31671
|
-
return parseJsonObject(text) ?? null;
|
|
31672
|
-
}
|
|
31673
|
-
|
|
31674
32203
|
// ../../packages/persistence/src/store-symlinks.ts
|
|
31675
32204
|
import { existsSync as existsSync5, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
31676
|
-
import { dirname as
|
|
32205
|
+
import { dirname as dirname3, join as join10, resolve } from "path";
|
|
31677
32206
|
|
|
31678
32207
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
31679
32208
|
import {
|
|
@@ -31687,19 +32216,19 @@ import {
|
|
|
31687
32216
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
31688
32217
|
import { execFileSync } from "child_process";
|
|
31689
32218
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
31690
|
-
import { chmodSync as chmodSync3, readFileSync as
|
|
31691
|
-
import { join as
|
|
32219
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
32220
|
+
import { join as join11 } from "path";
|
|
31692
32221
|
|
|
31693
32222
|
// ../../packages/persistence/src/vault/vault.ts
|
|
31694
32223
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
31695
32224
|
|
|
31696
32225
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
31697
32226
|
import { existsSync as existsSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
31698
|
-
import { join as
|
|
32227
|
+
import { join as join12 } from "path";
|
|
31699
32228
|
|
|
31700
32229
|
// ../../packages/plugin-sdk/src/config.ts
|
|
31701
32230
|
import { existsSync as existsSync7 } from "fs";
|
|
31702
|
-
import { join as
|
|
32231
|
+
import { join as join13 } from "path";
|
|
31703
32232
|
|
|
31704
32233
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
31705
32234
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
@@ -31753,7 +32282,7 @@ function resolveProvider() {
|
|
|
31753
32282
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
31754
32283
|
try {
|
|
31755
32284
|
ensureLayoutDirSync(base);
|
|
31756
|
-
const settingsFile =
|
|
32285
|
+
const settingsFile = join13(settingsDir(base), "settings.json");
|
|
31757
32286
|
if (existsSync7(settingsFile)) tightenFile(settingsFile);
|
|
31758
32287
|
} catch {
|
|
31759
32288
|
}
|
|
@@ -31777,9 +32306,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
31777
32306
|
}
|
|
31778
32307
|
|
|
31779
32308
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
31780
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
32309
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
31781
32310
|
import { homedir as homedir2 } from "os";
|
|
31782
|
-
import { basename as basename3, join as
|
|
32311
|
+
import { basename as basename3, join as join15 } from "path";
|
|
31783
32312
|
|
|
31784
32313
|
// ../../packages/detections/src/egress/registry.ts
|
|
31785
32314
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -34814,8 +35343,8 @@ function maskText(text) {
|
|
|
34814
35343
|
}
|
|
34815
35344
|
|
|
34816
35345
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
34817
|
-
import { existsSync as existsSync8, readFileSync as
|
|
34818
|
-
import { basename as basename2, dirname as
|
|
35346
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
35347
|
+
import { basename as basename2, dirname as dirname4, isAbsolute, join as join14, sep as sep2 } from "path";
|
|
34819
35348
|
|
|
34820
35349
|
// ../../packages/plugin-sdk/src/events.ts
|
|
34821
35350
|
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
@@ -34827,8 +35356,8 @@ import { Worker } from "worker_threads";
|
|
|
34827
35356
|
|
|
34828
35357
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
34829
35358
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
34830
|
-
import { readFileSync as
|
|
34831
|
-
import { join as
|
|
35359
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
35360
|
+
import { join as join16 } from "path";
|
|
34832
35361
|
|
|
34833
35362
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
34834
35363
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
@@ -34839,20 +35368,20 @@ import {
|
|
|
34839
35368
|
fstatSync,
|
|
34840
35369
|
mkdirSync as mkdirSync2,
|
|
34841
35370
|
openSync as openSync2,
|
|
34842
|
-
readFileSync as
|
|
35371
|
+
readFileSync as readFileSync11,
|
|
34843
35372
|
readSync,
|
|
34844
35373
|
writeFileSync as writeFileSync5
|
|
34845
35374
|
} from "fs";
|
|
34846
|
-
import { join as
|
|
35375
|
+
import { join as join17 } from "path";
|
|
34847
35376
|
var TAIL_BYTES = 256 * 1024;
|
|
34848
35377
|
|
|
34849
35378
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
34850
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
34851
|
-
import { join as
|
|
35379
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
35380
|
+
import { join as join18 } from "path";
|
|
34852
35381
|
|
|
34853
35382
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
34854
35383
|
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
34855
|
-
import { basename as basename4, dirname as
|
|
35384
|
+
import { basename as basename4, dirname as dirname5, sep as sep3 } from "path";
|
|
34856
35385
|
|
|
34857
35386
|
// ../../packages/plugin-sdk/src/posture.ts
|
|
34858
35387
|
function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
|
|
@@ -34866,7 +35395,7 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
|
|
|
34866
35395
|
|
|
34867
35396
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
34868
35397
|
import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
|
|
34869
|
-
import { basename as basename5, join as
|
|
35398
|
+
import { basename as basename5, join as join19 } from "path";
|
|
34870
35399
|
|
|
34871
35400
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
34872
35401
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -34984,11 +35513,11 @@ async function applySetupTriageSuppressions(entries, writer, opts) {
|
|
|
34984
35513
|
|
|
34985
35514
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
34986
35515
|
import { mkdirSync as mkdirSync4, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
34987
|
-
import { join as
|
|
35516
|
+
import { join as join20 } from "path";
|
|
34988
35517
|
|
|
34989
35518
|
// ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
|
|
34990
35519
|
import { writeFileSync as writeFileSync8 } from "fs";
|
|
34991
|
-
import { join as
|
|
35520
|
+
import { join as join21 } from "path";
|
|
34992
35521
|
|
|
34993
35522
|
// ../../packages/setup-wizard/src/triage/dedupe.ts
|
|
34994
35523
|
function dedupeKey(hit) {
|
|
@@ -35043,12 +35572,12 @@ function deriveFalsePositivePatterns(hits, rec, plan) {
|
|
|
35043
35572
|
}
|
|
35044
35573
|
|
|
35045
35574
|
// ../../packages/setup-wizard/src/triage/gate-display.ts
|
|
35046
|
-
function findContext(entry,
|
|
35047
|
-
const byFingerprint =
|
|
35575
|
+
function findContext(entry, join25) {
|
|
35576
|
+
const byFingerprint = join25.find(
|
|
35048
35577
|
(j) => j.valueFingerprint !== void 0 && j.valueFingerprint === entry.valueFingerprint
|
|
35049
35578
|
);
|
|
35050
35579
|
if (byFingerprint) return byFingerprint.maskedContext;
|
|
35051
|
-
const byRuleAndMask =
|
|
35580
|
+
const byRuleAndMask = join25.find(
|
|
35052
35581
|
(j) => j.ruleId === entry.ruleId && j.maskedMatch === entry.maskedValue
|
|
35053
35582
|
);
|
|
35054
35583
|
return byRuleAndMask?.maskedContext;
|
|
@@ -35119,13 +35648,13 @@ function renderShowcase(showcase) {
|
|
|
35119
35648
|
|
|
35120
35649
|
${blocks.join("\n\n")}`;
|
|
35121
35650
|
}
|
|
35122
|
-
function renderSuppressionGate(entries,
|
|
35651
|
+
function renderSuppressionGate(entries, join25) {
|
|
35123
35652
|
if (entries.length === 0) {
|
|
35124
35653
|
return "No false-positive suppressions to confirm \u2014 nothing will be written.";
|
|
35125
35654
|
}
|
|
35126
35655
|
const header = entries.length === 1 ? "This looks like a false positive \u2014 take a look before I suppress it:" : `These ${String(entries.length)} look like false positives \u2014 take a look before I suppress them:`;
|
|
35127
35656
|
const blocks = entries.map((entry, i) => {
|
|
35128
|
-
const context = findContext(entry,
|
|
35657
|
+
const context = findContext(entry, join25);
|
|
35129
35658
|
const lines = [
|
|
35130
35659
|
`${String(i + 1)}. ${entry.ruleId} [${entry.category}]`,
|
|
35131
35660
|
` value: ${entry.maskedValue}`,
|
|
@@ -35211,9 +35740,9 @@ function mergeRecommendations(verdicts) {
|
|
|
35211
35740
|
}
|
|
35212
35741
|
|
|
35213
35742
|
// ../../packages/setup-wizard/src/triage/plan-file.ts
|
|
35214
|
-
import { mkdtempSync, readFileSync as
|
|
35743
|
+
import { mkdtempSync, readFileSync as readFileSync13, rmdirSync, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
35215
35744
|
import { tmpdir } from "os";
|
|
35216
|
-
import { basename as basename6, dirname as
|
|
35745
|
+
import { basename as basename6, dirname as dirname6, join as join22 } from "path";
|
|
35217
35746
|
var SuppressionEntrySchema = external_exports.object({
|
|
35218
35747
|
ruleId: external_exports.string(),
|
|
35219
35748
|
category: DetectionCategory,
|
|
@@ -35268,19 +35797,19 @@ function serializePlan(plan, current) {
|
|
|
35268
35797
|
function writePlanFile(plan, current, rawValues, deps = {}) {
|
|
35269
35798
|
const serialized = serializePlan(plan, current);
|
|
35270
35799
|
assertRawFree(serialized, rawValues);
|
|
35271
|
-
const dir = (deps.mkTempDir ?? (() => mkdtempSync(
|
|
35272
|
-
const path =
|
|
35800
|
+
const dir = (deps.mkTempDir ?? (() => mkdtempSync(join22(tmpdir(), "aka-plan-"))))();
|
|
35801
|
+
const path = join22(dir, "setup-plan.json");
|
|
35273
35802
|
writeFileSync9(path, serialized, { encoding: "utf8", mode: 384 });
|
|
35274
35803
|
return path;
|
|
35275
35804
|
}
|
|
35276
35805
|
function readPlanFile(path) {
|
|
35277
|
-
const text =
|
|
35806
|
+
const text = readFileSync13(path, "utf8");
|
|
35278
35807
|
const json2 = JSON.parse(text);
|
|
35279
35808
|
return PersistedPlanSchema.parse(json2);
|
|
35280
35809
|
}
|
|
35281
35810
|
function deletePlanFile(path) {
|
|
35282
35811
|
rmSync7(path, { force: true });
|
|
35283
|
-
const dir =
|
|
35812
|
+
const dir = dirname6(path);
|
|
35284
35813
|
if (!basename6(dir).startsWith("aka-plan-")) return;
|
|
35285
35814
|
try {
|
|
35286
35815
|
rmdirSync(dir);
|
|
@@ -35348,8 +35877,8 @@ function buildJoinEntries(hits) {
|
|
|
35348
35877
|
}
|
|
35349
35878
|
|
|
35350
35879
|
// ../../packages/setup-wizard/src/triage/resolve.ts
|
|
35351
|
-
function resolveSuppressions(rec,
|
|
35352
|
-
const byId = new Map(
|
|
35880
|
+
function resolveSuppressions(rec, join25) {
|
|
35881
|
+
const byId = new Map(join25.map((e) => [e.id, e]));
|
|
35353
35882
|
const entries = [];
|
|
35354
35883
|
const skipped = [];
|
|
35355
35884
|
for (const cat of rec.perCategory) {
|
|
@@ -35451,7 +35980,7 @@ function parseTriageStream(text) {
|
|
|
35451
35980
|
return { hits, status: "complete" };
|
|
35452
35981
|
}
|
|
35453
35982
|
function planTriageWriteback(hits, rec) {
|
|
35454
|
-
const
|
|
35983
|
+
const join25 = buildJoinEntries(hits);
|
|
35455
35984
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
35456
35985
|
const skipped = [];
|
|
35457
35986
|
const posture = {};
|
|
@@ -35491,7 +36020,7 @@ function planTriageWriteback(hits, rec) {
|
|
|
35491
36020
|
}
|
|
35492
36021
|
const { entries, skipped: resolveSkips } = resolveSuppressions(
|
|
35493
36022
|
{ perCategory: safeCategories, notes: rec.notes },
|
|
35494
|
-
|
|
36023
|
+
join25
|
|
35495
36024
|
);
|
|
35496
36025
|
skipped.push(...resolveSkips);
|
|
35497
36026
|
let notes = rec.notes;
|
|
@@ -35501,7 +36030,7 @@ function planTriageWriteback(hits, rec) {
|
|
|
35501
36030
|
if (err instanceof RawEgressError) notes = SCRUBBED_NOTES;
|
|
35502
36031
|
else throw err;
|
|
35503
36032
|
}
|
|
35504
|
-
return { entries, posture, showcase, join:
|
|
36033
|
+
return { entries, posture, showcase, join: join25, notes, skipped };
|
|
35505
36034
|
}
|
|
35506
36035
|
function recommendedPosture(evidence) {
|
|
35507
36036
|
return { ...severityFloorPosture(), ...evidence };
|
|
@@ -35770,9 +36299,9 @@ function parseRecommendation(text) {
|
|
|
35770
36299
|
|
|
35771
36300
|
// src/triage/judge.ts
|
|
35772
36301
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
35773
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as
|
|
36302
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync14, rmSync as rmSync8 } from "fs";
|
|
35774
36303
|
import { tmpdir as tmpdir2 } from "os";
|
|
35775
|
-
import { dirname as
|
|
36304
|
+
import { dirname as dirname7, join as join23 } from "path";
|
|
35776
36305
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
35777
36306
|
|
|
35778
36307
|
// ../../packages/plugin-sdk/src/bare-command.ts
|
|
@@ -35883,8 +36412,8 @@ function planBareCommand(command, args, deps = {}) {
|
|
|
35883
36412
|
}
|
|
35884
36413
|
|
|
35885
36414
|
// src/triage/judge.ts
|
|
35886
|
-
var TRIAGE_DIR =
|
|
35887
|
-
var DEFAULT_RUBRIC_PATH =
|
|
36415
|
+
var TRIAGE_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
36416
|
+
var DEFAULT_RUBRIC_PATH = join23(
|
|
35888
36417
|
TRIAGE_DIR,
|
|
35889
36418
|
"..",
|
|
35890
36419
|
"..",
|
|
@@ -35921,7 +36450,7 @@ function judgeEnv(platform2 = process.platform) {
|
|
|
35921
36450
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
|
|
35922
36451
|
};
|
|
35923
36452
|
if (platform2 === "darwin") {
|
|
35924
|
-
env.CLAUDE_CONFIG_DIR = mkdtempSync2(
|
|
36453
|
+
env.CLAUDE_CONFIG_DIR = mkdtempSync2(join23(tmpdir2(), "aka-judge-cfg-"));
|
|
35925
36454
|
}
|
|
35926
36455
|
return env;
|
|
35927
36456
|
}
|
|
@@ -35956,7 +36485,7 @@ function runJudge(hits, deps) {
|
|
|
35956
36485
|
if (typeof deps.spawn !== "function") {
|
|
35957
36486
|
throw new TypeError("runJudge requires deps.spawn \u2014 there is no live-spawn fallback");
|
|
35958
36487
|
}
|
|
35959
|
-
const rubric = deps.loadRubric?.() ??
|
|
36488
|
+
const rubric = deps.loadRubric?.() ?? readFileSync14(DEFAULT_RUBRIC_PATH, "utf8");
|
|
35960
36489
|
const hitsJsonl = hits.map((h) => JSON.stringify(toJudgePayload(h))).join("\n");
|
|
35961
36490
|
const fullPrompt = `${rubric}
|
|
35962
36491
|
|
|
@@ -36225,11 +36754,11 @@ function resolveCreatedBy() {
|
|
|
36225
36754
|
}
|
|
36226
36755
|
}
|
|
36227
36756
|
function loadRubric() {
|
|
36228
|
-
const here =
|
|
36229
|
-
const shipped =
|
|
36230
|
-
if (existsSync11(shipped)) return
|
|
36231
|
-
return
|
|
36232
|
-
|
|
36757
|
+
const here = dirname8(fileURLToPath4(import.meta.url));
|
|
36758
|
+
const shipped = join24(here, "triage-rubric.md");
|
|
36759
|
+
if (existsSync11(shipped)) return readFileSync15(shipped, "utf8");
|
|
36760
|
+
return readFileSync15(
|
|
36761
|
+
join24(here, "..", "..", "..", "packages", "setup-wizard", "assets", "triage-rubric.md"),
|
|
36233
36762
|
"utf8"
|
|
36234
36763
|
);
|
|
36235
36764
|
}
|
|
@@ -36239,7 +36768,7 @@ async function main() {
|
|
|
36239
36768
|
argv,
|
|
36240
36769
|
// fd 0 = stdin; the wizard pipes `backfill.js --triage` into this on preview.
|
|
36241
36770
|
// Called only on the preview path — the confirm path never reads a stream.
|
|
36242
|
-
readStream: (streamPath) => streamPath !== void 0 ?
|
|
36771
|
+
readStream: (streamPath) => streamPath !== void 0 ? readFileSync15(streamPath, "utf8") : readFileSync15(0, "utf8"),
|
|
36243
36772
|
runJudge: (hits) => runJudge(hits, { spawn: spawnClaude, loadRubric }),
|
|
36244
36773
|
// The distinct model-judge egress consent, read from settings.json. When it
|
|
36245
36774
|
// is absent or stale the preview skips the judge instead of sending findings
|