translate

package
v0.2.49 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: GPL-2.0, GPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package translate handles ACP → domain event translation.

The Translator receives raw ACP notifications from kiro-cli bridges and converts them into domain events (SSE broadcasts, chat-store mutations). Hub remains the coordinator; this package owns the protocol-specific decode + dispatch logic.

Index

Constants

View Source
const (
	// PrimePreambleSwitch opens the model-switch recovery prime.
	PrimePreambleSwitch = "The context was just switched (new agent, new model, " +
		"or both). Below is the full conversation history. Read it " +
		"silently and reply with a single short line confirming " +
		"you're caught up.\n\n"
	// PrimePreambleRewind opens the degraded-rewind prime.
	PrimePreambleRewind = "This conversation was rewound to an earlier turn and is " +
		"resuming in a fresh session. Below is the conversation history " +
		"up to the rewind point. Read it silently and reply with a " +
		"single short line confirming you're caught up.\n\n"
)

Prime preambles: the fixed prefixes of the invisible priming prompt the bridge coordinator sends on a fresh session after a model-switch fallback or a degraded rewind (hub bridge_coord.go builds the prime as preamble + history). Exported so the coordinator and the focus filter share one definition — KAS derives a first-prompt title from whatever prompt text it sees first, and on a primed session that is this text.

View Source
const ContentTypeContent = "content"

ContentTypeContent is the ACP content-block type discriminator value "content". Distinct from jsonFieldContent which is the JSON field *name* "content".

View Source
const ContentTypeDiff = "diff"

ContentTypeDiff is the ACP content-block type for file-change diffs in tool_call and tool_call_update payloads.

Variables

This section is empty.

Functions

func DecodeGovernanceState added in v0.1.165

func DecodeGovernanceState(raw json.RawMessage) (api.GovernanceStatePayload, bool)

DecodeGovernanceState decodes a raw _kiro/governance/state params object into the domain payload. Exported so the hub can reuse it for the copy the utility bridge receives (whose notifications don't flow through this dispatcher) — keeping one wire→domain conversion. Returns false on empty/invalid params.

Types

type ACPChunkWire

type ACPChunkWire struct {
	Content struct {
		Type string `json:"type"`
		Text string `json:"text"`
	} `json:"content"`
	Meta ACPKiroMeta `json:"_meta"`
}

ACPChunkWire is the wire shape for agent_message_chunk and agent_thought_chunk session updates. On v3 (KAS) a nested subagent's chunks ride the parent session id and carry _meta.kiro.agentSubtaskId identifying which agent-subtask tool call they belong to.

type ACPKiroMeta added in v0.1.165

type ACPKiroMeta struct {
	Kiro struct {
		// Refusal is present only on the agent_message_chunk carrying a
		// model-refusal explanation (kiro-cli 2.13+, modelStopReason
		// "content_filtered"). KAS calls it a progressive-enhancement
		// marker: plain clients render the text, capable clients key off
		// it for a distinct refusal affordance. The turn then ends with
		// core stopReason "refusal".
		Refusal        *ACPRefusalMeta `json:"refusal"`
		Kind           string          `json:"kind"`
		AgentSubtaskID string          `json:"agentSubtaskId"`
		HookAsk        json.RawMessage `json:"hookAsk,omitempty"`
	} `json:"kiro"`
}

ACPKiroMeta is the top-level `_meta.kiro` block carried on v3 (KAS) tool_call / tool_call_update session updates. When Kind=="agent-subtask" the tool call is a subagent card (the model invoked invoke_sub_agent); AgentSubtaskID is the stable id that links the card to its nested agent_message_chunk / agent_thought_chunk deltas (which carry the same id under their own _meta.kiro).

HookAsk is present (non-empty) only on the synthetic tool call KAS emits to surface a pre-tool-use hook's ask-permission gate: KAS sends a kind:"other" tool_call/tool_call_update tagged _meta.kiro.hookAsk={kind:"pre-tool-use",toolName,reason[,decision]} — NOT a ToolKind "hook" (v3's zToolKind has no "hook"). Its presence is the signal HandleToolCall uses to suppress hook cards when the hooks.showStatus setting is off; the contents are opaque here.

