koma-miko 0.1.0-alpha.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 (47) hide show
  1. package/README.md +271 -0
  2. package/README.zh-CN.md +233 -0
  3. package/dist/claude-code.d.ts +42 -0
  4. package/dist/claude-code.d.ts.map +1 -0
  5. package/dist/claude-code.js +135 -0
  6. package/dist/claude-code.js.map +1 -0
  7. package/dist/claude-hook-cli.d.ts +3 -0
  8. package/dist/claude-hook-cli.d.ts.map +1 -0
  9. package/dist/claude-hook-cli.js +175 -0
  10. package/dist/claude-hook-cli.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +29 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +15 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +50 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/doctor.d.ts +16 -0
  20. package/dist/doctor.d.ts.map +1 -0
  21. package/dist/doctor.js +128 -0
  22. package/dist/doctor.js.map +1 -0
  23. package/dist/index.d.ts +193 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +629 -0
  26. package/dist/index.js.map +1 -0
  27. package/evals/audit-demo.mjs +208 -0
  28. package/evals/claude-hook.mjs +85 -0
  29. package/evals/claude-live.mjs +191 -0
  30. package/evals/claude-scale-live.mjs +312 -0
  31. package/evals/replay.mjs +64 -0
  32. package/evals/scale.mjs +154 -0
  33. package/examples/claude-code/contracts.json +20 -0
  34. package/examples/claude-code/miko.json +24 -0
  35. package/examples/claude-code/settings.json +62 -0
  36. package/package.json +61 -0
  37. package/schema/miko.schema.json +172 -0
  38. package/src/claude-code.test.ts +133 -0
  39. package/src/claude-code.ts +196 -0
  40. package/src/claude-hook-cli.ts +223 -0
  41. package/src/cli.ts +30 -0
  42. package/src/config.test.ts +46 -0
  43. package/src/config.ts +61 -0
  44. package/src/doctor.test.ts +83 -0
  45. package/src/doctor.ts +158 -0
  46. package/src/index.test.ts +404 -0
  47. package/src/index.ts +905 -0
