@akasecurity/ai-tc-claude-code 0.9.11 → 0.9.13

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.
@@ -20533,7 +20533,17 @@ var UNPRICEABLE_PROVIDERS = Object.freeze([
20533
20533
  "gateway",
20534
20534
  "unknown",
20535
20535
  "cli",
20536
- "api"
20536
+ "api",
20537
+ // The browser extension's native host records these as `llm_call.provider`
20538
+ // for a web-chat turn — the web tool id, deliberately never the vendor id
20539
+ // (`openai`/`anthropic`) the session root carries. Subscription traffic
20540
+ // burns rate-limit budget, not dollar credits, and listing them here is
20541
+ // what keeps that true structurally: a later maintainer who wants to price
20542
+ // web-chat traffic at API rates has to delete this entry first, and meet
20543
+ // the reason on the way, rather than quietly adding one to
20544
+ // PROVIDER_PLATFORM.
20545
+ "chatgpt",
20546
+ "claude-ai"
20537
20547
  ]);
20538
20548
 
20539
20549
  // ../../packages/schema/src/token/format.ts
@@ -20553,7 +20563,12 @@ var HARNESS = {
20553
20563
  ClaudeDesktop: "claudedesktop",
20554
20564
  ChatGpt: "chatgpt",
20555
20565
  ClaudeAi: "claudeai",
20556
- Api: "api"
20566
+ Api: "api",
20567
+ // Not a coding assistant a person drives — an in-process SDK embedded in an
20568
+ // application, so it has no IDE/CLI/desktop/web surface of its own. Carries
20569
+ // the same id as its SOURCE_TOOL counterpart, unlike every capture-side tool
20570
+ // whose wire spelling differs from its display spelling.
20571
+ AiTcSdk: "ai-tc-sdk"
20557
20572
  };
20558
20573
  var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
