Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Closes #

- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
- [ ] No third-party dependencies added to `Package.swift`
- [ ] No API keys, session IDs, or credentials in the diff
- [ ] No connector tokens, session IDs, or credentials in the diff
- [ ] `README.md` updated if public API or behavior changed
- [ ] SwiftUI and UIKit example ladders mirrored (if SDK behavior changed)
- [ ] `Version.swift` not touched (it is bumped only when cutting a release)
Expand Down
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ The agent joins and greets automatically. Full walkthrough: README **Step 1**.
rendering for you.
- **Suggestion pills** render under the last agent message and clear when the user sends
(see the example `ChatViewController` / `MessageBubbleView`).
- **Never log the API key.**
- **Never log the connector token.**

## Verifying changes

Expand All @@ -90,4 +90,4 @@ The agent joins and greets automatically. Full walkthrough: README **Step 1**.
- **Don't add third-party dependencies** — this package is intentionally dependency-free.
- When integrating into an app, **consume the public API**; don't edit
`Sources/PolyMessaging/` to make integration "easier."
- Keep credentials out of source — set the API key via `initialize(...)`.
- Keep credentials out of source — set the connector token via `initialize(...)`.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ signaling pipeline.
### Added
- **`PolyVoice.call(config:options:)`** → a `PolyCall` backed by a real WebRTC audio
engine (audio-only Opus, offer / answer / trickle ICE, mute). `VoiceOptions.webrtcToken`
is required — a distinct token from the API key.
is required — a distinct token from the connector token.
- **CallKit support** (`PolyVoice`): opt in with `VoiceOptions(callKit: true)` to run a
call as a system call. The SDK defers audio-session activation and the WebRTC audio
unit to CallKit; three new statics forward the `CXProviderDelegate` moments —
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ If you change the SDK, mirror the change across the SwiftUI and UIKit example la
- Public types and methods need a doc comment (`///`) describing **why** to use them, not just what they do.
- Don't introduce comments that simply restate the code. Only comment non-obvious invariants.
- The SDK is `@MainActor` where it touches `ChatSession` state — preserve those annotations.
- Never log API keys or session identifiers.
- Never log connector tokens or session identifiers.

## Commit conventions

Expand Down Expand Up @@ -101,5 +101,5 @@ For UI/example changes, open the relevant `Examples/<platform>/<NN-Name>` projec

- **No third-party dependencies.** This package is intentionally dependency-free.
- **Don't edit `Sources/PolyMessaging/` to make integration "easier"** — the public API is the contract; integration changes belong in the consuming app.
- **Keep credentials out of source.** API keys are set via `PolyMessaging.initialize(...)` at runtime, never committed.
- **Keep credentials out of source.** Connector tokens are set via `PolyMessaging.initialize(...)` at runtime, never committed.

2 changes: 1 addition & 1 deletion Examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ open Examples/SwiftUI/Chat/01-Hello/HelloSwiftUI.xcodeproj # or any other
# Cmd+R on an iPhone simulator
```

If you change `project.yml`, regenerate with `xcodegen` from inside that folder. Set your API key where the example calls `PolyMessaging.initialize(...)`.
If you change `project.yml`, regenerate with `xcodegen` from inside that folder. Set your connector token where the example calls `PolyMessaging.initialize(...)`.
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Chat/01-Hello/HelloApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct HelloApp: App {
// works anywhere in the app with no arguments.
// Replace with your connector token from Agent Studio before shipping.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
}
Expand Down
10 changes: 5 additions & 5 deletions Examples/SwiftUI/Chat/01-Hello/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ open HelloSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `HelloApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `HelloApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand All @@ -32,7 +32,7 @@ Configure the SDK once at launch:

```swift
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY" // from Agent Studio → Connector Settings
apiKey: "YOUR_CONNECTOR_TOKEN" // from Agent Studio → Connector Settings
// environment defaults to .us — add .uk / .euw / .cluster("dev") / .custom(...) only if needed
))
```
Expand All @@ -44,7 +44,7 @@ In an `@main` App:
struct HelloApp: App {
init() {
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
}
var body: some Scene { WindowGroup { ContentView() } }
Expand All @@ -53,7 +53,7 @@ struct HelloApp: App {

After this, `PolyMessaging.chat()` works from any view.

**Under the hood:** `initialize` just stashes your API key and environment process-wide — no network happens yet. The work starts when you call `chat()`.
**Under the hood:** `initialize` just stashes your connector token and environment process-wide — no network happens yet. The work starts when you call `chat()`.

*See [Quick start](../../../../README.md#quick-start).*

Expand Down Expand Up @@ -178,7 +178,7 @@ Sending stays available even while offline or reconnecting — gate only on `has

