Skip to content

Commit c2262aa

Browse files
committed
fix(ios): preserve agent ownership for session mutations
1 parent aa88006 commit c2262aa

3 files changed

Lines changed: 25 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
3030
- **Chutes OAuth deadlines:** bound token exchange, profile lookup, and refresh requests, and keep issued tokens when optional userinfo enrichment stalls. (#102026) Thanks @Alix-007.
3131
- **Control UI workspace avatars:** inline validated agent avatar files in bootstrap and identity responses so Personal card images render without unauthenticated avatar-route requests, while preserving configured emoji precedence. (#102892, #97602) Thanks @LZY3538.
3232
- **Exec safe-bin flags:** auto-approve curated read-only boolean flags for default stdin-only filters while keeping unknown flags, tail follow/retry modes, file operands, and custom profiles fail-closed. (#88953) Thanks @yetval.
33+
- **iOS session mutations:** scope rename, archive, pin, delete, and fork requests to the selected agent, preserving the parent agent for forked sessions so multi-agent chat actions cannot mutate or create sessions under the wrong agent. (#103366, #103415) Thanks @lin-hongkuan and @harjothkhara.
3334
- **Model pin hot reload and fallback:** keep explicit `/model` selections authoritative across Telegram config reloads and model fallback, capture one live config snapshot per assembled turn, and leave fallback candidates turn-local instead of persisting them over the user's pin. (#103324, #103417) Thanks @obviyus.
3435
- **Swift protocol initializers:** default every schema-optional generated initializer parameter to `nil` so additive protocol fields no longer break SDK construction call sites.
3536
- **OpenCode Go MiMo catalog:** stop exposing the deprecated `mimo-v2-omni` and `mimo-v2-pro` aliases that reject agent requests, and keep release validation on the active MiMo V2.5 routes. (#103311, #103329) Thanks @krissding.

apps/ios/Sources/Chat/IOSGatewayChatTransport.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
461461
label: label,
462462
parentSessionKey: parentTarget?.sessionKey,
463463
worktree: worktree)
464-
let res = try await self.gateway.request(method: "sessions.create", paramsJSON: json, timeoutSeconds: 15)
464+
let res = try await gateway.request(method: "sessions.create", paramsJSON: json, timeoutSeconds: 15)
465465
return try JSONDecoder().decode(OpenClawChatCreateSessionResponse.self, from: res)
466466
}
467467

@@ -480,12 +480,12 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
480480
archived: Bool) async throws -> OpenClawChatSessionsListResponse
481481
{
482482
let json = try Self.makeListSessionsParamsJSON(limit: limit, search: search, archived: archived)
483-
let res = try await self.gateway.request(method: "sessions.list", paramsJSON: json, timeoutSeconds: 15)
483+
let res = try await gateway.request(method: "sessions.list", paramsJSON: json, timeoutSeconds: 15)
484484
return try JSONDecoder().decode(OpenClawChatSessionsListResponse.self, from: res)
485485
}
486486

487487
func listModels() async throws -> [OpenClawChatModelChoice] {
488-
let response = try await self.gateway.request(
488+
let response = try await gateway.request(
489489
method: "models.list",
490490
paramsJSON: nil,
491491
timeoutSeconds: 15)
@@ -532,10 +532,11 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
532532

533533
func forkSession(parentKey: String) async throws -> String {
534534
let target = self.sessionTarget(for: parentKey)
535+
let childAgentID = target.agentID ?? Self.agentID(fromSessionKey: target.sessionKey)
535536
let json = try Self.makeForkSessionParamsJSON(
536537
parentKey: target.sessionKey,
537-
agentId: target.agentID)
538-
let response = try await self.requestSessionMutation(
538+
agentId: childAgentID)
539+
let response = try await requestSessionMutation(
539540
method: "sessions.create",
540541
paramsJSON: json,
541542
timeoutSeconds: 15)

apps/ios/Tests/IOSGatewayChatTransportTests.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ struct IOSGatewayChatTransportTests {
106106
}
107107

108108
@Test func `list sessions params request archived sessions explicitly`() throws {
109-
let params = try self.object(
109+
let params = try object(
110110
from: IOSGatewayChatTransport.makeListSessionsParamsJSON(limit: 12, archived: true))
111111
#expect(params["archived"] as? Bool == true)
112112
}
113113

114114
@Test func `patch session params preserve explicit null clearing`() throws {
115-
let params = try self.object(
115+
let params = try object(
116116
from: IOSGatewayChatTransport.makePatchSessionParamsJSON(
117117
key: "session-1",
118118
label: .some(nil),
@@ -128,7 +128,7 @@ struct IOSGatewayChatTransportTests {
128128
}
129129

130130
@Test func `patch session params include selected global agent`() throws {
131-
let params = try self.object(
131+
let params = try object(
132132
from: IOSGatewayChatTransport.makePatchSessionParamsJSON(
133133
key: "global",
134134
agentId: "reviewer",
@@ -139,7 +139,7 @@ struct IOSGatewayChatTransportTests {
139139
}
140140

141141
@Test func `fork session params preserve parent agent`() throws {
142-
let params = try self.object(
142+
let params = try object(
143143
from: IOSGatewayChatTransport.makeForkSessionParamsJSON(
144144
parentKey: "agent:reviewer:telegram:group:1",
145145
agentId: "reviewer"))
@@ -149,7 +149,7 @@ struct IOSGatewayChatTransportTests {
149149
}
150150

151151
@Test func `session model patch params include model and selected agent`() throws {
152-
let params = try self.object(
152+
let params = try object(
153153
from: IOSGatewayChatTransport.makeSessionPatchModelParamsJSON(
154154
sessionKey: "global",
155155
agentId: "reviewer",
@@ -160,7 +160,7 @@ struct IOSGatewayChatTransportTests {
160160
}
161161

162162
@Test func `session model patch params encode default model as null`() throws {
163-
let params = try self.object(
163+
let params = try object(
164164
from: IOSGatewayChatTransport.makeSessionPatchModelParamsJSON(
165165
sessionKey: "agent:main:main",
166166
model: nil))
@@ -304,27 +304,27 @@ struct IOSGatewayChatTransportTests {
304304
let requests = await recorder.all()
305305
#expect(requests.map(\.method) == Array(
306306
repeating: ["sessions.patch", "sessions.delete", "sessions.create"],
307-
count: 3).flatMap { $0 })
307+
count: 3).flatMap(\.self))
308308
#expect(requests.map(\.timeoutSeconds) == Array(repeating: 15, count: 9))
309309

310-
for (offset, expectedKey, expectedAgentID) in [
311-
(0, "agent:reviewer:Matrix:Channel:Room", nil),
312-
(3, "global", "reviewer"),
313-
(6, "agent:ops:main", nil),
314-
] as [(Int, String, String?)] {
315-
let patch = try self.object(from: requests[offset].paramsJSON)
310+
for (offset, expectedKey, expectedMutationAgentID, expectedForkAgentID) in [
311+
(0, "agent:reviewer:Matrix:Channel:Room", nil, "reviewer"),
312+
(3, "global", "reviewer", "reviewer"),
313+
(6, "agent:ops:main", nil, "ops"),
314+
] as [(Int, String, String?, String?)] {
315+
let patch = try object(from: requests[offset].paramsJSON)
316316
#expect(patch["key"] as? String == expectedKey)
317-
#expect(patch["agentId"] as? String == expectedAgentID)
317+
#expect(patch["agentId"] as? String == expectedMutationAgentID)
318318
#expect(patch["pinned"] as? Bool == true)
319319

320-
let delete = try self.object(from: requests[offset + 1].paramsJSON)
320+
let delete = try object(from: requests[offset + 1].paramsJSON)
321321
#expect(delete["key"] as? String == expectedKey)
322-
#expect(delete["agentId"] as? String == expectedAgentID)
322+
#expect(delete["agentId"] as? String == expectedMutationAgentID)
323323
#expect(delete["deleteTranscript"] as? Bool == true)
324324

325-
let fork = try self.object(from: requests[offset + 2].paramsJSON)
325+
let fork = try object(from: requests[offset + 2].paramsJSON)
326326
#expect(fork["parentSessionKey"] as? String == expectedKey)
327-
#expect(fork["agentId"] as? String == expectedAgentID)
327+
#expect(fork["agentId"] as? String == expectedForkAgentID)
328328
#expect(fork["fork"] as? Bool == true)
329329
}
330330
}

0 commit comments

Comments
 (0)