Repository navigation
Commit 0de3e90
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
- agents
- cli/src
- app
- intercept
- commands
- ui
- intercept
- log_query
- nodes
- operations
- recon
- settings
- common/src
- ai
- providers
- prompts
- docs/src
- architecture
- connectors
- getting-started
- reference
- usage
- node
- src
- acp_server
- acp
- agent_connectors
- lua
- praxis
- app
- handlers
- intercept
- proxy
- runtime
- utils
- tiny_c/src
- service
- src
- claude_bridge
- config
- database
- dispatch
- client
- doc_helper
- handlers
- intercept_targets
- prompts
- state
- tools/brainworm
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
0 commit comments