Repository navigation
chore: upstream reconcile through reference 99eaa993b - #323
Merged
Merged
Conversation
Close the session, wait for listeners to finish journal and live-state cleanup, then remove it and broadcast a workspace-scoped deletion event so clients can drop the session without races.
Arm terminal-notification suppression at the start of agent teardown so late background-task settlements cannot wake the model during quiescence or after the agent is gone.
Save MCP media that the model cannot take into session attachment storage, surface recoverable references in the tool result, and let Read open those daemon attachment URLs.
Mark sessions dirty on mirror writes and reconcile only those sessions on restart, replacing full mtime rescans with a schema-versioned checkpoint and session count.
Expose suggestFiles on the SDK and prefer the engine fs suggest path for extension @ mentions, with the existing directory browse and local search kept as fallbacks.
Seed config synchronously so experimental tower assembles at startup, and return typed enter failures so clients can name the real blocker.
Replace the agent telemetry context service with parent-linked ambient context so session, agent, and turn fields reach every event, and bind scopes from session and agent lifecycle.
Keep the free-row sweep acceptance ledger untracked.
Import a rebranded full-tree snapshot for the deferred engine wave so local main can merge it through the recorded sync path.
Land the rebranded engine-wave freeze on a sync branch so deferred loop-machine and related rows can clear through the recorded merge path.
Keep the English-only product surface after the vendor merge.
Keep a single implementation after the engine-wave snapshot merge left two copies of the helper in the same file.
Restore silently deleted kosong and protocol trees, drop orphaned pre-machine AgentRuntime and turn-budget services, and reapply the local auth surface so the stripped oauth package matches callers.
Align config, kosong, session, and workspace surfaces after the engine-wave vendor merge, strip managed oauth callers, and quarantine obsolete tests that still target deleted pre-machine APIs.
Drop Chinese PR/workflow shipping text brought in by the engine-wave snapshot so the sync branch can clear the English-only brand gate.
Replace Chinese comments and docs residue from the engine-wave snapshot with English so the sync branch does not grow the English-only surface.
Clear remaining Han fixture text and rewrite product CDN/site hosts away from kimi.com product surfaces while keeping provider API hosts.
Strip Han from migration snapshot fixtures and drop product-host parentheticals from login region flags.
Align docs, install URLs, telemetry endpoints, and tests with the Pythinker product hostnames after the engine-wave import.
Bring the loop-machine and human-layer engine snapshot onto main after typecheck and brand gates cleared on the sync branch.
Port Settings Plugins marketplace, permission labels, Esc panel behavior, diff wrap, mention Tab-complete, splash stages, file-preview refresh, skill reload, thicker scrollbar, media ordinals/mention/reorder, and tower mode UI; restage dist-web via build:web without adopting reference bundles.
Large tunnel payloads no longer trip V8 Irregexp stack overflow. Also point credential load at resolveOAuthTokenStorageName after the engine-wave import drift.
Rewritten assets keep a weak ETag so unchanged GETs/HEADs can return 304 instead of retransferring the body.
Strategies return self-describing records with optional prepare effects; the engine chains credentials recovery ahead of configured strategies.
PYTHINKER_CODE_PERMISSION_MODE_REMINDER=0 skips injecting the auto-mode reminder into model context.
Notify the turn via llm.request.retrying so interrupted tool-call streams cannot leak into the next attempt; settle duplicate tool call ids safely.
…agents Rebuild wire persistence so the machine journal projects the same file, flip agent DI ownership to session-spawned actors, and add human-domain compaction. Share model-catalog error codes between kosong and llm-adapter so barrel imports no longer double-register provider.not_found.
Remove the experimental auto_session_title flag so title generation is always available as a surface. Keep the local stub backend (no managed title call). Restore the oauth toolkit modules required by the SDK harness and fix engine import paths broken by the human-wire rename.
Moon waiting-state unit tests expect MOON_SPINNER_FRAMES. SDK/host header tests assert User-Agent only under the product boundary.
Avoid hard-coded pythinker providerType gate in env overlay. Align unsigned feedback signup URL and moon-loader verb label assertions.
…feedback Restore oauth formatDuration seconds→human, register codex login routes, relax brittle compaction token fixtures, restore feedback attachments/codebase. Local-only until 0122b22 matrix settles.
Complete storage stub size hooks, isolate workspace add-dir fixtures, restore ellipsis assertions stripped as brand false-positives.
Align RC setup copy assertion with the three-step product text. Add missing listWorkspaceSkills on the vscode bridge harness mock.
…logout Unpack LocalPlatformSelection and route non-device login through SDK runLogin. Default thinking for non-current model picks. Isolate logout target fixture. Swallow Codex OAuth abort on cancel mid-config.
… API Reinstate FeedbackAttachmentLevel prompts and input dialog. Add auth create/complete feedback upload methods. Coerce feedbackId to number.
…fixtures Guard catalogProviderId and normalize string/object platform selection. Restore TUI banner ● logo path, Remote Control relayKey resolution, V2 export harness, drainStdio exit, fd no auto-download, task/roster routing wire, Chinese search fixtures, and related test/fixture alignment.
Mock rg/spawn for toolExecutor Glob spill recovery so CI without system rg stays green. Align klient model catalog set_default URL with fixture model id. Stub getActiveToolNames on tower profile mocks; optional-call in tower service.
Use mkdtemp for doctor/config/reload/tower fixtures. Unbiased task-id alphabet sampling. Strict Vertex host parse. Cache-dir originals with O_EXCL write. Config domain allowlist. Inspect joinApiUrl + loopback-only base/WS. CodeQL paths-ignore for committed dist-web vendor bundles.
Drop catch comments in towerFeature tests. Always spawn C:\Windows\System32\cmd.exe for .bat/.cmd (never ComSpec).
Keep a single braille activity spinner for waiting, tool, and progress UI.
Merged
elkaix
pushed a commit
that referenced
this pull request
Sep 20, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @pymodel/pythinker-code@0.43.0 ### Minor Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Delete sessions from the session picker: press Ctrl+X on a session, then y to confirm. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Drop the experimental flat entity message protocol and history API; session inspect returns to the transcript surface. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Add per-server deferred MCP tool disclosure so large tool lists stay hidden until selected. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Delete sessions with serialized cleanup so journal and live listeners finish before the session is removed, and broadcast a workspace-scoped deletion event. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebuild telemetry as a scope-bound layered context registry so session, agent, and turn fields flow into every event without a separate agent telemetry context service. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Serve workspace @ file suggestions through the engine fs suggest API so the editor extension matches gateway search scoring. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Turn filesystem watch off by default. Set `[watch] enabled = true` or `PYTHINKER_CODE_WATCH=1` to attach watchers. ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Make the compaction attempt limit configurable with loop_control.compaction_max_attempts (default 5). - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Stop telling the model that the current working directory is always the project root. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Honor dynamically_loaded_tools from official model catalogs when building tool lists. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Preserve original MCP attachments that are omitted from model output, and let Read open those session attachment references. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Exclude time spent with the session closed from goal time budgets. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebaseline the terminal UI library native platform modules and mouse/search behavior. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Include the server token in the Remote Control Local UI link so it opens already signed in. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Remove the 24-hour limit on goal time budgets. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Skip the confirmation prompt for rm -rf commands that target only /tmp or /temp paths. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Track session-index freshness with a dirty journal so restarts reconcile only changed sessions instead of rescanning every mtime. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Deliver session approval and question events past agent filters, and stamp the requesting agent on the wire shape. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Session title generation no longer requires the experimental auto_session_title flag. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Allow steering messages to interrupt waits for background tasks. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Do not record a steer event when an unconsumed steer seeds the next turn after cancel. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Suppress background-task terminal notifications as soon as an agent starts closing, so teardown no longer wakes the model with late task settlements. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Tower mode (experimental, `PYTHINKER_CODE_EXPERIMENTAL_TOWER=1`): fix tower mode never starting when enabled through `[experimental] tower = true` in `config.toml` instead of the environment variable. When tower mode cannot be enabled, the error now names the actual blocker — the disabled experiment, a required restart, or the owning session. When another live session owns the workspace tower, the message also names the owning session's title alongside its id. /tower now also works in a directory that is not a git repository — it runs git init and commits what is there (an empty initial commit for empty directories). - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Tower mode reliability fixes across messaging, worktrees, and the review-to-merge gate. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Compress Remote Control tunnel responses with gzip. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Keep live turn ids above the wire-wide maximum and fold cold transcript rebuilds over the active branch chain. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Add `-y, --yes` to `pythinker upgrade` (alias `pythinker update`) to skip the confirmation prompt and install the update directly. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Warn at startup when a [models] entry in config.toml is missing the model field and cannot be used. ## @pymodel/pythinker-desktop@1.2.0 ### Minor Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Delete sessions from the session picker: press Ctrl+X on a session, then y to confirm. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Drop the experimental flat entity message protocol and history API; session inspect returns to the transcript surface. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Add per-server deferred MCP tool disclosure so large tool lists stay hidden until selected. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Delete sessions with serialized cleanup so journal and live listeners finish before the session is removed, and broadcast a workspace-scoped deletion event. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebuild telemetry as a scope-bound layered context registry so session, agent, and turn fields flow into every event without a separate agent telemetry context service. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Serve workspace @ file suggestions through the engine fs suggest API so the editor extension matches gateway search scoring. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Turn filesystem watch off by default. Set `[watch] enabled = true` or `PYTHINKER_CODE_WATCH=1` to attach watchers. ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Make the compaction attempt limit configurable with loop_control.compaction_max_attempts (default 5). - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Stop telling the model that the current working directory is always the project root. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Honor dynamically_loaded_tools from official model catalogs when building tool lists. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Preserve original MCP attachments that are omitted from model output, and let Read open those session attachment references. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Exclude time spent with the session closed from goal time budgets. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebaseline the terminal UI library native platform modules and mouse/search behavior. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Include the server token in the Remote Control Local UI link so it opens already signed in. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Remove the 24-hour limit on goal time budgets. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Skip the confirmation prompt for rm -rf commands that target only /tmp or /temp paths. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Track session-index freshness with a dirty journal so restarts reconcile only changed sessions instead of rescanning every mtime. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Deliver session approval and question events past agent filters, and stamp the requesting agent on the wire shape. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Session title generation no longer requires the experimental auto_session_title flag. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Allow steering messages to interrupt waits for background tasks. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Do not record a steer event when an unconsumed steer seeds the next turn after cancel. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Suppress background-task terminal notifications as soon as an agent starts closing, so teardown no longer wakes the model with late task settlements. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Tower mode (experimental, `PYTHINKER_CODE_EXPERIMENTAL_TOWER=1`): fix tower mode never starting when enabled through `[experimental] tower = true` in `config.toml` instead of the environment variable. When tower mode cannot be enabled, the error now names the actual blocker — the disabled experiment, a required restart, or the owning session. When another live session owns the workspace tower, the message also names the owning session's title alongside its id. /tower now also works in a directory that is not a git repository — it runs git init and commits what is there (an empty initial commit for empty directories). - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Tower mode reliability fixes across messaging, worktrees, and the review-to-merge gate. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Compress Remote Control tunnel responses with gzip. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Keep live turn ids above the wire-wide maximum and fold cold transcript rebuilds over the active branch chain. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Add `-y, --yes` to `pythinker upgrade` (alias `pythinker update`) to skip the confirmation prompt and install the update directly. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Warn at startup when a [models] entry in config.toml is missing the model field and cannot be used. ## @pymodel/agent-core-v2@0.5.0 ### Minor Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Persist the human runtime mirror in the agent wire journal and spawn agent DI scopes from the session layer. ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Discard streamed attempt state when the LLM requester retries below the turn so interrupted tool-call ids cannot leak into the next attempt. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Compose LLM recovery strategies outside the turn machine with self-describing proposals and opaque prepare effects. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Add `PYTHINKER_CODE_PERMISSION_MODE_REMINDER`: set it to `0` to stop injecting auto permission-mode reminders into the model context. ## @pymodel/pythinker-code-sdk@0.21.0 ### Minor Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Serve workspace @ file suggestions through the engine fs suggest API so the editor extension matches gateway search scoring. ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebuild telemetry as a scope-bound layered context registry so session, agent, and turn fields flow into every event without a separate agent telemetry context service. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Persist the human runtime mirror in the agent wire journal and spawn agent DI scopes from the session layer. ## @pymodel/pi-tui@0.85.0 ### Minor Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Rebaseline the terminal UI library native platform modules and mouse/search behavior. ## pythinker@0.7.6 ### Patch Changes - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/pythinker-code-sdk@0.21.0 - @pymodel/migration-legacy@0.1.17 ## @pymodel/acp-server@0.0.2 ### Patch Changes - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/agent-core-v2@0.5.0 - @pymodel/klient@0.1.3 ## @pymodel/agent-gateway@0.2.4 ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Persist the human runtime mirror in the agent wire journal and spawn agent DI scopes from the session layer. - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/agent-core-v2@0.5.0 - @pymodel/remote-control@0.0.1 ## @pymodel/klient@0.1.3 ### Patch Changes - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/agent-core-v2@0.5.0 ## @pymodel/migration-legacy@0.1.17 ### Patch Changes - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/agent-core-v2@0.5.0 ## @pymodel/remote-control@0.0.1 ### Patch Changes - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Validate Remote Control tunnel request base64 with a linear scan so large payloads no longer crash the tunnel as a generic connection failure. - [#323](#323) [`96d1a0c`](96d1a0c) Thanks [@elkaix](https://github.com/elkaix)! - Reuse unchanged Remote Control assets across page loads with ETag validation instead of retransferring them. - Updated dependencies [[`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c), [`96d1a0c`](96d1a0c)]: - @pymodel/agent-core-v2@0.5.0 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
elkaix
added a commit
that referenced
this pull request
Sep 20, 2026
## Related Issue No issue. Unblocks Release on `main`. ## Problem `#323` copied upstream identity onto `main`: - CLI version `2.1.0` (already on npm) rewound to `0.42.0`, then `#324` published a fake `0.43.0` GitHub tag that npm never received - VS Code publisher `pymodel` (live Marketplace id `pymodel.pythinker` 0.9.7) became `moonshot-ai`, so vsce refused every target: extension name `pythinker` already exists - Homebrew 404'd on `pythinker-code-0.42.0.tgz`; native upload looked for a `@pymodel/pythinker-code@0.42.0` release that does not exist; consistency check compared local `0.42.0` to npm `latest` `2.1.0` Required CI (build/test/lint/typecheck/nix) was already green. Only Release was red. ## What changed - Restore CLI to `2.1.0` and the 2.x changelog - Restore VS Code to publisher `pymodel`, id `pymodel.pythinker`, version `0.9.7`, and the 0.9.x changelog - Put the `#323` changesets back so the next `ci: release packages` PR bumps from `2.1.0` (likely `2.2.0`) instead of from `0.43.0` - Replace leftover `moonshot-ai` branding (capability marker, vis filter comment, migration doc). Left the Moonshot LLM provider id in `packages/oauth` — that names `api.moonshot.ai`, not our publisher After merge, Release should skip-publish npm `2.1.0`, skip-or-no-op native assets already on that tag, and skip Marketplace `0.9.7`. Then merge the regenerated version PR to ship the reconcile work on the 2.x line. ## Checklist - [x] I have read the CONTRIBUTING document. - [ ] I have linked a related issue (external PRs: the issue must have a maintainer's `/approve`. - [x] I have added tests that prove my feature works. - [x] Ran skill, or this PR needs no changeset. - [x] Ran skill, or this PR needs no doc update. EOF ) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added session deletion, workspace file suggestions, configurable compaction attempts, automatic session titles, noninteractive upgrades, and improved Tower mode. - Added deferred MCP tools, attachment access, Remote Control authentication and performance improvements, and configurable filesystem watching. - Rebranded the VS Code extension as Pythinker. - **Improvements** - Improved steering, session cleanup, telemetry, recovery, session indexing, diagnostics, and model configuration warnings. - Closed sessions no longer consume goal time, and the goal-time cap was removed. - **Breaking Changes** - Removed the experimental flat entity protocol and history API. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This was referenced Oct 1, 2026
Restore per-turn model fallback removed in #323 (ensureResolvableModel / ModelFallbackSwitched)
#351
Closed
elkaix
added a commit
that referenced
this pull request
Oct 1, 2026
## Requirement or Bug Remove the legacy `packages/agent-core-v2/src/kosong` request layer. Port the features that existed only there and fix the bugs it caused. Stacked on #349. ## Bug Reproduction Steps 1. Configure a provider with `type = "openai"` and `env = { OPENAI_API_KEY = "sk-..." }`, with no `apiKey` and no process env key. 2. Start a session and send a turn. 3. The auth check fails with `AuthTokenMissingError`, even though the request path would find the key. ## Root Cause 20 of the 62 files under `src/kosong` still loaded at runtime through `app/auth/*` and `app/kosongConfig/*`. kosong keeps its own provider-definition map, and at runtime that map held only the pythinker definitions, because the standard definitions never loaded. So `resolveModelAuthMaterial` / `resolveModelForReady` (auth check) and `envOverlay` (vendor `*_BASE_URL`) ignored provider-`env` values for every non-pythinker provider. Real requests use `llm-adapter` and find them. This is a fundamental fix: one provider-definition registry, the live one. ## Code Changes - Delete `src/kosong` (61 files, about 11k lines). The 19 importers now import the same symbols from `#/llm-adapter/*` (types are identical apart from the import path). - Port features that existed only in the deleted copy, each with a test that fails before and passes after: - **OpenCode billing errors:** a 401/402/403 whose body says "insufficient balance", "insufficient credit", "credits exhausted" or "please recharge" is a provider error, not `provider.auth_error`. Ordinary 401s stay auth errors. - **DSML / Hermes tool calls:** tool calls that some models write as text tags on the chat-completions stream are parsed into real tool calls. - **`modelRecordProviderId`** moved to `llm-adapter/model/model.ts`. - `apps/vis` imports `@pymodel/agent-core-v2/llm-adapter/contract/tokens` instead of the `kosong` subpath. - `scripts/check-identity-freeze.mjs` drops the deleted kosong path. - Test fix: an MCP registry test set the wrong home variable, so it did not isolate the home directory. It now sets `PYTHINKER_CODE_HOME`. The default-model fallback that also lived only in kosong is **not** restored: since #323 the gateway tests require that `default_model` is never rewritten. That is a product decision, tracked in #351. ## Behavior Changes and Affected Users | Behavior | Before | After | Who relies on the old behavior | Escape hatch | |---|---|---|---|---| | Vendor API key in a provider's `env` table | Auth check ignores it, turn fails with `AuthTokenMissingError` | Key is found, turn runs | Nobody (the old behavior was a bug) | n/a | | Vendor `*_BASE_URL` in an `env` provider of non-pythinker type | Ignored by `envOverlay` | Applied | Nobody (bug) | Remove the variable from `env` | | OpenCode 401/402/403 with a billing message | `provider.auth_error` ("not logged in") | Provider error with the billing message, not retried | Clients that map `provider.auth_error` to a re-login prompt for this case | None needed; the old message was wrong | | Chat-completions stream with DSML/Hermes tool tags | Tags shown as assistant text, no tool call | Parsed into tool calls. Text that could start a tag is held back until it is known not to be a tag, and `llm.streaming.finish` arrives after the stream ends | Nobody relies on raw tags | None | | `@pymodel/agent-core-v2/kosong/*` subpath import | Resolves | Gone | `apps/vis` (updated in this PR); no other consumer found in the repo | Import from `.../llm-adapter/*` | Affected modules and coverage: - `app/auth`: `test/app/auth/auth.test.ts` (provider-env key case, fails on `main`). - `app/kosongConfig/envOverlay`: new non-pythinker base-url case (fails on `main`). - `human/llm` openai format and stream: billing-error tests and DSML parser/recovery tests. - Full suites: agent-core-v2 6,491 pass, agent-gateway 1,407 pass, vis-server 173 pass; `tsc` and `tsgo` clean; no-comments and identity-freeze checks pass. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [x] I have linked a related issue (external PRs: issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update.
6 tasks done
elkaix
added a commit
that referenced
this pull request
Oct 2, 2026
…355) ## Requirement or Bug Native `pythinker update` fails with `native binary download returned HTTP 404`, and the 2.4.0 and 2.4.1 release runs on main end red. Related: #354. ## Bug Reproduction Steps 1. Install 2.1.0 with the native installer. 2. Run `pythinker update` and accept 2.4.0 (or 2.4.1). 3. `error: failed to download update 2.4.0: native binary download returned HTTP 404`. On 2.2.0–2.4.1 the same command fails earlier, because `manifest.json` comes back as an HTML page. Release runs 36917123003 (2.4.0) and 36942551995 (2.4.1) fail in `Update Homebrew tap` and `Release lane summary`. ## Root Cause Three separate defects. All three are fixed at the root, with no workarounds. 1. **Missing release asset (affects 2.1.0 clients).** `produce-manifest.mjs` writes `manifest.json` and `latest.json` entries that point at the bare binary `pythinker-code-<target>[.exe]`. It hashes that binary and then deletes it, so only `.zip`, `.zst` and `.tar.gz` get uploaded. A 2.1.0 client downloads the bare file and gets a 404. 2. **Wrong download base (affects 2.2.0–2.4.1 clients).** #323 moved the manifest and binary URLs to `code.pythinker.com/pythinker-code/binaries/<v>/…`. That route has never served a file: the site answers it with its SPA page and HTTP 200. These clients cannot update, and only a reinstall fixes them (#354). 3. **Brew job race (the red X).** `assertPublishedNpmVersion` ran `npm view` once, 24 s after `changeset publish`. npm showed 2.4.1 at 23:56:55, about 4 minutes after the check at 23:53:10. The job failed, and `Release lane summary` fails whenever `BREW_RESULT` is not success. ## Code Changes - `produce-manifest.mjs`: copy the binary into the upload set under the name the manifest uses, with a `.sha256` sidecar. The existing `dist-native-release/*` upload step then ships it. - `constant/app.ts`, `native-manifest.ts`, `native-stage.ts`: replace `pythinkerCodeCdnBinariesBase()` with `pythinkerCodeReleaseAssetUrl(version, filename)`. It returns the GitHub release URL with the encoded tag, which is the same shape as 2.1.0 and as `latest.json`. The `.zst`-first staging path does not change. - `update-brew-formula.mjs`: delete the one-shot `npm view` check. `downloadNpmTarball` already polls for 600 s, retries on 404, and refuses when the budget runs out, so it is the only gate now. - `cdn-consistency.mjs` + `verify-release-consistency.mjs`: the release gate now HEADs every URL that `latest.json` and the release `manifest.json` advertise, and fails on anything unreachable. It retries 403/429. Against the live 2.4.1 release it reports the 6 missing bare binaries, so it would have caught defect 1 on 2.3.0. - `README.md`: a pointer for stuck native installs to #354. ## Behavior Changes and Affected Users | Behavior | Before | After | Who relies on the old behavior | Escape hatch | |---|---|---|---|---| | Native release assets | `.zip`, `.zst`, `.tar.gz` (+ sidecars) | also the bare binary + `.sha256` | nobody; it adds an asset that manifests already named | n/a | | Native client download URLs (built from this tree) | `code.pythinker.com/pythinker-code/binaries/<v>/…` | `github.com/PyModel/pythinker-code/releases/download/<encoded tag>/…` | nobody: the CDN route never returned a file (SPA HTML, 200) | n/a | | Brew tap job when npm is slow to show the version | fails at once | polls the tarball up to 600 s | nobody; the old failure was a false negative | `RELEASE_LANE_BREW=disabled` (unchanged) | | Release consistency gate | checks version strings only | also fails on any unreachable advertised download | release operators | none needed; it only reports real 404s | Populations: - Native installs on 2.1.0 and earlier: they update normally from the next release on (defect 1). - Native installs on 2.2.0–2.4.1: they need one reinstall, because their own updater has the dead URL built in (defect 2, #354). - npm and Homebrew installs: not affected. Contract file touched: `apps/pythinker-code/src/cli/update/*` (CLI tripwire). The removed `/binaries/` base never served data, so no client from a previous release loses anything. Test coverage: - `release-artifacts.test.ts`: every file the manifest names exists in the upload set. Failed before the fix, passes after. - `native-manifest.test.ts`: exact encoded GitHub URLs. - `cdn-consistency.test.mjs`: URL collection, 404s, 403/429 retry. - `update-brew-formula.test.mjs`: the poll is the only gate. - `pnpm test:release` 54/54; app update suites 325/325; `tsc` clean; `pnpm lint` 0 errors. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [x] I have linked a related issue (external PRs: issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Native `pythinker update` now downloads the latest binary from the GitHub release, restoring updates for supported native installations. * If you’re using a native install on version 2.2.0–2.4.1, rerun the installation command once to receive a version that can update normally. After reinstalling, use `pythinker update` for future updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
6 tasks done
elkaix
added a commit
that referenced
this pull request
Oct 2, 2026
When the bound model no longer resolves at turn start, switch to the best-ranked ready model and emit ModelFallbackSwitched, a model-fallback warning, and model_fallback_triggered telemetry. #323 dropped this.
elkaix
added a commit
that referenced
this pull request
Oct 2, 2026
## Requirement or Bug Resolve #351 ## Bug Reproduction Steps Bind a session to a model, then delete that model (or its provider) from the config. The next turn fails with an unresolved-model error. Before #323 it switched to a ready model. ## Root Cause #323 (upstream reconcile) removed `ensureResolvableModel` from `AgentProfileService.resolveModelContext`, together with the `ModelFallbackSwitched` event, the `model_fallback_triggered` telemetry event, and the ranking policy. This PR restores them in the current `llm-adapter` layout. Fundamental fix. ## Code Changes - `llm-adapter/model/default-model-policy.ts`: `rankDefaultModelCandidates`, ported unchanged from the pre-#323 `kosong/model/defaultModelPolicy.ts`. - `profileService.ts`: `resolveModelContext(turnId)` runs `ensureResolvableModel` at turn bind. If the bound alias does not resolve, it switches to the first ranked candidate that does resolve, then emits `ModelFallbackSwitched`, a `model-fallback` `WarningIssued`, and `model_fallback_triggered` telemetry. If no candidate resolves, nothing changes. - `llmRequesterService.ts`: passes `turnId` when it builds the per-turn config. This is the only caller that passes it. Compaction and credential lookups stay read-only. - `profileOps.ts` and `telemetry/events.ts`: event and telemetry definitions restored. - Tests: `test/agent/profile/modelFallback.test.ts` ports the five pre-#323 cases. Three of them fail without the fix. Two test DI setups get `IModelService` and `IProviderService` stubs. Not restored: the global default-model fallback (`settleDefaultModel`). As the issue comment says, #323 made `default_model` never be rewritten, so bringing that back is a product decision. ## Behavior Changes and Affected Users | Behavior | Before | After | Who relies on the old behavior | Escape hatch | | --- | --- | --- | --- | --- | | Turn start with a bound model that no longer resolves | Turn fails | Switches to the best-ranked ready model, with a warning and an event | None found. This restores the 2.0-era behavior from #299 | Pick the model again with `/model` | | New observable event `turn.model_fallback.switched` | Not emitted | Emitted on a switch | Not durable, so no wire or state manifest change (regenerated, no drift) | None needed | Affected module: `agent-core-v2` profile. Full suites pass: agent-core-v2 6498/6498, agent-gateway 1407/1407. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [x] I have linked a related issue (external PRs: issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lands the local upstream-reconcile wave onto
PyModel/pythinker-codemain.origin/main(268d6914c→d91c801a4)99eaa993bHighlights (recent tips)
goods_versionparseTest plan