Self-contained extension at src/resources/extensions/ollama/ that
auto-detects a running Ollama instance, discovers locally pulled models,
and registers them as a first-class provider with zero configuration.
Features:
- Auto-discovery of local models via /api/tags on session_start
- Capability detection (vision, reasoning, context window) for 40+ model families
- /ollama slash command with status, list, pull, remove, ps subcommands
- ollama_manage LLM-callable tool for agent-driven model operations
- Onboarding flow with auto-detect (no API key required)
- Non-blocking async probe — doesn't delay TUI paint
- Respects OLLAMA_HOST env var for non-default endpoints
Core changes (minimal):
- Add "ollama" to KnownProvider in pi-ai types
- Add "ollama" key resolution in env-api-keys.ts
- Add "ollama" default model in model-resolver.ts
- Add "Ollama (Local)" to onboarding wizard with probe flow
- Add extension-manifest.ts and extension-sort.ts to pi-coding-agent
with manifest reading and Kahn's BFS topological sort algorithm
- Add extensionPathsTransform hook to DefaultResourceLoader that runs
between path merging and loadExtensions() — enables pre-load
filtering and reordering without modifying pi internals
- Wire GSD's buildResourceLoader() to provide a transform that:
1. Filters ALL extensions (including community) through the GSD registry
2. Sorts in topological dependency order via sortExtensionPaths()
- Mark discoverAndLoadExtensions() as @deprecated (dead code path)
- Add 16 tests covering manifest reading, dependency sorting, cycles,
missing deps, and non-array deps
Previously, dependencies.extensions in manifests was decorative (sort
existed but was never called), and gsd extensions disable only worked
for bundled extensions. Community extensions in ~/.gsd/agent/extensions/
bypassed the registry entirely.
When an agent requests read(file, offset: 30) on a 13-line file, the
read tool threw "Offset 30 is beyond end of file" which propagated as
invalid JSON downstream during milestone completion. Now clamps the
offset to the last line and prepends a notice, allowing the agent to
continue with valid content.
Fixes both read.ts and hashline-read.ts variants.
Closes#3007
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When a session grows beyond the context window of available models,
generateSummary() now detects the overflow and falls back to chunked
summarization: split messages into context-fitting chunks, summarize
the first chunk, then iteratively merge subsequent chunks using the
existing UPDATE_SUMMARIZATION_PROMPT path.
Closes#2932
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Three spawn call sites were missing `shell: process.platform === "win32"`,
causing ENOENT/EINVAL errors on Windows where npm-installed tools are .cmd
batch scripts that require shell resolution:
- exec.ts: hardcoded `shell: false` -> platform-guarded
- lsp/index.ts: missing shell option on project-type command spawn
- lsp/lspmux.ts: missing shell option on lspmux binary spawn
Adds a structural regression test that scans all spawn sites invoking
user-facing binaries and asserts the Windows shell guard is present.
Closes#2854
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: wrap custom messages with system notification prefix in LLM context
Background job completion notifications (delivered as custom messages via
sendMessage with deliverAs: "followUp") were converted to plain role: "user"
messages in convertToLlm(), making the LLM indistinguishable from actual
human input. This caused the agent to confuse background task output with
user messages, responding to job completions as if the user had typed them.
Wrap all custom messages with a clear system notification prefix that
includes the customType and an explicit instruction that the content is
an automated system event, not user input. This follows the same pattern
used by branchSummary and compactionSummary messages which already use
structured prefixes/suffixes.
Closes#3026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve TS import extension and type errors in messages test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When a session accumulates many images (screenshots, file reads), the
Anthropic API enforces a 2000px dimension limit for "many-image requests"
and returns a 400 error. Previously this error was not classified as
retryable, causing the session to get permanently stuck in an error loop
with no recovery path.
This adds automatic recovery: detect the specific "image dimensions exceed
max allowed size for many-image requests" error, strip older images from
the conversation history (keeping the 5 most recent), and auto-retry.
Also handles manual retry (continue/retry) by downsizing before retrying.
Closes#2874
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: repair YAML bullet lists in malformed tool-call JSON (#2660)
When LLMs copy YAML template formatting into tool-call arguments, they
produce `"key": - item` instead of `"key": ["item"]`, causing JSON parse
errors that block milestone completion. Add a repairToolJson() utility
that detects and converts YAML-style bullet lists into JSON arrays before
parsing. Integrated into both the PartialMessageBuilder (claude-code-cli)
and the anthropic-shared streaming provider, with fallback in
parseStreamingJson for all other providers.
Closes#2660
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use .js import extension in repair-tool-json test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: align @gsd/native module type with compiled output (#2861)
The package declared "type": "module" and used "import"-only export
conditions, but the addon loader used import.meta.url which is
incompatible when the parent package enforces ESM resolution on
Node.js v24. Switch to "type": "commonjs" with "default" export
conditions and remove the import.meta.url/__dirname shim (CJS
provides both natively).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restore dual CJS/ESM compat for native addon loader
The ESM-to-CJS conversion removed import.meta.url polyfills, but the CI
test loader (dist-redirect.mjs) transpiles this file to ESM via
ts.transpileModule — making __dirname and require unavailable at test time.
Add runtime typeof guards that use the CJS globals when available (compiled
output) and fall back to import.meta.url in ESM (test runner). Use
@ts-expect-error to suppress TS1470 for the import.meta branches that are
unreachable in the compiled CJS output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use indirect eval for import.meta.url to avoid CJS parse-time error
import.meta is a parse-time syntax error in CJS — typeof guards don't
help because Node.js rejects the syntax before executing any code.
Wrapping in new Function("return import.meta.url") hides the syntax
from the CJS parser while still working when executed as ESM (test runner).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: replace new Function(import.meta.url) with loader-injected CJS globals
import.meta is static syntax unavailable in new Function() and eval()
scopes, causing rtk-portability CI failures across all platforms.
Instead of trying to access import.meta.url indirectly, the test loader
(dist-redirect.mjs) now injects __dirname, __filename, and require as a
preamble when transpiling workspace packages to ESM. This lets native.ts
use __dirname/require directly in both CJS (production) and ESM (CI test)
contexts without any import.meta.url fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Map pause_turn to "pauseTurn" instead of "stop" so the agent loop
continues when Anthropic's server pauses a long-running turn (e.g.
native web search hitting its iteration limit). Previously the
incomplete server_tool_use block was saved to history, causing a
400 invalid_request_error on the next API call.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The "Extra usage is required for long context requests" error from
Anthropic is a billing gate, not a transient rate limit. Classify it as
quota_exhausted so the handler enters the fallback path instead of an
infinite backoff loop. When no cross-provider fallback exists, attempt a
[1m] to base model downgrade before stopping cleanly.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When gsd is spawned as an RPC bridge child process, stdout is a pipe
(process.stdout.isTTY === undefined). The TUI render loop would run at
~4,600 renders/sec writing ANSI escape codes to the pipe, consuming
500%+ CPU per process while idle.
Add isTTY guard to Terminal interface, ProcessTerminal.start(), TUI.start(),
and requestRender() so the entire render pipeline is skipped on non-TTY stdout.
RemoteTerminal (browser-backed) correctly reports isTTY=true.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Saves in-progress daemon work from M005-m138xe that was sitting uncommitted.
Includes orchestrator expansion, event bridge/formatter enhancements,
message batcher tweaks, and discord bot additions.
Tests cover: provider registration, base URL + API type, reasoning +
context window specs, and non-collision with generated zai models.
Required by CI lint gate (require-tests.sh).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GLM-5.1 is the latest Zhipu AI model with 204K context window and
131K max output tokens. It uses the Z.AI Coding Plan endpoint
(OpenAI-compatible) and supports reasoning via enable_thinking.
Not yet tracked by models.dev, so added to models.custom.ts alongside
existing alibaba-coding-plan entries. Merges additively with the
generated Z.AI provider (glm-5, glm-5-turbo, etc.).
Specs from https://docs.z.ai/devpack/using5.1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Migrated headless orchestrator to use execution_complete events,…
- "src/headless.ts"
- "src/headless-ui.ts"
- "src/tests/headless-v2-migration.test.ts"
GSD-Task: S06/T02
* test: Wired pi-coding-agent to re-export JSONL utils from @gsd/rpc-clie…
- "packages/pi-coding-agent/src/modes/rpc/jsonl.ts"
- "packages/pi-coding-agent/package.json"
- "packages/rpc-client/src/index.ts"
- "packages/rpc-client/src/jsonl.ts"
- "packages/rpc-client/src/rpc-client.ts"
- "packages/rpc-client/src/rpc-types.ts"
- "packages/rpc-client/src/rpc-client.test.ts"
- "packages/rpc-client/package.json"
GSD-Task: S06/T03
* feat: Wire --resume flag to resolve session IDs via prefix matching and…
- "src/headless.ts"
- "dist/headless.js"
GSD-Task: S01/T01
* test: Added 5 e2e integration tests proving headless JSON batch, SIGINT…
- "src/tests/integration/e2e-headless.test.ts"
GSD-Task: S01/T02
* test: Updated @gsd/rpc-client and @gsd/mcp-server to 2.52.0 with publis…
- "packages/rpc-client/package.json"
- "packages/mcp-server/package.json"
- "packages/rpc-client/.npmignore"
- "packages/mcp-server/.npmignore"
GSD-Task: S02/T01
* chore: auto-commit after complete-milestone
GSD-Unit: M002-gzq23a
* fix: revert jsonl.ts to inline implementation — @gsd-build/rpc-client not available at source-level test time in CI
The re-export from @gsd-build/rpc-client fails in CI because tests run against
TypeScript source (--experimental-strip-types) before any build step. The npm
dependency resolves to node_modules/ which requires dist/ to exist. Reverting
to the original inline implementation eliminates the cross-package dependency
for source-level imports.
The re-export from @gsd-build/rpc-client fails in CI because tests run against
TypeScript source (--experimental-strip-types) before any build step. The npm
dependency resolves to node_modules/ which requires dist/ to exist. Reverting
to the original inline implementation eliminates the cross-package dependency
for source-level imports.