Commit graph

339 commits

Author SHA1 Message Date
TÂCHES
41dda26b9a Merge pull request #2748 from gsd-build/fix/2743-web-search-duplicate-rendering
fix: Remove premature pendingTools.delete causing web_search duplicate rendering
2026-03-26 16:08:39 -06:00
Matt Haynes
c557aea8de fix(windows): prevent EINVAL by disabling detached process groups on Win32 (#2744)
On Windows, `spawn()` with `detached: true` sets the
CREATE_NEW_PROCESS_GROUP flag in CreateProcess. In certain terminal
contexts — notably VSCode's integrated terminal (ConPTY), Windows
Terminal, and some MSYS2/Git Bash configurations — this flag conflicts
with the parent process group hierarchy and causes a synchronous EINVAL
from libuv, making *every* bash/async_bash/bg_shell command fail
immediately with `spawn EINVAL`.

The bg-shell extension already guards against this with
`detached: process.platform !== "win32"` (process-manager.ts:109),
but three other spawn sites were missed:

- `packages/pi-coding-agent/src/core/tools/bash.ts` (bash tool)
- `packages/pi-coding-agent/src/core/bash-executor.ts` (RPC executor)
- `src/resources/extensions/async-jobs/async-bash-tool.ts` (async_bash)

This commit aligns all spawn sites with the bg-shell pattern.

Additionally fixes two related issues:

1. `killProcessTree()` in shell.ts used `detached: true` on its own
   `taskkill` spawn call — unnecessary and potentially problematic
   in the same terminal contexts. Removed.

2. `killTree()` in async-bash-tool.ts used Unix-only
   `process.kill(-pid)` with no Windows fallback. On Windows, negative
   PIDs (process group kill) are not supported, so orphaned child
   processes could survive timeout kills. Now uses `taskkill /F /T`
   on Windows, matching the bg-shell and shell.ts implementations.

Includes a regression test that statically verifies no spawn site
uses unconditional `detached: true`, plus a smoke test confirming
the platform-guarded pattern works on all platforms.

Reproduction: Run GSD v2.42-v2.51 inside VSCode on Windows 11 with
Git Bash as the shell. Any bash tool call fails with `spawn EINVAL`.
The error is 100% reproducible and affects all shell operations
(bash, async_bash, bg_shell start).

Co-authored-by: Matt Haynes <matt@auroraventures.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 16:08:03 -06:00
Lex Christopherson
ef310574da fix: Remove premature pendingTools.delete in webSearchResult handler (#2743)
The webSearchResult branch deleted entries from pendingTools after rendering,
which removed the duplicate-prevention guard. Subsequent streaming tokens
re-iterated content blocks, re-created the serverToolUse component, and
re-rendered the search result — producing 18+ duplicate blocks.

The message_end handler already calls pendingTools.clear(), so the explicit
deletes were unnecessary and harmful.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:03:07 -06:00
Lex Christopherson
c5b38d69e3 feat: Wire --bare mode across headless → pi-coding-agent → resource-loa…
- "src/headless.ts"
- "packages/pi-coding-agent/src/cli/args.ts"
- "packages/pi-coding-agent/src/main.ts"
- "src/tests/headless-cli-surface.test.ts"

GSD-Task: S02/T02
2026-03-26 11:39:25 -06:00
Lex Christopherson
4d218353ac test: Added 61 tests across 9 suites covering JSONL utilities, v2 type…
- "packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts"

GSD-Task: S01/T03
2026-03-26 11:12:04 -06:00
Lex Christopherson
c5bc9208c4 feat: Added runId generation on prompt/steer/follow_up commands, event…
- "packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts"
- "packages/pi-coding-agent/src/modes/rpc/rpc-client.ts"
- "packages/pi-coding-agent/src/modes/rpc/rpc-types.ts"

GSD-Task: S01/T02
2026-03-26 11:05:32 -06:00
Lex Christopherson
01e37670e1 feat: Added RPC protocol v2 types, init handshake with version detectio…
- "packages/pi-coding-agent/src/modes/rpc/rpc-types.ts"
- "packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts"
- "packages/pi-coding-agent/src/modes/rpc/rpc-client.ts"
- "packages/pi-coding-agent/src/modes/index.ts"
- "packages/pi-coding-agent/src/index.ts"

GSD-Task: S01/T01
2026-03-26 11:01:58 -06:00
github-actions[bot]
913984c26e release: v2.51.0 2026-03-26 15:58:14 +00:00
madjack
ab9bae397d feat: add /terminal slash command for direct shell execution (#2349)
Runs commands in the user's login shell ($SHELL -l -c) so PATH additions
and env vars from shell profiles (.zprofile/.profile) are available.
Shell aliases are intentionally not loaded (requires -i which causes
startup noise and job control side effects).

Implementation spawns $SHELL directly via a loginShell flag threaded
through the bash executor — no double-shell wrapping.

- Registered as builtin slash command with autocomplete
- Reuses existing bash execution pipeline (streaming, session recording)
- Output included in LLM context for agent reference
- Added loginShell option to executeBash and handleBashCommand
- Browser mode rejects /terminal (terminal-only command)
- Updated web-command-parity-contract tests

AI-assisted: This change was authored with Claude (AI pair programming).
2026-03-26 09:41:37 -06:00
DavidMei
89988bf610 fix: improve light theme warning contrast (#2674) 2026-03-26 09:40:51 -06:00
Andrew
815be0a698 feat: managed RTK integration with opt-in preference and web UI toggle (#2620)
* feat: integrate managed RTK across shell workflows

* fix(rtk): unify managed fallback and live savings wiring

* fix(rtk): improve TUI status visibility

* fix(tests): make portability tests independent of pi-coding-agent dist build

The CI portability test runs don't guarantee that
packages/pi-coding-agent has been compiled. Any test that
imported files pulling in @gsd/pi-coding-agent (resource-loader,
preferences-skills, async-bash-tool, etc.) crashed with
ERR_MODULE_NOT_FOUND pointing at dist/index.js.

Two changes to dist-redirect.mjs (the Node ESM loader hook used by
all unit tests):
- Redirect the bare @gsd/pi-coding-agent specifier to the workspace
  source entrypoint (src/index.ts) so no dist/ artifact is needed.
- Extend the load() hook to transpile *.ts files under
  packages/pi-coding-agent/src/ through TypeScript's transpileModule.
  Node's --experimental-strip-types can't handle parameter properties
  and similar syntax present in that package's source; full transpilation
  avoids the ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX crash.

Also fix the dashboard.tsx responsive grid:
- xl:grid-cols-5 → xl:grid-cols-4 2xl:grid-cols-5
  (5 metric cards no longer fit at xl without overflow; test contract
  expected xl:grid-cols-4)
- Keep loading-skeletons.tsx in sync with the same breakpoints.

Add src/tests/resolve-ts-loader.test.ts to guard the loader behaviour:
- bare @gsd/pi-coding-agent redirect points to workspace source
- direct source-entry rewrite (.js → .ts)
- transpilation removes TS parameter property syntax that strip-only
  mode cannot parse

* fix(tests): redirect all workspace package imports to source in portability tests

The previous fix only redirected @gsd/pi-coding-agent to its
source entrypoint. In CI, pi-coding-agent/src itself imports
@gsd/pi-ai (and other workspace packages) which were still pointing
at dist/. Since no workspace dist is built during the portability
test run, any transitive resolution hit the same ERR_MODULE_NOT_FOUND.

Changes to dist-redirect.mjs:
- Redirect @gsd/pi-ai, @gsd/pi-ai/oauth, @gsd/pi-agent-core, and
  @gsd/pi-tui bare imports to their workspace src/ entrypoints.
- Broaden the load() transpilation condition from
  '/packages/pi-coding-agent/src/' to '/packages/*/src/' so that
  all workspace source files are run through TypeScript's
  transpileModule, handling parameter properties and other syntax
  that Node's strip-only mode rejects.

Verified by hiding all four workspace dist/ directories locally and
running the failing test set — 96/96 pass.

* fix(tests): redirect @gsd/native sub-paths; fix Windows .cmd spawnSync

Two more portability failures after the previous fix:

1. @gsd/native sub-path imports (@gsd/native/fd, @gsd/native/text, etc.)
   were not redirected — the loader only handled the bare specifier.
   Added a prefix-match redirect for @gsd/native/* → packages/native/src/<sub>/index.ts.

2. Windows RTK tests failed because createFakeRtk produces a .cmd wrapper
   on Windows, and spawnSync(binaryPath, [...]) without shell:true silently
   returns non-zero when the binary is a .cmd file.
   Added shell: /\.(cmd|bat)$/i.test(binaryPath) to the spawnSync calls in:
   - src/resources/extensions/shared/rtk.ts (rewriteCommandWithRtk)
   - src/resources/extensions/shared/rtk-session-stats.ts (readCurrentRtkGainSummary)
   - packages/pi-coding-agent/src/utils/rtk.ts (rewriteCommandForGsd)
   Production use of rtk.exe is unaffected; the shell flag is only true for
   .cmd/.bat paths.

Verified: all 93 portability tests pass with all workspace dist/ directories
removed (simulating CI portability environment).

* fix(tests): Windows portability fixes — HOME env, managed RTK path, perf threshold

Four Windows-specific failures fixed:

1. app-smoke.test.ts: process.env.HOME is undefined on Windows (uses
   USERPROFILE instead). Changed to homedir() from node:os which works
   cross-platform.

2. Managed RTK path tests on Windows: tests placed a fake RTK as rtk.exe
   (by copying a .cmd script into a .exe filename), which Windows cannot
   execute. Two-part fix:
   - resolveRtkBinaryPath() in both rtk.ts files now falls back to rtk.cmd
     in the managed dir on Windows when rtk.exe is absent.
   - withManagedFakeRtk and equivalent patterns in rtk.test.ts,
     rtk-session-stats.test.ts, rtk-execution-seams.test.ts changed to
     place the fake at rtk.cmd instead of rtk.exe on Windows.

3. bg_shell RTK test on Windows: requires bash (for shell sessions), which
   is not available on the blacksmith-4vcpu-windows-2025 runner without
   Git Bash installed. Test now skips on win32.

4. derive-state-db perf assertion: 10ms threshold was too tight for Windows
   CI runners (measured 12ms under load). Raised to 25ms — still catches
   real regressions (baseline is 3ms locally and ~12ms on stressed runners).

* fix(tests): fix managed RTK path fallback on Windows in src/rtk.ts + fix copyable fake

Two remaining Windows failures:

1. src/rtk.ts was never patched with the rtk.cmd managed-dir fallback
   (only the shared/rtk.ts and pi-coding-agent/src/utils/rtk.ts were updated).
   Added the same rtk.cmd fallback and shell:.cmd detection to src/rtk.ts,
   which is what rtk.test.ts imports from.

2. createFakeRtk on Windows wrote '%~dp0\fake-rtk.js' in the .cmd content —
   this resolves relative to the .cmd file's own directory. When the test
   copies rtk.cmd to a different managed dir, %~dp0 resolves to the copy
   destination where fake-rtk.js does not exist. Fixed by embedding the
   absolute path to fake-rtk.js directly in the .cmd content so the fake
   works correctly regardless of where the .cmd is copied.

* feat(experimental): add RTK opt-in preference with web UI toggle

- Add `experimental` category to GSDPreferences with `rtk: boolean` (default: false)
- RTK is now opt-in: disabled by default for all projects unless explicitly enabled
- Validate experimental.* keys; unknown experimental keys produce warnings

Web UI:
- Add ExperimentalPanel component with animated toggle switch per flag
- Add /api/experimental route (GET/PATCH) to read/write flags in preferences.md
- Add 'Experimental' tab to settings dialog sidebar nav (FlaskConical icon)
- Include ExperimentalPanel at bottom of gsd-prefs mega-scroll
- Fix toggle disabled state: trigger loadSettingsData for 'experimental' section
  and self-fetch on mount when data is absent

Dashboard:
- Gate RTK Saved metric card on rtkEnabled from live auto state (web)
- Gate TUI dashboard RTK savings row on rtkEnabled
- Gate TUI footer RTK status updates on experimental.rtk preference
- Propagate rtkEnabled through AutoDashboardData → bridge-service → store

Build:
- Add scripts/build-if-stale.cjs: incremental build driver that skips each
  step (packages, root tsc, copy-resources, web) when output is newer than
  source; replaces full rebuild chain in gsd:web
- Add scripts/web-stop.cjs: robust stop with registry + legacy PID + orphan
  sweep via pgrep; handles crash/restart orphaned next-server processes
- gsd:web now uses build-if-stale.cjs (fast cold starts, instant when unchanged)
- gsd:web:stop / gsd:web:stop:all use web-stop.cjs directly

Fix: correct import path in rtk-status.ts (./preferences.js not ../preferences.js)

* fix: restore em-dash encoding in package.json to match upstream

* refactor(rtk): move command rewrite out of pi-coding-agent into GSD extension

Per review feedback from igouss: pi-coding-agent should not be modified to add
GSD-specific logic. Instead, add a proper extension point and wire RTK through it.

Changes to packages/pi-coding-agent (extension API only — no RTK logic):
- Add BashTransformEvent + BashTransformEventResult types to extension API
- Add on('bash_transform') overload to ExtensionAPI interface
- Add emitBashTransform() to ExtensionRunner (chains all handlers in order)
- Call emitBashTransform() in wrapToolWithExtensions before bash tool execution
- Export new types from extensions/index.ts and package index.ts
- Revert all RTK-specific changes from bash-executor.ts, tools/bash.ts
- Remove packages/pi-coding-agent/src/utils/rtk.ts entirely

Changes to GSD extension:
- Register bash_transform handler in register-hooks.ts that calls
  rewriteCommandWithRtk() from the existing shared/rtk.ts module
- Handler is a no-op when RTK is disabled or not installed

* fix: correct import path for shared/rtk.js in register-hooks

* fix(tests): remove deleted pi-coding-agent/utils/rtk imports from execution seams test

The RTK rewrite logic was moved out of pi-coding-agent into the GSD
extension (bash_transform hook). Tests that directly imported the
deleted utils/rtk.ts are removed; remaining tests verify the shared
RTK module and GSD-layer surfaces that still call rewriteCommandWithRtk.
2026-03-26 09:33:07 -06:00
Jeremy McSpadden
f8c6ab0c54 chore: consolidate docs, remove stale artifacts, and repo hygiene (#2665)
* fix(vscode): add extensionKind and error handler for Remote SSH support

* fix(vscode): reject failed RPC startup

* docs: consolidate docs, remove stale artifacts, and repo hygiene

- Remove docs-internal/ (duplicate of docs/); update pr-risk-check.mjs path
- Sync 9 diverged files to latest content (commands, config, troubleshooting, etc.)
- Fix pi --web → gsd --web naming in docs/README.md
- Copy FRONTIER-TECHNIQUES.md and ADR-004 to docs/ before removal
- Delete orphaned PR screenshot folders (pr-876/, pr-1530/) — unreferenced
- Remove committed pnpm-lock.yaml files (project uses npm)
- Move PLAN.md → .plans/doctor-cleanup-consolidation.md
- Move web/left-native-tui-main-session-plan.md → .plans/
- Delete .DS_Store and vscode-extension/dist/ from disk

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:13:41 -06:00
mastertyko
43b1de6d59 fix: signal malformed tool arguments in toolcall_end event (#2647)
When the API stream is truncated mid-tool-call, PartialMessageBuilder
emits a toolcall_end event with { _raw: "<broken json>" } in the
arguments — but the event looks identical to a healthy tool completion.
Downstream consumers (error classifiers, tool handlers, activity log)
have no way to distinguish a truncated call from a completed one.

Add a malformedArguments: boolean flag to the toolcall_end event variant
in AssistantMessageEvent. The flag is set to true only in the JSON parse
catch path, so existing consumers (which do not check for it) are
unaffected. New consumers like classifyProviderError can use it to
handle truncated tool calls appropriately.

Closes #2574
2026-03-26 08:15:16 -06:00
TÂCHES
dc378242bd Merge pull request #2624 from gsd-build/feat/ecosystem-skills-directory-merge
feat(skills): use ~/.agents/skills/ as primary skills directory with curated catalog
2026-03-26 00:01:11 -06:00
github-actions[bot]
50c7bd3af5 release: v2.50.0 2026-03-26 05:40:44 +00:00
Lex Christopherson
91ec77291a merge: resolve conflicts with origin/main for PR #2008
Merge main's userSubdirs guard pattern with ecosystem skills directory
migration logic. Keep both detection.ts entry sets (PR's expanded markers
+ main's .NET/Xcode/Docker entries). Preserve PR's skills test assertion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:36:37 -06:00
TÂCHES
cb2185fe70 Merge pull request #2059 from TheReaperJay/feature/login-cancel-no-crash-pr
fix(pi-coding-agent): prevent crash when login is cancelled
2026-03-25 22:15:51 -06:00
TÂCHES
19addd2aa5 Merge pull request #2168 from frizynn/fix/ai-providers-memory-leaks
fix(ai): resolve WebSocket listener leaks and bound session cache
2026-03-25 22:15:32 -06:00
TÂCHES
6a7e4b3ee9 Merge pull request #2173 from frizynn/fix/race-conditions
fix: resolve race conditions in blob-store, discovery-cache, and agent-loop
2026-03-25 22:15:29 -06:00
TÂCHES
13dcd1dbd9 Merge pull request #2166 from frizynn/fix/rpc-bugs-and-memory-leaks
fix(rpc): resolve double-set race, missing error ID, and stream handler
2026-03-25 22:15:27 -06:00
Lex Christopherson
751288675f fix(retry-handler): stop treating 5xx server errors as credential-level failures
Server errors (500/502/503/504) are server-side failures — rotating
credentials doesn't help. Only rate_limit and quota_exhausted are
meaningfully credential-scoped. This prevents the cascading backoff
where a single 500 backs off the sole API key for 20s, causing all
subsequent retries to fail with "All credentials temporarily backed off".

Closes #2588

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:06:37 -06:00
github-actions[bot]
419a74672e release: v2.49.0 2026-03-25 23:24:25 +00:00
github-actions[bot]
34ce83889d release: v2.48.0 2026-03-25 22:25:06 +00:00
TÂCHES
9a32ea9c17 Merge pull request #2543 from splichy/fix/provider-scoped-stream-routing
fix(model-registry): scope custom provider stream handlers to prevent clobbering built-ins
2026-03-25 15:44:55 -06:00
TÂCHES
0a789396b2 Merge pull request #2544 from gsd-build/feat/external-tool-execution
fix: revert insertChildBefore and clean up external tool rendering
2026-03-25 15:42:49 -06:00
Vojtěch Šplíchal
d56842ab7a fix(model-registry): scope custom provider stream handlers to prevent clobbering built-in API handlers
When a custom provider (e.g. claude-code-cli) registers a streamSimple
handler with the same api type as a built-in (e.g. 'anthropic-messages'),
the global API provider registry was overwritten, routing ALL models of
that api type through the custom handler.

This caused anthropic/claude-opus-4-6 requests to be dispatched through
the Claude Code SDK subprocess instead of the Anthropic API, resulting
in 'Tool not found' errors for Glob, Read, Edit, Bash (SDK tool names
not present in pi's tool registry).

Fix: wrap the registered handler with a model.provider guard so it only
fires for models from the registering provider, delegating to the
previous handler for all other providers.

Closes #2536
2026-03-25 22:33:48 +01:00
Lex Christopherson
b8b92b8481 revert: remove insertChildBefore usage in chat-controller
The insertChildBefore approach doesn't fix tool ordering because the
message component is already live-streaming text when tool_execution
events arrive. Proper fix requires T3 Code-style session-lifetime
architecture. Revert to simple addChild for now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:30:49 -06:00
Lex Christopherson
263d725ecd fix: render tool calls above text response for external providers
- Add insertChildBefore() to Box component for positional insertion
- In chat controller, insert tool_execution components before the last
  assistant message component (instead of appending after) when tools
  were executed externally
- Simplify agent-loop externalToolExecution path back to basic
  tool_execution_start/end emission
- Toolcall streaming events are filtered in the Claude Code adapter
  to prevent duplicate rendering via message_update

Result: externally-executed tool calls render above the text response,
matching the expected visual flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 14:38:39 -06:00
github-actions[bot]
55c8988900 release: v2.47.0 2026-03-25 19:53:13 +00:00
Lex Christopherson
a0ee03d331 feat(agent-core): add externalToolExecution mode for external providers
Adds `externalToolExecution` flag to AgentLoopConfig. When true, the
agent loop emits tool_execution_start/end events for TUI rendering but
skips local tool dispatch. Used by providers that handle tool execution
internally (e.g., Claude Code CLI via Agent SDK).

The flag is dynamically evaluated per-loop via a callback on
AgentOptions, so model switches mid-session are handled correctly.
Providers with authMode "externalCli" automatically use this mode.

Also updates the Claude Code CLI stream adapter to preserve tool call
blocks in the final message instead of stripping them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:57:47 -06:00
TÂCHES
9b548ba7f3 Merge pull request #2516 from jeremymcs/fix/packages-test-coverage
ci(test): wire packages/pi-coding-agent tests into CI
2026-03-25 12:38:57 -06:00
Jeremy McSpadden
d6bd17298f ci(test): add test:packages script and wire packages/pi-coding-agent tests into CI
The 13 test files in packages/pi-coding-agent/src/core/ were never executed
in CI or by `npm test`. The test:unit glob only covers src/resources/extensions/gsd/tests/
and src/tests/, leaving lifecycle-hooks, model-registry-auth-mode, auth-storage,
and 10 other suites with zero enforcement.

- Add `test:packages` script that runs compiled dist tests after build
- Wire into both the linux build job and windows-portability job in CI
- Fix two env-isolation bugs in auth-storage.test.ts: the "returns undefined"
  and "falls through to fallback resolver" tests were not clearing
  OPENROUTER_API_KEY before calling getApiKey, causing failures when the
  env var is set in the caller's environment
2026-03-25 12:14:17 -05:00
github-actions[bot]
652811212a release: v2.46.1 2026-03-25 17:13:41 +00:00
github-actions[bot]
e21526496e release: v2.46.0 2026-03-25 15:50:08 +00:00
Jay The Reaper
68902466ac fix(core): address PR review feedback for non-apikey provider support (#2452)
- Strip apiKey from options at streamSimple registration boundary for
  externalCli/none providers — enforced structurally, not by convention
- Add registration-time validation: externalCli/none requires streamSimple,
  rejects contradictory apiKey, improved error messages mentioning authMode
- Cache legacy hook module imports to prevent side-effect double-execution
- Add isReady() trust boundary documentation
- Add inline comments on compaction-orchestrator apiKey flow
- Refactor package-commands.test.ts to use t.after() cleanup
- Add lifecycle-hooks.test.ts with 24 unit tests for readManifestRuntimeDeps,
  collectRuntimeDependencies, verifyRuntimeDependencies, resolveLocalSourcePath
- Expand model-registry-auth-mode.test.ts with streamSimple apiKey boundary
  tests and registration validation tests (80 total tests across all files)
- Add afterRemove deleted-directory edge case test
- Fix help-text.ts wording: "lifecycle hooks" → "post-install validation"
- Fix event.message null check documentation (intentional tightening)
2026-03-25 08:45:20 -06:00
github-actions[bot]
51519e6cda release: v2.45.0 2026-03-25 06:32:10 +00:00
madjack
f4ecf9d11a fix: use Array.from instead of Buffer.from for native processStreamChunk state (#2348)
The napi StreamState fields (utf8Pending, ansiPending) expect plain arrays
(Vec<u8>), not Buffers. Passing Buffer.from() caused 'Given napi value is
not an array on StreamState.utf8Pending' crash on multi-chunk bash output.

Added regression test for multi-chunk state passing.

AI-assisted: This change was authored with Claude (AI pair programming).
2026-03-25 00:08:11 -06:00
madjack
f21ad837ac feat: add timestamps on user and assistant messages (#2368)
Shows absolute timestamps (date + time) on user prompts (right-aligned
above the message) and assistant replies (below the response). Format
is configurable via /settings → Timestamp format:

- date-time-iso: 2026-03-24 10:34 (default)
- date-time-us:  03-24-2026 10:34 AM

Setting persists in settings.json as timestampFormat.

- Added formatTimestamp utility with ISO and US format support
- Updated UserMessageComponent and AssistantMessageComponent
- Added timestampFormat to SettingsManager with getter/setter
- Added to /settings UI for runtime switching
- Unit tests for all format variants including AM/PM edge cases

AI-assisted: This change was authored with Claude (AI pair programming).
2026-03-24 23:18:42 -06:00
Tom Boucher
df269b3b00 feat: complete offline mode support (#2429)
* feat: complete offline mode support for local-only model setups

- Add isLocalModel() to detect localhost/127.0.0.1/0.0.0.0/::1/unix sockets
- Add isAllLocalChain() to verify all registry models are local
- Validate --offline flag rejects remote models with clear error
- Auto-enable PI_OFFLINE when all configured models are local
- Return dummy API key for local models to skip auth validation
- Filter web search results in offline mode (chat-controller + tool-execution)
- Add ECONNREFUSED/ENOTFOUND/ENETUNREACH to INFRA_ERROR_CODES for immediate
  failure (no retry) when network is intentionally unavailable
- Add comprehensive test suite (17 tests)

Fixes #2341

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

* fix(test): update infra-error test for new offline-mode error codes

The offline mode feature added ECONNREFUSED, ENOTFOUND, and ENETUNREACH
to INFRA_ERROR_CODES but the test still asserted size === 6. Update the
count to 9 and add detection tests for the three new codes.

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-24 22:35:45 -06:00
Tom Boucher
e4d21c40d0 refactor(test): replace try/finally with beforeEach/afterEach in packages tests (#2390) 2026-03-24 21:34:10 -06:00
github-actions[bot]
9e31a6985c release: v2.44.0 2026-03-24 22:09:37 +00:00
Jay The Reaper
bc278d12d9 feat(core): support for 'non-api-key' provider extensions like Claude Code CLI (#2382)
* feat(core): add generic native post-install hooks for package install

* feat(core): add before/after install/remove lifecycle hooks

* refactor(core): remove postInstall alias from lifecycle hook fallback

* feat(core): complete authMode support for keyless providers

The initial authMode implementation fixed model-registry, sdk, and
fallback-resolver but missed agent-session.ts (6 callsites) and
compaction-orchestrator.ts (2 callsites) that block externalCli
providers at runtime.

Architecture: separate readiness gating from credential retrieval.
- isProviderRequestReady(): authMode-aware readiness check
- getApiKey()/getApiKeyForProvider(): return undefined for
  externalCli/none providers instead of triggering auth errors
- All 8 callsites in agent-session and compaction-orchestrator
  now gate on readiness, not key presence
- Downstream signatures (compaction, branch-summarization) accept
  apiKey: string | undefined
- Replaced hardcoded ollama exception in discoverModels with
  isProviderRequestReady

Zero behavioral change for classic apiKey/oauth providers.

* feat(core): add isReady callback for provider readiness verification

Extensions can now provide an isReady() callback when registering any
provider. isProviderRequestReady() calls it before default auth checks,
allowing providers to verify actual reachability (CLI authenticated,
API key valid, service online) rather than relying solely on credential
presence.

* test(core): expand authMode test coverage

Cover all four auth modes (apiKey, oauth, externalCli, none),
isReady callback behavior, getProviderAuthMode defaults,
isProviderRequestReady for each mode, getAvailable filtering,
and getApiKey early-return for keyless providers.

* chore: remove provider-api-bridge files from this branch

These files implement GSD core → provider-api wiring (deps + tool
registry) and belong in a separate PR. Reverts register-extension.ts
to upstream state.
2026-03-24 15:50:12 -06:00
Tom Boucher
ab0bb9dece fix(extensions): detect TypeScript syntax in .js extension files and suggest renaming to .ts (#2386)
When a user creates a .js extension file but writes TypeScript syntax in it,
the loader now detects common TS patterns (type annotations, interfaces, enums,
generics) and provides a clear error message suggesting to rename the file to
.ts, instead of the previous cryptic "Extension does not export a valid factory
function" or opaque jiti parse errors.

Fixes #2381

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:12:36 -06:00
Jeremy McSpadden
867a4be297 fix(memory): fix memory and resource leaks across TUI, LSP, DB, and automation (#2314)
* fix(memory): fix memory and resource leaks across TUI, LSP, DB, and automation

Addresses all findings from a systematic memory leak audit across five
dimensions: event listeners, timers, file system handles, subscriptions/
closures, and GSD automation lifecycle.

Critical fixes:

rpc-client.ts: stderr .on("data") handler attached in start() was never
removed in stop(). Now stored as _stderrHandler and removed via
removeListener() on stop.

lsp/client.ts: Three process.on() handlers (beforeExit, SIGINT, SIGTERM)
registered at module load time with anonymous functions — impossible to
remove. Now stored as named references; new removeProcessHandlers() export
allows graceful teardown. stdout/stderr stream listeners in
startMessageReader/startStderrReader also stored per-client in
clientStreamHandlers map and removed in shutdownClient() and shutdownAll().

parallel-orchestrator.ts: spawnWorker() attached 5 listeners to child
process streams on every spawn with no removal on worker stop/respawn,
accumulating listeners indefinitely. Added cleanup() field to WorkerInfo;
called via removeAllListeners() on exit, graceful stop, stale detection,
and dead PID cleanup paths. Also: module-level state.workers Map was never
cleared between orchestration runs; startParallel() and resetOrchestrator()
now iterate and clean up all WorkerInfo entries before reassigning state.

scripts/watch-resources.js: fs.watch() return value was discarded (OS
watcher never closed) and the fallback setInterval handle was also
discarded (timer ran forever). Both now stored; process.on("exit") handler
closes/clears them.

gsd-db.ts: closeDatabase() did not checkpoint the WAL before closing —
.db-shm/.db-wal files accumulated on disk across crash-recovery cycles.
Now runs PRAGMA wal_checkpoint(TRUNCATE) before close. Also added a
one-time process.on("exit") handler in openDatabase() so the handle is
always closed even on unclean exits.

Medium fixes:

bg-shell/overlay.ts: 1-second refresh setInterval only cleared in
keyboard exit handler; abnormal teardown leaked the timer. Added dispose()
method that unconditionally clears it.

file-watcher.ts: pending debounce Map was scoped inside startFileWatcher()
making it inaccessible to stopFileWatcher(). Moved to module scope;
stopFileWatcher() now clears all pending timers and empties the map before
closing the watcher.

auto-supervisor.ts: registerSigtermHandler() could accumulate multiple
SIGTERM handlers if called without passing back the previous reference.
Added module-level _currentSigtermHandler; old handler is always removed
before registering the new one regardless of whether caller passes it.

Low-severity fixes:

print-mode.ts: session.subscribe() return value was discarded. Now stored
and called in a finally block to guarantee cleanup on both normal
completion and errors.

rpc-mode.ts: same — subscribe() unsubscribe now called in the shutdown
path before process.exit().

theme.ts: onThemeChangeCallback singleton silently overwrote any previous
subscriber. Converted to Set<() => void>; onThemeChange() now returns a
cleanup function. All four internal call sites updated to forEach().
Backward-compatible — existing callers that discard the return are unaffected.

* fix: ensure unsubscribe is called on error/abort in print-mode

The PR #2314 added unsubscribe storage but still called process.exit(1)
directly, bypassing the unsubscribe. Wrapped in try/finally to guarantee
cleanup runs before exit.
2026-03-24 07:23:36 -06:00
Tom Boucher
6793489b78 fix(pi-ai): restore alibaba-coding-plan provider via models.custom.ts (#2350)
The alibaba-coding-plan provider (8 models) was silently dropped when
models.generated.ts was regenerated from models.dev in PR #2118. This
provider uses a proprietary DashScope endpoint not tracked by models.dev,
so regeneration removes it every time.

Add models.custom.ts for manually-maintained providers that don't exist
in models.dev. The model registry (models.ts) now merges both generated
and custom models at startup. Custom entries are additive and never
overwrite generated ones.

Restores: qwen3.5-plus, qwen3-max-2026-01-23, qwen3-coder-next,
qwen3-coder-plus, MiniMax-M2.5, glm-5, glm-4.7, kimi-k2.5

Fixes #2339

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 07:19:27 -06:00
Tom Boucher
eb30d3afd4 feat(gsd): show per-prompt token cost in footer behind show_token_cost preference (#2357)
Adds opt-in per-prompt cost display to the interactive footer. Users
enable it by setting `show_token_cost: true` in their preferences.md.
Disabled by default — the footer behavior is unchanged unless opted in.

Fixes #1515

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 07:18:57 -06:00
github-actions[bot]
b67ba7c086 release: v2.43.0 2026-03-23 18:50:53 +00:00
Tom Boucher
297845f10c fix(auth): fall through to env/fallback when OAuth credential has no registered provider (#2097)
Fixes #2083

When an OpenRouter API key is stored in auth.json as type:"oauth" (instead
of type:"api_key"), getApiKey() calls getOAuthProvider("openrouter") which
returns undefined — OpenRouter is not a registered OAuth provider. Previously,
resolveCredentialApiKey returned undefined and getApiKey returned that directly,
never reaching the env-var or fallback-resolver paths.

Now, when resolveCredentialApiKey returns undefined, getApiKey falls through
to OPENROUTER_API_KEY env var and the fallback resolver instead of silently
failing with "Authentication failed."

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:03:05 -06:00
Tom Boucher
f4ee51017a perf: startup optimizations — pre-compiled extensions, compile cache, batch discovery (#2125)
Skip jiti JIT compilation for bundled extensions that have pre-compiled .js
siblings, enable V8 bytecode caching on Node 22+, and batch directory
discovery to reduce syscalls during resource loading.

Fixes #2108

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:02:30 -06:00
Juan Francisco Lebrero
c75f69610f fix(lsp): bound message buffer and clean up stale client state (#2171)
Fix three sources of unbounded memory growth in the LSP client:

1. Message buffer: Add a 10 MB cap on client.messageBuffer. If an LSP
   server sends incomplete or malformed data that causes the buffer to
   exceed this limit, the buffer is discarded and reset to prevent
   runaway memory usage.

2. Client/lock map eviction: clientLocks and fileOperationLocks entries
   were never removed when a client was shut down via shutdownClient().
   Now both maps are cleaned up alongside the clients map on shutdown.

3. Idle checker lifecycle: The idle check interval now stops itself when
   no clients remain, and shutdownAll() explicitly stops it and clears
   all global maps (clients, clientLocks, fileOperationLocks).
2026-03-23 09:54:12 -06:00