package/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # koma-miko (alpha)
2
+
3
+ Deterministic skill and action contract verification for AI agent workflows.
4
+
5
+ Think of each contract as an **Agent Spec**: a developer-owned executable test
6
+ for how an agent prepares, acts, and completes work—not an enterprise policy
7
+ console.
8
+
9
+ Miko checks observable evidence at three points:
10
+
11
+ 1. **Prepare** — were the required skills and references loaded?
12
+ 2. **Pre-action** — is the proposed tool, risk, and path scope allowed?
13
+ 3. **Complete** — did the required tests, reviews, or artifacts actually happen?
14
+
15
+ This source alpha is not published to npm yet. Its API may change before the
16
+ first public release.
17
+
18
+ ## Why
19
+
20
+ Agent instructions are useful guidance, but guidance is not enforcement. A skill
21
+ may fail to activate, lose influence in a long session, or be followed initially
22
+ while its completion checklist is skipped later. **Miko does not inspect hidden
23
+ model state, measure whether instructions still influence a near-million-token
24
+ context, or prove that an agent chose correctly among 100 skills.** A host
25
+ records observable events, and Miko checks those events against explicit
26
+ contracts.
27
+
28
+ ## Example
29
+
30
+ The developer-facing project file is `miko.json`:
31
+
32
+ ```json
33
+ {
34
+ "$schema": "./node_modules/koma-miko/schema/miko.schema.json",
35
+ "version": 1,
36
+ "specs": [
37
+ {
38
+ "id": "ui-change-v1",
39
+ "appliesWhen": {
40
+ "action": {
41
+ "tools": ["Edit", "Write"],
42
+ "pathPrefixes": ["src/ui"]
43
+ }
44
+ },
45
+ "requires": {
46
+ "skills": [
47
+ { "name": "product-design", "reloadAfterCompaction": true }
48
+ ]
49
+ },
50
+ "mode": "enforce"
51
+ }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ The TypeScript API consumes the same spec objects directly:
57
+
58
+ ```ts
59
+ import { createMiko } from 'koma-miko';
60
+
61
+ const miko = createMiko({
62
+ contracts: [{
63
+ id: 'ui-change-v1',
64
+ // The host can activate this contract from the actual action, even when
65
+ // the agent forgot to label the task as UI work.
66
+ appliesWhen: {
67
+ action: {
68
+ tools: ['write_file'],
69
+ pathPrefixes: ['src/ui'],
70
+ },
71
+ },
72
+ requires: {
73
+ skills: [{ name: 'product-design', reloadAfterCompaction: true }],
74
+ references: ['docs/design-system.md'],
75
+ },
76
+ actions: {
77
+ allow: ['read_file', 'write_file', 'run_check'],
78
+ deny: ['delete_file'],
79
+ maxRisk: 'medium',
80
+ scope: {
81
+ tools: ['write_file'],
82
+ allowedPathPrefixes: ['src/ui'],
83
+ },
84
+ },
85
+ completion: {
86
+ evidence: [
87
+ { type: 'check_passed', name: 'rendered-ui-review' },
88
+ { type: 'check_passed', name: 'targeted-tests' },
89
+ ],
90
+ },
91
+ mode: 'review',
92
+ }],
93
+ });
94
+
95
+ miko.startTask({
96
+ sessionId: 'session-7',
97
+ taskId: 'new-settings-page',
98
+ tags: ['ui'],
99
+ });
100
+
101
+ // Missing preparation is REVIEW in review mode (DENY in enforce mode).
102
+ miko.verifyPreparation('new-settings-page');
103
+
104
+ miko.record({
105
+ taskId: 'new-settings-page',
106
+ type: 'skill_loaded',
107
+ name: 'product-design',
108
+ source: 'observed',
109
+ });
110
+ miko.record({
111
+ taskId: 'new-settings-page',
112
+ type: 'reference_read',
113
+ path: 'docs/design-system.md',
114
+ source: 'observed',
115
+ });
116
+
117
+ miko.verifyAction({
118
+ taskId: 'new-settings-page',
119
+ tool: 'write_file',
120
+ risk: 'medium',
121
+ arguments: { path: 'src/ui/Settings.tsx' },
122
+ });
123
+
124
+ // REVIEW until both completion checks have been recorded.
125
+ miko.verifyCompletion('new-settings-page');
126
+ ```
127
+
128
+ Every result is machine-readable and explainable:
129
+
130
+ ```ts
131
+ {
132
+ decision: 'REVIEW',
133
+ checkpoint: 'COMPLETE',
134
+ reasonCode: 'COMPLETION_EVIDENCE_MISSING',
135
+ reason: 'Required completion evidence is missing.',
136
+ contractIds: ['ui-change-v1'],
137
+ missing: ['ui-change-v1:check_passed:rendered-ui-review']
138
+ }
139
+ ```
140
+
141
+ The plain-text renderer is intentionally developer-facing and bounded even when
142
+ many Agent Specs overlap:
143
+
144
+ ```text
145
+ 🔴 Miko DENY · PREPARE — PREPARATION_EVIDENCE_MISSING
146
+ Missing evidence:
147
+ - ui-change-v1:skill_loaded:product-design
148
+ Next: load the required skill/reference, then retry the blocked action.
149
+ ```
150
+
151
+ ## Claude Code hook mapping
152
+
153
+ `toClaudePreToolUseDecision(result)` maps Miko decisions to Claude Code's
154
+ structured `PreToolUse` output:
155
+
156
+ - `ALLOW` → `allow`
157
+ - `DENY` → `deny`
158
+ - `REVIEW` → `ask`
159
+
160
+ Non-ALLOW results also include a concise `systemMessage` for the user and
161
+ `additionalContext` for the agent. Miko Verifier has no graphical UI: each host
162
+ renders the same structured decision using its native text/approval surface.
163
+
164
+ The included `koma-miko-claude-hook` executable provides a minimal durable
165
+ Claude Code adapter. It observes automatic `Skill` calls, direct `/skill-name`
166
+ expansions, `Read`, `Edit`, and `Write` events; persists a privacy-minimized
167
+ JSONL ledger including non-ALLOW decisions; and can activate a contract from an
168
+ observed tool/path instead of model-supplied tags. See
169
+ [`examples/claude-code`](./examples/claude-code).
170
+
171
+ Skills declared with `reloadAfterCompaction: true` become missing again after a
172
+ Claude `PostCompact` event. The adapter keeps JSONL as the append-only audit
173
+ record and uses a compact materialized snapshot so each Hook only replays events
174
+ written after the latest snapshot. The ledger is auditable, not tamper-proof.
175
+
176
+ To try the source alpha, build/install the package, copy the example `miko.json`
177
+ to the project root, and merge the example hooks into
178
+ `.claude/settings.json`. The example is intentionally not enabled automatically:
179
+ its enforced `frontend-design` skill must actually exist in the target project.
180
+ Miko writes session metadata under `.miko/state/`, which should stay ignored.
181
+ Legacy `.miko/contracts.json` arrays remain readable but are no longer the
182
+ preferred developer interface.
183
+
184
+ Run an entirely offline preflight before spending model credits:
185
+
186
+ ```sh
187
+ npx koma-miko doctor
188
+ npx koma-miko doctor --strict --json
189
+ ```
190
+
191
+ Doctor validates Agent Specs and reports project Skill discovery, required
192
+ Claude Hook coverage, and whether `.miko/state/` is ignored. It never calls a
193
+ model or reads an API key.
194
+
195
+ Claude Code's local CLI, Desktop Code tab, and VS Code/Cursor extension share
196
+ settings, hooks, and skills. Cloud/remote sessions have different configuration
197
+ sources, and managed policies can disable project hooks, so adapters must expose
198
+ their detected capabilities rather than promise identical behavior everywhere.
199
+ See the official [platform overview](https://code.claude.com/docs/en/platforms),
200
+ [Desktop shared configuration](https://code.claude.com/docs/en/desktop), and
201
+ [VS Code settings](https://code.claude.com/docs/en/ide-integrations).
202
+
203
+ ## Automated replay
204
+
205
+ `npm run eval:replay -w koma-miko` runs ten simplified skill contracts without
206
+ an API key. For every skill it verifies three cases: missing evidence, an agent
207
+ claim (`asserted`), and a host-observed load (`observed`). Only observed or
208
+ external evidence can satisfy a contract. The first UI case is the narrow
209
+ enforcement demo; the other nine remain review-only.
210
+ `npm run eval:claude-hook -w koma-miko` additionally spawns three independent
211
+ hook processes and verifies an audited `DENY → observed Skill → ALLOW` plus
212
+ ledger privacy.
213
+ `npm run eval:scale -w koma-miko` uses no API key. It benchmarks 100/1,000 Agent
214
+ Specs, 10,000 indexed evidence events, 100 overlapping specs, and snapshot
215
+ restore while checking that terminal output remains bounded.
216
+ `npm run eval:audit-demo -w koma-miko` regenerates the 13-event fixture behind
217
+ the public guided CLI simulation from real Verifier results.
218
+ `eval:audit-demo:check` detects a stale fixture without changing it; the replay
219
+ contains no prompt, code, or model response and needs no backend. The friendly
220
+ terminal story is presentation only; expandable raw events preserve reason
221
+ codes, provenance, and contract IDs for inspection.
222
+
223
+ With `ANTHROPIC_API_KEY` set in the parent process,
224
+ `npm run eval:claude-live -w koma-miko` runs one disposable, real Claude Code
225
+ fixture. It exposes only `Read`, `Edit`, and `Skill`, defaults to Haiku, enforces
226
+ a `$0.10` per-run cap, and checks this sequence:
227
+
228
+ ```text
229
+ Read → Miko DENY → Claude loads frontend-design → Miko allows → Edit
230
+ ```
231
+
232
+ The fixture is created inside the package workspace so Claude Code treats it as
233
+ project content, then deleted. The runner never reads an env file. Override the
234
+ defaults with `MIKO_LIVE_MODEL` and `MIKO_LIVE_MAX_BUDGET_USD` (capped by the
235
+ runner at `$1`).
236
+
237
+ For the 100-Skill long-context fixture, start without spending credits:
238
+
239
+ ```sh
240
+ npm run eval:claude-scale-dry -w koma-miko
241
+ ```
242
+
243
+ Then run one approximately 20k-token Haiku case with a `$0.12` hard cap:
244
+
245
+ ```sh
246
+ MIKO_LIVE_CONTEXT_TOKENS=20000 \
247
+ MIKO_LIVE_MAX_BUDGET_USD=0.12 \
248
+ MIKO_LIVE_CAMPAIGN_BUDGET_USD=0.12 \
249
+ npm run eval:claude-scale-live -w koma-miko
250
+ ```
251
+
252
+ The runner accepts at most three comma-separated context sizes from 1,000 to
253
+ 190,000 tokens. It creates exactly 100 project Skills, exposes only
254
+ `Read`/`Edit`/`Skill`, runs in a disposable directory, records cache and cost
255
+ metrics, and never reads an env file. See the
256
+ [alpha evaluation record](../../docs/evals/miko-claude-haiku-alpha.md).
257
+
258
+ ## Alpha boundaries
259
+
260
+ - **No LLM call or semantic task classifier**
261
+ - **No planner, router, or agent runtime**
262
+ - **No context-window/token monitoring**
263
+ - **One Haiku/100-Skill fixture has passed at approximately 20k tokens; this is not evidence for 100k, 190k, or near-million-token behavior**
264
+ - **No hosted telemetry service** (the Claude adapter uses a local JSONL ledger)
265
+ - **No automatic rewriting of tool calls**
266
+ - **No claim that loading a skill proves the model understood, retained, or followed it**
267
+
268
+ See the [design and discovery notes](../../docs/design/miko.md), including the
269
+ first-hand failure case, public reports used as test discovery data, and the
270
+ post-alpha questions. Product positioning and executable follow-up work live in
271
+ the [developer roadmap](../../docs/design/miko-roadmap.md).
@@ -0,0 +1,233 @@
1
+ # koma-miko(alpha)
2
+
3
+ 面向 AI agent 工作流的确定性 skill / action 契约验证器。
4
+
5
+ 可以把每份 Contract 理解成开发者维护的 **Agent Spec(智能体测试用例)**:
6
+ 它像测试代码一样约束 agent 如何准备、行动和完成任务,而不是企业治理控制台。
7
+
8
+ Miko 在三个可观察节点检查证据:
9
+
10
+ 1. **准备阶段** — 必需的 skill 和参考文档是否真的被加载;
11
+ 2. **动作之前** — 工具、风险等级与路径范围是否符合契约;
12
+ 3. **完成之前** — 测试、UI 渲染检查或其他交付义务是否真的执行。
13
+
14
+ 当前是源码 alpha,尚未发布到 npm;首次公开发布前 API 仍可能调整。
15
+
16
+ ## 为什么
17
+
18
+ Agent 指令是有用的引导,但不等于强制约束。Skill 可能没有被自动触发,
19
+ 也可能在长会话中逐渐失去影响,或者 agent 前半段遵守规则、结束时却跳过
20
+ 测试清单。**Miko 不检查隐藏的模型状态,无法测量指令在接近百万 token 的
21
+ 上下文中是否仍有影响,也无法证明 agent 能从 100 个 Skill 中选对一个。**
22
+ 宿主记录可观察事件,Miko 根据明确契约验证这些证据。
23
+
24
+ ## 最小示例
25
+
26
+ 开发者直接维护项目根目录的 `miko.json`:
27
+
28
+ ```json
29
+ {
30
+ "$schema": "./node_modules/koma-miko/schema/miko.schema.json",
31
+ "version": 1,
32
+ "specs": [
33
+ {
34
+ "id": "ui-change-v1",
35
+ "appliesWhen": {
36
+ "action": {
37
+ "tools": ["Edit", "Write"],
38
+ "pathPrefixes": ["src/ui"]
39
+ }
40
+ },
41
+ "requires": {
42
+ "skills": [
43
+ { "name": "product-design", "reloadAfterCompaction": true }
44
+ ]
45
+ },
46
+ "mode": "enforce"
47
+ }
48
+ ]
49
+ }
50
+ ```
51
+
52
+ TypeScript API 可以直接使用同一批 Spec 对象:
53
+
54
+ ```ts
55
+ import { createMiko } from 'koma-miko';
56
+
57
+ const miko = createMiko({
58
+ contracts: [{
59
+ id: 'ui-change-v1',
60
+ appliesWhen: {
61
+ action: {
62
+ tools: ['write_file'],
63
+ pathPrefixes: ['src/ui'],
64
+ },
65
+ },
66
+ requires: {
67
+ skills: [{ name: 'product-design', reloadAfterCompaction: true }],
68
+ references: ['docs/design-system.md'],
69
+ },
70
+ actions: {
71
+ allow: ['read_file', 'write_file', 'run_check'],
72
+ deny: ['delete_file'],
73
+ maxRisk: 'medium',
74
+ scope: {
75
+ tools: ['write_file'],
76
+ allowedPathPrefixes: ['src/ui'],
77
+ },
78
+ },
79
+ completion: {
80
+ evidence: [
81
+ { type: 'check_passed', name: 'rendered-ui-review' },
82
+ { type: 'check_passed', name: 'targeted-tests' },
83
+ ],
84
+ },
85
+ mode: 'review',
86
+ }],
87
+ });
88
+
89
+ miko.startTask({
90
+ sessionId: 'session-7',
91
+ taskId: 'new-settings-page',
92
+ tags: ['ui'],
93
+ });
94
+
95
+ // 未记录 product-design 与 design-system 时返回 REVIEW。
96
+ miko.verifyPreparation('new-settings-page');
97
+
98
+ miko.record({
99
+ taskId: 'new-settings-page',
100
+ type: 'skill_loaded',
101
+ name: 'product-design',
102
+ source: 'observed',
103
+ });
104
+ miko.record({
105
+ taskId: 'new-settings-page',
106
+ type: 'reference_read',
107
+ path: 'docs/design-system.md',
108
+ source: 'observed',
109
+ });
110
+
111
+ miko.verifyAction({
112
+ taskId: 'new-settings-page',
113
+ tool: 'write_file',
114
+ risk: 'medium',
115
+ arguments: { path: 'src/ui/Settings.tsx' },
116
+ });
117
+
118
+ // 直到完成证据齐全前都返回 REVIEW。
119
+ miko.verifyCompletion('new-settings-page');
120
+ ```
121
+
122
+ `mode: 'review'` 会把缺失证据映射为 `REVIEW`;`mode: 'enforce'` 则映射为
123
+ `DENY`。明确越权的工具、风险或路径在两种模式下都会返回 `DENY`。
124
+
125
+ 终端文字采用有阶段含义、且长度受限的开发者红绿灯;即使许多 Agent Spec
126
+ 重叠,也只展开最重要的前三项:
127
+
128
+ ```text
129
+ 🔴 Miko DENY · PREPARE — PREPARATION_EVIDENCE_MISSING
130
+ Missing evidence:
131
+ - ui-change-v1:skill_loaded:product-design
132
+ Next: load the required skill/reference, then retry the blocked action.
133
+ ```
134
+
135
+ `toClaudePreToolUseDecision(result)` 可将 `ALLOW / DENY / REVIEW` 映射到
136
+ Claude Code `PreToolUse` 的 `allow / deny / ask`。非 ALLOW 结果还会同时
137
+ 向用户显示精简文字,并把恢复提示交给 agent。Miko Verifier 本身不带图形 UI;
138
+ CLI、桌面端或 IDE 使用各自原生的文字与审批界面渲染同一结构化结果。
139
+
140
+ 包内的 `koma-miko-claude-hook` 提供最小可用的持久化 Claude Code 适配器:
141
+ 它观察自动 `Skill` 调用、用户直接输入的 `/skill-name`、`Read`、`Edit` 与
142
+ `Write` 事件;使用仅含必要元数据、并记录非 ALLOW 决策的本地 JSONL 账本;
143
+ 并可根据真实工具与路径激活合约,不再只相信模型提供的任务标签。配置示例见
144
+ [`examples/claude-code`](./examples/claude-code)。
145
+
146
+ 设置 `reloadAfterCompaction: true` 的 Skill 会在 Claude `PostCompact` 后重新
147
+ 变为缺失。adapter 保留 append-only JSONL 作为审计记录,同时使用紧凑 snapshot,
148
+ 每次 Hook 只需回放 snapshot 之后的尾部事件。该账本便于审计,但并非防篡改账本。
149
+
150
+ 试用源码 alpha 时,先构建/安装该包,把示例 `miko.json` 复制到项目根目录,
151
+ 再把示例 Hook 合并进 `.claude/settings.json`。
152
+ 示例不会自动启用,因为目标项目中必须真实存在被强制要求的
153
+ `frontend-design` Skill。会话元数据写入 `.miko/state/`,该目录应保持忽略。
154
+ 旧 `.miko/contracts.json` 数组仍可读取,但不再是推荐的开发者入口。
155
+
156
+ 花费模型额度前,先运行完全离线的预检:
157
+
158
+ ```sh
159
+ npx koma-miko doctor
160
+ npx koma-miko doctor --strict --json
161
+ ```
162
+
163
+ Doctor 会验证 Agent Spec,并报告项目 Skill、Claude Hook 覆盖范围以及
164
+ `.miko/state/` 是否已忽略;它不会调用模型,也不会读取 API key。
165
+
166
+ Claude Code 本地 CLI、Desktop Code tab 与 VS Code/Cursor 扩展共享 settings、
167
+ Hooks 和 Skills;云端/远程会话的配置来源不同,企业策略也可能禁用项目 Hook,
168
+ 因此适配器应报告实际能力,而不是承诺所有表面完全一致。参见官方
169
+ [平台概览](https://code.claude.com/docs/en/platforms)、
170
+ [Desktop 共享配置](https://code.claude.com/docs/en/desktop) 与
171
+ [VS Code 配置](https://code.claude.com/docs/en/ide-integrations)。
172
+
173
+ ## 自动化回放
174
+
175
+ `npm run eval:replay -w koma-miko` 无需 API key,即可运行十个极简 Skill
176
+ 合约。每个 Skill 都验证三种情况:没有证据、agent 自述(`asserted`)以及
177
+ 宿主观察到的加载(`observed`)。只有 observed 或 external 证据能满足合约。
178
+ 第一个 UI 案例是窄范围强制演示,其余九个保持 review-only。
179
+ `npm run eval:claude-hook -w koma-miko` 还会启动三个彼此独立的 Hook 进程,
180
+ 验证带审计记录的 `DENY → 观察到 Skill → ALLOW`,并确认账本没有保存代码内容。
181
+ `npm run eval:scale -w koma-miko` 不需要 API key;它会测试 100/1,000 份
182
+ Agent Spec、10,000 条索引证据、100 份重叠 Spec、snapshot 恢复和终端输出上限。
183
+ `npm run eval:audit-demo -w koma-miko` 会用真实 Verifier 结果重新生成公开 CLI
184
+ 引导演示背后的 13 个事件;`eval:audit-demo:check` 只检查 fixture 是否过期。
185
+ 回放不包含 prompt、代码或模型回复,也不需要后端。终端故事只是展示层;可展开
186
+ 的原始事件仍保留 reason code、provenance 与 contract ID,供技术读者检查。
187
+
188
+ 父进程设置好 `ANTHROPIC_API_KEY` 后,
189
+ `npm run eval:claude-live -w koma-miko` 会运行一个可自动删除的真实 Claude Code
190
+ fixture。它只暴露 `Read`、`Edit` 与 `Skill`,默认使用 Haiku,单次硬上限为
191
+ `$0.10`,并验证:
192
+
193
+ ```text
194
+ Read → Miko DENY → Claude 加载 frontend-design → Miko 放行 → Edit
195
+ ```
196
+
197
+ fixture 建在包工作区内,使 Claude Code 把它视为项目内容,结束后删除。runner
198
+ 不会自行读取任何 env 文件。可用 `MIKO_LIVE_MODEL` 与
199
+ `MIKO_LIVE_MAX_BUDGET_USD` 覆盖默认值(runner 最高只接受 `$1`)。
200
+
201
+ 100 Skills 长上下文 fixture 应先离线检查,不消耗额度:
202
+
203
+ ```sh
204
+ npm run eval:claude-scale-dry -w koma-miko
205
+ ```
206
+
207
+ 然后再运行约 20k-token、硬上限 `$0.12` 的单次 Haiku 测试:
208
+
209
+ ```sh
210
+ MIKO_LIVE_CONTEXT_TOKENS=20000 \
211
+ MIKO_LIVE_MAX_BUDGET_USD=0.12 \
212
+ MIKO_LIVE_CAMPAIGN_BUDGET_USD=0.12 \
213
+ npm run eval:claude-scale-live -w koma-miko
214
+ ```
215
+
216
+ runner 最多接受三个以逗号分隔、范围为 1,000–190,000 的上下文档位;它会生成
217
+ 恰好 100 个项目 Skills,仅暴露 `Read`/`Edit`/`Skill`,在一次性目录运行并记录
218
+ cache、turn 与成本。runner 不会读取 env 文件。结果见
219
+ [alpha 评估记录](../../docs/evals/miko-claude-haiku-alpha.md)。
220
+
221
+ ## Alpha 边界
222
+
223
+ - **不调用 LLM,不做语义任务分类;**
224
+ - **不是 planner、router 或 agent runtime;**
225
+ - **不监控 context window 或 token;**
226
+ - **100-Skills fixture 仅在约 20k tokens 的一次 Haiku 测试中通过;这不能代表 100k、190k 或近百万 token 的行为;**
227
+ - **暂无托管遥测和云端策略服务;** Claude 适配器仅使用本地 JSONL 账本;
228
+ - **不自动改写工具调用;**
229
+ - **不声称“加载过 Skill”就等于模型理解、持续记住或遵守了 Skill。**
230
+
231
+ 研究案例、契约模型和 alpha 后问题见
232
+ [设计文档](../../docs/design/miko.md);产品定位与可执行 TODO 见
233
+ [开发者路线图](../../docs/design/miko-roadmap.md)。
@@ -0,0 +1,42 @@
1
+ import type { AdvanceContextResult, EvidenceEvent, Miko, RiskLevel, VerificationResult } from './index.js';
2
+ interface ClaudeHookBase {
3
+ session_id: string;
4
+ cwd: string;
5
+ hook_event_name: string;
6
+ }
7
+ export interface ClaudePreToolUseInput extends ClaudeHookBase {
8
+ hook_event_name: 'PreToolUse';
9
+ tool_name: string;
10
+ tool_input: Record<string, unknown>;
11
+ }
12
+ export interface ClaudePostToolUseInput extends ClaudeHookBase {
13
+ hook_event_name: 'PostToolUse';
14
+ tool_name: string;
15
+ tool_input: Record<string, unknown>;
16
+ }
17
+ export interface ClaudeUserPromptExpansionInput extends ClaudeHookBase {
18
+ hook_event_name: 'UserPromptExpansion';
19
+ expansion_type: 'slash_command' | 'mcp_prompt';
20
+ command_name: string;
21
+ }
22
+ export interface ClaudePostCompactInput extends ClaudeHookBase {
23
+ hook_event_name: 'PostCompact';
24
+ trigger?: 'manual' | 'auto';
25
+ }
26
+ export type ClaudeHookInput = ClaudePreToolUseInput | ClaudePostToolUseInput | ClaudeUserPromptExpansionInput | ClaudePostCompactInput | ClaudeHookBase;
27
+ export interface ClaudeHookHandlingResult {
28
+ output?: object;
29
+ evidence: EvidenceEvent[];
30
+ verification?: VerificationResult;
31
+ contextAdvance?: AdvanceContextResult;
32
+ }
33
+ export declare function toProjectRelativePath(value: string, cwd: string): string;
34
+ export declare function riskForClaudeTool(tool: string): RiskLevel;
35
+ export declare function evidenceFromClaudeEvent(input: ClaudeHookInput): EvidenceEvent[];
36
+ /**
37
+ * Handles the host-specific surface while leaving persistence to the caller.
38
+ * No prompt, file contents, Bash command, or model response is copied into evidence.
39
+ */
40
+ export declare function handleClaudeHookEvent(miko: Miko, taskId: string, input: ClaudeHookInput): ClaudeHookHandlingResult;
41
+ export {};
42
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../src/claude-code.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,IAAI,EACJ,SAAS,EACT,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAGpB,UAAU,cAAc;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,eAAe,EAAE,YAAY,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,eAAe,EAAE,aAAa,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,8BAA+B,SAAQ,cAAc;IACpE,eAAe,EAAE,qBAAqB,CAAC;IACvC,cAAc,EAAE,eAAe,GAAG,YAAY,CAAC;IAC/C,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,eAAe,EAAE,aAAa,CAAC;IAC/B,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,sBAAsB,GACtB,8BAA8B,GAC9B,sBAAsB,GACtB,cAAc,CAAC;AAEnB,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAQD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAYxE;AAmCD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAIzD;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,aAAa,EAAE,CAqC/E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,GACrB,wBAAwB,CAoC1B"}
@@ -0,0 +1,135 @@
1
+ import path from 'node:path';
2
+ import { formatMikoDecision, toClaudePreToolUseDecision } from './index.js';
3
+ const PATH_KEYS = ['file_path', 'path', 'filePath'];
4
+ function nonEmptyString(value) {
5
+ return typeof value === 'string' && value.trim().length > 0;
6
+ }
7
+ export function toProjectRelativePath(value, cwd) {
8
+ const normalizedValue = value.replace(/\\/g, '/');
9
+ const normalizedCwd = cwd.replace(/\\/g, '/').replace(/\/$/, '');
10
+ if (normalizedValue === normalizedCwd)
11
+ return '.';
12
+ if (normalizedValue.startsWith(`${normalizedCwd}/`)) {
13
+ return normalizedValue.slice(normalizedCwd.length + 1);
14
+ }
15
+ if (path.isAbsolute(value)) {
16
+ const relative = path.relative(cwd, value).replace(/\\/g, '/');
17
+ return relative.startsWith('../') || relative === '..' ? normalizedValue : relative;
18
+ }
19
+ return normalizedValue.replace(/^\.\//, '');
20
+ }
21
+ function relativeToolInput(input, cwd) {
22
+ const next = { ...input };
23
+ for (const key of PATH_KEYS) {
24
+ if (nonEmptyString(next[key]))
25
+ next[key] = toProjectRelativePath(next[key], cwd);
26
+ }
27
+ return next;
28
+ }
29
+ function privacySafeArguments(input, cwd) {
30
+ const safe = {};
31
+ for (const key of PATH_KEYS) {
32
+ if (nonEmptyString(input[key]))
33
+ safe[key] = toProjectRelativePath(input[key], cwd);
34
+ }
35
+ return Object.keys(safe).length > 0 ? safe : undefined;
36
+ }
37
+ function pathFromInput(input, cwd) {
38
+ const value = PATH_KEYS.map((key) => input[key]).find(nonEmptyString);
39
+ return value ? toProjectRelativePath(value, cwd) : undefined;
40
+ }
41
+ function skillFromToolInput(input) {
42
+ return ['skill', 'name', 'command_name']
43
+ .map((key) => input[key])
44
+ .find(nonEmptyString);
45
+ }
46
+ function skillFromReadPath(filePath) {
47
+ const parts = filePath.replace(/\\/g, '/').split('/').filter(Boolean);
48
+ if (parts.at(-1)?.toLowerCase() !== 'skill.md' || parts.length < 2)
49
+ return undefined;
50
+ return parts.at(-2);
51
+ }
52
+ export function riskForClaudeTool(tool) {
53
+ if (tool === 'Bash' || tool.startsWith('mcp__'))
54
+ return 'high';
55
+ if (tool === 'Edit' || tool === 'Write')
56
+ return 'medium';
57
+ return 'low';
58
+ }
59
+ export function evidenceFromClaudeEvent(input) {
60
+ if (input.hook_event_name === 'UserPromptExpansion') {
61
+ const event = input;
62
+ if (event.expansion_type === 'slash_command' && nonEmptyString(event.command_name)) {
63
+ return [{ type: 'skill_loaded', name: event.command_name, source: 'observed' }];
64
+ }
65
+ return [];
66
+ }
67
+ if (input.hook_event_name !== 'PostToolUse')
68
+ return [];
69
+ const event = input;
70
+ const evidence = [];
71
+ if (event.tool_name === 'Skill') {
72
+ const skill = skillFromToolInput(event.tool_input);
73
+ if (skill)
74
+ evidence.push({ type: 'skill_loaded', name: skill, source: 'observed' });
75
+ }
76
+ const filePath = pathFromInput(event.tool_input, event.cwd);
77
+ if (event.tool_name === 'Read' && filePath) {
78
+ evidence.push({ type: 'reference_read', path: filePath, source: 'observed' });
79
+ const skill = skillFromReadPath(filePath);
80
+ if (skill)
81
+ evidence.push({ type: 'skill_loaded', name: skill, source: 'observed' });
82
+ }
83
+ if ((event.tool_name === 'Edit' || event.tool_name === 'Write') && filePath) {
84
+ evidence.push({ type: 'artifact_changed', path: filePath, source: 'observed' });
85
+ }
86
+ evidence.push({
87
+ type: 'tool_succeeded',
88
+ tool: event.tool_name,
89
+ ...(privacySafeArguments(event.tool_input, event.cwd)
90
+ ? { arguments: privacySafeArguments(event.tool_input, event.cwd) }
91
+ : {}),
92
+ source: 'observed',
93
+ });
94
+ return evidence;
95
+ }
96
+ /**
97
+ * Handles the host-specific surface while leaving persistence to the caller.
98
+ * No prompt, file contents, Bash command, or model response is copied into evidence.
99
+ */
100
+ export function handleClaudeHookEvent(miko, taskId, input) {
101
+ const evidence = evidenceFromClaudeEvent(input);
102
+ for (const event of evidence)
103
+ miko.record({ taskId, ...event });
104
+ if (input.hook_event_name === 'PostCompact') {
105
+ return {
106
+ evidence,
107
+ contextAdvance: miko.advanceContext(taskId, 'compaction'),
108
+ };
109
+ }
110
+ if (input.hook_event_name === 'PreToolUse') {
111
+ const event = input;
112
+ if (event.tool_name === 'Skill')
113
+ return { evidence };
114
+ const verification = miko.verifyAction({
115
+ taskId,
116
+ tool: event.tool_name,
117
+ risk: riskForClaudeTool(event.tool_name),
118
+ arguments: relativeToolInput(event.tool_input, event.cwd),
119
+ });
120
+ return { output: toClaudePreToolUseDecision(verification), evidence, verification };
121
+ }
122
+ if (input.hook_event_name === 'Stop') {
123
+ const verification = miko.verifyCompletion(taskId);
124
+ if (verification.decision !== 'ALLOW') {
125
+ return {
126
+ output: { systemMessage: formatMikoDecision(verification) },
127
+ evidence,
128
+ verification,
129
+ };
130
+ }
131
+ return { evidence, verification };
132
+ }
133
+ return { evidence };
134
+ }
135
+ //# sourceMappingURL=claude-code.js.map