Managoat is the hosted Fountain. Fountain is the open-source engine, and its name is on the CLI, the API, the SDK and this manual. Everything here applies to Managoat unless a page says it is for a self-hosted server.
Swift SDK
The Swift SDK turns the conversation API and its event feed into one async job: start an agent, follow its turn and return the answer.
import Fountain
let fountain = try Fountain()
let run = fountain.run(
"Upgrade us to Phoenix 1.8 and open a PR",
agent: "reposage",
vault: "github-bot"
)
let result = try await run.value()
print(result.text)
print(result.url)
The vault value reaches the sandbox as an environment variable. It does not enter the prompt or the event feed that the SDK reads.
The package ships a second client, FountainKit, for applications: the same
API read into Swift types rather than JSON. Use Fountain to script a run,
and FountainKit to build against the API. They do not share types, and an
app has no reason to use both. Everything below is Fountain; the typed
client has its own section.
Install
Add Fountain as a package dependency:
dependencies: [
.package(
url: "https://github.com/BinaryBourbon/fountain.git",
from: "0.16.0"
),
]
Add the library product to your target: Fountain to script a run, or
FountainKit to build an application (it needs a release later than 0.16.0):
.target(
name: "YourApp",
dependencies: [
.product(name: "Fountain", package: "fountain"),
]
)
After that release, replace the branch requirement with its version.
Credentials
Fountain() uses the same credentials as the CLI. It resolves them in this
order:
apiKey: argument -> FOUNTAIN_API_KEY -> FOUNTAIN_TOKEN -> saved CLI login
baseURL: argument -> FOUNTAIN_BASE_URL -> saved CLI login -> hosted Fountain
Pass the API key and the base URL directly to keep the CLI credentials file out of the process. The SDK reads that file only when an argument and the environment both miss:
let fountain = try Fountain(
apiKey: secret,
baseURL: "https://fountain.example.com"
)
A base URL must carry a scheme and a host. Fountain() throws a
FountainError for a value such as localhost:4000. It does not fall back to
the hosted Fountain, because that sends your API key to a different host.
FOUNTAIN_TOKEN is the delegated token inside a Fountain sandbox. Code that
runs there can use Fountain() to start child conversations without another
credential.
Wait for the result
run returns immediately with a Run handle. Await its value() when you
only need the final answer:
let run = fountain.run(prompt, agent: "reposage")
let result = try await run.value()
switch result.state {
case .done:
print(result.text)
case .failed, .interrupted, .timeout:
print(result.reason ?? "The turn did not finish.")
}
A failed agent turn is a result. A rejected request, a connection failure or
an SDK timeout throws FountainError.
Stream a turn
Use textStream when the caller only needs the answer as it arrives:
let run = fountain.run(prompt, agent: "reposage")
for try await chunk in run.textStream {
print(chunk, terminator: "")
}
let result = try await run.value()
Use events when the caller also needs tool use, permission requests and turn
state:
for try await event in run.events {
switch event {
case .text(let text):
print(text, terminator: "")
case .tool(let name, _):
print("\nTool: \(name)")
case .permission(let request, _):
print("\nPermission needed: \(request.summary ?? request.requestID)")
default:
break
}
}
Both streams belong to the same run. After a stream finishes, value()
returns the result already produced by that run.
Continue on the same sandbox
Keep the conversation ID. A follow-up resumes the same conversation, checkout and agent session:
let first = try await fountain
.run("Open a pull request", agent: "reposage")
.value()
let second = try await fountain
.resume(first.conversationID)
.send("Address the review comments")
.value()
print(second.text)
The typed client
FountainKit is the same API with the JSON resolved into types: Agent,
Conversation, LogEvent, Block, AuthMe, and a namespace per resource.
Errors are an enum you branch on by case and by server code. Every server
enum decodes values it does not know rather than throws. A runtime added
after you shipped does not crash your app.
import FountainKit
let client = FountainClient(config: FountainConfig(baseURL: url, apiKey: key))
for agent in try await client.agents.list() {
print(agent.name, agent.model)
}
let run = try await client.run("Review this repository", agent: agent.id)
for try await event in run.events {
if case .text(let chunk) = event {
print(chunk, terminator: "")
}
}
let result = try await run.value()
print(result.state.rawValue, result.toolsUsed)
run.events replays from the start for every subscriber, and follows the
turn once. Two views can watch one run without a second stream. A turn that
fails is a result with a state of failed, not a thrown error. Only
client-side failures throw.
It wraps parts of the API the untyped client does not: admin, the audit
trail, runners, API keys, apply, agent avatars and turn images. Use
client.request(_:_:) to reach what neither of them wraps.
For a worked example, swift-goat is a macOS app built on it.
The TypeScript SDK covers the same workflow in TypeScript. Use the API reference for endpoints that need no Swift wrapper.