

























@@ -6,30 +6,162 @@ import OSLog
6677struct IOSGatewayChatTransport: OpenClawChatTransport {
88private static let logger = Logger(subsystem: "ai.openclaw", category: "ios.chat.transport")
9+static let defaultChatSendTimeoutMs = 30000
910private let gateway: GatewayNodeSession
101112+private struct CreateSessionParams: Codable {
13+var key: String
14+var label: String?
15+var parentSessionKey: String?
16+}
17+18+private struct RunParams: Codable {
19+var sessionKey: String
20+var runId: String
21+}
22+23+private struct ListSessionsParams: Codable {
24+var includeGlobal: Bool
25+var includeUnknown: Bool
26+var limit: Int?
27+}
28+29+private struct SessionKeyParams: Codable {
30+var key: String
31+}
32+33+private struct ChatSendParams: Codable {
34+var sessionKey: String
35+var message: String
36+var thinking: String
37+var attachments: [OpenClawChatAttachmentPayload]?
38+var timeoutMs: Int
39+var idempotencyKey: String
40+}
41+42+private struct AgentWaitParams: Codable {
43+var runId: String
44+var timeoutMs: Int
45+}
46+47+private struct AgentWaitResponse: Codable {
48+var runId: String?
49+var status: String?
50+var error: String?
51+}
52+53+struct AgentWaitCompletion: Equatable {
54+var runId: String
55+var status: String
56+var completed: Bool
57+}
58+59+static func isAgentWaitCompletionStatus(_ status: String) -> Bool {
60+switch status.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
61+case "ok", "completed", "success", "succeeded":
62+true
63+default:
64+false
65+}
66+}
67+1168init(gateway: GatewayNodeSession) {
1269self.gateway = gateway
1370}
147115-func abortRun(sessionKey: String, runId: String) async throws {
16-struct Params: Codable {
17-var sessionKey: String
18-var runId: String
72+static func agentWaitRequestTimeoutSeconds(timeoutMs: Int) -> Int {
73+max(1, Int(ceil(Double(timeoutMs) / 1000.0)) + 5)
74+}
75+76+static func makeListSessionsParamsJSON(limit: Int?) throws -> String {
77+try self.encodeParams(ListSessionsParams(includeGlobal: true, includeUnknown: false, limit: limit))
78+}
79+80+static func makeChatSendParamsJSON(
81+ sessionKey: String,
82+ message: String,
83+ thinking: String,
84+ idempotencyKey: String,
85+ attachments: [OpenClawChatAttachmentPayload]) throws -> String
86+{
87+let params = ChatSendParams(
88+ sessionKey: sessionKey,
89+ message: message,
90+ thinking: thinking,
91+ attachments: attachments.isEmpty ? nil : attachments,
92+ timeoutMs: self.defaultChatSendTimeoutMs,
93+ idempotencyKey: idempotencyKey)
94+return try self.encodeParams(params)
95+}
96+97+static func decodeAgentWaitCompletion(_ data: Data, fallbackRunId: String) throws -> AgentWaitCompletion {
98+let decoded = try JSONDecoder().decode(AgentWaitResponse.self, from: data)
99+let status = (decoded.status ?? "unknown").lowercased()
100+return AgentWaitCompletion(
101+ runId: decoded.runId ?? fallbackRunId,
102+ status: status,
103+ completed: self.isAgentWaitCompletionStatus(status))
104+}
105+106+private static func makeCreateSessionParamsJSON(
107+ key: String,
108+ label: String?,
109+ parentSessionKey: String?) throws -> String
110+{
111+let params = CreateSessionParams(
112+ key: key,
113+ label: label,
114+ parentSessionKey: parentSessionKey)
115+return try self.encodeParams(params)
116+}
117+118+private static func makeRunParamsJSON(sessionKey: String, runId: String) throws -> String {
119+try self.encodeParams(RunParams(sessionKey: sessionKey, runId: runId))
120+}
121+122+private static func makeSessionKeyParamsJSON(_ sessionKey: String) throws -> String {
123+try self.encodeParams(SessionKeyParams(key: sessionKey))
124+}
125+126+private static func makeHistoryParamsJSON(sessionKey: String) throws -> String {
127+struct Params: Codable { var sessionKey: String }
128+return try self.encodeParams(Params(sessionKey: sessionKey))
129+}
130+131+private static func makeAgentWaitParamsJSON(runId: String, timeoutMs: Int) throws -> String {
132+try self.encodeParams(AgentWaitParams(runId: runId, timeoutMs: timeoutMs))
133+}
134+135+private static func encodeParams(_ params: some Encodable) throws -> String {
136+let data = try JSONEncoder().encode(params)
137+guard let json = String(bytes: data, encoding: .utf8) else {
138+throw EncodingError.invalidValue(
139+ params,
140+EncodingError.Context(codingPath: [], debugDescription: "Encoded gateway params were not UTF-8"))
19141}
20-let data = try JSONEncoder().encode(Params(sessionKey: sessionKey, runId: runId))
21-let json = String(data: data, encoding: .utf8)
142+return json
143+}
144+145+func createSession(
146+ key: String,
147+ label: String?,
148+ parentSessionKey: String?) async throws -> OpenClawChatCreateSessionResponse
149+{
150+let json = try Self.makeCreateSessionParamsJSON(
151+ key: key,
152+ label: label,
153+ parentSessionKey: parentSessionKey)
154+let res = try await self.gateway.request(method: "sessions.create", paramsJSON: json, timeoutSeconds: 15)
155+return try JSONDecoder().decode(OpenClawChatCreateSessionResponse.self, from: res)
156+}
157+158+func abortRun(sessionKey: String, runId: String) async throws {
159+let json = try Self.makeRunParamsJSON(sessionKey: sessionKey, runId: runId)
22160 _ = try await self.gateway.request(method: "chat.abort", paramsJSON: json, timeoutSeconds: 10)
23161}
2416225163func listSessions(limit: Int?) async throws -> OpenClawChatSessionsListResponse {
26-struct Params: Codable {
27-var includeGlobal: Bool
28-var includeUnknown: Bool
29-var limit: Int?
30-}
31-let data = try JSONEncoder().encode(Params(includeGlobal: true, includeUnknown: false, limit: limit))
32-let json = String(data: data, encoding: .utf8)
164+let json = try Self.makeListSessionsParamsJSON(limit: limit)
33165let res = try await self.gateway.request(method: "sessions.list", paramsJSON: json, timeoutSeconds: 15)
34166return try JSONDecoder().decode(OpenClawChatSessionsListResponse.self, from: res)
35167}
@@ -40,23 +172,17 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
40172}
4117342174func resetSession(sessionKey: String) async throws {
43-struct Params: Codable { var key: String }
44-let data = try JSONEncoder().encode(Params(key: sessionKey))
45-let json = String(data: data, encoding: .utf8)
175+let json = try Self.makeSessionKeyParamsJSON(sessionKey)
46176 _ = try await self.gateway.request(method: "sessions.reset", paramsJSON: json, timeoutSeconds: 10)
47177}
4817849179func compactSession(sessionKey: String) async throws {
50-struct Params: Codable { var key: String }
51-let data = try JSONEncoder().encode(Params(key: sessionKey))
52-let json = String(data: data, encoding: .utf8)
180+let json = try Self.makeSessionKeyParamsJSON(sessionKey)
53181 _ = try await self.gateway.request(method: "sessions.compact", paramsJSON: json, timeoutSeconds: 10)
54182}
5518356184func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload {
57-struct Params: Codable { var sessionKey: String }
58-let data = try JSONEncoder().encode(Params(sessionKey: sessionKey))
59-let json = String(data: data, encoding: .utf8)
185+let json = try Self.makeHistoryParamsJSON(sessionKey: sessionKey)
60186let res = try await self.gateway.request(method: "chat.history", paramsJSON: json, timeoutSeconds: 15)
61187return try JSONDecoder().decode(OpenClawChatHistoryPayload.self, from: res)
62188}
@@ -73,35 +199,52 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
73199+ "len=\(message.count) attachments=\(attachments.count)"
74200Self.logger.info(
75201"\(startLogMessage, privacy: .public)")
76-struct Params: Codable {
77-var sessionKey: String
78-var message: String
79-var thinking: String
80-var attachments: [OpenClawChatAttachmentPayload]?
81-var timeoutMs: Int
82-var idempotencyKey: String
83-}
84-85-let params = Params(
202+GatewayDiagnostics.log(startLogMessage)
203+let json = try Self.makeChatSendParamsJSON(
86204 sessionKey: sessionKey,
87205 message: message,
88206 thinking: thinking,
89- attachments: attachments.isEmpty ? nil : attachments,
90- timeoutMs: 30000,
91- idempotencyKey: idempotencyKey)
92-let data = try JSONEncoder().encode(params)
93-let json = String(data: data, encoding: .utf8)
207+ idempotencyKey: idempotencyKey,
208+ attachments: attachments)
94209do {
95210let res = try await self.gateway.request(method: "chat.send", paramsJSON: json, timeoutSeconds: 35)
96211let decoded = try JSONDecoder().decode(OpenClawChatSendResponse.self, from: res)
97212Self.logger.info("chat.send ok runId=\(decoded.runId, privacy: .public)")
213+GatewayDiagnostics.log("chat.send ok runId=\(decoded.runId) status=\(decoded.status)")
98214return decoded
99215} catch {
100216Self.logger.error("chat.send failed \(error.localizedDescription, privacy: .public)")
217+GatewayDiagnostics.log("chat.send failed error=\(error.localizedDescription)")
101218throw error
102219}
103220}
104221222+func waitForRunCompletion(runId rawRunId: String, timeoutMs: Int) async -> Bool {
223+let runId = rawRunId.trimmingCharacters(in: .whitespacesAndNewlines)
224+guard !runId.isEmpty else { return false }
225+226+do {
227+let json = try Self.makeAgentWaitParamsJSON(runId: runId, timeoutMs: timeoutMs)
228+let requestTimeoutSeconds = Self.agentWaitRequestTimeoutSeconds(timeoutMs: timeoutMs)
229+GatewayDiagnostics.log("agent.wait start runId=\(runId)")
230+let res = try await self.gateway.request(
231+ method: "agent.wait",
232+ paramsJSON: json,
233+ timeoutSeconds: requestTimeoutSeconds)
234+let completion = try Self.decodeAgentWaitCompletion(res, fallbackRunId: runId)
235+GatewayDiagnostics.log("agent.wait completed runId=\(completion.runId) status=\(completion.status)")
236+if !completion.completed {
237+Self.logger.warning(
238+"agent.wait status \(completion.status, privacy: .public) runId=\(runId, privacy: .public)")
239+}
240+return completion.completed
241+} catch {
242+Self.logger.warning("agent.wait failed \(error.localizedDescription, privacy: .public)")
243+GatewayDiagnostics.log("agent.wait failed runId=\(runId) error=\(error.localizedDescription)")
244+return false
245+}
246+}
247+105248func requestHealth(timeoutMs: Int) async throws -> Bool {
106249let seconds = max(1, Int(ceil(Double(timeoutMs) / 1000.0)))
107250let res = try await self.gateway.request(method: "health", paramsJSON: nil, timeoutSeconds: seconds)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。