CodeMode.swift is a Swift package that implements CodeMode for Apple platform APIs by exposing agents to two tools:
searchJavaScriptAPI: code-driven discovery of the bundled JavaScript wrapped Apple API surface for the current host platform.executeJavaScript: constrained JavaScript execution with capability allowlisting and structured errors.
GitHub: velos/CodeMode.swift
- Platforms:
iOS 18+,macOS 15+,visionOS 2+ - Typed Swift host API through
CodeModeAgentTools - Streaming execution via
JavaScriptExecutionCall - Structured failures via
CodeModeToolError - Hybrid JS surface:
- web-style globals:
fetch,URL,URLSearchParams,setTimeout,console - cross-platform Apple namespaces:
apple.keychain,apple.location,apple.weather,apple.calendar,apple.reminders,apple.contacts,apple.photos,apple.vision,apple.notifications,apple.health,apple.home,apple.media,apple.fs - platform-specific namespaces when needed:
ios.alarm
- web-style globals:
- Node-style aliases for file operations through
globalThis.fs.promises - Sandboxed filesystem policy with allowed roots:
tmp,caches,documents - Search and execution only expose helpers supported on the current host platform
apple.*is the canonical cross-Apple namespace, not a promise that every helper exists on every Apple OS- Library-only package surface
Add CodeMode.swift with Swift Package Manager:
.package(name: "CodeMode", url: "https://github.com/velos/CodeMode.swift", from: "0.1.0")Then add the product to your target:
.product(name: "CodeMode", package: "CodeMode")CodeModeAgentToolssearchJavaScriptAPI(_:) async throws -> JavaScriptAPISearchResponseexecuteJavaScript(_:) async throws -> JavaScriptExecutionCallJavaScriptExecutionCall.eventsJavaScriptExecutionCall.resultJavaScriptExecutionCall.cancel()CodeModeToolErrorCodeModeConfigurationCodeModeFileSystemLocalCodeModeFileSystemCodeModeAgentToolDescriptions
import CodeMode
let tools = CodeModeAgentTools()
let searchResponse = try await tools.searchJavaScriptAPI(
JavaScriptAPISearchRequest(
code: """
async () => {
return api.references
.filter(ref => ref.tags.includes("reminders"))
.map(ref => ({
capability: ref.capability,
jsNames: ref.jsNames,
summary: ref.summary
}));
}
"""
)
)
print(searchResponse.result ?? .null)
let call = try await tools.executeJavaScript(
JavaScriptExecutionRequest(
code: """
await apple.fs.write({ path: "tmp:note.txt", data: "hello" });
return await fs.promises.readFile("tmp:note.txt", "utf8");
""",
allowedCapabilities: [.fsWrite, .fsRead]
)
)
for await event in call.events {
switch event {
case .log(let entry):
print(entry.message)
case .diagnostic(let diagnostic):
print(diagnostic.message)
case .syntaxError(let error),
.functionNotFound(let error),
.thrownError(let error),
.toolError(let error):
print("\(error.code): \(error.message)")
case .finished:
break
}
}
let result = try await call.result
print(result.output ?? .null)CodeMode keeps its JavaScript filesystem API stable while allowing hosts to provide the underlying operations:
let tools = CodeModeAgentTools(
config: CodeModeConfiguration(
fileSystem: MyCodeModeFileSystem()
)
)CodeModeFileSystem receives paths after PathPolicy resolution, so sandbox root enforcement stays in CodeMode while the host can route reads, writes, listings, moves, copies, deletes, and stats through another backing implementation. LocalCodeModeFileSystem preserves the default FileManager behavior.
searchJavaScriptAPI accepts JavaScriptAPISearchRequest:
code: JavaScript source that evaluates to an async function
Behavior:
- empty search input throws
CodeModeToolError(code: "INVALID_REQUEST", ...) - search executes your async function against a preloaded
apiobject apionly contains capabilities and JS names supported on the current host platform- returned output must be JSON-serializable
- syntax errors, invalid search programs, timeouts, and runtime failures throw
CodeModeToolError - responses include
result: JSONValue?plus non-fatal diagnostics
Available in search code:
interface JavaScriptAPIReference {
capability: string;
jsNames: string[];
summary: string;
tags: string[];
example: string;
requiredArguments: string[];
optionalArguments: string[];
argumentTypes: Record<string, string>;
argumentHints: Record<string, string>;
resultSummary: string;
}
declare const api: {
references: JavaScriptAPIReference[];
byCapability: Record<string, JavaScriptAPIReference>;
byJSName: Record<string, JavaScriptAPIReference>;
};Example search programs:
async () => {
return api.references
.filter(ref => ref.tags.includes("media"))
.map(ref => ({ capability: ref.capability, jsNames: ref.jsNames }));
}async () => {
return api.byJSName["apple.fs.read"];
}executeJavaScript accepts JavaScriptExecutionRequest:
codeallowedCapabilitiestimeoutMscontext
It returns a JavaScriptExecutionCall immediately.
Cross-platform privileged helpers are installed under apple.*. Platform-specific helpers are installed only where supported, for example ios.alarm.* on iOS hosts that support AlarmKit.
apple.location.requestPermission() is currently exposed only on iOS hosts. Other Apple platforms can expose apple.location.* helpers when supported, but the explicit permission-request helper is intentionally hidden outside iOS for now.
call.events is a non-throwing AsyncStream that can emit:
.log(ExecutionLog).diagnostic(ToolDiagnostic).syntaxError(CodeModeToolError).functionNotFound(CodeModeToolError).thrownError(CodeModeToolError).toolError(CodeModeToolError).finished
call.result is the throwing boundary:
- on success it returns
JavaScriptExecutionResult - on failure it throws
CodeModeToolError call.cancel()performs best-effort cancellation
CodeModeToolError includes structured fields such as:
codemessagefunctionNamecapabilitylinecolumnsuggestionsdiagnosticslogspermissionEvents
For hosts that vend these methods as LLM tool calls, use CodeModeAgentToolDescriptions.searchJavaScriptAPI and CodeModeAgentToolDescriptions.executeJavaScript as the canonical tool descriptions.
Host apps must provide privacy usage strings for bridged APIs that request protected resources.
Required Info.plist keys by capability:
- Location read (
location.read):NSLocationWhenInUseUsageDescription - Location permission request (
location.permission.request, iOS-only):NSLocationWhenInUseUsageDescription - Contacts (
contacts.read,contacts.search):NSContactsUsageDescription - Calendar read (
calendar.read):NSCalendarsFullAccessUsageDescription - Calendar write-only (
calendar.write):NSCalendarsWriteOnlyAccessUsageDescription - Reminders (
reminders.read,reminders.write):NSRemindersFullAccessUsageDescription - Photos (
photos.read,photos.export):NSPhotoLibraryUsageDescription - AlarmKit (
alarm.permission.request,alarm.read,alarm.schedule,alarm.cancel):NSAlarmKitUsageDescription - HealthKit read (
health.permission.request,health.read):NSHealthShareUsageDescription - HealthKit write (
health.permission.request,health.write):NSHealthUpdateUsageDescription - HomeKit (
home.read,home.write):NSHomeKitUsageDescription
Notifications:
- Local notification scheduling and management (
notifications.*) requires runtime authorization viaapple.notifications.requestPermission() - No additional Info.plist privacy string is required for
UNUserNotificationCenterauthorization prompts
AlarmKit:
alarm.*requiresiOS 26+and runtime authorization viaios.alarm.requestPermission()
HealthKit:
health.*requires the HealthKit entitlement and runtime authorization viaapple.health.requestPermission(...)
Weather:
weather.readrequires enabling the WeatherKit capability on the app target- Weather does not require a separate privacy prompt in this package because
weather.readexpects explicit coordinates
Programmatic validation helper:
import CodeMode
let required: Set<CapabilityID> = [
.locationRead,
.contactsSearch,
.weatherRead,
]
let issues = HostConfigurationValidator.validate(requiredCapabilities: required)
for issue in issues {
print("[\(issue.severity.rawValue)] \(issue.key): \(issue.message)")
}- Run the deterministic CodeMode eval harness:
swift run --package-path Tools/CodeModeEval codemode-eval
swift run --package-path Tools/CodeModeEval codemode-eval list
swift run --package-path Tools/CodeModeEval codemode-eval run fs.round-trip --show-code
swift run --package-path Tools/CodeModeEval codemode-eval run --jsonThe eval harness runs 21 built-in user-style scenarios through the same
searchJavaScriptAPI and executeJavaScript APIs that host apps expose to
agents. It validates tool order, discovered catalog output, generated JavaScript
fragments, exact allowedCapabilities, structured errors, repair suggestions,
console logs, diagnostics, and final output. The scenarios cover filesystem
workflows, capability minimization, path policy failures, permission failures,
catalog search behavior, helper suggestions, API argument-shape confusion,
recovery after structured tool errors, and execution timeouts.
- Run Wavelike-backed LLM evals:
swift run --package-path Tools/CodeModeEval codemode-eval llm fs.round-trip --show-code
swift run --package-path Tools/CodeModeEval codemode-eval plan --suite core --repeat 5 --request-delay-ms 1000
swift run --package-path Tools/CodeModeEval codemode-eval llm --suite smoke --repeat 3
swift run --package-path Tools/CodeModeEval codemode-eval llm --suite core --repeat 5 --request-delay-ms 1000 --output Tools/CodeModeEval/.build/reports/core-baseline.json
swift run --package-path Tools/CodeModeEval codemode-eval summarize Tools/CodeModeEval/.build/reports/core-baseline.json --output Tools/CodeModeEval/.build/reports/core-summary.json
swift run --package-path Tools/CodeModeEval codemode-eval report Tools/CodeModeEval/.build/reports/core-baseline.json --output Tools/CodeModeEval/.build/reports/core-baseline.md
swift run --package-path Tools/CodeModeEval codemode-eval compare Tools/CodeModeEval/.build/reports/core-baseline.json Tools/CodeModeEval/.build/reports/core-candidate.jsonThe LLM runner reads WAVELIKE_MODEL_ID, WAVELIKE_APP_ID,
WAVELIKE_API_KEY, and optional WAVELIKE_ENV from the process environment or
.env. It gives the model the real CodeMode tool descriptions, captures actual
model tool calls, executes those calls against the local CodeMode runtime, and
grades the final repaired transcript with the same deterministic expectations.
When no scenario IDs are supplied, llm defaults to --suite smoke; available
suites are smoke, core, failures, and all. The JSON output is an eval
report envelope with raw run results plus aggregate pass rate, average turns,
retry count, exact/minimal capability success, per-scenario metrics, and failure
categories such as wrong_tool, wrong_js, overbroad_capability,
failed_recovery, and no_final_answer.
Interactive live runs show an in-place progress bar with colored pass/fail
states; CI output falls back to line-oriented progress. Use plan to preview
request budgets, --output to save a JSON report, summarize to strip raw
transcripts before committing baselines, report to generate Markdown
diagnostics with tool-attempt retry traces, then compare to fail on pass-rate,
exact-capability, retry, or turn-count regressions. Tolerances are configurable
with --pass-rate-tolerance, --capability-rate-tolerance, --retry-tolerance,
and --turn-tolerance. Live LLM evals retry transient transport errors by
default; use --request-delay-ms, --model-retries, and --retry-delay-ms to
pace larger repeated runs against provider rate limits.
See EVALS.md for CI/nightly policy, baseline handling, and
recommended commands.
The CLI lives in Tools/CodeModeEval so library consumers do not resolve
ArgumentParser or Wavelike dependencies when they use the CodeMode product.
- License: MIT. See LICENSE
The "Code Mode" framing and the tool-oriented search and execution model in this repository were influenced by Cloudflare's Code Mode work:
This repository is an independent implementation and is not affiliated with Cloudflare.
Development of CodeMode.swift was done exclusively with Codex, initiated by an interactively built plan, executed by the model after the plan was finalized.
The package intentionally defers these to later phases:
- Push notifications / APNs token lifecycle
- PassKit wallet APIs
- Other frameworks such as Speech, MusicKit, Foundation Models