Commit graph

64 commits

Author SHA1 Message Date
Tom Boucher
cc4e3aa7b5 feat: add respectGitignoreInPicker setting for @ file picker (#979) (#1016)
Adds a new setting 'respectGitignoreInPicker' (default: true) that
controls whether the @ file picker respects .gitignore when listing
files. When set to false, gitignored files appear in fuzzy search
results.

Wired through:
- CombinedAutocompleteProvider: new constructor option + setter
- SettingsManager: getter/setter with persistence
- Settings selector UI: toggle in settings panel
- InteractiveMode: reads setting at init, updates provider on change
2026-03-17 18:03:02 -06:00
Jeremy McSpadden
0e0f47ef9f fix: failure recovery & resume safeguards (all 4 waves) (#956)
* fix: prevent data loss on crash with atomic writes, file locking, and error handling

Wave 1 of failure recovery safeguards:

1. Atomic session file rewrites (tmp+rename) — _rewriteFile() and forkFrom()
   now use atomicWriteFileSync to prevent session file corruption on crash
2. Atomic auto.lock writes — crash-recovery.ts writeLock() uses tmp+rename
   so the crash detection system itself can't be corrupted
3. unhandledRejection handler — catches silent process death from unhandled
   promise rejections in OAuth, extensions, LSP, or MCP connections
4. try/catch in emitToolCall — matches pattern used by emitUserBash,
   emitContext, and emitToolResult to prevent extension handler crashes
   from killing the entire agent turn
5. File locking on session appends — prevents concurrent pi instances from
   interleaving partial JSON lines in session JSONL files using the same
   proper-lockfile pattern established in auth-storage.ts and settings-manager.ts

* fix: add OAuth timeouts, RPC exit detection, and command context guards

Wave 2 of failure recovery safeguards:

1. OAuth fetch timeouts — all fetch() calls across all OAuth providers
   (Anthropic, OpenAI Codex, Google Antigravity, Google Gemini CLI,
   GitHub Copilot) now have 30-second AbortSignal.timeout() to prevent
   indefinite hangs when OAuth servers are unresponsive
2. RPC subprocess exit detection — pending requests are now rejected
   when the agent subprocess exits unexpectedly, preventing indefinite
   hangs in the RPC client
3. Extension command context guards — default handlers for newSession,
   fork, navigateTree, switchSession, and reload now throw explicit
   errors instead of silently returning success when called before
   bindCommandContext()
4. OAuth error detail preservation — token refresh errors now preserve
   the original error as `cause` for better diagnostics

* fix: resource cleanup, LSP retry, and crash detection on session resume

Wave 3 of failure recovery safeguards:

1. Atomic completed-units.json cleanup — milestone completion writes
   now use tmp+rename pattern for consistency with auto-recovery.ts
2. Bash temp file cleanup — track temp files created for large output
   and register a process exit handler to clean them up
3. Settings write queue flush on shutdown — call settingsManager.flush()
   during interactive mode shutdown so queued writes aren't lost
4. LSP initialization retry — wrap getOrCreateClient with up to 2 retries
   with exponential backoff (1s, 2s) for transient spawn failures
5. Crash detection on session resume — wasInterrupted() checks if last
   assistant turn had tool calls without results, shows warning on resume

* fix: blob garbage collection and LSP debug logging

Wave 4 of failure recovery safeguards:

1. Blob garbage collection — BlobStore.gc(referencedHashes) removes
   orphaned blobs not referenced by any session file, plus totalSize()
   for monitoring blob directory growth
2. LSP JSON parse error logging — malformed LSP messages are now logged
   at debug level (when DEBUG env is set) instead of being silently dropped
2026-03-17 16:03:49 -06:00
Tom Boucher
2306e6bb34 fix: LSP command resolution and ENOENT crash on Windows/MSYS (#901) (#925)
Two fixes:

1. lsp/config.ts: Use `where.exe` instead of `which` on Windows.
   MSYS's `which` returns POSIX paths (/c/Users/...) that Node's
   spawn() can't execute. `where.exe` returns native Windows paths.

2. lsp/client.ts: Handle spawn ENOENT error gracefully. When the LSP
   server binary doesn't exist, the error event now triggers a clean
   exit instead of bubbling up and crashing auto-mode.
2026-03-17 14:01:16 -06:00
Tom Boucher
3542b17c97 fix: normalize Windows paths in LLM-visible text to prevent bash failures (#874) (#884)
On Windows, process.cwd() returns backslash paths (C:\Users\name\...).
When these paths are injected into system prompts, worktree context
blocks, or tool results, the model copies them into bash commands.
Bash interprets backslashes as escape characters, silently stripping
them — producing invalid paths like 'C:Usersnamedevelopmentapp-name'.

This is not a regex hack — it's a proper cross-platform boundary:
- Filesystem operations (fs, path.join, spawn cwd) use native paths
  unchanged. Node handles both separators correctly for I/O.
- LLM-visible text (prompts, tool results, extension messages) uses
  toPosixPath() to normalize to forward slashes. C:/Users/name/...
  is valid in Git Bash, WSL bash, PowerShell, and Node.js.

Changes:

- utils/path-display.ts: New toPosixPath() utility in pi-coding-agent
  package (for system prompt) and shared extension module (for
  extensions that can't import from the compiled package at dev time)

- system-prompt.ts: Normalize resolvedCwd before injecting into the
  'Current working directory' line

- gsd/index.ts: Normalize all process.cwd() and originalBase paths in
  worktree context blocks injected into the system prompt

- bg-shell/index.ts: Normalize cwd in tool result text (start, env
  actions) that the model reads and may reference in commands

- path-display.test.ts: 9 regression tests covering toPosixPath
  behavior and system prompt output verification. Includes a scanner
  that fails if any Windows absolute paths with backslashes appear in
  buildSystemPrompt() output.

Audit scope: Checked all process.cwd() usage across pi-coding-agent
and all bundled extensions. Filesystem-only paths (join, readFile,
spawn cwd, existsSync) are correct and left unchanged. Only paths
entering LLM text are normalized.
2026-03-17 09:02:23 -06:00
Tom Boucher
62bbaa8e8e feat: integrate hashline edit mode into active workflow (#870) (#872) 2026-03-17 08:23:53 -06:00
Tom Boucher
0abc61987d fix: add Alt+V as clipboard image paste shortcut on macOS and document it (#852) (#854) 2026-03-17 08:19:13 -06:00
Tom Boucher
0873961550 fix: handle EPIPE in LSP sendNotification and wait for process exit on reload (#815) (#837) 2026-03-17 07:59:25 -06:00
TÂCHES
52848d7fd2 fix: raise maxDelayMs default from 60s to 300s (#756) (#773)
Anthropic rate limit reset windows are typically 60-120s. The previous 60s
default, combined with the +1s buffer in extractRetryAfterMs(), meant that
virtually all rate limit retries were immediately abandoned.

300s (5 min) covers the vast majority of rate limit windows and lets the
built-in retry logic work as intended.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:03:13 -06:00
Jeremy McSpadden
a3ff25c668 fix(bash): rewrite background commands to prevent pipe-open hang
Root cause: when the LLM runs `cmd &`, bash forks the process and
exits immediately. The forked process inherits Node's piped stdout/
stderr FDs. Node.js waits for all holders of those FDs to close before
firing the 'close' event — so the tool hangs until the background
process exits (which for a server is never).

Fix: add rewriteBackgroundCommand() in bash.ts. Before exec, detect
commands with a trailing & background operator and inject
>/dev/null 2>&1 before the & when stdout is not already redirected.
This severs the pipe inheritance so Node gets 'close' immediately
when the shell exits.

Guards:
- Commands already redirecting stdout (>, >>, &>, |) are not rewritten
- && (logical AND) is not affected
- & inside single-quoted strings is not affected
- A brief onUpdate advisory is surfaced when rewrite happens so the
  LLM knows to prefer nohup/setsid for robust detachment

Export rewriteBackgroundCommand from pi-coding-agent for testability.

Tests: bash-background.test.ts — 12 cases covering no-op paths,
rewrite paths, compound commands, and already-safe nohup patterns.
Closes #733
2026-03-16 18:03:01 -05:00
Jeremy McSpadden
2c926c12e3 fix: Phase 1 quick wins — bug fixes, security hardening, and performance
- Fix loadStoredEnvKeys divergent provider lists: add telegram_bot and
  custom-openai to wizard.ts (the canonical copy used by CLI), remove
  dead duplicate from onboarding.ts
- Security: add SAFE_COMMAND_PREFIXES allowlist to resolveConfigValue
  to prevent arbitrary RCE via settings.json shell commands
- Security: add TOFU (Trust On First Use) model for project-local
  extensions — skip untrusted .pi/extensions/ with stderr warning
- Performance: debounce sql.js MemoryStorage persistence (500ms window)
  so rapid mutations coalesce into a single db.export()+writeFileSync
- Fix double lstatSync call in tool-bootstrap.ts isRegularFile
- Add 26 new tests covering all changes
2026-03-16 13:18:02 -05:00
Tom Boucher
cdf42fe001 fix: prevent model config bleed between concurrent GSD instances (#650) (#652)
Two fixes for the model configuration bleeding between simultaneous
GSD instances that share the same global settings.json.

## Root Cause

1. `setDefaultModelAndProvider()` always persisted to `~/.gsd/agent/settings.json`
   (global), so when either instance's interactive mode changed models (via
   Ctrl+P or /model), it overwrote the other instance's saved default.

2. When auto-mode dispatched a new unit (after context wipe), if no
   per-unit-type model preference was configured, the session picked up
   the default from the now-contaminated global settings file.

## Fix 1: Project-scoped model persistence (settings-manager.ts)

`setDefaultModelAndProvider()`, `setDefaultModel()`, and `setDefaultProvider()`
now persist to project-level settings (`.pi/settings.json`) when a project
settings file exists, falling back to global only when no project context
is available. This prevents concurrent instances from overwriting each
other's model choice.

Added `hasProjectSettingsFile()` helper to detect project context.

## Fix 2: Auto-mode model capture (auto.ts)

Captures the session's model at auto-mode start (`autoModeStartModel`).
At each unit dispatch, if no model preference is configured for the unit
type, the captured model is re-applied with `persist: false`. This
ensures each auto-mode session maintains its own model regardless of
what other instances write to the shared settings file.

## Tests

3 new tests covering:
- Project settings file isolates model from global
- Two projects have independent model configs
- autoModeStartModel concept prevents model drift

All 448 existing tests pass.

Fixes #650
2026-03-16 10:59:12 -06:00
Tom Boucher
75e82a4236 fix(session): rebuild tools when cwd changes in newSession (#633) (#638)
Tools (write, read, edit, bash) capture cwd at creation time via
createWriteTool(cwd), createReadTool(cwd), etc. When auto-mode
enters a worktree, process.cwd() changes but tools were not
recreated — they continued resolving relative paths against the
original project root.

This caused artifacts to be written to the main project's .gsd/
directory instead of the worktree's .gsd/ directory. The dispatcher
then couldn't find the artifact at the expected worktree path and
retried the unit indefinitely.

Fix: detect cwd change in newSession() and call _buildRuntime()
to recreate tools with the updated cwd. This is a targeted rebuild
that only fires when cwd actually changed (typically once per
auto-mode session when entering/exiting a worktree).

Fixes #633
2026-03-16 09:23:19 -06:00
TÂCHES
fd29c02c81 feat(lsp): activate LSP by default, add call hierarchy/format/signature, sync edits (#639)
LSP was never activated in interactive sessions because the default
active tools list hardcoded only read/bash/edit/write. This adds lsp
to that list and ships four new capabilities alongside edit sync and
stronger prompt guidance.

- Add "lsp" to default active tools in agent-session.ts
- New actions: incoming_calls, outgoing_calls, format, signature
- Wire edit/write tools to notify LSP clients on file changes
- Strengthen system prompt and GSD prompt with full LSP operation catalog

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 09:22:52 -06:00
Gary Trakhman
1ea9163dea feat: add yaml support, run-hook command, and path sanitization (#637)
* feat: allow extensions to use 'yaml' and rework frontmatter parsing

* feat: add run-hook command for manual hook execution

* fix: sanitize slashes in unitType for runtime file paths
2026-03-16 09:22:23 -06:00
Flux Labs
9ed812ed54 feat: dynamic model discovery & provider management UX (#581) 2026-03-16 06:23:18 -06:00
Flux Labs
d35ae683f1 Fix #453 native hangs in GSD auto-mode paths (#502)
* fix: avoid native hangs in gsd auto paths

* fix: use .js extension in edit-diff.test.ts import for tsc compatibility

* fix: prevent OOM on large file diffs and implement context-line windowing

- Add size guard (MAX_DP_CELLS=4M) to buildLineDiff that falls back to a
  linear-time prefix/suffix matching algorithm for large files, preventing
  the O(n*m) DP table from causing OOM crashes
- Implement contextLines parameter in generateDiffString so only lines
  within N lines of a change are rendered (with "..." separators), matching
  unified diff behavior — the parameter was previously accepted but ignored
- Add tests for both context windowing and large-file fallback

---------

Co-authored-by: TÂCHES <afromanguy@me.com>
2026-03-15 22:22:58 -06:00
TÂCHES
7578292b6b fix: resolve TypeScript errors in GSD extension files (#571)
Add "success" to notify type union across ExtensionUIContext, interactive
mode, and RPC mode implementations. Fix null safety for readFileSync and
contextUsage.percent in auto.ts. Add discriminated union narrowing for
dispatch results. Add string type guards for select() return values in
commands.ts. Align ProviderErrorPauseUI notify signature. Simplify
AuthStorage return type.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:12:23 -06:00
Flux Labs
c20c57b941 feat: default to Opus 4.6 1M context variant (#565) 2026-03-15 18:57:46 -06:00
TÂCHES
8882ce484e fix(session): update cwd on newSession to reflect worktree chdir (#517)
When auto-mode creates a worktree and chdir's into it, the Node process
cwd changes but AgentSession._cwd stays frozen at the original path.
Every newSession() builds a system prompt telling the LLM "Current
working directory: /original/path", so the LLM cd's back there and
writes files to the wrong location.

Update _cwd = process.cwd() at the start of newSession() so the system
prompt reflects the actual working directory after chdir.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 15:45:15 -06:00
Flux Labs
e6d55f8aaf Perf/gsd startup speed (#497)
* docs: add startup performance analysis and optimization plan

Profiled GSD CLI startup finding 2.2s for --version and ~3.8s for
interactive mode. Identified 5 root causes with measured timings and
created a phased optimization plan targeting <0.2s for --version
and ~0.8s for interactive startup.

* perf: speed up GSD startup with lazy loading and fast paths

- Fast-path --version/-v and --help/-h in loader.ts before importing
  any heavy dependencies (2.2s → 0.15s, 14x faster)
- Lazy-load undici (~200ms) only when HTTP_PROXY env vars are set
- Skip initResources cpSync when managed-resources.json version
  matches current GSD version (~128ms saved per launch)
- Lazy-load Mistral SDK (~369ms) on first API call instead of startup
- Lazy-load Google GenAI SDK (~186ms) on first API call instead of
  startup
- Parallelize extension loading with Promise.all() instead of
  sequential for-loop

---------

Co-authored-by: TÂCHES <afromanguy@me.com>
2026-03-15 13:33:43 -06:00
Lex Christopherson
34cd1056ea feat(M003/S05): Self-healing git repair
Tasks:
- chore(M003/S05): auto-commit after complete-slice
- docs: tighten GSD system prompt tool-routing — prescriptive rules + anti-patterns
- chore(M003/S05/T02): auto-commit after execute-task
- chore(M003/S05/T01): auto-commit after execute-task
- chore(M003/S05): auto-commit after plan-slice
- docs(S05): add slice plan

Branch: gsd/M003/S05
2026-03-15 08:33:13 -06:00
Lex Christopherson
4ec30cdc2d chore(M003/S04): auto-commit after reassess-roadmap 2026-03-15 08:33:13 -06:00
Flux Labs
16364c7dba fix: prevent web_search tool injection for non-Anthropic providers serving Claude models (#444) (#446)
GitHub Copilot users with Claude models got 400 errors because the native
Anthropic web_search_20250305 tool was injected into requests to Copilot's
API proxy, which doesn't support it. The root cause was that model_select
never fires before the first API request on new sessions, so the fallback
heuristic (model name starts with "claude-") couldn't distinguish direct
Anthropic from proxied providers.

Fix: pass the resolved Model object through to the before_provider_request
event so extensions can check model.provider directly instead of relying on
model name heuristics.
2026-03-14 22:15:00 -06:00
TÂCHES
11234b7456 fix(agent-core): await event queue in tool hooks for safe parallel execution (#439)
Moves extension tool_call/tool_result interception from wrapToolsWithExtensions
(which fires inside the agent loop, bypassing event settlement) to
beforeToolCall/afterToolCall hooks that await _agentEventQueue. This ensures
extensions always see settled state — including the appended assistant message —
even when tools execute in parallel.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 21:16:17 -06:00
Flux Labs
ecf8125e39 feat: add Ollama Cloud as model and web tool provider (#430) (#434)
Add Ollama Cloud (ollama.com) as a built-in provider with both model
hosting and web search/fetch capabilities.

Model provider:
- 13 curated models via OpenAI-compatible API (Llama 3.1, Qwen 3,
  DeepSeek R1, Gemma 3, Mistral, Phi-4, GPT-OSS)
- Auth via OLLAMA_API_KEY environment variable
- Registered in onboarding, env hydration, and model resolver

Web tool provider:
- Search via POST ollama.com/api/web_search
- Page fetch via POST ollama.com/api/web_fetch (fallback after Jina)
- Added as third search provider option alongside Tavily and Brave
- /search-provider command updated with ollama option

Closes #430
2026-03-14 21:03:31 -06:00
TÂCHES
4334f3a27f Merge pull request #415 from fluxlabs/perf/repo-hotpath-optimizations 2026-03-14 15:58:04 -06:00
Flux Labs
f981d5aa79 perf: optimize discovery and interactive hot paths 2026-03-14 16:03:44 -05:00
Flux Labs
adca6901ec feat: add cross-provider fallback when rate/quota limits are hit (#125)
When all credentials for a provider are exhausted, the system now
automatically falls back to the next available provider in a
user-configured fallback chain. Higher-priority providers are
restored automatically when their backoff expires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:45:44 -05:00
Kassie Povinelli
c3ceb077d9 feat: add alibaba-coding-plan provider support (#295) 2026-03-14 09:09:54 -06:00
Flux Labs
f24f63f290 fix: add retry logic for transient network/auth failures instead of crashing (#365) 2026-03-14 08:50:35 -06:00
TÂCHES
9317816aa2 fix: prevent credential backoff on transport errors and handle quota exhaustion gracefully (#353)
Fixes #349, #339

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 07:15:00 -06:00
Copilot
31c03b6caf Fix crash on quota exhaustion for OAuth-backed providers (antigravity/Gemini) (#347)
* Initial plan

* Fix crash after antigravity quota exhaustion: catch exceptions in runLoop, avoid futile retries on quota_exhausted, better error message

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TÂCHES <afromanguy@me.com>
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
2026-03-14 07:03:13 -06:00
Lex Christopherson
ca8697ae26 feat: use server-requested retry delay for Anthropic rate limits
Anthropic's 429 responses include retry-after and x-ratelimit-reset-*
headers that tell us exactly when to retry. Previously we ignored these
and used exponential backoff (2s, 4s, 8s), which is both wrong and
misleading in the UI countdown.

- Add retryAfterMs to AssistantMessage as the structured carrier
- Extract retry-after / x-ratelimit-reset-requests / x-ratelimit-reset-tokens
  from Anthropic SDK APIError.headers in the provider catch block
- Session uses retryAfterMs when present (capped by maxDelayMs=60s),
  falls back to exponential backoff for errors with no timing hint

The UI countdown now shows the actual Anthropic reset time. No UI changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:51:17 -06:00
TÂCHES
b730ed87d0 feat: native Rust xxHash32 for hashline (#272)
* feat: replace pure-JS xxHash32 with native Rust implementation via napi

The hashline edit tool calls xxHash32 on every line of every file read/edit.
Moving this to a native Rust implementation (xxhash-rust crate) eliminates
JS overhead for this hot path. Hash output is identical -- verified by tests
comparing native vs JS reference across 11 input vectors including empty
strings, short/long inputs, unicode, and seeded variants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use typed native interface and remove version-drag comment in xxhash wrapper

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:46:08 -06:00
TÂCHES
8ac5a82409 feat: memory extraction pipeline (#261)
* feat: add memory extraction pipeline extension

Two-phase pipeline that extracts durable knowledge from session transcripts
and consolidates into project-scoped memory artifacts injected into future
sessions via system prompt.

- MemorySettings in settings-manager (disabled by default)
- SQLite storage with lease-based job queue (better-sqlite3)
- Phase 1: scan .jsonl sessions, extract knowledge via LLM
- Phase 2: consolidate extractions into MEMORY.md
- /memory command: view, clear, rebuild, stats
- Secret redaction on all stored output
- Watermark tracking to skip unchanged sessions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: harden memory extraction pipeline security and performance

- Expand secret redaction patterns (Stripe, JWT, PEM, npm, Anthropic, OpenAI keys)
- Stream-read session file headers instead of loading entire file for cwd check
- Add 50MB file size cap to prevent OOM with concurrent extraction workers
- Delete orphaned prompt .md files (prompts are inlined in pipeline.ts)
- Reset package-lock.json to current main to fix version drift

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 16:41:13 -06:00
TÂCHES
8a5465d901 feat: native Rust bash stream processor for single-pass chunk processing (#271)
Replaces the multi-pass JS pipeline (TextDecoder → stripAnsi → sanitizeBinaryOutput)
in bash-executor.ts with a single native Rust call that handles UTF-8 decoding,
ANSI stripping, binary sanitization, and CR removal in one pass.

Key features:
- StreamState tracks incomplete UTF-8 and ANSI sequences across chunk boundaries
- Standalone stripAnsiNative() and sanitizeBinaryOutputNative() for use elsewhere
- Comprehensive test coverage for split multibyte, split ANSI, binary data

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:34:17 -06:00
Juan Francisco Lebrero
5ff362ed0e feat: add claude-opus-4-6[1m] model with 1M context window (#288)
Add the 1M context variant of Claude Opus 4.6 to the model registry
and fix model resolver to try exact match before glob detection, so
model IDs containing bracket characters (like [1m]) are not
misinterpreted as glob patterns.
2026-03-13 16:25:45 -06:00
TÂCHES
54df619891 feat: task isolation for subagent filesystem safety (#254)
* feat: add task isolation for subagent filesystem safety

Subagents can run in isolated git worktrees (or FUSE overlays on Linux)
so concurrent tasks don't stomp on each other's files. Changes are
captured as unified diffs and merged back via git apply.

- New isolation.ts module with worktree and FUSE overlay backends
- TaskIsolationSettings in settings-manager (mode + merge strategy)
- isolated parameter on the subagent tool schema
- Baseline capture/apply mirrors the parent repo's dirty state
- Process exit handler for best-effort cleanup of stale worktrees

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct delta capture to exclude parent baseline state

The worktree backend now commits a baseline snapshot after applying the
parent's dirty state, so captureDeltaPatch diffs only the subagent's
actual changes against the post-baseline HEAD (not the original HEAD).

The FUSE overlay backend tracks the parent's dirty file set at mount
time and filters the upper dir during delta capture to exclude inherited
dirty files.

Also removes dead code: findGitRoot (unused), readIsolationMergeStrategy
(exported but never called).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 16:10:55 -06:00
Lex Christopherson
fdeb520332 fix: restore bashInterceptor settings dropped by async-jobs merge
The async-jobs PR (#260) accidentally dropped `bashInterceptor` from the
Settings interface and the getBashInterceptorEnabled/getBashInterceptorRules
methods from SettingsManager, breaking the TypeScript build on main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:06:21 -06:00
TÂCHES
fa9477f638 feat: async background jobs extension (#260) 2026-03-13 16:01:30 -06:00
TÂCHES
2452d34f53 Merge pull request #255 from gsd-build/feat/multi-credential
feat: multi-credential round-robin with rate-limit fallback
2026-03-13 15:49:57 -06:00
Lex Christopherson
e9676202e1 fix: add tests and clarify edge cases for multi-credential auth storage
- Add 14 tests covering round-robin, session-sticky, login accumulation,
  backoff/fallback, and getAll() truncation behavior
- Document getAll() truncation is intentional (OAuth refresh only)
- Add comment in markUsageLimitReached explaining round-robin race
  is benign in single-threaded event loop context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:49:44 -06:00
Lex Christopherson
e55b6dd994 fix: bash interceptor regex bugs and add unit tests
- Fix cat rule to exclude heredoc syntax (cat <<EOF) via negative lookahead
- Fix write rule: exclude >> append and digit-prefixed fd redirects (2>)
  using lookbehind (?<![|>\d])>(?!>)
- Add compileInterceptor() — pre-compiles rules once at construction time
  instead of on every bash call; export CompiledInterceptor type
- Update createBashTool to use pre-compiled interceptor instance
- Add 33 unit tests covering all rules, edge cases, and pass-throughs
2026-03-13 15:46:08 -06:00
Lex Christopherson
d0f84d9a38 feat: add bash interceptor to block commands that duplicate dedicated tools
Regex-based pre-execution check in the bash tool blocks shell commands
(grep, cat, sed -i, etc.) when the dedicated replacement tool is available
in the session. Configurable via bashInterceptor settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 14:50:10 -06:00
Lex Christopherson
4b6a43c2b3 feat: multi-credential round-robin with rate-limit fallback
Support multiple API keys per provider with automatic rotation:
- AuthStorageData accepts single credential or array per provider
- Round-robin selection across credentials (no sessionId)
- Session-sticky hashing when sessionId is provided
- Credential backoff on rate limits (30s), quota exhaustion (30min),
  server errors (20s)
- markUsageLimitReached() backs off failing credential and returns
  whether an alternate is available
- Login accumulation: duplicate provider logins append API keys
  instead of replacing
- Agent retry handler tries credential fallback before counting
  against retry budget (immediate retry, no delay)
- All getApiKey call sites thread sessionId for sticky selection

Backward compatible: single credentials work unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 14:45:35 -06:00
Lex Christopherson
4c97d59536 feat: add native Rust diff engine for edit tool
Move the edit tool's hot-path diffing operations from JS to native Rust:
- `normalizeForFuzzyMatch`: single-pass Unicode normalization (smart quotes,
  dashes, special spaces, trailing whitespace)
- `fuzzyFindText`: exact-then-fuzzy substring search with UTF-16 index
  conversion for JS compatibility
- `generateDiff`: unified diff generation using the `similar` crate
  (Myers' algorithm with optimizations)

The Rust module at `native/crates/engine/src/diff.rs` exposes three napi
functions. The TypeScript wrapper at `packages/native/src/diff/` follows
the existing module pattern. `edit-diff.ts` now delegates to native
implementations while keeping line-ending handling and file I/O in JS.

18 tests covering normalization, fuzzy matching (including UTF-16 index
correctness with emoji/surrogate pairs), and diff generation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 14:11:40 -06:00
Lex Christopherson
d5b7ecb58c fix: resolve TypeScript build errors in glob callback type and hashline test import 2026-03-13 13:47:33 -06:00
Lex Christopherson
a685c7f987 feat(find): replace glob npm package + fd with native Rust glob
The find tool's default path spawned `fd` and used the `glob` npm package
to discover nested .gitignore files. The native @gsd/native glob module
handles gitignore traversal natively via Rust's `ignore` crate, making
both dependencies unnecessary for this code path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 13:16:42 -06:00
TÂCHES
0d390688e3 fix: prevent move operation from silently overwriting existing files (#219) (#223)
Check if the destination file exists before performing a move in
hashline-edit. If it does, return an error instead of silently
overwriting the file.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:27:39 -06:00
TÂCHES
f7b3144291 fix: separate access/unlink error handling in delete path (#219) (#222)
The delete operation in hashline-edit.ts wrapped both access() and
unlink() in a single try/catch. If access succeeded but unlink failed
(e.g., permissions), the error was silently swallowed and "Deleted" was
falsely reported. Now access and unlink have separate error handling:
access failures indicate the file doesn't exist, while unlink failures
propagate to the caller.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:27:28 -06:00