kuzushi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/agent-runtime/claude.d.ts +8 -0
  4. package/dist/agent-runtime/claude.js +124 -0
  5. package/dist/agent-runtime/claude.js.map +1 -0
  6. package/dist/agent-runtime/index.d.ts +9 -0
  7. package/dist/agent-runtime/index.js +31 -0
  8. package/dist/agent-runtime/index.js.map +1 -0
  9. package/dist/agent-runtime/model-spec.d.ts +8 -0
  10. package/dist/agent-runtime/model-spec.js +17 -0
  11. package/dist/agent-runtime/model-spec.js.map +1 -0
  12. package/dist/agent-runtime/pi-ai.d.ts +8 -0
  13. package/dist/agent-runtime/pi-ai.js +365 -0
  14. package/dist/agent-runtime/pi-ai.js.map +1 -0
  15. package/dist/agent-runtime/tools.d.ts +3 -0
  16. package/dist/agent-runtime/tools.js +330 -0
  17. package/dist/agent-runtime/tools.js.map +1 -0
  18. package/dist/agent-runtime/types.d.ts +72 -0
  19. package/dist/agent-runtime/types.js +2 -0
  20. package/dist/agent-runtime/types.js.map +1 -0
  21. package/dist/agents/index.d.ts +7 -0
  22. package/dist/agents/index.js +24 -0
  23. package/dist/agents/index.js.map +1 -0
  24. package/dist/agents/registry.d.ts +14 -0
  25. package/dist/agents/registry.js +97 -0
  26. package/dist/agents/registry.js.map +1 -0
  27. package/dist/agents/scanner-adapter.d.ts +5 -0
  28. package/dist/agents/scanner-adapter.js +18 -0
  29. package/dist/agents/scanner-adapter.js.map +1 -0
  30. package/dist/agents/tasks/augur-analyze.d.ts +22 -0
  31. package/dist/agents/tasks/augur-analyze.js +418 -0
  32. package/dist/agents/tasks/augur-analyze.js.map +1 -0
  33. package/dist/agents/tasks/augur-extraction-agent.d.ts +44 -0
  34. package/dist/agents/tasks/augur-extraction-agent.js +507 -0
  35. package/dist/agents/tasks/augur-extraction-agent.js.map +1 -0
  36. package/dist/agents/tasks/augur-label.d.ts +21 -0
  37. package/dist/agents/tasks/augur-label.js +627 -0
  38. package/dist/agents/tasks/augur-label.js.map +1 -0
  39. package/dist/agents/tasks/augur-preflight.d.ts +36 -0
  40. package/dist/agents/tasks/augur-preflight.js +471 -0
  41. package/dist/agents/tasks/augur-preflight.js.map +1 -0
  42. package/dist/agents/tasks/augur-types.d.ts +111 -0
  43. package/dist/agents/tasks/augur-types.js +169 -0
  44. package/dist/agents/tasks/augur-types.js.map +1 -0
  45. package/dist/agents/tasks/context-gatherer.d.ts +6 -0
  46. package/dist/agents/tasks/context-gatherer.js +320 -0
  47. package/dist/agents/tasks/context-gatherer.js.map +1 -0
  48. package/dist/agents/types.d.ts +28 -0
  49. package/dist/agents/types.js +2 -0
  50. package/dist/agents/types.js.map +1 -0
  51. package/dist/bus/adapters/google-pubsub.d.ts +13 -0
  52. package/dist/bus/adapters/google-pubsub.js +26 -0
  53. package/dist/bus/adapters/google-pubsub.js.map +1 -0
  54. package/dist/bus/adapters/in-process.d.ts +12 -0
  55. package/dist/bus/adapters/in-process.js +118 -0
  56. package/dist/bus/adapters/in-process.js.map +1 -0
  57. package/dist/bus/adapters/index.d.ts +6 -0
  58. package/dist/bus/adapters/index.js +22 -0
  59. package/dist/bus/adapters/index.js.map +1 -0
  60. package/dist/bus/adapters/nats.d.ts +13 -0
  61. package/dist/bus/adapters/nats.js +26 -0
  62. package/dist/bus/adapters/nats.js.map +1 -0
  63. package/dist/bus/adapters/redis.d.ts +13 -0
  64. package/dist/bus/adapters/redis.js +26 -0
  65. package/dist/bus/adapters/redis.js.map +1 -0
  66. package/dist/bus/events.d.ts +295 -0
  67. package/dist/bus/events.js +2 -0
  68. package/dist/bus/events.js.map +1 -0
  69. package/dist/bus/helpers.d.ts +7 -0
  70. package/dist/bus/helpers.js +16 -0
  71. package/dist/bus/helpers.js.map +1 -0
  72. package/dist/bus/index.d.ts +30 -0
  73. package/dist/bus/index.js +66 -0
  74. package/dist/bus/index.js.map +1 -0
  75. package/dist/bus/orchestrator.d.ts +51 -0
  76. package/dist/bus/orchestrator.js +1350 -0
  77. package/dist/bus/orchestrator.js.map +1 -0
  78. package/dist/bus/types.d.ts +23 -0
  79. package/dist/bus/types.js +2 -0
  80. package/dist/bus/types.js.map +1 -0
  81. package/dist/bus/workers/audit-worker.d.ts +9 -0
  82. package/dist/bus/workers/audit-worker.js +125 -0
  83. package/dist/bus/workers/audit-worker.js.map +1 -0
  84. package/dist/bus/workers/poc-harness-worker.d.ts +9 -0
  85. package/dist/bus/workers/poc-harness-worker.js +96 -0
  86. package/dist/bus/workers/poc-harness-worker.js.map +1 -0
  87. package/dist/bus/workers/report-worker.d.ts +11 -0
  88. package/dist/bus/workers/report-worker.js +235 -0
  89. package/dist/bus/workers/report-worker.js.map +1 -0
  90. package/dist/bus/workers/scan-worker.d.ts +13 -0
  91. package/dist/bus/workers/scan-worker.js +223 -0
  92. package/dist/bus/workers/scan-worker.js.map +1 -0
  93. package/dist/bus/workers/store-worker.d.ts +10 -0
  94. package/dist/bus/workers/store-worker.js +62 -0
  95. package/dist/bus/workers/store-worker.js.map +1 -0
  96. package/dist/bus/workers/triage-worker.d.ts +13 -0
  97. package/dist/bus/workers/triage-worker.js +129 -0
  98. package/dist/bus/workers/triage-worker.js.map +1 -0
  99. package/dist/bus/workers/verification-worker.d.ts +9 -0
  100. package/dist/bus/workers/verification-worker.js +91 -0
  101. package/dist/bus/workers/verification-worker.js.map +1 -0
  102. package/dist/cli.d.ts +2 -0
  103. package/dist/cli.js +513 -0
  104. package/dist/cli.js.map +1 -0
  105. package/dist/config.d.ts +17 -0
  106. package/dist/config.js +866 -0
  107. package/dist/config.js.map +1 -0
  108. package/dist/context.d.ts +5 -0
  109. package/dist/context.js +26 -0
  110. package/dist/context.js.map +1 -0
  111. package/dist/deps.d.ts +15 -0
  112. package/dist/deps.js +18 -0
  113. package/dist/deps.js.map +1 -0
  114. package/dist/llm/anthropic.d.ts +3 -0
  115. package/dist/llm/anthropic.js +21 -0
  116. package/dist/llm/anthropic.js.map +1 -0
  117. package/dist/poc-harness.d.ts +49 -0
  118. package/dist/poc-harness.js +420 -0
  119. package/dist/poc-harness.js.map +1 -0
  120. package/dist/report-markdown.d.ts +3 -0
  121. package/dist/report-markdown.js +197 -0
  122. package/dist/report-markdown.js.map +1 -0
  123. package/dist/report-sarif.d.ts +3 -0
  124. package/dist/report-sarif.js +253 -0
  125. package/dist/report-sarif.js.map +1 -0
  126. package/dist/report.d.ts +18 -0
  127. package/dist/report.js +146 -0
  128. package/dist/report.js.map +1 -0
  129. package/dist/retry.d.ts +6 -0
  130. package/dist/retry.js +25 -0
  131. package/dist/retry.js.map +1 -0
  132. package/dist/scanner/claude-adk.d.ts +26 -0
  133. package/dist/scanner/claude-adk.js +265 -0
  134. package/dist/scanner/claude-adk.js.map +1 -0
  135. package/dist/scanner/resolve.d.ts +10 -0
  136. package/dist/scanner/resolve.js +99 -0
  137. package/dist/scanner/resolve.js.map +1 -0
  138. package/dist/scanner.d.ts +47 -0
  139. package/dist/scanner.js +123 -0
  140. package/dist/scanner.js.map +1 -0
  141. package/dist/scanners/agentic.d.ts +11 -0
  142. package/dist/scanners/agentic.js +71 -0
  143. package/dist/scanners/agentic.js.map +1 -0
  144. package/dist/scanners/claude-adk.d.ts +11 -0
  145. package/dist/scanners/claude-adk.js +74 -0
  146. package/dist/scanners/claude-adk.js.map +1 -0
  147. package/dist/scanners/codeql.d.ts +15 -0
  148. package/dist/scanners/codeql.js +97 -0
  149. package/dist/scanners/codeql.js.map +1 -0
  150. package/dist/scanners/finding-selection.d.ts +7 -0
  151. package/dist/scanners/finding-selection.js +120 -0
  152. package/dist/scanners/finding-selection.js.map +1 -0
  153. package/dist/scanners/index.d.ts +4 -0
  154. package/dist/scanners/index.js +14 -0
  155. package/dist/scanners/index.js.map +1 -0
  156. package/dist/scanners/registry.d.ts +12 -0
  157. package/dist/scanners/registry.js +70 -0
  158. package/dist/scanners/registry.js.map +1 -0
  159. package/dist/scanners/resolve-codeql.d.ts +9 -0
  160. package/dist/scanners/resolve-codeql.js +38 -0
  161. package/dist/scanners/resolve-codeql.js.map +1 -0
  162. package/dist/scanners/resolve-semgrep.d.ts +10 -0
  163. package/dist/scanners/resolve-semgrep.js +96 -0
  164. package/dist/scanners/resolve-semgrep.js.map +1 -0
  165. package/dist/scanners/run-agentic.d.ts +33 -0
  166. package/dist/scanners/run-agentic.js +267 -0
  167. package/dist/scanners/run-agentic.js.map +1 -0
  168. package/dist/scanners/run-claude-adk.d.ts +33 -0
  169. package/dist/scanners/run-claude-adk.js +267 -0
  170. package/dist/scanners/run-claude-adk.js.map +1 -0
  171. package/dist/scanners/run-codeql.d.ts +100 -0
  172. package/dist/scanners/run-codeql.js +538 -0
  173. package/dist/scanners/run-codeql.js.map +1 -0
  174. package/dist/scanners/run-semgrep.d.ts +41 -0
  175. package/dist/scanners/run-semgrep.js +115 -0
  176. package/dist/scanners/run-semgrep.js.map +1 -0
  177. package/dist/scanners/scoring.d.ts +7 -0
  178. package/dist/scanners/scoring.js +14 -0
  179. package/dist/scanners/scoring.js.map +1 -0
  180. package/dist/scanners/semgrep.d.ts +11 -0
  181. package/dist/scanners/semgrep.js +64 -0
  182. package/dist/scanners/semgrep.js.map +1 -0
  183. package/dist/scanners/types.d.ts +36 -0
  184. package/dist/scanners/types.js +2 -0
  185. package/dist/scanners/types.js.map +1 -0
  186. package/dist/scanners.d.ts +25 -0
  187. package/dist/scanners.js +88 -0
  188. package/dist/scanners.js.map +1 -0
  189. package/dist/store.d.ts +106 -0
  190. package/dist/store.js +609 -0
  191. package/dist/store.js.map +1 -0
  192. package/dist/triage.d.ts +74 -0
  193. package/dist/triage.js +314 -0
  194. package/dist/triage.js.map +1 -0
  195. package/dist/types.d.ts +166 -0
  196. package/dist/types.js +2 -0
  197. package/dist/types.js.map +1 -0
  198. package/dist/utils.d.ts +6 -0
  199. package/dist/utils.js +19 -0
  200. package/dist/utils.js.map +1 -0
  201. package/dist/verify.d.ts +56 -0
  202. package/dist/verify.js +298 -0
  203. package/dist/verify.js.map +1 -0
  204. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shayaun Nejad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,410 @@
