@fusengine/harness 0.1.78 → 0.1.80

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.
package/README.md CHANGED
@@ -130,6 +130,7 @@ also works elsewhere.
130
130
  | **gemini-cli** | `BeforeTool` denies via `{decision:"deny",reason}` (`gemini/index.ts:22-36`) | none | Thin stateless adapter — no session track, no APEX gates wired through it. |
131
131
  | **cline** | `PreToolUse` only; block → `{cancel:true}`, non-block → `contextModification` (`cline/index.ts:24-36`) | none | Same as gemini-cli: stateless guard only. |
132
132
  | **hermes** | `pre_tool_call` proven: reuses the Claude stdin reader, blocks via `{decision:"block",reason}` (`hermes/index.ts:12-36`) | untested — no lifecycle dispatch wired for Hermes in this repo | `ask`/`inform` degrade to non-blocking `{context}` — Hermes "has no interactive ask state" (`hermes/index.ts:27-28`). |
133
+ | **kimi** | `PreToolUse` denies via the camelCase JSON channel `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"…"}}` on stdout at **exit 0** (verified live against kimi-code v0.27.0 — exit 2 with stderr = reason also blocks, but is not required) — only `deny` is documented. `ask` is **downgraded to deny** prefixed `[downgraded from ask — Kimi Code has no interactive approval]`; `inform` rides plain stdout text at exit 0, never wrapped in JSON. Blocking events: `UserPromptSubmit`, `PreToolUse`, `Stop` (`kimi/index.ts`). | Observation only: `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionResult`, `SessionStart`, `SessionEnd`, `SubagentStart`, `SubagentStop`, `StopFailure`, `Interrupt`, `PreCompact`, `PostCompact`, `Notification` — Kimi delivers them but **ignores any response**, so no verdict can be returned from them. | A **hook** cannot request approval — Kimi's `ask` lives in a parallel, hook-unreachable system (`[[permission.rules]] decision = "ask"` in `config.toml`), hence the ask→deny downgrade. Hooks are configured **only** in the global `~/.kimi-code/config.toml` (no project-local hooks file), so `harness init` writes no kimi wiring — copy the TOML snippet from [docs/adapters.md](docs/adapters.md#kimi-code--manual-wiring), and emit **no field** beyond `event`/`matcher`/`command`/`timeout` or the whole config fails to load. **Fail-open by design**: any exit code other than 0/2, a timeout, or a crash lets the call through. Stdin payload is snake_case and carries only `hook_event_name`, `session_id`, `cwd`, `tool_name`, `tool_input.command` and an undocumented `tool_call_id` (unused here) — no `transcript_path`, no `permission_mode`, no `tool_response`. Verified live against kimi-code v0.27.0 for `PreToolUse`/`Bash`; validate hand-written config with `kimi doctor`. Instructions file is `AGENTS.md`, not `CLAUDE.md`. |
133
134
 
134
135
  ## What it enforces
135
136
 
@@ -191,6 +192,14 @@ Features shipped since 0.1.44, each with its own test:
191
192
  version + drift vs. npm, `.claude/BOARD.md`, and the one-shot summary, each
192
193
  collector isolated so one failure can't blank the rest
193
194
  (`src/runtime/lifecycle/snapshot/index.ts`, `test/snapshot.test.ts`).
195
+ - **Target-aware root-doc injection** — `SessionStart` and `UserPromptSubmit`
196
+ inject the target's own root instructions doc, not a hardcoded `CLAUDE.md`:
197
+ `apexDocName(id)` + `harnessHomeSegment(id)` (`src/policy/apex-target.ts`)
198
+ resolve `AGENTS.md` under `.codex`/`.kimi-code` for Codex/Kimi, `CLAUDE.md`
199
+ under `.claude` for every other target (byte-identical to the pre-target-aware
200
+ default). The `systemMessage` label follows suit (`"AGENTS.md injected"`
201
+ under Codex/Kimi) (`src/runtime/lifecycle/session-start.ts`,
202
+ `src/runtime/inject-context.ts`).
194
203
  - **Injection budget cap** — harness-produced context fragments (lessons,
195
204
  snapshot, APEX task context) are capped at ~8000 chars each; owner-authored
196
205
  content (CLAUDE.md/rules) is never capped
@@ -202,7 +211,11 @@ Features shipped since 0.1.44, each with its own test:
202
211
  carrying `tool_input.agent_type` is recorded as `subagent-<agent_type>` in the
203
212
  same SessionTrack as Claude Task evidence, so a Codex explore-codebase/sniper
204
213
  satisfies the APEX freshness gates exactly like a Claude subagent
205
- (`src/tracking/session-state.ts`, sim scenario 32b).
214
+ (`src/tracking/session-state.ts`, sim scenario 32b). Recognized
215
+ agent-launcher tools across targets: `Task` (Claude), `Agent`/`AgentSwarm`
216
+ (Kimi Code) — each credits its `subagent_type`/`agent_type` toward the same
217
+ freshness evidence (`src/runtime/activity.ts`,
218
+ `src/freshness/agent-evidence-record.ts`).
206
219
  - **File-size gate judges the real Edit outcome, not the on-disk count** — an
207
220
  Edit on a file already over `FUSE_SOLID_MAX_LINES` used to be denied
208
221
  unconditionally (the on-disk count always won); the gate now computes the
@@ -0,0 +1,79 @@
1
+ import { t as Prompt } from "../../types-DVbIl9md.mjs";
2
+
3
+ //#region src/adapters/kimi/interfaces/types.d.ts
4
+ /**
5
+ * Kimi Code CLI shell-hook wire types. Verified 2026 against
6
+ * kimi.com/code/docs/en/kimi-code-cli/customization/hooks.html (v0.27.0):
7
+ * hooks are configured in `~/.kimi-code/config.toml` under `[[hooks]]`,
8
+ * receive a snake_case JSON payload on stdin, and answer with a camelCase
9
+ * JSON envelope on stdout (a real, documented casing inconsistency).
10
+ */
11
+ /**
12
+ * `PreToolUse`/`UserPromptSubmit`/`Stop` stdin payload (subset). Same base
13
+ * wire shape as Claude Code (`hook_event_name`/`tool_name`/`tool_input`/
14
+ * `cwd`); `tool_input.command` is confirmed for `Bash` by the docs' example
15
+ * script (`payload.tool_input?.command`). Verified live against kimi-code
16
+ * v0.27.0 on `PreToolUse`/`Bash`: `tool_name` is present at top level (the
17
+ * docs never name it as a stdin key) and `tool_input.command` carries the
18
+ * shell string. The live payload also carries an UNDOCUMENTED `tool_call_id`
19
+ * (e.g. `"tool_LsTnl0gBxM62XuWvKw0GFGHs"`); the harness does not consume it,
20
+ * so it is deliberately absent from this interface. `cwd` is the
21
+ * symlink-resolved path (macOS: `/private/tmp/…` for a shell in `/tmp/…`).
22
+ */
23
+ interface KimiHookInput {
24
+ hook_event_name?: string;
25
+ tool_name?: string;
26
+ /**
27
+ * Verified against the official tool reference (`docs/en/reference/tools.md`,
28
+ * moonshotai/kimi-code) AND live against kimi-code v0.27.0: `Write` sends
29
+ * `{ path, content, mode? }`, `Edit` sends
30
+ * `{ path, old_string, new_string, replace_all? }` — the path key is `path`,
31
+ * NOT Claude's `file_path`. `file_path` is kept as a tolerated alias (read
32
+ * second) for Claude-shaped replay payloads, mirroring
33
+ * `runtime/normalize.ts` which resolves both on the CLI path.
34
+ */
35
+ tool_input?: {
36
+ path?: string;
37
+ file_path?: string;
38
+ content?: string;
39
+ new_string?: string;
40
+ command?: string | string[];
41
+ };
42
+ session_id?: string;
43
+ cwd?: string;
44
+ }
45
+ /**
46
+ * Hook stdout response. Only `"deny"` is documented for `permissionDecision`
47
+ * — no `"ask"`/`"allow"` value, and no `additionalContext` field exists on
48
+ * this envelope. Blocking events are `UserPromptSubmit`, `PreToolUse`, and
49
+ * `Stop` only; every other event's return value is ignored.
50
+ */
51
+ interface KimiResponse {
52
+ hookSpecificOutput?: {
53
+ permissionDecision?: "deny";
54
+ permissionDecisionReason?: string;
55
+ };
56
+ }
57
+ //#endregion
58
+ //#region src/adapters/kimi/index.d.ts
59
+ /** Read & parse the Kimi hook stdin payload (same wire shape as Claude for the fields we consume). */
60
+ declare function readKimiInput(): Promise<KimiHookInput>;
61
+ /**
62
+ * Render a portable {@link Prompt} as a Kimi hook response string:
63
+ * `block`/`ask` -> `{hookSpecificOutput:{permissionDecision:"deny",...}}`
64
+ * (only `"deny"` is documented; `ask` is downgraded with {@link ASK_PREFIX});
65
+ * `inform` -> raw text (Kimi appends stdout to context on exit 0 — no
66
+ * documented JSON envelope for non-blocking context injection).
67
+ * @param prompt - The portable policy prompt.
68
+ * @returns The native Kimi response string.
69
+ */
70
+ declare function toKimiResponse(prompt: Prompt): string;
71
+ /**
72
+ * Run the bundled policy over a Kimi hook payload and return the native
73
+ * response string, or null to allow (the hook then exits 0 with empty stdout).
74
+ * @param input - Parsed hook stdin payload.
75
+ * @returns Native response string, or null when the tool use is allowed.
76
+ */
77
+ declare function guard(input: KimiHookInput): string | null;
78
+ //#endregion
79
+ export { type KimiHookInput, type KimiResponse, guard, readKimiInput, toKimiResponse };
@@ -0,0 +1,2 @@
1
+ import { n as readKimiInput, r as toKimiResponse, t as guard } from "../../kimi-C7FGprQz.mjs";
2
+ export { guard, readKimiInput, toKimiResponse };