@akasecurity/ai-tc-claude-code 0.9.12 → 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.
@@ -20223,7 +20223,17 @@ var UNPRICEABLE_PROVIDERS = Object.freeze([
20223
20223
  "gateway",
20224
20224
  "unknown",
20225
20225
  "cli",
20226
- "api"
20226
+ "api",
20227
+ // The browser extension's native host records these as `llm_call.provider`
20228
+ // for a web-chat turn — the web tool id, deliberately never the vendor id
20229
+ // (`openai`/`anthropic`) the session root carries. Subscription traffic
20230
+ // burns rate-limit budget, not dollar credits, and listing them here is
20231
+ // what keeps that true structurally: a later maintainer who wants to price
20232
+ // web-chat traffic at API rates has to delete this entry first, and meet
20233
+ // the reason on the way, rather than quietly adding one to
20234
+ // PROVIDER_PLATFORM.
20235
+ "chatgpt",
20236
+ "claude-ai"
20227
20237
  ]);
20228
20238
 
20229
20239
  // ../../packages/schema/src/token/format.ts
@@ -20243,7 +20253,12 @@ var HARNESS = {
20243
20253
  ClaudeDesktop: "claudedesktop",
20244
20254
  ChatGpt: "chatgpt",
20245
20255
  ClaudeAi: "claudeai",
20246
- Api: "api"
20256
+ Api: "api",
20257
+ // Not a coding assistant a person drives — an in-process SDK embedded in an
20258
+ // application, so it has no IDE/CLI/desktop/web surface of its own. Carries
20259
+ // the same id as its SOURCE_TOOL counterpart, unlike every capture-side tool
20260
+ // whose wire spelling differs from its display spelling.
20261
+ AiTcSdk: "ai-tc-sdk"
20247
20262
  };
20248
20263
  var Harness = external_exports.enum(HARNESS).meta({ id: "Harness" });
20249
20264
  var SOURCE_TOOL = {
@@ -20259,9 +20274,15 @@ var SOURCE_TOOL = {
20259
20274
  // whose tool could not be identified both render through the read side's
20260
20275
  // miss path rather than as a harness of their own.
20261
20276
  Cli: "cli",
20262
- Unknown: "unknown"
20277
+ Unknown: "unknown",
20278
+ // The wire id an in-process, request-path SDK stamps on its own structural
20279
+ // rows (`request_decision`) — never a capture of prompt/response/tool text,
20280
+ // since the SDK sits in front of a model call rather than inside a coding
20281
+ // assistant's own hook contract.
20282
+ AiTcSdk: "ai-tc-sdk"
20263
20283
  };
20264
20284
  var SourceTool = external_exports.enum(SOURCE_TOOL).meta({ id: "SourceTool" });
20285
+ var WebSourceTool = SourceTool.extract(["ChatGpt", "ClaudeAi"]);
20265
20286
  var TOOL_TO_HARNESS = {
20266
20287
  [SOURCE_TOOL.ClaudeCode]: HARNESS.ClaudeCode,
20267
20288
  [SOURCE_TOOL.ClaudeDesktop]: HARNESS.ClaudeDesktop,
@@ -20270,7 +20291,12 @@ var TOOL_TO_HARNESS = {
20270
20291
  [SOURCE_TOOL.ChatGpt]: HARNESS.ChatGpt,
20271
20292
  [SOURCE_TOOL.Codex]: HARNESS.Codex,
20272
20293
  [SOURCE_TOOL.Antigravity]: HARNESS.Antigravity,
20273
- [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi
20294
+ [SOURCE_TOOL.ClaudeAi]: HARNESS.ClaudeAi,
20295
+ // Wire and display id are the same string here, but the row still belongs:
20296
+ // both vocabularies carry the `AiTcSdk` member, and the join is exactly
20297
+ // their intersection — leaving a shared member out would read as an
20298
+ // uninstrumented tool on both surfaces, which this one is not.
20299
+ [SOURCE_TOOL.AiTcSdk]: HARNESS.AiTcSdk
20274
20300
  };
20275
20301
 
20276
20302
  // ../../packages/schema/src/zod/finding.ts
@@ -20304,7 +20330,8 @@ var FindingProvider = Harness.extract([
20304
20330
  "ClaudeAi",
20305
20331
  "Codex",
20306
20332
  "Antigravity",
20307
- "Api"
20333
+ "Api",
20334
+ "AiTcSdk"
20308
20335
  ]).meta({ id: "FindingProvider" });
20309
20336
  var FindingCategory = external_exports.enum([
20310
20337
  "secret",
@@ -20678,18 +20705,41 @@ var AuditEventType = external_exports.enum([
20678
20705
  // 'tool_call' is the reconciler's structural row for every call, while
20679
20706
  // 'tool_use' exists only where a hook enforced against the arguments.
20680
20707
  "tool_use",
20681
- // One row per model REFUSAL: a switch onto a prohibited model that was
20682
- // denied, or a turn refused because the session was already running on one.
20683
- // A structural row like the ones above rather than a capture — it carries
20684
- // the model that was refused and nothing the user typed, because what is
20685
- // worth recording about a governance decision is the decision, and prompt
20686
- // text is the thing this product exists to keep from travelling.
20708
+ // One row per model REFUSAL, across all four seams a prohibited model can be
20709
+ // stopped at: a switch onto it, a turn already running on it, a subagent
20710
+ // spawn asking for it, or a request-path refusal an embedded request-path
20711
+ // SDK makes in-process before the call leaves the application. Which seam
20712
+ // rides `attributes.refusal_seam`, never this member name. A structural row
20713
+ // like the ones above rather than a capture — it carries the model that was
20714
+ // refused and nothing the user typed, because what is worth recording about
20715
+ // a governance decision is the decision, and prompt text is the thing this
20716
+ // product exists to keep from travelling.
20687
20717
  "model_refusal",
20718
+ // One row per request-path DECISION: a policy check an embedded request-path
20719
+ // SDK performs in-process before a model call leaves the application, or
20720
+ // against that call's non-streamed response. A structural row like
20721
+ // 'model_refusal' rather than a capture — content-free in the same way:
20722
+ // which side, which seam, what action and which field are decided rides
20723
+ // `attributes`, never this member name, and the matched text itself never
20724
+ // travels.
20725
+ //
20726
+ // A prohibited-model refusal on the request path is deliberately NOT this
20727
+ // member: it stays 'model_refusal' with `refusal_seam: 'request'`, so it
20728
+ // shares one bucket with the plugin's switch/turn/spawn refusals rather
20729
+ // than splitting one governance concept across two event types. This
20730
+ // member carries every OTHER request-path decision.
20731
+ "request_decision",
20688
20732
  // One row per config-inventory scan, hung off the session root. It is the
20689
20733
  // fact the posture inspection findings reference (findings require an
20690
20734
  // audit_event_id), and its started_at is the "scanned Nm ago" the read
20691
20735
  // surface renders.
20692
- "config_scan"
20736
+ "config_scan",
20737
+ // One row per reported browser-extension capture status, hung off the web
20738
+ // session root. The durable home of what one tab's network interception
20739
+ // is doing — a write-through of the native host's in-memory tracker, so a
20740
+ // second process (aka extension status) and a restarted host both have
20741
+ // somewhere to read it back from.
20742
+ "capture_status"
20693
20743
  ]).meta({ id: "AuditEventType" });
20694
20744
  var AttributeBag = external_exports.record(external_exports.string(), external_exports.unknown());
20695
20745
  var HostAttributes = external_exports.object({
@@ -20849,6 +20899,20 @@ var CaptureAttributes = external_exports.object({
20849
20899
  // repeated rather than referenced because a store reader opens this file.
20850
20900
  redact_degraded_to: ActionTaken.optional()
20851
20901
  }).catchall(external_exports.unknown());
20902
+ var CaptureStatusAttributes = external_exports.object({
20903
+ source_tool: external_exports.string().optional(),
20904
+ patched: external_exports.boolean().optional(),
20905
+ live: external_exports.boolean().optional(),
20906
+ blind: external_exports.boolean().optional(),
20907
+ sends_seen_dom: external_exports.number().int().nonnegative().optional(),
20908
+ exchanges_seen_net: external_exports.number().int().nonnegative().optional(),
20909
+ parse_failures: external_exports.number().int().nonnegative().optional(),
20910
+ unparsed_bodies: external_exports.number().int().nonnegative().optional(),
20911
+ shape_misses: external_exports.array(external_exports.string()).optional(),
20912
+ conversation_endpoints: external_exports.number().int().nonnegative().optional(),
20913
+ closed: external_exports.boolean().optional(),
20914
+ enforcement: external_exports.string().optional()
20915
+ }).catchall(external_exports.unknown());
20852
20916
  var ToolCallInspection = external_exports.object({
20853
20917
  ruleId: external_exports.string().min(1),
20854
20918
  ruleName: external_exports.string(),
@@ -21858,6 +21922,11 @@ var RemoteFailureKind = external_exports.enum([
21858
21922
  "rejected",
21859
21923
  "unreachable"
21860
21924
  ]);
21925
+ var ControlPlaneFailure = RemoteFailureKind.extract([
21926
+ "unauthorized",
21927
+ "forbidden",
21928
+ "unreachable"
21929
+ ]);
21861
21930
  var AttachDeviceRequest = external_exports.object({
21862
21931
  // This machine's own continuity id, so re-attaching ROTATES the credential
21863
21932
  // on one machine record instead of producing a second one. Client-minted
@@ -22280,7 +22349,12 @@ var EventMetadata = external_exports.object({
22280
22349
  // in — set by the browser extension's network capture so a stored `response`
22281
22350
  // row can be joined to the `llm_call` leaf describing the same turn. Absent
22282
22351
  // on every other capture path, which has no such id.
22283
- messageId: external_exports.string().optional(),
22352
+ //
22353
+ // Non-empty for the reason WebExchange.messageId is: it is the join key, and
22354
+ // a blank one matches no `llm_call` leaf. That refusal reaches only the
22355
+ // places an event is PARSED; the local write path types the event and parses
22356
+ // nothing, which is why `toCaptureAttributes` omits a blank one separately.
22357
+ messageId: external_exports.string().min(1).optional(),
22284
22358
  conversationId: external_exports.string().optional(),
22285
22359
  // How long THIS capture's inspection blocked its caller, in whole
22286
22360
  // milliseconds — the plugin's own added latency, NOT the LLM call it sat in
@@ -22885,6 +22959,18 @@ var HistorySyncConsent = external_exports.object({
22885
22959
  payloadVersion: external_exports.number().int().positive(),
22886
22960
  endpoint: external_exports.string()
22887
22961
  });
22962
+ var WebChatCaptureConsent = external_exports.object({
22963
+ acknowledgedAt: external_exports.iso.datetime(),
22964
+ version: external_exports.number().int().positive()
22965
+ });
22966
+ var WebChatResponseCapture = external_exports.enum(["with-findings", "always", "never"]);
22967
+ var WebChatCapture = external_exports.object({
22968
+ responses: WebChatResponseCapture.default("with-findings"),
22969
+ account: external_exports.boolean().default(false),
22970
+ // Absent until granted. Presence alone does not authorize anything — see
22971
+ // isWebChatCaptureConsentValid.
22972
+ consent: WebChatCaptureConsent.optional()
22973
+ });
22888
22974
  var BODY_RETENTION_DEFAULT_DAYS = 30;
22889
22975
  var BodyRetention = external_exports.object({
22890
22976
  enabled: external_exports.boolean().default(false),
@@ -22943,6 +23029,16 @@ var WorkspaceSettings = external_exports.object({
22943
23029
  // both widenings. Absent until granted, and a grant for a different endpoint
22944
23030
  // or an older payload no longer counts.
22945
23031
  historySyncConsent: HistorySyncConsent.optional(),
23032
+ // What the browser extension may record from a web chat, and the grant that
23033
+ // authorizes it. Absent until the user answers: recording something that was
23034
+ // never recorded before is never an assumed grant on upgrade, so the whole
23035
+ // block is optional rather than defaulted in. What an absent block means is
23036
+ // webChatCaptureOf's answer, in one place.
23037
+ //
23038
+ // Enforcement is NOT gated on this. A machine that has never answered still
23039
+ // blocks, redacts and warns on what a user sends; the grant covers what is
23040
+ // written down.
23041
+ webChatCapture: WebChatCapture.optional(),
22946
23042
  // Local body expiry (see BodyRetention). Off until switched on; expiring a
22947
23043
  // body never removes the row or its findings.
22948
23044
  bodyRetention: BodyRetention.default({
@@ -23300,12 +23396,14 @@ var RecommendedActionIdParam = external_exports.object({ id: external_exports.st
23300
23396
  // ../../packages/schema/src/zod/settings-action.ts
23301
23397
  var HistorySyncConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "HistorySyncConsentChoice" });
23302
23398
  var ModelJudgeConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "ModelJudgeConsentChoice" });
23399
+ var WebChatCaptureConsentChoice = external_exports.enum(["granted", "revoked", "unchanged"]).meta({ id: "WebChatCaptureConsentChoice" });
23303
23400
  var SaveSettingsInput = external_exports.object({
23304
23401
  historicalAccess: external_exports.string(),
23305
23402
  modelJudgeConsent: ModelJudgeConsentChoice,
23306
23403
  historySyncConsent: HistorySyncConsentChoice,
23307
23404
  vaultConsent: external_exports.string(),
23308
23405
  vaultInlineReveal: external_exports.string(),
23406
+ webChatCaptureConsent: WebChatCaptureConsentChoice,
23309
23407
  // Widened to `string` like its neighbours rather than typed as
23310
23408
  // `RedactFallback`, on this module's own layering rule: shape here, VALUE at
23311
23409
  // the call site, so the domain check receives the type it was written for.
@@ -23474,11 +23572,24 @@ var WebExchange = external_exports.object({
23474
23572
  turnIndex: external_exports.number().int().nonnegative().optional(),
23475
23573
  toolCalls: external_exports.array(WebToolCall).default([]),
23476
23574
  // Absent when the adapter recovered no text. Capped by the caller at
23477
- // RESPONSE_TEXT_MAX_BYTES; `truncated` records that the cap was reached, so a
23478
- // short capture is never mistaken for a short reply.
23575
+ // RESPONSE_TEXT_MAX_BYTES, so a short capture is never mistaken for a short
23576
+ // reply.
23479
23577
  responseText: external_exports.string().optional(),
23578
+ // The stored text is short of the reply. It does NOT say which of the two
23579
+ // ceilings on this path cut it: the caller applies its own cap on the raw
23580
+ // bytes it reads off the wire, which can be reached by a stream whose
23581
+ // recovered text stays well under RESPONSE_TEXT_MAX_BYTES, and applies that
23582
+ // one to the text. A reader cannot tell them apart, and nothing downstream
23583
+ // should branch as though it could.
23480
23584
  truncated: external_exports.boolean().default(false)
23481
23585
  });
23586
+ var WebEnforcementState = external_exports.enum([
23587
+ "watching",
23588
+ "composer-only",
23589
+ "button-only",
23590
+ "unattached",
23591
+ "unknown"
23592
+ ]);
23482
23593
  var RESPONSE_TEXT_MAX_BYTES = 2 * 1024 * 1024;
23483
23594
  var WebCaptureStatus = external_exports.object({
23484
23595
  patched: external_exports.boolean(),
@@ -23490,8 +23601,39 @@ var WebCaptureStatus = external_exports.object({
23490
23601
  unparsedBodies: external_exports.number().int().nonnegative(),
23491
23602
  // The adapter-declared JSON key paths that were absent from a real payload —
23492
23603
  // the earliest signal that a site's contract moved.
23493
- shapeMisses: external_exports.array(external_exports.string()).default([])
23494
- });
23604
+ shapeMisses: external_exports.array(external_exports.string()).default([]),
23605
+ // How many `kind: 'conversation'` endpoints the reporting tab's adapter
23606
+ // compiled. Zero means this build declares none for the site, so observing
23607
+ // nothing is the design rather than a fault — the one fact that separates a
23608
+ // site nobody has surveyed yet from one whose contract moved. Defaulted so a
23609
+ // build predating the field is read as declaring nothing rather than refused.
23610
+ conversationEndpoints: external_exports.number().int().nonnegative().default(0),
23611
+ // The document that sent this report is going away. The bridge sets it on
23612
+ // its `pagehide` report and nowhere else.
23613
+ //
23614
+ // A property of the REPORT rather than of capture health, which is why
23615
+ // nothing in `deriveWebCaptureState` reads it and why it stays out of the
23616
+ // bridge's own report signature — a closing tab's last word must not be
23617
+ // suppressed for carrying the same health as the report before it. What
23618
+ // reads it is the per-site fold: a document that said it was unloading stops
23619
+ // voting on the site's state, so the reload the `blind` remediation asks for
23620
+ // can actually clear the verdict it was shown. A document that dies without
23621
+ // sending one is covered by CAPTURE_STATUS_DOCUMENT_QUIET_MS instead.
23622
+ //
23623
+ // Defaulted so a build predating the field reads as a document that never
23624
+ // said it was closing — which keeps it voting, the same as every report that
23625
+ // is not a final one.
23626
+ closed: external_exports.boolean().default(false),
23627
+ // What the DOM enforcement path is doing, which none of the counters above
23628
+ // can say: `sendsSeenDom` rises only once a send has COMPLETED, so a tab
23629
+ // whose watcher never bound reports zero exactly like a tab nobody typed in.
23630
+ // Defaulted to 'unknown' rather than 'watching' so a status from a build
23631
+ // predating the field is not read as reporting a healthy one.
23632
+ enforcement: WebEnforcementState.default("unknown")
23633
+ });
23634
+ var CAPTURE_STATUS_RECENCY_MS = 30 * 24 * 60 * 60 * 1e3;
23635
+ var CAPTURE_STATUS_RECENCY_DAYS = CAPTURE_STATUS_RECENCY_MS / (24 * 60 * 60 * 1e3);
23636
+ var CAPTURE_STATUS_DOCUMENT_QUIET_MS = 12 * 60 * 60 * 1e3;
23495
23637
 
23496
23638
  // ../../packages/persistence/src/paths.ts
23497
23639
  import {
@@ -24495,6 +24637,56 @@ var CONFIG_POSTURE_RULES = [
24495
24637
  }
24496
24638
  ];
24497
24639
 
24640
+ // ../../packages/detections/src/posture/web-capture-posture.ts
24641
+ var RULE_VERSION2 = "1";
24642
+ var DRIFT_MIN_PARSE_FAILURES = 2;
24643
+ var WEB_CAPTURE_DRIFT_STATES = /* @__PURE__ */ new Set([
24644
+ "blind",
24645
+ "degraded"
24646
+ ]);
24647
+ var WEB_CAPTURE_DRIFT_RULE = {
24648
+ ruleId: "web-capture-drift",
24649
+ version: RULE_VERSION2,
24650
+ name: "Web chat capture is not reading the site",
24651
+ category: "config",
24652
+ severity: "medium",
24653
+ definition: JSON.stringify({
24654
+ kind: "web-capture-drift",
24655
+ states: [...WEB_CAPTURE_DRIFT_STATES],
24656
+ minParseFailures: DRIFT_MIN_PARSE_FAILURES
24657
+ })
24658
+ };
24659
+ var STATIC_COPY = {
24660
+ active: { headline: "turns are being observed on this site" },
24661
+ unreported: {
24662
+ // Says "recently" rather than "yet": the store read is bounded to
24663
+ // CAPTURE_STATUS_RECENCY_MS, so this state covers a site nothing has ever
24664
+ // reported for AND one whose last report has aged out. The two are the
24665
+ // same fact to a reader — nobody has confirmed anything lately — and the
24666
+ // copy may not claim the stronger of them.
24667
+ headline: `no report in the last ${String(CAPTURE_STATUS_RECENCY_DAYS)} days \u2014 open the site in Chrome with the extension loaded`
24668
+ },
24669
+ standby: {
24670
+ headline: "this build declares no endpoints for the site, so nothing is observed yet"
24671
+ },
24672
+ unpatched: {
24673
+ // Says what the flags say and no more. `patched` is false both for a tap
24674
+ // that installed and hooked neither transport and for one that never ran
24675
+ // at all — a page reports the same status either way, so the copy may not
24676
+ // assert one of them.
24677
+ headline: "the page tap captured neither fetch nor XHR \u2014 it may not have installed; reload the extension at chrome://extensions"
24678
+ },
24679
+ idle: { headline: "watching; no turn has been observed yet" },
24680
+ blind: {
24681
+ headline: "messages were sent in the page that the network capture never saw",
24682
+ 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."
24683
+ },
24684
+ degraded: {
24685
+ headline: "the site's payloads no longer carry the fields the extension reads",
24686
+ 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."
24687
+ }
24688
+ };
24689
+
24498
24690
  // ../../packages/detections/src/security/redos-probe.ts
24499
24691
  var BUDGET_MS = 100;
24500
24692
  var EXPONENTIAL_UNITS = [