@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 +14 -1
- package/dist/adapters/kimi/index.d.mts +79 -0
- package/dist/adapters/kimi/index.mjs +2 -0
- package/dist/cli/bin.mjs +632 -4
- package/dist/config/index.d.mts +1 -1
- package/dist/config/index.mjs +1 -1
- package/dist/detect/index.d.mts +1 -1
- package/dist/detect/index.mjs +1 -1
- package/dist/{dotenv-Jj8aL1FL.mjs → dotenv-B26N_uGr.mjs} +2 -1
- package/dist/{handle-Dthtlp7Z.mjs → handle-B7YufFz9.mjs} +46 -23
- package/dist/{harness-Cb9xR8dC.mjs → harness-BMuLJ9lm.mjs} +4 -2
- package/dist/{harness-s1Fce_dL.d.mts → harness-BQ5_Wnva.d.mts} +1 -1
- package/dist/{index-jj_EzZRv.d.mts → index-B_VHbsaJ.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/init/index.d.mts +1 -1
- package/dist/kimi-C7FGprQz.mjs +80 -0
- package/dist/policy/index.mjs +1 -1
- package/dist/runtime/index.d.mts +10 -3
- package/dist/runtime/index.mjs +1 -1
- package/dist/{skill-path-BO2N0XvB.mjs → skill-path-CDvR4bbF.mjs} +2 -2
- package/dist/{store-Bcn08y9m.mjs → store-nDZ3eLBQ.mjs} +2 -2
- package/dist/tracking/index.mjs +1 -1
- package/dist/{validate-DhOX5hDK.mjs → validate-CkmquRDV.mjs} +72 -15
- package/package.json +7 -2
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 };
|