Pin the provider codec refactor and refreshed documentation release to
591375990284bf90858e7f049c516777432970f7. Preserve every other package pin.
Validation: AgentKit build and 218 tests, macOS and iOS app builds, strict
Swift formatting, Chinese localization, and project checks passed.
Full macOS tests: 1,598 passed, 3 failed, 8 skipped. Full iOS tests:
584 passed, 2 failed. Focused reruns passed the two Agent timing cases
and mobile Settings layout case. The previously documented Metrics
lifecycle and mobile knowledge-card rendering failures remain.
Allow optional API keys in desktop and mobile settings and share credential
resolution across model requests. Missing stored credentials remain errors.
Enable user-configured HTTP endpoints and pin signed AgentKit 0.13.1, which
omits empty authentication headers for probes and inference.
Validation: AgentKit's 216 tests and iOS's 461 tests passed, along with Swift
formatting, localization checks, native screenshots, and real HTTP smoke tests
on both apps. macOS passed 1419 of 1422 tests initially; existing Metrics timing,
waveform PNG comparison, and MCP upload runner failures were observed. MCP and
Metrics passed a focused rerun, while Metrics remains intermittent and waveform
comparison still fails. iOS simulator LAN-IP access timed out; desktop LAN-IP
and both platforms' localhost, loopback-IP, and local-domain requests passed.
Keep previously installed starter skills as ordinary user-owned records. Provide hidden read-only Skill Creator and Skill Installer instructions, use AgentKit 0.13.0 for reviewed complete GitHub package installation, and remove skill seeding and Reset.
Move Hermes Memory below MCP, use Enabled for its switch, and localize its Chinese title as persistent memory. Preserve existing user edits and synchronize user skill packages through the existing atomic store.
Validation: AgentKit 213 tests and iOS 452 tests passed. macOS app 1183 tests passed; shared frameworks passed 231/232 with the existing SpeechWaveformViewTests PNG byte-equality failure. Related skill, sync, localization and UI checks passed. Strict Swift formatting, Chinese localization checks, and git diff --check passed.
Add opt-in GRDB memory with frozen conversation snapshots, background learning, session search, and a user-only Destroy action on both platforms. Persist and synchronize complete skill packages with the new AgentKit management tools.
Pin signed AgentKit 0.12.3 and GRDB 7.11.1. Add the V18 package migration, regression coverage, bilingual UI snapshots, and architecture records.
Validation: AgentKit and iOS suites, focused macOS skills/memory/architecture/localization tests, Swift formatting, and Chinese localization checks passed. Full macOS runs exposed intermittent existing waveform-rendering and metrics-timing failures.
The API Type picker named three vendors — OpenAI, Anthropic, Google — for
four wire protocols, so `.openAI` carried a Responses toggle to express the
fourth. Each kind also carried a default base URL, which the editor printed
as the Base URL placeholder, so `api.openai.com` was on screen for anyone
who had added a provider and typed nothing, and a provider with a key but no
address quietly resolved to a vendor endpoint nobody had named.
The picker now names the protocol: Automatic, Chat Completions, Responses,
Messages, generateContent. Automatic reads the protocol off the URL's own
suffix and falls back to Chat Completions when nothing matches, which is
what an endpoint that does not name itself is almost always serving.
Base URL and API Path collapse into one field. Splitting them made "does the
base URL include /v1" a question the user had to answer, and it is the
question every client of this shape gets wrong. Paste whatever the gateway's
documentation printed, at whatever depth it printed it; the two captions
underneath say what that produced — Full URL is read from the same value the
request is built from, and the line under it names the body shape, which is
how Automatic explains its own answer and how an explicit pick that
disagrees with the URL stops being a silent surprise. An explicit pick moves
the format, never the address: the person who pasted it keeps it.
Vertex AI goes with the vendor names. It stays in AgentKit, which is an
independent package and does not carry this app's constraints, but nothing
here drives it any more — the editor's project, location and service-account
import are gone, along with five persisted columns and their Keychain path.
`ProviderEndpoint` holds the two things the editor owns, the typed URL and
the picked type, because neither survives a round trip through the composed
result: a URL ending in /messages looks the same whether it was typed or
appended, and Automatic looks the same as picking the format it resolved to.
`AIConfigurationModel.setEndpoint` is the only writer of the derived
`inferenceURL`, `baseURL` and `apiFormat`.
Old rows fold back to the address they used to resolve to rather than to
Automatic, so a provider that worked before the change points at the same
place after it. That fold needs the pre-V12 columns, which is why `kind`,
`baseURL`, `apiPath` and `usesResponsesAPI` are still declared, and it needs
V11 frozen: every VersionedSchema in the plan points at live model types, so
editing a persisted model rewrites what the old version *means* and leaves
an installed store recording a version the plan no longer contains. That is
a refusal to open, not a migration. `TermindSchemaV11Models` is what gives
V11 to V12 two sides — any future attribute change needs the same freeze.
Requires AgentKit 0.5.0, where `ModelAPIFormat` replaces `ModelProviderKind`
and a provider carries `inferenceURL` and `baseURL` instead of a kind, a
base and a path.
Verified with the full test suite (994 tests, including the new
ProviderEndpointTests), swift-format lint --strict,
Scripts/check-zh-localization.sh, npm run build, and a launch of the app
against an existing V11 store.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scratch_edit is now scratch_replace, and takes a regex mode and a per-entry
count — omit it to require exactly one match as before, 0 for every occurrence,
n for the first n. scratch_copy and scratch_move move a staged file or subtree
without its bytes passing through the transcript, which is what duplicating a
file before a risky change used to cost.
The app-side changes are the ones the rename and the two new tools reach:
ActionKind.copy in both the English and Chinese verb tables, the pull → copy →
replace → push recipe in the built-in config-edit skill, and the same wording in
the system prompt and the plan contract. The legacy tables are deliberately
untouched: legacyToolNames and legacyMetadata describe cards written before
presenter IDs were persisted, and scratch_edit belongs in both. AgentKit keeps
builtin.scratch_edit registered as an alias, so a transcript from before the
rename still draws its diff rather than falling back to generic fields — which
aCardWrittenAsScratchEditStillFindsItsProjection now pins.
AppLanguageTests no longer requires %lld results. That plural moved to AgentKit
with web_search's card, which is what db8796c removed from this catalog; the
test's list had been left behind and was failing.
AGENTS.md said AgentKit was referenced as a local SPM package. It is a versioned
remote dependency, so an uncommitted change in ../AgentKit is invisible to the
app and a change spanning both sides needs a tagged release in between. Said so,
because working the other way wastes a build cycle before the mistake shows.
Verified: AgentKit 0.3.0 builds and passes its 112 tests, and the six suites
this touches pass here. The five failures in the full run — ColorSystemTests,
two ChatComposerStateTests, FileURLDropTargetTests and WindowStateTests — all
reproduce on a clean worktree at HEAD and are unrelated.
The loop, the approval gate, the provider adapters, the MCP client, and the
tools with no opinion about transports now live in a package and are consumed
from it. Termind keeps everything that reaches a machine or a person: the SSH
and SFTP tools, the connection pool, the Vault resolver, scratch_pull and
scratch_push, the SwiftData store, and every view.
They meet at four seams. TermindAgentTools registers our tools alongside the
built-in groups; AgentToolServices carries the SSH credential prompt to them
without that prompt appearing in a runtime protocol; TermindAgentPolicy owns the
system prompt, the plan contract, and the ssh_exec plan-mode rule the runtime
cannot write for us; TermindAgentPresentation extends the card vocabulary with
our own semantics.
ChatPermissionMode becomes AgentPermissionMode and moves out of the conversation
model — who authorizes an action is a property of the run, not of a view.
AgentRunRequest takes the session context as rendered text rather than our
SessionContext, because the runtime does nothing with it but insert it in front
of the turn it describes.
Termind's system prompt and plan contract are byte-identical to what shipped.
Every provider cache is a prefix cache, so editing a word there would miss the
cache on every replayed conversation at once.