type ACPModeUpdateWire

type ACPModeUpdateWire struct {
	ModeID string `json:"currentModeId"`
}

ACPModeUpdateWire is the wire shape for the current_mode_update session/update sub-kind. KAS keys the new mode on `currentModeId` (the bundle's zCurrentModeUpdate object), NOT `modeId` — `modeId` is the field name on the outbound session/set_mode REQUEST (command/mode.go), a different message. Reading the wrong key here left ModeID empty, so HandleModeUpdate never persisted agent-initiated mode changes.

type ACPPlanWire

type ACPPlanWire struct {
	Entries []api.PlanEntry `json:"entries"`
}

ACPPlanWire is the wire shape for plan session updates.

type ACPRefusalMeta added in v0.2.8

type ACPRefusalMeta struct {
	Category         string `json:"category"`
	Explanation      string `json:"explanation"`
	RecommendedModel string `json:"recommendedModel"`
}

ACPRefusalMeta is the _meta.kiro.refusal block on a refusal explanation chunk. Explanation duplicates the chunk text (KAS falls back to a canned message when absent), so only Category / RecommendedModel flow into the domain api.RefusalInfo.

type ACPSessionUpdateBase

type ACPSessionUpdateBase struct {
	Kind api.ACPUpdateKind `json:"sessionUpdate"`
}

ACPSessionUpdateBase extracts the sessionUpdate kind discriminator.

type ACPSessionUpdateEnvelope

type ACPSessionUpdateEnvelope struct {
	SessionID string          `json:"sessionId"`
	Update    json.RawMessage `json:"update"`
}

ACPSessionUpdateEnvelope is the outer envelope for session/update notifications.

type ACPToolCallContentBlock

type ACPToolCallContentBlock struct {
	Type    string `json:"type"`
	Path    string `json:"path"`
	OldText string `json:"oldText"`
	NewText string `json:"newText"`
	Content struct {
		Text string `json:"text"`
	} `json:"content"`
}

ACPToolCallContentBlock is one element in a tool_call or tool_call_update's content array.

type ACPToolCallUpdateWire

type ACPToolCallUpdateWire struct {
	Meta       ACPKiroMeta               `json:"_meta"`
	ToolCallID string                    `json:"toolCallId"`
	Title      string                    `json:"title"`
	Kind       api.ToolKind              `json:"kind"`
	Status     api.ToolStatus            `json:"status"`
	Locations  []api.ToolLocation        `json:"locations"`
	Content    []ACPToolCallContentBlock `json:"content"`
}

ACPToolCallUpdateWire is the wire shape for tool_call_update session updates. KAS's zToolCallUpdate also carries optional title/kind (a mid-flight card refinement) and rawOutput; we decode title/kind so the card can be relabelled, but leave rawOutput undecoded — the tool's textual output already arrives through the `content` blocks below and the domain ToolCall has no structured-output field.

type ACPToolCallWire

type ACPToolCallWire struct {
	Meta       ACPKiroMeta               `json:"_meta"`
	ToolCallID string                    `json:"toolCallId"`
	Title      string                    `json:"title"`
	Kind       api.ToolKind              `json:"kind"`
	Status     api.ToolStatus            `json:"status"`
	RawInput   json.RawMessage           `json:"rawInput"`
	Locations  []api.ToolLocation        `json:"locations"`
	Content    []ACPToolCallContentBlock `json:"content"`
}

ACPToolCallWire is the wire shape for tool_call session updates.

type BridgeComm

type BridgeComm interface {
	BridgeNotify(ctx context.Context, chatID api.ChatID, method string, params map[string]any) error
	BridgeRespond(ctx context.Context, chatID api.ChatID, requestID int64, result any, err error) error
}

BridgeComm provides bridge communication methods needed by compact.go and permission_handler.go for sending notifications and responses.

type BufferAccess

type BufferAccess interface {
	GetOrInit(chatID api.ChatID) *buffer.Buffer
}

BufferAccess is the consumer-side interface for buffer store access. Narrows the coupling: translate only needs GetOrInit.

type ChatStoreDeps

type ChatStoreDeps interface {
	Broadcast(ctx context.Context, evt api.ServerEvent)
	ChatStore() api.ChatStore
	ParentACPSession(chatID api.ChatID) string
}

ChatStoreDeps provides the minimal interface needed by handlers that only require chat store access and broadcast (init_errors).

type Deps

type Deps interface {
	StreamingAccess
	PermissionAccess
	BridgeComm
	// MCPRecorder returns the MCP state recorder sub-interface.
	MCPRecorder() MCPRecorder
	// SetGovernance caches the latest account/workspace governance state so
	// GET /api/governance can serve it with no chat open (see hub/governance.go).
	SetGovernance(state api.GovernanceStatePayload)
}

Deps abstracts the Hub methods that stateful translate handlers need. Hub satisfies this interface, allowing the Translator to operate without importing the hub package.

type LineRecorder

type LineRecorder interface {
	RecordFromDiffs(chatID api.ChatID, diffs []api.ToolDiff, turn int, kind string)
}

LineRecorder is the consumer-side interface for line tracking. Narrows the coupling: translate only needs RecordFromDiffs.

type MCPRecorder

type MCPRecorder interface {
	// RecordConnected marks a server connected and records the prompts +
	// resources it advertises (from _kiro/mcp/status). Both may be nil.
	RecordConnected(ctx context.Context, serverName string, prompts []api.MCPPromptInfo, resources []api.MCPResourceInfo)
	RecordOAuth(ctx context.Context, serverName, oauthURL string)
	RecordInitFailure(ctx context.Context, serverName, errMsg string)
	SignalReady()
	SetKnownTools(ctx context.Context, name string, tools []string)
}

MCPRecorder groups MCP server state tracking methods. Extracted from Deps to narrow the interface (21→17 methods) and allow independent stubbing in tests.

type Option

type Option func(*Translator)

Option configures a Translator.

func WithIDGenerator

func WithIDGenerator(fn func() string) Option

WithIDGenerator overrides the default message ID generator (for tests).

type PermissionAccess

type PermissionAccess interface {
	Broadcast(ctx context.Context, evt api.ServerEvent)
	BridgeRespond(ctx context.Context, chatID api.ChatID, requestID int64, result any, err error) error
	ChatStore() api.ChatStore
	NotifyPush(ctx context.Context, body string, kind api.PushKind)
	ParentACPSession(chatID api.ChatID) string
	PendingPermsAdd(requestID int64, evt api.ServerEvent)
	PendingPermsRemove(requestID int64)
}

PermissionAccess provides the methods needed by permission_handler.go for permission request handling and push notifications. Shell-command authorization is owned by kiro-cli's native Cedar policy: any session/request_permission that reaches vibekit is a genuine ask, so there is no auto-approval surface here.

type StreamingAccess

type StreamingAccess interface {
	Broadcast(ctx context.Context, evt api.ServerEvent)
	BufferStore() BufferAccess
	LineTracker() LineRecorder
	OpenPartialFile(ctx context.Context, chatID api.ChatID, buf *buffer.Buffer)
	IsHookStatusEnabled() bool
	ChatStore() api.ChatStore
	ParentACPSession(chatID api.ChatID) string
	WorkDir() string
}

StreamingAccess provides the methods needed by streaming_content.go / streaming_tools.go for content buffering, partial file recovery, and line tracking.

type Translator

type Translator struct {
	// contains filtered or unexported fields
}

Translator holds stateful translate logic extracted from Hub. It delegates Hub access through Deps.

func New

func New(deps Deps, opts ...Option) *Translator

New constructs a Translator with the given Hub dependency surface.

func (*Translator) HandleAgentConfigError

func (t *Translator) HandleAgentConfigError(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleAgentConfigError handles the _kiro/customAgent/config_error notification ({path, error}); the extra v3 sessionId is ignored.

func (*Translator) HandleAgentNotFound

func (t *Translator) HandleAgentNotFound(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleAgentNotFound handles the _kiro/customAgent/not_found notification: the requested agent (a mode id on v3) doesn't exist. Persists the fallback mode (always "vibe" on v3) as the chat's CurrentModeID and broadcasts a typed error — on v3 the fallback agent IS a mode id. v3 carries no model fields here — a bad model is an InvalidModelError RPC error on the set_config_option/prompt call, not a notification, so there is no model-not-found handler.

func (*Translator) HandleAssistantChunk

func (t *Translator) HandleAssistantChunk(ctx context.Context, chatID api.ChatID, raw json.RawMessage, isReasoning bool)

HandleAssistantChunk streams a text delta to clients and accumulates it for later persistence. Reasoning chunks (isReasoning=true) flow into buf.Reasoning; regular content chunks flow into buf.Content. The IsReasoning flag is forwarded on the SSE so the client routes each delta to the correct bubble (reasoning details vs content).

func (*Translator) HandleAvailableCommandsUpdate added in v0.1.165

func (t *Translator) HandleAvailableCommandsUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage, subSessionID string)

HandleAvailableCommandsUpdate maps the v3 command catalog onto the same commands_updated SSE the v2 _kiro.dev/commands/available handler emits, so the client slash-command type-ahead is engine-agnostic. Parent-only.

func (*Translator) HandleCodeReferences added in v0.1.165

func (t *Translator) HandleCodeReferences(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleCodeReferences accumulates the turn's licensed-code attributions onto the in-flight assistant buffer and broadcasts a code_references SSE so the client attaches an attribution chip to that turn. The references are also persisted onto the finalized assistant message at turn end (bridge_coord.go) so the chip survives reload — the streamed assistant turn is never re-broadcast as message_appended.

func (*Translator) HandleConfigOptionUpdate added in v0.1.165

func (t *Translator) HandleConfigOptionUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage)

HandleConfigOptionUpdate refreshes the chat's model catalog from the v3 config_option_update. Modes are intentionally NOT refreshed here: the config catalog omits the bundled/workspace source tag the picker groups by, so the authoritative mode list stays the one captured on session/new.

func (*Translator) HandleElicitationCreate added in v0.1.39

func (t *Translator) HandleElicitationCreate(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleElicitationCreate processes a _kiro/mcp/elicitation request from KAS. An MCP server has asked for structured input mid-tool-call; KAS forwarded it to us because we advertised the elicitation client capability. We surface a form to the user and the eventual reply is sent by CmdElicitationResponse via bridge.Respond.

The request is a real JSON-RPC request (envelope id present), routed here the same way fs/read_text_file is — so the correlation id is msg.ID. On v3 the elicitation body is NESTED under an "elicitation" object; sessionId/toolCallId stay top-level. We reuse the pending-permissions tracker for SSE replay so a dialog survives a reconnect, exactly like a permission prompt. There is no v3 elicitation-complete method (upstream cancel is not signalled).

func (*Translator) HandleGovernanceState added in v0.1.165

func (t *Translator) HandleGovernanceState(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleGovernanceState translates _kiro/governance/state into a governance_state SSE and caches the latest state hub-side. The SSE is broadcast with an empty chat id because the policy is account-global (a client on Settings with no active chat must still receive it). A subagent-session copy is skipped (KAS may re-emit per session; the parent copy carries the identical account-global flags) — the same dedup guard safety.go / code_references.go use.

func (*Translator) HandleKnowledgeIndexing added in v0.1.165

func (t *Translator) HandleKnowledgeIndexing(ctx context.Context, _ api.ChatID, msg *api.RPCResponse, started bool)

HandleKnowledgeIndexing translates a _kiro/knowledge/indexingStarted (started == true) or indexingCompleted (started == false) notification into a knowledge_indexing SSE. started stamps Status="started" and carries FileCount; completed carries the wire Status ("success"/failure) and, on success, ItemCount.

func (*Translator) HandleMCPStatus added in v0.1.165

func (t *Translator) HandleMCPStatus(ctx context.Context, _ api.ChatID, msg *api.RPCResponse)

HandleMCPStatus maps the consolidated v3 _kiro/mcp/status notification onto the same MCP-registry state the v2 mcp/* handlers drive: connected servers record their tool names + connected state; failed servers record an init failure, or an OAuth prompt when an authorization URL is present.

func (*Translator) HandleModeUpdate

func (t *Translator) HandleModeUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage)

HandleModeUpdate persists the agent's new mode and broadcasts mode_changed.

func (*Translator) HandlePermissionRequest

func (t *Translator) HandlePermissionRequest(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandlePermissionRequest processes session/request_permission from kiro-cli.

The v3 params object is FLAT — {sessionId, toolCall{...}, options[]} — and the JSON-RPC correlation id is on the envelope (msg.ID), NOT inside params. unmarshalParams decodes msg.Params directly, so the decode struct must match those fields at top level; a `params`-wrapped struct (or an `id` field read from params) decodes to all-zero, yielding an empty dialog and request_id=0 (the outcome would then be answered on id 0, wedging the tool call and disabling the shell auto-policy). Mirror HandleElicitationCreate, which decodes flat and reads *msg.ID.

func (*Translator) HandlePlan

func (t *Translator) HandlePlan(ctx context.Context, chatID api.ChatID, raw json.RawMessage)

HandlePlan persists a plan message directly.

func (*Translator) HandlePolicyChanged added in v0.1.165

func (t *Translator) HandlePolicyChanged(ctx context.Context, _ api.ChatID, msg *api.RPCResponse)

HandlePolicyChanged translates _kiro/policy/changed → the permissions_changed SSE. Clients refetch the native policy view.

func (*Translator) HandlePolicyError added in v0.1.165

func (t *Translator) HandlePolicyError(ctx context.Context, _ api.ChatID, msg *api.RPCResponse)

HandlePolicyError translates _kiro/policy/error → the policy_error SSE (rendered as a banner so a bad hand-edit or rejected rule is visible).

func (*Translator) HandleRateLimit

func (t *Translator) HandleRateLimit(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleRateLimit handles the _kiro/error/rate_limit notification ({message}); the extra v3 sessionId is ignored. Rendered as an auto-clearing amber banner.

func (*Translator) HandleSafetyPropertiesChanged added in v0.1.165

func (t *Translator) HandleSafetyPropertiesChanged(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleSafetyPropertiesChanged translates _kiro/safety/propertiesChanged into a safety_properties SSE (chat-scoped). A subagent-session copy is skipped so a subagent's formalized properties aren't attributed to the parent chat (mirrors code_references' subagent guard).

func (*Translator) HandleSafetyStatusChanged added in v0.1.165

func (t *Translator) HandleSafetyStatusChanged(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleSafetyStatusChanged translates _kiro/safety/statusChanged into a safety_status SSE (chat-scoped). status="idle" is forwarded too — the client uses it to clear a stale banner.

Enforcement model (verified against the KAS 2.12 acp-server bundle): the gate is a PreToolUse hook wrapping the tool executor. In ENFORCE mode a blocked write/shell tool is INTERCEPTED before execution — KAS returns the block as the tool's own result ("The tool was NOT executed") and never calls the inner executor, so it never issues the fs/write_text_file A→C request to us. Vibekit therefore cannot circumvent an enforced block: its fs write handler (hub.respondFSWrite) only ever writes in response to a KAS request, and a blocked tool produces no request. This handler's job is to SURFACE the refusal, not to gate a write (there is no write to gate). A block is non-tool-scoped here: the notification's toolId is the tool NAME (fs_write / str_replace / …), not the per-call tool_call id we round-trip, so it cannot be correlated to a specific rendered tool card.

func (*Translator) HandleSessionInfoUpdate added in v0.1.165

func (t *Translator) HandleSessionInfoUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage, subSessionID string)

HandleSessionInfoUpdate folds v3 context-usage into the chat's usage so the context ring works on the KAS engine (v2 sourced this from _kiro.dev/metadata), and routes v3 compaction status (v2 sourced this from _kiro.dev/compaction/status). Parent-only: subagent updates are dropped so they don't overwrite the parent chat.

func (*Translator) HandleSpecTaskChanged added in v0.1.165

func (t *Translator) HandleSpecTaskChanged(ctx context.Context, _ api.ChatID, msg *api.RPCResponse)

HandleSpecTaskChanged translates a _kiro/spec/taskStatusChanged notification into a spec_task_changed SSE. The feature name is derived from tasksFilePath so the client can target the affected spec; the raw path is forwarded too.

func (*Translator) HandleSystemNotify added in v0.1.165

func (t *Translator) HandleSystemNotify(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleSystemNotify handles the v3 _kiro/system/notify notification ({level, message}) — the replacement for v2's session/retry banner. KAS emits it as a connection-level "model under high load" notice: no attempt counter and no sessionId, so it is a bridge-scope broadcast (chatID may be empty). The message is surfaced verbatim as an auto-clearing banner; level (info/warning/error) is decoded for forward-compatibility but not separately surfaced — banner styling keys off the error code.

func (*Translator) HandleToolCall

func (t *Translator) HandleToolCall(ctx context.Context, chatID api.ChatID, raw json.RawMessage, subSessionID string)

HandleToolCall adds a tool call to the current assistant message buffer and broadcasts it. On v3 (KAS) a subagent is an ordinary tool call tagged _meta.kiro.kind=="agent-subtask"; AgentSubtaskID is threaded onto the domain ToolCall so the client can render a subagent card and nest the subagent's chunks (which carry the same id) under it.

func (*Translator) HandleToolCallUpdate

func (t *Translator) HandleToolCallUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage, subSessionID string)

HandleToolCallUpdate mutates an in-flight tool call's status and appends any new output chunks.

func (*Translator) HandleUsageUpdate added in v0.1.165

func (t *Translator) HandleUsageUpdate(ctx context.Context, chatID api.ChatID, raw json.RawMessage)

HandleUsageUpdate folds v3 usage_update into the chat's usage (context %, window size, and credits). Parent attribution is handled by ignoreSubSession in the dispatch table.

func (*Translator) HandleUserInput added in v0.2.43

func (t *Translator) HandleUserInput(ctx context.Context, chatID api.ChatID, msg *api.RPCResponse)

HandleUserInput processes a _kiro/userInput request from KAS (2.14+): the agent's user_input tool asked a structured question (plan-mode clarification, spec gate) and KAS forwarded it because we advertised the _meta.kiro.userInput initialize capability. We surface a question dialog and the eventual reply is sent by CmdUserInputResponse via bridge.Respond ({action:"answered", answer} — anything else advances the agent to its next phase upstream).

The request is a real JSON-RPC request (envelope id present), routed here like _kiro/mcp/elicitation; the correlation id is msg.ID. The pending-permissions tracker replays the dialog on reconnect, exactly like a permission prompt. The question also arrives as a pending tool_call (kind "other", _meta.kiro.toolId "user_input") that KAS completes itself once answered — no tool bookkeeping here.

func (*Translator) MCP

func (t *Translator) MCP() MCPRecorder

MCP returns the MCP state recorder sub-interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL