Add Ed Swift package
Co-Authored-By: siGit Code <noreply@sigit.si>
Seto Elkahfi committed
Sep 9, 2026 at 23:11 UTC
9dd887472e1ec0620f18e0897f94ceb6001f84f0
15 files changed
+4473
-1
.gitignore
new
+8
@@ -0,0 +1,8 @@
1
+.DS_Store
2
+.build/
3
+.local/
4
+build/
5
+DerivedData/
6
+EdFramework.xcframework/
7
+Examples/EdExample/*.xcodeproj/
8
+*.xcresult
Examples/EdExample/ChatViewModel.swift
new
+107
@@ -0,0 +1,107 @@
1
+import Ed
2
+import Foundation
3
+import UIKit
4
+
5
+@MainActor
6
+final class ChatViewModel: ObservableObject {
7
+ @Published var input = "What is my battery level?"
8
+ @Published var transcript = ""
9
+ @Published var status = "Not loaded"
10
+ @Published var isLoaded = false
11
+ @Published var isBusy = false
12
+ @Published var showApproval = false
13
+ @Published var approvalSummary = ""
14
+
15
+ private let notes = NotesStore()
16
+ private lazy var agent = EdAgent { [weak self] call in
17
+ guard let self else { return .deny }
18
+ return await self.requestApproval(for: call)
19
+ }
20
+ private var approvalContinuation: CheckedContinuation<EdApprovalDecision, Never>?
21
+
22
+ func load() async {
23
+ isBusy = true
24
+ defer { isBusy = false }
25
+ do {
26
+ try await agent.register(BatteryTool())
27
+ try await agent.register(SaveNoteTool(notes: notes))
28
+ _ = try await agent.loadDefaultAgentModel(
29
+ systemPrompt: "You are a concise assistant inside an iOS app. Use tools when useful."
30
+ )
31
+ isLoaded = true
32
+ status = "Ready"
33
+ } catch {
34
+ status = "Load failed: \(error.localizedDescription)"
35
+ }
36
+ }
37
+
38
+ func send() async {
39
+ let message = input
40
+ input = ""
41
+ isBusy = true
42
+ defer { isBusy = false }
43
+ do {
44
+ let reply = try await agent.run(message)
45
+ transcript += "You: \(message)\n\nEd: \(reply.text)\n\n"
46
+ status = "Finished in \(reply.duration) with \(reply.toolRounds) tool round(s)"
47
+ } catch {
48
+ status = "Turn failed: \(error.localizedDescription)"
49
+ }
50
+ }
51
+
52
+ func resolveApproval(_ decision: EdApprovalDecision) {
53
+ showApproval = false
54
+ approvalContinuation?.resume(returning: decision)
55
+ approvalContinuation = nil
56
+ }
57
+
58
+ private func requestApproval(for call: EdToolCall) async -> EdApprovalDecision {
59
+ approvalSummary = "\(call.name) wants to run with: \(call.arguments)"
60
+ showApproval = true
61
+ return await withCheckedContinuation { continuation in
62
+ approvalContinuation = continuation
63
+ }
64
+ }
65
+}
66
+
67
+private struct BatteryTool: EdTool {
68
+ let definition = EdToolDefinition.readOnly(
69
+ name: "battery_level",
70
+ description: "Read the device battery percentage",
71
+ parametersSchema: #"{"type":"object","properties":{},"additionalProperties":false}"#
72
+ )
73
+
74
+ func execute(arguments: String) async throws -> String {
75
+ await MainActor.run {
76
+ UIDevice.current.isBatteryMonitoringEnabled = true
77
+ let level = UIDevice.current.batteryLevel
78
+ return level < 0 ? "Battery level is unavailable." : "Battery is at \(Int(level * 100)) percent."
79
+ }
80
+ }
81
+}
82
+
83
+private actor NotesStore {
84
+ private var notes: [String] = []
85
+
86
+ func save(_ note: String) -> Int {
87
+ notes.append(note)
88
+ return notes.count
89
+ }
90
+}
91
+
92
+private struct SaveNoteTool: EdTool {
93
+ struct Arguments: Decodable { let text: String }
94
+
95
+ let notes: NotesStore
96
+ let definition = EdToolDefinition.mutating(
97
+ name: "save_note",
98
+ description: "Save a note in the app",
99
+ parametersSchema: #"{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false}"#
100
+ )
101
+
102
+ func execute(arguments: String) async throws -> String {
103
+ let decoded = try JSONDecoder().decode(Arguments.self, from: Data(arguments.utf8))
104
+ let count = await notes.save(decoded.text)
105
+ return "Saved note number \(count)."
106
+ }
107
+}
Examples/EdExample/ContentView.swift
new
+50
@@ -0,0 +1,50 @@
1
+import Ed
2
+import SwiftUI
3
+
4
+struct ContentView: View {
5
+ @StateObject private var model = ChatViewModel()
6
+
7
+ var body: some View {
8
+ NavigationStack {
9
+ VStack(spacing: 16) {
10
+ ScrollView {
11
+ Text(model.transcript.isEmpty ? "Load Ed, then ask about the battery or save a note." : model.transcript)
12
+ .frame(maxWidth: .infinity, alignment: .leading)
13
+ }
14
+
15
+ TextField("Message Ed", text: $model.input)
16
+ .textFieldStyle(.roundedBorder)
17
+
18
+ HStack {
19
+ Button(model.isLoaded ? "Loaded" : "Load Ed") {
20
+ Task { await model.load() }
21
+ }
22
+ .disabled(model.isBusy || model.isLoaded)
23
+
24
+ Button("Send") {
25
+ Task { await model.send() }
26
+ }
27
+ .buttonStyle(.borderedProminent)
28
+ .disabled(model.isBusy || !model.isLoaded || model.input.isEmpty)
29
+ }
30
+
31
+ Text(model.status)
32
+ .font(.caption)
33
+ .foregroundStyle(.secondary)
34
+ }
35
+ .padding()
36
+ .navigationTitle("Ed")
37
+ .confirmationDialog(
38
+ "Allow Ed to change app data?",
39
+ isPresented: $model.showApproval,
40
+ titleVisibility: .visible
41
+ ) {
42
+ Button("Allow once") { model.resolveApproval(.allowOnce) }
43
+ Button("Allow for this session") { model.resolveApproval(.allowForSession) }
44
+ Button("Deny", role: .cancel) { model.resolveApproval(.deny) }
45
+ } message: {
46
+ Text(model.approvalSummary)
47
+ }
48
+ }
49
+ }
50
+}
Examples/EdExample/EdExampleApp.swift
new
+10
@@ -0,0 +1,10 @@
1
+import SwiftUI
2
+
3
+@main
4
+struct EdExampleApp: App {
5
+ var body: some Scene {
6
+ WindowGroup {
7
+ ContentView()
8
+ }
9
+ }
10
+}
Examples/EdExample/README.md
new
+9
@@ -0,0 +1,9 @@
1
+# EdExample
2
+
3
+1. Run `make ios` from the repository root.
4
+2. Install XcodeGen if needed (`brew install xcodegen`).
5
+3. Run `xcodegen generate` in this directory.
6
+4. Open `EdExample.xcodeproj` and run on an arm64 simulator or device.
7
+
8
+The battery tool is read-only and runs automatically. The note tool is
9
+mutating, so Ed pauses and asks the user before Swift executes it.
Examples/EdExample/project.yml
new
+32
@@ -0,0 +1,32 @@
1
+name: EdExample
2
+options:
3
+ bundleIdPrefix: com.ondeinference
4
+ deploymentTarget:
5
+ iOS: "16.0"
6
+ xcodeVersion: "16.0"
7
+
8
+packages:
9
+ Ed:
10
+ path: ../..
11
+
12
+targets:
13
+ EdExample:
14
+ type: application
15
+ platform: iOS
16
+ sources:
17
+ - path: .
18
+ excludes:
19
+ - project.yml
20
+ - "*.xcodeproj"
21
+ dependencies:
22
+ - package: Ed
23
+ product: Ed
24
+ settings:
25
+ base:
26
+ PRODUCT_BUNDLE_IDENTIFIER: com.ondeinference.EdExample
27
+ GENERATE_INFOPLIST_FILE: YES
28
+ INFOPLIST_KEY_CFBundleDisplayName: Ed Example
29
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation: YES
30
+ INFOPLIST_KEY_UILaunchScreen_Generation: YES
31
+ SWIFT_VERSION: "6.0"
32
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]": x86_64
LICENSE-APACHE
new
+202
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Splitfire AB (Onde Inference)
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
LICENSE-MIT
new
+21
@@ -0,0 +1,21 @@
1
+MIT License
2
+
3
+Copyright (c) 2026 Splitfire AB (Onde Inference)
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.
Makefile
new
+92
@@ -0,0 +1,92 @@
1
+SHELL := /bin/sh
2
+
3
+SWIFT_REPO := $(CURDIR)
4
+ED_REPO ?= $(abspath ../onde-ed)
5
+MANIFEST := $(ED_REPO)/crates/ed-agent-ffi/Cargo.toml
6
+BINDGEN_MANIFEST := $(ED_REPO)/uniffi-bindgen/Cargo.toml
7
+BINDGEN := $(ED_REPO)/target/release/uniffi-bindgen
8
+APPLE_TARGET_DIR := $(ED_REPO)/target/apple
9
+
10
+IOS_DEPLOYMENT_TARGET ?= 16.0
11
+MACOS_DEPLOYMENT_TARGET ?= 14.0
12
+TVOS_DEPLOYMENT_TARGET ?= 16.0
13
+VISIONOS_DEPLOYMENT_TARGET ?= 1.0
14
+WATCHOS_DEPLOYMENT_TARGET ?= 9.0
15
+
16
+LOCAL_DIR := $(SWIFT_REPO)/.local
17
+GENERATED_DIR := $(LOCAL_DIR)/generated/Ed
18
+HEADERS_DIR := $(LOCAL_DIR)/Headers
19
+FRAMEWORK_DIR := $(SWIFT_REPO)/EdFramework.xcframework
20
+SWIFT_GLUE := $(SWIFT_REPO)/Sources/Ed/ed_agent_ffi.swift
21
+
22
+.PHONY: help ios macos tvos visionos watchos build-bindgen prepare generate-swift clean
23
+
24
+help:
25
+ @printf '%s\n' \
26
+ 'Ed Swift local builds' \
27
+ '' \
28
+ 'make ios | macos | tvos | visionos | watchos' \
29
+ 'Set ED_REPO=/path/to/onde-ed when the repositories are not siblings.'
30
+
31
+validate:
32
+ @test -f "$(MANIFEST)" || { echo "Could not find onde-ed at $(ED_REPO)"; exit 1; }
33
+
34
+build-bindgen: validate
35
+ cargo build --manifest-path "$(BINDGEN_MANIFEST)" --release
36
+
37
+prepare:
38
+ @rm -rf "$(FRAMEWORK_DIR)" "$(GENERATED_DIR)" "$(HEADERS_DIR)"
39
+ @mkdir -p "$(GENERATED_DIR)" "$(HEADERS_DIR)"
40
+
41
+generate-swift:
42
+ @test -n "$(BINDGEN_INPUT)" || { echo "BINDGEN_INPUT is required"; exit 1; }
43
+ cd "$(ED_REPO)" && "$(BINDGEN)" generate "$(BINDGEN_INPUT)" --crate ed_agent_ffi --language swift --out-dir "$(GENERATED_DIR)"
44
+ cp "$(GENERATED_DIR)/ed_agent_ffi.swift" "$(SWIFT_GLUE)"
45
+ cp "$(GENERATED_DIR)/ed_agent_ffiFFI.h" "$(HEADERS_DIR)/ed_agent_ffiFFI.h"
46
+ cp "$(GENERATED_DIR)/ed_agent_ffiFFI.modulemap" "$(HEADERS_DIR)/module.modulemap"
47
+
48
+ios: build-bindgen prepare
49
+ IPHONEOS_DEPLOYMENT_TARGET=$(IOS_DEPLOYMENT_TARGET) cargo rustc --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-ios --release --lib --crate-type staticlib
50
+ IPHONEOS_DEPLOYMENT_TARGET=$(IOS_DEPLOYMENT_TARGET) cargo rustc --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-ios-sim --release --lib --crate-type staticlib
51
+ $(MAKE) generate-swift BINDGEN_INPUT="$(APPLE_TARGET_DIR)/aarch64-apple-ios/release/libed_agent_ffi.a"
52
+ xcodebuild -create-xcframework \
53
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-ios/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
54
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-ios-sim/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
55
+ -output "$(FRAMEWORK_DIR)"
56
+
57
+macos: build-bindgen prepare
58
+ MACOSX_DEPLOYMENT_TARGET=$(MACOS_DEPLOYMENT_TARGET) cargo rustc --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-darwin --release --lib --crate-type staticlib
59
+ $(MAKE) generate-swift BINDGEN_INPUT="$(APPLE_TARGET_DIR)/aarch64-apple-darwin/release/libed_agent_ffi.a"
60
+ xcodebuild -create-xcframework \
61
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-darwin/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
62
+ -output "$(FRAMEWORK_DIR)"
63
+
64
+tvos: build-bindgen prepare
65
+ TVOS_DEPLOYMENT_TARGET=$(TVOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-tvos --release --lib --crate-type staticlib
66
+ TVOS_DEPLOYMENT_TARGET=$(TVOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-tvos-sim --release --lib --crate-type staticlib
67
+ $(MAKE) generate-swift BINDGEN_INPUT="$(APPLE_TARGET_DIR)/aarch64-apple-tvos/release/libed_agent_ffi.a"
68
+ xcodebuild -create-xcframework \
69
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-tvos/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
70
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-tvos-sim/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
71
+ -output "$(FRAMEWORK_DIR)"
72
+
73
+visionos: build-bindgen prepare
74
+ XROS_DEPLOYMENT_TARGET=$(VISIONOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-visionos --release --lib --crate-type staticlib
75
+ XROS_DEPLOYMENT_TARGET=$(VISIONOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-visionos-sim --release --lib --crate-type staticlib
76
+ $(MAKE) generate-swift BINDGEN_INPUT="$(APPLE_TARGET_DIR)/aarch64-apple-visionos/release/libed_agent_ffi.a"
77
+ xcodebuild -create-xcframework \
78
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-visionos/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
79
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-visionos-sim/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
80
+ -output "$(FRAMEWORK_DIR)"
81
+
82
+watchos: build-bindgen prepare
83
+ WATCHOS_DEPLOYMENT_TARGET=$(WATCHOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-watchos --release --lib --crate-type staticlib
84
+ WATCHOS_DEPLOYMENT_TARGET=$(WATCHOS_DEPLOYMENT_TARGET) cargo +nightly rustc -Z build-std --target-dir "$(APPLE_TARGET_DIR)" --manifest-path "$(MANIFEST)" --target aarch64-apple-watchos-sim --release --lib --crate-type staticlib
85
+ $(MAKE) generate-swift BINDGEN_INPUT="$(APPLE_TARGET_DIR)/aarch64-apple-watchos/release/libed_agent_ffi.a"
86
+ xcodebuild -create-xcframework \
87
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-watchos/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
88
+ -library "$(APPLE_TARGET_DIR)/aarch64-apple-watchos-sim/release/libed_agent_ffi.a" -headers "$(HEADERS_DIR)" \
89
+ -output "$(FRAMEWORK_DIR)"
90
+
91
+clean:
92
+ rm -rf "$(FRAMEWORK_DIR)" "$(LOCAL_DIR)"
Package.swift
new
+70
@@ -0,0 +1,70 @@
1
+// swift-tools-version:6.2
2
+import Foundation
3
+import PackageDescription
4
+
5
+let packageRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent()
6
+let localFrameworkPath = "EdFramework.xcframework"
7
+let localFrameworkAbsolutePath = packageRoot.appendingPathComponent(localFrameworkPath).path
8
+
9
+// Updated automatically by onde-ed's release-sdk-swift workflow.
10
+let releaseFrameworkURL =
11
+ "https://github.com/ondeinference/onde-ed/releases/download/0.1.0/EdFramework.xcframework.zip"
12
+let releaseFrameworkChecksum =
13
+ "0000000000000000000000000000000000000000000000000000000000000000"
14
+
15
+let edFrameworkTarget: Target
16
+if FileManager.default.fileExists(atPath: localFrameworkAbsolutePath) {
17
+ edFrameworkTarget = .binaryTarget(
18
+ name: "EdFramework",
19
+ path: localFrameworkPath
20
+ )
21
+} else {
22
+ edFrameworkTarget = .binaryTarget(
23
+ name: "EdFramework",
24
+ url: releaseFrameworkURL,
25
+ checksum: releaseFrameworkChecksum
26
+ )
27
+}
28
+
29
+let package = Package(
30
+ name: "Ed",
31
+ platforms: [
32
+ .iOS(.v16),
33
+ .macOS(.v14),
34
+ .tvOS(.v16),
35
+ .visionOS(.v1),
36
+ .watchOS(.v9),
37
+ ],
38
+ products: [
39
+ .library(name: "Ed", targets: ["Ed"]),
40
+ ],
41
+ targets: [
42
+ edFrameworkTarget,
43
+ .target(
44
+ name: "Ed",
45
+ dependencies: [.target(name: "EdFramework")],
46
+ path: "Sources/Ed",
47
+ linkerSettings: [
48
+ .linkedFramework("CoreFoundation"),
49
+ .linkedFramework("Foundation"),
50
+ .linkedFramework("IOKit", .when(platforms: [.macOS])),
51
+ .linkedFramework("Metal"),
52
+ .linkedFramework("Security"),
53
+ .linkedFramework("SystemConfiguration"),
54
+ .linkedLibrary("c++"),
55
+ .linkedLibrary("iconv"),
56
+ .linkedLibrary("resolv"),
57
+ .linkedLibrary("z"),
58
+ ]
59
+ ),
60
+ .testTarget(
61
+ name: "EdTests",
62
+ dependencies: ["Ed"],
63
+ path: "Tests/EdTests"
64
+ ),
65
+ ],
66
+ // UniFFI 0.31's generated async callback shim predates Swift 6's region
67
+ // isolation checks. The hand-written Ed API remains concurrency-safe; the
68
+ // generated bridge compiles in Swift 5 language mode until UniFFI updates.
69
+ swiftLanguageModes: [.v5]
70
+)
README.md
new
+116
@@ -0,0 +1,116 @@
1
+# Ed for Swift
2
+
3
+Ed is a private, on-device agent for Swift apps. Onde runs the language model;
4
+Ed gives that model a safe way to ask your app to do things.
5
+
6
+It runs on iPhone, iPad, Mac, Apple TV, Apple Vision Pro, and Apple Watch. Model
7
+input, conversation history, and tool results stay on the device unless a tool
8
+you write deliberately sends data elsewhere.
9
+
10
+## The mental model
11
+
12
+If you are new to agents, think of Ed as a loop:
13
+
14
+1. Your app sends a message to the model.
15
+2. The model either answers, or requests one of your registered tools.
16
+3. Ed checks the tool's risk level.
17
+4. Your Swift code runs the approved tool and returns text.
18
+5. The model reads that result and writes the final answer.
19
+
20
+The model cannot call arbitrary Swift APIs. It only sees tools your app
21
+registers.
22
+
23
+## Installation
24
+
25
+In Xcode, choose **File > Add Package Dependencies** and enter:
26
+
27
+```text
28
+https://github.com/ondeinference/onde-ed-swift
29
+```
30
+
31
+Then add the `Ed` product to your target and import it:
32
+
33
+```swift
34
+import Ed
35
+```
36
+
37
+## Quick start
38
+
39
+First, describe a tool and implement it. A JSON schema tells the model which
40
+arguments it may provide:
41
+
42
+```swift
43
+import Ed
44
+
45
+struct BatteryTool: EdTool {
46
+ let definition = EdToolDefinition.readOnly(
47
+ name: "battery_level",
48
+ description: "Read the device battery percentage",
49
+ parametersSchema: #"{"type":"object","properties":{}}"#
50
+ )
51
+
52
+ func execute(arguments: String) async throws -> String {
53
+ // Read the real value from your platform API here.
54
+ "The battery is at 73 percent."
55
+ }
56
+}
57
+```
58
+
59
+Then create the agent, register the tool, and load the default model:
60
+
61
+```swift
62
+let ed = EdAgent { call in
63
+ // This callback is used only for mutating tools.
64
+ let userApproved = await showApproval(for: call)
65
+ return userApproved ? .allowOnce : .deny
66
+}
67
+
68
+try await ed.register(BatteryTool())
69
+try await ed.loadDefaultAgentModel(
70
+ systemPrompt: "You are a concise assistant inside my app."
71
+)
72
+
73
+let reply = try await ed.run("How much battery do I have?")
74
+print(reply.text)
75
+```
76
+
77
+Use `.readOnly` for tools that only inspect state. Use `.mutating` for tools
78
+that save, send, purchase, delete, unlock, or otherwise change something. Ed
79
+runs read-only tools automatically and sends every mutating call through your
80
+approval callback.
81
+
82
+Observe progress and tool activity with an async stream:
83
+
84
+```swift
85
+for await event in ed.events() {
86
+ print(event)
87
+}
88
+```
89
+
90
+Cancelling the Swift task also cancels the active Ed turn:
91
+
92
+```swift
93
+let task = Task { try await ed.run("Do the task") }
94
+task.cancel()
95
+```
96
+
97
+See [`Examples/EdExample`](Examples/EdExample) for a complete SwiftUI example
98
+with a read-only device tool and an approval-gated mutating tool.
99
+
100
+## Local development
101
+
102
+Keep `onde`, `onde-ed`, and `onde-ed-swift` next to one another, then build a
103
+local framework before running Swift tests:
104
+
105
+```sh
106
+make macos
107
+swift test
108
+```
109
+
110
+Use `make ios`, `make tvos`, `make visionos`, or `make watchos` for another
111
+Apple platform. The package automatically uses `EdFramework.xcframework` when
112
+that local file exists. Without it, SwiftPM downloads the signed release asset.
113
+
114
+## Licence
115
+
116
+Ed is available under either the MIT License or Apache License 2.0.
Sources/Ed/EdAgent.swift
new
+582
@@ -0,0 +1,582 @@
1
+import Foundation
2
+
3
+/// A capability your app makes available to the local model.
4
+///
5
+/// Ed never invents native capabilities. Your app registers each tool and
6
+/// remains responsible for executing it.
7
+public protocol EdTool: Sendable {
8
+ var definition: EdToolDefinition { get }
9
+
10
+ /// Executes the tool with the JSON object produced by the model.
11
+ func execute(arguments: String) async throws -> String
12
+}
13
+
14
+public enum EdToolRisk: Sendable {
15
+ /// Safe to run without asking, such as reading app state.
16
+ case readOnly
17
+
18
+ /// Changes state and must be approved first, such as saving or deleting.
19
+ case mutating
20
+}
21
+
22
+public struct EdToolDefinition: Sendable {
23
+ public let name: String
24
+ public let description: String
25
+ public let parametersSchema: String
26
+ public let risk: EdToolRisk
27
+
28
+ public init(
29
+ name: String,
30
+ description: String,
31
+ parametersSchema: String,
32
+ risk: EdToolRisk
33
+ ) {
34
+ self.name = name
35
+ self.description = description
36
+ self.parametersSchema = parametersSchema
37
+ self.risk = risk
38
+ }
39
+
40
+ public static func readOnly(
41
+ name: String,
42
+ description: String,
43
+ parametersSchema: String
44
+ ) -> Self {
45
+ Self(
46
+ name: name,
47
+ description: description,
48
+ parametersSchema: parametersSchema,
49
+ risk: .readOnly
50
+ )
51
+ }
52
+
53
+ public static func mutating(
54
+ name: String,
55
+ description: String,
56
+ parametersSchema: String
57
+ ) -> Self {
58
+ Self(
59
+ name: name,
60
+ description: description,
61
+ parametersSchema: parametersSchema,
62
+ risk: .mutating
63
+ )
64
+ }
65
+}
66
+
67
+public struct EdToolCall: Sendable, Equatable {
68
+ public let id: String
69
+ public let name: String
70
+ public let arguments: String
71
+}
72
+
73
+public enum EdApprovalDecision: Sendable {
74
+ case allowOnce
75
+ case allowForSession
76
+ case deny
77
+}
78
+
79
+public typealias EdApprovalHandler = @Sendable (EdToolCall) async -> EdApprovalDecision
80
+
81
+public struct EdAgentConfiguration: Sendable {
82
+ public var maxToolRounds: UInt8
83
+ public var maxToolOutputCharacters: UInt64
84
+
85
+ public init(maxToolRounds: UInt8 = 8, maxToolOutputCharacters: UInt64 = 10_000) {
86
+ self.maxToolRounds = maxToolRounds
87
+ self.maxToolOutputCharacters = maxToolOutputCharacters
88
+ }
89
+}
90
+
91
+public struct EdAgentReply: Sendable, Equatable {
92
+ public let text: String
93
+ public let durationSeconds: Double
94
+ public let duration: String
95
+ public let toolRounds: UInt8
96
+}
97
+
98
+public struct EdReply: Sendable, Equatable {
99
+ public let text: String
100
+ public let duration: String
101
+}
102
+
103
+public enum EdEngineStatus: Sendable {
104
+ case unloaded
105
+ case loading
106
+ case ready
107
+ case generating
108
+ case error
109
+}
110
+
111
+public struct EdEngineInfo: Sendable {
112
+ public let status: EdEngineStatus
113
+ public let modelName: String?
114
+ public let approximateMemory: String?
115
+ public let historyLength: UInt64
116
+}
117
+
118
+public enum EdChatRole: Sendable {
119
+ case system
120
+ case user
121
+ case assistant
122
+}
123
+
124
+public struct EdChatMessage: Sendable {
125
+ public let role: EdChatRole
126
+ public let content: String
127
+}
128
+
129
+public enum EdEvent: Sendable {
130
+ case statusChanged(status: EdEngineStatus, modelName: String?, error: String?)
131
+ case toolRequested(EdToolCall)
132
+ case approvalRequested(EdToolCall)
133
+ case toolStarted(EdToolCall)
134
+ case toolFinished(callID: String, content: String, isError: Bool)
135
+ case agentReplied(EdAgentReply)
136
+ case warning(String)
137
+}
138
+
139
+public struct EdSamplingConfiguration: Sendable {
140
+ public var temperature: Double?
141
+ public var topP: Double?
142
+ public var topK: UInt64?
143
+ public var minP: Double?
144
+ public var maxTokens: UInt64?
145
+ public var frequencyPenalty: Float?
146
+ public var presencePenalty: Float?
147
+
148
+ public init(
149
+ temperature: Double? = nil,
150
+ topP: Double? = nil,
151
+ topK: UInt64? = nil,
152
+ minP: Double? = nil,
153
+ maxTokens: UInt64? = nil,
154
+ frequencyPenalty: Float? = nil,
155
+ presencePenalty: Float? = nil
156
+ ) {
157
+ self.temperature = temperature
158
+ self.topP = topP
159
+ self.topK = topK
160
+ self.minP = minP
161
+ self.maxTokens = maxTokens
162
+ self.frequencyPenalty = frequencyPenalty
163
+ self.presencePenalty = presencePenalty
164
+ }
165
+}
166
+
167
+public struct EdGGUFModelConfiguration: Sendable {
168
+ public var modelID: String
169
+ public var files: [String]
170
+ public var tokenizerModelID: String?
171
+ public var displayName: String
172
+ public var approximateMemory: String
173
+ public var chatTemplate: String?
174
+
175
+ public init(
176
+ modelID: String,
177
+ files: [String],
178
+ tokenizerModelID: String? = nil,
179
+ displayName: String,
180
+ approximateMemory: String,
181
+ chatTemplate: String? = nil
182
+ ) {
183
+ self.modelID = modelID
184
+ self.files = files
185
+ self.tokenizerModelID = tokenizerModelID
186
+ self.displayName = displayName
187
+ self.approximateMemory = approximateMemory
188
+ self.chatTemplate = chatTemplate
189
+ }
190
+}
191
+
192
+public struct EdUQFFModelConfiguration: Sendable {
193
+ public var modelID: String
194
+ public var files: [String]
195
+ public var displayName: String
196
+ public var approximateMemory: String
197
+ public var chatTemplate: String?
198
+
199
+ public init(
200
+ modelID: String,
201
+ files: [String],
202
+ displayName: String,
203
+ approximateMemory: String,
204
+ chatTemplate: String? = nil
205
+ ) {
206
+ self.modelID = modelID
207
+ self.files = files
208
+ self.displayName = displayName
209
+ self.approximateMemory = approximateMemory
210
+ self.chatTemplate = chatTemplate
211
+ }
212
+}
213
+
214
+public enum EdEnvironment: Sendable {
215
+ case development
216
+ case production
217
+}
218
+
219
+/// A local agent powered by Onde.
220
+///
221
+/// The actor protects model history, the tool registry, and inference so your
222
+/// UI can call it safely from Swift concurrency.
223
+public actor EdAgent {
224
+ private let core: FfiEdAgent
225
+ private let tools: ToolDispatcher
226
+ private let approvals: ApprovalAdapter
227
+ private nonisolated let eventHub: EventHub
228
+
229
+ public init(
230
+ configuration: EdAgentConfiguration = .init(),
231
+ approvalHandler: @escaping EdApprovalHandler = { _ in .deny }
232
+ ) {
233
+ let tools = ToolDispatcher()
234
+ let approvals = ApprovalAdapter(handler: approvalHandler)
235
+ let eventHub = EventHub()
236
+
237
+ self.tools = tools
238
+ self.approvals = approvals
239
+ self.eventHub = eventHub
240
+ self.core = FfiEdAgent(
241
+ executor: tools,
242
+ approvals: approvals,
243
+ events: eventHub,
244
+ config: FfiAgentConfig(
245
+ maxToolRounds: configuration.maxToolRounds,
246
+ maxToolOutputChars: configuration.maxToolOutputCharacters
247
+ )
248
+ )
249
+ }
250
+
251
+ /// A separate stream for each observer. Stop its task to unsubscribe.
252
+ public nonisolated func events() -> AsyncStream<EdEvent> {
253
+ eventHub.makeStream()
254
+ }
255
+
256
+ public func register(_ tool: any EdTool) async throws {
257
+ try await core.registerTool(tool: tool.definition.ffi)
258
+ tools.set(tool)
259
+ }
260
+
261
+ @discardableResult
262
+ public func unregisterTool(named name: String) async -> Bool {
263
+ let removed = await core.unregisterTool(name: name)
264
+ if removed {
265
+ tools.remove(named: name)
266
+ }
267
+ return removed
268
+ }
269
+
270
+ public func removeAllTools() async {
271
+ await core.removeAllTools()
272
+ tools.removeAll()
273
+ }
274
+
275
+ /// Runs the full agent loop: model -> tool calls -> tool results -> model.
276
+ public func run(_ message: String) async throws -> EdAgentReply {
277
+ try await withTaskCancellationHandler {
278
+ try await core.run(message: message).swift
279
+ } onCancel: {
280
+ core.cancel()
281
+ }
282
+ }
283
+
284
+ /// Sends one ordinary chat turn without exposing tools to the model.
285
+ public func send(_ message: String) async throws -> EdReply {
286
+ let reply = try await core.send(message: message)
287
+ return EdReply(text: reply.text, duration: reply.duration)
288
+ }
289
+
290
+ /// Loads a small tool-capable model chosen for the current Apple device.
291
+ @discardableResult
292
+ public func loadDefaultAgentModel(systemPrompt: String? = nil) async throws -> Double {
293
+ try await core.loadDefaultAgentModel(systemPrompt: systemPrompt)
294
+ }
295
+
296
+ @discardableResult
297
+ public func load(
298
+ gguf config: EdGGUFModelConfiguration,
299
+ systemPrompt: String? = nil,
300
+ sampling: EdSamplingConfiguration? = nil
301
+ ) async throws -> Double {
302
+ try await core.loadGgufModel(
303
+ config: config.ffi,
304
+ systemPrompt: systemPrompt,
305
+ sampling: sampling?.ffi
306
+ )
307
+ }
308
+
309
+ @discardableResult
310
+ public func load(
311
+ uqff config: EdUQFFModelConfiguration,
312
+ systemPrompt: String? = nil,
313
+ sampling: EdSamplingConfiguration? = nil
314
+ ) async throws -> Double {
315
+ try await core.loadUqffModel(
316
+ config: config.ffi,
317
+ systemPrompt: systemPrompt,
318
+ sampling: sampling?.ffi
319
+ )
320
+ }
321
+
322
+ @discardableResult
323
+ public func loadAssignedModel(
324
+ environment: EdEnvironment,
325
+ appID: String,
326
+ appSecret: String,
327
+ systemPrompt: String? = nil,
328
+ sampling: EdSamplingConfiguration? = nil
329
+ ) async throws -> Double {
330
+ try await core.loadAssignedModel(
331
+ environment: environment.ffi,
332
+ appId: appID,
333
+ appSecret: appSecret,
334
+ systemPrompt: systemPrompt,
335
+ sampling: sampling?.ffi
336
+ )
337
+ }
338
+
339
+ public nonisolated func cancel() {
340
+ core.cancel()
341
+ }
342
+
343
+ @discardableResult
344
+ public func unload() async -> String? {
345
+ await core.unload()
346
+ }
347
+
348
+ public func isLoaded() async -> Bool {
349
+ await core.isLoaded()
350
+ }
351
+
352
+ public func info() async -> EdEngineInfo {
353
+ let info = await core.info()
354
+ return EdEngineInfo(
355
+ status: info.status.swift,
356
+ modelName: info.modelName,
357
+ approximateMemory: info.approxMemory,
358
+ historyLength: info.historyLength
359
+ )
360
+ }
361
+
362
+ public func history() async -> [EdChatMessage] {
363
+ await core.history().map(\.swift)
364
+ }
365
+
366
+ @discardableResult
367
+ public func clearHistory() async -> UInt64 {
368
+ await core.clearHistory()
369
+ }
370
+
371
+ public static func configureCacheDirectory(_ url: URL) {
372
+ configureCacheDir(path: url.path)
373
+ }
374
+
375
+ public static func defaultAgentModelConfiguration() -> EdGGUFModelConfiguration {
376
+ defaultAgentModelConfig().swift
377
+ }
378
+}
379
+
380
+private final class ToolDispatcher: FfiToolExecutor, @unchecked Sendable {
381
+ private let lock = NSLock()
382
+ private var tools: [String: any EdTool] = [:]
383
+
384
+ func set(_ tool: any EdTool) {
385
+ lock.withLock { tools[tool.definition.name] = tool }
386
+ }
387
+
388
+ func remove(named name: String) {
389
+ lock.withLock { _ = tools.removeValue(forKey: name) }
390
+ }
391
+
392
+ func removeAll() {
393
+ lock.withLock { tools.removeAll() }
394
+ }
395
+
396
+ func execute(toolName: String, arguments: String) async -> FfiToolOutput {
397
+ let tool = lock.withLock { tools[toolName] }
398
+ guard let tool else {
399
+ return FfiToolOutput(content: "", error: "Unknown tool: \(toolName)")
400
+ }
401
+
402
+ do {
403
+ return FfiToolOutput(
404
+ content: try await tool.execute(arguments: arguments),
405
+ error: nil
406
+ )
407
+ } catch {
408
+ return FfiToolOutput(content: "", error: String(describing: error))
409
+ }
410
+ }
411
+}
412
+
413
+private final class ApprovalAdapter: FfiApprovalHandler, @unchecked Sendable {
414
+ private let handler: EdApprovalHandler
415
+
416
+ init(handler: @escaping EdApprovalHandler) {
417
+ self.handler = handler
418
+ }
419
+
420
+ func approve(call: FfiToolCall) async -> FfiApprovalDecision {
421
+ await handler(call.swift).ffi
422
+ }
423
+}
424
+
425
+private final class EventHub: FfiEventListener, @unchecked Sendable {
426
+ private let lock = NSLock()
427
+ private var continuations: [UUID: AsyncStream<EdEvent>.Continuation] = [:]
428
+
429
+ func makeStream() -> AsyncStream<EdEvent> {
430
+ let id = UUID()
431
+ return AsyncStream { continuation in
432
+ lock.withLock { continuations[id] = continuation }
433
+ continuation.onTermination = { [weak self] _ in
434
+ self?.lock.withLock { _ = self?.continuations.removeValue(forKey: id) }
435
+ }
436
+ }
437
+ }
438
+
439
+ func statusChanged(update: FfiStatusUpdate) {
440
+ emit(.statusChanged(
441
+ status: update.status.swift,
442
+ modelName: update.modelName,
443
+ error: update.error
444
+ ))
445
+ }
446
+
447
+ func toolRequested(call: FfiToolCall) { emit(.toolRequested(call.swift)) }
448
+ func approvalRequested(call: FfiToolCall) { emit(.approvalRequested(call.swift)) }
449
+ func toolStarted(call: FfiToolCall) { emit(.toolStarted(call.swift)) }
450
+
451
+ func toolFinished(toolCallId: String, content: String, isError: Bool) {
452
+ emit(.toolFinished(callID: toolCallId, content: content, isError: isError))
453
+ }
454
+
455
+ func agentReplied(reply: FfiAgentReply) { emit(.agentReplied(reply.swift)) }
456
+ func warning(message: String) { emit(.warning(message)) }
457
+
458
+ private func emit(_ event: EdEvent) {
459
+ let current = lock.withLock { Array(continuations.values) }
460
+ current.forEach { $0.yield(event) }
461
+ }
462
+}
463
+
464
+private extension EdToolDefinition {
465
+ var ffi: FfiToolDefinition {
466
+ FfiToolDefinition(
467
+ name: name,
468
+ description: description,
469
+ parametersSchema: parametersSchema,
470
+ risk: risk == .readOnly ? .readOnly : .mutating
471
+ )
472
+ }
473
+}
474
+
475
+private extension FfiToolCall {
476
+ var swift: EdToolCall { EdToolCall(id: id, name: name, arguments: arguments) }
477
+}
478
+
479
+private extension EdApprovalDecision {
480
+ var ffi: FfiApprovalDecision {
481
+ switch self {
482
+ case .allowOnce: .allowOnce
483
+ case .allowForSession: .allowForSession
484
+ case .deny: .deny
485
+ }
486
+ }
487
+}
488
+
489
+private extension FfiAgentReply {
490
+ var swift: EdAgentReply {
491
+ EdAgentReply(
492
+ text: text,
493
+ durationSeconds: durationSeconds,
494
+ duration: duration,
495
+ toolRounds: toolRounds
496
+ )
497
+ }
498
+}
499
+
500
+private extension FfiEngineStatus {
501
+ var swift: EdEngineStatus {
502
+ switch self {
503
+ case .unloaded: .unloaded
504
+ case .loading: .loading
505
+ case .ready: .ready
506
+ case .generating: .generating
507
+ case .error: .error
508
+ }
509
+ }
510
+}
511
+
512
+private extension FfiChatMessage {
513
+ var swift: EdChatMessage {
514
+ let role: EdChatRole = switch self.role {
515
+ case .system: .system
516
+ case .user: .user
517
+ case .assistant: .assistant
518
+ }
519
+ return EdChatMessage(role: role, content: content)
520
+ }
521
+}
522
+
523
+private extension EdSamplingConfiguration {
524
+ var ffi: FfiSamplingConfig {
525
+ FfiSamplingConfig(
526
+ temperature: temperature,
527
+ topP: topP,
528
+ topK: topK,
529
+ minP: minP,
530
+ maxTokens: maxTokens,
531
+ frequencyPenalty: frequencyPenalty,
532
+ presencePenalty: presencePenalty
533
+ )
534
+ }
535
+}
536
+
537
+private extension EdGGUFModelConfiguration {
538
+ var ffi: FfiGgufModelConfig {
539
+ FfiGgufModelConfig(
540
+ modelId: modelID,
541
+ files: files,
542
+ tokModelId: tokenizerModelID,
543
+ displayName: displayName,
544
+ approxMemory: approximateMemory,
545
+ chatTemplate: chatTemplate
546
+ )
547
+ }
548
+}
549
+
550
+private extension FfiGgufModelConfig {
551
+ var swift: EdGGUFModelConfiguration {
552
+ EdGGUFModelConfiguration(
553
+ modelID: modelId,
554
+ files: files,
555
+ tokenizerModelID: tokModelId,
556
+ displayName: displayName,
557
+ approximateMemory: approxMemory,
558
+ chatTemplate: chatTemplate
559
+ )
560
+ }
561
+}
562
+
563
+private extension EdUQFFModelConfiguration {
564
+ var ffi: FfiUqffModelConfig {
565
+ FfiUqffModelConfig(
566
+ modelId: modelID,
567
+ files: files,
568
+ displayName: displayName,
569
+ approxMemory: approximateMemory,
570
+ chatTemplate: chatTemplate
571
+ )
572
+ }
573
+}
574
+
575
+private extension EdEnvironment {
576
+ var ffi: FfiEnvironment {
577
+ switch self {
578
+ case .development: .development
579
+ case .production: .production
580
+ }
581
+ }
582
+}
Sources/Ed/ed_agent_ffi.swift
new
+3155
@@ -0,0 +1,3155 @@
1
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
2
+// Trust me, you don't want to mess with it!
3
+
4
+// swiftlint:disable all
5
+import Foundation
6
+
7
+// Depending on the consumer's build setup, the low-level FFI code
8
+// might be in a separate module, or it might be compiled inline into
9
+// this module. This is a bit of light hackery to work with both.
10
+#if canImport(ed_agent_ffiFFI)
11
+import ed_agent_ffiFFI
12
+#endif
13
+
14
+fileprivate extension RustBuffer {
15
+ // Allocate a new buffer, copying the contents of a `UInt8` array.
16
+ init(bytes: [UInt8]) {
17
+ let rbuf = bytes.withUnsafeBufferPointer { ptr in
18
+ RustBuffer.from(ptr)
19
+ }
20
+ self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21
+ }
22
+
23
+ static func empty() -> RustBuffer {
24
+ RustBuffer(capacity: 0, len:0, data: nil)
25
+ }
26
+
27
+ static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
28
+ try! rustCall { ffi_ed_agent_ffi_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29
+ }
30
+
31
+ // Frees the buffer in place.
32
+ // The buffer must not be used after this is called.
33
+ func deallocate() {
34
+ try! rustCall { ffi_ed_agent_ffi_rustbuffer_free(self, $0) }
35
+ }
36
+}
37
+
38
+fileprivate extension ForeignBytes {
39
+ init(bufferPointer: UnsafeBufferPointer<UInt8>) {
40
+ self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41
+ }
42
+}
43
+
44
+// For every type used in the interface, we provide helper methods for conveniently
45
+// lifting and lowering that type from C-compatible data, and for reading and writing
46
+// values of that type in a buffer.
47
+
48
+// Helper classes/extensions that don't change.
49
+// Someday, this will be in a library of its own.
50
+
51
+fileprivate extension Data {
52
+ init(rustBuffer: RustBuffer) {
53
+ self.init(
54
+ bytesNoCopy: rustBuffer.data!,
55
+ count: Int(rustBuffer.len),
56
+ deallocator: .none
57
+ )
58
+ }
59
+}
60
+
61
+// Define reader functionality. Normally this would be defined in a class or
62
+// struct, but we use standalone functions instead in order to make external
63
+// types work.
64
+//
65
+// With external types, one swift source file needs to be able to call the read
66
+// method on another source file's FfiConverter, but then what visibility
67
+// should Reader have?
68
+// - If Reader is fileprivate, then this means the read() must also
69
+// be fileprivate, which doesn't work with external types.
70
+// - If Reader is internal/public, we'll get compile errors since both source
71
+// files will try define the same type.
72
+//
73
+// Instead, the read() method and these helper functions input a tuple of data
74
+
75
+fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
76
+ (data: data, offset: 0)
77
+}
78
+
79
+// Reads an integer at the current offset, in big-endian order, and advances
80
+// the offset on success. Throws if reading the integer would move the
81
+// offset past the end of the buffer.
82
+fileprivate func readInt<T: FixedWidthInteger>(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
83
+ let range = reader.offset..<reader.offset + MemoryLayout<T>.size
84
+ guard reader.data.count >= range.upperBound else {
85
+ throw UniffiInternalError.bufferOverflow
86
+ }
87
+ if T.self == UInt8.self {
88
+ let value = reader.data[reader.offset]
89
+ reader.offset += 1
90
+ return value as! T
91
+ }
92
+ var value: T = 0
93
+ let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
94
+ reader.offset = range.upperBound
95
+ return value.bigEndian
96
+}
97
+
98
+// Reads an arbitrary number of bytes, to be used to read
99
+// raw bytes, this is useful when lifting strings
100
+fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array<UInt8> {
101
+ let range = reader.offset..<(reader.offset+count)
102
+ guard reader.data.count >= range.upperBound else {
103
+ throw UniffiInternalError.bufferOverflow
104
+ }
105
+ var value = [UInt8](repeating: 0, count: count)
106
+ value.withUnsafeMutableBufferPointer({ buffer in
107
+ reader.data.copyBytes(to: buffer, from: range)
108
+ })
109
+ reader.offset = range.upperBound
110
+ return value
111
+}
112
+
113
+// Reads a float at the current offset.
114
+fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
115
+ return Float(bitPattern: try readInt(&reader))
116
+}
117
+
118
+// Reads a float at the current offset.
119
+fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
120
+ return Double(bitPattern: try readInt(&reader))
121
+}
122
+
123
+// Indicates if the offset has reached the end of the buffer.
124
+fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
125
+ return reader.offset < reader.data.count
126
+}
127
+
128
+// Define writer functionality. Normally this would be defined in a class or
129
+// struct, but we use standalone functions instead in order to make external
130
+// types work. See the above discussion on Readers for details.
131
+
132
+fileprivate func createWriter() -> [UInt8] {
133
+ return []
134
+}
135
+
136
+fileprivate func writeBytes<S>(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
137
+ writer.append(contentsOf: byteArr)
138
+}
139
+
140
+// Writes an integer in big-endian order.
141
+//
142
+// Warning: make sure what you are trying to write
143
+// is in the correct type!
144
+fileprivate func writeInt<T: FixedWidthInteger>(_ writer: inout [UInt8], _ value: T) {
145
+ var value = value.bigEndian
146
+ withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
147
+}
148
+
149
+fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
150
+ writeInt(&writer, value.bitPattern)
151
+}
152
+
153
+fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
154
+ writeInt(&writer, value.bitPattern)
155
+}
156
+
157
+// Protocol for types that transfer other types across the FFI. This is
158
+// analogous to the Rust trait of the same name.
159
+fileprivate protocol FfiConverter {
160
+ associatedtype FfiType
161
+ associatedtype SwiftType
162
+
163
+ static func lift(_ value: FfiType) throws -> SwiftType
164
+ static func lower(_ value: SwiftType) -> FfiType
165
+ static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
166
+ static func write(_ value: SwiftType, into buf: inout [UInt8])
167
+}
168
+
169
+// Types conforming to `Primitive` pass themselves directly over the FFI.
170
+fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
171
+
172
+extension FfiConverterPrimitive {
173
+#if swift(>=5.8)
174
+ @_documentation(visibility: private)
175
+#endif
176
+ public static func lift(_ value: FfiType) throws -> SwiftType {
177
+ return value
178
+ }
179
+
180
+#if swift(>=5.8)
181
+ @_documentation(visibility: private)
182
+#endif
183
+ public static func lower(_ value: SwiftType) -> FfiType {
184
+ return value
185
+ }
186
+}
187
+
188
+// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
189
+// Used for complex types where it's hard to write a custom lift/lower.
190
+fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
191
+
192
+extension FfiConverterRustBuffer {
193
+#if swift(>=5.8)
194
+ @_documentation(visibility: private)
195
+#endif
196
+ public static func lift(_ buf: RustBuffer) throws -> SwiftType {
197
+ var reader = createReader(data: Data(rustBuffer: buf))
198
+ let value = try read(from: &reader)
199
+ if hasRemaining(reader) {
200
+ throw UniffiInternalError.incompleteData
201
+ }
202
+ buf.deallocate()
203
+ return value
204
+ }
205
+
206
+#if swift(>=5.8)
207
+ @_documentation(visibility: private)
208
+#endif
209
+ public static func lower(_ value: SwiftType) -> RustBuffer {
210
+ var writer = createWriter()
211
+ write(value, into: &writer)
212
+ return RustBuffer(bytes: writer)
213
+ }
214
+}
215
+// An error type for FFI errors. These errors occur at the UniFFI level, not
216
+// the library level.
217
+fileprivate enum UniffiInternalError: LocalizedError {
218
+ case bufferOverflow
219
+ case incompleteData
220
+ case unexpectedOptionalTag
221
+ case unexpectedEnumCase
222
+ case unexpectedNullPointer
223
+ case unexpectedRustCallStatusCode
224
+ case unexpectedRustCallError
225
+ case unexpectedStaleHandle
226
+ case rustPanic(_ message: String)
227
+
228
+ public var errorDescription: String? {
229
+ switch self {
230
+ case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
231
+ case .incompleteData: return "The buffer still has data after lifting its containing value"
232
+ case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
233
+ case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
234
+ case .unexpectedNullPointer: return "Raw pointer value was null"
235
+ case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
236
+ case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
237
+ case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
238
+ case let .rustPanic(message): return message
239
+ }
240
+ }
241
+}
242
+
243
+fileprivate extension NSLock {
244
+ func withLock<T>(f: () throws -> T) rethrows -> T {
245
+ self.lock()
246
+ defer { self.unlock() }
247
+ return try f()
248
+ }
249
+}
250
+
251
+fileprivate let CALL_SUCCESS: Int8 = 0
252
+fileprivate let CALL_ERROR: Int8 = 1
253
+fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
254
+fileprivate let CALL_CANCELLED: Int8 = 3
255
+
256
+fileprivate extension RustCallStatus {
257
+ init() {
258
+ self.init(
259
+ code: CALL_SUCCESS,
260
+ errorBuf: RustBuffer.init(
261
+ capacity: 0,
262
+ len: 0,
263
+ data: nil
264
+ )
265
+ )
266
+ }
267
+}
268
+
269
+private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
270
+ let neverThrow: ((RustBuffer) throws -> Never)? = nil
271
+ return try makeRustCall(callback, errorHandler: neverThrow)
272
+}
273
+
274
+private func rustCallWithError<T, E: Swift.Error>(
275
+ _ errorHandler: @escaping (RustBuffer) throws -> E,
276
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
277
+ try makeRustCall(callback, errorHandler: errorHandler)
278
+}
279
+
280
+private func makeRustCall<T, E: Swift.Error>(
281
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
282
+ errorHandler: ((RustBuffer) throws -> E)?
283
+) throws -> T {
284
+ uniffiEnsureEdAgentFfiInitialized()
285
+ var callStatus = RustCallStatus.init()
286
+ let returnedVal = callback(&callStatus)
287
+ try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
288
+ return returnedVal
289
+}
290
+
291
+private func uniffiCheckCallStatus<E: Swift.Error>(
292
+ callStatus: RustCallStatus,
293
+ errorHandler: ((RustBuffer) throws -> E)?
294
+) throws {
295
+ switch callStatus.code {
296
+ case CALL_SUCCESS:
297
+ return
298
+
299
+ case CALL_ERROR:
300
+ if let errorHandler = errorHandler {
301
+ throw try errorHandler(callStatus.errorBuf)
302
+ } else {
303
+ callStatus.errorBuf.deallocate()
304
+ throw UniffiInternalError.unexpectedRustCallError
305
+ }
306
+
307
+ case CALL_UNEXPECTED_ERROR:
308
+ // When the rust code sees a panic, it tries to construct a RustBuffer
309
+ // with the message. But if that code panics, then it just sends back
310
+ // an empty buffer.
311
+ if callStatus.errorBuf.len > 0 {
312
+ throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
313
+ } else {
314
+ callStatus.errorBuf.deallocate()
315
+ throw UniffiInternalError.rustPanic("Rust panic")
316
+ }
317
+
318
+ case CALL_CANCELLED:
319
+ fatalError("Cancellation not supported yet")
320
+
321
+ default:
322
+ throw UniffiInternalError.unexpectedRustCallStatusCode
323
+ }
324
+}
325
+
326
+private func uniffiTraitInterfaceCall<T>(
327
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
328
+ makeCall: () throws -> T,
329
+ writeReturn: (T) -> ()
330
+) {
331
+ do {
332
+ try writeReturn(makeCall())
333
+ } catch let error {
334
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
335
+ callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
336
+ }
337
+}
338
+
339
+private func uniffiTraitInterfaceCallWithError<T, E>(
340
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
341
+ makeCall: () throws -> T,
342
+ writeReturn: (T) -> (),
343
+ lowerError: (E) -> RustBuffer
344
+) {
345
+ do {
346
+ try writeReturn(makeCall())
347
+ } catch let error as E {
348
+ callStatus.pointee.code = CALL_ERROR
349
+ callStatus.pointee.errorBuf = lowerError(error)
350
+ } catch {
351
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
352
+ callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
353
+ }
354
+}
355
+// Initial value and increment amount for handles.
356
+// These ensure that SWIFT handles always have the lowest bit set
357
+fileprivate let UNIFFI_HANDLEMAP_INITIAL: UInt64 = 1
358
+fileprivate let UNIFFI_HANDLEMAP_DELTA: UInt64 = 2
359
+
360
+fileprivate final class UniffiHandleMap<T>: @unchecked Sendable {
361
+ // All mutation happens with this lock held, which is why we implement @unchecked Sendable.
362
+ private let lock = NSLock()
363
+ private var map: [UInt64: T] = [:]
364
+ private var currentHandle: UInt64 = UNIFFI_HANDLEMAP_INITIAL
365
+
366
+ func insert(obj: T) -> UInt64 {
367
+ lock.withLock {
368
+ return doInsert(obj)
369
+ }
370
+ }
371
+
372
+ // Low-level insert function, this assumes `lock` is held.
373
+ private func doInsert(_ obj: T) -> UInt64 {
374
+ let handle = currentHandle
375
+ currentHandle += UNIFFI_HANDLEMAP_DELTA
376
+ map[handle] = obj
377
+ return handle
378
+ }
379
+
380
+ func get(handle: UInt64) throws -> T {
381
+ try lock.withLock {
382
+ guard let obj = map[handle] else {
383
+ throw UniffiInternalError.unexpectedStaleHandle
384
+ }
385
+ return obj
386
+ }
387
+ }
388
+
389
+ func clone(handle: UInt64) throws -> UInt64 {
390
+ try lock.withLock {
391
+ guard let obj = map[handle] else {
392
+ throw UniffiInternalError.unexpectedStaleHandle
393
+ }
394
+ return doInsert(obj)
395
+ }
396
+ }
397
+
398
+ @discardableResult
399
+ func remove(handle: UInt64) throws -> T {
400
+ try lock.withLock {
401
+ guard let obj = map.removeValue(forKey: handle) else {
402
+ throw UniffiInternalError.unexpectedStaleHandle
403
+ }
404
+ return obj
405
+ }
406
+ }
407
+
408
+ var count: Int {
409
+ get {
410
+ map.count
411
+ }
412
+ }
413
+}
414
+
415
+
416
+// Public interface members begin here.
417
+// Magic number for the Rust proxy to call using the same mechanism as every other method,
418
+// to free the callback once it's dropped by Rust.
419
+private let IDX_CALLBACK_FREE: Int32 = 0
420
+// Callback return codes
421
+private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0
422
+private let UNIFFI_CALLBACK_ERROR: Int32 = 1
423
+private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2
424
+
425
+#if swift(>=5.8)
426
+@_documentation(visibility: private)
427
+#endif
428
+fileprivate struct FfiConverterUInt8: FfiConverterPrimitive {
429
+ typealias FfiType = UInt8
430
+ typealias SwiftType = UInt8
431
+
432
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 {
433
+ return try lift(readInt(&buf))
434
+ }
435
+
436
+ public static func write(_ value: UInt8, into buf: inout [UInt8]) {
437
+ writeInt(&buf, lower(value))
438
+ }
439
+}
440
+
441
+#if swift(>=5.8)
442
+@_documentation(visibility: private)
443
+#endif
444
+fileprivate struct FfiConverterUInt64: FfiConverterPrimitive {
445
+ typealias FfiType = UInt64
446
+ typealias SwiftType = UInt64
447
+
448
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 {
449
+ return try lift(readInt(&buf))
450
+ }
451
+
452
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
453
+ writeInt(&buf, lower(value))
454
+ }
455
+}
456
+
457
+#if swift(>=5.8)
458
+@_documentation(visibility: private)
459
+#endif
460
+fileprivate struct FfiConverterFloat: FfiConverterPrimitive {
461
+ typealias FfiType = Float
462
+ typealias SwiftType = Float
463
+
464
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Float {
465
+ return try lift(readFloat(&buf))
466
+ }
467
+
468
+ public static func write(_ value: Float, into buf: inout [UInt8]) {
469
+ writeFloat(&buf, lower(value))
470
+ }
471
+}
472
+
473
+#if swift(>=5.8)
474
+@_documentation(visibility: private)
475
+#endif
476
+fileprivate struct FfiConverterDouble: FfiConverterPrimitive {
477
+ typealias FfiType = Double
478
+ typealias SwiftType = Double
479
+
480
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Double {
481
+ return try lift(readDouble(&buf))
482
+ }
483
+
484
+ public static func write(_ value: Double, into buf: inout [UInt8]) {
485
+ writeDouble(&buf, lower(value))
486
+ }
487
+}
488
+
489
+#if swift(>=5.8)
490
+@_documentation(visibility: private)
491
+#endif
492
+fileprivate struct FfiConverterBool : FfiConverter {
493
+ typealias FfiType = Int8
494
+ typealias SwiftType = Bool
495
+
496
+ public static func lift(_ value: Int8) throws -> Bool {
497
+ return value != 0
498
+ }
499
+
500
+ public static func lower(_ value: Bool) -> Int8 {
501
+ return value ? 1 : 0
502
+ }
503
+
504
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
505
+ return try lift(readInt(&buf))
506
+ }
507
+
508
+ public static func write(_ value: Bool, into buf: inout [UInt8]) {
509
+ writeInt(&buf, lower(value))
510
+ }
511
+}
512
+
513
+#if swift(>=5.8)
514
+@_documentation(visibility: private)
515
+#endif
516
+fileprivate struct FfiConverterString: FfiConverter {
517
+ typealias SwiftType = String
518
+ typealias FfiType = RustBuffer
519
+
520
+ public static func lift(_ value: RustBuffer) throws -> String {
521
+ defer {
522
+ value.deallocate()
523
+ }
524
+ if value.data == nil {
525
+ return String()
526
+ }
527
+ let bytes = UnsafeBufferPointer<UInt8>(start: value.data!, count: Int(value.len))
528
+ return String(bytes: bytes, encoding: String.Encoding.utf8)!
529
+ }
530
+
531
+ public static func lower(_ value: String) -> RustBuffer {
532
+ return value.utf8CString.withUnsafeBufferPointer { ptr in
533
+ // The swift string gives us int8_t, we want uint8_t.
534
+ ptr.withMemoryRebound(to: UInt8.self) { ptr in
535
+ // The swift string gives us a trailing null byte, we don't want it.
536
+ let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
537
+ return RustBuffer.from(buf)
538
+ }
539
+ }
540
+ }
541
+
542
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
543
+ let len: Int32 = try readInt(&buf)
544
+ return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
545
+ }
546
+
547
+ public static func write(_ value: String, into buf: inout [UInt8]) {
548
+ let len = Int32(value.utf8.count)
549
+ writeInt(&buf, len)
550
+ writeBytes(&buf, value.utf8)
551
+ }
552
+}
553
+
554
+
555
+
556
+
557
+public protocol FfiEdAgentProtocol: AnyObject, Sendable {
558
+
559
+ func cancel()
560
+
561
+ func clearHistory() async -> UInt64
562
+
563
+ func history() async -> [FfiChatMessage]
564
+
565
+ func info() async -> FfiEngineInfo
566
+
567
+ func isLoaded() async -> Bool
568
+
569
+ func loadAssignedModel(environment: FfiEnvironment, appId: String, appSecret: String, systemPrompt: String?, sampling: FfiSamplingConfig?) async throws -> Double
570
+
571
+ func loadDefaultAgentModel(systemPrompt: String?) async throws -> Double
572
+
573
+ func loadGgufModel(config: FfiGgufModelConfig, systemPrompt: String?, sampling: FfiSamplingConfig?) async throws -> Double
574
+
575
+ func loadUqffModel(config: FfiUqffModelConfig, systemPrompt: String?, sampling: FfiSamplingConfig?) async throws -> Double
576
+
577
+ func registerTool(tool: FfiToolDefinition) async throws
578
+
579
+ func removeAllTools() async
580
+
581
+ func run(message: String) async throws -> FfiAgentReply
582
+
583
+ func send(message: String) async throws -> FfiReply
584
+
585
+ func unload() async -> String?
586
+
587
+ func unregisterTool(name: String) async -> Bool
588
+
589
+}
590
+open class FfiEdAgent: FfiEdAgentProtocol, @unchecked Sendable {
591
+ fileprivate let handle: UInt64
592
+
593
+ /// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly.
594
+#if swift(>=5.8)
595
+ @_documentation(visibility: private)
596
+#endif
597
+ public struct NoHandle {
598
+ public init() {}
599
+ }
600
+
601
+ // TODO: We'd like this to be `private` but for Swifty reasons,
602
+ // we can't implement `FfiConverter` without making this `required` and we can't
603
+ // make it `required` without making it `public`.
604
+#if swift(>=5.8)
605
+ @_documentation(visibility: private)
606
+#endif
607
+ required public init(unsafeFromHandle handle: UInt64) {
608
+ self.handle = handle
609
+ }
610
+
611
+ // This constructor can be used to instantiate a fake object.
612
+ // - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
613
+ //
614
+ // - Warning:
615
+ // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash.
616
+#if swift(>=5.8)
617
+ @_documentation(visibility: private)
618
+#endif
619
+ public init(noHandle: NoHandle) {
620
+ self.handle = 0
621
+ }
622
+
623
+#if swift(>=5.8)
624
+ @_documentation(visibility: private)
625
+#endif
626
+ public func uniffiCloneHandle() -> UInt64 {
627
+ return try! rustCall { uniffi_ed_agent_ffi_fn_clone_ffiedagent(self.handle, $0) }
628
+ }
629
+public convenience init(executor: FfiToolExecutor, approvals: FfiApprovalHandler, events: FfiEventListener, config: FfiAgentConfig?) {
630
+ let handle =
631
+ try! rustCall() {
632
+ uniffi_ed_agent_ffi_fn_constructor_ffiedagent_new(
633
+ FfiConverterCallbackInterfaceFfiToolExecutor_lower(executor),
634
+ FfiConverterCallbackInterfaceFfiApprovalHandler_lower(approvals),
635
+ FfiConverterCallbackInterfaceFfiEventListener_lower(events),
636
+ FfiConverterOptionTypeFfiAgentConfig.lower(config),$0
637
+ )
638
+}
639
+ self.init(unsafeFromHandle: handle)
640
+}
641
+
642
+ deinit {
643
+ if handle == 0 {
644
+ // Mock objects have handle=0 don't try to free them
645
+ return
646
+ }
647
+
648
+ try! rustCall { uniffi_ed_agent_ffi_fn_free_ffiedagent(handle, $0) }
649
+ }
650
+
651
+
652
+
653
+
654
+open func cancel() {try! rustCall() {
655
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_cancel(
656
+ self.uniffiCloneHandle(),$0
657
+ )
658
+}
659
+}
660
+
661
+open func clearHistory()async -> UInt64 {
662
+ return
663
+ try! await uniffiRustCallAsync(
664
+ rustFutureFunc: {
665
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_clear_history(
666
+ self.uniffiCloneHandle()
667
+
668
+ )
669
+ },
670
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_u64,
671
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_u64,
672
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_u64,
673
+ liftFunc: FfiConverterUInt64.lift,
674
+ errorHandler: nil
675
+
676
+ )
677
+}
678
+
679
+open func history()async -> [FfiChatMessage] {
680
+ return
681
+ try! await uniffiRustCallAsync(
682
+ rustFutureFunc: {
683
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_history(
684
+ self.uniffiCloneHandle()
685
+
686
+ )
687
+ },
688
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_rust_buffer,
689
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_rust_buffer,
690
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_rust_buffer,
691
+ liftFunc: FfiConverterSequenceTypeFfiChatMessage.lift,
692
+ errorHandler: nil
693
+
694
+ )
695
+}
696
+
697
+open func info()async -> FfiEngineInfo {
698
+ return
699
+ try! await uniffiRustCallAsync(
700
+ rustFutureFunc: {
701
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_info(
702
+ self.uniffiCloneHandle()
703
+
704
+ )
705
+ },
706
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_rust_buffer,
707
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_rust_buffer,
708
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_rust_buffer,
709
+ liftFunc: FfiConverterTypeFfiEngineInfo_lift,
710
+ errorHandler: nil
711
+
712
+ )
713
+}
714
+
715
+open func isLoaded()async -> Bool {
716
+ return
717
+ try! await uniffiRustCallAsync(
718
+ rustFutureFunc: {
719
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_is_loaded(
720
+ self.uniffiCloneHandle()
721
+
722
+ )
723
+ },
724
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_i8,
725
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_i8,
726
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_i8,
727
+ liftFunc: FfiConverterBool.lift,
728
+ errorHandler: nil
729
+
730
+ )
731
+}
732
+
733
+open func loadAssignedModel(environment: FfiEnvironment, appId: String, appSecret: String, systemPrompt: String?, sampling: FfiSamplingConfig?)async throws -> Double {
734
+ return
735
+ try await uniffiRustCallAsync(
736
+ rustFutureFunc: {
737
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_load_assigned_model(
738
+ self.uniffiCloneHandle(),
739
+ FfiConverterTypeFfiEnvironment_lower(environment),FfiConverterString.lower(appId),FfiConverterString.lower(appSecret),FfiConverterOptionString.lower(systemPrompt),FfiConverterOptionTypeFfiSamplingConfig.lower(sampling)
740
+ )
741
+ },
742
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_f64,
743
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_f64,
744
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_f64,
745
+ liftFunc: FfiConverterDouble.lift,
746
+ errorHandler: FfiConverterTypeFfiEdError_lift
747
+ )
748
+}
749
+
750
+open func loadDefaultAgentModel(systemPrompt: String?)async throws -> Double {
751
+ return
752
+ try await uniffiRustCallAsync(
753
+ rustFutureFunc: {
754
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_load_default_agent_model(
755
+ self.uniffiCloneHandle(),
756
+ FfiConverterOptionString.lower(systemPrompt)
757
+ )
758
+ },
759
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_f64,
760
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_f64,
761
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_f64,
762
+ liftFunc: FfiConverterDouble.lift,
763
+ errorHandler: FfiConverterTypeFfiEdError_lift
764
+ )
765
+}
766
+
767
+open func loadGgufModel(config: FfiGgufModelConfig, systemPrompt: String?, sampling: FfiSamplingConfig?)async throws -> Double {
768
+ return
769
+ try await uniffiRustCallAsync(
770
+ rustFutureFunc: {
771
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_load_gguf_model(
772
+ self.uniffiCloneHandle(),
773
+ FfiConverterTypeFfiGgufModelConfig_lower(config),FfiConverterOptionString.lower(systemPrompt),FfiConverterOptionTypeFfiSamplingConfig.lower(sampling)
774
+ )
775
+ },
776
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_f64,
777
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_f64,
778
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_f64,
779
+ liftFunc: FfiConverterDouble.lift,
780
+ errorHandler: FfiConverterTypeFfiEdError_lift
781
+ )
782
+}
783
+
784
+open func loadUqffModel(config: FfiUqffModelConfig, systemPrompt: String?, sampling: FfiSamplingConfig?)async throws -> Double {
785
+ return
786
+ try await uniffiRustCallAsync(
787
+ rustFutureFunc: {
788
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_load_uqff_model(
789
+ self.uniffiCloneHandle(),
790
+ FfiConverterTypeFfiUqffModelConfig_lower(config),FfiConverterOptionString.lower(systemPrompt),FfiConverterOptionTypeFfiSamplingConfig.lower(sampling)
791
+ )
792
+ },
793
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_f64,
794
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_f64,
795
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_f64,
796
+ liftFunc: FfiConverterDouble.lift,
797
+ errorHandler: FfiConverterTypeFfiEdError_lift
798
+ )
799
+}
800
+
801
+open func registerTool(tool: FfiToolDefinition)async throws {
802
+ return
803
+ try await uniffiRustCallAsync(
804
+ rustFutureFunc: {
805
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_register_tool(
806
+ self.uniffiCloneHandle(),
807
+ FfiConverterTypeFfiToolDefinition_lower(tool)
808
+ )
809
+ },
810
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_void,
811
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_void,
812
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_void,
813
+ liftFunc: { $0 },
814
+ errorHandler: FfiConverterTypeFfiEdError_lift
815
+ )
816
+}
817
+
818
+open func removeAllTools()async {
819
+ return
820
+ try! await uniffiRustCallAsync(
821
+ rustFutureFunc: {
822
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_remove_all_tools(
823
+ self.uniffiCloneHandle()
824
+
825
+ )
826
+ },
827
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_void,
828
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_void,
829
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_void,
830
+ liftFunc: { $0 },
831
+ errorHandler: nil
832
+
833
+ )
834
+}
835
+
836
+open func run(message: String)async throws -> FfiAgentReply {
837
+ return
838
+ try await uniffiRustCallAsync(
839
+ rustFutureFunc: {
840
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_run(
841
+ self.uniffiCloneHandle(),
842
+ FfiConverterString.lower(message)
843
+ )
844
+ },
845
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_rust_buffer,
846
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_rust_buffer,
847
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_rust_buffer,
848
+ liftFunc: FfiConverterTypeFfiAgentReply_lift,
849
+ errorHandler: FfiConverterTypeFfiEdError_lift
850
+ )
851
+}
852
+
853
+open func send(message: String)async throws -> FfiReply {
854
+ return
855
+ try await uniffiRustCallAsync(
856
+ rustFutureFunc: {
857
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_send(
858
+ self.uniffiCloneHandle(),
859
+ FfiConverterString.lower(message)
860
+ )
861
+ },
862
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_rust_buffer,
863
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_rust_buffer,
864
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_rust_buffer,
865
+ liftFunc: FfiConverterTypeFfiReply_lift,
866
+ errorHandler: FfiConverterTypeFfiEdError_lift
867
+ )
868
+}
869
+
870
+open func unload()async -> String? {
871
+ return
872
+ try! await uniffiRustCallAsync(
873
+ rustFutureFunc: {
874
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_unload(
875
+ self.uniffiCloneHandle()
876
+
877
+ )
878
+ },
879
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_rust_buffer,
880
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_rust_buffer,
881
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_rust_buffer,
882
+ liftFunc: FfiConverterOptionString.lift,
883
+ errorHandler: nil
884
+
885
+ )
886
+}
887
+
888
+open func unregisterTool(name: String)async -> Bool {
889
+ return
890
+ try! await uniffiRustCallAsync(
891
+ rustFutureFunc: {
892
+ uniffi_ed_agent_ffi_fn_method_ffiedagent_unregister_tool(
893
+ self.uniffiCloneHandle(),
894
+ FfiConverterString.lower(name)
895
+ )
896
+ },
897
+ pollFunc: ffi_ed_agent_ffi_rust_future_poll_i8,
898
+ completeFunc: ffi_ed_agent_ffi_rust_future_complete_i8,
899
+ freeFunc: ffi_ed_agent_ffi_rust_future_free_i8,
900
+ liftFunc: FfiConverterBool.lift,
901
+ errorHandler: nil
902
+
903
+ )
904
+}
905
+
906
+
907
+
908
+}
909
+
910
+
911
+#if swift(>=5.8)
912
+@_documentation(visibility: private)
913
+#endif
914
+public struct FfiConverterTypeFfiEdAgent: FfiConverter {
915
+ typealias FfiType = UInt64
916
+ typealias SwiftType = FfiEdAgent
917
+
918
+ public static func lift(_ handle: UInt64) throws -> FfiEdAgent {
919
+ return FfiEdAgent(unsafeFromHandle: handle)
920
+ }
921
+
922
+ public static func lower(_ value: FfiEdAgent) -> UInt64 {
923
+ return value.uniffiCloneHandle()
924
+ }
925
+
926
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiEdAgent {
927
+ let handle: UInt64 = try readInt(&buf)
928
+ return try lift(handle)
929
+ }
930
+
931
+ public static func write(_ value: FfiEdAgent, into buf: inout [UInt8]) {
932
+ writeInt(&buf, lower(value))
933
+ }
934
+}
935
+
936
+
937
+#if swift(>=5.8)
938
+@_documentation(visibility: private)
939
+#endif
940
+public func FfiConverterTypeFfiEdAgent_lift(_ handle: UInt64) throws -> FfiEdAgent {
941
+ return try FfiConverterTypeFfiEdAgent.lift(handle)
942
+}
943
+
944
+#if swift(>=5.8)
945
+@_documentation(visibility: private)
946
+#endif
947
+public func FfiConverterTypeFfiEdAgent_lower(_ value: FfiEdAgent) -> UInt64 {
948
+ return FfiConverterTypeFfiEdAgent.lower(value)
949
+}
950
+
951
+
952
+
953
+
954
+public struct FfiAgentConfig: Equatable, Hashable {
955
+ public var maxToolRounds: UInt8
956
+ public var maxToolOutputChars: UInt64
957
+
958
+ // Default memberwise initializers are never public by default, so we
959
+ // declare one manually.
960
+ public init(maxToolRounds: UInt8, maxToolOutputChars: UInt64) {
961
+ self.maxToolRounds = maxToolRounds
962
+ self.maxToolOutputChars = maxToolOutputChars
963
+ }
964
+
965
+
966
+
967
+
968
+}
969
+
970
+#if compiler(>=6)
971
+extension FfiAgentConfig: Sendable {}
972
+#endif
973
+
974
+#if swift(>=5.8)
975
+@_documentation(visibility: private)
976
+#endif
977
+public struct FfiConverterTypeFfiAgentConfig: FfiConverterRustBuffer {
978
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiAgentConfig {
979
+ return
980
+ try FfiAgentConfig(
981
+ maxToolRounds: FfiConverterUInt8.read(from: &buf),
982
+ maxToolOutputChars: FfiConverterUInt64.read(from: &buf)
983
+ )
984
+ }
985
+
986
+ public static func write(_ value: FfiAgentConfig, into buf: inout [UInt8]) {
987
+ FfiConverterUInt8.write(value.maxToolRounds, into: &buf)
988
+ FfiConverterUInt64.write(value.maxToolOutputChars, into: &buf)
989
+ }
990
+}
991
+
992
+
993
+#if swift(>=5.8)
994
+@_documentation(visibility: private)
995
+#endif
996
+public func FfiConverterTypeFfiAgentConfig_lift(_ buf: RustBuffer) throws -> FfiAgentConfig {
997
+ return try FfiConverterTypeFfiAgentConfig.lift(buf)
998
+}
999
+
1000
+#if swift(>=5.8)
1001
+@_documentation(visibility: private)
1002
+#endif
1003
+public func FfiConverterTypeFfiAgentConfig_lower(_ value: FfiAgentConfig) -> RustBuffer {
1004
+ return FfiConverterTypeFfiAgentConfig.lower(value)
1005
+}
1006
+
1007
+
1008
+public struct FfiAgentReply: Equatable, Hashable {
1009
+ public var text: String
1010
+ public var durationSeconds: Double
1011
+ public var duration: String
1012
+ public var toolRounds: UInt8
1013
+
1014
+ // Default memberwise initializers are never public by default, so we
1015
+ // declare one manually.
1016
+ public init(text: String, durationSeconds: Double, duration: String, toolRounds: UInt8) {
1017
+ self.text = text
1018
+ self.durationSeconds = durationSeconds
1019
+ self.duration = duration
1020
+ self.toolRounds = toolRounds
1021
+ }
1022
+
1023
+
1024
+
1025
+
1026
+}
1027
+
1028
+#if compiler(>=6)
1029
+extension FfiAgentReply: Sendable {}
1030
+#endif
1031
+
1032
+#if swift(>=5.8)
1033
+@_documentation(visibility: private)
1034
+#endif
1035
+public struct FfiConverterTypeFfiAgentReply: FfiConverterRustBuffer {
1036
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiAgentReply {
1037
+ return
1038
+ try FfiAgentReply(
1039
+ text: FfiConverterString.read(from: &buf),
1040
+ durationSeconds: FfiConverterDouble.read(from: &buf),
1041
+ duration: FfiConverterString.read(from: &buf),
1042
+ toolRounds: FfiConverterUInt8.read(from: &buf)
1043
+ )
1044
+ }
1045
+
1046
+ public static func write(_ value: FfiAgentReply, into buf: inout [UInt8]) {
1047
+ FfiConverterString.write(value.text, into: &buf)
1048
+ FfiConverterDouble.write(value.durationSeconds, into: &buf)
1049
+ FfiConverterString.write(value.duration, into: &buf)
1050
+ FfiConverterUInt8.write(value.toolRounds, into: &buf)
1051
+ }
1052
+}
1053
+
1054
+
1055
+#if swift(>=5.8)
1056
+@_documentation(visibility: private)
1057
+#endif
1058
+public func FfiConverterTypeFfiAgentReply_lift(_ buf: RustBuffer) throws -> FfiAgentReply {
1059
+ return try FfiConverterTypeFfiAgentReply.lift(buf)
1060
+}
1061
+
1062
+#if swift(>=5.8)
1063
+@_documentation(visibility: private)
1064
+#endif
1065
+public func FfiConverterTypeFfiAgentReply_lower(_ value: FfiAgentReply) -> RustBuffer {
1066
+ return FfiConverterTypeFfiAgentReply.lower(value)
1067
+}
1068
+
1069
+
1070
+public struct FfiChatMessage: Equatable, Hashable {
1071
+ public var role: FfiChatRole
1072
+ public var content: String
1073
+
1074
+ // Default memberwise initializers are never public by default, so we
1075
+ // declare one manually.
1076
+ public init(role: FfiChatRole, content: String) {
1077
+ self.role = role
1078
+ self.content = content
1079
+ }
1080
+
1081
+
1082
+
1083
+
1084
+}
1085
+
1086
+#if compiler(>=6)
1087
+extension FfiChatMessage: Sendable {}
1088
+#endif
1089
+
1090
+#if swift(>=5.8)
1091
+@_documentation(visibility: private)
1092
+#endif
1093
+public struct FfiConverterTypeFfiChatMessage: FfiConverterRustBuffer {
1094
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiChatMessage {
1095
+ return
1096
+ try FfiChatMessage(
1097
+ role: FfiConverterTypeFfiChatRole.read(from: &buf),
1098
+ content: FfiConverterString.read(from: &buf)
1099
+ )
1100
+ }
1101
+
1102
+ public static func write(_ value: FfiChatMessage, into buf: inout [UInt8]) {
1103
+ FfiConverterTypeFfiChatRole.write(value.role, into: &buf)
1104
+ FfiConverterString.write(value.content, into: &buf)
1105
+ }
1106
+}
1107
+
1108
+
1109
+#if swift(>=5.8)
1110
+@_documentation(visibility: private)
1111
+#endif
1112
+public func FfiConverterTypeFfiChatMessage_lift(_ buf: RustBuffer) throws -> FfiChatMessage {
1113
+ return try FfiConverterTypeFfiChatMessage.lift(buf)
1114
+}
1115
+
1116
+#if swift(>=5.8)
1117
+@_documentation(visibility: private)
1118
+#endif
1119
+public func FfiConverterTypeFfiChatMessage_lower(_ value: FfiChatMessage) -> RustBuffer {
1120
+ return FfiConverterTypeFfiChatMessage.lower(value)
1121
+}
1122
+
1123
+
1124
+public struct FfiEngineInfo: Equatable, Hashable {
1125
+ public var status: FfiEngineStatus
1126
+ public var modelName: String?
1127
+ public var approxMemory: String?
1128
+ public var historyLength: UInt64
1129
+
1130
+ // Default memberwise initializers are never public by default, so we
1131
+ // declare one manually.
1132
+ public init(status: FfiEngineStatus, modelName: String?, approxMemory: String?, historyLength: UInt64) {
1133
+ self.status = status
1134
+ self.modelName = modelName
1135
+ self.approxMemory = approxMemory
1136
+ self.historyLength = historyLength
1137
+ }
1138
+
1139
+
1140
+
1141
+
1142
+}
1143
+
1144
+#if compiler(>=6)
1145
+extension FfiEngineInfo: Sendable {}
1146
+#endif
1147
+
1148
+#if swift(>=5.8)
1149
+@_documentation(visibility: private)
1150
+#endif
1151
+public struct FfiConverterTypeFfiEngineInfo: FfiConverterRustBuffer {
1152
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiEngineInfo {
1153
+ return
1154
+ try FfiEngineInfo(
1155
+ status: FfiConverterTypeFfiEngineStatus.read(from: &buf),
1156
+ modelName: FfiConverterOptionString.read(from: &buf),
1157
+ approxMemory: FfiConverterOptionString.read(from: &buf),
1158
+ historyLength: FfiConverterUInt64.read(from: &buf)
1159
+ )
1160
+ }
1161
+
1162
+ public static func write(_ value: FfiEngineInfo, into buf: inout [UInt8]) {
1163
+ FfiConverterTypeFfiEngineStatus.write(value.status, into: &buf)
1164
+ FfiConverterOptionString.write(value.modelName, into: &buf)
1165
+ FfiConverterOptionString.write(value.approxMemory, into: &buf)
1166
+ FfiConverterUInt64.write(value.historyLength, into: &buf)
1167
+ }
1168
+}
1169
+
1170
+
1171
+#if swift(>=5.8)
1172
+@_documentation(visibility: private)
1173
+#endif
1174
+public func FfiConverterTypeFfiEngineInfo_lift(_ buf: RustBuffer) throws -> FfiEngineInfo {
1175
+ return try FfiConverterTypeFfiEngineInfo.lift(buf)
1176
+}
1177
+
1178
+#if swift(>=5.8)
1179
+@_documentation(visibility: private)
1180
+#endif
1181
+public func FfiConverterTypeFfiEngineInfo_lower(_ value: FfiEngineInfo) -> RustBuffer {
1182
+ return FfiConverterTypeFfiEngineInfo.lower(value)
1183
+}
1184
+
1185
+
1186
+public struct FfiGgufModelConfig: Equatable, Hashable {
1187
+ public var modelId: String
1188
+ public var files: [String]
1189
+ public var tokModelId: String?
1190
+ public var displayName: String
1191
+ public var approxMemory: String
1192
+ public var chatTemplate: String?
1193
+
1194
+ // Default memberwise initializers are never public by default, so we
1195
+ // declare one manually.
1196
+ public init(modelId: String, files: [String], tokModelId: String?, displayName: String, approxMemory: String, chatTemplate: String?) {
1197
+ self.modelId = modelId
1198
+ self.files = files
1199
+ self.tokModelId = tokModelId
1200
+ self.displayName = displayName
1201
+ self.approxMemory = approxMemory
1202
+ self.chatTemplate = chatTemplate
1203
+ }
1204
+
1205
+
1206
+
1207
+
1208
+}
1209
+
1210
+#if compiler(>=6)
1211
+extension FfiGgufModelConfig: Sendable {}
1212
+#endif
1213
+
1214
+#if swift(>=5.8)
1215
+@_documentation(visibility: private)
1216
+#endif
1217
+public struct FfiConverterTypeFfiGgufModelConfig: FfiConverterRustBuffer {
1218
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiGgufModelConfig {
1219
+ return
1220
+ try FfiGgufModelConfig(
1221
+ modelId: FfiConverterString.read(from: &buf),
1222
+ files: FfiConverterSequenceString.read(from: &buf),
1223
+ tokModelId: FfiConverterOptionString.read(from: &buf),
1224
+ displayName: FfiConverterString.read(from: &buf),
1225
+ approxMemory: FfiConverterString.read(from: &buf),
1226
+ chatTemplate: FfiConverterOptionString.read(from: &buf)
1227
+ )
1228
+ }
1229
+
1230
+ public static func write(_ value: FfiGgufModelConfig, into buf: inout [UInt8]) {
1231
+ FfiConverterString.write(value.modelId, into: &buf)
1232
+ FfiConverterSequenceString.write(value.files, into: &buf)
1233
+ FfiConverterOptionString.write(value.tokModelId, into: &buf)
1234
+ FfiConverterString.write(value.displayName, into: &buf)
1235
+ FfiConverterString.write(value.approxMemory, into: &buf)
1236
+ FfiConverterOptionString.write(value.chatTemplate, into: &buf)
1237
+ }
1238
+}
1239
+
1240
+
1241
+#if swift(>=5.8)
1242
+@_documentation(visibility: private)
1243
+#endif
1244
+public func FfiConverterTypeFfiGgufModelConfig_lift(_ buf: RustBuffer) throws -> FfiGgufModelConfig {
1245
+ return try FfiConverterTypeFfiGgufModelConfig.lift(buf)
1246
+}
1247
+
1248
+#if swift(>=5.8)
1249
+@_documentation(visibility: private)
1250
+#endif
1251
+public func FfiConverterTypeFfiGgufModelConfig_lower(_ value: FfiGgufModelConfig) -> RustBuffer {
1252
+ return FfiConverterTypeFfiGgufModelConfig.lower(value)
1253
+}
1254
+
1255
+
1256
+public struct FfiReply: Equatable, Hashable {
1257
+ public var text: String
1258
+ public var duration: String
1259
+
1260
+ // Default memberwise initializers are never public by default, so we
1261
+ // declare one manually.
1262
+ public init(text: String, duration: String) {
1263
+ self.text = text
1264
+ self.duration = duration
1265
+ }
1266
+
1267
+
1268
+
1269
+
1270
+}
1271
+
1272
+#if compiler(>=6)
1273
+extension FfiReply: Sendable {}
1274
+#endif
1275
+
1276
+#if swift(>=5.8)
1277
+@_documentation(visibility: private)
1278
+#endif
1279
+public struct FfiConverterTypeFfiReply: FfiConverterRustBuffer {
1280
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiReply {
1281
+ return
1282
+ try FfiReply(
1283
+ text: FfiConverterString.read(from: &buf),
1284
+ duration: FfiConverterString.read(from: &buf)
1285
+ )
1286
+ }
1287
+
1288
+ public static func write(_ value: FfiReply, into buf: inout [UInt8]) {
1289
+ FfiConverterString.write(value.text, into: &buf)
1290
+ FfiConverterString.write(value.duration, into: &buf)
1291
+ }
1292
+}
1293
+
1294
+
1295
+#if swift(>=5.8)
1296
+@_documentation(visibility: private)
1297
+#endif
1298
+public func FfiConverterTypeFfiReply_lift(_ buf: RustBuffer) throws -> FfiReply {
1299
+ return try FfiConverterTypeFfiReply.lift(buf)
1300
+}
1301
+
1302
+#if swift(>=5.8)
1303
+@_documentation(visibility: private)
1304
+#endif
1305
+public func FfiConverterTypeFfiReply_lower(_ value: FfiReply) -> RustBuffer {
1306
+ return FfiConverterTypeFfiReply.lower(value)
1307
+}
1308
+
1309
+
1310
+public struct FfiSamplingConfig: Equatable, Hashable {
1311
+ public var temperature: Double?
1312
+ public var topP: Double?
1313
+ public var topK: UInt64?
1314
+ public var minP: Double?
1315
+ public var maxTokens: UInt64?
1316
+ public var frequencyPenalty: Float?
1317
+ public var presencePenalty: Float?
1318
+
1319
+ // Default memberwise initializers are never public by default, so we
1320
+ // declare one manually.
1321
+ public init(temperature: Double?, topP: Double?, topK: UInt64?, minP: Double?, maxTokens: UInt64?, frequencyPenalty: Float?, presencePenalty: Float?) {
1322
+ self.temperature = temperature
1323
+ self.topP = topP
1324
+ self.topK = topK
1325
+ self.minP = minP
1326
+ self.maxTokens = maxTokens
1327
+ self.frequencyPenalty = frequencyPenalty
1328
+ self.presencePenalty = presencePenalty
1329
+ }
1330
+
1331
+
1332
+
1333
+
1334
+}
1335
+
1336
+#if compiler(>=6)
1337
+extension FfiSamplingConfig: Sendable {}
1338
+#endif
1339
+
1340
+#if swift(>=5.8)
1341
+@_documentation(visibility: private)
1342
+#endif
1343
+public struct FfiConverterTypeFfiSamplingConfig: FfiConverterRustBuffer {
1344
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiSamplingConfig {
1345
+ return
1346
+ try FfiSamplingConfig(
1347
+ temperature: FfiConverterOptionDouble.read(from: &buf),
1348
+ topP: FfiConverterOptionDouble.read(from: &buf),
1349
+ topK: FfiConverterOptionUInt64.read(from: &buf),
1350
+ minP: FfiConverterOptionDouble.read(from: &buf),
1351
+ maxTokens: FfiConverterOptionUInt64.read(from: &buf),
1352
+ frequencyPenalty: FfiConverterOptionFloat.read(from: &buf),
1353
+ presencePenalty: FfiConverterOptionFloat.read(from: &buf)
1354
+ )
1355
+ }
1356
+
1357
+ public static func write(_ value: FfiSamplingConfig, into buf: inout [UInt8]) {
1358
+ FfiConverterOptionDouble.write(value.temperature, into: &buf)
1359
+ FfiConverterOptionDouble.write(value.topP, into: &buf)
1360
+ FfiConverterOptionUInt64.write(value.topK, into: &buf)
1361
+ FfiConverterOptionDouble.write(value.minP, into: &buf)
1362
+ FfiConverterOptionUInt64.write(value.maxTokens, into: &buf)
1363
+ FfiConverterOptionFloat.write(value.frequencyPenalty, into: &buf)
1364
+ FfiConverterOptionFloat.write(value.presencePenalty, into: &buf)
1365
+ }
1366
+}
1367
+
1368
+
1369
+#if swift(>=5.8)
1370
+@_documentation(visibility: private)
1371
+#endif
1372
+public func FfiConverterTypeFfiSamplingConfig_lift(_ buf: RustBuffer) throws -> FfiSamplingConfig {
1373
+ return try FfiConverterTypeFfiSamplingConfig.lift(buf)
1374
+}
1375
+
1376
+#if swift(>=5.8)
1377
+@_documentation(visibility: private)
1378
+#endif
1379
+public func FfiConverterTypeFfiSamplingConfig_lower(_ value: FfiSamplingConfig) -> RustBuffer {
1380
+ return FfiConverterTypeFfiSamplingConfig.lower(value)
1381
+}
1382
+
1383
+
1384
+public struct FfiStatusUpdate: Equatable, Hashable {
1385
+ public var status: FfiEngineStatus
1386
+ public var modelName: String?
1387
+ public var error: String?
1388
+
1389
+ // Default memberwise initializers are never public by default, so we
1390
+ // declare one manually.
1391
+ public init(status: FfiEngineStatus, modelName: String?, error: String?) {
1392
+ self.status = status
1393
+ self.modelName = modelName
1394
+ self.error = error
1395
+ }
1396
+
1397
+
1398
+
1399
+
1400
+}
1401
+
1402
+#if compiler(>=6)
1403
+extension FfiStatusUpdate: Sendable {}
1404
+#endif
1405
+
1406
+#if swift(>=5.8)
1407
+@_documentation(visibility: private)
1408
+#endif
1409
+public struct FfiConverterTypeFfiStatusUpdate: FfiConverterRustBuffer {
1410
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiStatusUpdate {
1411
+ return
1412
+ try FfiStatusUpdate(
1413
+ status: FfiConverterTypeFfiEngineStatus.read(from: &buf),
1414
+ modelName: FfiConverterOptionString.read(from: &buf),
1415
+ error: FfiConverterOptionString.read(from: &buf)
1416
+ )
1417
+ }
1418
+
1419
+ public static func write(_ value: FfiStatusUpdate, into buf: inout [UInt8]) {
1420
+ FfiConverterTypeFfiEngineStatus.write(value.status, into: &buf)
1421
+ FfiConverterOptionString.write(value.modelName, into: &buf)
1422
+ FfiConverterOptionString.write(value.error, into: &buf)
1423
+ }
1424
+}
1425
+
1426
+
1427
+#if swift(>=5.8)
1428
+@_documentation(visibility: private)
1429
+#endif
1430
+public func FfiConverterTypeFfiStatusUpdate_lift(_ buf: RustBuffer) throws -> FfiStatusUpdate {
1431
+ return try FfiConverterTypeFfiStatusUpdate.lift(buf)
1432
+}
1433
+
1434
+#if swift(>=5.8)
1435
+@_documentation(visibility: private)
1436
+#endif
1437
+public func FfiConverterTypeFfiStatusUpdate_lower(_ value: FfiStatusUpdate) -> RustBuffer {
1438
+ return FfiConverterTypeFfiStatusUpdate.lower(value)
1439
+}
1440
+
1441
+
1442
+public struct FfiToolCall: Equatable, Hashable {
1443
+ public var id: String
1444
+ public var name: String
1445
+ public var arguments: String
1446
+
1447
+ // Default memberwise initializers are never public by default, so we
1448
+ // declare one manually.
1449
+ public init(id: String, name: String, arguments: String) {
1450
+ self.id = id
1451
+ self.name = name
1452
+ self.arguments = arguments
1453
+ }
1454
+
1455
+
1456
+
1457
+
1458
+}
1459
+
1460
+#if compiler(>=6)
1461
+extension FfiToolCall: Sendable {}
1462
+#endif
1463
+
1464
+#if swift(>=5.8)
1465
+@_documentation(visibility: private)
1466
+#endif
1467
+public struct FfiConverterTypeFfiToolCall: FfiConverterRustBuffer {
1468
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiToolCall {
1469
+ return
1470
+ try FfiToolCall(
1471
+ id: FfiConverterString.read(from: &buf),
1472
+ name: FfiConverterString.read(from: &buf),
1473
+ arguments: FfiConverterString.read(from: &buf)
1474
+ )
1475
+ }
1476
+
1477
+ public static func write(_ value: FfiToolCall, into buf: inout [UInt8]) {
1478
+ FfiConverterString.write(value.id, into: &buf)
1479
+ FfiConverterString.write(value.name, into: &buf)
1480
+ FfiConverterString.write(value.arguments, into: &buf)
1481
+ }
1482
+}
1483
+
1484
+
1485
+#if swift(>=5.8)
1486
+@_documentation(visibility: private)
1487
+#endif
1488
+public func FfiConverterTypeFfiToolCall_lift(_ buf: RustBuffer) throws -> FfiToolCall {
1489
+ return try FfiConverterTypeFfiToolCall.lift(buf)
1490
+}
1491
+
1492
+#if swift(>=5.8)
1493
+@_documentation(visibility: private)
1494
+#endif
1495
+public func FfiConverterTypeFfiToolCall_lower(_ value: FfiToolCall) -> RustBuffer {
1496
+ return FfiConverterTypeFfiToolCall.lower(value)
1497
+}
1498
+
1499
+
1500
+public struct FfiToolDefinition: Equatable, Hashable {
1501
+ public var name: String
1502
+ public var description: String
1503
+ public var parametersSchema: String
1504
+ public var risk: FfiToolRisk
1505
+
1506
+ // Default memberwise initializers are never public by default, so we
1507
+ // declare one manually.
1508
+ public init(name: String, description: String, parametersSchema: String, risk: FfiToolRisk) {
1509
+ self.name = name
1510
+ self.description = description
1511
+ self.parametersSchema = parametersSchema
1512
+ self.risk = risk
1513
+ }
1514
+
1515
+
1516
+
1517
+
1518
+}
1519
+
1520
+#if compiler(>=6)
1521
+extension FfiToolDefinition: Sendable {}
1522
+#endif
1523
+
1524
+#if swift(>=5.8)
1525
+@_documentation(visibility: private)
1526
+#endif
1527
+public struct FfiConverterTypeFfiToolDefinition: FfiConverterRustBuffer {
1528
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiToolDefinition {
1529
+ return
1530
+ try FfiToolDefinition(
1531
+ name: FfiConverterString.read(from: &buf),
1532
+ description: FfiConverterString.read(from: &buf),
1533
+ parametersSchema: FfiConverterString.read(from: &buf),
1534
+ risk: FfiConverterTypeFfiToolRisk.read(from: &buf)
1535
+ )
1536
+ }
1537
+
1538
+ public static func write(_ value: FfiToolDefinition, into buf: inout [UInt8]) {
1539
+ FfiConverterString.write(value.name, into: &buf)
1540
+ FfiConverterString.write(value.description, into: &buf)
1541
+ FfiConverterString.write(value.parametersSchema, into: &buf)
1542
+ FfiConverterTypeFfiToolRisk.write(value.risk, into: &buf)
1543
+ }
1544
+}
1545
+
1546
+
1547
+#if swift(>=5.8)
1548
+@_documentation(visibility: private)
1549
+#endif
1550
+public func FfiConverterTypeFfiToolDefinition_lift(_ buf: RustBuffer) throws -> FfiToolDefinition {
1551
+ return try FfiConverterTypeFfiToolDefinition.lift(buf)
1552
+}
1553
+
1554
+#if swift(>=5.8)
1555
+@_documentation(visibility: private)
1556
+#endif
1557
+public func FfiConverterTypeFfiToolDefinition_lower(_ value: FfiToolDefinition) -> RustBuffer {
1558
+ return FfiConverterTypeFfiToolDefinition.lower(value)
1559
+}
1560
+
1561
+
1562
+public struct FfiToolOutput: Equatable, Hashable {
1563
+ public var content: String
1564
+ public var error: String?
1565
+
1566
+ // Default memberwise initializers are never public by default, so we
1567
+ // declare one manually.
1568
+ public init(content: String, error: String?) {
1569
+ self.content = content
1570
+ self.error = error
1571
+ }
1572
+
1573
+
1574
+
1575
+
1576
+}
1577
+
1578
+#if compiler(>=6)
1579
+extension FfiToolOutput: Sendable {}
1580
+#endif
1581
+
1582
+#if swift(>=5.8)
1583
+@_documentation(visibility: private)
1584
+#endif
1585
+public struct FfiConverterTypeFfiToolOutput: FfiConverterRustBuffer {
1586
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiToolOutput {
1587
+ return
1588
+ try FfiToolOutput(
1589
+ content: FfiConverterString.read(from: &buf),
1590
+ error: FfiConverterOptionString.read(from: &buf)
1591
+ )
1592
+ }
1593
+
1594
+ public static func write(_ value: FfiToolOutput, into buf: inout [UInt8]) {
1595
+ FfiConverterString.write(value.content, into: &buf)
1596
+ FfiConverterOptionString.write(value.error, into: &buf)
1597
+ }
1598
+}
1599
+
1600
+
1601
+#if swift(>=5.8)
1602
+@_documentation(visibility: private)
1603
+#endif
1604
+public func FfiConverterTypeFfiToolOutput_lift(_ buf: RustBuffer) throws -> FfiToolOutput {
1605
+ return try FfiConverterTypeFfiToolOutput.lift(buf)
1606
+}
1607
+
1608
+#if swift(>=5.8)
1609
+@_documentation(visibility: private)
1610
+#endif
1611
+public func FfiConverterTypeFfiToolOutput_lower(_ value: FfiToolOutput) -> RustBuffer {
1612
+ return FfiConverterTypeFfiToolOutput.lower(value)
1613
+}
1614
+
1615
+
1616
+public struct FfiUqffModelConfig: Equatable, Hashable {
1617
+ public var modelId: String
1618
+ public var files: [String]
1619
+ public var displayName: String
1620
+ public var approxMemory: String
1621
+ public var chatTemplate: String?
1622
+
1623
+ // Default memberwise initializers are never public by default, so we
1624
+ // declare one manually.
1625
+ public init(modelId: String, files: [String], displayName: String, approxMemory: String, chatTemplate: String?) {
1626
+ self.modelId = modelId
1627
+ self.files = files
1628
+ self.displayName = displayName
1629
+ self.approxMemory = approxMemory
1630
+ self.chatTemplate = chatTemplate
1631
+ }
1632
+
1633
+
1634
+
1635
+
1636
+}
1637
+
1638
+#if compiler(>=6)
1639
+extension FfiUqffModelConfig: Sendable {}
1640
+#endif
1641
+
1642
+#if swift(>=5.8)
1643
+@_documentation(visibility: private)
1644
+#endif
1645
+public struct FfiConverterTypeFfiUqffModelConfig: FfiConverterRustBuffer {
1646
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiUqffModelConfig {
1647
+ return
1648
+ try FfiUqffModelConfig(
1649
+ modelId: FfiConverterString.read(from: &buf),
1650
+ files: FfiConverterSequenceString.read(from: &buf),
1651
+ displayName: FfiConverterString.read(from: &buf),
1652
+ approxMemory: FfiConverterString.read(from: &buf),
1653
+ chatTemplate: FfiConverterOptionString.read(from: &buf)
1654
+ )
1655
+ }
1656
+
1657
+ public static func write(_ value: FfiUqffModelConfig, into buf: inout [UInt8]) {
1658
+ FfiConverterString.write(value.modelId, into: &buf)
1659
+ FfiConverterSequenceString.write(value.files, into: &buf)
1660
+ FfiConverterString.write(value.displayName, into: &buf)
1661
+ FfiConverterString.write(value.approxMemory, into: &buf)
1662
+ FfiConverterOptionString.write(value.chatTemplate, into: &buf)
1663
+ }
1664
+}
1665
+
1666
+
1667
+#if swift(>=5.8)
1668
+@_documentation(visibility: private)
1669
+#endif
1670
+public func FfiConverterTypeFfiUqffModelConfig_lift(_ buf: RustBuffer) throws -> FfiUqffModelConfig {
1671
+ return try FfiConverterTypeFfiUqffModelConfig.lift(buf)
1672
+}
1673
+
1674
+#if swift(>=5.8)
1675
+@_documentation(visibility: private)
1676
+#endif
1677
+public func FfiConverterTypeFfiUqffModelConfig_lower(_ value: FfiUqffModelConfig) -> RustBuffer {
1678
+ return FfiConverterTypeFfiUqffModelConfig.lower(value)
1679
+}
1680
+
1681
+// Note that we don't yet support `indirect` for enums.
1682
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1683
+
1684
+public enum FfiApprovalDecision: Equatable, Hashable {
1685
+
1686
+ case allowOnce
1687
+ case allowForSession
1688
+ case deny
1689
+
1690
+
1691
+
1692
+
1693
+
1694
+}
1695
+
1696
+#if compiler(>=6)
1697
+extension FfiApprovalDecision: Sendable {}
1698
+#endif
1699
+
1700
+#if swift(>=5.8)
1701
+@_documentation(visibility: private)
1702
+#endif
1703
+public struct FfiConverterTypeFfiApprovalDecision: FfiConverterRustBuffer {
1704
+ typealias SwiftType = FfiApprovalDecision
1705
+
1706
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiApprovalDecision {
1707
+ let variant: Int32 = try readInt(&buf)
1708
+ switch variant {
1709
+
1710
+ case 1: return .allowOnce
1711
+
1712
+ case 2: return .allowForSession
1713
+
1714
+ case 3: return .deny
1715
+
1716
+ default: throw UniffiInternalError.unexpectedEnumCase
1717
+ }
1718
+ }
1719
+
1720
+ public static func write(_ value: FfiApprovalDecision, into buf: inout [UInt8]) {
1721
+ switch value {
1722
+
1723
+
1724
+ case .allowOnce:
1725
+ writeInt(&buf, Int32(1))
1726
+
1727
+
1728
+ case .allowForSession:
1729
+ writeInt(&buf, Int32(2))
1730
+
1731
+
1732
+ case .deny:
1733
+ writeInt(&buf, Int32(3))
1734
+
1735
+ }
1736
+ }
1737
+}
1738
+
1739
+
1740
+#if swift(>=5.8)
1741
+@_documentation(visibility: private)
1742
+#endif
1743
+public func FfiConverterTypeFfiApprovalDecision_lift(_ buf: RustBuffer) throws -> FfiApprovalDecision {
1744
+ return try FfiConverterTypeFfiApprovalDecision.lift(buf)
1745
+}
1746
+
1747
+#if swift(>=5.8)
1748
+@_documentation(visibility: private)
1749
+#endif
1750
+public func FfiConverterTypeFfiApprovalDecision_lower(_ value: FfiApprovalDecision) -> RustBuffer {
1751
+ return FfiConverterTypeFfiApprovalDecision.lower(value)
1752
+}
1753
+
1754
+
1755
+// Note that we don't yet support `indirect` for enums.
1756
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1757
+
1758
+public enum FfiChatRole: Equatable, Hashable {
1759
+
1760
+ case system
1761
+ case user
1762
+ case assistant
1763
+
1764
+
1765
+
1766
+
1767
+
1768
+}
1769
+
1770
+#if compiler(>=6)
1771
+extension FfiChatRole: Sendable {}
1772
+#endif
1773
+
1774
+#if swift(>=5.8)
1775
+@_documentation(visibility: private)
1776
+#endif
1777
+public struct FfiConverterTypeFfiChatRole: FfiConverterRustBuffer {
1778
+ typealias SwiftType = FfiChatRole
1779
+
1780
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiChatRole {
1781
+ let variant: Int32 = try readInt(&buf)
1782
+ switch variant {
1783
+
1784
+ case 1: return .system
1785
+
1786
+ case 2: return .user
1787
+
1788
+ case 3: return .assistant
1789
+
1790
+ default: throw UniffiInternalError.unexpectedEnumCase
1791
+ }
1792
+ }
1793
+
1794
+ public static func write(_ value: FfiChatRole, into buf: inout [UInt8]) {
1795
+ switch value {
1796
+
1797
+
1798
+ case .system:
1799
+ writeInt(&buf, Int32(1))
1800
+
1801
+
1802
+ case .user:
1803
+ writeInt(&buf, Int32(2))
1804
+
1805
+
1806
+ case .assistant:
1807
+ writeInt(&buf, Int32(3))
1808
+
1809
+ }
1810
+ }
1811
+}
1812
+
1813
+
1814
+#if swift(>=5.8)
1815
+@_documentation(visibility: private)
1816
+#endif
1817
+public func FfiConverterTypeFfiChatRole_lift(_ buf: RustBuffer) throws -> FfiChatRole {
1818
+ return try FfiConverterTypeFfiChatRole.lift(buf)
1819
+}
1820
+
1821
+#if swift(>=5.8)
1822
+@_documentation(visibility: private)
1823
+#endif
1824
+public func FfiConverterTypeFfiChatRole_lower(_ value: FfiChatRole) -> RustBuffer {
1825
+ return FfiConverterTypeFfiChatRole.lower(value)
1826
+}
1827
+
1828
+
1829
+
1830
+public enum FfiEdError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError {
1831
+
1832
+
1833
+
1834
+ case Failure(reason: String
1835
+ )
1836
+
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+ public var errorDescription: String? {
1843
+ String(reflecting: self)
1844
+ }
1845
+
1846
+}
1847
+
1848
+#if compiler(>=6)
1849
+extension FfiEdError: Sendable {}
1850
+#endif
1851
+
1852
+#if swift(>=5.8)
1853
+@_documentation(visibility: private)
1854
+#endif
1855
+public struct FfiConverterTypeFfiEdError: FfiConverterRustBuffer {
1856
+ typealias SwiftType = FfiEdError
1857
+
1858
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiEdError {
1859
+ let variant: Int32 = try readInt(&buf)
1860
+ switch variant {
1861
+
1862
+
1863
+
1864
+
1865
+ case 1: return .Failure(
1866
+ reason: try FfiConverterString.read(from: &buf)
1867
+ )
1868
+
1869
+ default: throw UniffiInternalError.unexpectedEnumCase
1870
+ }
1871
+ }
1872
+
1873
+ public static func write(_ value: FfiEdError, into buf: inout [UInt8]) {
1874
+ switch value {
1875
+
1876
+
1877
+
1878
+
1879
+
1880
+ case let .Failure(reason):
1881
+ writeInt(&buf, Int32(1))
1882
+ FfiConverterString.write(reason, into: &buf)
1883
+
1884
+ }
1885
+ }
1886
+}
1887
+
1888
+
1889
+#if swift(>=5.8)
1890
+@_documentation(visibility: private)
1891
+#endif
1892
+public func FfiConverterTypeFfiEdError_lift(_ buf: RustBuffer) throws -> FfiEdError {
1893
+ return try FfiConverterTypeFfiEdError.lift(buf)
1894
+}
1895
+
1896
+#if swift(>=5.8)
1897
+@_documentation(visibility: private)
1898
+#endif
1899
+public func FfiConverterTypeFfiEdError_lower(_ value: FfiEdError) -> RustBuffer {
1900
+ return FfiConverterTypeFfiEdError.lower(value)
1901
+}
1902
+
1903
+// Note that we don't yet support `indirect` for enums.
1904
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1905
+
1906
+public enum FfiEngineStatus: Equatable, Hashable {
1907
+
1908
+ case unloaded
1909
+ case loading
1910
+ case ready
1911
+ case generating
1912
+ case error
1913
+
1914
+
1915
+
1916
+
1917
+
1918
+}
1919
+
1920
+#if compiler(>=6)
1921
+extension FfiEngineStatus: Sendable {}
1922
+#endif
1923
+
1924
+#if swift(>=5.8)
1925
+@_documentation(visibility: private)
1926
+#endif
1927
+public struct FfiConverterTypeFfiEngineStatus: FfiConverterRustBuffer {
1928
+ typealias SwiftType = FfiEngineStatus
1929
+
1930
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiEngineStatus {
1931
+ let variant: Int32 = try readInt(&buf)
1932
+ switch variant {
1933
+
1934
+ case 1: return .unloaded
1935
+
1936
+ case 2: return .loading
1937
+
1938
+ case 3: return .ready
1939
+
1940
+ case 4: return .generating
1941
+
1942
+ case 5: return .error
1943
+
1944
+ default: throw UniffiInternalError.unexpectedEnumCase
1945
+ }
1946
+ }
1947
+
1948
+ public static func write(_ value: FfiEngineStatus, into buf: inout [UInt8]) {
1949
+ switch value {
1950
+
1951
+
1952
+ case .unloaded:
1953
+ writeInt(&buf, Int32(1))
1954
+
1955
+
1956
+ case .loading:
1957
+ writeInt(&buf, Int32(2))
1958
+
1959
+
1960
+ case .ready:
1961
+ writeInt(&buf, Int32(3))
1962
+
1963
+
1964
+ case .generating:
1965
+ writeInt(&buf, Int32(4))
1966
+
1967
+
1968
+ case .error:
1969
+ writeInt(&buf, Int32(5))
1970
+
1971
+ }
1972
+ }
1973
+}
1974
+
1975
+
1976
+#if swift(>=5.8)
1977
+@_documentation(visibility: private)
1978
+#endif
1979
+public func FfiConverterTypeFfiEngineStatus_lift(_ buf: RustBuffer) throws -> FfiEngineStatus {
1980
+ return try FfiConverterTypeFfiEngineStatus.lift(buf)
1981
+}
1982
+
1983
+#if swift(>=5.8)
1984
+@_documentation(visibility: private)
1985
+#endif
1986
+public func FfiConverterTypeFfiEngineStatus_lower(_ value: FfiEngineStatus) -> RustBuffer {
1987
+ return FfiConverterTypeFfiEngineStatus.lower(value)
1988
+}
1989
+
1990
+
1991
+// Note that we don't yet support `indirect` for enums.
1992
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1993
+
1994
+public enum FfiEnvironment: Equatable, Hashable {
1995
+
1996
+ case development
1997
+ case production
1998
+
1999
+
2000
+
2001
+
2002
+
2003
+}
2004
+
2005
+#if compiler(>=6)
2006
+extension FfiEnvironment: Sendable {}
2007
+#endif
2008
+
2009
+#if swift(>=5.8)
2010
+@_documentation(visibility: private)
2011
+#endif
2012
+public struct FfiConverterTypeFfiEnvironment: FfiConverterRustBuffer {
2013
+ typealias SwiftType = FfiEnvironment
2014
+
2015
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiEnvironment {
2016
+ let variant: Int32 = try readInt(&buf)
2017
+ switch variant {
2018
+
2019
+ case 1: return .development
2020
+
2021
+ case 2: return .production
2022
+
2023
+ default: throw UniffiInternalError.unexpectedEnumCase
2024
+ }
2025
+ }
2026
+
2027
+ public static func write(_ value: FfiEnvironment, into buf: inout [UInt8]) {
2028
+ switch value {
2029
+
2030
+
2031
+ case .development:
2032
+ writeInt(&buf, Int32(1))
2033
+
2034
+
2035
+ case .production:
2036
+ writeInt(&buf, Int32(2))
2037
+
2038
+ }
2039
+ }
2040
+}
2041
+
2042
+
2043
+#if swift(>=5.8)
2044
+@_documentation(visibility: private)
2045
+#endif
2046
+public func FfiConverterTypeFfiEnvironment_lift(_ buf: RustBuffer) throws -> FfiEnvironment {
2047
+ return try FfiConverterTypeFfiEnvironment.lift(buf)
2048
+}
2049
+
2050
+#if swift(>=5.8)
2051
+@_documentation(visibility: private)
2052
+#endif
2053
+public func FfiConverterTypeFfiEnvironment_lower(_ value: FfiEnvironment) -> RustBuffer {
2054
+ return FfiConverterTypeFfiEnvironment.lower(value)
2055
+}
2056
+
2057
+
2058
+// Note that we don't yet support `indirect` for enums.
2059
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
2060
+
2061
+public enum FfiToolRisk: Equatable, Hashable {
2062
+
2063
+ case readOnly
2064
+ case mutating
2065
+
2066
+
2067
+
2068
+
2069
+
2070
+}
2071
+
2072
+#if compiler(>=6)
2073
+extension FfiToolRisk: Sendable {}
2074
+#endif
2075
+
2076
+#if swift(>=5.8)
2077
+@_documentation(visibility: private)
2078
+#endif
2079
+public struct FfiConverterTypeFfiToolRisk: FfiConverterRustBuffer {
2080
+ typealias SwiftType = FfiToolRisk
2081
+
2082
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiToolRisk {
2083
+ let variant: Int32 = try readInt(&buf)
2084
+ switch variant {
2085
+
2086
+ case 1: return .readOnly
2087
+
2088
+ case 2: return .mutating
2089
+
2090
+ default: throw UniffiInternalError.unexpectedEnumCase
2091
+ }
2092
+ }
2093
+
2094
+ public static func write(_ value: FfiToolRisk, into buf: inout [UInt8]) {
2095
+ switch value {
2096
+
2097
+
2098
+ case .readOnly:
2099
+ writeInt(&buf, Int32(1))
2100
+
2101
+
2102
+ case .mutating:
2103
+ writeInt(&buf, Int32(2))
2104
+
2105
+ }
2106
+ }
2107
+}
2108
+
2109
+
2110
+#if swift(>=5.8)
2111
+@_documentation(visibility: private)
2112
+#endif
2113
+public func FfiConverterTypeFfiToolRisk_lift(_ buf: RustBuffer) throws -> FfiToolRisk {
2114
+ return try FfiConverterTypeFfiToolRisk.lift(buf)
2115
+}
2116
+
2117
+#if swift(>=5.8)
2118
+@_documentation(visibility: private)
2119
+#endif
2120
+public func FfiConverterTypeFfiToolRisk_lower(_ value: FfiToolRisk) -> RustBuffer {
2121
+ return FfiConverterTypeFfiToolRisk.lower(value)
2122
+}
2123
+
2124
+
2125
+
2126
+
2127
+
2128
+public protocol FfiApprovalHandler: AnyObject, Sendable {
2129
+
2130
+ func approve(call: FfiToolCall) async -> FfiApprovalDecision
2131
+
2132
+}
2133
+
2134
+
2135
+// Put the implementation in a struct so we don't pollute the top-level namespace
2136
+fileprivate struct UniffiCallbackInterfaceFfiApprovalHandler {
2137
+
2138
+ // Create the VTable using a series of closures.
2139
+ // Swift automatically converts these into C callback functions.
2140
+ //
2141
+ // This creates 1-element array, since this seems to be the only way to construct a const
2142
+ // pointer that we can pass to the Rust code.
2143
+ static let vtable: [UniffiVTableCallbackInterfaceFfiApprovalHandler] = [UniffiVTableCallbackInterfaceFfiApprovalHandler(
2144
+ uniffiFree: { (uniffiHandle: UInt64) -> () in
2145
+ do {
2146
+ try FfiConverterCallbackInterfaceFfiApprovalHandler.handleMap.remove(handle: uniffiHandle)
2147
+ } catch {
2148
+ print("Uniffi callback interface FfiApprovalHandler: handle missing in uniffiFree")
2149
+ }
2150
+ },
2151
+ uniffiClone: { (uniffiHandle: UInt64) -> UInt64 in
2152
+ do {
2153
+ return try FfiConverterCallbackInterfaceFfiApprovalHandler.handleMap.clone(handle: uniffiHandle)
2154
+ } catch {
2155
+ fatalError("Uniffi callback interface FfiApprovalHandler: handle missing in uniffiClone")
2156
+ }
2157
+ },
2158
+ approve: { (
2159
+ uniffiHandle: UInt64,
2160
+ call: RustBuffer,
2161
+ uniffiFutureCallback: @escaping UniffiForeignFutureCompleteRustBuffer,
2162
+ uniffiCallbackData: UInt64,
2163
+ uniffiOutDroppedCallback: UnsafeMutablePointer<UniffiForeignFutureDroppedCallbackStruct>
2164
+ ) in
2165
+ let makeCall = {
2166
+ () async throws -> FfiApprovalDecision in
2167
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiApprovalHandler.handleMap.get(handle: uniffiHandle) else {
2168
+ throw UniffiInternalError.unexpectedStaleHandle
2169
+ }
2170
+ return await uniffiObj.approve(
2171
+ call: try FfiConverterTypeFfiToolCall_lift(call)
2172
+ )
2173
+ }
2174
+
2175
+ let uniffiHandleSuccess = { (returnValue: FfiApprovalDecision) in
2176
+ uniffiFutureCallback(
2177
+ uniffiCallbackData,
2178
+ UniffiForeignFutureResultRustBuffer(
2179
+ returnValue: FfiConverterTypeFfiApprovalDecision_lower(returnValue),
2180
+ callStatus: RustCallStatus()
2181
+ )
2182
+ )
2183
+ }
2184
+ let uniffiHandleError = { (statusCode, errorBuf) in
2185
+ uniffiFutureCallback(
2186
+ uniffiCallbackData,
2187
+ UniffiForeignFutureResultRustBuffer(
2188
+ returnValue: RustBuffer.empty(),
2189
+ callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf)
2190
+ )
2191
+ )
2192
+ }
2193
+ uniffiTraitInterfaceCallAsync(
2194
+ makeCall: makeCall,
2195
+ handleSuccess: uniffiHandleSuccess,
2196
+ handleError: uniffiHandleError,
2197
+ droppedCallback: uniffiOutDroppedCallback
2198
+ )
2199
+ }
2200
+ )]
2201
+}
2202
+
2203
+private func uniffiCallbackInitFfiApprovalHandler() {
2204
+ uniffi_ed_agent_ffi_fn_init_callback_vtable_ffiapprovalhandler(UniffiCallbackInterfaceFfiApprovalHandler.vtable)
2205
+}
2206
+
2207
+// FfiConverter protocol for callback interfaces
2208
+#if swift(>=5.8)
2209
+@_documentation(visibility: private)
2210
+#endif
2211
+fileprivate struct FfiConverterCallbackInterfaceFfiApprovalHandler {
2212
+ fileprivate static let handleMap = UniffiHandleMap<FfiApprovalHandler>()
2213
+}
2214
+
2215
+#if swift(>=5.8)
2216
+@_documentation(visibility: private)
2217
+#endif
2218
+extension FfiConverterCallbackInterfaceFfiApprovalHandler : FfiConverter {
2219
+ typealias SwiftType = FfiApprovalHandler
2220
+ typealias FfiType = UInt64
2221
+
2222
+#if swift(>=5.8)
2223
+ @_documentation(visibility: private)
2224
+#endif
2225
+ public static func lift(_ handle: UInt64) throws -> SwiftType {
2226
+ try handleMap.get(handle: handle)
2227
+ }
2228
+
2229
+#if swift(>=5.8)
2230
+ @_documentation(visibility: private)
2231
+#endif
2232
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2233
+ let handle: UInt64 = try readInt(&buf)
2234
+ return try lift(handle)
2235
+ }
2236
+
2237
+#if swift(>=5.8)
2238
+ @_documentation(visibility: private)
2239
+#endif
2240
+ public static func lower(_ v: SwiftType) -> UInt64 {
2241
+ return handleMap.insert(obj: v)
2242
+ }
2243
+
2244
+#if swift(>=5.8)
2245
+ @_documentation(visibility: private)
2246
+#endif
2247
+ public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
2248
+ writeInt(&buf, lower(v))
2249
+ }
2250
+}
2251
+
2252
+
2253
+#if swift(>=5.8)
2254
+@_documentation(visibility: private)
2255
+#endif
2256
+public func FfiConverterCallbackInterfaceFfiApprovalHandler_lift(_ handle: UInt64) throws -> FfiApprovalHandler {
2257
+ return try FfiConverterCallbackInterfaceFfiApprovalHandler.lift(handle)
2258
+}
2259
+
2260
+#if swift(>=5.8)
2261
+@_documentation(visibility: private)
2262
+#endif
2263
+public func FfiConverterCallbackInterfaceFfiApprovalHandler_lower(_ v: FfiApprovalHandler) -> UInt64 {
2264
+ return FfiConverterCallbackInterfaceFfiApprovalHandler.lower(v)
2265
+}
2266
+
2267
+
2268
+
2269
+
2270
+public protocol FfiEventListener: AnyObject, Sendable {
2271
+
2272
+ func statusChanged(update: FfiStatusUpdate)
2273
+
2274
+ func toolRequested(call: FfiToolCall)
2275
+
2276
+ func approvalRequested(call: FfiToolCall)
2277
+
2278
+ func toolStarted(call: FfiToolCall)
2279
+
2280
+ func toolFinished(toolCallId: String, content: String, isError: Bool)
2281
+
2282
+ func agentReplied(reply: FfiAgentReply)
2283
+
2284
+ func warning(message: String)
2285
+
2286
+}
2287
+
2288
+
2289
+// Put the implementation in a struct so we don't pollute the top-level namespace
2290
+fileprivate struct UniffiCallbackInterfaceFfiEventListener {
2291
+
2292
+ // Create the VTable using a series of closures.
2293
+ // Swift automatically converts these into C callback functions.
2294
+ //
2295
+ // This creates 1-element array, since this seems to be the only way to construct a const
2296
+ // pointer that we can pass to the Rust code.
2297
+ static let vtable: [UniffiVTableCallbackInterfaceFfiEventListener] = [UniffiVTableCallbackInterfaceFfiEventListener(
2298
+ uniffiFree: { (uniffiHandle: UInt64) -> () in
2299
+ do {
2300
+ try FfiConverterCallbackInterfaceFfiEventListener.handleMap.remove(handle: uniffiHandle)
2301
+ } catch {
2302
+ print("Uniffi callback interface FfiEventListener: handle missing in uniffiFree")
2303
+ }
2304
+ },
2305
+ uniffiClone: { (uniffiHandle: UInt64) -> UInt64 in
2306
+ do {
2307
+ return try FfiConverterCallbackInterfaceFfiEventListener.handleMap.clone(handle: uniffiHandle)
2308
+ } catch {
2309
+ fatalError("Uniffi callback interface FfiEventListener: handle missing in uniffiClone")
2310
+ }
2311
+ },
2312
+ statusChanged: { (
2313
+ uniffiHandle: UInt64,
2314
+ update: RustBuffer,
2315
+ uniffiOutReturn: UnsafeMutableRawPointer,
2316
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2317
+ ) in
2318
+ let makeCall = {
2319
+ () throws -> () in
2320
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2321
+ throw UniffiInternalError.unexpectedStaleHandle
2322
+ }
2323
+ return uniffiObj.statusChanged(
2324
+ update: try FfiConverterTypeFfiStatusUpdate_lift(update)
2325
+ )
2326
+ }
2327
+
2328
+
2329
+ let writeReturn = { () }
2330
+ uniffiTraitInterfaceCall(
2331
+ callStatus: uniffiCallStatus,
2332
+ makeCall: makeCall,
2333
+ writeReturn: writeReturn
2334
+ )
2335
+ },
2336
+ toolRequested: { (
2337
+ uniffiHandle: UInt64,
2338
+ call: RustBuffer,
2339
+ uniffiOutReturn: UnsafeMutableRawPointer,
2340
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2341
+ ) in
2342
+ let makeCall = {
2343
+ () throws -> () in
2344
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2345
+ throw UniffiInternalError.unexpectedStaleHandle
2346
+ }
2347
+ return uniffiObj.toolRequested(
2348
+ call: try FfiConverterTypeFfiToolCall_lift(call)
2349
+ )
2350
+ }
2351
+
2352
+
2353
+ let writeReturn = { () }
2354
+ uniffiTraitInterfaceCall(
2355
+ callStatus: uniffiCallStatus,
2356
+ makeCall: makeCall,
2357
+ writeReturn: writeReturn
2358
+ )
2359
+ },
2360
+ approvalRequested: { (
2361
+ uniffiHandle: UInt64,
2362
+ call: RustBuffer,
2363
+ uniffiOutReturn: UnsafeMutableRawPointer,
2364
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2365
+ ) in
2366
+ let makeCall = {
2367
+ () throws -> () in
2368
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2369
+ throw UniffiInternalError.unexpectedStaleHandle
2370
+ }
2371
+ return uniffiObj.approvalRequested(
2372
+ call: try FfiConverterTypeFfiToolCall_lift(call)
2373
+ )
2374
+ }
2375
+
2376
+
2377
+ let writeReturn = { () }
2378
+ uniffiTraitInterfaceCall(
2379
+ callStatus: uniffiCallStatus,
2380
+ makeCall: makeCall,
2381
+ writeReturn: writeReturn
2382
+ )
2383
+ },
2384
+ toolStarted: { (
2385
+ uniffiHandle: UInt64,
2386
+ call: RustBuffer,
2387
+ uniffiOutReturn: UnsafeMutableRawPointer,
2388
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2389
+ ) in
2390
+ let makeCall = {
2391
+ () throws -> () in
2392
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2393
+ throw UniffiInternalError.unexpectedStaleHandle
2394
+ }
2395
+ return uniffiObj.toolStarted(
2396
+ call: try FfiConverterTypeFfiToolCall_lift(call)
2397
+ )
2398
+ }
2399
+
2400
+
2401
+ let writeReturn = { () }
2402
+ uniffiTraitInterfaceCall(
2403
+ callStatus: uniffiCallStatus,
2404
+ makeCall: makeCall,
2405
+ writeReturn: writeReturn
2406
+ )
2407
+ },
2408
+ toolFinished: { (
2409
+ uniffiHandle: UInt64,
2410
+ toolCallId: RustBuffer,
2411
+ content: RustBuffer,
2412
+ isError: Int8,
2413
+ uniffiOutReturn: UnsafeMutableRawPointer,
2414
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2415
+ ) in
2416
+ let makeCall = {
2417
+ () throws -> () in
2418
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2419
+ throw UniffiInternalError.unexpectedStaleHandle
2420
+ }
2421
+ return uniffiObj.toolFinished(
2422
+ toolCallId: try FfiConverterString.lift(toolCallId),
2423
+ content: try FfiConverterString.lift(content),
2424
+ isError: try FfiConverterBool.lift(isError)
2425
+ )
2426
+ }
2427
+
2428
+
2429
+ let writeReturn = { () }
2430
+ uniffiTraitInterfaceCall(
2431
+ callStatus: uniffiCallStatus,
2432
+ makeCall: makeCall,
2433
+ writeReturn: writeReturn
2434
+ )
2435
+ },
2436
+ agentReplied: { (
2437
+ uniffiHandle: UInt64,
2438
+ reply: RustBuffer,
2439
+ uniffiOutReturn: UnsafeMutableRawPointer,
2440
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2441
+ ) in
2442
+ let makeCall = {
2443
+ () throws -> () in
2444
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2445
+ throw UniffiInternalError.unexpectedStaleHandle
2446
+ }
2447
+ return uniffiObj.agentReplied(
2448
+ reply: try FfiConverterTypeFfiAgentReply_lift(reply)
2449
+ )
2450
+ }
2451
+
2452
+
2453
+ let writeReturn = { () }
2454
+ uniffiTraitInterfaceCall(
2455
+ callStatus: uniffiCallStatus,
2456
+ makeCall: makeCall,
2457
+ writeReturn: writeReturn
2458
+ )
2459
+ },
2460
+ warning: { (
2461
+ uniffiHandle: UInt64,
2462
+ message: RustBuffer,
2463
+ uniffiOutReturn: UnsafeMutableRawPointer,
2464
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
2465
+ ) in
2466
+ let makeCall = {
2467
+ () throws -> () in
2468
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiEventListener.handleMap.get(handle: uniffiHandle) else {
2469
+ throw UniffiInternalError.unexpectedStaleHandle
2470
+ }
2471
+ return uniffiObj.warning(
2472
+ message: try FfiConverterString.lift(message)
2473
+ )
2474
+ }
2475
+
2476
+
2477
+ let writeReturn = { () }
2478
+ uniffiTraitInterfaceCall(
2479
+ callStatus: uniffiCallStatus,
2480
+ makeCall: makeCall,
2481
+ writeReturn: writeReturn
2482
+ )
2483
+ }
2484
+ )]
2485
+}
2486
+
2487
+private func uniffiCallbackInitFfiEventListener() {
2488
+ uniffi_ed_agent_ffi_fn_init_callback_vtable_ffieventlistener(UniffiCallbackInterfaceFfiEventListener.vtable)
2489
+}
2490
+
2491
+// FfiConverter protocol for callback interfaces
2492
+#if swift(>=5.8)
2493
+@_documentation(visibility: private)
2494
+#endif
2495
+fileprivate struct FfiConverterCallbackInterfaceFfiEventListener {
2496
+ fileprivate static let handleMap = UniffiHandleMap<FfiEventListener>()
2497
+}
2498
+
2499
+#if swift(>=5.8)
2500
+@_documentation(visibility: private)
2501
+#endif
2502
+extension FfiConverterCallbackInterfaceFfiEventListener : FfiConverter {
2503
+ typealias SwiftType = FfiEventListener
2504
+ typealias FfiType = UInt64
2505
+
2506
+#if swift(>=5.8)
2507
+ @_documentation(visibility: private)
2508
+#endif
2509
+ public static func lift(_ handle: UInt64) throws -> SwiftType {
2510
+ try handleMap.get(handle: handle)
2511
+ }
2512
+
2513
+#if swift(>=5.8)
2514
+ @_documentation(visibility: private)
2515
+#endif
2516
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2517
+ let handle: UInt64 = try readInt(&buf)
2518
+ return try lift(handle)
2519
+ }
2520
+
2521
+#if swift(>=5.8)
2522
+ @_documentation(visibility: private)
2523
+#endif
2524
+ public static func lower(_ v: SwiftType) -> UInt64 {
2525
+ return handleMap.insert(obj: v)
2526
+ }
2527
+
2528
+#if swift(>=5.8)
2529
+ @_documentation(visibility: private)
2530
+#endif
2531
+ public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
2532
+ writeInt(&buf, lower(v))
2533
+ }
2534
+}
2535
+
2536
+
2537
+#if swift(>=5.8)
2538
+@_documentation(visibility: private)
2539
+#endif
2540
+public func FfiConverterCallbackInterfaceFfiEventListener_lift(_ handle: UInt64) throws -> FfiEventListener {
2541
+ return try FfiConverterCallbackInterfaceFfiEventListener.lift(handle)
2542
+}
2543
+
2544
+#if swift(>=5.8)
2545
+@_documentation(visibility: private)
2546
+#endif
2547
+public func FfiConverterCallbackInterfaceFfiEventListener_lower(_ v: FfiEventListener) -> UInt64 {
2548
+ return FfiConverterCallbackInterfaceFfiEventListener.lower(v)
2549
+}
2550
+
2551
+
2552
+
2553
+
2554
+public protocol FfiToolExecutor: AnyObject, Sendable {
2555
+
2556
+ func execute(toolName: String, arguments: String) async -> FfiToolOutput
2557
+
2558
+}
2559
+
2560
+
2561
+// Put the implementation in a struct so we don't pollute the top-level namespace
2562
+fileprivate struct UniffiCallbackInterfaceFfiToolExecutor {
2563
+
2564
+ // Create the VTable using a series of closures.
2565
+ // Swift automatically converts these into C callback functions.
2566
+ //
2567
+ // This creates 1-element array, since this seems to be the only way to construct a const
2568
+ // pointer that we can pass to the Rust code.
2569
+ static let vtable: [UniffiVTableCallbackInterfaceFfiToolExecutor] = [UniffiVTableCallbackInterfaceFfiToolExecutor(
2570
+ uniffiFree: { (uniffiHandle: UInt64) -> () in
2571
+ do {
2572
+ try FfiConverterCallbackInterfaceFfiToolExecutor.handleMap.remove(handle: uniffiHandle)
2573
+ } catch {
2574
+ print("Uniffi callback interface FfiToolExecutor: handle missing in uniffiFree")
2575
+ }
2576
+ },
2577
+ uniffiClone: { (uniffiHandle: UInt64) -> UInt64 in
2578
+ do {
2579
+ return try FfiConverterCallbackInterfaceFfiToolExecutor.handleMap.clone(handle: uniffiHandle)
2580
+ } catch {
2581
+ fatalError("Uniffi callback interface FfiToolExecutor: handle missing in uniffiClone")
2582
+ }
2583
+ },
2584
+ execute: { (
2585
+ uniffiHandle: UInt64,
2586
+ toolName: RustBuffer,
2587
+ arguments: RustBuffer,
2588
+ uniffiFutureCallback: @escaping UniffiForeignFutureCompleteRustBuffer,
2589
+ uniffiCallbackData: UInt64,
2590
+ uniffiOutDroppedCallback: UnsafeMutablePointer<UniffiForeignFutureDroppedCallbackStruct>
2591
+ ) in
2592
+ let makeCall = {
2593
+ () async throws -> FfiToolOutput in
2594
+ guard let uniffiObj = try? FfiConverterCallbackInterfaceFfiToolExecutor.handleMap.get(handle: uniffiHandle) else {
2595
+ throw UniffiInternalError.unexpectedStaleHandle
2596
+ }
2597
+ return await uniffiObj.execute(
2598
+ toolName: try FfiConverterString.lift(toolName),
2599
+ arguments: try FfiConverterString.lift(arguments)
2600
+ )
2601
+ }
2602
+
2603
+ let uniffiHandleSuccess = { (returnValue: FfiToolOutput) in
2604
+ uniffiFutureCallback(
2605
+ uniffiCallbackData,
2606
+ UniffiForeignFutureResultRustBuffer(
2607
+ returnValue: FfiConverterTypeFfiToolOutput_lower(returnValue),
2608
+ callStatus: RustCallStatus()
2609
+ )
2610
+ )
2611
+ }
2612
+ let uniffiHandleError = { (statusCode, errorBuf) in
2613
+ uniffiFutureCallback(
2614
+ uniffiCallbackData,
2615
+ UniffiForeignFutureResultRustBuffer(
2616
+ returnValue: RustBuffer.empty(),
2617
+ callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf)
2618
+ )
2619
+ )
2620
+ }
2621
+ uniffiTraitInterfaceCallAsync(
2622
+ makeCall: makeCall,
2623
+ handleSuccess: uniffiHandleSuccess,
2624
+ handleError: uniffiHandleError,
2625
+ droppedCallback: uniffiOutDroppedCallback
2626
+ )
2627
+ }
2628
+ )]
2629
+}
2630
+
2631
+private func uniffiCallbackInitFfiToolExecutor() {
2632
+ uniffi_ed_agent_ffi_fn_init_callback_vtable_ffitoolexecutor(UniffiCallbackInterfaceFfiToolExecutor.vtable)
2633
+}
2634
+
2635
+// FfiConverter protocol for callback interfaces
2636
+#if swift(>=5.8)
2637
+@_documentation(visibility: private)
2638
+#endif
2639
+fileprivate struct FfiConverterCallbackInterfaceFfiToolExecutor {
2640
+ fileprivate static let handleMap = UniffiHandleMap<FfiToolExecutor>()
2641
+}
2642
+
2643
+#if swift(>=5.8)
2644
+@_documentation(visibility: private)
2645
+#endif
2646
+extension FfiConverterCallbackInterfaceFfiToolExecutor : FfiConverter {
2647
+ typealias SwiftType = FfiToolExecutor
2648
+ typealias FfiType = UInt64
2649
+
2650
+#if swift(>=5.8)
2651
+ @_documentation(visibility: private)
2652
+#endif
2653
+ public static func lift(_ handle: UInt64) throws -> SwiftType {
2654
+ try handleMap.get(handle: handle)
2655
+ }
2656
+
2657
+#if swift(>=5.8)
2658
+ @_documentation(visibility: private)
2659
+#endif
2660
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2661
+ let handle: UInt64 = try readInt(&buf)
2662
+ return try lift(handle)
2663
+ }
2664
+
2665
+#if swift(>=5.8)
2666
+ @_documentation(visibility: private)
2667
+#endif
2668
+ public static func lower(_ v: SwiftType) -> UInt64 {
2669
+ return handleMap.insert(obj: v)
2670
+ }
2671
+
2672
+#if swift(>=5.8)
2673
+ @_documentation(visibility: private)
2674
+#endif
2675
+ public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
2676
+ writeInt(&buf, lower(v))
2677
+ }
2678
+}
2679
+
2680
+
2681
+#if swift(>=5.8)
2682
+@_documentation(visibility: private)
2683
+#endif
2684
+public func FfiConverterCallbackInterfaceFfiToolExecutor_lift(_ handle: UInt64) throws -> FfiToolExecutor {
2685
+ return try FfiConverterCallbackInterfaceFfiToolExecutor.lift(handle)
2686
+}
2687
+
2688
+#if swift(>=5.8)
2689
+@_documentation(visibility: private)
2690
+#endif
2691
+public func FfiConverterCallbackInterfaceFfiToolExecutor_lower(_ v: FfiToolExecutor) -> UInt64 {
2692
+ return FfiConverterCallbackInterfaceFfiToolExecutor.lower(v)
2693
+}
2694
+
2695
+#if swift(>=5.8)
2696
+@_documentation(visibility: private)
2697
+#endif
2698
+fileprivate struct FfiConverterOptionUInt64: FfiConverterRustBuffer {
2699
+ typealias SwiftType = UInt64?
2700
+
2701
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2702
+ guard let value = value else {
2703
+ writeInt(&buf, Int8(0))
2704
+ return
2705
+ }
2706
+ writeInt(&buf, Int8(1))
2707
+ FfiConverterUInt64.write(value, into: &buf)
2708
+ }
2709
+
2710
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2711
+ switch try readInt(&buf) as Int8 {
2712
+ case 0: return nil
2713
+ case 1: return try FfiConverterUInt64.read(from: &buf)
2714
+ default: throw UniffiInternalError.unexpectedOptionalTag
2715
+ }
2716
+ }
2717
+}
2718
+
2719
+#if swift(>=5.8)
2720
+@_documentation(visibility: private)
2721
+#endif
2722
+fileprivate struct FfiConverterOptionFloat: FfiConverterRustBuffer {
2723
+ typealias SwiftType = Float?
2724
+
2725
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2726
+ guard let value = value else {
2727
+ writeInt(&buf, Int8(0))
2728
+ return
2729
+ }
2730
+ writeInt(&buf, Int8(1))
2731
+ FfiConverterFloat.write(value, into: &buf)
2732
+ }
2733
+
2734
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2735
+ switch try readInt(&buf) as Int8 {
2736
+ case 0: return nil
2737
+ case 1: return try FfiConverterFloat.read(from: &buf)
2738
+ default: throw UniffiInternalError.unexpectedOptionalTag
2739
+ }
2740
+ }
2741
+}
2742
+
2743
+#if swift(>=5.8)
2744
+@_documentation(visibility: private)
2745
+#endif
2746
+fileprivate struct FfiConverterOptionDouble: FfiConverterRustBuffer {
2747
+ typealias SwiftType = Double?
2748
+
2749
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2750
+ guard let value = value else {
2751
+ writeInt(&buf, Int8(0))
2752
+ return
2753
+ }
2754
+ writeInt(&buf, Int8(1))
2755
+ FfiConverterDouble.write(value, into: &buf)
2756
+ }
2757
+
2758
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2759
+ switch try readInt(&buf) as Int8 {
2760
+ case 0: return nil
2761
+ case 1: return try FfiConverterDouble.read(from: &buf)
2762
+ default: throw UniffiInternalError.unexpectedOptionalTag
2763
+ }
2764
+ }
2765
+}
2766
+
2767
+#if swift(>=5.8)
2768
+@_documentation(visibility: private)
2769
+#endif
2770
+fileprivate struct FfiConverterOptionString: FfiConverterRustBuffer {
2771
+ typealias SwiftType = String?
2772
+
2773
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2774
+ guard let value = value else {
2775
+ writeInt(&buf, Int8(0))
2776
+ return
2777
+ }
2778
+ writeInt(&buf, Int8(1))
2779
+ FfiConverterString.write(value, into: &buf)
2780
+ }
2781
+
2782
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2783
+ switch try readInt(&buf) as Int8 {
2784
+ case 0: return nil
2785
+ case 1: return try FfiConverterString.read(from: &buf)
2786
+ default: throw UniffiInternalError.unexpectedOptionalTag
2787
+ }
2788
+ }
2789
+}
2790
+
2791
+#if swift(>=5.8)
2792
+@_documentation(visibility: private)
2793
+#endif
2794
+fileprivate struct FfiConverterOptionTypeFfiAgentConfig: FfiConverterRustBuffer {
2795
+ typealias SwiftType = FfiAgentConfig?
2796
+
2797
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2798
+ guard let value = value else {
2799
+ writeInt(&buf, Int8(0))
2800
+ return
2801
+ }
2802
+ writeInt(&buf, Int8(1))
2803
+ FfiConverterTypeFfiAgentConfig.write(value, into: &buf)
2804
+ }
2805
+
2806
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2807
+ switch try readInt(&buf) as Int8 {
2808
+ case 0: return nil
2809
+ case 1: return try FfiConverterTypeFfiAgentConfig.read(from: &buf)
2810
+ default: throw UniffiInternalError.unexpectedOptionalTag
2811
+ }
2812
+ }
2813
+}
2814
+
2815
+#if swift(>=5.8)
2816
+@_documentation(visibility: private)
2817
+#endif
2818
+fileprivate struct FfiConverterOptionTypeFfiSamplingConfig: FfiConverterRustBuffer {
2819
+ typealias SwiftType = FfiSamplingConfig?
2820
+
2821
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
2822
+ guard let value = value else {
2823
+ writeInt(&buf, Int8(0))
2824
+ return
2825
+ }
2826
+ writeInt(&buf, Int8(1))
2827
+ FfiConverterTypeFfiSamplingConfig.write(value, into: &buf)
2828
+ }
2829
+
2830
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
2831
+ switch try readInt(&buf) as Int8 {
2832
+ case 0: return nil
2833
+ case 1: return try FfiConverterTypeFfiSamplingConfig.read(from: &buf)
2834
+ default: throw UniffiInternalError.unexpectedOptionalTag
2835
+ }
2836
+ }
2837
+}
2838
+
2839
+#if swift(>=5.8)
2840
+@_documentation(visibility: private)
2841
+#endif
2842
+fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer {
2843
+ typealias SwiftType = [String]
2844
+
2845
+ public static func write(_ value: [String], into buf: inout [UInt8]) {
2846
+ let len = Int32(value.count)
2847
+ writeInt(&buf, len)
2848
+ for item in value {
2849
+ FfiConverterString.write(item, into: &buf)
2850
+ }
2851
+ }
2852
+
2853
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] {
2854
+ let len: Int32 = try readInt(&buf)
2855
+ var seq = [String]()
2856
+ seq.reserveCapacity(Int(len))
2857
+ for _ in 0 ..< len {
2858
+ seq.append(try FfiConverterString.read(from: &buf))
2859
+ }
2860
+ return seq
2861
+ }
2862
+}
2863
+
2864
+#if swift(>=5.8)
2865
+@_documentation(visibility: private)
2866
+#endif
2867
+fileprivate struct FfiConverterSequenceTypeFfiChatMessage: FfiConverterRustBuffer {
2868
+ typealias SwiftType = [FfiChatMessage]
2869
+
2870
+ public static func write(_ value: [FfiChatMessage], into buf: inout [UInt8]) {
2871
+ let len = Int32(value.count)
2872
+ writeInt(&buf, len)
2873
+ for item in value {
2874
+ FfiConverterTypeFfiChatMessage.write(item, into: &buf)
2875
+ }
2876
+ }
2877
+
2878
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [FfiChatMessage] {
2879
+ let len: Int32 = try readInt(&buf)
2880
+ var seq = [FfiChatMessage]()
2881
+ seq.reserveCapacity(Int(len))
2882
+ for _ in 0 ..< len {
2883
+ seq.append(try FfiConverterTypeFfiChatMessage.read(from: &buf))
2884
+ }
2885
+ return seq
2886
+ }
2887
+}
2888
+private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0
2889
+private let UNIFFI_RUST_FUTURE_POLL_WAKE: Int8 = 1
2890
+
2891
+fileprivate let uniffiContinuationHandleMap = UniffiHandleMap<UnsafeContinuation<Int8, Never>>()
2892
+
2893
+fileprivate func uniffiRustCallAsync<F, T>(
2894
+ rustFutureFunc: () -> UInt64,
2895
+ pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (),
2896
+ completeFunc: (UInt64, UnsafeMutablePointer<RustCallStatus>) -> F,
2897
+ freeFunc: (UInt64) -> (),
2898
+ liftFunc: (F) throws -> T,
2899
+ errorHandler: ((RustBuffer) throws -> Swift.Error)?
2900
+) async throws -> T {
2901
+ // Make sure to call the ensure init function since future creation doesn't have a
2902
+ // RustCallStatus param, so doesn't use makeRustCall()
2903
+ uniffiEnsureEdAgentFfiInitialized()
2904
+ let rustFuture = rustFutureFunc()
2905
+ defer {
2906
+ freeFunc(rustFuture)
2907
+ }
2908
+ var pollResult: Int8;
2909
+ repeat {
2910
+ pollResult = await withUnsafeContinuation {
2911
+ pollFunc(
2912
+ rustFuture,
2913
+ { handle, pollResult in
2914
+ uniffiFutureContinuationCallback(handle: handle, pollResult: pollResult)
2915
+ },
2916
+ uniffiContinuationHandleMap.insert(obj: $0)
2917
+ )
2918
+ }
2919
+ } while pollResult != UNIFFI_RUST_FUTURE_POLL_READY
2920
+
2921
+ return try liftFunc(makeRustCall(
2922
+ { completeFunc(rustFuture, $0) },
2923
+ errorHandler: errorHandler
2924
+ ))
2925
+}
2926
+
2927
+// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They
2928
+// lift the return value or error and resume the suspended function.
2929
+fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) {
2930
+ if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) {
2931
+ continuation.resume(returning: pollResult)
2932
+ } else {
2933
+ print("uniffiFutureContinuationCallback invalid handle")
2934
+ }
2935
+}
2936
+private func uniffiTraitInterfaceCallAsync<T>(
2937
+ makeCall: @escaping () async throws -> T,
2938
+ handleSuccess: @escaping (T) -> (),
2939
+ handleError: @escaping (Int8, RustBuffer) -> (),
2940
+ droppedCallback: UnsafeMutablePointer<UniffiForeignFutureDroppedCallbackStruct>
2941
+) {
2942
+ let task = Task {
2943
+ // Note: it's important we call either `handleSuccess` or `handleError` exactly once. Each
2944
+ // call consumes an Arc reference, which means there should be no possibility of a double
2945
+ // call. The following code is structured so that will will never call both `handleSuccess`
2946
+ // and `handleError`, even in the face of weird errors.
2947
+ //
2948
+ // On platforms that need extra machinery to make C-ABI calls, like JNA or ctypes, it's
2949
+ // possible that we fail to make either call. However, it doesn't seem like this is
2950
+ // possible on Swift since swift can just make the C call directly.
2951
+ var callResult: T
2952
+ do {
2953
+ callResult = try await makeCall()
2954
+ } catch {
2955
+ handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error)))
2956
+ return
2957
+ }
2958
+ handleSuccess(callResult)
2959
+ }
2960
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
2961
+ droppedCallback.pointee = UniffiForeignFutureDroppedCallbackStruct(
2962
+ handle: handle,
2963
+ free: uniffiForeignFutureDroppedCallback
2964
+ )
2965
+}
2966
+
2967
+private func uniffiTraitInterfaceCallAsyncWithError<T, E>(
2968
+ makeCall: @escaping () async throws -> T,
2969
+ handleSuccess: @escaping (T) -> (),
2970
+ handleError: @escaping (Int8, RustBuffer) -> (),
2971
+ lowerError: @escaping (E) -> RustBuffer,
2972
+ droppedCallback: UnsafeMutablePointer<UniffiForeignFutureDroppedCallbackStruct>
2973
+) {
2974
+ let task = Task {
2975
+ // See the note in uniffiTraitInterfaceCallAsync for details on `handleSuccess` and
2976
+ // `handleError`.
2977
+ var callResult: T
2978
+ do {
2979
+ callResult = try await makeCall()
2980
+ } catch let error as E {
2981
+ handleError(CALL_ERROR, lowerError(error))
2982
+ return
2983
+ } catch {
2984
+ handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error)))
2985
+ return
2986
+ }
2987
+ handleSuccess(callResult)
2988
+ }
2989
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
2990
+ droppedCallback.pointee = UniffiForeignFutureDroppedCallbackStruct(
2991
+ handle: handle,
2992
+ free: uniffiForeignFutureDroppedCallback
2993
+ )
2994
+}
2995
+
2996
+// Borrow the callback handle map implementation to store foreign future handles
2997
+// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823)
2998
+fileprivate let UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap<UniffiForeignFutureTask>()
2999
+
3000
+// Protocol for tasks that handle foreign futures.
3001
+//
3002
+// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done
3003
+// with the task object itself, since has generic parameters.
3004
+fileprivate protocol UniffiForeignFutureTask {
3005
+ func cancel()
3006
+}
3007
+
3008
+extension Task: UniffiForeignFutureTask {}
3009
+
3010
+private func uniffiForeignFutureDroppedCallback(handle: UInt64) {
3011
+ do {
3012
+ let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle)
3013
+ // Set the cancellation flag on the task. If it's still running, the code can check the
3014
+ // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is
3015
+ // a no-op.
3016
+ task.cancel()
3017
+ } catch {
3018
+ print("uniffiForeignFutureDroppedCallback: handle missing from handlemap")
3019
+ }
3020
+}
3021
+
3022
+// For testing
3023
+public func uniffiForeignFutureHandleCountEdAgentFfi() -> Int {
3024
+ UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count
3025
+}
3026
+public func configureCacheDir(path: String) {try! rustCall() {
3027
+ uniffi_ed_agent_ffi_fn_func_configure_cache_dir(
3028
+ FfiConverterString.lower(path),$0
3029
+ )
3030
+}
3031
+}
3032
+public func defaultAgentModelConfig() -> FfiGgufModelConfig {
3033
+ return try! FfiConverterTypeFfiGgufModelConfig_lift(try! rustCall() {
3034
+ uniffi_ed_agent_ffi_fn_func_default_agent_model_config($0
3035
+ )
3036
+})
3037
+}
3038
+
3039
+private enum InitializationResult {
3040
+ case ok
3041
+ case contractVersionMismatch
3042
+ case apiChecksumMismatch
3043
+}
3044
+// Use a global variable to perform the versioning checks. Swift ensures that
3045
+// the code inside is only computed once.
3046
+private let initializationResult: InitializationResult = {
3047
+ // Get the bindings contract version from our ComponentInterface
3048
+ let bindings_contract_version = 30
3049
+ // Get the scaffolding contract version by calling the into the dylib
3050
+ let scaffolding_contract_version = ffi_ed_agent_ffi_uniffi_contract_version()
3051
+ if bindings_contract_version != scaffolding_contract_version {
3052
+ return InitializationResult.contractVersionMismatch
3053
+ }
3054
+ if (uniffi_ed_agent_ffi_checksum_func_configure_cache_dir() != 30620) {
3055
+ return InitializationResult.apiChecksumMismatch
3056
+ }
3057
+ if (uniffi_ed_agent_ffi_checksum_func_default_agent_model_config() != 44321) {
3058
+ return InitializationResult.apiChecksumMismatch
3059
+ }
3060
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_cancel() != 42085) {
3061
+ return InitializationResult.apiChecksumMismatch
3062
+ }
3063
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_clear_history() != 19615) {
3064
+ return InitializationResult.apiChecksumMismatch
3065
+ }
3066
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_history() != 36014) {
3067
+ return InitializationResult.apiChecksumMismatch
3068
+ }
3069
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_info() != 9944) {
3070
+ return InitializationResult.apiChecksumMismatch
3071
+ }
3072
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_is_loaded() != 28871) {
3073
+ return InitializationResult.apiChecksumMismatch
3074
+ }
3075
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_load_assigned_model() != 60966) {
3076
+ return InitializationResult.apiChecksumMismatch
3077
+ }
3078
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_load_default_agent_model() != 47518) {
3079
+ return InitializationResult.apiChecksumMismatch
3080
+ }
3081
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_load_gguf_model() != 17854) {
3082
+ return InitializationResult.apiChecksumMismatch
3083
+ }
3084
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_load_uqff_model() != 11023) {
3085
+ return InitializationResult.apiChecksumMismatch
3086
+ }
3087
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_register_tool() != 34863) {
3088
+ return InitializationResult.apiChecksumMismatch
3089
+ }
3090
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_remove_all_tools() != 36535) {
3091
+ return InitializationResult.apiChecksumMismatch
3092
+ }
3093
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_run() != 39233) {
3094
+ return InitializationResult.apiChecksumMismatch
3095
+ }
3096
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_send() != 839) {
3097
+ return InitializationResult.apiChecksumMismatch
3098
+ }
3099
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_unload() != 29612) {
3100
+ return InitializationResult.apiChecksumMismatch
3101
+ }
3102
+ if (uniffi_ed_agent_ffi_checksum_method_ffiedagent_unregister_tool() != 63973) {
3103
+ return InitializationResult.apiChecksumMismatch
3104
+ }
3105
+ if (uniffi_ed_agent_ffi_checksum_constructor_ffiedagent_new() != 19584) {
3106
+ return InitializationResult.apiChecksumMismatch
3107
+ }
3108
+ if (uniffi_ed_agent_ffi_checksum_method_ffiapprovalhandler_approve() != 12591) {
3109
+ return InitializationResult.apiChecksumMismatch
3110
+ }
3111
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_status_changed() != 50680) {
3112
+ return InitializationResult.apiChecksumMismatch
3113
+ }
3114
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_tool_requested() != 58928) {
3115
+ return InitializationResult.apiChecksumMismatch
3116
+ }
3117
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_approval_requested() != 54477) {
3118
+ return InitializationResult.apiChecksumMismatch
3119
+ }
3120
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_tool_started() != 23754) {
3121
+ return InitializationResult.apiChecksumMismatch
3122
+ }
3123
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_tool_finished() != 4350) {
3124
+ return InitializationResult.apiChecksumMismatch
3125
+ }
3126
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_agent_replied() != 33842) {
3127
+ return InitializationResult.apiChecksumMismatch
3128
+ }
3129
+ if (uniffi_ed_agent_ffi_checksum_method_ffieventlistener_warning() != 43170) {
3130
+ return InitializationResult.apiChecksumMismatch
3131
+ }
3132
+ if (uniffi_ed_agent_ffi_checksum_method_ffitoolexecutor_execute() != 36465) {
3133
+ return InitializationResult.apiChecksumMismatch
3134
+ }
3135
+
3136
+ uniffiCallbackInitFfiApprovalHandler()
3137
+ uniffiCallbackInitFfiEventListener()
3138
+ uniffiCallbackInitFfiToolExecutor()
3139
+ return InitializationResult.ok
3140
+}()
3141
+
3142
+// Make the ensure init function public so that other modules which have external type references to
3143
+// our types can call it.
3144
+public func uniffiEnsureEdAgentFfiInitialized() {
3145
+ switch initializationResult {
3146
+ case .ok:
3147
+ break
3148
+ case .contractVersionMismatch:
3149
+ fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
3150
+ case .apiChecksumMismatch:
3151
+ fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
3152
+ }
3153
+}
3154
+
3155
+// swiftlint:enable all
\ No newline at end of file
Tests/EdTests/EdPublicTypesTests.swift
new
+19
@@ -0,0 +1,19 @@
1
+import Testing
2
+@testable import Ed
3
+
4
+@Test func readOnlyDefinitionKeepsItsSchema() {
5
+ let definition = EdToolDefinition.readOnly(
6
+ name: "current_battery",
7
+ description: "Read the current battery percentage",
8
+ parametersSchema: #"{"type":"object","properties":{}}"#
9
+ )
10
+
11
+ #expect(definition.name == "current_battery")
12
+ #expect(definition.parametersSchema.contains("properties"))
13
+}
14
+
15
+@Test func configurationHasSafeDefaults() {
16
+ let configuration = EdAgentConfiguration()
17
+ #expect(configuration.maxToolRounds == 8)
18
+ #expect(configuration.maxToolOutputCharacters == 10_000)
19
+}
readme.md
deleted
-1
@@ -1 +0,0 @@
1
-# Ed Swift