返回 DeepSeek-Reasonix
protocol.go
根目录 / internal / acp / protocol.go
1 // Package acp implements the Agent Client Protocol (https://agentclientprotocol.com)
2 // transport: a stdio JSON-RPC 2.0 agent that editors and other host clients speak
3 // to drive Reasonix. Many tools integrated with the v1 (main-branch) agent over
4 // ACP, so v2 keeps the wire contract identical — the wire types in this file are a
5 // faithful port of main's src/acp/protocol.ts (ACP protocol version 1).
6 //
7 // The package is an adapter layer over the v2 kernel and depends only on stable
8 // contracts: it maps the agent's typed event.Event stream onto session/update
9 // notifications (see dispatch.go), bridges permission.Approver onto
10 // session/request_permission round-trips (see permission.go), and exposes the
11 // whole thing over NDJSON JSON-RPC (see server.go). How a per-session agent is
12 // actually assembled — provider, tools rooted at the session cwd, per-session MCP
13 // — is left to a Factory the composition root supplies (see service.go), so this
14 // package stays independent of the cli wiring.
15 package acp
16
17 import (
18 "encoding/json"
19 "fmt"
20 "strings"
21 )
22
23 // ProtocolVersion is the ACP version this agent implements. Matches main.
24 const ProtocolVersion = 1
25
26 // JSON-RPC 2.0 error codes (subset used on the wire). Mirrors protocol.ts.
27 const (
28 ErrParse = -32700
29 ErrInvalidRequest = -32600
30 ErrMethodNotFound = -32601
31 ErrInvalidParams = -32602
32 ErrInternal = -32603
33 )
34
35 // initialize
36
37 // InitializeParams is the client's handshake. The agent records the client's
38 // capabilities — fs read/write proxying and host terminals are used when
39 // offered — and advertises its own fixed capability set in reply.
40 type InitializeParams struct {
41 ProtocolVersion int `json:"protocolVersion"`
42 ClientInfo *Implementation `json:"clientInfo,omitempty"`
43 ClientCapabilities ClientCapabilities `json:"clientCapabilities,omitempty"`
44 }
45
46 // ClientCapabilities is what the client offers the agent: filesystem proxy
47 // methods (fs/read_text_file, fs/write_text_file) that see unsaved editor
48 // buffers, and host-owned terminals (terminal/*). Meta carries vendor
49 // capability blocks (e.g. _meta["reasonix.io"]) for tolerant parse — unknown
50 // or malformed entries simply mean the vendor feature stays off.
51 type ClientCapabilities struct {
52 FS FSCapabilities `json:"fs,omitempty"`
53 Terminal bool `json:"terminal,omitempty"`
54 Meta map[string]any `json:"_meta,omitempty"`
55 }
56
57 // FSCapabilities reports which client filesystem methods are available.
58 type FSCapabilities struct {
59 ReadTextFile bool `json:"readTextFile,omitempty"`
60 WriteTextFile bool `json:"writeTextFile,omitempty"`
61 }
62
63 // Implementation names a participant (client or agent) on the wire.
64 type Implementation struct {
65 Name string `json:"name"`
66 Title string `json:"title,omitempty"`
67 Version string `json:"version,omitempty"`
68 }
69
70 // InitializeResult advertises what this agent supports: persisted session load,
71 // ACP v1 session lifecycle helpers, inline resource text (embeddedContext) but
72 // not image/audio, and stdio / Streamable HTTP MCP (no legacy sse).
73 type InitializeResult struct {
74 ProtocolVersion int `json:"protocolVersion"`
75 AgentCapabilities AgentCapabilities `json:"agentCapabilities"`
76 AgentInfo Implementation `json:"agentInfo"`
77 AuthMethods []AuthMethod `json:"authMethods"`
78 }
79
80 // AgentCapabilities is the agentCapabilities object in InitializeResult.
81 type AgentCapabilities struct {
82 LoadSession bool `json:"loadSession"`
83 SessionCapabilities SessionCapabilities `json:"sessionCapabilities,omitempty"`
84 PromptCapabilities PromptCapabilities `json:"promptCapabilities"`
85 MCPCapabilities MCPCapabilities `json:"mcpCapabilities"`
86 Meta map[string]any `json:"_meta,omitempty"`
87 }
88
89 // SessionSteerCapability identifies the vendor-namespaced steering method.
90 type SessionSteerCapability struct {
91 Method string `json:"method"`
92 }
93
94 // SessionInboxCapability advertises durable inbox methods (schemaVersion 1).
95 type SessionInboxCapability struct {
96 SchemaVersion int `json:"schemaVersion"`
97 Methods map[string]string `json:"methods"`
98 }
99
100 // SessionReloadExtensionsCapability identifies the vendor-namespaced runtime
101 // reload method.
102 type SessionReloadExtensionsCapability struct {
103 Method string `json:"method"`
104 }
105
106 const (
107 // reasonixExtensionSurfaceSchemaVersion versions the extension-surface DTO
108 // carried by the vendor session/update variant.
109 reasonixExtensionSurfaceSchemaVersion = 1
110 // extensionSurfaceUpdateKind discriminates the vendor session/update
111 // variant that carries a structured extension-UI surface.
112 extensionSurfaceUpdateKind = "_reasonix.io/extension_surface"
113 )
114
115 // ExtensionSurfaceCapability advertises that a participant renders structured
116 // extension-UI surfaces (Extension Protocol v2) natively.
117 type ExtensionSurfaceCapability struct {
118 Supported bool `json:"supported"`
119 SchemaVersion int `json:"schemaVersion"`
120 }
121
122 // EmptyCapability serializes to {} for ACP capability flags.
123 type EmptyCapability struct{}
124
125 // SessionCapabilities advertises optional session lifecycle methods.
126 type SessionCapabilities struct {
127 List *EmptyCapability `json:"list,omitempty"`
128 Resume *EmptyCapability `json:"resume,omitempty"`
129 Close *EmptyCapability `json:"close,omitempty"`
130 Delete *EmptyCapability `json:"delete,omitempty"`
131 }
132
133 // PromptCapabilities reports which content-block kinds prompts may carry.
134 type PromptCapabilities struct {
135 Image bool `json:"image"`
136 Audio bool `json:"audio"`
137 EmbeddedContext bool `json:"embeddedContext"`
138 }
139
140 // MCPCapabilities reports which MCP transports session/new accepts.
141 type MCPCapabilities struct {
142 HTTP bool `json:"http"`
143 SSE bool `json:"sse"`
144 }
145
146 // AuthMethod advertises how a client can prepare credentials for the agent.
147 type AuthMethod struct {
148 ID string `json:"id"`
149 Name string `json:"name"`
150 Description string `json:"description,omitempty"`
151 Type string `json:"type,omitempty"`
152 Args []string `json:"args,omitempty"`
153 Env map[string]string `json:"env,omitempty"`
154 }
155
156 // AuthenticateParams selects one advertised auth method. Terminal methods are
157 // normally handled by the client by launching the agent with the method's args;
158 // accepting this request keeps clients that call authenticate directly working.
159 type AuthenticateParams struct {
160 MethodID string `json:"methodId"`
161 }
162
163 // AuthenticateResult is the empty authentication ack.
164 type AuthenticateResult struct{}
165
166 // session/new
167
168 // SessionNewParams opens a session rooted at cwd, optionally with MCP servers
169 // the agent should connect for the session's lifetime.
170 type SessionNewParams struct {
171 Cwd string `json:"cwd,omitempty"`
172 MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
173 }
174
175 // MCPServerSpec describes one MCP server the client asks the agent to connect.
176 type MCPServerSpec struct {
177 Name string `json:"name"`
178 Type string `json:"type,omitempty"`
179 Command string `json:"command,omitempty"`
180 Args []string `json:"args,omitempty"`
181 Env MCPEnv `json:"env,omitempty"`
182 URL string `json:"url,omitempty"`
183 Headers MCPHeaders `json:"headers,omitempty"`
184 }
185
186 // MCPEnv accepts ACP's official EnvVariable[] shape while still accepting the
187 // older map shape that Reasonix v1 clients used.
188 type MCPEnv map[string]string
189
190 // MCPHeaders accepts ACP's official HTTPHeader[] shape while still accepting
191 // the older map shape that Reasonix v1 clients used. The official spec
192 // (https://agentclientprotocol.com) ships HTTP/SSE MCP headers as an array of
193 // {name,value} objects, even when empty.
194 type MCPHeaders map[string]string
195
196 // EnvVariable is one official ACP MCP environment variable entry. The same
197 // {name,value} shape is also used by HTTP/SSE headers in the ACP spec, so we
198 // reuse it as the parse target for [MCPHeaders] too.
199 type EnvVariable struct {
200 Name string `json:"name"`
201 Value string `json:"value"`
202 }
203
204 func (e *MCPEnv) UnmarshalJSON(raw []byte) error {
205 out, err := unmarshalNameValueMap(raw, "env")
206 if err != nil {
207 return err
208 }
209 *e = out
210 return nil
211 }
212
213 func (h *MCPHeaders) UnmarshalJSON(raw []byte) error {
214 out, err := unmarshalNameValueMap(raw, "headers")
215 if err != nil {
216 return err
217 }
218 *h = out
219 return nil
220 }
221
222 // unmarshalNameValueMap parses ACP's official [{name,value}, ...] array shape
223 // or the legacy {name: value, ...} map shape into a map. field names the JSON
224 // field for error messages.
225 func unmarshalNameValueMap(raw []byte, field string) (map[string]string, error) {
226 if s := strings.TrimSpace(string(raw)); s == "" || s == "null" {
227 return nil, nil
228 }
229
230 var vars []EnvVariable
231 if err := json.Unmarshal(raw, &vars); err == nil {
232 out := make(map[string]string, len(vars))
233 for i, v := range vars {
234 if strings.TrimSpace(v.Name) == "" {
235 return nil, fmt.Errorf("%s[%d].name is required", field, i)
236 }
237 out[v.Name] = v.Value
238 }
239 return out, nil
240 }
241
242 var legacy map[string]string
243 if err := json.Unmarshal(raw, &legacy); err == nil {
244 return legacy, nil
245 }
246 return nil, fmt.Errorf("%s must be an array of {name,value} objects", field)
247 }
248
249 // SessionNewResult returns the opaque id used to address the session thereafter.
250 type SessionNewResult struct {
251 SessionID string `json:"sessionId"`
252 Models *SessionModelState `json:"models,omitempty"`
253 Modes *SessionModeState `json:"modes,omitempty"`
254 ConfigOptions []SessionConfigOption `json:"configOptions,omitempty"`
255 }
256
257 // session modes
258
259 // SessionMode is one operating mode the client can switch the session into.
260 type SessionMode struct {
261 ID string `json:"id"`
262 Name string `json:"name"`
263 Description string `json:"description,omitempty"`
264 }
265
266 // SessionModeState reports the current mode and the full mode list.
267 type SessionModeState struct {
268 CurrentModeID string `json:"currentModeId"`
269 AvailableModes []SessionMode `json:"availableModes"`
270 }
271
272 // SessionSetModeParams switches a session's operating mode.
273 type SessionSetModeParams struct {
274 SessionID string `json:"sessionId"`
275 ModeID string `json:"modeId"`
276 }
277
278 // SessionSetModeResult is the empty ack.
279 type SessionSetModeResult struct{}
280
281 // ModelInfo describes one selectable model in ACP's legacy model selector.
282 type ModelInfo struct {
283 ModelID string `json:"modelId"`
284 Name string `json:"name"`
285 Description string `json:"description,omitempty"`
286 }
287
288 // SessionModelState is ACP's legacy model selector state. New clients should
289 // prefer the category:"model" config option, but some hosts still probe this.
290 type SessionModelState struct {
291 AvailableModels []ModelInfo `json:"availableModels"`
292 CurrentModelID string `json:"currentModelId"`
293 }
294
295 // session/load
296
297 // SessionLoadParams resumes a session saved under sessionId (the id a prior
298 // session/new returned), optionally re-rooting it at cwd with fresh MCP servers.
299 // The agent replays the stored conversation as session/update notifications
300 // before the request returns.
301 type SessionLoadParams struct {
302 SessionID string `json:"sessionId"`
303 Cwd string `json:"cwd,omitempty"`
304 MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
305 }
306
307 // SessionLoadResult is the empty ack; the conversation has already arrived as a
308 // burst of session/update notifications by the time it is sent.
309 type SessionLoadResult struct {
310 Models *SessionModelState `json:"models,omitempty"`
311 Modes *SessionModeState `json:"modes,omitempty"`
312 ConfigOptions []SessionConfigOption `json:"configOptions,omitempty"`
313 }
314
315 // session/resume
316
317 // SessionResumeParams resumes a session without replaying its transcript.
318 type SessionResumeParams struct {
319 SessionID string `json:"sessionId"`
320 Cwd string `json:"cwd,omitempty"`
321 MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
322 }
323
324 // SessionResumeResult is the empty ack returned once the session is ready.
325 type SessionResumeResult struct {
326 Models *SessionModelState `json:"models,omitempty"`
327 Modes *SessionModeState `json:"modes,omitempty"`
328 ConfigOptions []SessionConfigOption `json:"configOptions,omitempty"`
329 }
330
331 // session/set_config_option
332
333 // SetSessionConfigOptionParams changes one advertised session config option.
334 type SetSessionConfigOptionParams struct {
335 SessionID string `json:"sessionId"`
336 ConfigID string `json:"configId"`
337 Value string `json:"value"`
338 }
339
340 type SetSessionConfigOptionResult struct {
341 ConfigOptions []SessionConfigOption `json:"configOptions"`
342 DeprecatedNotice string `json:"deprecatedNotice,omitempty"`
343 }
344
345 // SessionConfigOption is a single-value ACP session selector.
346 type SessionConfigOption struct {
347 ID string `json:"id"`
348 Name string `json:"name"`
349 Description string `json:"description,omitempty"`
350 Category string `json:"category,omitempty"`
351 Type string `json:"type"`
352 CurrentValue string `json:"currentValue"`
353 Options []SessionConfigSelectOption `json:"options"`
354 }
355
356 // SessionConfigSelectOption is one selectable value for a config option.
357 type SessionConfigSelectOption struct {
358 Value string `json:"value"`
359 Name string `json:"name"`
360 Description string `json:"description,omitempty"`
361 }
362
363 // session/set_model
364
365 // SetSessionModelParams is ACP's legacy model-switching request.
366 type SetSessionModelParams struct {
367 SessionID string `json:"sessionId"`
368 ModelID string `json:"modelId"`
369 }
370
371 // SetSessionModelResult is the empty ack for legacy model switching.
372 type SetSessionModelResult struct{}
373
374 // session/list
375
376 // SessionListParams lists known sessions, optionally filtered by cwd.
377 type SessionListParams struct {
378 Cwd string `json:"cwd,omitempty"`
379 Cursor string `json:"cursor,omitempty"`
380 }
381
382 // SessionListResult is the first and only page of sessions Reasonix currently
383 // returns. NextCursor is omitted because the in-process list is unpaged.
384 type SessionListResult struct {
385 Sessions []SessionInfo `json:"sessions"`
386 NextCursor string `json:"nextCursor,omitempty"`
387 }
388
389 // SessionInfo is the ACP session/list item shape.
390 type SessionInfo struct {
391 SessionID string `json:"sessionId"`
392 Cwd string `json:"cwd"`
393 Title string `json:"title,omitempty"`
394 UpdatedAt string `json:"updatedAt,omitempty"`
395 Meta map[string]any `json:"_meta,omitempty"`
396 }
397
398 // session/close
399
400 // SessionCloseParams closes an active session and releases its resources.
401 type SessionCloseParams struct {
402 SessionID string `json:"sessionId"`
403 }
404
405 // SessionCloseResult is the empty close ack.
406 type SessionCloseResult struct{}
407
408 // session/delete
409
410 // SessionDeleteParams removes a session from future session/list results.
411 type SessionDeleteParams struct {
412 SessionID string `json:"sessionId"`
413 }
414
415 // SessionDeleteResult is the empty delete ack.
416 type SessionDeleteResult struct{}
417
418 // content blocks (inbound prompt)
419
420 // ContentBlock is one piece of a prompt. The agent reads text blocks and the
421 // inline text of resource blocks (embeddedContext); image/audio are accepted on
422 // the wire but ignored, matching the advertised capabilities.
423 type ContentBlock struct {
424 Type string `json:"type"`
425 Text string `json:"text,omitempty"`
426 Resource *ResourceContents `json:"resource,omitempty"`
427 MimeType string `json:"mimeType,omitempty"`
428 Data string `json:"data,omitempty"`
429 }
430
431 // ResourceContents is the embedded resource of a "resource" content block.
432 type ResourceContents struct {
433 URI string `json:"uri"`
434 MimeType string `json:"mimeType,omitempty"`
435 Text string `json:"text,omitempty"`
436 }
437
438 // FlattenPrompt extracts the user-visible prompt text out of ACP content blocks.
439 // Text blocks contribute their text; resource blocks contribute their inline
440 // text when present (embeddedContext). Other block kinds are dropped. Ported from
441 // protocol.ts flattenPrompt.
442 func FlattenPrompt(blocks []ContentBlock) string {
443 parts := make([]string, 0, len(blocks))
444 for _, b := range blocks {
445 switch b.Type {
446 case "text":
447 if b.Text != "" {
448 parts = append(parts, b.Text)
449 }
450 case "resource":
451 if b.Resource != nil && b.Resource.Text != "" {
452 parts = append(parts, b.Resource.Text)
453 }
454 }
455 }
456 return strings.TrimSpace(strings.Join(parts, "\n\n"))
457 }
458
459 // session/prompt
460
461 // SessionPromptParams sends a turn's prompt to a session.
462 type SessionPromptParams struct {
463 SessionID string `json:"sessionId"`
464 Prompt []ContentBlock `json:"prompt"`
465 // Action is an optional Reasonix extension. Empty preserves ACP's standard
466 // prompt behavior; final_readiness_recovery explicitly resumes the newest
467 // paused host check without trusting ordinary prose as authorization.
468 Action string `json:"action,omitempty"`
469 RecoveryID string `json:"recoveryId,omitempty"`
470 }
471
472 // SessionSteerParams is the Reasonix ACP v1 extension for injecting user
473 // guidance into an active prompt without cancelling it.
474 type SessionSteerParams struct {
475 SessionID string `json:"sessionId"`
476 Prompt []ContentBlock `json:"prompt"`
477 }
478
479 // SessionSteerResult acknowledges durable steer admission.
480 type SessionSteerResult struct {
481 ItemID string `json:"itemId,omitempty"`
482 Disposition string `json:"disposition,omitempty"`
483 }
484
485 // sessionSteerMethod follows ACP v1's reserved vendor-extension namespace.
486 const sessionSteerMethod = "_reasonix.io/session/steer"
487
488 const (
489 sessionInboxSchemaVersion = 1
490 sessionInboxEnqueueMethod = "_reasonix.io/session/inbox/enqueue"
491 sessionInboxListMethod = "_reasonix.io/session/inbox/list"
492 sessionInboxGetMethod = "_reasonix.io/session/inbox/get"
493 sessionInboxUpdateMethod = "_reasonix.io/session/inbox/update"
494 sessionInboxDeleteMethod = "_reasonix.io/session/inbox/delete"
495 sessionInboxMoveMethod = "_reasonix.io/session/inbox/move"
496 sessionInboxPauseMethod = "_reasonix.io/session/inbox/setPaused"
497 sessionInboxRetryMethod = "_reasonix.io/session/inbox/retry"
498 sessionInboxRefreshMethod = "_reasonix.io/session/inbox/refresh"
499 )
500
501 // SessionInboxEnqueueParams is the durable inbox enqueue request.
502 type SessionInboxEnqueueParams struct {
503 SessionID string `json:"sessionId"`
504 Text string `json:"text"`
505 Intent string `json:"intent,omitempty"` // followup | steer
506 IdempotencyKey string `json:"idempotencyKey,omitempty"`
507 }
508
509 // SessionInboxItemParams identifies one inbox item.
510 type SessionInboxItemParams struct {
511 SessionID string `json:"sessionId"`
512 ItemID string `json:"itemId"`
513 }
514
515 // SessionInboxUpdateParams rewrites an item body.
516 type SessionInboxUpdateParams struct {
517 SessionID string `json:"sessionId"`
518 ItemID string `json:"itemId"`
519 Text string `json:"text"`
520 }
521
522 // SessionInboxMoveParams reorders an item (toIndex is 0-based).
523 type SessionInboxMoveParams struct {
524 SessionID string `json:"sessionId"`
525 ItemID string `json:"itemId"`
526 ToIndex int `json:"toIndex"`
527 }
528
529 // SessionInboxPauseParams toggles pause.
530 type SessionInboxPauseParams struct {
531 SessionID string `json:"sessionId"`
532 Paused bool `json:"paused"`
533 }
534
535 // SessionReloadExtensionsParams addresses one live ACP session.
536 type SessionReloadExtensionsParams struct {
537 SessionID string `json:"sessionId"`
538 }
539
540 // SessionReloadExtensionsResult reports whether the runtime reload ran
541 // immediately (Queued false) or was coalesced behind a turn/rebuild in flight
542 // to run when the session goes idle (Queued true).
543 type SessionReloadExtensionsResult struct {
544 Queued bool `json:"queued,omitempty"`
545 }
546
547 // sessionReloadExtensionsMethod follows ACP v1's reserved vendor-extension
548 // namespace, like sessionSteerMethod: only the "_<vendor>/" prefix is reserved
549 // for vendor methods, so the bare "reasonix/session/reloadExtensions" form
550 // could collide with a future official ACP method and must not be used.
551 const sessionReloadExtensionsMethod = "_reasonix.io/session/reloadExtensions"
552
553 // StopReason tells the client why a turn ended. Reasonix only emits values from
554 // the ACP v1 enum; failed turns are returned as JSON-RPC errors instead.
555 type StopReason string
556
557 // SessionPromptResult ends a session/prompt. TranscriptPath is reserved for a
558 // future on-disk transcript pointer; omitted (null) for now.
559 type SessionPromptResult struct {
560 StopReason StopReason `json:"stopReason"`
561 TranscriptPath *string `json:"transcriptPath,omitempty"`
562 }
563
564 // session/update (agent → client notifications)
565 //
566 // SessionUpdate is a tagged union discriminated by sessionUpdate. The variants
567 // reuse the JSON key "content" with two incompatible shapes (a single block for
568 // message chunks, an array for tool results), so we model each variant as its own
569 // struct rather than one struct with conflicting tags, and carry it through
570 // SessionUpdateParams.Update as an interface value.
571
572 // SessionUpdateParams wraps one update for a session.
573 type SessionUpdateParams struct {
574 SessionID string `json:"sessionId"`
575 Update any `json:"update"`
576 }
577
578 // messageChunk is agent_message_chunk / agent_thought_chunk.
579 type messageChunk struct {
580 SessionUpdate string `json:"sessionUpdate"`
581 Content ContentBlock `json:"content"`
582 Metadata *updateMeta `json:"metadata,omitempty"`
583 }
584
585 // extensionSurfaceUpdate is the vendor session/update variant that carries one
586 // structured extension-UI surface to clients that negotiated
587 // reasonix.extensionSurface in initialize. ACP has no standard notification for
588 // extension surfaces, so the DTO (the shared eventwire JSON contract) rides
589 // _meta["reasonix.io"]["extensionSurface"], mirroring how the initialize
590 // handshake namespaces vendor data under "reasonix.io". The sink always pairs
591 // it with a flattened agent_message_chunk text fallback (belt and suspenders):
592 // a client that ignores the vendor variant still shows the content.
593 type extensionSurfaceUpdate struct {
594 SessionUpdate string `json:"sessionUpdate"`
595 Meta map[string]any `json:"_meta"`
596 }
597
598 // updateMeta carries optional error detail on an agent_message_chunk.
599 type updateMeta struct {
600 Error *updateError `json:"error,omitempty"`
601 }
602
603 type updateError struct {
604 Name string `json:"name"`
605 Message string `json:"message"`
606 }
607
608 // toolCall is a "tool_call" update (announces a call, with title/kind/rawInput).
609 type toolCall struct {
610 SessionUpdate string `json:"sessionUpdate"`
611 ToolCallID string `json:"toolCallId"`
612 Title string `json:"title,omitempty"`
613 Kind string `json:"kind,omitempty"`
614 Status string `json:"status,omitempty"`
615 RawInput json.RawMessage `json:"rawInput,omitempty"`
616 Locations []ToolCallLocation `json:"locations,omitempty"`
617 }
618
619 // ToolCallLocation names a file (and optionally a line) a tool call touches, so
620 // the client can follow along in the editor.
621 type ToolCallLocation struct {
622 Path string `json:"path"`
623 Line *int `json:"line,omitempty"`
624 }
625
626 // toolCallUpdateMsg is a "tool_call_update" update (status + result content).
627 type toolCallUpdateMsg struct {
628 SessionUpdate string `json:"sessionUpdate"`
629 ToolCallID string `json:"toolCallId"`
630 Status string `json:"status,omitempty"`
631 Content []toolContent `json:"content,omitempty"`
632 }
633
634 // toolContent wraps a tool result's text, per the ACP tool_call_update shape.
635 type toolContent struct {
636 Type string `json:"type"`
637 Content ContentBlock `json:"content"`
638 }
639
640 // availableCommandsUpdate advertises slash commands that the ACP client may
641 // surface in its composer. The client sends invocations back as normal
642 // session/prompt text such as "/review diff".
643 type availableCommandsUpdate struct {
644 SessionUpdate string `json:"sessionUpdate"`
645 AvailableCommands []AvailableCommand `json:"availableCommands"`
646 }
647
648 // AvailableCommand is one slash command available in a session.
649 type AvailableCommand struct {
650 Name string `json:"name"`
651 Description string `json:"description"`
652 Input *AvailableCommandInput `json:"input,omitempty"`
653 }
654
655 // AvailableCommandInput describes a command's free-form text argument.
656 type AvailableCommandInput struct {
657 Hint string `json:"hint"`
658 }
659
660 // configOptionUpdate reports a complete refreshed session config state.
661 type configOptionUpdate struct {
662 SessionUpdate string `json:"sessionUpdate"`
663 ConfigOptions []SessionConfigOption `json:"configOptions"`
664 }
665
666 // planUpdate is a "plan" update: the agent's current task list. Each update
667 // carries the complete plan and replaces the previous one, mirroring the
668 // todo_write contract it is derived from.
669 type planUpdate struct {
670 SessionUpdate string `json:"sessionUpdate"`
671 Entries []PlanEntry `json:"entries"`
672 }
673
674 // PlanEntry is one task in a plan update.
675 type PlanEntry struct {
676 Content string `json:"content"`
677 Priority string `json:"priority"`
678 Status string `json:"status"`
679 }
680
681 // currentModeUpdate reports that the session switched operating modes.
682 type currentModeUpdate struct {
683 SessionUpdate string `json:"sessionUpdate"`
684 CurrentModeID string `json:"currentModeId"`
685 }
686
687 // fs/* (agent → client requests)
688
689 // FSReadTextFileParams asks the client for a file's current text, including
690 // unsaved editor state. Line (1-based) and Limit page the content; Reasonix
691 // always reads whole files and pages locally, so it sends neither.
692 type FSReadTextFileParams struct {
693 SessionID string `json:"sessionId"`
694 Path string `json:"path"`
695 Line *int `json:"line,omitempty"`
696 Limit *int `json:"limit,omitempty"`
697 }
698
699 // FSReadTextFileResult carries the file content.
700 type FSReadTextFileResult struct {
701 Content string `json:"content"`
702 }
703
704 // FSWriteTextFileParams asks the client to write content to path, updating any
705 // open buffer as well as the file on disk.
706 type FSWriteTextFileParams struct {
707 SessionID string `json:"sessionId"`
708 Path string `json:"path"`
709 Content string `json:"content"`
710 }
711
712 // terminal/* (agent → client requests)
713
714 // TerminalCreateParams starts a command in a client-owned terminal.
715 // Env follows ACP v1's official EnvVariable[] shape (same as MCP env): only
716 // the overrides Reasonix owns (typically TMPDIR/TMP/TEMP) are sent — never a
717 // full host environment dump.
718 type TerminalCreateParams struct {
719 SessionID string `json:"sessionId"`
720 Command string `json:"command"`
721 Args []string `json:"args,omitempty"`
722 Cwd string `json:"cwd,omitempty"`
723 Env []EnvVariable `json:"env,omitempty"`
724 OutputByteLimit int `json:"outputByteLimit,omitempty"`
725 }
726
727 // TerminalCreateResult returns the id used by the other terminal methods.
728 type TerminalCreateResult struct {
729 TerminalID string `json:"terminalId"`
730 }
731
732 // TerminalIDParams addresses one terminal (output / kill / wait / release).
733 type TerminalIDParams struct {
734 SessionID string `json:"sessionId"`
735 TerminalID string `json:"terminalId"`
736 }
737
738 // TerminalOutputResult is the terminal's captured output so far.
739 type TerminalOutputResult struct {
740 Output string `json:"output"`
741 Truncated bool `json:"truncated"`
742 ExitStatus *TerminalExitStatus `json:"exitStatus,omitempty"`
743 }
744
745 // TerminalWaitResult reports how the command exited.
746 type TerminalWaitResult struct {
747 ExitCode *int `json:"exitCode,omitempty"`
748 Signal *string `json:"signal,omitempty"`
749 }
750
751 // TerminalExitStatus mirrors TerminalWaitResult inside terminal/output.
752 type TerminalExitStatus struct {
753 ExitCode *int `json:"exitCode,omitempty"`
754 Signal *string `json:"signal,omitempty"`
755 }
756
757 // session/cancel (client → agent notification)
758
759 // SessionCancelParams cancels an in-progress turn.
760 type SessionCancelParams struct {
761 SessionID string `json:"sessionId"`
762 }
763
764 // session/request_permission (agent → client request)
765
766 // PermissionOptionKind classifies an option for host UI styling. It is an ACP v1
767 // wire enum, so host-visible permission choices must stay within the official
768 // protocol values.
769 type PermissionOptionKind string
770
771 const (
772 OptAllowOnce PermissionOptionKind = "allow_once"
773 OptAllowAlways PermissionOptionKind = "allow_always"
774 OptRejectOnce PermissionOptionKind = "reject_once"
775 OptRejectAlways PermissionOptionKind = "reject_always"
776 )
777
778 // PermissionOption is one choice offered to the user for a permission request.
779 type PermissionOption struct {
780 OptionID string `json:"optionId"`
781 Name string `json:"name"`
782 Kind PermissionOptionKind `json:"kind"`
783 }
784
785 // PermissionRequestParams asks the client to approve a pending tool call.
786 type PermissionRequestParams struct {
787 SessionID string `json:"sessionId"`
788 ToolCall PermissionToolCall `json:"toolCall"`
789 Options []PermissionOption `json:"options"`
790 }
791
792 // PermissionToolCall describes the call awaiting approval.
793 type PermissionToolCall struct {
794 ToolCallID string `json:"toolCallId"`
795 Title string `json:"title,omitempty"`
796 Kind string `json:"kind,omitempty"`
797 Status string `json:"status,omitempty"`
798 Content []toolContent `json:"content,omitempty"`
799 RawInput json.RawMessage `json:"rawInput,omitempty"`
800 Locations []ToolCallLocation `json:"locations,omitempty"`
801 Meta map[string]any `json:"_meta,omitempty"`
802 }
803
804 // PermissionRequestResult is the client's reply to a permission request.
805 type PermissionRequestResult struct {
806 Outcome PermissionOutcome `json:"outcome"`
807 }
808
809 // PermissionOutcome is "selected" (with optionId) or "cancelled".
810 type PermissionOutcome struct {
811 Outcome string `json:"outcome"`
812 OptionID string `json:"optionId,omitempty"`
813 }
814
814 lines GO