Commit graph

3367 commits

Author SHA1 Message Date
Jeremy McSpadden
96f77d8ff8 Merge pull request #4037 from mastertyko/fix/3925-claude-code-overflow-detect
fix(pi-ai): detect claude-code overflow text
2026-04-13 18:21:36 -05:00
Jeremy McSpadden
ef6abf48bc Merge pull request #4147 from NilsR0711/fix/bun-update-command
fix(gsd): use bun for update when installed via Bun
2026-04-13 18:03:03 -05:00
Jeremy McSpadden
cdd257e59a Merge pull request #4059 from mastertyko/fix/4054-compaction-safe-role-markers
fix(pi-coding-agent): use safe compaction role markers
2026-04-13 17:58:04 -05:00
Nils Reeh
e3e72174fa fix(gsd): use bun for update when installed via Bun (#4145)
When GSD is installed with `bun add -g`, running `gsd update` or
`/gsd update` previously shelled out to `npm install -g`, which fails
with EACCES on systems where npm has no write access to the global
node_modules directory.

Adds `resolveInstallCommand(pkg)` to `update-check.ts` that returns
`bun add -g <pkg>` when `process.versions.bun` is defined (i.e. the
current runtime is Bun), and `npm install -g <pkg>` otherwise.  All
three update paths — `update-cmd.ts`, `commands-handlers.ts`, and the
interactive startup prompt in `update-check.ts` — now use this helper,
including the fallback error message shown to the user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 00:52:08 +02:00
Jeremy McSpadden
6ba83c83c2 Merge pull request #4146 from jeremymcs/fix/4144-inline-tool-calls
fix(tui): render assistant tool calls inline with text instead of grouped at end
2026-04-13 17:41:41 -05:00
Jeremy
c8c416802f fix(tui): render assistant tool calls inline with text instead of grouped at end
Previously the chat-controller created one AssistantMessageComponent per
assistant message and removed/re-appended it to the chat container's tail
on every tool block, forcing all narration after every tool execution
regardless of stream order. Users had to scroll up to read text that was
written before each tool call.

Replace the reorder hack with a stream-order segment walker that walks
content[] left-to-right, collapses contiguous text/thinking blocks into
text-run segments, emits one segment per tool block, and append-only adds
new segments to chatContainer. AssistantMessageComponent gains a
ContentRange API so a single message can spawn multiple text-run
components, plus a separate showMetadata flag so timestamp/error footers
render only on the trailing segment without duplicating earlier text.

Adds a regression test that streams [text, tool, text, tool, text] and
asserts both interleaved order and per-segment rendered text content.

Closes #4144
2026-04-13 17:23:17 -05:00
Jeremy McSpadden
1ec1a8c4c4 Merge pull request #4060 from mastertyko/fix/3917-claude-code-effort
feat(claude-code): pass thinking level as effort
2026-04-13 16:07:59 -05:00
Jeremy McSpadden
e114f458fd Merge pull request #4141 from jeremymcs/claude/gsd-bootstrap-deep-dive-bQo8K
feat(gsd): extend flat-rate provider detection to custom/externalCli providers
2026-04-13 16:07:24 -05:00
Jeremy
8cf8d2bcf2 fix(gsd): restore isAutoMode plumbing and workflow-logger catch in auto-model-selection
CI on #4141 failed because threading an explicit flatRateCtx parameter
through resolvePreferredModelConfig broke two contracts the test suite
locks in:

  1. interactive-routing-bypass (#3962) asserts that
     resolvePreferredModelConfig is invoked with exactly three positional
     arguments and that its `if (!isAutoMode) return undefined` guard
     lives within the first 600 chars of the function body. The new
     flatRateCtx param + JSDoc pushed the guard past that window and
     lengthened the call site.

  2. silent-catch-diagnostics (#3348) requires migrated files to route
     through workflow-logger instead of leaving empty catch blocks. The
     new buildFlatRateContext() swallowed registry lookup errors with a
     comment-only catch.

Fix both without regressing flat-rate detection:

- Hang the flat-rate context off autoModeStartModel itself via an
  optional `flatRateCtx` field. selectAndApplyModel now enriches
  autoModeStartModel up front (preserving the variable name) and
  resolvePreferredModelConfig reads autoModeStartModel.flatRateCtx —
  signature shrinks back to three params, call site returns to the
  3-arg form the test anchors on.
- Replace the empty catch in buildFlatRateContext() with a
  logWarning("dispatch", ...) that surfaces the lookup failure while
  still falling through with authMode undefined, matching the
  fail-closed policy everywhere else in the file.
2026-04-13 16:00:01 -05:00
Claude
9a93563a64 feat(gsd): extend flat-rate provider detection to custom/externalCli providers
The 3-entry hard-coded FLAT_RATE_PROVIDERS set in auto-model-selection.ts
treated only github-copilot/copilot/claude-code as flat-rate, so dynamic
routing would happily downgrade units on user-registered subscription
proxies and any externalCli CLI wrapper — quality loss with no cost
benefit for users whose provider charges a flat rate per request.

Make isFlatRateProvider extensible by composing three signals:

  1. Built-in list (unchanged, wins first for regression safety).
  2. externalCli auto-detection via ctx.modelRegistry.getProviderAuthMode()
     — any CLI wrapper around the user's subscription is inherently
     flat-rate.
  3. User-declared `flat_rate_providers` preference for private
     subscription-backed proxies, enterprise-gated deployments, and custom
     CLI wrappers the built-in list doesn't know about.

Add a buildFlatRateContext() helper so every call site constructs the
context the same way and degrades gracefully when ctx/prefs/registry are
unavailable (never breaks flat-rate detection).

Thread the context through:

- resolvePreferredModelConfig (routing synthesis guard)
- selectAndApplyModel primary-model and fallback provider checks
- auto-start.ts dynamic-routing banner so the startup message matches
  dispatch-time reality

Preferences:
- Add `flat_rate_providers?: string[]` to GSDPreferences and
  KNOWN_PREFERENCE_KEYS in preferences-types.ts.
- Add a string-array validator in preferences-validation.ts that trims
  whitespace and drops empty entries.

Tests:
- Extend flat-rate-routing-guard.test.ts with 13 new cases covering
  externalCli auto-detection, userFlatRate preference matching
  (case-insensitive), combined signals, buildFlatRateContext() behavior
  (including registry-lookup-throws and non-canonical auth-mode
  responses), plus regression cases for the built-in list.
- Add 5 validator cases in preferences.test.ts for the new
  flat_rate_providers field (string-array accepted, whitespace trimmed,
  non-array rejected, non-string elements rejected, known-key warning
  check).
2026-04-13 20:25:26 +00:00
Jeremy McSpadden
24f51fd76b Merge pull request #4138 from jeremymcs/claude/investigate-issue-4122-6Vi1I
fix(gsd): preserve custom-model selection on /gsd auto bootstrap (#4122)
2026-04-13 15:00:58 -05:00
Jeremy
4fad01694c Merge upstream/main into fix/4122 custom-provider bootstrap 2026-04-13 14:05:12 -05:00
Claude
73558e7557 fix(gsd): preserve custom-model selection on /gsd auto bootstrap (#4122)
When a user picks a custom-provider model via /gsd model (Ollama, vLLM,
LM Studio, OpenAI-compatible proxies — anything defined in
~/.gsd/agent/models.json) and then runs /gsd auto, the bootstrap silently
swaps it out for whichever model PREFERENCES.md happens to list. That
model is invariably a built-in provider (claude-code, anthropic) the user
isn't logged into, so auto-mode immediately fails with
"Not logged in · Please run /login", pauses, and resets the session to
claude-code/claude-sonnet-4-6.

Root cause: #3517 made resolveDefaultSessionModel() (PREFERENCES.md) take
priority over ctx.model (settings.json) in auto-start.ts. That fix was
correct for the scenario where settings.json had a stale built-in default
but PREFERENCES.md was freshly configured, but it has no awareness of
custom providers — PREFERENCES.md cannot reference them, so honoring it
when the session provider is custom always discards the user's explicit
choice.

Add isCustomProvider() to preferences-models.ts which checks whether a
provider is declared in ~/.gsd/agent/models.json (with ~/.pi/agent
fallback). Read the file directly with JSON.parse to avoid pulling in
the model-registry at this call site, and treat any read or parse error
as not-custom so a malformed models.json never breaks bootstrap.

In bootstrapAutoSession(), when the session provider is custom, use
ctx.model directly. Otherwise fall through to the existing #3517
behavior (preferredModel ?? ctx.model).

Tests:
- New behavioral regression in model-isolation.test.ts that mirrors
  the auto-start.ts logic and verifies the four interesting cases:
  custom session beats PREFERENCES.md, built-in session still defers
  to PREFERENCES.md (#3517 preserved), custom session with no
  PREFERENCES.md uses ctx.model, and null ctx.model falls through.
- New string-grep guard in auto-start-model-capture.test.ts that the
  isCustomProvider() call is wired into the snapshot path.
- Updated #3517 grep to allow the new branching shape while still
  asserting preferredModel remains a snapshot source for built-ins.

https://claude.ai/code/session_01QLYCeiXWjSFPEXFxjkSLni
2026-04-13 17:53:32 +00:00
github-actions[bot]
01df12f14d release: v2.73.1 2026-04-13 17:00:39 +00:00
Tom Boucher
3ff8989a62 fix(gsd): address 3 silent-crash secondary issues from #3348 post-#3696 (#4133)
* fix(ci): address 5 pipeline integrity issues from release audit

- version-stamp.mjs: regenerate package-lock.json after dev version stamp
  (mirrors the same fix applied to bump-version.mjs in #4116)

- bump-version.mjs: regenerate root and web/package-lock.json after version
  bump so both lockfiles are always in sync at release time

- pipeline.yml: add post-bump validation step that verifies all package.json
  files parse as valid JSON before the release commit is made

- pipeline.yml: split "Commit, tag, and push" — commit+tag+rebase happen
  before build, but git push is deferred until after build and npm publish
  both succeed, preventing a broken tag from landing on main

- pipeline.yml: emit a :⚠️: annotation when live LLM tests fail so
  failures are visible in the Actions UI instead of silently swallowed

Closes #4118

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

* fix(gsd): address 3 silent-crash secondary issues from #3348 post-#3696

Three gaps that remained after the double-fault fix in #3696:

1. unhandledRejection not wired — installEpipeGuard only registered
   uncaughtException; promise rejections that escaped without a catch
   were not handled by the GSD error path. Added _gsdRejectionGuard
   alongside _gsdEpipeGuard.

2. Non-fatal overcorrection — the #3696 fix replaced re-throwing with
   log-and-continue, leaving the process running in an indeterminate
   state after any non-EPIPE/non-ENOENT exception. Replaced with
   writeCrashLog + process.exit(1). writeCrashLog is extracted into
   bootstrap/crash-log.ts (zero deps) so tests can import it without
   pulling in the full extension graph.

3. unit-end not emitted after crash-with-side-effects — hameltomor
   observed that complete-milestone M001 wrote SUMMARY.md and updated
   the DB but never emitted unit-end (#3348 comment-4237533440). Added
   emitCrashRecoveredUnitEnd() in crash-recovery.ts: on the next
   auto-mode startup, if a stale lock references a unit whose
   unit-start has no matching unit-end in the journal, a synthetic
   unit-end with status "crash-recovered" is emitted before the lock
   is cleared. This closes the causal chain for downstream tooling
   and forensics without requiring changes to the lock file schema.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 12:33:16 -04:00
mastertyko
3c44e3d4e2 fix(gsd): tolerate corrupt task arrays (#4056) 2026-04-13 12:09:51 -04:00
mastertyko
5474e99ae2 feat(claude-code): pass thinking level as effort 2026-04-13 18:05:19 +02:00
mastertyko
e6110976e7 fix(gsd): discard milestone DB and worktree state (#4065) 2026-04-13 12:04:38 -04:00
Jeremy McSpadden
cf34383104 Merge pull request #4114 from NilsR0711/fix/reactive-subagent-model-wiring
fix(gsd): wire subagent_model preference through to dispatch prompt builders
2026-04-13 10:37:48 -05:00
Jeremy McSpadden
f82a0655f3 Merge pull request #4124 from jeremymcs/claude/model-agnostic-selection-rmDX3
Remove hard-coded model defaults; use provider stickiness instead
2026-04-13 10:36:22 -05:00
Jeremy
cce3bc6828 fix(model-resolver): gate saved default restore on provider readiness
Restore the isProviderRequestReady() guard lost during the main merge.
Tests in model-resolver.test.ts and model-resolver-initial-model-auth.test.ts
require findInitialModel() to skip an unauth'd saved default and fall
through to the first available model.
2026-04-13 10:26:28 -05:00
Jeremy
bafa4e483d Merge remote-tracking branch 'upstream/main' into claude/model-agnostic-selection-rmDX3
# Conflicts:
#	packages/pi-coding-agent/src/core/model-resolver.ts
#	src/cli.ts
2026-04-13 10:22:16 -05:00
Jeremy McSpadden
4fcf5d6e6b Merge pull request #4117 from NilsR0711/fix/localhost-custom-provider-compaction-auth
fix(pi-coding-agent): skip localhost dummy key when fallback resolver provides a configured key
2026-04-13 09:12:17 -05:00
Claude
0ed576ac00 Make model selection model-agnostic
Remove hard-coded Anthropic/Claude defaults and silent provider swaps so
the app honors whatever model/provider the user has configured.

- src/cli.ts: drop the anthropic->claude-code auto-migration blocks that
  were rewriting the user's saved defaultProvider on every startup.
- packages/pi-coding-agent/src/core/model-resolver.ts: delete the
  defaultModelPerProvider table, drop the "recommended variant" swap
  that silently upgraded e.g. claude-opus-4-6 to -extended, and replace
  the provider-iteration first-available fallback with provider-sticky
  (user's saved provider first, then first registry entry).
- src/startup-model-validation.ts: replace the openai/anthropic-first
  fallback chain with Pi-default -> same-provider -> first-available.
- src/help-text.ts: use a generic provider/model-id example for --model
  instead of claude-opus-4-6.
- src/tests/startup-model-validation.test.ts: update the fallback test
  to assert provider stickiness rather than a specific Claude model id.

https://claude.ai/code/session_01CvuUuzuVjRcQN25263nG6V
2026-04-13 14:03:35 +00:00
Jeremy McSpadden
3adafde442 Merge pull request #4121 from jeremymcs/fix/4120-pinned-output-duplication
fix(tui): stop pinned latest-output from duplicating streaming text
2026-04-13 08:30:08 -05:00
Jeremy
9ffde91020 test(tui): regression test for pinned latest-output duplication
Extract the post-tool text-block selection logic into a small pure
helper (`findLatestPinnableText`) so the regression scenario can be
covered without standing up the full interactive controller harness.
The new test pins the bug from #4120: when content blocks are
`[text1, tool1, text2_streaming]`, the helper must return `text1`
(not `text2`), because `text2` is still streaming live into the chat
container and mirroring it would render the same tokens twice.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 08:20:24 -05:00
Jeremy
dc84694c65 fix(tui): stop pinned latest-output mirror from duplicating streaming text
The pinned `Working · Latest Output` border above the editor mirrors
the assistant's latest text block while tools run, so prose stays
visible after a tool's output scrolls it off-screen. The mirror walked
content blocks from the end and picked the last text block — but when
the assistant streams a *new* text block after a tool call (sequence
`[text1, tool1, text2_streaming]`), it picked `text2`, which was also
being streamed live into the chat container. Result: identical tokens
rendered in two places at once.

Restrict the search to text blocks whose index is strictly less than
the index of the most recent tool call. Text after the last tool call
stays in the chat container only; earlier prose (e.g. `text1`) remains
mirrored the entire time the new text streams, so context isn't lost
and the loading-animation handoff is undisturbed.

Fixes #4120

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 08:16:16 -05:00
github-actions[bot]
4733cf7bed release: v2.73.0 2026-04-13 13:04:12 +00:00
Nils Reeh
1a635ac72c fix(gsd): wire subagent_model preference through to dispatch prompt builders
reactive_execution.subagent_model was validated and stored but never
passed to the prompt builders that generate subagent dispatch instructions.
The executing agent therefore autonomously chose its default model instead
of the configured preference.

- buildReactiveExecutePrompt: add subagentModel? param, inject into
  instruction string; auto-dispatch passes reactiveConfig.subagent_model
  with fallback to resolveModelWithFallbacksForUnit("subagent")
- buildParallelResearchSlicesPrompt: same pattern, resolves from
  models.subagent preference
- buildGateEvaluatePrompt: same pattern
- system-context: inject configured subagent model into system prompt
  so the executing agent always knows which model to use for subagents

Closes #4078

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 14:59:04 +02:00
Tom Boucher
a75167fab2 fix(ci): address 5 pipeline integrity issues from release audit (#4119)
- version-stamp.mjs: regenerate package-lock.json after dev version stamp
  (mirrors the same fix applied to bump-version.mjs in #4116)

- bump-version.mjs: regenerate root and web/package-lock.json after version
  bump so both lockfiles are always in sync at release time

- pipeline.yml: add post-bump validation step that verifies all package.json
  files parse as valid JSON before the release commit is made

- pipeline.yml: split "Commit, tag, and push" — commit+tag+rebase happen
  before build, but git push is deferred until after build and npm publish
  both succeed, preventing a broken tag from landing on main

- pipeline.yml: emit a :⚠️: annotation when live LLM tests fail so
  failures are visible in the Actions UI instead of silently swallowed

Closes #4118

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 08:56:12 -04:00
Tom Boucher
202a1cb11e fix(ci): regenerate package-lock.json during version bump (#4116)
bump-version.mjs was updating package.json and sub-packages but never
regenerating package-lock.json, causing the lockfile to drift behind
by one version on every release.

Adds `npm install --package-lock-only` as the final step so the lockfile
is always in sync with the version being committed. Also regenerates the
current lockfile to fix the existing 2.58.0 → 2.64.0 drift.

Closes #4115

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 08:37:59 -04:00
Nils Reeh
1ef6ba16f9 fix(pi-coding-agent): skip localhost dummy key when fallback resolver provides a configured key
Custom OpenAI-compatible providers running on localhost (e.g. a local proxy)
with an explicit apiKey in models.json received 'local-no-key-needed' during
compaction instead of their configured key, causing 401 errors.

The localhost shortcut in AuthStorage.getApiKey() was unconditional. Normal
dispatch calls getApiKeyForProvider() which skips the baseUrl check entirely,
so the fallback resolver was reached and the real key was used. Compaction
calls getApiKey(model) which passes baseUrl, hitting the shortcut first.

Closes #4106
2026-04-13 14:32:16 +02:00
Iouri Goussev
71f10a0d53 chore(gsd): delete 3 unreferenced dead files and orphaned test (#3728)
Phase 0 of #3631 — remove dead code before screaming architecture reorg.

- auto-observability.ts (72 LOC): zero imports anywhere in codebase
- rtk-status.ts (53 LOC): zero imports anywhere in codebase
- file-watcher.ts (100 LOC): zero imports anywhere in codebase
- file-watcher.test.ts: test for dead file-watcher.ts

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:30:32 -04:00
Jeremy McSpadden
9e18dc46d7 Merge pull request #4113 from jeremymcs/fix/4099-followup-bypass-permissions
fix(claude-code): default GSD subagents to bypassPermissions (#4099 follow-up)
2026-04-13 07:22:22 -05:00
Tibsfox
2978bacb74 fix(gsd): reconcile stale slice rows and rebuild STATE.md before DB close (#3658)
* fix(gsd): reconcile stale slice rows and rebuild STATE.md before DB close

Two coupled defects caused auto-mode split-brain where dispatch falsely
reported "No slice eligible" while STATE.md showed executable work:

1. deriveStateFromDb() reconciled missing slice rows but not stale
   existing ones. A slice with status "pending" in the DB but a SUMMARY
   file on disk was never repaired, permanently blocking downstream
   slices. Added slice-level stale reconciliation matching the existing
   task-level pattern.

2. stopAuto() closed the DB before rebuilding STATE.md, forcing
   deriveState() into filesystem fallback mode. Moved rebuildState()
   before closeDatabase() so stop-time STATE.md uses the same
   authoritative DB backend as dispatch.

Fixes #3599

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

* test: add regression test for stale slice row reconciliation

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-04-13 08:17:06 -04:00
Tibsfox
05edc2f484 fix(gsd): block direct writes to gsd.db via hooks to prevent corruption (#3674)
* fix(gsd): block direct writes to gsd.db via hooks to prevent corruption

When gsd_complete_task tool was unavailable, agents fell back to shell-
based sqlite3/sql.js writes to .gsd/gsd.db, corrupting the WAL-backed
database.

Extend write-intercept to block:
- File writes to gsd.db, gsd.db-wal, gsd.db-shm
- Bash commands using sqlite3/sql.js/better-sqlite3 targeting gsd.db
- Shell redirects/cp/mv targeting gsd.db

Closes #3625

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

* test: add regression test for blocking direct gsd.db writes

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-04-13 08:14:03 -04:00
Iouri Goussev
67d68e2684 fix(gsd): break 3 circular dependencies in extension modules (#3730)
Phase 1 of #3631 — eliminate circular imports before screaming arch reorg.

Cycle 1 (auto.ts ↔ auto-direct-dispatch.ts):
  Remove redundant re-export of dispatchDirectPhase from auto.ts.
  No consumer imported it through auto.ts.

Cycle 2 (context-injector.ts ↔ custom-workflow-engine.ts):
  Extract readFrozenDefinition to new definition-io.ts.
  context-injector now imports from definition-io directly.

Cycle 3 (preferences.ts ↔ preferences-skills.ts):
  Move formatSkillRef to preferences-types.ts (pure fn, depends only on
  SkillResolution which is already there).
  Move resolveSkillDiscoveryMode + resolveSkillStalenessDays into
  preferences.ts (trivial wrappers over loadEffectiveGSDPreferences).

Tests: new definition-io.test.ts (3 tests), preferences-formatting.test.ts
(6 tests covering all formatSkillRef branches).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:13:43 -04:00
Jeremy
937aef2c71 fix(claude-code): default GSD subagents to bypassPermissions and pre-authorize safe built-ins (#4099 follow-up)
The first pass at #4099 only pre-authorized `mcp__<server>__*` tools, but
in `acceptEdits` mode the SDK still gates Read, Write, Glob/Grep, and
basic shell inspection commands like `ls`. GSD subagents need the full
workflow toolset and were still hitting "This command requires approval"
prompts on every tool call.

Two changes:

1. `resolveClaudePermissionMode` now returns `bypassPermissions` for all
   GSD subagent runs (auto + interactive), dropping the `acceptEdits`
   branch and the `isAutoActive` dynamic import. The host Claude Code
   session's permission model is the user-visible gate; the inner SDK
   process re-prompting on every tool was approval fatigue with no net
   safety benefit. `GSD_CLAUDE_CODE_PERMISSION_MODE` env override stays
   so security-conscious users can opt back into a stricter mode.

2. Expanded the pre-authorized `allowedTools` list to include Read,
   Write, Edit, Glob, Grep, `Bash(ls:*)`, and `Bash(pwd)` alongside the
   MCP server globs. Acts as a belt-and-suspenders safety net for users
   who set the env override to `acceptEdits`.

Tradeoff documented inline: bypass means a prompt-injection payload read
from an untrusted file could trigger tool calls without a second gate.
Accepted because the workflow is explicit user intent and the
alternative is continuous approval fatigue that blocks real work.

Tests updated for the new allowedTools shape; permission-mode tests
already accepted bypass as the default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 07:13:00 -05:00
Tibsfox
9c2e530d83 fix(gsd): add memory pressure watchdog and persist stuck detection state (#3708)
* fix(gsd): add memory pressure watchdog and persist stuck detection state

Two architectural improvements to auto-mode resilience:

1. Memory pressure monitoring (#3331): checks heap usage every 5
   iterations and triggers graceful shutdown at 85% of V8 heap limit,
   preventing OOM SIGKILL after long-running sessions.

2. Stuck detection persistence (#3704): saves loopState (recentUnits,
   stuckRecoveryAttempts) to .gsd/runtime/stuck-state.json so counters
   survive session restarts. Previously, restarting auto-mode reset all
   stuck detection, allowing the same blocked unit to burn a full retry
   budget each session.

Closes #3331
Closes #3704

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

* fix: use valid LogComponent 'dispatch' instead of 'autoLoop'

* fix: replace empty catch blocks with debug logging in auto/loop.ts

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:11:20 -04:00
drkthng
479ffd127c fix(state): prevent false degraded-mode warning when DB not yet initialized (#3922)
* fix(state): prevent false degraded-mode warning when DB not yet initialized

deriveState() is called during before_agent_start context injection,
before any tool invocation has had a chance to open the DB. Previously,
isDbAvailable() returning false in this path triggered a misleading
"DB unavailable — using filesystem state derivation (degraded mode)"
warning, even though the DB was simply not yet initialized (not failed).

Add a _dbOpenAttempted flag in gsd-db.ts that tracks whether
openDatabase() has been called at least once. The degraded-mode warning
now only fires when the DB was actually attempted and failed to open,
not when it hasn't been initialized yet.

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

* test(state): add regression test for false degraded-mode warning

Adds the test file that was missing from the previous commit,
fixing the CI require-tests gate.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 08:10:57 -04:00
drkthng
d9a3aabf75 fix(async-jobs): suppress stale follow-up for jobs consumed by await_job (#3787) (#3788)
The queueMicrotask() deferral in deliverResult() only prevented duplicate
follow-ups when a job completed *while* await_job was blocked in Promise.race().
For jobs that completed before await_job was called (common in multi-turn
interactive sessions), the microtask had already fired and queued the follow-up
message before suppressFollowUp could run.

Fix: replace queueMicrotask with setTimeout(0), storing the timer handle on
the job object. suppressFollowUp() (new method on AsyncJobManager) cancels
that timer and marks awaited = true atomically, handling both the within-turn
and cross-turn cases.

await-tool.ts now calls manager.suppressFollowUp(id) instead of directly
setting j.awaited = true, which gives it the cancellable timer path.

Adds a regression test specifically for the cross-turn case.
2026-04-13 08:10:09 -04:00
drkthng
a460463371 docs: add change management guide for mid-project reshuffling (#3792)
Covers 10 real-world scenarios users face after milestones are underway:
- Quick fixes and captures during auto-mode
- Steering a running slice
- Inserting new milestones before planned ones
- Parking/unparking milestones to reorder execution
- Dedicated bugfix milestones
- Handling bugs in completed slices
- Course-correcting a milestone that went wrong

Adds navigation entry under Features in docs.json.
2026-04-13 08:09:29 -04:00
mastertyko
eb499116d4 fix(gsd): rebuild STATE.md after unit completion (#3876) 2026-04-13 08:08:34 -04:00
mastertyko
a5b46eaca3 fix(gsd): let doctor heal dispatch fixable warnings (#3875) 2026-04-13 08:08:18 -04:00
mastertyko
daef91f7b8 fix(gsd): preserve experimental preferences in merges (#3847) 2026-04-13 08:07:54 -04:00
mastertyko
b13c980ecc fix(gsd): heal legacy task arrays and evidence rows (#4027) 2026-04-13 08:07:26 -04:00
mastertyko
1d8e7c95ff fix(gsd): unlock depth verification outside guided flow (#4058) 2026-04-13 08:07:07 -04:00
mastertyko
65ba0fc30b fix(gsd): preserve paused auto badge after provider pause (#4062) 2026-04-13 08:05:59 -04:00
NilsR0711
ae1bcc572d chore(pi-ai): regenerate model registry from upstream APIs (#3887)
* chore(pi-ai): regenerate model registry from upstream APIs

Regenerated models.generated.ts by running generate-models.ts against
live provider APIs. Last generated: 2026-04-09.

+48 models added, 19 removed across all providers.
Notable additions: z-ai/glm-5.1 via OpenRouter (closes #4069,
supersedes custom entry in #4055), zai-org/GLM-5.1, z-ai/glm-5v-turbo.

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

* test(pi-ai): add structural and regression tests for models.generated.ts

- Regression #3582: pins qwen/qwen3.6-plus in openrouter
- Regression #4069: pins z-ai/glm-5.1 in openrouter
- Structural invariants across all 23 providers / all models
- Registry shape: exact provider list, model count lower bound
- Removed models guard: decommissioned models must stay absent
- Spot-checks for notable models added in this regeneration

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 08:05:03 -04:00
NilsR0711
ddff956a91 feat(pi-ai): add Alibaba DashScope as standalone provider (#3891)
* feat(pi-ai): add Alibaba DashScope as standalone provider

Adds `alibaba-dashscope` for users with a regular DashScope API key,
separate from the existing `alibaba-coding-plan` free-tier provider.

- types.ts: register `alibaba-dashscope` as KnownProvider
- env-api-keys.ts: map to DASHSCOPE_API_KEY
- models.custom.ts: add qwen3-max, qwen3.5-plus, qwen3.5-flash,
  qwen3-coder-plus with international endpoint and real pricing
- model-resolver.ts: default model qwen3.5-plus
- key-manager.ts: add alibaba-coding-plan and alibaba-dashscope
  to PROVIDER_REGISTRY so /gsd keys add works for both

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

* feat(pi-ai): add qwen3.6-plus to alibaba-dashscope provider

qwen3.6-plus is available on DashScope international endpoint.
Pricing: $0.5/M input, $3/M output (base tier, 0-256K tokens).
Supports thinking mode (reasoning: true).

Source: https://www.alibabacloud.com/help/en/model-studio/model-pricing

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

* test(pi-ai): add tests for alibaba-dashscope provider and key-manager regression

- packages/pi-ai/src/models.test.ts: add describe block covering all 5
  alibaba-dashscope models (presence, base URL, API, provider field,
  context window, paid pricing, per-model reasoning/cost assertions,
  independence from alibaba-coding-plan, failure path for unknown model)
- src/resources/extensions/gsd/tests/key-manager.test.ts: add regression
  tests for #3891 — alibaba-coding-plan was missing from PROVIDER_REGISTRY,
  causing /gsd keys add alibaba-coding-plan to fail silently; also covers
  alibaba-dashscope registration, env var separation, and getAllKeyStatuses

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

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-04-13 08:04:39 -04:00