Watch: activity line on working cards, All threads browsing, a thread's subagents, Load earlier, and markdown messages

This commit is contained in:
gib committed 2026-11-02 13:10:02 -05:00
1 parent abd2fd0e13
commit d916bfbab5
21 files changed
+928 -46

No files matched your search

+23 -6
View File
@@ -46,6 +46,8 @@ protocol GatewayAPI: Sendable {
func feed() async throws -> Feed
func summary() async throws -> Summary
func thread(hostId: String, threadId: String) async throws -> ThreadDetail
/// The 20 messages before `cursor`, a thread's `earlierCursor` or the last page's.
func history(hostId: String, threadId: String, cursor: String) async throws -> HistoryResponse
func sendMessage(hostId: String, threadId: String, text: String) async throws
func respond(hostId: String, threadId: String, request: RespondRequest) async throws
func stop(hostId: String, threadId: String) async throws
@@ -58,6 +60,8 @@ protocol GatewayAPI: Sendable {
/// Settles every completed thread in Recent. Failed threads stay.
func settleRecent() async throws -> SettleRecentResponse
func projects() async throws -> ProjectsResponse
/// A project's newest 50 threads, settled ones included. Subagents are left out.
func projectThreads(hostId: String, projectId: String) async throws -> ThreadListResponse
func newThread(_ request: NewThreadRequest) async throws -> NewThreadResponse
func registerPush(_ registration: PushRegistration) async throws
func status() async throws -> StatusResponse
@@ -93,6 +97,10 @@ struct GatewayClient: GatewayAPI {
try await call("GET", ["v1", "threads", hostId, threadId])
}
func history(hostId: String, threadId: String, cursor: String) async throws -> HistoryResponse {
try await call("GET", ["v1", "threads", hostId, threadId, "history"], query: [URLQueryItem(name: "cursor", value: cursor)])
}
func sendMessage(hostId: String, threadId: String, text: String) async throws {
try await callIgnoringBody("POST", ["v1", "threads", hostId, threadId, "messages"], body: SendMessageRequest(text: text))
}
@@ -125,6 +133,10 @@ struct GatewayClient: GatewayAPI {
try await call("GET", ["v1", "projects"])
}
func projectThreads(hostId: String, projectId: String) async throws -> ThreadListResponse {
try await call("GET", ["v1", "projects", hostId, projectId, "threads"])
}
func newThread(_ request: NewThreadRequest) async throws -> NewThreadResponse {
try await call("POST", ["v1", "threads"], body: request)
}
@@ -155,14 +167,16 @@ struct GatewayClient: GatewayAPI {
// MARK: - Plumbing
private func call<Response: Decodable>(_ method: String, _ path: [String]) async throws -> Response {
try await call(method, path, body: nil as SendMessageRequest?)
private func call<Response: Decodable>(
_ method: String, _ path: [String], query: [URLQueryItem] = []
) async throws -> Response {
try await call(method, path, query: query, body: nil as SendMessageRequest?)
}
private func call<Body: Encodable, Response: Decodable>(
_ method: String, _ path: [String], body: Body?
_ method: String, _ path: [String], query: [URLQueryItem] = [], body: Body?
) async throws -> Response {
let data = try await perform(method, path, body: body)
let data = try await perform(method, path, query: query, body: body)
do {
return try JSON.decoder().decode(Response.self, from: data)
} catch {
@@ -174,9 +188,12 @@ struct GatewayClient: GatewayAPI {
_ = try await perform(method, path, body: body)
}
private func perform<Body: Encodable>(_ method: String, _ path: [String], body: Body?) async throws -> Data {
private func perform<Body: Encodable>(
_ method: String, _ path: [String], query: [URLQueryItem] = [], body: Body?
) async throws -> Data {
// appending(component:) percent-encodes each segment, slashes included.
let url = path.reduce(baseURL) { $0.appending(component: $1) }
var url = path.reduce(baseURL) { $0.appending(component: $1) }
if !query.isEmpty { url.append(queryItems: query) }
var request = URLRequest(url: url, timeoutInterval: 20)
request.httpMethod = method
request.setValue("application/json", forHTTPHeaderField: "Accept")