Commit Graph
34 Commits
Author SHA1 Message Date
runeandClaude Sonnet 5 69dbf17f5e Add PDF text extraction to MCP read_file and search_files
MCP's read_file previously hard-required UTF-8 text decoding, so any
PDF in an allowed folder failed with "Cannot read file as UTF-8
text" — chat attachments already supported PDFs (raw bytes to
vision-capable models), but the AI couldn't read one on its own
during agentic file-tool use. search_files' content_search had the
same gap, silently skipping PDFs.

Adds MCPService.extractPDFText(atPath:) using PDFKit (built into
macOS, no new dependency) to pull text from a PDF's text layer.
Wired into both read_file and search_files' content search. Returns
a clear error for scanned/image-only PDFs with no text layer.
Automatically covers the Research Agents sub-agent tool loop too,
since it shares the same executeTool dispatcher.

Verified live: asked the AI to read a real PDF containing "The
secret code is PINEAPPLE-42." via read_file — it extracted the text
correctly through the MCP tool-call path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 11:44:27 +02:00
rune c6a7439648 Phase 4 seam: in-memory DatabaseService + DB injection for ContextSelectionService
DatabaseService gains a testable init(dbQueue:) plus a makeInMemory()
convenience and schema-introspection helpers (tableExists/columnNames),
so migration tests don't need the test target to import GRDB directly.
ContextSelectionService now takes an injected DatabaseService (defaults
to .shared) and its DB-coupled methods (smartSelection,
getSummariesForExcludedRange, isMessageStarred) are bumped to internal.
No logic changes to existing call sites.
2026-07-24 09:16:42 +02:00
rune 02bf73fec6 Actually commit the oAITests target wiring in project.pbxproj
The oAITests PBXNativeTarget (PBXContainerItemProxy, PBXTargetDependency,
XCBuildConfiguration with TEST_HOST/BUNDLE_LOADER, the "recommended
settings" changes accepted when the target was created -- DEAD_CODE_STRIPPING,
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED, STRING_CATALOG_GENERATE_SYMBOLS,
DEVELOPMENT_TEAM moved to project-level inheritance) was somehow never
actually staged in the very first "Add real oAITests target" commit
(8c7fb59) despite every xcodebuild test run since then depending on it
being present on disk. Every subsequent commit this session only staged
specific file paths (never oAI.xcodeproj again), so the gap went
unnoticed until a full `git status` review here.

Without this, anyone else pulling the branch (or a truly clean checkout
on this machine) would have all the .swift test files but no target to
compile them into -- xcodebuild test would fail to find oAITests at all.
Confirmed the diff is exactly the expected target-wiring content, nothing
unrelated or corrupted, before committing.
2026-07-23 14:19:53 +02:00
rune a053d4a983 Phase 2: tests for the provider layer and other exposed pure helpers
66 new tests across 8 files (93 total in the suite now):

- GitSyncServiceTests: convertToSSH/injectCredentials/sanitizeFilename/
  detectSecretsInText, including a below-threshold false-positive check
  on the secret regex.
- ChatViewModelPureLogicTests: detectGoodbyePhrase, inferProvider,
  calculateCost -- including the cache-read (0.1x) / cache-write (1.25x)
  pricing multipliers, which is real billing-affecting logic.
- EmbeddingServiceTests / ContextSelectionServiceTests: embedding
  (de)serialization round-trip, importance-score weighting, and the
  token-estimate fallback (content.count / 4) when no real count exists.
- OpenRouterProviderTests / OllamaProviderTests / OpenAIProviderTests /
  AnthropicProviderTests: request-building (attachments, online mode,
  cache_control breakpoints, o1/o3 temperature omission, tool schema
  conversion) and response-parsing (text, tool_use blocks, empty-choices
  fallback behavior) for all four providers, with no network involved.

Also marks calculateCost/inferProvider/detectGoodbyePhrase (ChatViewModel)
and serializeEmbedding/deserializeEmbedding (EmbeddingService) as
`nonisolated` -- discovered via the actual test failures, not
speculation: the project's `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`
setting isolates the classes that carry an explicit `@MainActor`
(ChatViewModel), so calling their static members from a plain
synchronous @Test needs the pure ones marked `nonisolated`. Matches
the existing convention already used elsewhere in EmbeddingService
(cosineSimilarity was already nonisolated before this change).

Phase 2 of the test-suite rollout plan (peaceful-baking-kurzweil).
2026-07-23 14:11:50 +02:00
rune f39527b1d8 Phase 2 seam: expose pure helpers for testing (no logic changes)
Every change here is either dropping `private` (still invisible
outside the module, @testable import just needs internal-or-wider)
or converting a self-independent instance method to `static func`
(ChatViewModel.inferProvider/calculateCost/detectGoodbyePhrase --
none of the three ever touched `self`, and constructing a real
ChatViewModel triggers a real network call in init, so static-ifying
them sidesteps that entirely rather than fighting it). Call sites
updated to `Self.foo(...)` where the static conversion required it.

Touches: GitSyncService's URL/secret-scanning helpers,
EmbeddingService's embedding (de)serialization, ContextSelectionService's
importance scoring, and the request-building/response-parsing helpers
on all four providers (OpenRouter, Ollama, OpenAI, Anthropic) -- the
core AI request/response layer, previously 100% untested.

Verified: full existing test suite (27 tests) still green, no
regressions. Tests for these functions land in the next commit.
Phase 2 of the test-suite rollout plan (peaceful-baking-kurzweil).
2026-07-23 13:48:41 +02:00
rune e9aceca4e7 Remove now-dead isContactsHiddenPendingAppleFix kill switch
Contacts was never actually broken by an Apple/OS bug -- it was a
wrong entitlement key in oAI.entitlements, now fixed (abf25bd). No
functional change here: the flag was already false in both places
this session, so behavior is identical; this just removes the
now-pointless beta-badge/conditional-row scaffolding built around it.
2026-07-21 15:01:19 +02:00
rune abf25bd897 Fix Contacts TCC entitlement key: contacts -> addressbook
Confirmed root cause via tccd's own log output: the hardened-runtime
hardened-runtime prompting policy checks for
com.apple.security.personal-information.addressbook (the Contacts
framework's TCC service is still internally named kTCCServiceAddressBook,
a holdover from the old AddressBook framework), not
com.apple.security.personal-information.contacts as the entitlements
file had. With the correct key, tccd allows the prompt and access is
now granted correctly on both macOS 27 beta and 26.5.1 stable — this
was never an OS bug, notarization requirement, or beta-only issue.

Also keeps the fuller CNError domain/code/userInfo logging added while
diagnosing this, in case Contacts TCC issues resurface.
2026-07-21 14:59:39 +02:00
rune 77cc646ee0 Re-enable Contacts row to retest on macOS 27 beta 4
isContactsHiddenPendingAppleFix was flipped true on beta 2 after
CNContactStore.requestAccess returned instant "Access Denied" under
hardened runtime, while Calendar/Reminders/Location worked fine.
Rune just installed beta 4 and wants to retest.
2026-07-21 14:16:03 +02:00
rune 5db1b40552 Union favorites on tied timestamps instead of dropping one side
Two machines that already had favorites before this sync feature
shipped will both have an empty favoriteModelsUpdatedAt, so the
first sync between them ties. Previously that meant the second
machine silently kept only its own set; now tied timestamps merge
via union and re-push, so no pre-existing favorites are lost.
2026-07-21 14:06:05 +02:00
rune fc786d48f8 Sync starred models across machines; add automatic backup scheduling
Favorites now push/pull through a small oai_favorites.json file in the
same iCloud Drive folder used by Settings > Backup, reconciled by
last-write-wins timestamp on launch and app-become-active. Also adds
an Off/Daily/Weekly frequency picker so the full settings backup can
run itself (checked at launch and hourly) instead of requiring a
manual "Back Up Now" click every time.
2026-07-21 13:19:50 +02:00
runeandClaude Sonnet 5 bd686873c4 Update commercial licensing contact URL to oai.pm
Consolidates the mac.oai.pm subdomain references (introduced in the
PolyForm Noncommercial relicense) to the root oai.pm domain, across
the LICENSE file, README, and all Swift source file headers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:33:53 +02:00
runeandClaude Sonnet 5 cf3f4ebfe4 Relicense oAI from AGPL-3.0-or-later to PolyForm Noncommercial 1.0.0
Switches the project from AGPL to a source-available license that
restricts commercial use — selling oAI or any part of it, standalone
or bundled into another product/service, now requires a separate
commercial license from the copyright holder. Noncommercial use,
study, modification, and sharing remain fully permitted.

Updates: LICENSE (canonical PolyForm Noncommercial 1.0.0 text +
commercial licensing contact note), SPDX headers and file-header
boilerplate across all Swift source files, the in-app About dialog's
license link (+ its localization catalog entry), README.md and
DEVELOPMENT.md license sections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:16:09 +02:00
runeandClaude Sonnet 5 e6f965ff19 Add external MCP server support (stdio JSON-RPC)
Lets the AI connect to any external stdio MCP server (e.g. safaridriver
--mcp) configured in Settings, with tools auto-discovered and prefixed
by server slug. Includes crash detection with backoff restart (5s/15s/30s)
and a Settings UI to add/enable/disable/remove servers.

Fixes the temp-dir allowlist in MCPService.isPathAllowed to also match
/tmp and /private/tmp (not just NSTemporaryDirectory(), which resolves
to a different per-user Darwin temp dir) so the MCP file tools can
actually read files external servers and image generation write there.
Also switches the Add Server sheet's argument parsing to a quote-aware
tokenizer so args containing spaces survive intact.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 08:01:00 +02:00
runeandClaude Sonnet 4.6 40c5f25517 Add personal data tools, 2nd Brain trust toggle, and research agents
Personal Data Tools: native Calendar, Reminders, Contacts (hidden pending
Apple TCC fix in beta), and Location & Maps access via EventKit, Contacts
framework, and MapKit. Write actions (create event/reminder, complete
reminder) gate through an approval sheet. Four hardened-runtime entitlements
added to oAI.entitlements; Info.plist usage strings added for all services.
Personal Data section shows a β badge while Contacts is hidden.

2nd Brain always-trust: inline toggle on the Agent Skills row for the skill
named "2nd Brain" skips the bash approval dialog when the command contains
.brain_helper.py, gated by three runtime checks in MCPService.

Research agents: spawn_research_agents tool runs up to 5 concurrent read-only
sub-agents (read_file, list_directory, search_files, web_search — no write,
no bash, no nesting). Bounded by maxConcurrentAgents setting (default 3) and
a hard ceiling of 8 tasks. Added items field to Tool.Function.Parameters.Property
for JSON Schema array support; wired into AnthropicProvider.convertParametersToDict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 14:04:47 +02:00
runeandClaude Sonnet 4.6 e7c7b9b5c6 Fix combined conversation's model to reflect sources, not the merge model
primaryModel was being set to the model that performed the merge (or,
in AI mode, stamped onto every synthesized message). It should instead
be the most recently used model among the source conversations being
combined.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:54:28 +02:00
runeandClaude Sonnet 4.6 3dff8a8c8e Add combine saved conversations feature (simple + AI-assisted merge)
Lets users multi-select 2+ saved conversations and merge them into one,
either by chronological concatenation or by having the default model
synthesize a single coherent conversation from the source transcripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:45:56 +02:00
runeandClaude Sonnet 4.6 92e393ab03 Fix Swift 6 actor-isolation warnings in model inits and services
- Message, Conversation, EmailLog: add nonisolated to inits — plain value
  types have no actor isolation, but the macOS 27 SDK was inferring it
- EncryptionService: replace lazy var encryptionKey (which mutates self and
  gets inferred as @MainActor) with an eagerly-initialized let in init()
- FileLogger: add nonisolated to shared, write, and minimumLevel so they
  are callable from nonisolated AppLogger methods without warnings
- LogLevel.<: add nonisolated to the Comparable conformance method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 14:59:07 +02:00
runeandClaude Sonnet 4.6 8451db1142 UI redesign Phase 1: NavigationSplitView with collapsible sidebar
- Replace root VStack with NavigationSplitView (2-column, collapsible sidebar)
- Add SidebarView: new chat button, conversation search, list with swipe actions
- Slim HeaderView to text-only (provider + model + star); remove all icon rows
- Move status pills (Online, MCP, Synced) to footer right side
- Remove version number and shortcut hints from footer
- Add resizable InputBar with drag handle (persisted height) and globe/network.slash online toggle
- Fix Norwegian menu appearing on English systems (CFBundleLocalizations in Info.plist)
- Add View menu (Model Info, History, Stats, Credits, Online Mode toggle ⌘⇧O)
- Add ⌘L as alias for Search Conversations (muscle memory for /load users)
- Add Check for Updates to Help menu with download URL from Gitea API
- Add one-time Intel/Rosetta deprecation warning on first launch
- Swift 6: fix self.Self.isoString() call sites in DatabaseService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:18:48 +02:00
runeandClaude Sonnet 4.6 13699864d8 New release v2.3.9
- Jarvis integration: manage oAI-Web agents and usage from inside the app (/jarvis command, Settings tab 11)
- Model category filter: keyword-based categorisation with popover picker in model selector
- Categories shown in ModelInfoView with coloured chips; dot indicators on model rows

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:05:47 +02:00
rune 3f9b30bfa1 New release v2.3.8 2026-03-05 13:17:53 +01:00
rune 49f842f119 New version v2.3.6 2026-03-04 10:19:16 +01:00
rune 98d9ee2b51 Fix EmbeddingService 2026-02-27 14:50:47 +01:00
rune e9d0ad3c66 iCloud Backup, better chatview exp. bugfixes++ 2026-02-27 14:05:11 +01:00
rune 3997f3feee updates 2026-02-25 08:24:05 +01:00
rune 079eccbc4e Several bugs fixed 2026-02-23 07:54:16 +01:00
rune 41185cc08b Version 2.3.2 2026-02-20 14:49:56 +01:00
rune 4acf538d8f Added install instructions, and version check 2026-02-20 09:55:30 +01:00
rune f3d673ab27 First public release v2.3.1 2026-02-19 16:39:23 +01:00
rune 52e3d0c07e updated with correct license 2026-02-18 12:43:47 +01:00
rune 54a8c47df4 Added skills, shortcuts, and bugifixes++ 2026-02-18 11:58:45 +01:00
rune 25bcca213e Small feature changes and bug fixes 2026-02-16 13:17:08 +01:00
rune 04c9b8da1e Added a lot of functionality. Bugfixes and changes 2026-02-15 16:46:06 +01:00
rune 7265d22438 Bug gixes, features added, GUI updates and more 2026-02-12 14:29:35 +01:00
rune 42f54954c1 Initial commit 2026-02-11 22:22:55 +01:00