@halofy/agent-connect 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -19,7 +19,7 @@ agent connections:
19
19
  There is one setup path for every packaged client:
20
20
 
21
21
  ```bash
22
- npx --yes @halofy/agent-connect@0.2.0 install <client-kind> \
22
+ npx --yes @halofy/agent-connect@0.3.0 install <client-kind> \
23
23
  --server https://app.halofy.ai \
24
24
  --claim '<one-time-claim>'
25
25
  ```
@@ -32,6 +32,7 @@ permits only these reviewed client kinds:
32
32
  | `claude-code` | complete for declared text/tool lifecycle hooks | image bodies, artifact bodies, context-use evidence |
33
33
  | `cursor` | complete for declared text/tool lifecycle hooks | image bodies, artifact bodies, context-use evidence |
34
34
  | `gemini-cli` | complete for declared text/tool lifecycle hooks | image bodies, artifact bodies, subagents, context-use evidence |
35
+ | `kimi-cli` | partial | assistant responses, image and artifact bodies, context-use evidence |
35
36
  | `codex` | partial | assistant responses, tool failures, session end, binary bodies |
36
37
  | `vscode` | partial | assistant responses, binary bodies, context-use evidence |
37
38
  | `cline` | partial | assistant responses, tool failures, subagents, compaction, session end, binary bodies |
@@ -60,7 +61,7 @@ and query rules. Publication is fail-closed until the package tarball, current
60
61
  Claude fixtures, cross-implementation signature fixtures, and deployed server
61
62
  protocol have all passed for the exact version. Server versions configured
62
63
  with the Claude-only `0.1.x` artifact keep every additional client fenced as
63
- `Not supported yet`.
64
+ `Not supported yet`; `0.2.x` keeps Kimi fenced until its `0.3.x` adapter.
64
65
 
65
66
  Every packaged adapter advertises archive protocol v1 explicitly. The Claude
66
67
  adapter's current
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halofy/agent-connect",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Signed Halofy lifecycle installer and runtime for supported agents",
6
6
  "bin": {
@@ -5,6 +5,7 @@ export const CLIENT_KINDS = Object.freeze([
5
5
  "vscode",
6
6
  "cline",
7
7
  "gemini-cli",
8
+ "kimi-cli",
8
9
  ]);
9
10
 
10
11
  const BASE_CAPABILITIES = Object.freeze({
@@ -80,6 +81,21 @@ export const CLIENT_REGISTRY = Object.freeze({
80
81
  compactionCheckpoints: true, contextRecalled: true,
81
82
  }),
82
83
  }),
84
+ "kimi-cli": Object.freeze({
85
+ clientKind: "kimi-cli",
86
+ label: "Kimi Code CLI",
87
+ command: "kimi",
88
+ coverage: "partial",
89
+ reason: "assistant_message_hook_unavailable",
90
+ capabilities: capabilities({
91
+ sessionStart: true, userPromptSubmit: true, stop: true, preCompact: true,
92
+ sessionEnd: true, subagentStart: true, subagentStop: true,
93
+ postToolUse: true, postToolUseFailure: true, userMessages: true,
94
+ toolInputs: true, toolOutputs: true, toolFailures: true,
95
+ artifactReferences: true, subagents: true, compactionCheckpoints: true,
96
+ contextRecalled: true,
97
+ }),
98
+ }),
83
99
  vscode: Object.freeze({
84
100
  clientKind: "vscode",
85
101
  label: "VS Code",
@@ -137,6 +137,68 @@ export async function configureGemini({
137
137
  return { configuredPaths: [settingsPath], hookEvents: Object.keys(GEMINI_HOOKS), replacedLegacyMcpEntries };
138
138
  }
139
139
 
140
+ const KIMI_HOOKS = Object.freeze({
141
+ SessionStart: "SessionStart",
142
+ UserPromptSubmit: "UserPromptSubmit",
143
+ Stop: "Stop",
144
+ PostToolUse: "PostToolUse",
145
+ PostToolUseFailure: "PostToolUseFailure",
146
+ PreCompact: "PreCompact",
147
+ SessionEnd: "SessionEnd",
148
+ SubagentStart: "SubagentStart",
149
+ SubagentStop: "SubagentStop",
150
+ });
151
+
152
+ const KIMI_MANAGED_BEGIN = "# BEGIN HALOFY LIFECYCLE";
153
+ const KIMI_MANAGED_END = "# END HALOFY LIFECYCLE";
154
+
155
+ function replaceKimiManagedHooks(source, replacement) {
156
+ const text = String(source || "");
157
+ const start = text.indexOf(KIMI_MANAGED_BEGIN);
158
+ const end = text.indexOf(KIMI_MANAGED_END);
159
+ if ((start === -1) !== (end === -1) || (start !== -1 && end < start)) {
160
+ throw new Error("existing Halofy Kimi hook configuration is malformed");
161
+ }
162
+ const withoutManaged = start === -1
163
+ ? text
164
+ : `${text.slice(0, start)}${text.slice(end + KIMI_MANAGED_END.length)}`;
165
+ const body = withoutManaged.trimEnd();
166
+ return `${body ? `${body}\n\n` : ""}${replacement}\n`;
167
+ }
168
+
169
+ export async function configureKimi({
170
+ installationId,
171
+ nodePath = process.execPath,
172
+ runtimePath,
173
+ kimiRoot = process.env.KIMI_CODE_HOME || join(homedir(), ".kimi-code"),
174
+ }) {
175
+ validateInputs({ installationId, runtimePath });
176
+ const configPath = join(kimiRoot, "config.toml");
177
+ let source = "";
178
+ try { source = await readFile(configPath, "utf8"); } catch (error) { if (error?.code !== "ENOENT") throw error; }
179
+ const hookBlock = [
180
+ KIMI_MANAGED_BEGIN,
181
+ ...Object.entries(KIMI_HOOKS).flatMap(([hostEvent, runtimeEvent]) => [
182
+ "[[hooks]]",
183
+ `event = ${JSON.stringify(hostEvent)}`,
184
+ `command = ${JSON.stringify(managedCommand(nodePath, runtimePath, runtimeEvent, installationId))}`,
185
+ "timeout = 20",
186
+ "",
187
+ ]),
188
+ KIMI_MANAGED_END,
189
+ ].join("\n");
190
+ const mcpPath = join(kimiRoot, "mcp.json");
191
+ const mcp = await readJson(mcpPath, {});
192
+ mcp.mcpServers = mcp.mcpServers && typeof mcp.mcpServers === "object" ? mcp.mcpServers : {};
193
+ const replacedLegacyMcpEntries = replaceHalofyMcpEntries(
194
+ mcp.mcpServers,
195
+ managedMcp(nodePath, runtimePath, installationId),
196
+ );
197
+ await writePrivateFile(configPath, replaceKimiManagedHooks(source, hookBlock));
198
+ await writePrivateFile(mcpPath, `${JSON.stringify(mcp, null, 2)}\n`);
199
+ return { configuredPaths: [configPath, mcpPath], hookEvents: Object.keys(KIMI_HOOKS), replacedLegacyMcpEntries };
200
+ }
201
+
140
202
  const VSCODE_HOOKS = Object.freeze({
141
203
  SessionStart: "SessionStart",
142
204
  UserPromptSubmit: "UserPromptSubmit",
@@ -10,7 +10,7 @@ import {
10
10
  localMcpSnippet,
11
11
  } from "./install.mjs";
12
12
  import { configureClaudeProject } from "./claude-config.mjs";
13
- import { configureCline, configureCodex, configureCursor, configureGemini, configureVscode } from "./host-config.mjs";
13
+ import { configureCline, configureCodex, configureCursor, configureGemini, configureKimi, configureVscode } from "./host-config.mjs";
14
14
  import { CLIENT_KINDS, lifecycleClient } from "./client-registry.mjs";
15
15
  import { defaultRuntimeDirectory } from "./storage.mjs";
16
16
  import { DISCLOSURE_VERSION, INSTALLER_VERSION } from "./version.mjs";
@@ -172,13 +172,15 @@ export async function runInstaller(argv, {
172
172
  ? await configureCursor(common)
173
173
  : input.clientKind === "gemini-cli"
174
174
  ? await configureGemini(common)
175
- : input.clientKind === "vscode"
176
- ? await configureVscode(common)
177
- : input.clientKind === "codex"
178
- ? await configureCodex(common)
179
- : input.clientKind === "cline"
180
- ? await configureCline(common)
181
- : (() => { throw new Error("the selected adapter is not packaged yet"); })();
175
+ : input.clientKind === "kimi-cli"
176
+ ? await configureKimi(common)
177
+ : input.clientKind === "vscode"
178
+ ? await configureVscode(common)
179
+ : input.clientKind === "codex"
180
+ ? await configureCodex(common)
181
+ : input.clientKind === "cline"
182
+ ? await configureCline(common)
183
+ : (() => { throw new Error("the selected adapter is not packaged yet"); })();
182
184
  let heartbeat = false;
183
185
  try {
184
186
  heartbeat = await heartbeatInstalledConnection({
package/src/runtime.mjs CHANGED
@@ -4,6 +4,7 @@ import { BoundedEncryptedQueue } from "./queue.mjs";
4
4
  import { CursorStore, deriveSessionHash, readClaudeTranscriptSuffix } from "./session.mjs";
5
5
  import { SignedRuntimeTransport } from "./transport.mjs";
6
6
  import { withFileLock } from "./storage.mjs";
7
+ import { RUNTIME_VERSION } from "./version.mjs";
7
8
 
8
9
  export class LifecycleRuntime {
9
10
  constructor(connection, {
@@ -255,7 +256,7 @@ export class LifecycleRuntime {
255
256
  async heartbeat(capabilities) {
256
257
  const queue = await this.queue.diagnostics();
257
258
  return this.transport.heartbeat(capabilities, {
258
- pluginVersion: this.connection.pluginVersion || "0.2.0-local",
259
+ pluginVersion: this.connection.pluginVersion || `${RUNTIME_VERSION}-local`,
259
260
  proofStorage: this.connection.proofStorage || "unknown",
260
261
  diagnostics: {
261
262
  queueDepth: queue.depth,
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  export const PACKAGE_NAME = "@halofy/agent-connect";
2
- export const INSTALLER_VERSION = "0.2.0";
3
- export const RUNTIME_VERSION = "0.2.0";
2
+ export const INSTALLER_VERSION = "0.3.0";
3
+ export const RUNTIME_VERSION = "0.3.0";
4
4
  export const DISCLOSURE_VERSION = "halofy-agent-lifecycle-2026-08-29";