@zooid/acp-client 0.12.0 → 0.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zooid/acp-client",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Zooid's ACP client: spawn agent subprocesses, drive them via Agent Client Protocol, route events and permission requests through callbacks.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,7 +53,7 @@ describe('AcpClient — context MCP wiring', () => {
53
53
  })
54
54
  injectConnection(client, calls)
55
55
  await client.ensureSession('!room:hs')
56
- expect(contextSpawn).toHaveBeenCalledWith('!room:hs', undefined)
56
+ expect(contextSpawn).toHaveBeenCalledWith('!room:hs', undefined, '!room:hs')
57
57
  const params = calls.find((c) => c.method === 'newSession')!.params as {
58
58
  mcpServers: Array<{ name: string; args: string[] }>
59
59
  }
@@ -80,7 +80,12 @@ describe('AcpClient — context MCP wiring', () => {
80
80
  })
81
81
  const conn = injectConnection(client, calls)
82
82
  // @ts-expect-error
83
- client.store = { load: async () => {}, get: () => 'sess-old', set: async () => {}, delete: async () => {} }
83
+ client.store = {
84
+ load: async () => {},
85
+ get: () => 'sess-old',
86
+ set: async () => {},
87
+ delete: async () => {},
88
+ }
84
89
  // @ts-expect-error
85
90
  client.storeLoaded = Promise.resolve()
86
91
  await client.ensureSession('!room:hs')
@@ -109,7 +114,7 @@ describe('AcpClient — context MCP wiring', () => {
109
114
  // Handoff-arc session: key is composed, context ref is the real root —
110
115
  // zooid_get_history must read the real Matrix thread.
111
116
  await client.ensureSession('$root|$p1', '!r:example.com', '$root')
112
- expect(contextSpawn).toHaveBeenCalledWith('$root', '!r:example.com')
117
+ expect(contextSpawn).toHaveBeenCalledWith('$root', '!r:example.com', '$root|$p1')
113
118
  })
114
119
 
115
120
  it('defaults the context ref to the session key when contextThreadId is omitted (back-compat)', async () => {
@@ -128,6 +133,6 @@ describe('AcpClient — context MCP wiring', () => {
128
133
  })
129
134
  injectConnection(client, calls)
130
135
  await client.ensureSession('$root', '!r:example.com')
131
- expect(contextSpawn).toHaveBeenCalledWith('$root', '!r:example.com')
136
+ expect(contextSpawn).toHaveBeenCalledWith('$root', '!r:example.com', '$root')
132
137
  })
133
138
  })
package/src/acp-client.ts CHANGED
@@ -11,10 +11,7 @@ import { AgentProcess } from './agent-process.js'
11
11
  import { SessionMap } from './session-map.js'
12
12
  import { JsonFileSessionStore } from './session-store.js'
13
13
  import { resolvePreset } from './presets.js'
14
- import {
15
- acpUpdateToAgentEvent,
16
- approvalDecisionToPermissionResponse,
17
- } from './event-mapping.js'
14
+ import { acpUpdateToAgentEvent, approvalDecisionToPermissionResponse } from './event-mapping.js'
18
15
  import { TurnTracker, type TapEvent } from './turn-tracker.js'
19
16
  import { classify } from './errors.js'
20
17
  import type {
@@ -73,7 +70,11 @@ export interface AcpClientOptions {
73
70
  * connects to the daemon-side zooid-context MCP server for the session
74
71
  * lifetime. Called once per `ensureSession(threadId)`.
75
72
  */
76
- contextSpawn?: (threadId: string, channelId?: string) => Promise<{
73
+ contextSpawn?: (
74
+ threadId: string,
75
+ channelId?: string,
76
+ sessionKey?: string,
77
+ ) => Promise<{
77
78
  name: 'zooid-context'
78
79
  command: string
79
80
  args: string[]
@@ -177,13 +178,19 @@ export class AcpClient {
177
178
  if (cached) return cached.sessionId
178
179
 
179
180
  const mcpServers = this.options.contextSpawn
180
- ? [await this.options.contextSpawn(contextThreadId ?? threadId, channelId)]
181
+ ? [await this.options.contextSpawn(contextThreadId ?? threadId, channelId, threadId)]
181
182
  : []
182
183
  process.stderr.write(
183
184
  `[acp-client:${this.options.agent.id}] ensureSession(${threadId}) mcpServers=${
184
185
  mcpServers.length === 0
185
186
  ? '[]'
186
- : JSON.stringify(mcpServers.map((s) => ({ name: s.name, command: s.command, args: s.args })))
187
+ : JSON.stringify(
188
+ mcpServers.map((s) => ({
189
+ name: s.name,
190
+ command: s.command,
191
+ args: s.args,
192
+ })),
193
+ )
187
194
  }\n`,
188
195
  )
189
196
 
@@ -264,20 +271,22 @@ export class AcpClient {
264
271
  let sessionId: string | null = null
265
272
  let turnId: string | null = null
266
273
  try {
267
- sessionId = await this.ensureSession(
268
- input.threadId,
269
- input.channelId,
270
- input.contextThreadId,
271
- )
274
+ sessionId = await this.ensureSession(input.threadId, input.channelId, input.contextThreadId)
272
275
  const promptText = stringifyPromptForLog(input.content)
273
276
  turnId = this.turns?.startTurn({ sessionId, promptText }) ?? null
274
- debugLog(this.options.agent.id, 'prompt →', { sessionId, content: input.content })
277
+ debugLog(this.options.agent.id, 'prompt →', {
278
+ sessionId,
279
+ content: input.content,
280
+ })
275
281
  const result = await this.connection!.prompt({
276
282
  sessionId,
277
283
  prompt: input.content,
278
284
  })
279
285
  this.turns?.endTurn({ sessionId, stopReason: result.stopReason })
280
- debugLog(this.options.agent.id, 'prompt ←', { sessionId, stopReason: result.stopReason })
286
+ debugLog(this.options.agent.id, 'prompt ←', {
287
+ sessionId,
288
+ stopReason: result.stopReason,
289
+ })
281
290
  return { stopReason: result.stopReason }
282
291
  } catch (err) {
283
292
  const c = classify(err)