Commit graph

245 commits

Author SHA1 Message Date
github-actions[bot]
1783559610 release: v2.58.0 2026-03-28 02:14:33 +00:00
github-actions[bot]
ab7961000e release: v2.57.0 2026-03-28 00:06:04 +00:00
github-actions[bot]
b5715c20bb release: v2.56.0 2026-03-27 21:29:07 +00:00
github-actions[bot]
a6bb48e82d release: v2.55.0 2026-03-27 20:44:58 +00:00
mastertyko
c5907c3677 fix(interactive): fully remove providers from /providers (#2852)
* test(integration): suppress npm pack buffer overflows

* fix(interactive): fully remove providers from /providers
2026-03-27 09:53:35 -06:00
github-actions[bot]
97de0a6d94 release: v2.54.0 2026-03-27 14:54:34 +00:00
github-actions[bot]
112090706e release: v2.53.0 2026-03-27 02:50:02 +00:00
github-actions[bot]
34bbee21bc release: v2.52.0 2026-03-27 00:07:48 +00:00
Jeremy McSpadden
f8814f5a15 refactor(pi-ai): replace model-ID pattern matching with capability metadata (#2548)
* refactor(pi-ai): replace model-ID pattern matching with capability metadata

Add ModelCapabilities to Model<TApi> and a CAPABILITY_PATCHES mechanism
so call sites read model.capabilities fields instead of parsing model IDs
or hardcoding provider names.

- types.ts: add ModelCapabilities interface (supportsXhigh, requiresToolCallId,
  supportsServiceTier, charsPerToken) and capabilities?: ModelCapabilities to
  Model<TApi>
- models.ts: add CAPABILITY_PATCHES table applied at registry init; patches
  declare GPT-5.x and Opus 4.6 capabilities once instead of repeating ID
  checks at every call site; supportsXhigh() now reads capabilities only
- service-tier.ts: extract SERVICE_TIER_MODEL_PREFIXES constant so the gating
  list has a single named home; add path comment pointing to issue #2546 for
  the full capability-driven follow-up

No behaviour change. New models and providers can declare capabilities in
their model definitions without touching function logic.

Closes #2546

* fix(pi-ai): apply capability patches to custom/discovered/extension models

Models constructed outside the static pi-ai registry (custom models
from models.json, extension-registered models, discovered models)
bypassed CAPABILITY_PATCHES — causing supportsXhigh() to silently
return false for GPT-5.x or Opus 4.6 variants registered through
those paths.

Export applyCapabilityPatches() from pi-ai and call it in ModelRegistry
after model assembly in all three construction paths: loadModels(),
applyProviderConfig(), and discoverModels().

Add regression tests covering patching, precedence, idempotency,
and synthetic models that mimic the custom/extension path.

Closes #2546
2026-03-26 16:38:29 -06:00
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
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
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
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
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
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
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