1
+ <img src="kuzushi.png" alt="Kuzushi" width="200" />
2
+
3
+ # Kuzushi — Agentic SAST Orchestrator
4
+
5
+ Agentic SAST orchestrator. Runs security analysis tasks — scanners, AI triage, exploit verification, and more — as a dependency graph on an event-driven pipeline, then tells you what's actually dangerous.
6
+
7
+ ## Quick Start
8
+
9
+ Prereqs: Node 22+, an API key for at least one supported LLM provider.
10
+
11
+ **With Anthropic (default):**
12
+
13
+ 1. Get an API key at https://console.anthropic.com/
14
+ 2. Set it: `export ANTHROPIC_API_KEY=sk-ant-...`
15
+ 3. Scan: `npx kuzushi /path/to/your/repo`
16
+
17
+ **With OpenAI, Google, or any pi-ai-supported provider:**
18
+
19
+ 1. Set the provider key: `export OPENAI_API_KEY=sk-...` (or `GEMINI_API_KEY`, etc.)
20
+ 2. Scan: `npx kuzushi /path/to/repo --agent-runtime pi-ai --model openai:gpt-4o`
21
+
22
+ That's it. Kuzushi auto-downloads Opengrep if you don't have a scanner installed. To add CodeQL, see [CodeQL Setup](#codeql-setup).
23
+
24
+ ## What It Does
25
+
26
+ - **Runs Opengrep/Semgrep** with severity-ranked rule matching
27
+ - **Runs configurable scanners** (`semgrep`, `agentic`, `codeql`) in one orchestration flow
28
+ - **Gathers repo context** — auto-detects language, frameworks, auth patterns, ORMs, and sanitization libraries to enrich AI analysis
29
+ - **Scores and deduplicates** findings by severity, likelihood, impact, and subcategory — cross-scanner normalization merges equivalent findings from different scanners at the same location
30
+ - **AI-triages selected findings** — after dedupe/resume/max filters, agent investigates with repo tools, assigns tp/fp/needs_review with confidence and rationale
31
+ - **Verifies exploitability** — optional post-triage phase constructs concrete proof-of-concept payloads for true positives (e.g., SQL injection strings, XSS vectors)
32
+ - **Generates PoC harnesses** — optional post-verification phase produces runnable exploit scripts (TypeScript, Python, etc.) for verified-exploitable findings
33
+ - **Vendor-agnostic LLM runtime** — swap between Anthropic, OpenAI, Google, and 15+ other providers via the `pi-ai` backend with zero consumer-code changes
34
+ - **Augur integration** — multi-pass CodeQL-based source/sink labeling pipeline with LLM-assisted classification, checkpoint gating, and deterministic library generation
35
+ - **Tracks cost** — per-finding triage, verification, and PoC harness costs are persisted and displayed in the summary
36
+ - **Event-driven pipeline** — pluggable message bus interface (in-process backend implemented; Redis/Google Pub/Sub/NATS adapters are scaffolded)
37
+ - **DAG-based task orchestration** — tasks declare dependencies, run in parallel groups, pass outputs downstream
38
+ - **Extensible agent framework** — `AgentTask` interface for adding new analysis types (threat modeling, binary analysis, etc.)
39
+ - **Persists results** in SQLite — resume interrupted scans, skip already-triaged findings
40
+ - **Resumable runs** — checkpoint pipeline state to SQLite; `--resume` picks up where a crashed or interrupted scan left off
41
+ - **Retry with backoff** — transient agent failures are retried automatically with exponential backoff
42
+ - **Audit logging** — optional JSONL audit trail of every agent decision for debugging and accountability
43
+ - **Markdown reports** — export a shareable `.md` report for CI pipelines and team review
44
+ - **Prints a styled report** showing only what matters: true positives, needs-review items, and verified exploits with PoC payloads
45
+
46
+ ## How It Works
47
+
48
+ Semgrep/Opengrep catches syntactic patterns but can't verify data flow or intent. LLMs can reason about code but hallucinate when scanning from scratch (95%+ false positive rate). Kuzushi combines both: SAST signal narrows the search space, LLM reasoning eliminates false positives. This hybrid approach matches human researcher agreement rates.
49
+
50
+ Under the hood, Kuzushi uses an event-driven architecture with a DAG-based task orchestrator:
51
+
52
+ 1. **Context gathering** (optional, enabled by default) — the context-gatherer task analyzes the repo structure (package.json, go.mod, etc.) to identify the tech stack, frameworks, and security-relevant libraries.
53
+ 2. **Pipeline starts** — the orchestrator resolves enabled tasks into a dependency graph and groups them into parallel stages.
54
+ 3. **Scanners run** — scanner tasks (Semgrep, CodeQL, Agentic, etc.) execute concurrently within their stage, emitting findings as typed events on the message bus.
55
+ 4. **Results gate downstream tasks** — the orchestrator waits for each stage to complete before starting dependent stages. Upstream outputs are forwarded to dependent tasks via `TaskContext`.
56
+ 5. **Triage stage** — findings are deduplicated (fingerprint + cross-scanner location/CWE/rule normalization), ranked, and sent to an LLM for semantic verification with configurable concurrency. The repo context from step 1 enriches every triage prompt.
57
+ 6. **Verification stage** (optional) — triaged findings that pass verification gates (`verifyVerdicts`, scanner-level `scannerConfig.<id>.verify`, and `verifyMinConfidence`) are sent to a verification agent that attempts to construct concrete PoC exploit payloads.
58
+ 7. **PoC harness generation** (optional) — verified-exploitable findings are sent to a harness generator that produces runnable exploit scripts with syntax validation.
59
+ 8. **Report** — final results are persisted, rendered to terminal, and optionally exported as markdown.
60
+
61
+ All communication happens through a transport-agnostic `MessageBus` interface. The default in-process bus works out of the box; distributed adapters (Redis, Google Pub/Sub, NATS) are planned and scaffolded behind the same interface.
62
+
63
+ ## Commands
64
+
65
+ ### Scan (default)
66
+
67
+ ```
68
+ kuzushi <repo> # scan with defaults
69
+ kuzushi <repo> --scanners codeql
70
+ kuzushi <repo> --scanners semgrep,codeql
71
+ kuzushi <repo> --scanners semgrep,agentic
72
+ kuzushi <repo> --severity ERROR # only ERROR-level findings
73
+ kuzushi <repo> --max 20 # triage top 20 findings only
74
+ kuzushi <repo> --model claude-opus-4-20250514 # use a different model
75
+ kuzushi <repo> --triage-model claude-opus-4-20250514 # separate model for triage
76
+ kuzushi <repo> --triage-max-turns 15 # triage agent turn budget
77
+ kuzushi <repo> --api-key sk-ant-... --base-url https://basecamp.stark.rubrik.com/
78
+ kuzushi <repo> --fresh # clear prior results, re-triage everything
79
+ kuzushi <repo> --db ./my.sqlite3 # custom database path
80
+ kuzushi <repo> --resume # resume the most recent interrupted run
81
+ kuzushi <repo> --resume <run-id> # resume a specific run by ID
82
+ ```
83
+
84
+ ### Vendor-Agnostic Runtime
85
+
86
+ ```
87
+ kuzushi <repo> --agent-runtime pi-ai --model openai:gpt-4o
88
+ kuzushi <repo> --agent-runtime pi-ai --model google:gemini-2.0-flash
89
+ kuzushi <repo> --agent-runtime pi-ai --model anthropic:claude-sonnet-4-20250514
90
+ kuzushi config set agentRuntimeBackend pi-ai
91
+ kuzushi config set model openai:gpt-4o
92
+ ```
93
+
94
+ When `agentRuntimeBackend` is `pi-ai`, model strings use `provider:modelId` format. The pi-ai backend implements its own agentic tool-calling loop with local Read/Glob/Grep tools, structured output enforcement, budget tracking, and abort support. All consumer code (triage, verify, PoC harness, scanners) works unchanged — the `AgentRuntime` abstraction handles it.
95
+
96
+ ### Verification
97
+
98
+ ```
99
+ kuzushi <repo> --verify # enable exploit verification for TPs
100
+ kuzushi <repo> --verify --verify-model claude-haiku-4-5-20251001 # cheaper model for verification
101
+ kuzushi <repo> --verify --verify-max-turns 20
102
+ kuzushi <repo> --verify --verify-concurrency 3
103
+ kuzushi <repo> --verify --verify-min-confidence 0.7 # skip low-confidence TPs
104
+ ```
105
+
106
+ ### PoC Harness Generation
107
+
108
+ ```
109
+ kuzushi <repo> --verify --poc-harness # generate exploit scripts for verified findings
110
+ kuzushi <repo> --verify --poc-harness --poc-harness-model claude-haiku-4-5-20251001
111
+ kuzushi <repo> --verify --poc-harness --poc-harness-max-turns 25
112
+ kuzushi <repo> --verify --poc-harness --poc-harness-concurrency 2
113
+ ```
114
+
115
+ ### Output & Observability
116
+
117
+ ```
118
+ kuzushi <repo> --output report.md # export markdown report
119
+ kuzushi <repo> --sarif results.sarif # export SARIF v2.1.0
120
+ kuzushi <repo> --audit-log # write agent activity to .kuzushi/runs/{runId}/
121
+ kuzushi <repo> --no-context # disable repo context gathering
122
+ ```
123
+
124
+ ### Retry
125
+
126
+ ```
127
+ kuzushi <repo> --max-triage-retries 3 # retry failed triage calls (default: 2)
128
+ kuzushi <repo> --max-verify-retries 3 # retry failed verification calls (default: 2)
129
+ kuzushi <repo> --retry-backoff-ms 10000 # initial backoff delay (default: 5000)
130
+ ```
131
+
132
+ ### Config
133
+
134
+ ```
135
+ kuzushi config get # show all config
136
+ kuzushi config get model # show one key
137
+ kuzushi config set model claude-opus-4-20250514
138
+ kuzushi config set scanners semgrep,agentic
139
+ kuzushi config set scannerConfig.codeql.dbPath ./codeql-db
140
+ kuzushi config set scannerConfig.codeql.suite javascript-security-extended
141
+ kuzushi config set scannerConfig.semgrep.binary opengrep
142
+ kuzushi config set scannerConfig.semgrep.configFlag auto
143
+ kuzushi config set scannerConfig.agentic.model claude-sonnet-4-20250514
144
+ kuzushi config set scannerConfig.agentic.maxFindings 25
145
+ kuzushi config set severity ERROR,WARNING,INFO
146
+ kuzushi config set verify true
147
+ kuzushi config set verifyMinConfidence 0.7
148
+ kuzushi config set auditLog true
149
+ kuzushi config unset model # reset to default
150
+ kuzushi config path # print config file location
151
+ ```
152
+
153
+ Global config lives at `~/.kuzushi/config.json`. Optional project overrides can live at `<repo>/.kuzushi/config.json`. CLI flags override config values.
154
+
155
+ Security note: `agentRuntimeConfig.apiKey` is stored in plaintext in config files. Prefer `--api-key` for one-off runs or `ANTHROPIC_API_KEY` from your shell/secret manager.
156
+
157
+ ## Configuration
158
+
159
+ | Key | Default | Description |
160
+ | --- | --- | --- |
161
+ | `model` | `claude-sonnet-4-20250514` | LLM model for scanners and default triage model |
162
+ | `triageModel` | _(uses `model`)_ | Override model used by the triage agent |
163
+ | `triageMaxTurns` | `10` | Max agentic turns per triage call |
164
+ | `scanners` | `["semgrep"]` | Scanner plugins to run, in order |
165
+ | `severity` | `["ERROR","WARNING"]` | Semgrep severity filter |
166
+ | `excludePatterns` | `["test","tests","node_modules",...]` | Directories/globs to skip |
167
+ | `scannerConfig` | `{ semgrep: {...}, agentic: {...}, codeql: {...} }` | Per-scanner config blocks keyed by scanner id |
168
+ | `busBackend` | `"in-process"` | Message bus transport (`in-process`, future: `redis`, `google-pubsub`, `nats`) |
169
+ | `triageConcurrency` | `1` | Parallel LLM triage calls |
170
+ | `scanMode` | `"sequential"` | Scanner execution mode (`sequential` or `concurrent`) |
171
+ | `enabledTasks` | `[]` | Additional agent tasks beyond scanners |
172
+ | `agentRuntimeBackend` | `"claude-sdk"` | Agent runtime backend (`claude-sdk`, `pi-ai`, future: `acp`) |
173
+ | `verify` | `false` | Enable proof-of-exploitability verification |
174
+ | `verifyModel` | _(uses `triageModel` or `model`)_ | Override model for verification agent |
175
+ | `verifyMaxTurns` | `15` | Max turns for verification agent |
176
+ | `verifyConcurrency` | `1` | Parallel verification calls |
177
+ | `verifyVerdicts` | `["tp"]` | Which triage verdicts to verify |
178
+ | `verifyMinConfidence` | `0` | Minimum triage confidence to trigger verification (0-1) |
179
+ | `pocHarness` | `false` | Enable post-verification PoC harness generation (requires `--verify`) |
180
+ | `pocHarnessModel` | _(uses `triageModel` or `model`)_ | Override model for PoC harness agent |
181
+ | `pocHarnessMaxTurns` | `20` | Max turns for PoC harness agent |
182
+ | `pocHarnessConcurrency` | `1` | Parallel PoC harness generation calls |
183
+ | `enableContextGathering` | `true` | Run repo context analysis before triage |
184
+ | `auditLog` | `false` | Write agent activity to JSONL audit files |
185
+ | `reportOutput` | _(unset)_ | Write markdown report output to this path |
186
+ | `sarifOutput` | _(unset)_ | Write SARIF v2.1.0 output to this path |
187
+ | `maxTriageRetries` | `2` | Retry failed triage calls |
188
+ | `maxVerifyRetries` | `2` | Retry failed verification calls |
189
+ | `maxPocHarnessRetries` | `2` | Retry failed PoC harness generation calls |
190
+ | `retryBackoffMs` | `5000` | Initial retry backoff delay in ms |
191
+ | `retryBackoffMultiplier` | `2` | Exponential backoff multiplier |
192
+
193
+ Example:
194
+
195
+ ```json
196
+ {
197
+ "scanners": ["semgrep", "codeql", "agentic"],
198
+ "scanMode": "concurrent",
199
+ "triageConcurrency": 3,
200
+ "verify": true,
201
+ "verifyMinConfidence": 0.7,
202
+ "auditLog": true,
203
+ "enabledTasks": [],
204
+ "scannerConfig": {
205
+ "codeql": { "dbPath": "./codeql-db", "suite": "javascript-security-extended" },
206
+ "semgrep": { "binary": "opengrep", "configFlag": "auto" },
207
+ "agentic": { "model": "claude-sonnet-4-20250514", "maxFindings": 20 }
208
+ }
209
+ }
210
+ ```
211
+
212
+ ### Environment Variables
213
+
214
+ | Variable | Required | Description |
215
+ | --- | --- | --- |
216
+ | `ANTHROPIC_API_KEY` | Yes (claude-sdk backend) | Anthropic API key — required when `agentRuntimeBackend` is `claude-sdk` |
217
+ | `OPENAI_API_KEY` | When using `openai:*` models | OpenAI API key for pi-ai backend |
218
+ | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | When using `google:*` models | Google API key for pi-ai backend |
219
+
220
+ ## Scanner Plugins
221
+
222
+ - `semgrep`: traditional SAST via Opengrep/Semgrep binary
223
+ - `codeql`: semantic dataflow/taint analysis via GitHub CodeQL CLI (SARIF output)
224
+ - `agentic`: AI-driven agentic scanner — LLM with read-only repo tools via any supported runtime
225
+ - `augur`: multi-pass CodeQL source/sink labeling pipeline — runs preflight (database creation, candidate extraction), LLM-assisted labeling with human-in-the-loop checkpoint, and deterministic library/query generation + analysis
226
+
227
+ ## Semgrep Resolution
228
+
229
+ For the `semgrep` plugin, Kuzushi finds a scanner binary in this order:
230
+
231
+ 1. `opengrep` on your PATH
232
+ 2. `semgrep` on your PATH
233
+ 3. Previously downloaded binary at `~/.kuzushi/bin/opengrep`
234
+ 4. Auto-downloads Opengrep from GitHub releases (~40 MB, cached for future runs)
235
+
236
+ No pip, no brew, no manual install needed.
237
+
238
+ ## CodeQL Setup
239
+
240
+ The `codeql` scanner requires the [CodeQL CLI](https://github.com/github/codeql-cli-binaries/releases) to be installed separately. Unlike Semgrep, it is **not auto-downloaded** (the CLI is ~500 MB and requires accepting GitHub's license).
241
+
242
+ Install it:
243
+
244
+ ```sh
245
+ # Via GitHub CLI (recommended):
246
+ gh extension install github/gh-codeql && gh codeql install-stub
247
+
248
+ # Or download directly from:
249
+ # https://github.com/github/codeql-cli-binaries/releases
250
+ ```
251
+
252
+ Kuzushi finds the CodeQL binary in this order:
253
+
254
+ 1. `codeql` on your PATH
255
+ 2. Previously placed binary at `~/.kuzushi/bin/codeql`
256
+ 3. Fails with install instructions if not found
257
+
258
+ CodeQL is **opt-in** — the default scanner list is `["semgrep"]`. To enable it:
259
+
260
+ ```sh
261
+ kuzushi <repo> --scanners codeql # CodeQL only
262
+ kuzushi <repo> --scanners semgrep,codeql # both scanners
263
+ kuzushi config set scanners semgrep,codeql # persist as default
264
+ ```
265
+
266
+ CodeQL builds a database from your source code before running queries. You can skip this step by pointing to a pre-built database:
267
+
268
+ ```sh
269
+ kuzushi config set scannerConfig.codeql.dbPath ./codeql-db
270
+ ```
271
+
272
+ ## Pi-AI Runtime
273
+
274
+ The `pi-ai` backend uses `@mariozechner/pi-ai` to provide vendor-agnostic LLM access. It supports 15+ providers (Anthropic, OpenAI, Google, Groq, Mistral, etc.) through a single interface.
275
+
276
+ Unlike the Claude SDK backend (which has a built-in agentic loop), the pi-ai backend implements its own:
277
+
278
+ 1. **Tool-calling loop** — call model, parse tool calls, execute tools, feed results back, repeat until stop or max turns
279
+ 2. **Local tool implementations** — Read (file reader with line numbers), Glob (Node 22+ `globSync`), Grep (regex search across files)
280
+ 3. **Structured output** — system prompt injection + post-hoc JSON extraction from fenced code blocks or raw text
281
+ 4. **Safety controls** — max turns, budget enforcement, abort signal, permission gating via `canUseTool`
282
+
283
+ ```sh
284
+ # Use with any supported provider:
285
+ OPENAI_API_KEY=... kuzushi <repo> --agent-runtime pi-ai --model openai:gpt-4o
286
+ GEMINI_API_KEY=... kuzushi <repo> --agent-runtime pi-ai --model google:gemini-2.0-flash
287
+ ANTHROPIC_API_KEY=... kuzushi <repo> --agent-runtime pi-ai --model anthropic:claude-sonnet-4-20250514
288
+ ```
289
+
290
+ ## Augur Setup
291
+
292
+ The `augur` scanner is a multi-pass CodeQL-based pipeline that uses LLM-assisted classification to label sources, sinks, sanitizers, and summaries. It requires:
293
+
294
+ 1. **CodeQL CLI** — same requirement as the `codeql` scanner
295
+ 2. **Python 3** — used by Augur's scripts for query generation
296
+
297
+ Augur's templates, references, and scripts are bundled as the [`@kuzushi/augur`](https://www.npmjs.com/package/@kuzushi/augur) npm package and installed automatically with `pnpm install`. No manual clone or `AUGUR_PATH` setup needed.
298
+
299
+ ```sh
300
+ kuzushi <repo> --scanners augur
301
+ kuzushi <repo> --scanners augur --approve-checkpoint # auto-approve label review
302
+ kuzushi config set scannerConfig.augur.labelingModel claude-sonnet-4-20250514
303
+ kuzushi config set scannerConfig.augur.passes "[1,2,3,4,5,6]"
304
+ ```
305
+
306
+ To override the bundled augur assets (e.g., for local development), set `AUGUR_PATH` or `scannerConfig.augur.augurPath`:
307
+
308
+ ```sh
309
+ export AUGUR_PATH=/path/to/local/augur
310
+ kuzushi config set scannerConfig.augur.augurPath /path/to/local/augur
311
+ ```
312
+
313
+ Augur runs in three DAG-ordered stages: **preflight** (database creation, candidate extraction), **label** (LLM classification with checkpoint gate), and **analyze** (library generation, query execution, finding extraction). A human-in-the-loop checkpoint pauses after labeling for review — use `--approve-checkpoint` to auto-approve in CI.
314
+
315
+ ## Output
316
+
317
+ Results are stored in SQLite at `<repo>/.kuzushi/findings.sqlite3`. Each finding includes:
318
+
319
+ - **verdict**: `tp` (true positive), `fp` (false positive), or `needs_review`
320
+ - **confidence**: 0.0-1.0
321
+ - **rationale**: why the LLM reached that verdict, referencing specific code
322
+ - **verification_steps**: 2-6 steps a human reviewer can follow
323
+ - **fix_patch**: suggested fix (when applicable)
324
+ - **exploitability** (with `--verify`): whether a concrete exploit was constructed, PoC payload, attack vector, and preconditions
325
+ - **cost**: per-finding triage and verification cost in USD
326
+
327
+ The terminal report shows true positives first, then needs-review items. False positives are counted but hidden. Verified exploitable findings are highlighted with their PoC payloads.
328
+
329
+ Use `--output report.md` to export a shareable markdown report.
330
+ Use `--sarif results.sarif` to export SARIF v2.1.0 for code scanning platforms.
331
+
332
+ ## SARIF / GitHub Code Scanning
333
+
334
+ Kuzushi can emit SARIF v2.1.0 directly. GitHub Code Scanning ingests SARIF and creates inline annotations.
335
+
336
+ ```
337
+ kuzushi <repo> --sarif results.sarif
338
+
339
+ gh api \
340
+ -X POST \
341
+ repos/OWNER/REPO/code-scanning/sarifs \
342
+ -f commit_sha="$(git rev-parse HEAD)" \
343
+ -f ref="refs/heads/$(git rev-parse --abbrev-ref HEAD)" \
344
+ -f sarif="$(gzip -c results.sarif | base64 | tr -d '\n')"
345
+ ```
346
+
347
+ ## Resume Support
348
+
349
+ Kuzushi fingerprints every finding (content-based SHA-256 that survives line shifts). Re-running a scan skips already-triaged findings automatically. Use `--fresh` to start over.
350
+
351
+ For interrupted runs, use `--resume` to pick up where the pipeline left off. Kuzushi checkpoints pipeline state (scan findings, triage progress, verification progress) to SQLite. On resume, completed phases are skipped and only remaining work is executed.
352
+
353
+ ```
354
+ kuzushi <repo> --resume # resume most recent interrupted run
355
+ kuzushi <repo> --resume abc-123 # resume a specific run by ID
356
+ ```
357
+
358
+ ## Audit Logging
359
+
360
+ With `--audit-log`, Kuzushi writes a structured audit trail to `.kuzushi/runs/{runId}/`:
361
+
362
+ - `triage.jsonl` — every tool call, reasoning step, and verdict from triage agents
363
+ - `verify.jsonl` — same for verification agents
364
+ - `run.json` — run config and scan options
365
+ - `stats.json` — final pipeline statistics
366
+
367
+ Use this to debug verdicts, review agent reasoning, or build compliance records.
368
+
369
+ ## Architecture
370
+
371
+ Kuzushi is built on three core abstractions:
372
+
373
+ **Message Bus** — A transport-agnostic `MessageBus` interface (`publish`, `subscribe`, `waitFor`) that decouples pipeline stages. The default in-process implementation uses an `EventEmitter`; the interface supports swapping in Redis, Google Pub/Sub, or NATS for distributed setups.
374
+
375
+ **AgentTask + DAG** — Every unit of work (context gatherer, scanner, future threat modeler, etc.) implements the `AgentTask` interface: an `id`, `dependsOn` list, `outputKind`, and a `run()` method. The `TaskRegistry` resolves enabled tasks into a DAG, groups them into parallel stages, detects cycles, and hands execution to the `PipelineOrchestrator`. Upstream task outputs are forwarded to dependents automatically.
376
+
377
+ **Pipeline Phases** — After the DAG completes, the orchestrator drives three sequential phases: triage (classify findings), verification (construct PoC exploits), and report (display results). Each phase has its own concurrency control, cost tracking, and checkpoint support.
378
+
379
+ Existing `ScannerPlugin` implementations (Semgrep, Agentic) are adapted into `AgentTask` via `adaptScannerPlugin()`, so the scanner plugin API remains stable.
380
+
381
+ See [AGENTS.md](AGENTS.md) for the full developer guide on adding new agent tasks.
382
+
383
+ ## Development
384
+
385
+ ```
386
+ pnpm install # install deps
387
+ pnpm dev -- /path/to/repo # run in dev mode
388
+ pnpm typecheck # type check
389
+ pnpm test # run tests (214 tests across 31 files)
390
+ pnpm test:coverage # tests + coverage (70% threshold)
391
+ pnpm build # compile to dist/
392
+ ```
393
+
394
+ Tests are organized by subsystem: `tests/bus/` for orchestrator, workers, and event bus tests, `tests/agents/` for DAG, task registry, and context-gatherer tests, and `tests/` for scanners, triage, verification, store, config, retry, and report.
395
+
396
+ ## Troubleshooting
397
+
398
+ - **"Error: ANTHROPIC_API_KEY environment variable is required."**: Export your key — `export ANTHROPIC_API_KEY=sk-ant-...` (only required for `claude-sdk` backend; use `--agent-runtime pi-ai` with other providers)
399
+ - **"No findings from scanner. Code looks clean."**: Your code is clean, or try `--severity ERROR,WARNING,INFO` to include lower-severity rules
400
+ - **Scan interrupted**: Re-run the same command (already-triaged findings are skipped), or use `--resume` to continue from the exact checkpoint
401
+ - **Wrong model**: `kuzushi config set model claude-opus-4-20250514` or pass `--model` per-scan
402
+ - **Scanner download fails**: Install Opengrep or Semgrep manually, ensure it's on your PATH
403
+ - **High triage cost**: Use `--triage-model claude-haiku-4-5-20251001` for cheaper triage, or `--max 10` to limit findings
404
+ - **Verification too expensive**: Use `--verify-min-confidence 0.8` to only verify high-confidence TPs, or `--verify-model claude-haiku-4-5-20251001`
405
+ - **pi-ai model not found**: Ensure the model string uses `provider:modelId` format (e.g., `openai:gpt-4o`, not just `gpt-4o`)
406
+ - **Augur checkpoint blocks CI**: Pass `--approve-checkpoint` to auto-approve label review in non-interactive environments
407
+
408
+ ## License
409
+
410
+ MIT
@@ -0,0 +1,8 @@
1
+ import type { AgentRuntimeConfig } from "../types.js";
2
+ import type { AgentMessage, AgentQueryRequest, AgentRuntime } from "./types.js";
3
+ export declare class ClaudeSdkAgentRuntime implements AgentRuntime {
4
+ readonly id = "claude-sdk";
5
+ private readonly runtimeConfig?;
6
+ constructor(runtimeConfig?: AgentRuntimeConfig);
7
+ query(request: AgentQueryRequest): AsyncIterable<AgentMessage>;
8
+ }
@@ -0,0 +1,124 @@
1
+ import { query, } from "@anthropic-ai/claude-agent-sdk";
2
+ import { isRecord } from "../utils.js";
3
+ export class ClaudeSdkAgentRuntime {
4
+ id = "claude-sdk";
5
+ runtimeConfig;
6
+ constructor(runtimeConfig) {
7
+ this.runtimeConfig = runtimeConfig;
8
+ }
9
+ async *query(request) {
10
+ const canUseTool = request.options.canUseTool
11
+ ? adaptCanUseTool(request.options.canUseTool)
12
+ : undefined;
13
+ const runtimeEnv = buildRuntimeEnv(this.runtimeConfig);
14
+ for await (const message of query({
15
+ prompt: request.prompt,
16
+ options: {
17
+ cwd: request.options.cwd,
18
+ model: request.options.model,
19
+ maxTurns: request.options.maxTurns,
20
+ allowedTools: request.options.allowedTools,
21
+ disallowedTools: request.options.disallowedTools,
22
+ permissionMode: request.options.permissionMode,
23
+ maxBudgetUsd: request.options.maxBudgetUsd,
24
+ abortController: request.options.abortController,
25
+ outputFormat: request.options.outputFormat
26
+ ? {
27
+ type: "json_schema",
28
+ schema: request.options.outputFormat.schema,
29
+ }
30
+ : undefined,
31
+ canUseTool,
32
+ env: runtimeEnv,
33
+ },
34
+ })) {
35
+ yield mapMessage(message);
36
+ }
37
+ }
38
+ }
39
+ function buildRuntimeEnv(runtimeConfig) {
40
+ if (!runtimeConfig?.apiKey && !runtimeConfig?.baseUrl) {
41
+ return undefined;
42
+ }
43
+ return {
44
+ ...process.env,
45
+ ANTHROPIC_API_KEY: runtimeConfig.apiKey ?? process.env["ANTHROPIC_API_KEY"],
46
+ ANTHROPIC_BASE_URL: runtimeConfig.baseUrl ?? process.env["ANTHROPIC_BASE_URL"],
47
+ };
48
+ }
49
+ function mapMessage(message) {
50
+ if (message.type === "result") {
51
+ if (message.subtype === "success") {
52
+ return {
53
+ type: "result",
54
+ subtype: "success",
55
+ result: message.result,
56
+ structuredOutput: message.structured_output,
57
+ durationMs: message.duration_ms,
58
+ numTurns: message.num_turns,
59
+ totalCostUsd: message.total_cost_usd,
60
+ };
61
+ }
62
+ return {
63
+ type: "result",
64
+ subtype: message.subtype,
65
+ errors: [...message.errors],
66
+ durationMs: message.duration_ms,
67
+ numTurns: message.num_turns,
68
+ totalCostUsd: message.total_cost_usd,
69
+ };
70
+ }
71
+ if (message.type === "assistant") {
72
+ const content = Array.isArray(message.message.content)
73
+ ? message.message.content.filter(isRecord)
74
+ : [];
75
+ return {
76
+ type: "assistant",
77
+ content,
78
+ };
79
+ }
80
+ if (message.type === "tool_progress") {
81
+ return {
82
+ type: "tool-progress",
83
+ toolUseId: message.tool_use_id,
84
+ toolName: message.tool_name,
85
+ elapsedSeconds: message.elapsed_time_seconds,
86
+ };
87
+ }
88
+ if (message.type === "tool_use_summary") {
89
+ return {
90
+ type: "tool-summary",
91
+ summary: message.summary,
92
+ precedingToolUseIds: [...message.preceding_tool_use_ids],
93
+ };
94
+ }
95
+ return {
96
+ type: "unknown",
97
+ raw: message,
98
+ };
99
+ }
100
+ function adaptCanUseTool(handler) {
101
+ return async (toolName, input, options) => {
102
+ const decision = await handler(toolName, input, {
103
+ signal: options.signal,
104
+ toolUseId: options.toolUseID,
105
+ blockedPath: options.blockedPath,
106
+ decisionReason: options.decisionReason,
107
+ });
108
+ return toPermissionResult(decision);
109
+ };
110
+ }
111
+ function toPermissionResult(decision) {
112
+ if (decision.behavior === "allow") {
113
+ return {
114
+ behavior: "allow",
115
+ updatedInput: undefined,
116
+ updatedPermissions: undefined,
117
+ };
118
+ }
119
+ return {
120
+ behavior: "deny",
121
+ message: decision.message ?? "Permission denied",
122
+ };
123
+ }
124
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agent-runtime/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { KuzushiConfig } from "../types.js";
2
+ import { ClaudeSdkAgentRuntime } from "./claude.js";
3
+ import { PiAiAgentRuntime } from "./pi-ai.js";
4
+ import type { AgentRuntime } from "./types.js";
5
+ export declare function getAgentRuntime(): AgentRuntime;
6
+ export declare function setAgentRuntime(runtime: AgentRuntime): void;
7
+ export declare function configureAgentRuntime(config: KuzushiConfig): AgentRuntime;
8
+ export * from "./types.js";
9
+ export { ClaudeSdkAgentRuntime, PiAiAgentRuntime };
@@ -0,0 +1,31 @@
1
+ import { ClaudeSdkAgentRuntime } from "./claude.js";
2
+ import { PiAiAgentRuntime } from "./pi-ai.js";
3
+ const DEFAULT_BACKEND = "claude-sdk";
4
+ let currentRuntime = new ClaudeSdkAgentRuntime();
5
+ export function getAgentRuntime() {
6
+ return currentRuntime;
7
+ }
8
+ export function setAgentRuntime(runtime) {
9
+ currentRuntime = runtime;
10
+ }
11
+ export function configureAgentRuntime(config) {
12
+ const backend = config.agentRuntimeBackend ?? DEFAULT_BACKEND;
13
+ const runtimeConfig = config.agentRuntimeConfig;
14
+ if (backend === "claude-sdk") {
15
+ currentRuntime = new ClaudeSdkAgentRuntime(runtimeConfig);
16
+ return currentRuntime;
17
+ }
18
+ if (backend === "pi-ai") {
19
+ currentRuntime = new PiAiAgentRuntime(runtimeConfig);
20
+ return currentRuntime;
21
+ }
22
+ if (backend === "acp") {
23
+ throw new Error("agentRuntimeBackend=acp is not available yet. "
24
+ + "Use agentRuntimeBackend=claude-sdk or agentRuntimeBackend=pi-ai until ACP runtime adapter is implemented.");
25
+ }
26
+ throw new Error(`Unknown agent runtime backend: ${String(backend)}. `
27
+ + "Expected one of: claude-sdk, pi-ai, acp.");
28
+ }
29
+ export * from "./types.js";
30
+ export { ClaudeSdkAgentRuntime, PiAiAgentRuntime };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent-runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,eAAe,GAAG,YAAY,CAAC;AAErC,IAAI,cAAc,GAAiB,IAAI,qBAAqB,EAAE,CAAC;AAE/D,MAAM,UAAU,eAAe;IAC7B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAqB;IACnD,cAAc,GAAG,OAAO,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,OAAO,GAAG,MAAM,CAAC,mBAAmB,IAAI,eAAe,CAAC;IAC9D,MAAM,aAAa,GAAG,MAAM,CAAC,kBAAkB,CAAC;IAEhD,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,cAAc,GAAG,IAAI,qBAAqB,CAAC,aAAa,CAAC,CAAC;QAC1D,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,cAAc,GAAG,IAAI,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACrD,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gDAAgD;cAC9C,2GAA2G,CAC9G,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,KAAK,CACb,kCAAkC,MAAM,CAAC,OAAO,CAAC,IAAI;UACnD,0CAA0C,CAC7C,CAAC;AACJ,CAAC;AAED,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface PiAiModelSpec {
2
+ provider: string;
3
+ modelId: string;
4
+ }
5
+ /**
6
+ * Parse a pi-ai model identifier in provider:modelId format.
7
+ */
8
+ export declare function parsePiAiModelSpec(model: string): PiAiModelSpec;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Parse a pi-ai model identifier in provider:modelId format.
3
+ */
4
+ export function parsePiAiModelSpec(model) {
5
+ const value = model.trim();
6
+ const separatorIndex = value.indexOf(":");
7
+ if (separatorIndex === -1) {
8
+ throw new Error(`pi-ai model "${model}" is invalid. Expected format: provider:modelId`);
9
+ }
10
+ const provider = value.slice(0, separatorIndex).trim();
11
+ const modelId = value.slice(separatorIndex + 1).trim();
12
+ if (!provider || !modelId) {
13
+ throw new Error(`pi-ai model "${model}" is invalid. Expected format: provider:modelId`);
14
+ }
15
+ return { provider, modelId };
16
+ }
17
+ //# sourceMappingURL=model-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-spec.js","sourceRoot":"","sources":["../../src/agent-runtime/model-spec.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAE1C,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,gBAAgB,KAAK,iDAAiD,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEvD,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,gBAAgB,KAAK,iDAAiD,CACvE,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { AgentRuntimeConfig } from "../types.js";
2
+ import type { AgentMessage, AgentQueryRequest, AgentRuntime } from "./types.js";
3
+ export declare class PiAiAgentRuntime implements AgentRuntime {
4
+ readonly id = "pi-ai";
5
+ private readonly runtimeConfig?;
6
+ constructor(runtimeConfig?: AgentRuntimeConfig);
7
+ query(request: AgentQueryRequest): AsyncIterable<AgentMessage>;
8
+ }