*See [Integration guide › The core pattern](../../../../README.md#the-core-pattern-render-messages-yourself).*

### Catch a bad API key — `ContentView.swift`
### Catch a bad connector token — `ContentView.swift`

Detect a terminal failure + offer retry:

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/02-Standard/App/StandardApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ struct StandardApp: App {
init() {
// Initialize once at app launch. After this, PolyMessaging.chat()
// works anywhere in the app with no arguments.
// Replace YOUR_API_KEY with your connector token from Agent Studio.
// Replace YOUR_CONNECTOR_TOKEN with your connector token from Agent Studio.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Chat/02-Standard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ open StandardSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `App/StandardApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `App/StandardApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/03-RichContent/App/RichContentApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ struct RichContentApp: App {
init() {
// Initialize once at app launch. After this, PolyMessaging.chat()
// works anywhere in the app with no arguments.
// Replace YOUR_API_KEY with your connector token from Agent Studio.
// Replace YOUR_CONNECTOR_TOKEN with your connector token from Agent Studio.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Chat/03-RichContent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ open RichContentSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `App/RichContentApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `App/RichContentApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/04-Resilience/App/ResilienceApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ struct ResilienceApp: App {
init() {
// Initialize once at app launch. After this, PolyMessaging.chat()
// works anywhere in the app with no arguments.
// Replace YOUR_API_KEY with your connector token from Agent Studio.
// Replace YOUR_CONNECTOR_TOKEN with your connector token from Agent Studio.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Chat/04-Resilience/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ open ResilienceSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `ResilienceApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `ResilienceApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/05-Handoff/App/HandoffApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ struct HandoffApp: App {
init() {
// Initialize once at app launch. After this, PolyMessaging.chat()
// works anywhere in the app with no arguments.
// Replace YOUR_API_KEY with your connector token from Agent Studio.
// Replace YOUR_CONNECTOR_TOKEN with your connector token from Agent Studio.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Chat/05-Handoff/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ open HandoffSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `HandoffApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `HandoffApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct FullReferenceApp: App {
// live. ContentView's resume / start-new flows use the no-arg facade
// (chat(), start(), hasResumableSession()), which reuse this config.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY",
apiKey: "YOUR_CONNECTOR_TOKEN",
environment: .us,
streamingEnabled: true,
logLevel: .error
Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/06-FullReference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ open FullReferenceSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `FullReferenceApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `FullReferenceApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand Down Expand Up @@ -98,7 +98,7 @@ struct ContentView: View {
}
```

**Under the hood:** `initialize` stashes the API key and environment process-wide — no network happens yet. The no-arg facade calls (`chat()`, `start()`, `hasResumableSession()`) reuse that config from any view. `chat()` resumes the persisted session when it's still valid (within the session timeout) and otherwise creates a fresh one; `start()` always discards. `hasResumableSession()` is a pure on-disk probe with no side effects, so it's safe to call on every render of the connect screen.
**Under the hood:** `initialize` stashes the connector token and environment process-wide — no network happens yet. The no-arg facade calls (`chat()`, `start()`, `hasResumableSession()`) reuse that config from any view. `chat()` resumes the persisted session when it's still valid (within the session timeout) and otherwise creates a fresh one; `start()` always discards. `hasResumableSession()` is a pure on-disk probe with no side effects, so it's safe to call on every render of the connect screen.

*See [Integration guide › Quick start](../../../../README.md#quick-start) and [Integration guide › Session lifecycle](../../../../README.md#session-lifecycle).*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct PlaygroundApp: App {
// Configuration from DevSettings on every connect, so this just primes a
// sane default.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY",
apiKey: "YOUR_CONNECTOR_TOKEN",
environment: .us
))
if CommandLine.arguments.contains("-uiTestFreshStart") { PolyMessaging.clearResumableSession() }
Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Chat/07-Playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ open PlaygroundSwiftUI.xcodeproj # from this folder
# Cmd+R on an iPhone simulator
```

Set your API key in `PlaygroundApp.swift` (currently `"YOUR_API_KEY"`).
Set your connector token in `PlaygroundApp.swift` (currently `"YOUR_CONNECTOR_TOKEN"`).

## What this example demonstrates

Expand All @@ -29,7 +29,7 @@ Each subsection leads with **the SDK call(s)** (the actual API), then shows **ho

### Runtime configuration via `DevSettings` — `Views/SettingsSheet.swift`

`DevSettings` is a **public SDK type** (`Sources/PolyMessaging/Public/DevSettings.swift`) — an `@MainActor open class DevSettings: ObservableObject` backed by `UserDefaults`. Construct it with no arguments after `initialize(_:)`; it reads the API key from `PolyMessaging.currentConfig` and seeds its environment from there, so it bakes in no credentials. Edit the published knobs live; `buildConfiguration()` folds them into a `Configuration` the SDK consumes on the **next** session.
`DevSettings` is a **public SDK type** (`Sources/PolyMessaging/Public/DevSettings.swift`) — an `@MainActor open class DevSettings: ObservableObject` backed by `UserDefaults`. Construct it with no arguments after `initialize(_:)`; it reads the connector token from `PolyMessaging.currentConfig` and seeds its environment from there, so it bakes in no credentials. Edit the published knobs live; `buildConfiguration()` folds them into a `Configuration` the SDK consumes on the **next** session.

The SDK calls:

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Voice/01-Hello/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ struct ContentView: View {

private func startCall() {
// Fill in your connector from Agent Studio › Connector Settings.
let config = Configuration(apiKey: "YOUR_API_KEY")
let config = Configuration(apiKey: "YOUR_CONNECTOR_TOKEN")
do {
let newCall = try PolyVoice.call(
config: config,
options: VoiceOptions(webrtcToken: "YOUR_WEBRTC_TOKEN")
options: VoiceOptions(webrtcToken: "YOUR_WEB_CALLING_TOKEN")
)
setupFailure = nil
call = newCall
Expand Down
8 changes: 4 additions & 4 deletions Examples/SwiftUI/Voice/01-Hello/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ open VoiceSwiftUI.xcodeproj # from this folder
```

1. Set your team under **Signing & Capabilities** (a device build needs one).
2. In `ContentView.swift`, fill in both credentials from **Agent Studio › Connector Settings**: `apiKey` (your connector token, currently `"YOUR_API_KEY"`) and `webrtcToken` (the gateway token — a **distinct** value, currently `"YOUR_WEBRTC_TOKEN"`).
2. In `ContentView.swift`, fill in both credentials from **Agent Studio › Connector Settings**: `apiKey` (your connector token, currently `"YOUR_CONNECTOR_TOKEN"`) and `webrtcToken` (the web calling token — a **distinct** value, currently `"YOUR_WEB_CALLING_TOKEN"`).
3. Run on a **physical iPhone** — the simulator can't carry WebRTC media. Allow the microphone, tap **Start call**, and talk.

Mic permission (`NSMicrophoneUsageDescription`) and the `audio` background mode are already configured via `project.yml`, so the call keeps running when you background the app.
Expand All @@ -32,8 +32,8 @@ Each subsection leads with **the SDK call** (the actual API), then shows **how i

```swift
let call = try PolyVoice.call(
config: Configuration(apiKey: "YOUR_API_KEY"), // connector token
options: VoiceOptions(webrtcToken: "YOUR_WEBRTC_TOKEN") // gateway token — a distinct value
config: Configuration(apiKey: "YOUR_CONNECTOR_TOKEN"), // connector token
options: VoiceOptions(webrtcToken: "YOUR_WEB_CALLING_TOKEN") // web calling token — a distinct value
) // throws PolyError.invalidConfiguration on a blank token,
// or a .custom environment without VoiceOptions.signalingHost
```
Expand All @@ -50,7 +50,7 @@ do {
call = newCall
```

**Under the hood:** building the call does no network work — it validates the tokens and wires the WebRTC engine to the same REST/session/signaling pipeline the SDK's tests exercise. Everything starts at `start()`. The two tokens do different jobs: the API key authenticates the call session, the WebRTC token authenticates the signaling offer and the ICE-servers fetch.
**Under the hood:** building the call does no network work — it validates the tokens and wires the WebRTC engine to the same REST/session/signaling pipeline the SDK's tests exercise. Everything starts at `start()`. The two tokens do different jobs: the connector token authenticates the call session, the web calling token authenticates the signaling offer and the ICE-servers fetch.

*See [voice guide › Credentials](../../../../docs/PolyVoice.md#credentials).*

Expand Down
4 changes: 2 additions & 2 deletions Examples/SwiftUI/Voice/02-CallKit/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ struct ContentView: View {

private func startCall() {
// Fill in your connector from Agent Studio › Connector Settings.
let config = Configuration(apiKey: "YOUR_API_KEY")
let config = Configuration(apiKey: "YOUR_CONNECTOR_TOKEN")
let newCall: PolyCall
do {
newCall = try PolyVoice.call(
config: config,
options: VoiceOptions(
webrtcToken: "YOUR_WEBRTC_TOKEN",
webrtcToken: "YOUR_WEB_CALLING_TOKEN",
callKit: callKitAvailable // audio start/stop deferred to CallKit
)
)
Expand Down
2 changes: 1 addition & 1 deletion Examples/SwiftUI/Voice/02-CallKit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Run it

1. Open `VoiceCallKitSwiftUI.xcodeproj` (or `xcodegen generate` first if you changed `project.yml`).
2. Drop your **connector token** and **WebRTC token** into the `PolyVoice.call(...)` block in `ContentView.swift` (both from Agent Studio › Connector Settings — see the [voice guide › Credentials](../../../../docs/PolyVoice.md#credentials)).
2. Drop your **connector token** and **web calling token** into the `PolyVoice.call(...)` block in `ContentView.swift` (both from Agent Studio › Connector Settings — see the [voice guide › Credentials](../../../../docs/PolyVoice.md#credentials)).
3. Everything the app needs is preconfigured in `project.yml`: the mic permission (`NSMicrophoneUsageDescription` — allow it on first call) and `UIBackgroundModes: [audio, voip]`. Note that **`voip` is required** — without it CallKit refuses every transaction (`requesttransaction Code=1`) and the Start button appears dead.
4. Run on a **physical device**. WebRTC media can't cross the simulator, and CallKit itself is broken there (iOS 17+ simulators auto-end the call; `didActivate` never fires) — on the simulator this example deliberately falls back to a plain `01-Hello`-style call.

Expand Down
4 changes: 2 additions & 2 deletions Examples/UIKit/Chat/01-Hello/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
) -> Bool {
// Initialize once at app launch. After this, PolyMessaging.chat()
// works anywhere in the app with no arguments.
// Replace YOUR_API_KEY with your connector token from Agent Studio.
// Replace YOUR_CONNECTOR_TOKEN with your connector token from Agent Studio.
PolyMessaging.initialize(.init(
apiKey: "YOUR_API_KEY"
apiKey: "YOUR_CONNECTOR_TOKEN"
))
// UITests pass -uiTestFreshStart to force a brand-new greeting +
// suggestions instead of resuming a prior conversation.
Expand Down
Loading