package format import ( "fmt" "strings" ) // Formatting constants for the 9P chat log protocol. // Used by fs.NewEventHandler and fs.replayMessagesToLog. // // Block format: [[[role:name#blockId]]] or [[[role#blockId]]] // The blockId is a hex string that uniquely identifies the block. const ( // EndTag marks the end of a message block. EndTag = "[[[end]]]" // RoleTag formats a role delimiter, e.g. "[[[user]]]". RoleTag = "[[[%s]]]" // ToolFence is the opening fence for tool output blocks. ToolFence = "```\n" // AssistantHeaderPrefix is prepended to assistant headers when a response ID is present. AssistantHeaderPrefix = "[[[assistant:" // AssistantTag is the bare assistant header (no response ID). AssistantTag = "[[[assistant]]]" // UserTag is the user role delimiter. UserTag = "[[[user]]]" // RetryTag is the retry role delimiter. RetryTag = "[[[retry]]]" // ToolDelimPrefix is the prefix for tool delimiters, e.g. "[[[tool:shell]]]". ToolDelimPrefix = "[[[tool:" // ToolTag is the bare tool role delimiter (no name). ToolTag = "[[[tool]]]" // CallDelimPrefix is the prefix for call delimiters, e.g. "[[[call:file_read]]]". CallDelimPrefix = "[[[call:" ) // RoleDelim returns a formatted role delimiter. func RoleDelim(role string) string { return fmt.Sprintf(RoleTag, role) } // ToolDelim returns a formatted tool delimiter. func ToolDelim(name string) string { return fmt.Sprintf(ToolDelimPrefix+"%s]]]\n", name) } // CallDelim returns a formatted call delimiter. func CallDelim(name string) string { return fmt.Sprintf(CallDelimPrefix+"%s]]]\n", name) } // BlockDelim returns a formatted block delimiter with a block ID. // Format: [[[role#blockId]]] or [[[role:name#blockId]]] func BlockDelim(role, name, blockID string) string { if name != "" { return fmt.Sprintf("[[[%s:%s#%s]]]\n", role, name, blockID) } return fmt.Sprintf("[[[%s#%s]]]\n", role, blockID) } // BlockHeader is a single-line [[[...]]] marker that opens a log block. type BlockHeader struct { Role string // e.g. "user", "assistant", "tool", "retry", or "tool:shell" Name string // for tool/call: the sub-name (e.g. "shell"), empty for bare roles BlockID string // unique block identifier (hex), empty if not present } // ParseBlockHeader extracts a BlockHeader from a line like "[[[tool:shell#a1b2]]]" // or "[[[assistant]]]". Returns nil if the line is not a valid block header. func ParseBlockHeader(line string) *BlockHeader { const prefix, suffix = "[[[", "]]]" if !strings.HasPrefix(line, prefix) || !strings.HasSuffix(line, suffix) { return nil } inner := strings.TrimPrefix(line, prefix) inner = strings.TrimSuffix(inner, suffix) if inner == "" { return nil } bh := &BlockHeader{Role: inner} // Extract blockId if present (after #) if idx := strings.LastIndex(inner, "#"); idx >= 0 { bh.BlockID = inner[idx+1:] inner = inner[:idx] bh.Role = inner } // Extract name if present (after :) if idx := strings.Index(inner, ":"); idx >= 0 { bh.Role = inner[:idx] bh.Name = inner[idx+1:] } return bh } // IsEndMarker reports whether the line is exactly the [[[end]]] closing marker. func IsEndMarker(line string) bool { return line == EndTag }