Skip to content

Commit 0de3e90

Browse files
depletionmodeclaudetholms
authored
Release v1.0.0 (#240)
* Architecture refactor: dedup DB layer, shared transport, module splits (#215) * Refactor database layer: shared exec helpers, single row parsers Add service/src/database/exec.rs providing DbRow (backend-agnostic row with get/get_bool/get_timestamp), Arg (bind enum handling the SQLite-vs-PostgreSQL boolean width difference), and Database helpers (db_fetch_all/db_fetch_optional/db_fetch_one/db_execute/ db_insert_returning_id) that own the pool match. Convert all entity modules to the new layer: paired parse_*_row_sqlite/_postgres functions merge into single parsers, the ~190 DatabasePool match arms collapse to single calls where SQL is identical on both backends, and inserts use db_insert_returning_id instead of hand-rolled last_insert_rowid()/RETURNING id pairs. The BindValue enum in rules.rs is replaced by Arg with args.len()-based parameter indexing. Delete service/src/database/queries.rs: a fully dead legacy abstraction layer (#[allow(dead_code)], zero references) superseded by exec.rs. No SQL semantics, bind order, or function signatures changed. Net: -1,929 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Split dispatch/client.rs into domain handler modules The 3,728-line flat file of 78 client-message handlers becomes dispatch/client/ with one module per domain: nodes, semantic_ops, config, opdefs, traffic, intercept, intercept_targets, logs, toolkit, chains, lua_scripts, agent_chat, payloads. The handle() routing match and the shared send_capability_error helper live in client/mod.rs. Pure reorganization — handler bodies, routing, and behavior unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Share RabbitMQ client transport; collapse CLI request boilerplate Add common::ClientTransport owning the connect/declare/bind sequence and the dual direct+broadcast consumer loop that was duplicated verbatim between cli/src/client.rs and service/src/mcp.rs. Both clients now plug in only their message handlers. Add a generic Client::request() helper in the CLI: store a oneshot sender in the pending slot, publish, await with timeout, and clear the slot on publish failure or timeout. The ~25 hand-rolled copies of this pattern (config, traffic log/search/matches/clear, rules list, rule create/update/delete, log query) collapse to a few lines each with uniform timeout error messages. Convert the two spin-poll waits to the same oneshot pattern: - register() no longer polls every 100ms for the initial StateUpdate - run_semantic_op() no longer polls for SemanticOpQueued, fixing a race where a response arriving before the first poll was missed (pending_semantic_op is now a oneshot sender armed before publish) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Correctness fixes: poison-safe locks, proxy shutdown unification - Add node utils::LockExt providing lock_safe(), a poison-recovering Mutex lock, and use it at all 68 former .lock().unwrap() sites in the node crate. A panic while holding one of these registry locks (process handles, cancel flags, ACP state) no longer cascades into "lock poisoned" panics across unrelated sessions. Verified on both Linux and Windows targets. - Deduplicate the intercept proxy's two near-identical listener loops into one accept_loop() and unify shutdown on CancellationToken; the parallel oneshot shutdown channel for the HTTPS listener is removed. - cli subscribe_acp_events(): replace block_in_place + block_on (which panics on a current-thread runtime and can stall the executor) with the same fire-and-forget spawn used by the sibling subscribe methods. Investigated and rejected three audit findings as false positives: packet_engine's blocking TUN read is properly unblocked by device.shutdown() on cancellation (poll-with-timeout on Linux, session shutdown on Windows); the orchestrator's Arc<AtomicBool> flags follow the project's prescribed cancellation idiom and need per-prompt reset semantics a CancellationToken can't express; the ACP prompt wait must wake periodically to propagate an external cancel flag, which its 50ms sleep loop does correctly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Single source of truth for the log-query table schema Move the log-query table/column definitions to common::log_query_schema, shared by: - service log_query: table_columns() now derives from the canonical schema; resolve_table() iterates a new ALL_TABLES list instead of a hand-written name match - CLI: app/log_query/schema.rs becomes a thin re-export of the common schema (autocomplete and the schema sidebar are unchanged consumers) New tests lock the remaining sync points down: - service: every schema table maps to a VirtualTable (and vice versa), and SQL-backed tables' sql_config kql column lists match the schema exactly, in order - common: docs/src/usage/log-query.md must mention every table and column — this immediately caught that ToolkitActionsLog was missing from the docs, now added Note: CLAUDE.md's log-query sync rule still lists web/frontend files, but web/ was removed from the tree in #193; the remaining manual sync point is the docs page, now covered by the drift test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Split node intercept modes into modules; fix command-handle leak intercept/mod.rs (1,147 lines) kept all four interception methods' platform code inline. The VPN mode implementation (Windows wintun + Linux TUN + cleanup) moves to intercept/vpn_mode.rs and the Linux TPROXY implementation to intercept/tproxy_mode.rs, as inherent-impl blocks on NodeInterceptManager. mod.rs (675 lines) retains the manager state, the enable/disable orchestration, and the Proxy/Hosts cleanups. Pure code movement; verified on Linux and Windows targets. Fix unbounded growth of the Lua COMMAND_HANDLES registry: anonymous (per-invocation) command handles were inserted with a fresh UUID key and never removed, leaking one entry per run_command call for the node process lifetime. A drop guard now removes anonymous entries on every exit path (success, timeout, reset, spawn error); named handles still persist so Lua can abort them across calls. Investigated and rejected two more audit findings: the proxy's domain set and the packet engine's IP set are not duplicated state (domains are fixed per intercept session; IPs are a DNS-rotation cache refreshed from the resolver by design), and the connectors' lifecycle methods already share the AgentSession trait with legitimately different bodies, so a shared CancellationContext would be forced abstraction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CLI: event-driven list refreshes, panic-free recon mouse handling Replace the publish-then-sleep(300ms)-then-read-cache pattern in the operations refresh helpers with awaitable list fetches: the client now exposes fetch_operation_definitions / fetch_operations / fetch_chain_definitions / fetch_chain_executions / fetch_chain_triggers built on the request() helper, with the list-response handlers firing the pending oneshot alongside the cache update. Refreshes now complete as soon as the service responds instead of guessing a delay; on timeout they fall back to the cached copy rather than an empty view. The now-unused fire-and-forget request variants are removed. refresh_execution_lists_after keeps its snapshot-compare loop — that one deliberately waits for service-side state to change, not just for a response. Replace the ten .unwrap() calls on self.nodes.recon in the recon mouse handler with let-else / if-let guards, removing the panic potential if the entry-guard invariant is ever broken. (A single guard binding at the top doesn't compile here: handle_recon_enter().await needs the borrow released mid-function.) Examined and declined three audit suggestions: the modal key-dispatch guard chain stays (explicit, commented, and the command palette's fall-through-to-input behavior doesn't fit a uniform modal stack); the chain-form hit map already flows out of render_chain_form's return value, with the RefCell serving only as the &App-compatible store; the EventHandler's spawned tasks already shut down via channel-close, which is the right lifecycle for a process-lifetime singleton. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Consolidate small CLI duplications into shared helpers - DEFAULT_RABBITMQ_URL was defined in both cli/src/config.rs and common/src/messaging.rs; the common constant is now pub and the CLI copy is gone. - The case-insensitive node-id-prefix lookup was implemented three times across cli/src/commands/{node,session,agent}.rs; it now lives once in commands/mod.rs as find_node/find_node_id. Examined and declined two cross-component audit suggestions: the three time-formatting helpers in cli/ui are distinct single-use functions (timestamp, duration, relative age), not duplicates; and cli's config module intentionally differs from common::FileConfig (BTreeMap for deterministic key order on save, format documented to mirror /etc/praxis/env), so unifying them would regress behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Delete dead code that is unused on Linux, macOS, and Windows Methodology: removed all 195 #[allow(dead_code)] attributes, collected dead_code diagnostics from cargo check --workspace --all-targets on both Linux and x86_64-pc-windows-gnu, and screened the intersection against code regions only macOS compiles (cfg(target_os = "macos") and the not(windows)+not(linux) fallbacks). Items dead on every platform were deleted; items dead on only one platform got their allowance restored per-item; ~60 attributes turned out to be stale (their items are used) and stay removed. 195 allowances -> 50, net -1,447 lines. Deleted across the workspace, including: the unused AgentSession trait surface (session_id/process_path/working_dir/mode/as_any and AgentMode) plus all connector impls; seven orphaned process utilities and the superseded semantic-parser prompt constants in node/utils; five unused DnsResolver methods (and the network-dependent test that existed only to exercise one of them); the legacy read_response/read_chunked_body HTTP helpers in the proxy; the PoolConfig struct and raw pool accessors in the service database layer; seven of eight RunningOperation fields (everything but cancel_tx) with their now-unused constructor parameter chain; the never-constructed ProtocolFilter and BodyMode UI variants with their render paths; eight unused theme constants; and the request_traffic_search client path end to end. Kept with reasons: DummyAgent and its module survive (re-exported and referenced by the connector factory for tests); graph.rs terminal_elements/get_output_count are used by tests and are now cfg(test); orchestrator's session task is now spawned as a detached statement since only the never-read JoinHandle field was removed. Verified: zero warnings on Linux and Windows targets, full test suite green except the pre-existing live-DNS test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Bump version to 0.9.30 * Release process: clarify patch-only version bump policy * Fix Windows installer for the irm|iex path (#218) Two bugs made the advertised one-liner (irm https://praxis.originhq.com/install.ps1 | iex) fail on Windows: 1. The [ValidateSet('docker')] attribute on $Service aborted with "The attribute cannot be added because variable Service with value would no longer be valid." Under Invoke-Expression the param() block is evaluated in the caller scope, which forces immediate validation of the empty default against the set. Replaced with manual validation in the body. 2. The script contained 255 non-ASCII characters (box-drawing banner, braille spinner, menu/status glyphs) and shipped without a BOM, so Windows PowerShell 5.1 -- the default Windows shell -- failed to parse it as ANSI, and the irm|iex pipe mis-decoded it too. Declared the glyphs as [char] code points, decode the banner from base64, and set the console output encoding to UTF-8. The source is now pure ASCII and parses on both Windows PowerShell 5.1 and PowerShell 7 while still rendering the glyphs. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Bump version to 0.9.31 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: add post-release back-merge step to release process The squash-merge into main and the automated AUR commits land only on main. Without merging main back into prerelease after each release, prerelease drifts behind main -- which produces misleading merge-base diffs on the next release PR and risks silently reverting main-only changes (such as hotfixes) when squash-merged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Fix Windows TUI launch crash, Docker CRLF breakage, and stale Cargo.lock (#220) * fix(cli): prevent TUI crash on Windows from keyboard enhancement flags crossterm 0.29 marks Push/PopKeyboardEnhancementFlags as unsupported on Windows (is_ansi_code_supported() == false), so the execute! macro always routes them to the erroring winapi impl regardless of ConPTY/VT support. This crashed the TUI at launch on every Windows console, including inside Windows Terminal, with "Keyboard progressive enhancement not implemented for the legacy Windows API." Gate the push and its matching pops on supports_keyboard_enhancement(), which is hardcoded to Ok(false) on Windows and genuinely queries the terminal on Unix. The TUI now launches cleanly on Windows with no behavioral change on other platforms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(docker): keep praxisctl LF line endings for Linux images The extensionless pkg/praxisctl/praxisctl script can be checked out with CRLF on Windows, leaving a `bash\r` shebang that fails to execute inside the Linux image. Pin it to LF via .gitattributes, and strip any stray CR in the Dockerfile before chmod as a defensive guard for existing clones. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: sync Cargo.lock to 0.9.31 The v0.9.31 release bumped the workspace crate versions in Cargo.toml but did not regenerate Cargo.lock, leaving the four praxis_* crates pinned at 0.9.30. A plain cargo build auto-heals this, but the source AUR package builds with `cargo build --frozen`, which hard-fails on the mismatch. Regenerate the lockfile so it matches Cargo.toml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(release): require regenerating Cargo.lock after version bump Add a release-process step to run cargo build / cargo update -w and commit the refreshed Cargo.lock after bumping the version, so the lockfile never lags Cargo.toml and the source AUR --frozen build keeps working. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(recon): hide console windows for spawned processes on Windows (#221) The node runs as a GUI-subsystem app in release (windows_subsystem = "windows"), so console children spawned without CREATE_NO_WINDOW allocate new terminal windows. During recon this flashed a burst of windows (MCP server discovery spawns node.exe per server). Add a shared crate::utils::silence_tokio_command helper and apply CREATE_NO_WINDOW at every Windows-reachable async spawn site (MCP discovery, ACP agent, praxis cmd runner), plus route the sqlite_query sqlite3 spawn through silent_command. Audited all node spawn sites; every remaining raw spawn is Linux-only (cfg-gated). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(orchestrator): resilient MCP startup, real errors, and session recovery (#222) Three related fixes to the Orchestrator session lifecycle plus retry-based recovery: - MCP restart race: McpServerManager skips redundant restarts on the same port and stop() waits for the socket to release before rebinding — fixes "Address already in use" leaving no server running after a burst of config-change events. - Real startup errors: create_session connects to MCP and loads tools synchronously and returns a Result, so failures surface on session/new with the actual reason instead of a dead session failing later with "channel closed". The CLI surfaces session-creation errors. - Lost-session recovery with backoff: prompts to a session the service no longer has (e.g. after a service restart) return ERR_ORCHESTRATOR_SESSION_NOT_FOUND (-32001); the TUI transparently recreates the session and resends the prompt, retrying with capped exponential backoff across the ~30-60s service-startup window before surfacing an error. Per-attempt MCP connect/list timeouts shortened 15s -> 8s. * Improve intercept TUI layout, navigation, and search (#223) * Improve intercept TUI layout, navigation, and match workflow Rework the intercept window with Traffic-first tab order, per-node status strip, enablement banner, and richer traffic/matches panes. Add agent columns, match flags, body mode cycling (pretty/raw/hex), follow-tail, server-side search, lazy body fetch for matches, cross-tab navigation, rule duplicate/quick-create, regex live preview, and fix filtered rule selection. * Address intercept TUI review follow-ups and add ^r re-summarize Unify / search across Traffic, Rules, and Matches with a shared filter bar and consistent regex semantics. Fix Matches PageUp/PageDown to scroll detail when focused, derive node/agent filters from registered nodes, compute tab click hit regions from rendered widths, and extract shared Tier-1 navigation hints. Add TrafficMatchResummarize API so ^r on Matches clears and re-runs summarization for the selected match. * Remove ^r match re-summarize feature Drop TrafficMatchResummarize API, service handler, DB helpers, and CLI wiring. Summaries are generated once on match; no manual re-run. * TUI: render-authoritative HitLayer mouse handling (#224) * Fix intercept TUI mouse hit-test alignment body_idx pointed at the hints row instead of tab content, breaking pane clicks, row selection, and split dragging. Extract shared layout helpers (chrome_layout, filter_split, table_row_at) so render and mouse handling use identical geometry. Handle Rules split-form and skip body hit-tests when the rule form overlay covers Traffic/Matches. * Align mouse hit-tests across all TUI windows Extract shared table/point helpers in ui::common. Fix status bar navigation (was missing connection dot and wrong label format). Fix Operations tab widths and split drag bounds; use margin-header row offset for list tables. Fix Nodes list rows and hint-bar regions. Fix Settings tab widths and content-area inset. Fix Log Query results table when row expanded. Rewrite Orchestrator mouse layout to match render (tabs, meta, dynamic input height). Intercept helpers moved to common for reuse. * Add render-authoritative HitLayer for TUI mouse handling Each render pass clears and repopulates a shared hit layer with clickable rects (tabs, hint chips, table rows, split borders). Mouse down events look up (col, row) and dispatch MouseAction instead of re-deriving layout math in per-window handlers. Removes tab_hit_regions, status_bar::hit_test, and nodes::hint_action_at. Overlay UIs (session chat, settings forms) keep bespoke handlers for now. * Extract overlay mouse dispatch into dedicated HitLayer modules Move popup/confirm/form/chain overlay actions out of app.rs and mouse.rs into mouse_overlay + overlay_hits so render-time hit registration and click dispatch stay paired and the main mouse router stays window-focused. * Fix HitLayer compile errors and align overlay hit geometry Popup/confirm/session/settings hit registration now uses the same modal_content_rect geometry as chrome::modal_panel so clicks land on the painted widgets. Fix PopupItem dispatch, session chat hint labels, and model-form field coordinates. Remove dead pre-HitLayer mouse handlers and add HitLayer unit tests. * Route all TUI mouse paths through HitLayer Migrate recon, intercept rule form, and chain op-picker to render-time hit registration. Remove bespoke handle_recon_mouse and dead hit_vertical_border/tab_at helpers so left-clicks only dispatch via the shared HitLayer. * fix(cli): stop shared client queues from stealing session replies (#225) Non-interactive CLI and --status were reusing the TUI's persistent client id, so multiple consumers sat on Client_<id>. RabbitMQ round-robined ACP responses (session/new hung on "connecting…") and queue_purge dropped in-flight replies for the other process. Use ephemeral client ids for one-shot commands, exclusive direct-queue consumers, skip purge when consumers already exist, and log dropped node ACP outbound frames. * TUI: full-height intercept, form modals, chain props modal (#226) Fix intercept chrome so tab content fills remaining height when the status banner is shown. Present new/edit intercept rules and new-op as centered form modals with shared form_modal chrome (multiline prompt, field nav, footer hits). Chain builder opens properties on double-click instead of a permanent properties strip. Domain forms live under operations/ and intercept/; popup stays generic overlays only. * Make TUI chain builder usable for real authoring (#227) * Make the TUI chain builder usable for real authoring Forgiving port/edge hits, full model surface (session groups, block config, memory mode), pickers and multiline prompts, auto-wire on add, keyboard selection and shortcuts, validation with dirty discard, elevated modal chrome with click-through fix, and docs that match the shipped TUI builder (Ctrl+Shift+N for new chain). * Use Ctrl+Alt+N (^!n) for new chain shortcut Restore Ctrl+Alt+N for Library new-chain; hint bar shows ^!n so it is distinct from ^n (new op). * Show scroll-down indicator when orchestrator/session is scrolled up (#228) Add a centered bold ▼ above the input when the transcript is scrolled away from the live bottom, so users can see that newer content is below. * TUI: hierarchical recon browser with expand/collapse and filter (#229) Redesign the recon overlay as a tree browser (Grok MCP modal style): MCP servers expand to tools, config groups by type, sessions by project. Adds filter bar, per-row mouse hits/hover, and keyboard expand/collapse. * fix(cli): rebind Intercept/Terminal keys; clean intercept and modal chrome (#230) Ctrl+I collides with Tab in classic terminals and under tmux, so the Intercept window never opened. Move Intercept to Ctrl+T and the node PTY terminal to Ctrl+Y (avoiding tmux's default Ctrl+B prefix). Drop keyboard-hint footers and green modal borders, and make Traffic filter pills clickable with a non-button showing count. * TUI: unify keybinding grammar, filter chrome, and pane drag (#231) Apply a shared shortcut grammar (bare = view, Ctrl = mutate), shared filter/hint/list-detail modules, consistent Esc ladders, and fix mouse split-drag hit order so dividers are resizable across windows. * feat(cli): open Log Query in $EDITOR; clarify query vs results (#232) Ctrl+E in the query editor suspends the TUI and opens the current query in $VISUAL/$EDITOR; a clean save replaces the in-app buffer. Style the query pane as an elevated input surface with a separator so it no longer blends into the results table. * feat: add model-driven documentation helper (#233) * feat(helper): add documentation helper agent overlay Adds a doc-seeded conversational assistant, summonable with Ctrl+H from any TUI window, that answers natural-language questions about using Praxis. It is deliberately separate from the orchestrator: read-only, no tools, and its own model feature assignment. Service: - New DocHelperManager streams answers via direct client messages (DocHelperPrompt/Cancel -> DocHelperChunk/Complete/Error), keyed by request_id with per-request cancellation. No ACP/orchestrator coupling. - Documentation sources (docs/src/**/*.md) are embedded at build time and chunked at heading level; a lightweight lexical ranker selects excerpts per question (no vector store). - New config keys llm_feature_doc_helper (falls back to the orchestrator model) and doc_helper_auto_context. CLI: - Summonable overlay with streaming, wrap-aware scrolling, and cancellation on close. Ctrl+H open/close, Ctrl+T toggle context, Ctrl+C stop, Esc close. Status-bar hint added. - Low-sensitivity screen context capture (window identity + safe counts only) fenced as untrusted in the prompt; sensitive surfaces are never collected. - Settings > LLM > Feature Assignments gains a Documentation Helper Model row. Docs: new usage/help-assistant.md page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(helper): give the doc helper search_docs/read_doc tools Turn the documentation helper from a single retrieval-seeded pass into a tool loop so it retrieves more context instead of refusing when the pre-selected excerpts fall short. Uses the same manual tool-call convention as the orchestrator. - Add search() and read_page() to the retrieval module: lexical section search returning ranked snippets, and path-normalized full-page reads. - search_docs / read_doc tool definitions; the model calls them to find and read pages on demand. - Tool loop with per-iteration cancellation, a MAX_TOOL_ITERATIONS cap, and hold-back streaming so raw tool JSON is never shown in the overlay. - Shrink the initial retrieval seed (the tools cover depth) and relax the system prompt to drive tool use and allow thorough answers, keeping the do-not-fabricate guardrail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(helper): wrap long input and match orchestrator spinner - The help overlay input now grows and wraps long questions across multiple rows (capped, then scrolls to keep the caret in view) instead of running off the edge of the overlay. - The waiting indicator now uses the shared animated spinner_char() the orchestrator uses, shown until the first token arrives; is_animating() includes the help overlay so the spinner refreshes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(helper): add request/response logging to the doc helper Log each doc-helper request (model + truncated prompt), the response length and hold-back state per tool-loop iteration, and any tool calls made. Aids diagnosis of the streaming/tool-loop path without dumping answer content into the journal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(helper): polish model-driven documentation assistance * fix(helper): handle direct tool arguments * fix(helper): require lookup acknowledgement --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix: declare named RabbitMQ queues as durable (#235) RabbitMQ 4.3 denies transient non-exclusive classic queues by default, which prevented broker boot against existing Praxis queue metadata and would fail new declares after a clean start. Share durable_queue_options() and use it for all named queues; leave exclusive broadcast queues alone. * feat(orchestrator): concurrent multi-tool calling per model turn (#236) Collect every tool-call JSON from a response, run independent tools concurrently with cancel-aware JoinSet, and pair start/result by stable tool_call_id so same-name concurrent tools and report_plan do not cross-wire in the TUI. * Harden intercept lifecycle: races, cleanup, bounded buffers, tests (#234) * fix(intercept): harden MITM lifecycle, clear epochs, and host cleanup Make intercept enable/disable lifecycle authoritative (CleanupRequired blocks re-enable even when is_enabled is stale), bound host OS commands and engine joins without detaching ownership, and scope clear generation to a correlated service instance so delayed A events cannot rebind or transplant epochs after B. Also deliver live traffic/matches on bounded paths, ServiceOnline re-registration with nonce correlation, cleanup_required status in the TUI, dirty rules snapshot retry, and docs for protocol/lifecycle/trigger-after-clear behavior. * fix(intercept): close registration races and cleanup false success Prevent stale overlapping services from overwriting accepted client state (skip mismatched StateUpdate; gate late direct updates; supersede in-flight ServiceOnline re-registration). Treat systemctl unset-environment non-zero exit as cleanup failure, dirty the rules snapshot before rule mutations, and check cancel between TPROXY host steps and per-IP installs. Also retain intercept status on the node registry for CLI status lines, harden process-group kill on host-command timeout, and tighten registration ack/retry correlation. * fix(intercept): bound TUI gen maps, harden enable/disable lifecycle CLI: - Prune live_entry_gens / live_match_gens when rows are evicted past the buffer caps, so the gen maps stay bounded by the buffers rather than by total traffic seen. Only drop a gen once no buffer still references it. - Only take the pending_toggles-tracked path when an event channel exists; otherwise fire-and-forget. Without a channel the InterceptToggleResult event never arrives, which would strand the node "pending" forever. Node: - Fail enable with rollback instead of panicking when required DNS resolution returns an empty address set while holding the manager lock. - Retain the CA handle for retry based on a typed flag, not failure-message text matching. - Recover saved proxy settings from write-ahead state on cleanup so a partial enable restores the user's original proxy instead of ProxyEnable=0. - Atomically replace the Windows hosts file via MoveFileExW so a crash mid-swap cannot truncate it; skip our own marked entries when scanning so crash residue does not block re-enable. - Treat a confirmed listener abort as a clean stop, not a failure. - Clarify process-reaper comment: the detached waiter is not leaked permanently unless the process is truly unkillable. - Add Win32_Storage_FileSystem feature for MoveFileExW. Service: - Enable SQLite foreign keys per-connection so ON DELETE CASCADE actually removes dependent traffic_matches rows. - Fabricate a minimal success status when a node deregisters between the enable command and its response, so the client gets an ok result instead of hanging until the reaper fires. - Always clear cleanup_required on a successful enable/disable command result so a stale flag from a prior failed op cannot re-stick on the client; add tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(intercept): cover gen-map bounding, status clearing, FK cascade Add regression tests for the intercept lifecycle fixes, and make the SQLite foreign_keys pragma robust to pool reconnects. CLI: - Tests that paused-buffer and match-buffer overflow prune live_entry_gens / live_match_gens for evicted rows, and that a generation is retained while a duplicate copy of the same id is still queued. Service (registries): - Async NodeRegistry tests that a successful enable clears a stale cleanup_required while preserving port/domains, and that a disable clears cleanup_required and the sparse fields. Service (database): - Set every per-connection SQLite PRAGMA (journal_mode, synchronous, busy_timeout, locking_mode, foreign_keys) via SqliteConnectOptions rather than one-shot queries against the pool. Per-connection settings must be re-applied on every reconnect; the previous one-shot foreign_keys pragma survived only because the pool is max_connections(1) and would silently revert to FK-off on any reconnect, re-breaking cascade deletes. - Tests that clear_all_traffic and delete_rule cascade-delete dependent traffic_matches rows (counted directly, since a JOIN would hide orphans). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(intercept): share rule matching across service and CLI Extract intercept-rule matching into common/src/intercept_match.rs so service ingest, snapshot rebuild, rule backfill, and the CLI rule-form preview all agree on which fields a pattern hits and how direction/scope filter an entry. Previously each site re-implemented field walking, which let a body-only regex (e.g. `(?i)system` against an Anthropic `<system-reminder>` body) match in one path but not another. - Add rule_matches_entry (regex + direction + scope) and pattern_matches_entry (pattern-only, for form preview while scope pickers are incomplete), with 12 unit tests covering direction gating, scope, headers, status, non-UTF-8 bodies, and the system-reminder regression. - Wire service rules_snapshot, rules backfill, and CLI intercept form preview to the shared matcher. - Cover backfill: body-only match, idempotency, disabled-rule skip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(doc-helper): address PR #233 review findings (#237) * fix(doc-helper): address review findings from PR #233 Follow-up fixes to the documentation helper feature, based on a multi-axis review of #233 plus an independent second opinion from codex on scope and implementation approach. - Handle output-limit truncation: a response cut off mid tool-call no longer streams raw JSON scaffolding to the operator, across all three providers' finish/stop-reason spellings. - Fix multi-turn history dropping the documentation answer: only the spoken lookup preamble was being replayed to the model, not the actual follow-up answer. - Revert the shared manual-tool-call parser to the documented strict {"tool","args"} contract used by the orchestrator, semantic-ops, and the Praxis node agent; the doc helper's own lenient variant tolerates the flattened-argument format models occasionally drift to. - Surface mid-stream SSE error envelopes from OpenAI-compatible providers instead of dropping them as unparseable, and stop discarding usage-only final chunks with no choices. - Add server-side logging for every doc-helper failure path, and capture/log token usage per iteration and per request. - Stop logging operator question content; log length only. - Restore a failed question into the input on error (only when the input is still empty) so Enter resends it. - Fix documentation path normalization order (slash/case before the "./" and ".md" strips), which previously failed to resolve Windows-style ".\..." paths and uppercase ".MD" extensions. - Add unit test coverage for the retrieval module (chunking, tokenizing, scoring, path normalization) and for the streaming loop's truncation classification, none of which had any tests. - Fix three miscalibrated existing tests: one asserted on prompt-file line-wrapping whitespace, one never exercised the UTF-8 boundary-walkback it claimed to cover, one asserted a negative that passed vacuously. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(doc-helper): stop suppressing the thinking spinner and re-searching for answered follow-ups Found during manual verification of PR #237's multi-turn history fix. - cli/src/ui/help.rs: awaiting_initial_text checked the whole message history for any prior non-empty assistant answer, so once turn one had an answer the "thinking" spinner never showed again on later questions (the separate FollowUp/lookup spinner has its own, correctly-scoped check, which is why it kept appearing). Scope the check to messages after the last User entry instead. - service/src/prompts/doc_helper.prompt: the "don't search if already answered" instruction was a weak trailing clause subordinate to the much stronger "search first" imperative in the same sentence, so the model re-searched for follow-ups whose answer was already in the conversation (e.g. asking "what's the IP" right after being given a command containing it). Move the condition into the main instruction and add a dedicated, concrete-example paragraph. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * feat: add Antigravity CLI connector (#238) * feat(connectors): add Antigravity CLI support * fix: scope agy recon workspaces * fix(orchestrator): fast /clear via shared MCP and ACP close/new (#239) * fix(orchestrator): make /clear instant and keep MCP sessions alive /clear now mints a new session id and wipes conversation history in place via _praxis/session/reset, keeping the live MCP connection and tools warm instead of close-then-recreate (which raced MCP teardown and timed out). Also guard against double session/new while create is in flight, increase MCP connect timeout with one retry, surface create errors without a live session, and disable rmcp's 5-minute idle keep-alive so long-lived orchestrator MCP sessions are not killed mid-conversation. * refactor(orchestrator): shared MCP + ACP-native close/new for /clear Replace the _praxis/session/reset extension with a process-wide shared MCP client on OrchestratorManager. session/close tears down conversation only; session/new reuses the warm MCP peer so /clear (close then new) is fast after the first connect. Remove dead multi-session helpers and the reset code path. * Bump version to 1.0.0 * docs: fill 1.0.0 release gaps for Help Assistant and Agy Document Antigravity in the agent list, surface Ctrl+H / Documentation Helper across CLI and config docs, and align recon overlay with the hierarchical TUI browser. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Tyler <83924806+tholms@users.noreply.github.com> Co-authored-by: tyler <tyler.holmwood@gmail.com>
1 parent a1cdf0a commit 0de3e90

160 files changed

Lines changed: 29595 additions & 8400 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitattributes‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# Ensure shell scripts always use LF line endings
22
*.sh text eol=lf
3+
4+
# Extensionless shell scripts that ship into Linux images/packages must
5+
# stay LF, otherwise a CRLF shebang (e.g. `bash\r`) breaks execution.
6+
pkg/praxisctl/praxisctl text eol=lf

‎CLAUDE.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@
9191
- **Do not initiate any release steps unless explicitly asked to.**
9292

9393
1. **Version bump**: Ensure the version is updated on the `prerelease` branch. Bump the last (patch) number only over the current `main` version, unless specifically asked otherwise.
94-
2. **Documentation**: Before creating the release PR, ensure all documentation in `docs/` is updated to reflect every change included in the release. Review all commits since the last release and update relevant doc pages.
95-
3. **Create PR**: Create a PR from `prerelease` into `main` with a clear, well-written description summarizing all changes since the last release.
96-
4. **Squash merge**: Squash-merge the PR into `main`.
97-
5. **GitHub release**: Create a GitHub release on `main` with an excellent changelog covering all notable changes, new features, fixes, and breaking changes.
98-
6. **Tag and push**: Create a version tag matching the version number set on the `prerelease` branch (e.g., `v0.10.0`), push the tag, and push the release.
99-
7. **AUR package**: Handled automatically — a GitHub Action updates the `originsec/praxis-aur` repo on each release.
94+
2. **Regenerate `Cargo.lock`**: After bumping the version, run `cargo build` (or `cargo update -w`) so the workspace crate versions in `Cargo.lock` match `Cargo.toml`, then commit the regenerated `Cargo.lock`. Skipping this leaves the lockfile a patch behind and breaks the source AUR build, which compiles with `cargo build --frozen` (fails on any lockfile mismatch).
95+
3. **Documentation**: Before creating the release PR, ensure all documentation in `docs/` is updated to reflect every change included in the release. Review all commits since the last release and update relevant doc pages.
96+
4. **Create PR**: Create a PR from `prerelease` into `main` with a clear, well-written description summarizing all changes since the last release.
97+
5. **Squash merge**: Squash-merge the PR into `main`.
98+
6. **GitHub release**: Create a GitHub release on `main` with an excellent changelog covering all notable changes, new features, fixes, and breaking changes.
99+
7. **Tag and push**: Create a version tag matching the version number set on the `prerelease` branch (e.g., `v0.10.0`), push the tag, and push the release.
100+
8. **AUR package**: Handled automatically — a GitHub Action updates the `originsec/praxis-aur` repo on each release.
101+
9. **Back-merge into `prerelease`**: After the release completes (and once the automated AUR commits have landed on `main`), merge `main` back into `prerelease`. The squash-merge from step 5 and the AUR auto-commits land only on `main`, so without this step `prerelease` drifts behind `main`. That drift makes the next release PR show a misleading merge-base diff and risks silently reverting `main`-only changes (e.g. hotfixes merged directly to `main`) when squash-merged.

‎Cargo.lock‎

Lines changed: 53 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ members = ["cli", "common", "node", "semantic_parser", "service"]
44
exclude = ["web"]
55

66
[workspace.package]
7-
version = "0.9.31"
7+
version = "1.0.0"
88
edition = "2024"
99
license = "Apache-2.0"
1010
repository = "https://github.com/originsec/praxis"

0 commit comments

Comments
 (0)