feat(ios): align chat library memory and tasks with web

This commit is contained in:
snowspeeder committed 2026-10-04 10:41:17 -04:00
1 parent 8905dc2c2a
commit 235dd65487
12 files changed
+957 -128

No files matched your search

+19 -3
View File
@@ -20,7 +20,22 @@ struct HopeMessage: Codable, Identifiable, Equatable {
var status: String
var runId: String?
var createdAt: Double
// Attachments are deliberately unavailable until their contract is qualified.
var attachments: [String] = []
enum CodingKeys: String, CodingKey { case id, conversationId, role, content, status, runId, createdAt, attachments }
init(id: String, conversationId: String, role: String, content: String, status: String, runId: String?, createdAt: Double, attachments: [String] = []) {
self.id = id; self.conversationId = conversationId; self.role = role; self.content = content; self.status = status; self.runId = runId; self.createdAt = createdAt; self.attachments = attachments
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
conversationId = try values.decode(String.self, forKey: .conversationId)
role = try values.decode(String.self, forKey: .role)
content = try values.decode(String.self, forKey: .content)
status = try values.decode(String.self, forKey: .status)
runId = try values.decodeIfPresent(String.self, forKey: .runId)
createdAt = try values.decode(Double.self, forKey: .createdAt)
attachments = try values.decodeIfPresent([String].self, forKey: .attachments) ?? []
}
}
struct HopeRun: Codable, Identifiable, Equatable {
var id: String
@@ -188,7 +203,7 @@ final class OfflineChatAPI: ChatTransport {
if method == "GET" { return try JSONSerialization.data(withJSONObject: [kind: featureObjects[kind] ?? [], "next_cursor": NSNull()]) }
var item = body; item["id"] = UUID().uuidString; item["created_at"] = now; item["updated_at"] = now
if kind == "artifacts" { item["revision"] = 1 }
if kind == "tasks" { item["enabled"] = true }
if kind == "tasks" { item["enabled"] = !(body["paused"] as? Bool ?? false) }
featureObjects[kind, default: []].insert(item, at: 0)
return try JSONSerialization.data(withJSONObject: [singular: item])
}
@@ -223,7 +238,8 @@ final class OfflineChatAPI: ChatTransport {
if method == "POST" { let c = HopeConversation(id: UUID().uuidString, title: body["title"] as? String ?? "New chat", workspaceId: body["workspace_id"] as? String, pinned: false, pinRank: 0, archived: false, temporary: false, revision: 1, createdAt: now, updatedAt: now); conversations.insert(c, at: 0); return try wrap("conversation", c) }
let q = query.first(where: { $0.name == "q" })?.value ?? ""
let archived = query.first(where: { $0.name == "archived" })?.value == "true"
let filtered = conversations.filter { $0.archived == archived && (q.isEmpty || $0.title.localizedCaseInsensitiveContains(q) || (messages[$0.id] ?? []).contains { $0.content.localizedCaseInsensitiveContains(q) }) }
let workspace = query.first(where: { $0.name == "workspace_id" })?.value
let filtered = conversations.filter { $0.archived == archived && (workspace == nil || $0.workspaceId == workspace) && (q.isEmpty || $0.title.localizedCaseInsensitiveContains(q) || (messages[$0.id] ?? []).contains { $0.content.localizedCaseInsensitiveContains(q) }) }
return try JSONSerialization.data(withJSONObject: ["conversations": JSONSerialization.jsonObject(with: encode(filtered)), "next_cursor": NSNull()])
}
if parts.count >= 3, parts[1] == "conversations", let index = conversations.firstIndex(where: { $0.id == parts[2] }) {