@akasecurity/ai-tc-claude-code 0.9.0 → 0.9.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aka",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "AI Traffic Control — inspect and govern AI prompts in Claude Code. Detection runs locally; events are recorded to a local SQLite store on your machine.",
5
5
  "homepage": "https://github.com/akasecurity/ai-tc",
6
6
  "author": { "name": "AKA Security" }
package/README.md CHANGED
@@ -7,7 +7,9 @@
7
7
 
8
8
  The Claude Code plugin for **[AI Traffic Control](https://github.com/akasecurity/ai-tc)** (`ai-tc`). It hooks into a Claude Code session and inspects its traffic — prompts, tool calls, tool results, file reads — scanning each event against your rule packs and applying inline **warn / redact / block** policies. Every event is recorded to a local SQLite store at `~/.aka/data/aka.db`.
9
9
 
10
- Detection runs entirely on your machine. There's no account and no backend — nothing leaves your computer to be scanned.
10
+ Detection runs entirely on your machine. There's no account and no backend — nothing is sent anywhere to be scanned.[^egress]
11
+
12
+ [^egress]: Live detection and enforcement run locally. The one exception is the **opt-in** `/aka:setup` calibration below: to rate what an initial history scan finds, its judge step sends those findings to the model API through the `claude` CLI — the same provider your Claude session already uses. For each finding that means the raw (unmasked) value including any secret, plus roughly 120 characters of the surrounding transcript text on either side (any other secrets detected in that window are masked). It runs only after you explicitly opt in, and it keeps those values out of your local Claude transcript — but a copy of them does leave the machine. Withdrawing the grant later stops future scans; it cannot recall what was already sent.
11
13
 
12
14
  ## Install
13
15
 
package/commands/setup.md CHANGED
@@ -15,10 +15,18 @@ dashboard. Everything the user sees is derived from their _actual_ history — n
15
15
  a fabricated or demo number. When there isn't enough history to judge, the wizard
16
16
  falls back to a conservative severity-derived floor instead of guessing.
17
17
 
18
- The false-positive/severity judgment itself runs in a **separate, transient
19
- subprocess that writes no transcript** the raw (unmasked) finding values are
20
- never read into this conversation or your scannable history. You act only on the
21
- raw-free plan that subprocess prints back.
18
+ The false-positive/severity judgment needs the raw (unmasked) findings to rate
19
+ them accurately, so it **sends them to the model API** through separate `claude`
20
+ CLI subprocesses (a large history is judged in several batches). Two things
21
+ cross for each finding: its **raw value**, and about **120 characters of the
22
+ surrounding transcript text** on either side of it (any other secrets detected in
23
+ that window are masked). Those subprocesses ask the `claude` CLI to write
24
+ no transcript, so the raw values do not enter this conversation or your scannable
25
+ history — but a copy of them **does leave the machine**, sent to the model
26
+ provider like any other Claude prompt. You act only on the raw-free plan the
27
+ subprocesses print back. The scan that produces those values is offered for
28
+ explicit consent in step 1, and that consent must state the model-API egress
29
+ plainly before it is given.
22
30
 
23
31
  Follow the steps below **in order**. Nothing is written to the policy store
24
32
  until step 5 (or a floor fallback in step 3 if the calibration can't complete).
@@ -63,8 +71,8 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/intro.js" "${CLAUDE_PLUGIN_ROOT}/.claude-plu
63
71
  Before showing any recommended posture — the start-light default table in
64
72
  step 2 or the calibrated posture in step 4 — look at the **current project's**
65
73
  working tree yourself, with your own Read/Glob tools. There is no script for
66
- this: it is your own reasoning over facts you read directly, not the isolated
67
- triage subprocess's raw-free plan, and it needs no user interaction.
74
+ this: it is your own reasoning over facts you read directly, not the triage
75
+ subprocess's raw-free plan, and it needs no user interaction.
68
76
 
69
77
  **In scope:** the manifest's declared frameworks/dependencies
70
78
  (`package.json` or equivalent), payment or other third-party API SDKs among
@@ -101,7 +109,7 @@ posture the user reads, not a separate write. Persisting a tightened level happe
101
109
  only where the wizard already writes a per-category override — the adjust fork's
102
110
  `onboard.js --posture` write (step 4b), where the user picks each category's level
103
111
  explicitly. The keep-defaults path writes the severity floor (`--floor`, step 2)
104
- and the calibrated accept path applies the isolated subprocess's saved plan
112
+ and the calibrated accept path applies the triage subprocess's saved plan
105
113
  verbatim (`--confirmed --plan`, step 5); neither carries the tightening on its
106
114
  own, so a tightened level the user wants persisted is chosen through the adjust
107
115
  fork. Do **not** bolt on an extra `onboard.js --posture` overlay to auto-re-persist
@@ -124,18 +132,36 @@ interactive picker. The plugin can't draw its own selectable UI (it can't
124
132
  capture keystrokes), so do **not** print a fake option list or ask the user to
125
133
  "reply with a number"; let the picker collect the answer.
126
134
 
127
- **Want me to look over what Claude's been up to?**"I'll review Claude's recent work — transcripts, temp files, agent memory — to tune what I bring to you next."
135
+ **Disclose the model-API egress plainly before you show the pickerthis is what
136
+ the user is consenting to, so it must be visible before they choose.** State it in
137
+ your own words, without softening it: if they say yes, AKA scans the last 30 days
138
+ of Claude history, and to rate what it finds it **sends the raw, unmasked values —
139
+ including any secrets — to the model API through the `claude` CLI**. Name what
140
+ travels with each one: **its raw value, and about 120 characters of the
141
+ surrounding transcript text on either side** (any other secrets detected in that
142
+ window are masked). That is real network egress to the model provider (the same one your
143
+ Claude session already uses), not a purely local review. **A copy of each value
144
+ leaves the machine.** The transcripts those values came from stay on disk
145
+ untouched — nothing here removes them; that is the separate redaction step in
146
+ step 6. The values are kept out of your local Claude transcript, and the findings
147
+ are also recorded, masked, to the local store. The grant is revocable from the
148
+ dashboard's **Settings → Historical access**, which stops future scans — it
149
+ cannot recall anything already sent. Do not present the picker until you have
150
+ said this.
151
+
152
+ **Want me to look over what Claude's been up to?** — "I'll scan Claude's recent work — transcripts, temp files, agent memory — and send what I find to the model to rate it, so I can tune what I bring you next."
128
153
 
129
154
  Offer exactly two options:
130
155
 
131
- - **Yes, take a look** — "tune what I bring you, based on Claude's real work here"
156
+ - **Yes, take a look** — "scan my real work here; raw findings go to the model to be rated, then tune what you bring me"
132
157
  - **Not now** — "start light and I'll learn as we go"
133
158
 
134
159
  Choosing **Yes, take a look** records the same historical-review consent the wizard has
135
- always recorded — the identical scope, the one-time grant, and the
136
- revocable-under-Policies semantics — so the simpler question broadens nothing
137
- about what AKA may access. Those granular scope and revocation details stay
138
- inspectable on request and in the dashboard.
160
+ always recorded — the identical scope (which includes the model-API judgment disclosed
161
+ above), the one-time grant, and the same revocation semantics — so the simpler question
162
+ broadens nothing about what AKA may access. Those granular scope and revocation details
163
+ stay inspectable on request and in the dashboard, under **Settings → Historical access**,
164
+ whose own copy repeats the model-API disclosure.
139
165
 
140
166
  ## 2. Save the answer, then branch
141
167
 
@@ -221,10 +247,62 @@ it.
221
247
  step 6 already follows when no calibration frame was emitted). Step 7
222
248
  then runs as written.
223
249
 
224
- ## 3. Run the evidence triage — isolated judgment, nothing written yet
250
+ ## 3. Run the evidence triage — off-transcript judgment, nothing written yet
225
251
 
226
- Pipe the backfill's triage stream straight into the `apply-suppressions`
227
- adapter in **PREVIEW** mode (no `--confirmed`):
252
+ **Model-judge consent a distinct opt-in, asked here before the pipe.** The
253
+ false-positive/severity judgment runs by sending each finding to the Anthropic
254
+ model API through `claude`. That is a separate egress from the historical-read
255
+ consent collected in step 1 (which only let AKA _read_ the local transcripts), so
256
+ it needs its own explicit grant. Before running the pipe, state plainly what
257
+ leaves the machine, then use **AskUserQuestion** — the built-in picker (never a
258
+ printed numbered list) — to collect the answer.
259
+
260
+ Say plainly, before the picker: to sort real leaks from routine noise, AKA sends
261
+ each finding's `rawMatch` (the raw detected value) plus the surrounding context
262
+ window to the model API via `claude`. The **`filePath` is not sent**, and any
263
+ secrets detected in that context window are **masked** before it goes — so the
264
+ finding's own value is the only raw value that leaves. Nothing else about the
265
+ finding or the file crosses. Note that masking covers the secrets AKA's rules
266
+ detect; ordinary text in that window travels as-is.
267
+
268
+ **Send findings to the model to sort real leaks from noise?** — "I'll send each
269
+ detected value, plus a bit of surrounding context with any secrets in it masked,
270
+ to the model to tell real leaks from routine noise. The file path stays local."
271
+
272
+ - **Yes, send them** — "let the model triage what I found"
273
+ - **No, keep it local** — "skip the model triage and start from the safe defaults"
274
+
275
+ Present both options flat — this grant sends the user's data off the machine, so
276
+ do **not** mark either one recommended.
277
+
278
+ **Branch on the choice:**
279
+
280
+ - **If the user chose "Yes, send them"** — record the model-judge consent, then
281
+ run the pipe below:
282
+
283
+ ```bash
284
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --model-judge-consent
285
+ ```
286
+
287
+ - **If the user chose "No, keep it local"** — do **not** run the pipe. The judge
288
+ refuses to run without consent (it would only print a clean skip line), so there
289
+ is no calibrated plan to confirm. Fall back to the conservative severity floor,
290
+ tell the user the model triage was skipped, and continue to step 6:
291
+
292
+ ```bash
293
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --floor
294
+ ```
295
+
296
+ Say plainly: the model triage was declined, so AKA is starting from the
297
+ conservative severity floor instead of a calibrated posture, and it can be
298
+ re-run any time with `/aka:setup`. Declining here only skips this run — it does
299
+ **not** withdraw a grant made earlier; a stored grant is revoked under
300
+ **Settings → Model-judge consent** in the dashboard. **Skip steps 4 and 5** —
301
+ there is no plan to confirm — and rejoin the spine at step 6 with **no
302
+ `--surfaced`** (the same floor-fallback rule step 6 already follows).
303
+
304
+ On the **Yes, send them** path, pipe the backfill's triage stream straight into
305
+ the `apply-suppressions` adapter in **PREVIEW** mode (no `--confirmed`):
228
306
 
229
307
  ```bash
230
308
  node "${CLAUDE_PLUGIN_ROOT}/scripts/backfill.js" --triage | node "${CLAUDE_PLUGIN_ROOT}/scripts/apply-suppressions.js"
@@ -233,8 +311,11 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/backfill.js" --triage | node "${CLAUDE_PLUGI
233
311
  The backfill sweeps prior Claude Code transcripts (last 30 days, all projects)
234
312
  and streams one masked-plus-raw triage hit per line; masked findings are
235
313
  recorded to the local store as a side effect. The adapter runs the
236
- false-positive/severity **judgment in a separate transient subprocess** (no
237
- transcript), then prints back a **raw-free plan** you can safely show the user:
314
+ false-positive/severity **judgment in separate `claude` subprocesses that send the
315
+ raw hits each one's value and its surrounding transcript text (other secrets in
316
+ that window masked) — to the model API** (a large history is split into several batches; each asks
317
+ the CLI to write no local transcript), then prints back a **raw-free plan** you
318
+ can safely show the user:
238
319
  the calibrated-result card (the real-count headline and the recommended posture),
239
320
  the per-category reasoning, the masked false positives it would suppress, any
240
321
  categories it skipped, and its notes.
@@ -269,7 +350,8 @@ names its pattern and count from — never invent either off-signal.
269
350
 
270
351
  Everything you show the user in step 4 comes from **this command's output**. You
271
352
  never read the raw finding values yourself — do not echo, quote, or reconstruct
272
- them; by design they stay inside the isolated subprocess.
353
+ them; the judge subprocesses send them to the model API and return only the
354
+ raw-free plan, so they never enter this conversation.
273
355
 
274
356
  **Failed or truncated triage — never proceed silently (fallback).** If this
275
357
  command exits non-zero, or the adapter reports a truncated / sentinel-less
@@ -685,23 +767,24 @@ interface + terminal dashboard and on-demand scans."
685
767
  - **Not now** — "skip — you can add it anytime with the one-liner below"
686
768
 
687
769
  **Yes, add it** is the install authorization — run the bootstrap installer
688
- directly, with no second picker (it ensures Node is available and installs the
689
- global CLI from the public npm registry). Run the line for their OS:
770
+ directly, with no second picker (it downloads the self-contained `aka` binary for
771
+ their platform no Node.js or npm required — and links it onto PATH). Run the
772
+ line for their OS:
690
773
 
691
774
  ```bash
692
775
  # macOS / Linux
693
- curl -fsSL https://raw.githubusercontent.com/akasecurity/ai-tc/cli-latest/tools/installer/install.sh | sh
776
+ curl -fsSL https://raw.githubusercontent.com/akasecurity/ai-tc/bin-latest/tools/installer/install.sh | sh
694
777
 
695
778
  # Windows (PowerShell)
696
- irm https://raw.githubusercontent.com/akasecurity/ai-tc/cli-latest/tools/installer/install.ps1 | iex
779
+ irm https://raw.githubusercontent.com/akasecurity/ai-tc/bin-latest/tools/installer/install.ps1 | iex
697
780
  ```
698
781
 
699
- The one-liner pins to the stable release **tag** (`cli-latest`), never `main` —
700
- each release points that tag at its published `cli-v*` version, and the bootstrap
701
- scripts it fetches hold the installer's pinned ref + checksum, so fetching them
702
- from a mutable branch would defeat the integrity gate. To pin an exact version
703
- instead, set `AKA_INSTALL_REF=cli-v<version>` before running the line. Until the
704
- first release is published the one-liner 404s (fail-closed).
782
+ The one-liner pins to the latest published binary release **tag** (`bin-latest`),
783
+ never `main` — each binary release (`bin-v*`) moves that tag to its commit, and
784
+ the installer verifies the downloaded binary against the release's `SHA256SUMS`
785
+ (fail-closed), so a corrupted or tampered download is refused. To pin an exact
786
+ version instead, set `AKA_INSTALL_REF=bin-v<version>` before running the line. If
787
+ no `bin-v*` release exists yet the one-liner fails closed rather than guessing.
705
788
 
706
789
  After it completes, point them at `aka init` then `aka dashboard`. If they chose
707
790
  **Not now**, show the one-liner once so they can add it later, and move on —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akasecurity/ai-tc-claude-code",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "AI Traffic Control — inspect and govern AI prompts in Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -28,11 +28,11 @@
28
28
  "vitest": "^4.1.10",
29
29
  "zod": "^4.0.0",
30
30
  "@akasecurity/eslint-config": "0.8.0",
31
- "@akasecurity/plugin-sdk": "0.8.0",
32
31
  "@akasecurity/persistence": "0.8.0",
33
- "@akasecurity/scanner": "0.8.0",
32
+ "@akasecurity/plugin-runtime": "0.8.0",
33
+ "@akasecurity/plugin-sdk": "0.8.0",
34
34
  "@akasecurity/schema": "0.8.0",
35
- "@akasecurity/plugin-runtime": "0.8.0"
35
+ "@akasecurity/scanner": "0.8.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup",