nandi/oripublic Fork 0
3017563d783a895a85f8f8ad162b58bb5d944946
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

protocol.go · 107 lines · 4.5 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
package bridge

import "encoding/json"

// This file is the wire contract between the SPA and the backend, over a
// WebSocket carrying one JSON message per text frame. ACP payloads (session
// updates, permission requests) are relayed verbatim so the frontend works
// with the protocol's own shapes, documented at
// https://agentclientprotocol.com.

// Incoming message types (browser → server).
const (
	// IncomingPrompt starts a turn: {"type":"prompt","text":"...","attachments":[...]}.
	IncomingPrompt = "prompt"
	// IncomingCancel interrupts the current turn: {"type":"cancel"}.
	IncomingCancel = "cancel"
	// IncomingPermissionResponse answers a permission request:
	// {"type":"permission_response","requestId":"...","optionId":"..."} or
	// {"type":"permission_response","requestId":"...","cancelled":true}.
	IncomingPermissionResponse = "permission_response"
)

// Outgoing message types (server → browser).
const (
	// OutgoingHello is sent once per connection with the session state.
	OutgoingHello = "hello"
	// OutgoingUserMessage echoes the prompt that started a turn, so every
	// client (and every reconnection replay) renders the user's side of the
	// conversation from the same source of truth as the agent's side.
	OutgoingUserMessage = "user_message"
	// OutgoingSessionUpdate relays one ACP session/update notification;
	// its "update" field is the raw ACP SessionUpdate object.
	OutgoingSessionUpdate = "session_update"
	// OutgoingPermissionRequest relays an ACP permission request; its
	// "request" field is the raw ACP RequestPermissionRequest object.
	OutgoingPermissionRequest = "permission_request"
	// OutgoingPermissionResolved tells every client a permission request
	// was answered (so open dialogs close everywhere).
	OutgoingPermissionResolved = "permission_resolved"
	// OutgoingTurnStarted marks the start of a prompt turn.
	OutgoingTurnStarted = "turn_started"
	// OutgoingTurnEnded marks the end of a turn, with its stop reason.
	OutgoingTurnEnded = "turn_ended"
	// OutgoingError carries a human-readable error message.
	OutgoingError = "error"
)

// Attachment is a workspace file the user mentioned in a prompt (with the
// "@" selector). The server turns it into an ACP resource_link content block
// so the agent can open the file, while the "@name" mention stays visible in
// the prompt text.
type Attachment struct {
	// Path is the file path, absolute or relative to the workspace root.
	Path string `json:"path"`
	// Name is the label shown for the file (the relative path in the UI);
	// it defaults to Path.
	Name string `json:"name,omitempty"`
}

// Incoming is a message sent by the browser.
type Incoming struct {
	Type string `json:"type"`
	// Text is the prompt text (IncomingPrompt).
	Text string `json:"text,omitempty"`
	// Attachments are the files mentioned in the prompt (IncomingPrompt).
	Attachments []Attachment `json:"attachments,omitempty"`
	// RequestId identifies the permission request being answered.
	RequestId string `json:"requestId,omitempty"`
	// OptionId is the chosen ACP permission option.
	OptionId string `json:"optionId,omitempty"`
	// Cancelled dismisses the permission request instead of choosing.
	Cancelled bool `json:"cancelled,omitempty"`
}

// Outgoing is a message sent to the browser.
type Outgoing struct {
	Type string `json:"type"`
	// SessionId is set on OutgoingHello.
	SessionId string `json:"sessionId,omitempty"`
	// TurnActive is set on OutgoingHello: true while a prompt turn runs.
	TurnActive bool `json:"turnActive,omitempty"`
	// Update is the raw ACP SessionUpdate (OutgoingSessionUpdate).
	Update json.RawMessage `json:"update,omitempty"`
	// RequestId identifies a permission request across messages.
	RequestId string `json:"requestId,omitempty"`
	// Request is the raw ACP RequestPermissionRequest (OutgoingPermissionRequest).
	Request json.RawMessage `json:"request,omitempty"`
	// Text is the user's prompt text (OutgoingUserMessage).
	Text string `json:"text,omitempty"`
	// Attachments are the files the prompt mentioned (OutgoingUserMessage).
	Attachments []Attachment `json:"attachments,omitempty"`
	// StopReason is the ACP stop reason (OutgoingTurnEnded).
	StopReason string `json:"stopReason,omitempty"`
	// Message is a human-readable error (OutgoingError).
	Message string `json:"message,omitempty"`
}

// encode marshals an Outgoing message; the bridge treats a marshalling
// failure as a programming error and panics, because every field is
// marshal-safe by construction.
func encode(msg Outgoing) []byte {
	raw, err := json.Marshal(msg)
	if err != nil {
		panic(err)
	}
	return raw
}