Add conversation history, document attachments, and safer server binding - #74
Open
bayeasscamera wants to merge 20 commits into
Open
Add conversation history, document attachments, and safer server binding#74bayeasscamera wants to merge 20 commits into
bayeasscamera wants to merge 20 commits into
Conversation
Mac app: - Persistent conversation sidebar with search, load, delete, and clear - Attach PDF/DOCX/TXT/MD/RTF documents; extracted text is injected into the prompt with context markers - Restore InspectorView as the NavigationSplitView detail pane (runtime controls were dropped by the sidebar refactor) - Remove dead cognitive-mode placeholder that skipped generation state initialization Server: - Add --host and --allow-remote; binding a non-loopback host is refused unless --allow-remote is passed explicitly CI: - Cache SwiftPM, add a debug build as a fast first signal, and run swift-format lint as a non-blocking step - Ignore local TurboFieldfare.app bundle builds
startAccessingSecurityScopedResource() was called on every imported URL but never balanced with stopAccessingSecurityScopedResource(), leaking access tokens for the whole session. Move the scope handling into AppModel.attachDocuments and always release it with defer after extraction. This also covers drag-and-drop URLs, which previously had no scope at all.
ConversationStoreTests: insert/update ordering, delete, clear-all, JSON persistence across store instances, missing-file startup, and Conversation title/response preview formatting. DocumentAttachmentTests: extraction from real TXT, MD, RTF, DOCX, and PDF files (generated in-memory), unsupported-format, oversized (sparse file), and empty-document errors, long-text truncation, and attachment prompt context/preview/size presentation. AppModelAttachmentTests: attachDocuments extraction and error surfacing, promptWithAttachments composition, makeRequest prompt injection, size totals, and removal/clear helpers.
bayeasscamera
force-pushed
the
feature/conversation-history-attachments
branch
from
August 3, 2026 13:59
5046a3b to
ff7f73c
Compare
Documentation: - README: conversation history sidebar, document attachments section, --host/--allow-remote mention, status list update - OPENAI_SERVER.md: bind-address section explaining --allow-remote Conversation history: - Inject ConversationStore into AppModel (tests no longer touch real Application Support data) - Regenerating the same prompt updates the active conversation instead of creating duplicates; a new prompt starts a new entry - Persist document attachments in Conversation (Codable) with legacy JSON decode fallback; loadConversation restores them - Replace the single attachmentError with an attachmentErrors list so every failed file surfaces in the composer Tests: 13 new (injectable store, upsert semantics, attachment round-trip, legacy format decoding, conversation lifecycle) — 613 total passing.
…ttachment limits Sidebar: - Pin conversations to the top of the list (pinned sort first) - Rename inline via double-click, context menu, or keyboard - Export/import history as JSON (file exporter/importer) - Keyboard shortcuts: ⌘N new conversation, ⌘F focus search Attachment limits: - DocumentExtractor takes instance limits (defaults unchanged) - New persisted settings for max file size and extracted text length - InspectorView Attachments section with pickers Tests: pinned ordering, rename behavior, export/import round-trip and id replacement, custom extractor limits — 622 total passing.
The app never loads the tokenizer in-process, so the composer hint uses a character-based estimate of the effective prompt (documents included). The capsule turns orange with an explanatory tooltip when the estimate exceeds the selected context window. README: document pinning, renaming, export/import, configurable limits, and the token hint.
CI: - swift-format lint now covers only the Swift files a PR changes instead of the whole tree (the codebase predates swift-format, so a global run reports 26k style warnings and is unusable as a signal) Conversation store: - Log decode/save failures with os.Logger instead of failing silently - Accept fractional-second ISO8601 timestamps on decode so history files written by other tools still load Tests: fractional timestamp decoding — 626 total passing.
When enabled (inspector > Runtime, persisted), generation runs four passes instead of one. Each pass streams into the output pane under its own header; the conversation history stores the final revision. - CognitiveCycleEngine: pure stateful builder of the four prompts, each embedding the outputs of the previous passes - AppModel orchestrates the passes with per-pass HUD resets; cancel stops after the current pass; failures end the run like single-pass - Save conversation with an explicit response so history keeps the revised version rather than the whole cycle transcript Tests: engine ordering, prompt composition, idempotent nextStep, completion; full cycle through MockInferenceClient (four passes, final revision in history, cancel mid-pass) — 640 total passing.
…s, history safeguards - Scripts/build-app.sh + install-app.sh: bundle (+ ad-hoc signature), version from git tag (--version to override), --force install - CLI --cognitive-mode: runs the plan/draft/critique/final cycle; engine moved into the shared runtime so both app and CLI use it - Per-conversation maxNewTokens: persisted and restored on load (B.2) - Attachment errors: per-file list with individual dismiss + Clear all (C.4); alert on unreadable history file (preserved as .corrupted) (C.3) - Cognitive passes segmented in-memory for future UI polish (B.1 foundation) - Tests: history recovery, maxNewTokens, CLI flags, cognitive engine round-trip — 649 total passing
Conversation now stores ordered turns instead of a single exchange. Loading a history entry restores the full transcript and clears the composer so the dialogue can continue: the next message is sent with the whole turn history, and regenerating the same prompt replaces the previous model answer instead of appending a duplicate. - Turn model + legacy JSON migration (prompt/response keys still decode) - continuationPromptText composes history + new user message (documents included); makeRequest uses it when a conversation is active - saveCurrentConversation appends turns, or replaces the last answer when the same prompt is regenerated - Sidebar search covers turn text; composer placeholder switches to "Continue the conversation…"; README updated Tests: asPrompt/displayTranscript, continuation composition, append and replace semantics, legacy migration — 657 total passing.
- G.3: extracted PDFs separate pages with [Page N] markers so answers can cite where information came from - F.2: fork a conversation from the context menu (optionally up to a given turn); forks link to their parent via parentConversationID - H.4: per-conversation Markdown export via the context menu with a filesystem-safe filename Tests: fork semantics (full + up-to-turn), Markdown rendering — 660 total passing.
- F.3: mark any conversation as a reusable template; a Templates filter button in the sidebar shows only those - F.4: per-conversation tags (context menu > Edit Tags), #tag search in the sidebar; tags and template flags survive history upserts - C.2: install.log journal in Application Support records install phase transitions and failures for diagnostics Tests: template flag, tag normalization/dedup, flag preservation on regeneration — 663 total passing.
- C.4: --export-conversations <path> copies the app conversation history JSON (validated) without requiring a model - I.3: GET /metrics exposes Prometheus-style counters for served completions, uptime, and model info Tests: metrics endpoint state and counter growth — 665 total passing.
- B.1: after a cognitive cycle finishes, the output pane shows each pass as a collapsible disclosure group instead of one long transcript - H.2: CognitiveStopPolicy detects critiques that found nothing to fix and skips the final revision pass, keeping the draft as the answer Tests: stop policy (empty/positive/actionable critiques) — 668 total passing.
- H.3: compare any conversation with the latest run of the same prompt in a side-by-side sheet (context menu > Compare with latest run) - H.1: Summarize starts a new conversation whose first turn asks the model to summarize the selected history, then generates immediately Tests: shared-prompt filtering, summary launch through the mock client — 670 total passing.
- G.2: DocumentLibrary stores extracted documents by filename in Application Support; attachDocuments caches new extractions there - G.1: when attached documents would overflow the context window, promptWithAttachments keeps only the chunks most relevant to the prompt (word-chunk overlap + TF-IDF scoring, pure Swift) Tests: chunking (short/long/overlap), retriever ranking and fallbacks, library upsert/lookup/persistence, extraction caching — 680 total passing.
- I.1: chat messages accept an optional document reference (inline text, file path, or base64); text is injected into the prompt with a [Document: name] marker. Binary formats are refused with a clear error - I.2: GET /v1/conversations serves the app's history JSON (empty array when no history file exists) Tests: extractor (inline/base64/path/errors), prompt injection through a recording backend, conversations route — 689 total passing.
The previous version had a typo in the build_args expansion that caused zsh to fail when called via bash. Pin destination and application_dir directly; use so the build step succeeds even when no flags are passed.
- TurboFieldfareAppearance: shared tokens (dark background, surface, accent/danger, corner radii) used across the app - RootView/Inspector/HUD/Examples/Error/ModelAction surfaces now use the dark surface instead of system window backgrounds - Sidebar rows show pin and template glyphs plus tag badges (max 2 + N) - Composer: raised surface, accent focus ring, iconic placeholder, continued-conversation hint - Attachment rows: tinted icon tiles and accent hover stroke - Cognitive passes render as tinted, collapsible cards per pass - Install progress bar tinted with the accent color No behavior changes; 689 tests still passing.
- Terminal responses now render as formatted Markdown (headings, code, lists, emphasis) instead of raw text; the prompt stays above in italic gray. Streaming keeps the live NSTextView until generation ends - Prompt example cards get accent hover states (fill + stroke) via a dedicated subview to keep type-checking tractable Pure visual changes; 689 tests still passing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds three independent improvements to the Mac app and the loopback server.
Mac app: conversation history
NavigationSplitView) listing past conversations, with search, load, delete, and clear-all.Mac app: document attachments
[Document: …]context markers.NSAttributedStringfor DOCX; files over 500k characters are truncated with a visible warning.Fixes
InspectorViewin thedetailpane of theNavigationSplitView. The sidebar refactor had dropped the runtime controls (context length, expert-cache slots, temperature/Top-K/Top-P, prefill, RDADVISE, unload) from the layout entirely.runCognitiveCycle) that skipped generation state initialization (transcript reset,activeRunRuntimeKey, live counters).startAccessingSecurityScopedResource()was never balanced withstopAccessingSecurityScopedResource(), leaking access tokens; drag-and-drop URLs had no scope handling at all.Server hardening
--hostand--allow-remote. Binding a non-loopback host is refused unless--allow-remoteis passed explicitly, since the server has no authentication or TLS.CI
swift-formatlint as a non-blocking step.TurboFieldfare.appbundle builds.Validation
swift build(debug): cleanScripts/test.sh: 520 tests in 108 suites, all passing (includes 5 new tests covering--host/--allow-remoteparsing)