20559
20574
  var SOURCE_TOOL = {
@@ -20569,9 +20584,15 @@ var SOURCE_TOOL = {
20569
20584
  // whose tool could not be identified both render through the read side's
20570
20585
  // miss path rather than as a harness of their own.
20571
20586
  Cli: "cli",
20572
- Unknown: "unknown"
20587
+ Unknown: "unknown",
20588
+ // The wire id an in-process, request-path SDK stamps on its own structural
20589
+ // rows (`request_decision`) — never a capture of prompt/response/tool text,
20590
+ // since the SDK sits in front of a model call rather than inside a coding
20591
+ // assistant's own hook contract.
20592
+ AiTcSdk: "ai-tc-sdk"
20573
20593
  };
20574
20594
  var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
20595
+ var WebSourceTool = SourceTool.extract(["ChatGpt", "ClaudeAi"]);
20575
20596
  var TOOL_TO_HARNESS = {
20576
20597
  [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
20577
20598
  [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
@@ -20580,7 +20601,12 @@ var TOOL_TO_HARNESS = {
20580
20601
  [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
20581
20602
  [SOURCE_TOOL.Codex]: HARNESS.Codex,
20582
20603
  [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
20583
- [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
20604
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi,
20605
+ // Wire and display id are the same string here, but the row still belongs:
20606
+ // both vocabularies carry the `AiTcSdk` member, and the join is exactly
20607
+ // their intersection — leaving a shared member out would read as an
20608
+ // uninstrumented tool on both surfaces, which this one is not.
20609
+ [SOURCE_TOOL.AiTcSdk]: HARNESS.AiTcSdk
20584
20610
  };
20585
20611
 
20586
20612
  // ../../packages/schema/src/zod/finding.ts
@@ -20614,7 +20640,8 @@ var FindingProvider = Harness.extract([
20614
20640
  "ClaudeAi",
20615
20641
  "Codex",
20616
20642
  "Antigravity",
20617
- "Api"
20643
+ "Api",
20644
+ "AiTcSdk"
20618
20645
  ]).meta({ id: "FindingProvider" });
20619
20646
  var FindingCategory = external_exports.enum([
20620
20647
  "secret",
@@ -20630,6 +20657,15 @@ var FindingCategory = external_exports.enum([
20630
20657
  ]).meta({ id: "FindingCategory" });
20631
20658
  var FindingOrigin = external_exports.enum(["in-flight", "at-rest"]).meta({ id: "FindingOrigin" });
20632
20659
  var FindingStatus = external_exports.enum(["open", "handled", "resolved", "dismissed"]).meta({ id: "FindingStatus" });
20660
+ var SyncFailureReason = external_exports.enum(["deployment_refused", "payload_invalid", "detached_undelivered"]).meta({ id: "SyncFailureReason" });
20661
+ var FindingDeliveryState = external_exports.enum(["sent", "queued", "not_sent", "never_offered", "local_scan"]).meta({ id: "FindingDeliveryState" });
20662
+ var FindingDelivery = external_exports.object({
20663
+ state: FindingDeliveryState,
20664
+ // The delivery time for `sent`; the failure time for `not_sent` when recorded.
20665
+ at: external_exports.iso.datetime().optional(),
20666
+ // Only on `not_sent`, and only when a known reason was recorded.
20667
+ reason: SyncFailureReason.optional()
20668
+ }).meta({ id: "FindingDelivery" });
20633
20669
  var ResolutionMethod = external_exports.enum([
20634
20670
  "enforced-in-flight",
20635
20671
  "fixed-at-source",
@@ -20686,7 +20722,10 @@ var FindingInstance = external_exports.object({
20686
20722
  // The session that event belongs to, when it has one — the seam a
20687
20723
  // per-instance "view session" link needs. Absent for events captured
20688
20724
  // outside a session.
20689
- sessionId: external_exports.string().optional()
20725
+ sessionId: external_exports.string().optional(),
20726
+ // The delivery state of the event above (see FindingDelivery). Optional so
20727
+ // readers that do not project it stay valid.
20728
+ delivery: FindingDelivery.optional()
20690
20729
  }).meta({ id: "FindingInstance" });
20691
20730
  var FindingGroup = external_exports.object({
20692
20731
  id: external_exports.string(),
@@ -20738,7 +20777,10 @@ var FindingFacets = external_exports.object({
20738
20777
  // Host tool (attributes.tool_name). Present only on the instance-level
20739
20778
  // reads, which can filter by it; the type-level read omits the dimension
20740
20779
  // because a group spans tools.
20741
- tool: external_exports.array(FindingFacetItem).optional()
20780
+ tool: external_exports.array(FindingFacetItem).optional(),
20781
+ // Delivery states (FindingDeliveryState). Present only on the
20782
+ // instance-level reads, like `tool`.
20783
+ deployment: external_exports.array(FindingFacetItem).optional()
20742
20784
  }).meta({ id: "FindingFacets" });
20743
20785
  var FindingTypeSummary = FindingGroup.omit({ instances: true, match: true }).meta({
20744
20786
  id: "FindingTypeSummary"
@@ -20847,6 +20889,8 @@ var ListFindingInstancesQuery = external_exports.object({
20847
20889
  // Exact host-tool names (attributes.tool_name, e.g. 'Bash'). A real filter,
20848
20890
  // where the free-text `q` can only match the rendered "via Bash" label.
20849
20891
  tool: external_exports.array(external_exports.string()).optional(),
20892
+ // The delivery state of each finding's event (see FindingDelivery).
20893
+ deployment: external_exports.array(FindingDeliveryState).optional(),
20850
20894
  // Exact repository / file-path matches, for the drill-down out of the
20851
20895
  // locations view. A row whose event carries no repo/file matches neither.
20852
20896
  repo: external_exports.string().optional(),
@@ -20867,6 +20911,10 @@ var ListFindingInstancesResponse = external_exports.object({
20867
20911
  items: external_exports.array(FindingInstanceDetail),
20868
20912
  nextCursor: external_exports.string().nullable()
20869
20913
  }).meta({ id: "ListFindingInstancesResponse" });
20914
+ var ListFindingInstancesPage = external_exports.object({
20915
+ items: external_exports.array(FindingInstanceDetail),
20916
+ nextCursor: external_exports.string().nullable()
20917
+ }).meta({ id: "ListFindingInstancesPage" });
20870
20918
  var FindingLocationSummary = external_exports.object({
20871
20919
  // Opaque, stable, minted from the pair by encodeLocationId. It exists
20872
20920
  // because a location's identity is two values and a URL param carries one:
@@ -20908,6 +20956,8 @@ var ListFindingLocationsQuery = external_exports.object({
20908
20956
  // instances that match, and folds its status from those.
20909
20957
  status: external_exports.array(FindingStatus).optional(),
20910
20958
  tool: external_exports.array(external_exports.string()).optional(),
20959
+ // The delivery state of each finding's event (see FindingDelivery).
20960
+ deployment: external_exports.array(FindingDeliveryState).optional(),
20911
20961
  q: external_exports.string().optional(),
20912
20962
  sessionId: external_exports.string().optional(),
20913
20963
  from: external_exports.iso.datetime().optional(),
@@ -20965,18 +21015,41 @@ var AuditEventType = external_exports.enum([
20965
21015
  // 'tool_call' is the reconciler's structural row for every call, while
20966
21016
  // 'tool_use' exists only where a hook enforced against the arguments.
20967
21017
  "tool_use",
20968
- // One row per model REFUSAL: a switch onto a prohibited model that was
20969
- // denied, or a turn refused because the session was already running on one.
20970
- // A structural row like the ones above rather than a capture — it carries
20971
- // the model that was refused and nothing the user typed, because what is
20972
- // worth recording about a governance decision is the decision, and prompt
20973
- // text is the thing this product exists to keep from travelling.
21018
+ // One row per model REFUSAL, across all four seams a prohibited model can be
21019
+ // stopped at: a switch onto it, a turn already running on it, a subagent
21020
+ // spawn asking for it, or a request-path refusal an embedded request-path
21021
+ // SDK makes in-process before the call leaves the application. Which seam
21022
+ // rides `attributes.refusal_seam`, never this member name. A structural row
21023
+ // like the ones above rather than a capture — it carries the model that was
21024
+ // refused and nothing the user typed, because what is worth recording about
21025
+ // a governance decision is the decision, and prompt text is the thing this
21026
+ // product exists to keep from travelling.
20974
21027
  "model_refusal",
21028
+ // One row per request-path DECISION: a policy check an embedded request-path
21029
+ // SDK performs in-process before a model call leaves the application, or
21030
+ // against that call's non-streamed response. A structural row like
21031
+ // 'model_refusal' rather than a capture — content-free in the same way:
21032
+ // which side, which seam, what action and which field are decided rides
21033
+ // `attributes`, never this member name, and the matched text itself never
21034
+ // travels.
21035
+ //
21036
+ // A prohibited-model refusal on the request path is deliberately NOT this
21037
+ // member: it stays 'model_refusal' with `refusal_seam: 'request'`, so it
21038
+ // shares one bucket with the plugin's switch/turn/spawn refusals rather
21039
+ // than splitting one governance concept across two event types. This
21040
+ // member carries every OTHER request-path decision.
21041
+ "request_decision",
20975
21042
  // One row per config-inventory scan, hung off the session root. It is the
20976
21043
  // fact the posture inspection findings reference (findings require an
20977
21044
  // audit_event_id), and its started_at is the "scanned Nm ago" the read
20978
21045
  // surface renders.
20979
- "config_scan"
21046
+ "config_scan",
21047
+ // One row per reported browser-extension capture status, hung off the web
21048
+ // session root. The durable home of what one tab's network interception
21049
+ // is doing — a write-through of the native host's in-memory tracker, so a
21050
+ // second process (aka extension status) and a restarted host both have
21051
+ // somewhere to read it back from.
21052
+ "capture_status"
20980
21053
  ]).meta({ id: "AuditEventType" });
20981
21054
  var AttributeBag = external_exports.record(external_exports.string(), external_exports.unknown());
20982
21055
  var HostAttributes = external_exports.object({
@@ -21110,6 +21183,10 @@ var CaptureAttributes = external_exports.object({
21110
21183
  // to 'allow' — the enforcement audit trail's link back to the grant that
21111
21184
  // authorized the bypass.
21112
21185
  exception_ids: external_exports.array(external_exports.guid()).optional(),
21186
+ // The persisted spellings of EventMetadata's messageId/conversationId — the
21187
+ // join back to the `llm_call` leaf for the same assistant turn.
21188
+ message_id: external_exports.string().optional(),
21189
+ conversation_id: external_exports.string().optional(),
21113
21190
  // Whole milliseconds this capture's inspection blocked its caller — the
21114
21191
  // plugin's own added latency (see EventMetadata.inspectionMs, whose value
21115
21192
  // this is). Promoted to the `inspection_ms` generated column so the facet is
@@ -21118,7 +21195,33 @@ var CaptureAttributes = external_exports.object({
21118
21195
  // inline json_extract and is not itself an optimization.
21119
21196
  // ABSENT on replayed captures (backfill / worktree scan) and on rows written
21120
21197
  // before the measurement shipped — never present as a placeholder 0.
21121
- inspection_ms: external_exports.number().int().nonnegative().optional()
21198
+ inspection_ms: external_exports.number().int().nonnegative().optional(),
21199
+ // What a `redact` this capture could not carry out became instead (see
21200
+ // EventMetadata.redactDegradedTo, whose value this is). Present only when a
21201
+ // degrade actually happened, so absence is the ordinary case rather than a
21202
+ // reader having to distinguish it from a zero.
21203
+ //
21204
+ // PER CAPTURE, while `inspection_findings.action_taken` is per finding —
21205
+ // so on a multi-finding row this does not say which finding degraded, and
21206
+ // its presence does not mean the fallback decided the capture's action. A
21207
+ // capture denied by another finding's own Block policy carries `block`
21208
+ // here too. The full statement is on EventMetadata.redactDegradedTo; it is
21209
+ // repeated rather than referenced because a store reader opens this file.
21210
+ redact_degraded_to: ActionTaken.optional()
21211
+ }).catchall(external_exports.unknown());
21212
+ var CaptureStatusAttributes = external_exports.object({
21213
+ source_tool: external_exports.string().optional(),
21214
+ patched: external_exports.boolean().optional(),
21215
+ live: external_exports.boolean().optional(),
21216
+ blind: external_exports.boolean().optional(),
21217
+ sends_seen_dom: external_exports.number().int().nonnegative().optional(),
21218
+ exchanges_seen_net: external_exports.number().int().nonnegative().optional(),
21219
+ parse_failures: external_exports.number().int().nonnegative().optional(),
21220
+ unparsed_bodies: external_exports.number().int().nonnegative().optional(),
21221
+ shape_misses: external_exports.array(external_exports.string()).optional(),
21222
+ conversation_endpoints: external_exports.number().int().nonnegative().optional(),
21223
+ closed: external_exports.boolean().optional(),
21224
+ enforcement: external_exports.string().optional()
21122
21225
  }).catchall(external_exports.unknown());
21123
21226
  var ToolCallInspection = external_exports.object({
21124
21227
  ruleId: external_exports.string().min(1),
@@ -21314,7 +21417,17 @@ var AuditEvent = external_exports.object({
21314
21417
  /** `share` to a first-party/internal destination. */
21315
21418
  internal: external_exports.boolean(),
21316
21419
  /** Event needs review (e.g. unverified egress). */
21317
- flagged: external_exports.boolean()
21420
+ flagged: external_exports.boolean(),
21421
+ /**
21422
+ * The body this event's `title` is drawn from was cleared by local body
21423
+ * expiry, so an EMPTY title here means "gone", not "never had one".
21424
+ *
21425
+ * A separate flag rather than a sentinel written into `title`: the title is
21426
+ * rendered text, and a store-layer module that invented display copy for it
21427
+ * would be choosing words the view is supposed to choose. Additive and
21428
+ * defaulted, so an older producer still validates.
21429
+ */
21430
+ bodyExpired: external_exports.boolean().default(false)
21318
21431
  }).meta({ id: "ActivityAuditEvent" });
21319
21432
  var ActivitySessionSummary = external_exports.object({
21320
21433
  id: external_exports.string(),
@@ -22119,6 +22232,11 @@ var RemoteFailureKind = external_exports.enum([
22119
22232
  "rejected",
22120
22233
  "unreachable"
22121
22234
  ]);
22235
+ var ControlPlaneFailure = RemoteFailureKind.extract([
22236
+ "unauthorized",
22237
+ "forbidden",
22238
+ "unreachable"
22239
+ ]);
22122
22240
  var AttachDeviceRequest = external_exports.object({
22123
22241
  // This machine's own continuity id, so re-attaching ROTATES the credential
22124
22242
  // on one machine record instead of producing a second one. Client-minted
@@ -22537,6 +22655,17 @@ var EventMetadata = external_exports.object({
22537
22655
  // to 'allow' — the enforcement audit trail's link back to the grant that
22538
22656
  // authorized the bypass. Absent on captures where no exception applied.
22539
22657
  exceptionIds: external_exports.array(external_exports.guid()).optional(),
22658
+ // The assistant message this capture belongs to, and the conversation it sits
22659
+ // in — set by the browser extension's network capture so a stored `response`
22660
+ // row can be joined to the `llm_call` leaf describing the same turn. Absent
22661
+ // on every other capture path, which has no such id.
22662
+ //
22663
+ // Non-empty for the reason WebExchange.messageId is: it is the join key, and
22664
+ // a blank one matches no `llm_call` leaf. That refusal reaches only the
22665
+ // places an event is PARSED; the local write path types the event and parses
22666
+ // nothing, which is why `toCaptureAttributes` omits a blank one separately.
22667
+ messageId: external_exports.string().min(1).optional(),
22668
+ conversationId: external_exports.string().optional(),
22540
22669
  // How long THIS capture's inspection blocked its caller, in whole
22541
22670
  // milliseconds — the plugin's own added latency, NOT the LLM call it sat in
22542
22671
  // front of. Measured inside `capture()` (@akasecurity/plugin-sdk) across
@@ -22549,7 +22678,37 @@ var EventMetadata = external_exports.object({
22549
22678
  // Absent is also what every pre-measurement client writes, and what a
22550
22679
  // clock failure degrades to — a reader must treat absence as "not measured"
22551
22680
  // and never as a zero, which would read as "inspection is free".
22552
- inspectionMs: external_exports.number().int().nonnegative().optional()
22681
+ inspectionMs: external_exports.number().int().nonnegative().optional(),
22682
+ // What a `redact` this capture COULD NOT CARRY OUT became instead — the
22683
+ // workspace's `redactFallback`, applied because the field could not be
22684
+ // masked in place (a shell command, a URL, or any argument on a host whose
22685
+ // hook contract offers no rewrite channel).
22686
+ //
22687
+ // It exists because the action alone cannot say why. A finding recorded as
22688
+ // `warn` reads identically whether its detection was ASSIGNED Warn or was
22689
+ // assigned Redact on a field that could not take one — and those are
22690
+ // different facts about the same row: the first is a policy the user chose,
22691
+ // the second is a masking the host could not perform. Absent means no
22692
+ // degrade happened, which is every ordinary capture.
22693
+ //
22694
+ // TWO LIMITS a reader of a stored row has to know, because the grain here
22695
+ // is the CAPTURE while `actionTaken` is per FINDING:
22696
+ //
22697
+ // - It does not say WHICH finding degraded. A capture carrying a degraded
22698
+ // `redact` alongside a finding ASSIGNED the same action stores both
22699
+ // identically and one reason for the pair; attributing it to both
22700
+ // describes the assigned one wrongly, and to neither loses the degrade.
22701
+ // - PRESENCE IS NOT CAUSATION. The value is the action the lost redact
22702
+ // became, not the reason the capture ended as it did — a capture denied
22703
+ // by some other finding's own Block policy still carries `block` here,
22704
+ // and clearing the workspace's fallback would not have let it through.
22705
+ // Gate on the value against what a fallback can produce; never read the
22706
+ // field's presence as "this was the fallback's doing".
22707
+ //
22708
+ // Both are pinned as behaviour in @akasecurity/plugin-sdk's runtime suite.
22709
+ // Closing either means moving the reason onto the finding row, which
22710
+ // already carries its own action.
22711
+ redactDegradedTo: ActionTaken.optional()
22553
22712
  }).meta({ id: "EventMetadata" });
22554
22713
  var Event = external_exports.object({
22555
22714
  id: external_exports.guid(),
@@ -22659,6 +22818,15 @@ var RotateKeyInput = external_exports.object({
22659
22818
  confirmation: external_exports.string()
22660
22819
  });
22661
22820
 
22821
+ // ../../packages/schema/src/zod/finding-delivery.ts
22822
+ var KNOWN_REASONS = SyncFailureReason.options;
22823
+
22824
+ // ../../packages/schema/src/zod/findings-group-build.ts
22825
+ function rankByOrder(members2) {
22826
+ return Object.fromEntries(members2.map((member, index) => [member, index]));
22827
+ }
22828
+ var SEVERITY_RANK = rankByOrder(Severity.options);
22829
+
22662
22830
  // ../../packages/schema/src/zod/installed-pack.ts
22663
22831
  var InstalledPack = external_exports.object({
22664
22832
  id: external_exports.guid(),
@@ -22725,6 +22893,11 @@ var Policy = external_exports.object({
22725
22893
  // test `prohibitedModels` passes and `reversibleRuleIds` fails.
22726
22894
  provenance: PolicyProvenance.optional()
22727
22895
  }).meta({ id: "Policy" });
22896
+ var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
22897
+ var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
22898
+ var RedactFallback = BuiltinPolicyId.extract(["monitor", "warn", "block"]).meta({
22899
+ id: "RedactFallback"
22900
+ });
22728
22901
  var PolicyBundle = external_exports.object({
22729
22902
  version: external_exports.string(),
22730
22903
  policies: external_exports.array(Policy),
@@ -22772,6 +22945,16 @@ var PolicyBundle = external_exports.object({
22772
22945
  // control plane), so no name resolution stands between the decision and the
22773
22946
  // comparison.
22774
22947
  prohibitedModels: external_exports.array(external_exports.string()).optional(),
22948
+ // What a resolved `redact` becomes on a field the host cannot rewrite, as
22949
+ // the ORGANIZATION would have it. Merged raise-only against the device's own
22950
+ // `WorkspaceSettings.redactFallback` (see strongerRedactFallback below), so
22951
+ // a control plane can tighten a machine and never loosen one — the same
22952
+ // direction `mergeRaiseOnly` enforces for policies.
22953
+ //
22954
+ // Optional so an older backend, and an older on-disk cache, still parses;
22955
+ // absent leaves the device's own setting in force, which is the behaviour
22956
+ // that predates the field and the safe direction to default.
22957
+ redactFallback: RedactFallback.optional(),
22775
22958
  customKeywords: external_exports.array(external_exports.string()),
22776
22959
  fetchedAt: external_exports.iso.datetime()
22777
22960
  }).meta({ id: "PolicyBundle" });
@@ -22801,11 +22984,6 @@ function severityFloorPolicy(category) {
22801
22984
  const peak = CATEGORY_PEAK_SEVERITY[category];
22802
22985
  return peak === "critical" || peak === "high" ? "warn" : "monitor";
22803
22986
  }
22804
- var KNOWN_BUILTIN_IDS = ["monitor", "warn", "redact", "vault", "block"];
22805
- var BuiltinPolicyId = external_exports.enum(KNOWN_BUILTIN_IDS).meta({ id: "BuiltinPolicyId" });
22806
- var RedactFallback = BuiltinPolicyId.extract(["monitor", "warn", "block"]).meta({
22807
- id: "RedactFallback"
22808
- });
22809
22987
  var BUILTIN_POLICY_SPECS = {
22810
22988
  monitor: {
22811
22989
  name: "Monitor",
@@ -23067,7 +23245,7 @@ var VaultConsent = external_exports.object({
23067
23245
  });
23068
23246
 
23069
23247
  // ../../packages/schema/src/zod/local.ts
23070
- var WORKSPACE_SETTINGS_SPEC_VERSION = 7;
23248
+ var WORKSPACE_SETTINGS_SPEC_VERSION = 8;
23071
23249
  var RunMode = external_exports.enum(["standalone", "attached"]);
23072
23250
  var ControlPlaneConnection = external_exports.object({
23073
23251
  endpoint: external_exports.string().min(1),
@@ -23086,6 +23264,27 @@ var HistorySyncConsent = external_exports.object({
23086
23264
  payloadVersion: external_exports.number().int().positive(),
23087
23265
  endpoint: external_exports.string()
23088
23266
  });
23267
+ var WebChatCaptureConsent = external_exports.object({
23268
+ acknowledgedAt: external_exports.iso.datetime(),
23269
+ version: external_exports.number().int().positive()
23270
+ });
23271
+ var WebChatResponseCapture = external_exports.enum(["with-findings", "always", "never"]);
23272
+ var WebChatCapture = external_exports.object({
23273
+ responses: WebChatResponseCapture.default("with-findings"),
23274
+ account: external_exports.boolean().default(false),
23275
+ // Absent until granted. Presence alone does not authorize anything — see
23276
+ // isWebChatCaptureConsentValid.
23277
+ consent: WebChatCaptureConsent.optional()
23278
+ });
23279
+ var BODY_RETENTION_DEFAULT_DAYS = 30;
23280
+ var BodyRetention = external_exports.object({
23281
+ enabled: external_exports.boolean().default(false),
23282
+ // Never 0, and the ceiling is a fat-finger guard rather than a policy
23283
+ // limit — `enabled` is the real gate. A low value cannot reach a row the
23284
+ // sync ledger still owes: the sweep's age filter only ever NARROWS a
23285
+ // candidate set that is already bounded by "delivered, or never owed".
23286
+ retainDays: external_exports.number().int().min(1).max(3650).default(BODY_RETENTION_DEFAULT_DAYS)
23287
+ }).meta({ id: "BodyRetention" });
23089
23288
  var WorkspaceSettings = external_exports.object({
23090
23289
  specVersion: external_exports.number().int().positive().default(WORKSPACE_SETTINGS_SPEC_VERSION),
23091
23290
  runMode: RunMode.default("standalone"),
@@ -23134,7 +23333,23 @@ var WorkspaceSettings = external_exports.object({
23134
23333
  // carry prompt/reply/tool-output text in `content`; the key name predates
23135
23334
  // both widenings. Absent until granted, and a grant for a different endpoint
23136
23335
  // or an older payload no longer counts.
23137
- historySyncConsent: HistorySyncConsent.optional()
23336
+ historySyncConsent: HistorySyncConsent.optional(),
23337
+ // What the browser extension may record from a web chat, and the grant that
23338
+ // authorizes it. Absent until the user answers: recording something that was
23339
+ // never recorded before is never an assumed grant on upgrade, so the whole
23340
+ // block is optional rather than defaulted in. What an absent block means is
23341
+ // webChatCaptureOf's answer, in one place.
23342
+ //
23343
+ // Enforcement is NOT gated on this. A machine that has never answered still
23344
+ // blocks, redacts and warns on what a user sends; the grant covers what is
23345
+ // written down.
23346
+ webChatCapture: WebChatCapture.optional(),
23347
+ // Local body expiry (see BodyRetention). Off until switched on; expiring a
23348
+ // body never removes the row or its findings.
23349
+ bodyRetention: BodyRetention.default({
23350
+ enabled: false,
23351
+ retainDays: BODY_RETENTION_DEFAULT_DAYS
23352
+ })
23138
23353
  });
23139
23354
 
23140
23355
  // ../../packages/schema/src/zod/managed.ts
@@ -23147,13 +23362,22 @@ var ManagedSettingKey = external_exports.enum([
23147
23362
  "vaultInlineReveal",
23148
23363
  "modelJudgeConsent",
23149
23364
  "dataSharesInPlace",
23150
- "redactFallback"
23365
+ "redactFallback",
23366
+ // Pins the toggle and the day count together — see BodyRetention on why the
23367
+ // two are one unit. An administrator mandating a window wants the count
23368
+ // enforced with it, not one a user can widen while the toggle stays on.
23369
+ "bodyRetention"
23151
23370
  ]).meta({ id: "ManagedSettingKey" });
23152
23371
  function isManagedSettingKey(value) {
23153
23372
  return ManagedSettingKey.safeParse(value).success;
23154
23373
  }
23155
23374
  var ManagedSettingsValues = external_exports.object({
23156
23375
  runMode: external_exports.enum(["standalone", "attached"]).optional(),
23376
+ // `controlPlane` and `bodyRetention` are the two nested values, and both are
23377
+ // plain, non-strict objects: a key under either that this build does not know
23378
+ // is stripped and nothing reports it. The unknown-value split in
23379
+ // ManagedSettings below classifies top-level names only, so it stops at
23380
+ // these boundaries.
23157
23381
  controlPlane: external_exports.object({
23158
23382
  endpoint: external_exports.string().min(1),
23159
23383
  label: external_exports.string().min(1).optional()
@@ -23164,7 +23388,8 @@ var ManagedSettingsValues = external_exports.object({
23164
23388
  vaultInlineReveal: external_exports.enum(["masked", "full", "off"]).optional(),
23165
23389
  modelJudgeConsent: external_exports.boolean().optional(),
23166
23390
  dataSharesInPlace: external_exports.boolean().optional(),
23167
- redactFallback: RedactFallback.optional()
23391
+ redactFallback: RedactFallback.optional(),
23392
+ bodyRetention: BodyRetention.optional()
23168
23393
  }).meta({ id: "ManagedSettingsValues" });
23169
23394
  var ManagedSettings = external_exports.object({
23170
23395
  specVersion: external_exports.number().int().positive().default(MANAGED_SETTINGS_SPEC_VERSION),
@@ -23172,7 +23397,21 @@ var ManagedSettings = external_exports.object({
23172
23397
  // decision from a bug. Absent renders as a generic "your organization".
23173
23398
  organization: external_exports.string().min(1).optional(),
23174
23399
  // What the administrator pinned.
23175
- values: ManagedSettingsValues.default({}),
23400
+ //
23401
+ // Parsed as a RECORD rather than as the nested schema, and split below for
23402
+ // the same reason `lockedFields` is parsed as names: a plain `z.object`
23403
+ // drops an unrecognised key and succeeds, so a pin this build does not know
23404
+ // vanished and nothing anywhere said so. A pin with no lock is a supported
23405
+ // shape — it is a DEFAULT the user may still change — so that silence hit
23406
+ // exactly the file an administrator is most likely to write while a fleet
23407
+ // is mid-upgrade.
23408
+ //
23409
+ // Splitting here rather than calling `.strict()`: strict would REFUSE the
23410
+ // file, which is the outcome the lock half already rejected — an older
23411
+ // build then runs entirely unmanaged, every pin and lock gone. A bad KNOWN
23412
+ // value still fails, because the nested schema is re-run over the known
23413
+ // subset and its issues are re-raised on this parse.
23414
+ values: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
23176
23415
  // Which of those the user may not change. A key here with no matching value
23177
23416
  // freezes whatever the user last chose; a value with no lock is a DEFAULT
23178
23417
  // the user may still override. The two are separable on purpose.
@@ -23185,17 +23424,31 @@ var ManagedSettings = external_exports.object({
23185
23424
  // the fleets most likely to carry a version skew. A name outside the enum
23186
23425
  // is still never HONOURED: the lockable set stays explicit above.
23187
23426
  lockedFields: external_exports.array(external_exports.string()).default([])
23188
- }).transform(({ lockedFields, ...rest }) => {
23427
+ }).transform(({ lockedFields, values, ...rest }, ctx) => {
23189
23428
  const known = [];
23190
23429
  const unknown2 = [];
23191
23430
  for (const name of lockedFields) {
23192
23431
  if (isManagedSettingKey(name)) known.push(name);
23193
23432
  else unknown2.push(name);
23194
23433
  }
23434
+ const knownValues = /* @__PURE__ */ Object.create(null);
23435
+ const unknownValues = [];
23436
+ for (const [name, value] of Object.entries(values)) {
23437
+ if (Object.hasOwn(ManagedSettingsValues.shape, name)) knownValues[name] = value;
23438
+ else unknownValues.push(name);
23439
+ }
23440
+ const pinned = ManagedSettingsValues.safeParse(knownValues);
23441
+ if (!pinned.success) {
23442
+ for (const issue2 of pinned.error.issues)
23443
+ ctx.addIssue({ ...issue2, path: ["values", ...issue2.path] });
23444
+ return external_exports.NEVER;
23445
+ }
23195
23446
  return {
23196
23447
  ...rest,
23448
+ values: pinned.data,
23197
23449
  lockedFields: known,
23198
- ...unknown2.length > 0 ? { unknownLockedFields: unknown2 } : {}
23450
+ ...unknown2.length > 0 ? { unknownLockedFields: unknown2 } : {},
23451
+ ...unknownValues.length > 0 ? { unknownValueFields: unknownValues } : {}
23199
23452
  };
23200
23453
  }).meta({ id: "ManagedSettings" });
23201
23454
 
@@ -23448,12 +23701,30 @@ var RecommendedActionIdParam = external_exports.object({ id: external_exports.st
23448
23701
  // ../../packages/schema/src/zod/settings-action.ts
23449
23702
  var HistorySyncConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "HistorySyncConsentChoice" });
23450
23703
  var ModelJudgeConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "ModelJudgeConsentChoice" });
23704
+ var WebChatCaptureConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "WebChatCaptureConsentChoice" });
23451
23705
  var SaveSettingsInput = external_exports.object({
23452
23706
  historicalAccess: external_exports.string(),
23453
23707
  modelJudgeConsent: ModelJudgeConsentChoice,
23454
23708
  historySyncConsent: HistorySyncConsentChoice,
23455
23709
  vaultConsent: external_exports.string(),
23456
- vaultInlineReveal: external_exports.string()
23710
+ vaultInlineReveal: external_exports.string(),
23711
+ webChatCaptureConsent: WebChatCaptureConsentChoice,
23712
+ // Widened to `string` like its neighbours rather than typed as
23713
+ // `RedactFallback`, on this module's own layering rule: shape here, VALUE at
23714
+ // the call site, so the domain check receives the type it was written for.
23715
+ //
23716
+ // NOT because a narrower schema would reject differently. `parseActionInput`
23717
+ // is a `safeParse` wrapper and throws for no field schema, so either spelling
23718
+ // reaches a recoverable `{ ok: false }` and there is no rejected promise to
23719
+ // trade against. The real cost runs the other way and is the part worth
23720
+ // knowing: a value this schema admits and the domain enum then rejects lands
23721
+ // on the action's shared refusal, which names NO field, where a shape
23722
+ // rejection reaches `malformedInput` and names the schema key.
23723
+ redactFallback: external_exports.string(),
23724
+ // Shape only, the way the enum fields above are strings only: the RANGE is
23725
+ // `BodyRetention`'s and the action checks it there, so there is one place
23726
+ // that decides what a legal horizon is rather than two that can drift.
23727
+ bodyRetention: external_exports.object({ enabled: external_exports.boolean(), retainDays: external_exports.number() })
23457
23728
  });
23458
23729
  var AttachInput = external_exports.object({
23459
23730
  endpoint: external_exports.string(),
@@ -23579,6 +23850,96 @@ var SetupHandoffOffer = external_exports.object({
23579
23850
  path: ["liveKeys"]
23580
23851
  });
23581
23852
 
23853
+ // ../../packages/schema/src/zod/web-capture.ts
23854
+ var WebUsageSource = external_exports.enum(["site", "estimated", "none"]);
23855
+ var WebUsage = external_exports.object({
23856
+ inputTokens: external_exports.number().int().nonnegative().optional(),
23857
+ outputTokens: external_exports.number().int().nonnegative().optional(),
23858
+ cacheReadInputTokens: external_exports.number().int().nonnegative().optional(),
23859
+ cacheCreationInputTokens: external_exports.number().int().nonnegative().optional()
23860
+ });
23861
+ var WebToolCall = external_exports.object({
23862
+ toolUseId: external_exports.string().min(1),
23863
+ toolName: external_exports.string().min(1),
23864
+ target: external_exports.string().optional(),
23865
+ isError: external_exports.boolean().optional(),
23866
+ inputSize: external_exports.number().int().nonnegative().optional(),
23867
+ outputSize: external_exports.number().int().nonnegative().optional()
23868
+ });
23869
+ var WebExchange = external_exports.object({
23870
+ messageId: external_exports.string().min(1),
23871
+ startedAt: external_exports.iso.datetime(),
23872
+ model: external_exports.string().optional(),
23873
+ usage: WebUsage.optional(),
23874
+ usageSource: WebUsageSource,
23875
+ stopReason: external_exports.string().optional(),
23876
+ conversationId: external_exports.string().optional(),
23877
+ turnIndex: external_exports.number().int().nonnegative().optional(),
23878
+ toolCalls: external_exports.array(WebToolCall).default([]),
23879
+ // Absent when the adapter recovered no text. Capped by the caller at
23880
+ // RESPONSE_TEXT_MAX_BYTES, so a short capture is never mistaken for a short
23881
+ // reply.
23882
+ responseText: external_exports.string().optional(),
23883
+ // The stored text is short of the reply. It does NOT say which of the two
23884
+ // ceilings on this path cut it: the caller applies its own cap on the raw
23885
+ // bytes it reads off the wire, which can be reached by a stream whose
23886
+ // recovered text stays well under RESPONSE_TEXT_MAX_BYTES, and applies that
23887
+ // one to the text. A reader cannot tell them apart, and nothing downstream
23888
+ // should branch as though it could.
23889
+ truncated: external_exports.boolean().default(false)
23890
+ });
23891
+ var WebEnforcementState = external_exports.enum([
23892
+ "watching",
23893
+ "composer-only",
23894
+ "button-only",
23895
+ "unattached",
23896
+ "unknown"
23897
+ ]);
23898
+ var RESPONSE_TEXT_MAX_BYTES = 2 * 1024 * 1024;
23899
+ var WebCaptureStatus = external_exports.object({
23900
+ patched: external_exports.boolean(),
23901
+ live: external_exports.boolean(),
23902
+ blind: external_exports.boolean(),
23903
+ sendsSeenDom: external_exports.number().int().nonnegative(),
23904
+ exchangesSeenNet: external_exports.number().int().nonnegative(),
23905
+ parseFailures: external_exports.number().int().nonnegative(),
23906
+ unparsedBodies: external_exports.number().int().nonnegative(),
23907
+ // The adapter-declared JSON key paths that were absent from a real payload —
23908
+ // the earliest signal that a site's contract moved.
23909
+ shapeMisses: external_exports.array(external_exports.string()).default([]),
23910
+ // How many `kind: 'conversation'` endpoints the reporting tab's adapter
23911
+ // compiled. Zero means this build declares none for the site, so observing
23912
+ // nothing is the design rather than a fault — the one fact that separates a
23913
+ // site nobody has surveyed yet from one whose contract moved. Defaulted so a
23914
+ // build predating the field is read as declaring nothing rather than refused.
23915
+ conversationEndpoints: external_exports.number().int().nonnegative().default(0),
23916
+ // The document that sent this report is going away. The bridge sets it on
23917
+ // its `pagehide` report and nowhere else.
23918
+ //
23919
+ // A property of the REPORT rather than of capture health, which is why
23920
+ // nothing in `deriveWebCaptureState` reads it and why it stays out of the
23921
+ // bridge's own report signature — a closing tab's last word must not be
23922
+ // suppressed for carrying the same health as the report before it. What
23923
+ // reads it is the per-site fold: a document that said it was unloading stops
23924
+ // voting on the site's state, so the reload the `blind` remediation asks for
23925
+ // can actually clear the verdict it was shown. A document that dies without
23926
+ // sending one is covered by CAPTURE_STATUS_DOCUMENT_QUIET_MS instead.
23927
+ //
23928
+ // Defaulted so a build predating the field reads as a document that never
23929
+ // said it was closing — which keeps it voting, the same as every report that
23930
+ // is not a final one.
23931
+ closed: external_exports.boolean().default(false),
23932
+ // What the DOM enforcement path is doing, which none of the counters above
23933
+ // can say: `sendsSeenDom` rises only once a send has COMPLETED, so a tab
23934
+ // whose watcher never bound reports zero exactly like a tab nobody typed in.
23935
+ // Defaulted to 'unknown' rather than 'watching' so a status from a build
23936
+ // predating the field is not read as reporting a healthy one.
23937
+ enforcement: WebEnforcementState.default("unknown")
23938
+ });
23939
+ var CAPTURE_STATUS_RECENCY_MS = 30 * 24 * 60 * 60 * 1e3;
23940
+ var CAPTURE_STATUS_RECENCY_DAYS = CAPTURE_STATUS_RECENCY_MS / (24 * 60 * 60 * 1e3);
23941
+ var CAPTURE_STATUS_DOCUMENT_QUIET_MS = 12 * 60 * 60 * 1e3;
23942
+
23582
23943
  // ../../packages/detections/src/posture/config-posture.ts
23583
23944
  var RULE_VERSION = "1";
23584
23945
  var KNOWN_TOOLS = /* @__PURE__ */ new Set([
@@ -23636,6 +23997,56 @@ var CONFIG_POSTURE_RULES = [
23636
23997
  }
23637
23998
  ];
23638
23999
 
24000
+ // ../../packages/detections/src/posture/web-capture-posture.ts
24001
+ var RULE_VERSION2 = "1";
24002
+ var DRIFT_MIN_PARSE_FAILURES = 2;
24003
+ var WEB_CAPTURE_DRIFT_STATES = /* @__PURE__ */ new Set([
24004
+ "blind",
24005
+ "degraded"
24006
+ ]);
24007
+ var WEB_CAPTURE_DRIFT_RULE = {
24008
+ ruleId: "web-capture-drift",
24009
+ version: RULE_VERSION2,
24010
+ name: "Web chat capture is not reading the site",
24011
+ category: "config",
24012
+ severity: "medium",
24013
+ definition: JSON.stringify({
24014
+ kind: "web-capture-drift",
24015
+ states: [...WEB_CAPTURE_DRIFT_STATES],
24016
+ minParseFailures: DRIFT_MIN_PARSE_FAILURES
24017
+ })
24018
+ };
24019
+ var STATIC_COPY = {
24020
+ active: { headline: "turns are being observed on this site" },
24021
+ unreported: {
24022
+ // Says "recently" rather than "yet": the store read is bounded to
24023
+ // CAPTURE_STATUS_RECENCY_MS, so this state covers a site nothing has ever
24024
+ // reported for AND one whose last report has aged out. The two are the
24025
+ // same fact to a reader — nobody has confirmed anything lately — and the
24026
+ // copy may not claim the stronger of them.
24027
+ headline: `no report in the last ${String(CAPTURE_STATUS_RECENCY_DAYS)} days \u2014 open the site in Chrome with the extension loaded`
24028
+ },
24029
+ standby: {
24030
+ headline: "this build declares no endpoints for the site, so nothing is observed yet"
24031
+ },
24032
+ unpatched: {
24033
+ // Says what the flags say and no more. `patched` is false both for a tap
24034
+ // that installed and hooked neither transport and for one that never ran
24035
+ // at all — a page reports the same status either way, so the copy may not
24036
+ // assert one of them.
24037
+ headline: "the page tap captured neither fetch nor XHR \u2014 it may not have installed; reload the extension at chrome://extensions"
24038
+ },
24039
+ idle: { headline: "watching; no turn has been observed yet" },
24040
+ blind: {
24041
+ headline: "messages were sent in the page that the network capture never saw",
24042
+ remediation: "reload the tab. If it persists after `aka update` and reloading the extension at chrome://extensions, the site's send path has changed and needs a new extension build."
24043
+ },
24044
+ degraded: {
24045
+ headline: "the site's payloads no longer carry the fields the extension reads",
24046
+ remediation: "run `aka update`, then reload the extension at chrome://extensions. If it stays degraded after an update, the site's contract has changed and needs a new extension build."
24047
+ }
24048
+ };
24049
+
23639
24050
  // ../../packages/detections/src/security/redos-probe.ts
23640
24051
  var BUDGET_MS = 100;
23641
24052
  var EXPONENTIAL_UNITS = [