Skip to content

D-7: AgentChat package + domain types #257

@kirich1409

Description

@kirich1409

Description

Create the AgentChat SPM package — pure domain + TCA layer, no SwiftUI, no gRPC, no SwiftTerm. Defines the abstract model for an agent conversation (messages, tool calls, thinking, approval) and the AgentChatSession protocol.

Spec: Epic #250 §4.1 (packages), §4.4 (AgentStreamEvent designed close to ACP shape); docs/architecture/dialogue-events.md Appendix A (AgentStreamEvent model).

Scope

Package skeleton

MacApp/Packages/AgentChat/Package.swift:

  • swift-tools-version: 6.3
  • .swiftLanguageMode(.v6) on all targets
  • Platforms: .macOS(.v26)
  • Targets: AgentChat (lib) + AgentChatTests
  • Dependencies: TerminalAbstraction (for rawStdout consumption in later tasks), SharedModels, ComposableArchitecture

Public types

Structure under Sources/AgentChat/Model/:

  • AgentMessage.swiftstruct AgentMessage: Identifiable, Equatable, Sendable with id: MessageID (value class wrapping String), role: MessageRole, content: [ContentSegment], createdAt: Date, model: String?, stopReason: StopReason?, usage: TokenUsage?, error: AssistantError?.
  • MessageRole.swift.user / .assistant / .system.
  • ContentSegment.swift — enum: .text(String) (streamed), .thinking(Thinking), .toolCall(ToolCallID) (reference — tool cards live in separate list), .citation(Citation).
  • ToolCall.swiftstruct ToolCall: Identifiable, Equatable, Sendable with id: ToolCallID, name: String, kind: ToolKind (inferred: .read / .edit / .execute / .search / .fetch / .think / .subagent / .other — derived from tool name, aligned with ACP kind), input: JSONValue, status: ToolCallStatus (.pending / .inProgress(startedAt:) / .completed(at:duration:output:) / .failed(at:reason:)), parentMessageID: MessageID, parentToolUseID: ToolCallID? (for subagent nesting).
  • ApprovalRequest.swift — placeholder for future (MVP uses bypassPermissions; keep type in model for forward-compat).
  • AgentStreamEvent.swift — sealed enum as in Appendix A of events spec. ACP-compatible shape.
  • AgentChatStatus.swift.idle / .streaming / .thinking / .awaitingApproval / .error(Error) / .terminated.
  • SessionInitInfo.swift, SessionResult.swift, TokenUsage.swift, StopReason.swift, ToolKind.swift, RetryReason.swift, RateLimitInfo.swift, CompactTrigger.swift, JSONValue.swift (minimal local type for opaque tool inputs).

Value classes for IDs

Wrap strings in value class (Swift term for struct with single field + equality):
```swift
public struct MessageID: Hashable, Sendable, Codable { public let rawValue: String }
public struct ToolCallID: Hashable, Sendable, Codable { public let rawValue: String }
public struct SessionID: Hashable, Sendable, Codable { public let rawValue: String }
```

AgentChatSession protocol

```swift
@mainactor
public protocol AgentChatSession: AnyObject, Sendable {
var id: SessionID { get }
var kind: AgentKind { get }
var status: AgentChatStatus { get }
var transcript: AsyncStream { get }

func start() async throws
func send(_ input: AgentInput) async throws
func interrupt() async
func approve(_ request: ApprovalRequest, decision: ApprovalDecision) async throws
func terminate() async

}

public enum AgentInput: Sendable {
case text(String)
case attachment(Data, mimeType: String) // future
}
```

Acceptance Criteria

  • AgentChat package created with swift-tools-version: 6.3, Swift 6 language mode.
  • All model types declared in their own files (one public type per file).
  • Codable roundtrip tests for AgentMessage, ToolCall, AgentStreamEvent (at least happy path per variant).
  • AgentChatSession protocol compiles under SWIFT_STRICT_CONCURRENCY=complete.
  • Value-class IDs: MessageID, ToolCallID, SessionID present.
  • No imports from AgentChatUI, TerminalSwiftTerm, RemoteTerminal — AgentChat is pure domain.
  • Package builds + tests pass: swift build, swift test in MacApp/Packages/AgentChat/.
  • Added to root Package.swift / Relay.xcodeproj as referenced package (empty consumers allowed; D-10 will consume).

Relationships

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions