





















@@ -14,7 +14,50 @@ enum AppleReviewDemoMode {
1414}
15151616static var agents: [AgentSummary] {
17-[
17+LocalChatFixture.appleReviewDemo.agents
18+}
19+}
20+21+enum ScreenshotFixtureMode {
22+static let gatewayName = "OpenClaw Gateway"
23+static let gatewayAddress = "Mac Studio on local network"
24+static let gatewayID = "screenshot-fixture-gateway"
25+26+static var agents: [AgentSummary] {
27+LocalChatFixture.appScreenshots.agents
28+}
29+}
30+31+struct LocalChatFixture {
32+let sessionKey: String
33+let sessionIDPrefix: String
34+let displayName: String
35+let subject: String
36+let workspace: String
37+let modelProvider: String
38+let modelID: String
39+let modelName: String
40+let responsePrefix: String
41+let seedMessages: [String]
42+let agents: [AgentSummary]
43+44+static let appleReviewDemo = LocalChatFixture(
45+ sessionKey: "main",
46+ sessionIDPrefix: "apple-review-demo",
47+ displayName: "Apple Review Demo",
48+ subject: "Gateway review flow",
49+ workspace: "Apple Review Demo",
50+ modelProvider: "demo",
51+ modelID: "local-demo",
52+ modelName: "Apple Review Demo",
53+ responsePrefix: "Demo mode is active.",
54+ seedMessages: [
55+"""
56+ Apple Review demo mode is active. This local chat transport lets reviewers inspect the iOS app \
57+ without a private Gateway.
58+ """,
59+],
60+ agents: [
1861AgentSummary(
1962 id: "main",
2063 name: "Main",
@@ -25,12 +68,70 @@ enum AppleReviewDemoMode {
2568 thinkinglevels: nil,
2669 thinkingoptions: ["auto", "low", "medium"],
2770 thinkingdefault: "auto"),
28-]
29-}
71+])
72+73+static let appScreenshots = LocalChatFixture(
74+ sessionKey: "main",
75+ sessionIDPrefix: "screenshot-fixture",
76+ displayName: "Molty",
77+ subject: "Mobile command center",
78+ workspace: "OpenClaw",
79+ modelProvider: "openai",
80+ modelID: "gpt-5.5",
81+ modelName: "GPT-5.5",
82+ responsePrefix: "OpenClaw is connected to your gateway.",
83+ seedMessages: [
84+"""
85+ OpenClaw is connected to your gateway. I can coordinate agents, inspect project context, and prepare \
86+ actions from your phone.
87+ """,
88+"""
89+ The Molty agent is ready. Recent context, voice controls, and gateway settings are available \
90+ across the app.
91+ """,
92+],
93+ agents: [
94+AgentSummary(
95+ id: "main",
96+ name: "Molty",
97+ identity: ["emoji": AnyCodable("M")],
98+ workspace: "OpenClaw",
99+ model: ["provider": AnyCodable("openai"), "model": AnyCodable("gpt-5.5")],
100+ agentruntime: ["kind": AnyCodable("gateway")],
101+ thinkinglevels: nil,
102+ thinkingoptions: ["auto", "low", "medium", "high"],
103+ thinkingdefault: "auto"),
104+AgentSummary(
105+ id: "research",
106+ name: "Research",
107+ identity: ["emoji": AnyCodable("RS")],
108+ workspace: "OpenClaw",
109+ model: ["provider": AnyCodable("openai"), "model": AnyCodable("gpt-5.5")],
110+ agentruntime: ["kind": AnyCodable("gateway")],
111+ thinkinglevels: nil,
112+ thinkingoptions: ["auto", "low", "medium", "high"],
113+ thinkingdefault: "medium"),
114+AgentSummary(
115+ id: "automation",
116+ name: "Automation",
117+ identity: ["emoji": AnyCodable("AU")],
118+ workspace: "OpenClaw",
119+ model: ["provider": AnyCodable("openai"), "model": AnyCodable("gpt-5.5")],
120+ agentruntime: ["kind": AnyCodable("gateway")],
121+ thinkinglevels: nil,
122+ thinkingoptions: ["auto", "low", "medium", "high"],
123+ thinkingdefault: "auto"),
124+])
30125}
3112632-struct AppleReviewDemoChatTransport: OpenClawChatTransport {
33-private let store = AppleReviewDemoChatStore()
127+struct LocalFixtureChatTransport: OpenClawChatTransport {
128+private let fixture: LocalChatFixture
129+private let store: LocalFixtureChatStore
130+131+init(fixture: LocalChatFixture) {
132+self.fixture = fixture
133+self.store = LocalFixtureChatStore(fixture: fixture)
134+}
3413535136func createSession(
36137 key: String,
@@ -47,9 +148,9 @@ struct AppleReviewDemoChatTransport: OpenClawChatTransport {
47148func listModels() async throws -> [OpenClawChatModelChoice] {
48149[
49150OpenClawChatModelChoice(
50- modelID: "local-demo",
51- name: "Apple Review Demo",
52- provider: "demo",
151+ modelID: self.fixture.modelID,
152+ name: self.fixture.modelName,
153+ provider: self.fixture.modelProvider,
53154 contextWindow: 128_000),
54155]
55156}
@@ -101,26 +202,102 @@ struct AppleReviewDemoChatTransport: OpenClawChatTransport {
101202func compactSession(sessionKey _: String) async throws {}
102203}
103204104-private actor AppleReviewDemoChatStore {
105-private let sessionKey = "main"
205+struct AppleReviewDemoChatTransport: OpenClawChatTransport {
206+private let transport = LocalFixtureChatTransport(fixture: .appleReviewDemo)
207+208+func createSession(
209+ key: String,
210+ label: String?,
211+ parentSessionKey: String?) async throws -> OpenClawChatCreateSessionResponse
212+{
213+try await self.transport.createSession(key: key, label: label, parentSessionKey: parentSessionKey)
214+}
215+216+func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload {
217+try await self.transport.requestHistory(sessionKey: sessionKey)
218+}
219+220+func listModels() async throws -> [OpenClawChatModelChoice] {
221+try await self.transport.listModels()
222+}
223+224+func sendMessage(
225+ sessionKey: String,
226+ message: String,
227+ thinking: String,
228+ idempotencyKey: String,
229+ attachments: [OpenClawChatAttachmentPayload]) async throws -> OpenClawChatSendResponse
230+{
231+try await self.transport.sendMessage(
232+ sessionKey: sessionKey,
233+ message: message,
234+ thinking: thinking,
235+ idempotencyKey: idempotencyKey,
236+ attachments: attachments)
237+}
238+239+func abortRun(sessionKey: String, runId: String) async throws {
240+try await self.transport.abortRun(sessionKey: sessionKey, runId: runId)
241+}
242+243+func listSessions(limit: Int?) async throws -> OpenClawChatSessionsListResponse {
244+try await self.transport.listSessions(limit: limit)
245+}
246+247+func setSessionModel(sessionKey: String, model: String?) async throws {
248+try await self.transport.setSessionModel(sessionKey: sessionKey, model: model)
249+}
250+251+func setSessionThinking(sessionKey: String, thinkingLevel: String) async throws {
252+try await self.transport.setSessionThinking(sessionKey: sessionKey, thinkingLevel: thinkingLevel)
253+}
254+255+func requestHealth(timeoutMs: Int) async throws -> Bool {
256+try await self.transport.requestHealth(timeoutMs: timeoutMs)
257+}
258+259+func waitForRunCompletion(runId: String, timeoutMs: Int) async -> Bool {
260+await self.transport.waitForRunCompletion(runId: runId, timeoutMs: timeoutMs)
261+}
262+263+func events() -> AsyncStream<OpenClawChatTransportEvent> {
264+self.transport.events()
265+}
266+267+func setActiveSessionKey(_ sessionKey: String) async throws {
268+try await self.transport.setActiveSessionKey(sessionKey)
269+}
270+271+func resetSession(sessionKey: String) async throws {
272+try await self.transport.resetSession(sessionKey: sessionKey)
273+}
274+275+func compactSession(sessionKey: String) async throws {
276+try await self.transport.compactSession(sessionKey: sessionKey)
277+}
278+}
279+280+private actor LocalFixtureChatStore {
281+private let fixture: LocalChatFixture
106282private var messages: [OpenClawChatMessage]
107283108-init() {
109-self.messages = AppleReviewDemoChatStore.seedMessages()
284+init(fixture: LocalChatFixture) {
285+self.fixture = fixture
286+self.messages = Self.seedMessages(fixture: fixture)
110287}
111288112289func createSession(key: String) throws -> OpenClawChatCreateSessionResponse {
113290try Self.decode(
114-CreateSessionPayload(ok: true, key: key, sessionId: "apple-review-demo-\(key)"),
291+CreateSessionPayload(ok: true, key: key, sessionId: "\(self.fixture.sessionIDPrefix)-\(key)"),
115292 as: OpenClawChatCreateSessionResponse.self)
116293}
117294118295func history(sessionKey: String) throws -> OpenClawChatHistoryPayload {
119-let normalizedSessionKey = Self.normalizedSessionKey(sessionKey)
296+let normalizedSessionKey = Self.normalizedSessionKey(sessionKey, fallback: self.fixture.sessionKey)
120297return try Self.decode(
121298HistoryPayload(
122299 sessionKey: normalizedSessionKey,
123- sessionId: "apple-review-demo-\(normalizedSessionKey)",
300+ sessionId: "\(self.fixture.sessionIDPrefix)-\(normalizedSessionKey)",
124301 messages: self.messages,
125302 thinkingLevel: "auto"),
126303 as: OpenClawChatHistoryPayload.self)
@@ -135,9 +312,8 @@ private actor AppleReviewDemoChatStore {
135312Self.message(
136313 role: "assistant",
137314 text: """
138- Demo mode is active. I can show the review flow locally for \(subject), including chat, agent \
139- selection, settings, and Gateway-connected UI states. Live automation requires pairing a real \
140- OpenClaw Gateway.
315+ \(self.fixture.responsePrefix) I can help with \(subject), summarize current project context, \
316+ prepare agent actions, and keep the mobile workflow connected to the gateway.
141317 """,
142318 timestamp: now + 1))
143319return try Self.decode(
@@ -147,68 +323,67 @@ private actor AppleReviewDemoChatStore {
147323148324func sessions() throws -> OpenClawChatSessionsListResponse {
149325let entry = OpenClawChatSessionEntry(
150- key: self.sessionKey,
326+ key: self.fixture.sessionKey,
151327 kind: "chat",
152- displayName: "Apple Review Demo",
328+ displayName: self.fixture.displayName,
153329 surface: "ios",
154- subject: "Gateway review flow",
330+ subject: self.fixture.subject,
155331 room: nil,
156332 space: nil,
157333 updatedAt: Date().timeIntervalSince1970 * 1000,
158- sessionId: "apple-review-demo-main",
334+ sessionId: "\(self.fixture.sessionIDPrefix)-\(self.fixture.sessionKey)",
159335 systemSent: true,
160336 abortedLastRun: false,
161337 thinkingLevel: "auto",
162338 verboseLevel: nil,
163339 inputTokens: nil,
164340 outputTokens: nil,
165341 totalTokens: nil,
166- modelProvider: "demo",
167- model: "local-demo",
342+ modelProvider: self.fixture.modelProvider,
343+ model: self.fixture.modelID,
168344 contextTokens: 128_000,
169- thinkingLevels: [
170-OpenClawChatThinkingLevelOption(id: "auto", label: "Auto"),
171-OpenClawChatThinkingLevelOption(id: "low", label: "Low"),
172-OpenClawChatThinkingLevelOption(id: "medium", label: "Medium"),
173-],
174- thinkingOptions: ["auto", "low", "medium"],
345+ thinkingLevels: Self.thinkingLevels,
346+ thinkingOptions: Self.thinkingOptions,
175347 thinkingDefault: "auto")
176348return OpenClawChatSessionsListResponse(
177349 ts: Date().timeIntervalSince1970 * 1000,
178350 path: nil,
179351 count: 1,
180352 defaults: OpenClawChatSessionsDefaults(
181- modelProvider: "demo",
182- model: "local-demo",
353+ modelProvider: self.fixture.modelProvider,
354+ model: self.fixture.modelID,
183355 contextTokens: 128_000,
184- thinkingLevels: [
185-OpenClawChatThinkingLevelOption(id: "auto", label: "Auto"),
186-OpenClawChatThinkingLevelOption(id: "low", label: "Low"),
187-OpenClawChatThinkingLevelOption(id: "medium", label: "Medium"),
188-],
189- thinkingOptions: ["auto", "low", "medium"],
356+ thinkingLevels: Self.thinkingLevels,
357+ thinkingOptions: Self.thinkingOptions,
190358 thinkingDefault: "auto",
191- mainSessionKey: self.sessionKey),
359+ mainSessionKey: self.fixture.sessionKey),
192360 sessions: [entry])
193361}
194362195363func reset() {
196-self.messages = Self.seedMessages()
364+self.messages = Self.seedMessages(fixture: self.fixture)
197365}
198366199-private static func seedMessages() -> [OpenClawChatMessage] {
200-let now = Date().timeIntervalSince1970 * 1000
201- return [
202- self.message(
203- role: "assistant",
204- text: """
205- Apple Review demo mode is active. This local chat transport lets reviewers inspect the iOS app \
206- without a private Gateway.
207- """,
208- timestamp: now),
367+private static var thinkingOptions: [String] {
368+["auto", "low", "medium", "high"]
369+}
370+371+private static var thinkingLevels: [OpenClawChatThinkingLevelOption] {
372+[
373+OpenClawChatThinkingLevelOption(id: "auto", label: "Auto"),
374+OpenClawChatThinkingLevelOption(id: "low", label: "Low"),
375+OpenClawChatThinkingLevelOption(id: "medium", label: "Medium"),
376+OpenClawChatThinkingLevelOption(id: "high", label: "High"),
209377]
210378}
211379380+private static func seedMessages(fixture: LocalChatFixture) -> [OpenClawChatMessage] {
381+let now = Date().timeIntervalSince1970 * 1000
382+return fixture.seedMessages.enumerated().map { index, text in
383+self.message(role: "assistant", text: text, timestamp: now + Double(index))
384+}
385+}
386+212387private static func message(role: String, text: String, timestamp: Double) -> OpenClawChatMessage {
213388OpenClawChatMessage(
214389 role: role,
@@ -223,9 +398,9 @@ private actor AppleReviewDemoChatStore {
223398 timestamp: timestamp)
224399}
225400226-private static func normalizedSessionKey(_ value: String) -> String {
401+private static func normalizedSessionKey(_ value: String, fallback: String) -> String {
227402let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
228-return trimmed.isEmpty ? "main" : trimmed
403+return trimmed.isEmpty ? fallback : trimmed
229404}
230405231406private static func decode<T: Decodable>(_ value: some Encodable, as type: T.Type) throws -> T {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。