diff --git a/.changeset/vscode-sign-in-plan-link.md b/.changeset/vscode-sign-in-plan-link.md new file mode 100644 index 000000000..802f62bb1 --- /dev/null +++ b/.changeset/vscode-sign-in-plan-link.md @@ -0,0 +1,5 @@ +--- +"@pymodel/pythinker-code": patch +--- + +Fix the VS Code sign-in plan-upgrade button opening a stale third-party page instead of the Pythinker plan page. diff --git a/apps/vscode/webview-ui/src/components/LoginScreen.tsx b/apps/vscode/webview-ui/src/components/LoginScreen.tsx index 12988869c..21b64a969 100644 --- a/apps/vscode/webview-ui/src/components/LoginScreen.tsx +++ b/apps/vscode/webview-ui/src/components/LoginScreen.tsx @@ -70,12 +70,9 @@ export function LoginScreen({ onLoginSuccess, onSkip }: LoginScreenProps) { }; const handleSubscribe = () => { - // TODO(region-split): derive this from the region profile's siteBase - // (`https://www.kimi.ai/code` for overseas logins). The webview cannot - // resolve the region itself — @pymodel/pythinker-code-oauth is not a - // webview dependency and its region resolver is Node-only — so the - // extension host needs to hand the site URL over the bridge first. - window.open("https://www.kimi.com/code", "_blank"); + // Both region profiles share the same site base, so the plan page URL + // is static and the webview does not need a bridge round-trip. + window.open("https://www.pythinker.com/code", "_blank"); setShowSubscribeDialog(false); }; diff --git a/docs/en/customization/plugins.md b/docs/en/customization/plugins.md deleted file mode 100644 index 381534a92..000000000 --- a/docs/en/customization/plugins.md +++ /dev/null @@ -1,498 +0,0 @@ -# Plugins - -Plugins package reusable Pythinker Code CLI capabilities into installable units: they can add [Agent Skills](./skills.md), custom [agents](./agents.md), automatically load a specified Skill at session start, contribute system-prompt instructions, and declare MCP servers to provide real tool capabilities. They are ideal for sharing workflows with a team, connecting to external services, or installing extensions from the [official plugins](#official-plugins). - -## Installation and Management - -Run `/plugins` in the TUI to open the plugin manager. It is a single panel with four tabs, switched with `Tab` / `Shift-Tab`: - -- **Installed**: Manage installed plugins -- **Official**: Pythinker-maintained marketplace plugins -- **Curated**: Third-party plugins from Pythinker partners in the default marketplace -- **Custom**: Install from a URL - -Common keys: - -| Key | Action | -| --- | --- | -| `Tab` / `Shift-Tab` | Switch between the Installed / Official / Curated / Custom tabs | -| `Space` | Enable or disable the selected installed plugin (Installed tab) | -| `D` | Remove the selected installed plugin (Installed tab) | -| `M` | Manage MCP servers for the selected plugin (Installed tab) | -| `R` | Reload `installed.json` and all manifests (Installed tab) | -| `Enter` | Installed: update if available, or view details · Official/Curated: install or update · Custom: install | -| `I` | View plugin details (Installed tab) | -| `Esc` | Go back or cancel | - -You can also use slash commands directly: - -| Command | Description | -| --- | --- | -| `/plugins` | Open the interactive plugin manager | -| `/plugins list` | List installed plugins | -| `/plugins install ` | Install from a local directory, zip URL, or GitHub repository URL | -| `/plugins marketplace [source]` | Browse the official marketplace, or pass a custom marketplace JSON path or URL | -| `/plugins info ` | View plugin details and diagnostics | -| `/plugins enable ` | Enable a plugin | -| `/plugins disable ` | Disable a plugin | -| `/plugins remove ` | Remove a plugin (requires confirmation) | -| `/plugins reload` | Reload `installed.json` and all plugin manifests | -| `/plugins mcp enable ` | Enable an MCP server declared by a plugin | -| `/plugins mcp disable ` | Disable an MCP server declared by a plugin | - -### Installing from GitHub - -Use `/plugins install ` to install directly from a GitHub repository. Four URL forms are supported: - -- `https://github.com//`: Install the latest release; falls back to the default branch if no release exists -- `https://github.com///tree/`: Install a specific branch, tag, or short commit SHA -- `https://github.com///releases/tag/`: Pin to a specific tag -- `https://github.com///commit/`: Pin to a specific commit - -Network requests only go through `github.com` redirects and `codeload.github.com` downloads; `api.github.com` is not called. - -### Notes - -- Plugin changes apply after `/reload` or in new sessions. After installing, enabling/disabling, or removing a plugin, run `/reload` or `/new`; the current session will not update. -- Local installations are copied to `$PYTHINKER_CODE_HOME/plugins/managed//`, and the CLI always runs from this managed copy. Editing the original source directory after installation has no effect; you must reinstall. -- Removing a plugin only deletes the installation record; the managed copy and original source files remain on disk. -- Plugins are currently installed per-user and apply to all projects; project-level installation scope is not yet supported. - -### Custom marketplace JSON - -Pass a custom marketplace JSON path or URL to `/plugins marketplace `, or set [`PYTHINKER_CODE_PLUGIN_MARKETPLACE_URL`](../configuration/env-vars.md) to override the default catalog. Each entry in the `plugins` array needs an `id` and a `source` (local path, zip URL, or GitHub URL): - -```json -{ - "version": "2", - "plugins": [ - { - "id": "my-plugin", - "displayName": "My Plugin", - "source": "./my-plugin" - } - ] -} -``` - -## Official Plugins - -Official plugins are plugins and built-in product capabilities maintained by Pythinker. There are currently three: - -- **[Pythinker Datasource](#pythinker-datasource)**: Query financial market data, financial news, macroeconomic indicators, corporate registration records, academic literature, Chinese laws and regulations, and official data from intergovernmental organizations in natural language -- **[Pythinker Pythinker Browser Extension](#pythinker-webbridge)**: Let AI drive your own browser to get web tasks done -- **[Pythinker Computer Use](#pythinker-computer-use)**: Let AI operate your desktop apps (macOS and Windows) - -### Installation and Upgrade - -All official plugins share the same installation and upgrade flow: - -1. Run `/plugins` and press `Tab` to select **Official** -2. Find the plugin you want and press `Enter` to install -3. After installation completes, run `/reload` or `/new` to activate it - -::: info Note -Pythinker Pythinker Browser Extension installs in two parts: after the steps above, you also need to [install the browser extension](#install-the-browser-extension) before it works. -::: - -Official plugins do not update automatically. When an update is available, you'll be prompted the next time you use the old version. To upgrade, repeat the installation steps above. - -### Pythinker Datasource - -Pythinker Datasource is the official Pythinker Code data plugin, letting you query financial market data, financial news, macroeconomic indicators, corporate registration records, academic literature, Chinese laws and regulations, and official data from intergovernmental organizations in natural language. No manual API calls or data accounts required. - -Sources include authoritative institutions and leading databases such as the World Bank, IMF, OECD, FRED, WHO, FAO, the National Bureau of Statistics of China, Wind, S&P Capital IQ, SEC EDGAR, Caixin, Xinhua Finance, and Hundsun Juyuan, all traceable to their original publishers. - -You must first complete OAuth login with a Pythinker Code account via `/login`; data queries consume your Pythinker Code plan quota. - -#### How to use - -1. Describe your need in natural language, and Pythinker Code will automatically invoke the data capabilities -2. Explicitly trigger the data query skill with `/skill:pythinker-datasource` - -#### What you can do - -::: details **Live market research** — Want to run a quantitative analysis on a stock? -Pull three years of daily closing prices, MACD, and KDJ signals in a single query, no third-party data platforms needed. -::: - -::: details **Cross-country macro comparison** — Studying supply-chain shifts across China, India, and Vietnam? -Get complete GDP growth, trade volume, and demographic time-series for multiple countries from World Bank data spanning 50+ years, all in one go. -::: - -::: details **Pre-contract risk check** — Need to vet a counterparty minutes before signing? -Type the company name and instantly get business registration, equity structure, litigation disputes, and credit blacklist status, right when you need it. -::: - -::: details **Literature review acceleration** — Tracing the research arc of RLHF for a paper? -Get the most-cited papers, key authors, and core findings in seconds, so your literature review outline takes shape in half the time. -::: - -::: details **On-the-spot legal lookup** — Need to confirm the statute behind a residence-right contract dispute? -Pinpoint the relevant Civil Code articles (full text, authority level, and validity) in one query, then pull a few comparable precedents to back them up, without digging through statute databases. -::: - -::: details **Institutional-grade US equity research** — Writing a deep dive on a US stock? -Pull the annual report, standardized financial metrics, top-50 holders, and consensus estimates in one go, no more juggling multiple data terminals. -::: - -::: details **Financial news and industry data** — Tracking market hotspots or policy moves? -Query Caixin's market news, bond/fund/futures data, and listed-company supply-chain relationships, plus news, policies, announcements, and market flashes from the Xinhua Finance national financial information platform. All sources are authoritative and traceable. -::: - -::: details **Standards lookup** — Need to check compliance against Chinese standards? -Look up national (GB), industry, local, and association standards by number or topic, with status and full-text entry points. -::: - -#### Coverage - -| Category | Scope | -|---|---| -| Stocks & financial markets | Wind, S&P Capital IQ, SEC EDGAR; A-share/HK/US quotes, indicators, financials, valuation, estimates; 8,000+ US-listed filings | -| Financial news & industry data | Caixin, Xinhua Finance; market news and flashes, company announcements, regulatory policy, bond/fund/futures data, credit-violation records, supply-chain ties | -| Macroeconomics | World Bank, IMF, OECD, FRED, China's NBS, WHO, FAO; 50+ years, 189 countries; national/provincial/municipal China indicators (GDP, trade, population, exchange rates, CPI, balance of payments) | -| China standards | National (GB), industry, local, and association standards: IDs, titles, status, details; official full text for some GB and public association standards | -| Corporate data | Registration, equity chain, legal risk, and related-entity graph for mainland Chinese companies | -| Academic literature | Millions of papers in physics, mathematics, CS, quantitative finance, economics, including preprints | -| Legal | Yuandian Legal and other leading legal databases: Chinese laws, regulations, judicial cases; statute search across authority levels; ordinary and authoritative case search | -| Smart screening | Gildata and other well-known databases: natural-language screening of stocks, funds, and fund managers; macro-industry data, research reports, announcements, news | - -#### Billing and limitations - -- Data queries are billed per call and consume Pythinker Code account credits -- The plugin provides read-only queries; no write or trading functionality is available -- Technical indicators and real-time prices are only available during active trading hours -- AI-generated output is for reference only and does not constitute investment or business advice - -### Pythinker Pythinker Browser Extension - -Pythinker Pythinker Browser Extension lets AI drive your browser directly: not an emulator, not a crawler, but the browser you use every day, with your login sessions and cookies. AI can open pages, read content, click buttons, fill in forms, and take screenshots just like you do, taking repetitive web operations off your hands. See the [Pythinker Pythinker Browser Extension site](https://www.kimi.com/features/webbridge) for a product overview. - -#### Install the browser extension - -After installing via `/plugins`, you also need the Pythinker Pythinker Browser Extension extension in your browser before AI can drive it. There are two ways to install it: - -**Option 1: Install from a store (recommended)** - -Open the [Chrome Web Store](https://chromewebstore.google.com/detail/pythinker-webbridge/fldmhceldgbpfpkbgopacenieobmligc) or [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/pythinker-webbridge/bnlffdbcfnanfbknnlaflhlhkocccckg) page and click Add. - -**Option 2: Install manually** - -Use this when you can't reach the stores: - -1. [Download the extension package](https://pythinker-web-img.pymodel.cn/webbridge/latest/extension/pythinker-webbridge-extension.zip) and unzip it -2. Type `chrome://extensions/` in the address bar to open the extensions page, then turn on **Developer mode** in the top-right corner - - ![Turn on Developer mode](../../media/webbridge-dev-mode.jpeg) - -3. Click **Load unpacked** in the top-left corner and select the unzipped `pythinker-webbridge-extension` folder - - ![Load the unpacked extension](../../media/webbridge-load-unpacked.jpeg) - -4. Once installed, the Pythinker Pythinker Browser Extension icon appears in the browser toolbar. Seeing the icon means the installation succeeded, and AI can start working on web pages for you. - - ![The Pythinker Pythinker Browser Extension icon in the browser toolbar](../../media/webbridge-install-success.jpeg) - -#### What you can do - -- **Web automation**: Just say what you need, and AI clicks through pages, fills in forms, reads content, and takes screenshots for you -- **Social trending research**: Automatically browse trending topics on X (Twitter), Weibo, and Xiaohongshu, open the top-liked posts one by one to screenshot and extract key viewpoints, then organize everything into a research library with topic suggestions -- **Job listing collection**: Filter positions on recruiting sites by keyword, city, and job type, and organize titles, links, companies, salaries, and application methods into a table -- **Competitive analysis**: Batch-question multiple AI products and collect their answers to build side-by-side comparison reports -- **Flight price comparison**: Query the same itinerary across multiple travel platforms, record airlines, departure/arrival times, and links sorted by price, and get recommended options - -### Pythinker Computer Use - -Pythinker Computer Use lets AI operate your desktop apps directly, clicking, dragging, scrolling, and typing. The macOS version works silently in the background without taking over your mouse (a few popup actions may still bring an app to the foreground); see [the notes below](#notes-for-the-windows-version) for how the Windows version differs. - -#### Authorization (macOS) - -The first time you use Pythinker Computer Use after installation, it shows an authorization window. Just follow the prompts: - -1. Click **Authorize** next to **Accessibility** and **Screen Recording**, and enable both permissions in System Settings: the former lets it perform clicks, typing, and scrolling; the latter lets it read screen content and locate UI elements -2. Turn on the **Pythinker Code** switch under "Connect local agents", then restart Pythinker Code for it to take effect - -
- -![Pythinker Computer Use authorization window](../../media/pythinker-computer-use-auth.jpeg) - -
- -#### Notes for the Windows version - -The Windows version (WinCU) installs differently from the macOS one: run `/plugins install https://cdn.pythinker.com/pythinker-computer-use-windows/latest/pythinker-cu-win-plugin.zip` in Pythinker Code, then restart after installation. A few things to know before using it: - -- **It may briefly take over your mouse and keyboard**: Unlike the macOS version, the Windows version cannot reliably inject input in the background; it may briefly activate the target window and use your real mouse and keyboard while performing actions -- **System requirements**: Windows 10 version 1903 (Build 18362) or later, or Windows 11, x64; a real interactive desktop session is required, and Windows Server needs Desktop Experience -- **No extra permissions needed**: Windows does not require the Accessibility and Screen Recording grants that macOS does -- **Matching privilege level**: If the target app runs as administrator, PythinkerCU must run at the same privilege level - -#### What you can do - -- **Organize and enter information**: Have AI gather scattered information into Notes, spreadsheets, or your note-taking app, instead of typing everything in by hand -- **Walk through site and app flows**: After changing a page, let AI click through the key flows and screenshot each step to confirm rendering and navigation work -- **Handle repetitive operations**: Repeatedly opening, copying, pasting, and checking can run silently in the background without taking over your mouse -- **Run fixed-step tasks**: For flows with clear steps, spell them out and AI follows along; for example, ask AI to open NetEase Cloud Music and play a specific song -- **Handle software that has no API**: Plenty of professional tools and internal systems have no CLI or API at all; what used to require your own clicking can now be handed to AI, like trimming the first three seconds off a clip in Final Cut Pro and exporting it - -::: warning Note -Don't hand it anything involving money, accounts, or publishing, such as payments and transfers, deleting important files, changing passwords, or posting content. To judge whether a task is suitable, check three things: the result is verifiable, the action is reversible, and the risk of getting it wrong is low. -::: - -## Plugin Manifest - -A plugin is a directory or zip file containing a manifest. The manifest can be placed at either of the following locations: - -```text -/pythinker.plugin.json -/.pythinker-plugin/plugin.json -``` - -When both files exist, `pythinker.plugin.json` takes precedence. - -Example: - -```json -{ - "name": "pythinker-finance", - "version": "1.0.0", - "description": "Finance data and analysis workflows for Pythinker Code CLI", - "skills": "./skills/", - "systemPromptPath": "./SYSTEM.md", - "sessionStart": { - "skill": "using-finance" - }, - "interface": { - "displayName": "Pythinker Finance", - "shortDescription": "Market data and financial analysis workflows" - } -} -``` - -Supported fields: - -| Field | Description | -| --- | --- | -| `name` | Required; serves as the plugin id. Must match `[a-z0-9][a-z0-9_-]{0,63}` | -| `version`, `description`, `keywords`, `author`, `homepage`, `license` | Display metadata | -| `interface` | Shown in `/plugins`: `displayName`, `shortDescription`, `longDescription`, `developerName`, `websiteURL` | -| `skills` | One or more `./` paths within the plugin root; if omitted, root `SKILL.md` is the single Skill root | -| `agents` | One or more `./` paths within the plugin root, pointing to [agent files](./agents.md#custom-agents); if omitted, `agents/` is auto-discovered | -| `sessionStart.skill` | Loads the specified plugin Skill into the main Agent when a new or resumed session starts | -| `skillInstructions` | Additional instructions appended whenever a Skill from this plugin is loaded | -| `systemPrompt` | Inline instructions contributed to the agent's system prompt while the plugin is enabled | -| `systemPromptPath` | A `./` path to a UTF-8 text file; content is appended after `systemPrompt` when both are present | -| `mcpServers` | MCP server declarations; enabled by default, can be disabled from `/plugins` | -| `hooks` | Hook rules run on lifecycle events while enabled; see [Hooks in Plugins](#hooks-in-plugins) | -| `commands` | One or more `./` paths to a directory or `.md` file; registers the Markdown files inside as slash commands. See [Plugin Slash Commands](#plugin-slash-commands) | - -Unsupported runtime fields such as `tools`, `apps`, `inject`, and `configFile` appear as diagnostics and are ignored. - -### System-prompt instructions - -Plugins inject instructions into the agent's system prompt through the `systemPrompt` and `systemPromptPath` fields. This section covers three parts: writing format and read timing, size limits, and the differences between the two engines. - -### Writing format and read timing - -Use `systemPrompt` for a short inline instruction, or `systemPromptPath` to keep longer instructions in a file inside the plugin root. If both fields are present, the inline text appears first, followed by the file content. The file content is read when the plugin is installed or reloaded, so edits take effect only after `/plugins reload`. For example: - -```json -{ - "name": "code-review", - "systemPromptPath": "./SYSTEM.md" -} -``` - -The built-in agent prompt includes instructions from enabled plugins automatically. A custom `SYSTEM.md` or agent file owns its template, so include `${plugin_sections}` where plugin-contributed instructions should appear. If the custom template includes `${base_prompt}` and that effective default already contains the plugin block, do not add `${plugin_sections}` again. See [Custom agents and SYSTEM.md](./agents.md#overriding-the-main-agents-system-prompt-with-systemmd) for the complete variable table. - -### Size limits - -Each field (the inline `systemPrompt` and the `systemPromptPath` file) is limited to 32 KB (UTF-8 bytes): oversized content is ignored and reported in the plugin diagnostics. Across all enabled plugins, one prompt build injects at most 64 KB of instructions; contributions beyond the budget are skipped with a warning, including a single plugin whose inline text and file together exceed that budget. - -### Differences between the two engines - -System-prompt contributions take effect on every Pythinker Code surface: the interactive TUI, `pythinker -p`, and `pythinker web` all run on the v2 engine. - -
-Instruction refresh behavior under the two engines - -New sessions and newly created agents read the contributions from the plugins currently enabled. An in-flight request keeps its existing system prompt. `/plugins reload` refreshes the plugin skill list and requests prompt rebuilds for live agents; use it when you need the change to converge deliberately before the next turn. - -On the v2 engine, installing, enabling, disabling, or removing a plugin updates the catalog immediately, and a later prompt rebuild (for example after compaction or a tool-policy change) may pick up the new sections. The legacy engine keeps each live session's plugin snapshot until `/plugins reload` or a new session. A resumed session starts from its persisted prompt, and later rebuilds follow the engine-specific behavior above. Toggling a plugin's MCP server does not change system-prompt sections. - -
- -## Plugin Slash Commands - -Slash commands save a prompt you use often as a `/command`, so you can trigger it by typing the command instead of retyping the whole thing. - -Here is a minimal end-to-end example. The plugin's directory structure: - -```text -pythinker-finance/ - pythinker.plugin.json - commands/ - report.md -``` - -In the manifest (`pythinker.plugin.json`), the `commands` field points to where the command files live: - -```json -{ - "name": "pythinker-finance", - "version": "1.0.0", - "commands": "./commands/" -} -``` - -The command file `commands/report.md`. The block between the two `---` lines at the top is frontmatter (metadata describing the command); everything below is the prompt sent to the Agent: - -```markdown ---- -description: Pull and summarize a stock's latest financials ---- - -Pull the latest financials for $ARGUMENTS and summarize revenue, profit, and key risks. -``` - -After installing and enabling the plugin, type this in the chat: - -```text -/pythinker-finance:report TSLA -``` - -Pythinker replaces `$ARGUMENTS` in the body with `TSLA`, then runs the prompt. The three details below cover each step. - -### Declaring Commands (the `commands` field) - -`commands` takes a single `./` path or an array of paths, each pointing to a directory or `.md` file inside the plugin root: - -- Pointing at a **directory**: collects every `.md` file under it recursively; each becomes one command. -- Pointing at a **single `.md` file**: registers just that one. -- Pointing at a non-`.md` file or a missing path: appears as a diagnostic (shown in the `/plugins` panel) and is ignored. - -### Writing a Command File - -A command file has two parts: an optional **frontmatter** (the metadata between the two `---` lines at the top, where you set `name` and `description`) and the **body** (the prompt after the `---`). When a field is omitted, it falls back as follows: - -- `name` (the command name): derived from the file's path relative to the declared `commands` path (without `.md`, using `/` separators), e.g. `commands/frontend/component.md` → `frontend/component`. A `name` set in the frontmatter takes precedence. -- `description` (shown in the command list): the first non-empty line of the body (truncated past 240 characters); if the body is empty too, `No description provided.` is shown. - -### Running Commands and Passing Arguments - -Commands are prefixed with the plugin id (their namespace) and registered as `:`, so the command above is actually `/pythinker-finance:report`. This keeps same-named commands from different plugins from colliding. - -Whatever you type after the command replaces `$ARGUMENTS` in the body (above, `TSLA` replaces `$ARGUMENTS`). If the body has no `$ARGUMENTS` but you pass arguments anyway, they are not dropped; they are appended to the end of the body as `ARGUMENTS: `. - -## Skills and Session Start - -Plugin Skills use the same `SKILL.md` format as ordinary [Agent Skills](./skills.md). A typical directory structure: - -```text -my-plugin/ - pythinker.plugin.json - skills/ - using-my-plugin/ - SKILL.md - another-workflow/ - SKILL.md -``` - -`sessionStart.skill` loads a plugin Skill into the main Agent at session start, making it suitable for initialization instructions, workflow rules, or mapping terminology from other tools to Pythinker Code CLI. It only injects text; it does not execute code. - -Regardless of how a Skill is loaded (`sessionStart.skill`, `/skill:`, or automatic model invocation), `skillInstructions` appears alongside that plugin's Skill. - -## Plugin Agents - -A plugin can ship custom agents: declare one or more `./` directories in the manifest's `agents` field (or simply place an `agents/` directory under the plugin root). The agent files inside use the same format as [custom agents](./agents.md#custom-agents) and, while the plugin is enabled, are discovered automatically and can be delegated to as sub-agents by the main Agent. - -```text -my-plugin/ - pythinker.plugin.json - agents/ - reviewer.md -``` - -Plugin agents rank below every other file source: on a name collision, user-level, extra, project-level, and `--agent-file` agents all win over the plugin-provided one, and replacing a built-in agent still requires an explicit `override: true` in the frontmatter. After installing, enabling, disabling, or removing a plugin, the agent list refreshes in a new session (or on `/reload`); on the v2 engine the live session also refreshes after `/plugins reload`. - -## MCP Servers in Plugins - -When a plugin needs real tool capabilities, it can declare `mcpServers` in its manifest, reusing the [MCP](./mcp.md) schema. - -Stdio server (local command): - -```json -{ - "mcpServers": { - "finance": { - "command": "uvx", - "args": ["pythinker-finance-mcp"] - } - } -} -``` - -HTTP server (remote service): - -```json -{ - "mcpServers": { - "docs": { - "url": "https://example.com/mcp" - } - } -} -``` - -For stdio servers, `command` can be a command on `PATH` or a path starting with `./` within the plugin root directory. `cwd` likewise must start with `./` and be within the plugin root directory; otherwise the server is ignored. - -Plugin MCP servers start after `/reload` or in new sessions. To enable or disable a server: - -```sh -/plugins mcp disable pythinker-finance finance -/reload - -/plugins mcp enable pythinker-finance finance -/reload -``` - -## Hooks in Plugins - -A plugin can declare hook rules in its manifest that run on lifecycle events while the plugin is enabled. Each entry uses the same fields as a [`[[hooks]]` rule in `config.toml`](./hooks.md#configuration) (`event`, `matcher`, `command`, `timeout`): - -```json -{ - "hooks": [ - { - "event": "PreToolUse", - "matcher": "Bash", - "command": "node ./hooks/check-bash.mjs", - "timeout": 5 - } - ] -} -``` - -Plugin hooks reuse the same mechanism as global hooks. See [Hooks](./hooks.md) for the event list, the stdin JSON payload, and how exit codes and return values affect the main flow. The differences are: - -- A plugin's hooks are active only while the plugin is **enabled**; disabling the plugin stops its hooks. -- Each hook runs with its working directory set to the plugin root, so `command` can use `./` paths inside the plugin. -- The hook process receives two extra environment variables: `PYTHINKER_CODE_HOME` and `PYTHINKER_PLUGIN_ROOT` (the plugin root directory). - -Installing a plugin never runs its hooks by itself. They only fire when their matching event occurs while the plugin is enabled. - -## Security Model - -Plugins have a limited loading scope. The following operations do not occur during installation or session startup: - -- Command-type plugin tools and legacy tool runtimes are not executed -- All paths must remain within the plugin root directory after symbolic link resolution -- MCP servers of enabled plugins start after `/reload` or in new sessions and can be disabled at any time from `/plugins` -- Broken manifests or unsafe paths appear in `/plugins info ` diagnostics and do not affect other sessions - -## Next steps - -- [Agent Skills](./skills.md) — Learn the `SKILL.md` format and write Skills that ship with your plugins -- [Custom agents](./agents.md) — Agent file format and directory-scope precedence -- [MCP](./mcp.md) — The schema that MCP server declarations in plugins reuse -- [Hooks](./hooks.md) — The global hook mechanism that plugin hooks reuse diff --git a/docs/en/customization/themes.md b/docs/en/customization/themes.md deleted file mode 100644 index 51b06985b..000000000 --- a/docs/en/customization/themes.md +++ /dev/null @@ -1,116 +0,0 @@ -# Custom Themes - -Pythinker Code CLI can use a built-in color scheme or a custom JSON theme file. Custom files live in the themes directory and appear in `/theme` alongside the built-in choices. - -## Built-in color tokens - -Custom themes can override the tokens below. The `dark` and `light` columns show the built-in values; `auto` resolves to one of those palettes at startup, and falls back to `dark` when terminal background detection is unavailable. - -| Token | `dark` | `light` | What it controls | -| --- | --- | --- | --- | -| `primary` | `#4FA8FF` | `#1565C0` | The most-used color. Links, inline code, selected items in dialogs, focus borders, badges, spinners | -| `accent` | `#5BC0BE` | `#00838F` | Secondary highlight. Approval `▶` prefix, device-code box, image placeholder, panes, registry import | -| `text` | `#E0E0E0` | `#1A1A1A` | Body text. Dialog bodies, todo titles, footer model label, Markdown headings, list bullets | -| `textStrong` | `#F5F5F5` | `#1A1A1A` | Emphasized / bold text. Input dialogs, status messages | -| `textDim` | `#888888` | `#454545` | Secondary, dimmed text. Thinking, hints, completed todos, Markdown quotes, footer status bar | -| `textMuted` | `#6B6B6B` | `#5F5F5F` | Faintest text. Counters, scroll info, Markdown link URLs, code-block borders | -| `border` | `#5A5A5A` | `#737373` | Pane and editor borders, Markdown horizontal rule | -| `borderFocus` | `#E8A838` | `#92660A` | Focus / attention border, currently only the approval panel | -| `success` | `#4EC87E` | `#0E7A38` | Success state. `✓`, "enabled", completed | -| `warning` | `#E8A838` | `#92660A` | Warning state. Ask When Needed/Never Ask badges, stale markers, Plan mode hint | -| `error` | `#E85454` | `#B91C1C` | Error state. Error messages, failed tool output | -| `diffAdded` | `#4EC87E` | `#0E7A38` | Diff added lines | -| `diffRemoved` | `#E85454` | `#B91C1C` | Diff removed lines | -| `diffAddedStrong` | `#7AD99B` | `#0E7A38` | Diff intra-line changed words, added and bold | -| `diffRemovedStrong` | `#F08585` | `#B91C1C` | Diff intra-line changed words, removed and bold | -| `diffGutter` | `#6B6B6B` | `#737373` | Diff line-number gutter | -| `diffMeta` | `#888888` | `#5F5F5F` | Diff meta / hunk headers | -| `roleUser` | `#FFCB6B` | `#9A4A00` | User message bullet and text, skill-activation name | -| `shellMode` | `#BD93F9` | `#7C3AED` | Shell mode (`!`) prompt, editor border, and the echoed `$ command` line | - -## Use the custom-theme skill - -You do not need to write the JSON by hand. Run the built-in `/custom-theme [extra text]` skill command to enter the custom-theme workflow; the skill can choose colors, write the file under `~/.pythinker-code/themes/`, validate the hex values, and tell you how to apply it. - -Example invocations: - -- `/custom-theme Create a warm dark theme with amber accents.` -- `/custom-theme Make a light theme based on Solarized, but keep errors easy to see.` -- `/custom-theme Tweak my ember theme so diffs have higher contrast.` - -After activation, the skill usually asks whether you want a light or dark base, what mood or palette you prefer, and whether you have exact colors to include. If you use it to edit an existing theme, make sure it reads and backs up the file before overwriting it. - -## Create a theme - -Add a `.json` file to the themes directory: - -- `~/.pythinker-code/themes/` -- or `$PYTHINKER_CODE_HOME/themes/` when the `PYTHINKER_CODE_HOME` environment variable is set - -Create the directory if it does not exist. **The filename is the theme name**: `ember.json` appears in `/theme` as `Custom: ember`. - -A minimal theme only sets the colors you want to change; the rest fall back to the **base palette** (`dark` by default): - -```json -{ - "name": "ember", - "colors": { - "primary": "#83A598", - "accent": "#FE8019" - } -} -``` - -Fields: - -- `name` (required): the theme identifier. -- `displayName` (optional): a human-readable name. -- `base` (optional): the built-in palette that unspecified tokens inherit, `"dark"` (default) or `"light"`. Set `"base": "light"` when you are building a **light** theme so the tokens you leave out stay readable on a light background (otherwise they fall back to the dark palette). -- `colors` (optional): the color tokens to override, each a 6-digit hex value (e.g. `#FE8019`). - -Use the token names from [Built-in color tokens](#built-in-color-tokens). Any token you omit falls back to the selected base palette, so partial themes are fine: - -```json -{ - "name": "just-blue", - "colors": { - "primary": "#3B82F6", - "roleUser": "#3B82F6" - } -} -``` - -## Select a theme - -Two ways: - -1. **The `/theme` command** (recommended): opens the theme picker, where custom themes appear as `Custom: `. The picker **re-scans the themes directory every time it opens**, so a theme file you just added shows up **without a restart**. -2. **[`tui.toml`](../configuration/config-files.md#tuitoml)**: set `theme` to your theme name: - - ```toml - # ~/.pythinker-code/tui.toml - theme = "ember" - ``` - -## What happens on errors - -Custom themes are designed to never get in your way: - -- **An invalid color value** (not `#` followed by 6 hex digits): that one entry is silently skipped and falls back to the selected base palette; the rest of the colors still apply. -- **An unrecognized token**: ignored, with no effect on other colors. -- **A missing custom theme file or malformed JSON**: silently falls back to the built-in `dark` palette. It does not retry `auto`. - -## Editing the active theme - -If you edit the theme file that is **currently active**, the change is not reloaded automatically. To apply the new colors: - -- run `/reload-tui`, which reloads `tui.toml` and re-applies the current theme (including re-reading the theme file); or -- switch to another theme in `/theme` and back. - -::: warning Note -Re-selecting the **same** theme in `/theme` does not reload it (you get a "Theme unchanged" message). To reload changes to the active theme, use one of the two methods above. -::: - -## Next steps - -- [Configuration files](../configuration/config-files.md#tuitoml) — Full field reference for `tui.toml`, including the `theme` option diff --git a/docs/en/reference/pythinker-acp.md b/docs/en/reference/pythinker-acp.md deleted file mode 100644 index 3575020c2..000000000 --- a/docs/en/reference/pythinker-acp.md +++ /dev/null @@ -1,97 +0,0 @@ -# `pythinker acp` Subcommand - -`pythinker acp` switches Pythinker Code CLI to **ACP (Agent Client Protocol)** mode: it communicates with an ACP client (such as Zed, JetBrains AI Chat, etc.) via JSON-RPC over stdin/stdout, letting the IDE directly drive pythinker's sessions, prompts, and tool calls. - -```sh -pythinker acp -``` - -Once started, the command prints no banner and immediately waits for the ACP client to send an `initialize` request on stdin. Logs are written to stderr (as well as the diagnostic log under `~/.pythinker-code/logs/`), so the ACP channel itself stays clean. - -::: tip Who calls this? -You typically do not need to run `pythinker acp` manually — this command is the subprocess entry point for IDEs. For IDE-side configuration, see [Using in IDEs](../guides/ides.md). -::: - -## Capability matrix - -The table below lists the capabilities declared by the ACP server. The `agentCapabilities` field is returned in full in the `initialize` response, so the IDE can adjust its UI accordingly. - -| Capability | Value | Description | -| --- | --- | --- | -| `loadSession` | `true` | Supports `session/load` to resume an existing session, replaying history on load | -| `promptCapabilities.image` | `true` | Supports ACP `image` content blocks (base64 + mimeType) | -| `promptCapabilities.audio` | `false` | Audio prompts not yet supported | -| `promptCapabilities.embeddedContext` | `true` | Client may send `resource`/`resource_link` embedded resource blocks; text content is injected into the prompt as `...`; blob resources are dropped with a warn | -| `sessionCapabilities.list` | `{}` | Supports `session/list` to enumerate the current user's sessions | -| `sessionCapabilities.resume` | `{}` | Supports `session/resume` to reattach to a session without history replay | -| `sessionCapabilities.close` | `{}` | Supports `session/close` to tear down a live session | -| `sessionCapabilities.delete` | `{}` | Supports `session/delete` to permanently remove a session | -| `sessionCapabilities.fork` | `{}` | Supports `session/fork` to branch an existing session | -| `sessionCapabilities.additionalDirectories` | `{}` | Extra working directories; honored on `session/new` only | -| `mcpCapabilities.http` | `true` | Forwards HTTP MCP services configured by the IDE | -| `mcpCapabilities.sse` | `true` | Forwards legacy SSE MCP services configured by the IDE | -| `auth.logout` | `{}` | Supports ACP `logout` to drop the managed provider's token | - -## ACP method coverage - -With `@agentclientprotocol/sdk@1.x`, the ACP method set is organized by namespace: `core` and `session` cover the main agent flow, while `providers`, `nes` (inline-edit prediction), and `document` (buffer sync) are optional extension surfaces. On the client side, reverse-RPC methods are grouped under `session`, `fs`, `terminal`, and `elicitation`. - -**Summary: the ACP server implements the full core (3/3) and session (11/11) agent-side surface, 10/11 client reverse-RPC methods, and the `session/set_model` extension. Not implemented: `providers/*`, `nes/*`, `document/*`, and `elicitation/complete` — requests for them return `methodNotFound`.** - -### Core agent-side — IDE → agent (3 / 3) - -| Method | Implemented | Description | -| --- | --- | --- | -| `initialize` | Yes | Version negotiation; returns `agentInfo: { name: 'Pythinker Code CLI', version }`, capability matrix, and `authMethods` (first-class `type:'terminal'` plus the legacy `_meta['terminal-auth']` fallback) | -| `authenticate` | Yes | Validates `method_id='login'`; returns `authRequired (-32000)` if the token is missing, `invalidParams (-32602)` for an unknown ID | -| `logout` | Yes | Drops the managed provider's token; subsequent gated calls return `auth_required` again | - -### Session agent-side — IDE → agent (11 / 11) - -| Method | Implemented | Description | -| --- | --- | --- | -| `session/new` | Yes | Accepts `cwd` / `mcpServers` / `additionalDirectories`; returns `sessionId` + `configOptions[]` + `modes` | -| `session/load` | Yes | Restores a session from disk and replays history via `session/update` before the response settles | -| `session/resume` | Yes | Lightweight sibling of `session/load`; skips history replay | -| `session/list` | Yes | Enumerates sessions on disk, filterable by `cwd` | -| `session/fork` | Yes | Branches a source session; `cwd` / `additionalDirectories` / `mcpServers` on the request are ignored with a warning | -| `session/close` | Yes | Best-effort teardown: cancels any in-flight turn, disposes per-session resources, and closes the live session; an unknown id is not an error | -| `session/delete` | Yes | Permanently removes a session and its persisted data; an unknown id returns `invalidParams (-32602)` | -| `session/prompt` | Yes | Accepts `text` / `image` / `resource` / `resource_link` content blocks; streams `agent_message_chunk` | -| `session/cancel` | Yes | Interrupts the current turn (a JSON-RPC `$/cancel_request` for a prompt lands in the same cancel path) | -| `session/set_mode` | Yes | Validates `modeId`; the same underlying mode switch as `set_config_option({configId:'mode'})` | -| `session/set_config_option` | Yes | Unified model / thinking / mode picker dispatcher | - -### Client-side reverse-RPC — agent → IDE (10 / 11) - -| Method | Implemented | Description | -| --- | --- | --- | -| `session/update` | Yes | Streams `agent_message_chunk` / `tool_call*` / `plan` / `config_option_update` / `available_commands_update` | -| `session/request_permission` | Yes | Shared channel for tool approval and question prompts | -| `fs/read_text_file` | Yes | Engine file reads are routed to the client when it advertises `fsCapabilities` | -| `fs/write_text_file` | Yes | Engine file writes are routed to the client | -| `terminal/create` · `output` · `release` · `kill` · `wait_for_exit` | Yes | Shell executions reverse-RPC to the client when it advertises `clientCapabilities.terminal` | -| `elicitation/create` | Yes | Ask-user questions go through the native form when the client advertises `elicitation.form`; RPC failures fall back to `session/request_permission` | -| `elicitation/complete` | No | | - -### Extension methods - -| Method | Implemented | Description | -| --- | --- | --- | -| `session/set_model` | Yes | Carried over from the ACP 0.23 unstable surface as an extension method; equivalent to `set_config_option({configId:'model'})` | - -All methods not listed above return `methodNotFound`. - -## MCP forwarding - -When an ACP client provides `mcpServers` in `session/new` or `session/load`, the ACP server performs the following conversions: - -- `http` → pythinker's `transport: 'http'` configuration -- `stdio` → pythinker's `transport: 'stdio'` configuration -- `sse` → pythinker's `transport: 'sse'` configuration -- `acp` → discarded with a warn log entry - -## Next steps - -- [Using in IDEs](../guides/ides.md) — Zed / JetBrains configuration steps and troubleshooting -- [`pythinker` Command Reference](./pythinker-command.md) — Complete subcommand list diff --git a/docs/en/release-notes/changelog.md b/docs/en/release-notes/changelog.md deleted file mode 100644 index 64d0e2136..000000000 --- a/docs/en/release-notes/changelog.md +++ /dev/null @@ -1,1675 +0,0 @@ ---- -outline: 2 ---- - -# Changelog - -This page documents the changes in each Pythinker Code CLI release. - -## 0.42.0 (2026-09-09) - -### Features - -- Remote Control is now always on; the experimental `PYTHINKER_CODE_EXPERIMENTAL_REMOTE_CONTROL` flag has been removed. See [Remote Control](https://code.pythinker.com/pythinker-code/guides/remote-control.html) for details. -- web: Support permanently deleting sessions from the session row context menu, with a confirmation prompt. -- Add read-only tools to the `/btw` side agent. -- web: Preview images and videos in a reorderable media rail in the composer, mention them in the text on demand, and keep the previews after queueing and sending. -- Accept HEIC, HEIF, and BMP images in prompt attachments and `ReadMediaFile` when the model is served by Pythinker. - -### Polish - -- Collapse finished tool calls in the transcript to a header plus one marked outcome row: short output is shown whole, hidden output is counted (`N more lines`, `+N more`) and revealed by `Ctrl-O`, which the footer advertises while it is available. -- Upgrade the default thinking effort to the recommended level for eligible users. -- The subagent model pool (`[secondary_model]`) is now always on; the experimental secondary-model flag and the `PYTHINKER_CODE_EXPERIMENTAL_SECONDARY_MODEL` opt-out have been removed. -- Add configurable character limits and resumable long-line file reads without repeated output truncation; see [`read`](https://code.pythinker.com/pythinker-code/configuration/config-files.html#read) for details. -- The minidb session-index read model and global search worker are now always on; the experimental flags have been replaced by the `[database]` config section and the `PYTHINKER_CODE_PERSISTENCE_MINIDB_READMODEL` / `PYTHINKER_CODE_SEARCH_WORKER` env vars; see [`database`](https://code.pythinker.com/pythinker-code/configuration/config-files.html#database) for details. - -### Bug Fixes - -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.41.0 (2026-09-04) - -### Features - -- web: Add tower multi-agent collaboration mode (experimental), enabled via the `/tower` command or the composer plus menu; `/tower` supports specifying a base branch (e.g. `/tower add-new-feature`). -- web: Add selection annotation — select text in messages, file previews, the diff and per-turn changes panels, or the terminal to add a comment or quote it into the chat. -- CLI: Add a session rating prompt that invites you to rate the session at appropriate times above the input box. - -### Polish - -- Auto permission mode no longer blocks dangerous commands and commands that cannot be statically analyzed. -- Remind the model of its context budget before automatic compaction, and after compaction point it at the session's event log for exact details. -- web: Rename the three permission modes to Always Ask / Ask When Needed / Never Ask and update their descriptions; switching to Ask When Needed or Never Ask permission mode now warns that files may be modified or deleted directly in that mode. -- web: Esc no longer closes the right detail panel. -- web: Restyle Bash commands in the right-side panel in terminal style. -- Deliver background question answers to the agent directly instead of via a saved output file. -- Subagent final messages under 200 characters are no longer bounced back for expansion. - -### Bug Fixes - -- Fix print mode (`pythinker -p`) losing session records when the run exits on an error or a termination signal. -- Fix print mode (`pythinker -p`) ignoring the `PYTHINKER_DISABLE_TELEMETRY` environment variable. -- Tower mode (experimental): fix tower mode never starting when enabled through `[experimental] tower = true` in config.toml instead of the environment variable, and make `/tower` work in directories that are not git repositories; enablement errors now name the actual blocker. -- Fix background questions being cancelled as soon as the agent finishes its turn. -- Fix resuming a subagent by its agent id after the session is reopened in a new process; the resumed subagent follows the current permission mode and is matched by its own profile in permission rules. -- web: Fix per-turn file change previews showing added/removed lines that never existed and inaccurate line counts when the same file is edited multiple times in one turn; change cards now show only exact line statistics. -- web: Fix the default thinking effort in settings not being settable to the highest level (Max). -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.40.1 (2026-09-02) - -### Bug Fixes - -- Fix the condition for showing the pythinker-cli migration prompt. - -## 0.40.0 (2026-09-02) - -### Features - -- web: Add a Plugins panel to Settings for browsing the plugin marketplace and installing, enabling, disabling, and removing plugins. -- web: Support activating multiple skills from a single message. -- Add the `pythinker session list` command to list sessions from the command line. -- Tower mode (experimental, `PYTHINKER_CODE_EXPERIMENTAL_TOWER=1`): the agent no longer enters tower mode on its own — turn it on with `/tower on` or `/tower `. -- The subagent model setting (`[secondary_model]`) graduates from experimental to stable. -- Block dangerous shell commands such as shutdown, reboot, or rm -rf in Auto mode, and always ask before running them in Manual and YOLO modes; disable the guard with `[permission] dangerous_command_guard = false` or `PYTHINKER_CODE_DANGEROUS_COMMAND_GUARD=false`. - -### Polish - -- Preserve comments, key order, and formatting in config.toml when configuration values are updated. -- Remove the workspace restriction on the Bash tool's cwd parameter. -- Default the workspace trust prompt selection to "Trust this folder" instead of "Don't trust". -- The `pythinker acp` subcommand no longer honors `PYTHINKER_CODE_LEGACY_FLAG`; it always runs on the default agent engine. -- web: Add a code wrap toggle to the diff panel and streamline its header. - -### Bug Fixes - -- Honor explicit `[experimental]` config entries over the `PYTHINKER_CODE_EXPERIMENTAL_FLAG` master switch, so a flag set to `false` in config.toml stays off; per-feature `PYTHINKER_CODE_EXPERIMENTAL_` variables still override both. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.39.1 (2026-08-28) - -### Bug Fixes - -- web: Fix switching the permission mode in one session changing it for every session; the permission mode is now scoped per session. -- web: Fix signed-in users without a usable model being wrongly asked to sign in (and getting stuck there on web); the send gate now offers picking or configuring a model instead. -- web: Fix the first IME (or keyboard) character being silently swallowed after clicking the composer placeholder. -- web: Fix attachments in a newly created session still showing as uploading after the upload has finished. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.39.0 (2026-08-27) - -### Features - -- Add Remote Control as an experimental feature for accessing a local web session remotely. Enable it with `PYTHINKER_CODE_EXPERIMENTAL_REMOTE_CONTROL=1`, then run `pythinker rc`, `pythinker web --remote-control`, or `/remote-control` to start it. -- Add experimental tower mode for multi-agent orchestration; set `PYTHINKER_CODE_EXPERIMENTAL_TOWER=1`, then run `/tower on` and `/tower ` to start. -- Add an optional `fork` parameter to subagent and dynamic_workflow tools that starts the subagent with a snapshot of the calling agent's conversation history; set `PYTHINKER_CODE_EXPERIMENTAL_SUBAGENT_FORK=1` or `subagent_fork = true` under `[experimental]` in config.toml to enable it. -- web: Allow moving a running foreground Bash command or subagent to the background via the "Move to background" button on the running card. -- web: Add a flat/by-workspace tab to the mobile session list. -- Add the Tencent CloudBase plugin to the curated marketplace. -- Add a dedicated `[dynamic_workflow] timeout_ms` config option (or the `PYTHINKER_CODE_DYNAMIC_WORKFLOW_TIMEOUT_MS` env var) for AgentDynamicWorkflow subagent timeouts, which no longer follow `[subagent] timeout_ms`. - -### Polish - -- web: Revamp the right sidebar as a multi-tab panel. -- web: Improve composer interaction, including the presentation of file, folder, and media attachments. -- web: Improve mobile UI styling. - -### Bug Fixes - -- Fix file tools and shell working directories failing to resolve Git Bash paths such as /c/Users or /tmp on Windows. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.38.0 (2026-08-20) - -### Features - -- Support two OAuth login methods — the regional OAuth endpoints. -- Add the WaitFor tool: the agent can now wait for a background task to finish within the current turn instead of ending the turn and being re-invoked. -- Add 13 data sources to the official Pythinker Datasource plugin — Chinese government data (NDA/NBS) and standards (GB/HB/DB/TT), eight international organization datasets (WHO, FAO, UNSD, ECB, Eurostat, UNICEF, OECD, FRED), Xinhua Finance, and Caixin. Update the plugin from the Official tab in /plugins. -- web: Add a Pin action to the chat header more-menu. - -### Polish - -- Edit and Write now require reading an existing file before modifying it. - -- Collapse long `!` shell command output instead of flooding the transcript. Press ctrl+o to expand or collapse it together with tool output. - -### Bug Fixes - -- Fix config.toml entries being lost when the file had a syntax error or was edited outside the app. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.37.2 (2026-08-19) - -### Polish - -- web: Settings gains a Lab tab with a new multi-tab sidebar toggle; when enabled, the sidebar shows the Open / Done / Workspaces tabs. -- Make several refinements and internal improvements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.37.1 (2026-08-18) - -### Bug Fixes - -- Fix pasted images and videos failing to reach the model. - -## 0.37.0 (2026-08-18) - -### Features - -- Activate multiple skills in a single prompt. Type `/` after whitespace to insert a skill token. -- The Windows native (single-binary) CLI now supports automatic updates. -- web: The sidebar gains Open / Done / Workspaces tabs, and sessions can be marked as done. -- web: Add a session management page. - -### Polish - -- Queue slash skill commands entered while the agent is busy instead of rejecting them. -- web: @-mentioned files, folders, and skills in chat messages now render as icon pills. -- web: The browser tab title now shows the current workspace directory name. -- web: The search dialog now finds workspaces too, and picking a workspace or session result expands the sidebar and scrolls the item into view. -- web: Renamed the Subagent panel to "Background Agent". -- Warn when a typed `/goal` objective exceeds the 4000-character limit, and keep the input if it is rejected. - -### Bug Fixes - -- Fix Gemini tool-calling sessions failing on follow-up requests. -- web: Fix Ctrl+K in the composer opening session search on macOS — session search now only answers to Cmd+K. -- web: Fix the Background Agent panel showing incorrect task counts and statuses. -- web: Fix pasting a copied folder into the composer failing the upload with a connection error — folders are now skipped instead. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.36.1 (2026-08-14) - -### Features - -- web: Generate session titles with AI (experimental). Off by default — set `PYTHINKER_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE=1` (or the master flag `PYTHINKER_CODE_EXPERIMENTAL_FLAG=1`) to turn it on. - -### Polish - -- web: Polish the Plan, Goal, and DynamicWorkflow toggles in the composer, which now live in the + menu next to the input box. - -### Bug Fixes - -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.36.0 (2026-08-13) - -### Features - -- Upgrade the experimental subagent model setting to a model pool: the `[secondary_model]` section can now hold a set of candidate models with descriptions, and the main agent picks from them per spawn based on the task. - - Set `PYTHINKER_CODE_EXPERIMENTAL_SECONDARY_MODEL=1` (or the master flag `PYTHINKER_CODE_EXPERIMENTAL_FLAG=1`) before starting Pythinker to enable it. - - Recommended setups: - - - Minimal: run `/secondary-model` in the TUI, or write a single `default_model` line in `config.toml`, to make every subagent run the same model by default; add `force = true` to pin that choice so the main agent cannot override it. - - Declare a named pool with a one-line scenario description for each alias — the descriptions are what the main agent sees when choosing: - - ```toml - [secondary_model] - default_model = "pythinker-code/kimi-for-coding-highspeed" - [secondary_model.models] - "pythinker-code/kimi-for-coding-highspeed" = "Fast and cheap — good for daily refactoring, code explanation, and small edits." - "pythinker-code/k3" = "Strong at complex reasoning and deep debugging — pick it for hard problems." - ``` - - See the [subagent model pool docs](https://code.pythinker.com/pythinker-code/en/configuration/config-files.html#subagent-model-pool) for details. -- Add an experimental fullscreen TUI mode. Set the `PYTHINKER_CODE_TUI_FULL_SCREEN=1` environment variable to enable it. -- Support rendering LaTeX math formulas (`$…$` / `$$…$$`) in TUI messages as Unicode formulas. - -### Bug Fixes - -- Show project MCP launch targets in the workspace trust prompt, default to declining trust, and resolve `fd` and `stty` binaries to absolute paths so untrusted workspaces cannot plant bare-name executables before confirmation. -- Fix sessions failing with a provider 400 error on every follow-up request after a turn is interrupted while the model is still thinking, on strict OpenAI-compatible providers (e.g. DeepSeek). -- Fix Ctrl+C being ignored during automatic retries of failed API requests. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.35.0 (2026-08-12) - -### Features - -- Add the Modern Web Guidance plugin to the bundled plugin marketplace. Run `/plugins` and select Modern Web Guidance to install it. -- Show the live work progress of background subagents in the `/tasks` panel. - -### Bug Fixes - -- Fix coder subagents spawning further subagents by default. -- Fix the token counts reported after compaction reading far below the real context size; they now match the numbers shown while the session runs. -- Fix two binary-planting risks on Windows. -- Fix several known issues and make various refinements. See the [changelog on GitHub](https://github.com/PyModel/pythinker-code/blob/main/apps/pythinker-code/CHANGELOG.md) for more technical entries. - -## 0.34.0 (2026-08-06) - -### Features - -- web: Add a flat view to the sidebar session list. -- The Pythinker Computer Use plugin now supports Windows x64 — install it from `/plugins`. -- Show a cache-expiry reminder when resuming or sending after a long idle. Set [`cache_expiry_hint`](https://code.pythinker.com/pythinker-code/en/configuration/config-files.html#tui-toml) to `false` to disable it. - -### Polish - -- web: Subagent tasks show their model and thinking level. -- web: Show a failure card with one-click resume when a model request fails. -- web: Show retry progress (attempt N of M) in the working status during automatic retries. -- Show browser extension links and activation steps after installing Pythinker Pythinker Browser Extension. - -### Bug Fixes - -- Fix UTF-16 LE/BE text files (with or without a BOM) failing to load. -- web: Fix attachments being dropped when sent with a skill command. -- web: Fix the model picker overflowing the screen when many models are available. -- web: Fix a file path with spaces opening the Documents folder instead of the file on Windows. -- web: Fix the thinking level resetting to the model default when a new session starts with a skill command. -- web: Fix manually cancelled sessions showing an error marker in the sidebar; it now appears only when the last turn failed. -- web: Fix IME composition while renaming a session — Enter and Esc no longer act mid-composition. -- web: Fix dragging to select text while renaming moving the whole list item. -- web: Fix the background-tasks and todos pills jumping to the top when the plan approval dialog expands. -- web: Fix the chevron direction on the "show less" button of the changed-files summary card. -- Fix `pythinker -p` exiting before background tasks and subagents finish. -- `/feedback` now works for signed-in users on any model; signed-out users see the sign-up page and GitHub Issues links. -- Fix removing an MCP server breaking open sessions: its tools stay visible but calls fail with a removal notice. -- Fix the last turn's outcome being lost across server restarts — failed turns now stay flagged in session lists and resumed sessions. -- Fix resumed sessions showing background-task completion as raw protocol text instead of a status card. - -## 0.33.0 (2026-08-05) - -### Features - -- Add Pythinker Computer Use and Pythinker Pythinker Browser Extension as built-in official marketplace entries in the v2 CLI. Installing from `/plugins` sets up the latest managed runtime and plugin together, reports incomplete manual steps, and supports retrying interrupted setup. -- web: Add and manage custom providers in settings. -- web: Pin sessions to the top of the sidebar. -- web: Set an emoji for the session title. -- web: Show the signed-in account and plan usage. -- Add /bug as an alias for the /feedback slash command. Type /bug to submit feedback. - -### Polish - -- Ask whether to trust the current folder on startup. -- `/fork` no longer switches to the forked session: the current session stays active and its background tasks keep running. Find the fork in `/sessions`. -- web: Overhaul the UI/UX and fix known issues. -- Start the interactive TUI without creating a session. -- Rename the partner plugin marketplace tab to Curated and clarify that it contains third-party plugins from Pythinker partners. - -### Bug Fixes - -- Fix all tool calls failing with spawn EBADF on macOS when a skill folder contains a very large file tree. -- Fix MCP OAuth re-authorization always failing with "Invalid redirect URI"; the stale client registration is now dropped and re-created with the current callback URI. -- Ensure the first request waits for MCP startup to finish while the interface still opens immediately. -- MCP tool results now surface the spec-defined `structuredContent` field and `_meta` server metadata to the model instead of silently dropping them, so servers that return their machine-readable contract in these fields work the same as on other MCP hosts. -- Fix built-in capability availability and installed status in `/plugins`, preserve legacy Pythinker Browser Extension skills as backups during updates, and prevent Computer Use updates from duplicating or disconnecting MCP servers. - -### Refactors - -- Run the CLI surfaces (interactive TUI, `pythinker -p`, `pythinker acp`, `pythinker export`, `pythinker provider`) on the agent-core-v2 engine by default. Set `PYTHINKER_CODE_LEGACY_FLAG=1` to fall back to the legacy engine. - -## 0.32.0 (2026-08-04) - -### Features - -- Add four hook events: `TurnStarted`, `UserPromptQueued`, `TaskStarted`, and `SessionHeartbeat`. Configure them under `[[hooks]]` in `config.toml` — see [Hooks](https://code.pythinker.com/pythinker-code/en/customization/hooks.html) for details. - -### Polish - -- Rename two `[loop_control]` keys: `max_retries_per_step` → `max_attempts_per_step` and `max_steps_per_run` → `max_steps_per_turn`; the old keys stop working with a rename warning at startup — see [loop_control](https://code.pythinker.com/pythinker-code/en/configuration/config-files.html#loop-control). -- Add a `[token_counting]` config section: when a provider doesn't report token usage, switch the context-size display to local estimates — see [token_counting](https://code.pythinker.com/pythinker-code/en/configuration/config-files.html#token-counting). - -### Bug Fixes - -- Fix answers to interactive question prompts being rejected when the model provider returns tool call IDs containing colons (some OpenAI-compatible gateways). -- Fix automatic context compaction getting stuck retrying an oversized request until it fails. -- Fall back to the built-in models.dev catalog snapshot when the public catalog is unreachable, so importing a known provider still works offline or in blocked networks. -- Fix the context window limit showing as 0 when no model is configured; it now falls back to the default model. -- web: Fix dark-mode monochrome controls and align the chat composer corner radius with the design system. -- Fix the `/login` already-logged-in confirmation being hard to read; it now uses the success color. - -## 0.31.1 (2026-07-31) - -### Polish - -- Reduce frequent full-screen redraws in the TUI. -- Preserve the assistant's partial output when a turn is interrupted with Esc, and remind the model that the previous turn was deliberately interrupted. -- web: Order permission modes from safest to most permissive across settings surfaces, and fix the swapped yolo/auto risk colors in the status panel and mobile settings. -- web: Enable Monaco-based highlighting for code blocks, and fix line numbers overlapping or drifting out of alignment in fallback-rendered code blocks. - -### Bug Fixes - -- Fix sporadic "model is not configured" errors when starting pythinker web, caused by the background provider-model refresh transiently clearing the model catalog while the first session was being created. -- web: Fix new sessions showing the thinking level (e.g. Max) while the first message actually ran with thinking off. -- web: Make the @ file mention work in a new-session draft, before the first prompt creates the session. -- web: Fix chat code blocks rendering in the proportional UI font at the wrong size after the markdown renderer upgrade, and align the loading fallback with the highlighted block. - -## 0.31.0 (2026-07-30) - -### Features - -- Support Markdown-defined custom agents on agent-core. -- Add the /secondary_model slash command to configure the secondary model used by subagents (experimental; enable it in /experiments first). -- Plugins can contribute custom agents, discovered automatically and available for sub-agent delegation. -- Plugins can contribute system prompt instructions through `systemPrompt` or `systemPromptPath` in `pythinker.plugin.json`. - -### Bug Fixes - -- Remove the blocking `block`/`timeout` wait from the TaskOutput tool so checking a background task can no longer stall the conversation; it now always returns an immediate snapshot, and completion still arrives via automatic notification. -- Fix sessions missing from the session picker when their cached metadata predates the archived flag. -- Fix request headers not being passed correctly on some requests. - -## 0.30.0 (2026-07-29) - -### Features - -- Add a customizable footer status line, configured via `[status_line]` in `tui.toml`. - -### Polish - -- Show a quota note after installing official plugins that bill against plan quota (such as Pythinker Datasource). -- Show a notice when an official plugin used in the session has an update available — run /plugins to update. -- Remove the 50 MB size limit on file uploads to the built-in server. - -### Bug Fixes - -- Fail fast when account quota or balance is exhausted instead of silently retrying for ~3 minutes. -- Stop the turn after repeated invalid tool calls instead of retrying indefinitely. -- web: Fix garbled line numbers in code blocks. - -## 0.29.2 (2026-07-27) - -### Bug Fixes - -- Fix goal pursuit pausing when a goal turn hits the per-turn step limit (`loop_control.max_steps_per_turn`). -- Fix messages sent during goal pursuit being rejected. -- Fix /undo to restore conversation history, todo lists, plan mode, and task notifications consistently. -- web: Fix copying selected chat text over plain HTTP overwriting the clipboard with an event placeholder. - -## 0.29.1 (2026-07-24) - -### Features - -- Add global default MCP server timeouts in `config.toml` and env vars. -- Add environment variables to configure the web search and web fetch services without OAuth login. -- Add experimental secondary-model bindings for newly spawned subagents, including per-agent model preferences and subagent-only model overrides. - -### Bug Fixes - -- Fix loss of thinking content with OpenAI-compatible endpoints that return reasoning under a different field name (e.g. newer vLLM). - -## 0.29.0 (2026-07-22) - -### Features - -- web: Support defining agents in Markdown files, declaring system prompt, name, description, and tool permissions. [Details](https://code.pythinker.com/pythinker-code/en/customization/agents.html#agent-file-format) -- web: Permanently override the main agent's system prompt with SYSTEM.md. [Details](https://code.pythinker.com/pythinker-code/en/customization/agents.html#overriding-the-main-agent-s-system-prompt-with-system-md) -- web: Globally enable or disable tools across all sessions via config.toml. [Details](https://code.pythinker.com/pythinker-code/en/configuration/config-files.html#tools) -- Videos attached to a prompt now reach the model together with the prompt, with no extra tool round trip. -- Support selecting a thinking effort level from ACP clients. -- Add environment variable overrides for agent loop and background task limits. - -### Polish - -- Import many more providers from the models.dev catalog. -- Improve TUI performance and resume speed for long-running sessions. -- Reconnect a dropped MCP server connection automatically when one of its tools is called, and retry the call once. -- Remove red coloring from syntax highlighting in code previews and markdown code blocks. -- Add a reminder for third-party install sources to use the official installer in the update prompt. - -### Bug Fixes - -- Fix sessions getting stuck with a provider "message must not be empty" error after a content-filtered response. -- Fix cancelled model requests being wrapped as retryable provider errors. -- Fix thinking levels being offered for models that do not support them. -- Fix config environment overrides being persisted into config.toml while the env var is set. -- Send the session prompt cache key to OpenAI and OpenAI Responses providers. -- Fix ReadMediaFile failing on videos when the provider has no file upload channel. -- Fix goal mode continuation prompts leaking into the transcript when resuming a session. -- web: Show transparent images over a checkerboard canvas. -- Remove references to the non-existent `pythinker resume` command from the scheduled-task tool descriptions. - -## 0.28.1 (2026-07-20) - -### Features - -- Allow ACP sessions to start with configured non-OAuth model credentials instead of requiring terminal login. - -### Polish - -- Run web servers foreground-only end to end: the /web slash command now always starts a new server, and the `pythinker web kill` / `pythinker web ps` subcommands are removed — foreground servers stop with Ctrl+C. `pythinker server kill` remains as a deprecated fallback that only stops servers started by a version before 0.28.0. - -### Bug Fixes - -- Fix running subagents not observing permission mode switches made after they started. - -## 0.28.0 (2026-07-20) - -### Features - -- **Breaking:** - - The `pythinker server` command tree is deprecated; use `pythinker web` instead. - - `pythinker web` now runs in the foreground of the current terminal and opens the browser; stop it with Ctrl+C. - -### Polish - -- Thinking effort persists only levels below the model's top tier (max). -- web: Add a note in the model switcher that switching models or thinking effort invalidates the existing prompt cache. - -### Bug Fixes - -- Correct the YOLO and Auto permission mode descriptions: YOLO auto-approves tool actions but the agent may still ask questions, while Auto is fully autonomous and never asks. -- Fix the web backend ignoring symbolic links when loading AGENTS.md files and reading files. - -## 0.27.0 (2026-07-17) - -### Features - -- Add the /copy slash command to copy the last assistant message to the clipboard. -- Using an API key for Pythinker coding models now also fetches the latest model list automatically. - -### Polish - -- OAuth connection errors now include the underlying network cause (DNS, refused connection, TLS, or timeout) instead of a bare "fetch failed". - -### Bug Fixes - -- Fix repeated request rejections after an interrupted model response. -- Fix the built-in URL fetch tool's network safeguards: crafted domains and redirect chains can no longer reach loopback or internal network services. -- web: Fix LaTeX formulas rendering as garbled overlapping text when the web UI is accessed over the network. -- web: Fix queued messages silently re-sending previously uploaded files when a session is reopened. -- web: Remember the thinking level per model, fixing an empty, unresponsive thinking picker when the model doesn't support the stored level. -- web: Fix duplicate workspace groups on Windows when the same folder is opened with different path spellings; its sessions now list under one merged group. -- Fix AGENTS.md files installed as symbolic links being ignored by the web backend. -- Fix Esc and Ctrl+C cancelling compaction instead of closing an open /btw panel. -- Fix whitespace-only thinking content rendering as a blank line in the transcript. -- Fix `/export-debug-zip` and `pythinker export` overwriting the previous ZIP on repeated runs for the same session; the default filename now includes a timestamp. - -## 0.26.0 (2026-07-16) Say hi to the BIIIG DAY! - -### Polish - -- Expand the coder subagent tool set to include background tasks, todo lists, plan mode, skill invocation, and nested agents, mirroring the main agent's capabilities. -- Warn in the `/model` and `/effort` pickers that switching invalidates the existing prompt cache, and hint to use `/new` to avoid extra token costs. -- web: Refresh the model catalog for all providers when opening the model picker, so newly available models always show up. -- Optimize the unit formatting of the context usage display. - -### Bug Fixes - -- Fix a resumed session being marked as just updated and jumping to the top of the session list without any new activity. -- Fix the context size indicator under-reporting the model's actual context usage. -- Fix Pythinker-provider models routed through the Anthropic protocol incorrectly showing reasoning effort options. -- Honor an explicit thinking "off" on OpenAI-compatible (chat completions) providers. -- Report when users stop tasks and preserve other stop reasons in model context. -- Fix a race where resuming a background subagent right after it was manually stopped could fail with an "already running" error. -- Replay empty thinking content verbatim instead of substituting a placeholder space on Anthropic-compatible and Pythinker preserved-thinking endpoints. -- Keep legacy migrations idempotent across multiple Pythinker homes and report damaged or unmapped sessions instead of silently skipping them. -- web: Fix the sidebar resize handle being covered by the chat composer background. - -## 0.25.0 (2026-07-16) - -### Features - -- web: Attach any file type in chat — files can be dropped anywhere in the window, and sent files, images, and videos show as chips in the message bubble. - -### Polish - -- web: Show full diagnostics for model request failures. -- Apply official Anthropic effort profiles and a 128k output fallback for unknown models. - -### Bug Fixes - -- Fix the web server bearer-token check being bypassed by percent-encoded API paths, which allowed unauthenticated access to every API route. -- Fix the session filesystem API following symlinks that point outside the workspace, which allowed accessing host files beyond the session directory. -- web: Keep session activity indicators in sync with agent work and prevent duplicate streamed content after session activation races or LLM retries. -- Fix custom-named models on Anthropic-compatible providers starting new sessions with thinking effort off and not showing the thinking control in ACP clients. -- Honor adaptive_thinking = false on Anthropic-compatible models by omitting the effort parameter from requests. -- web: Fix the Content-Security-Policy on non-loopback server binds blocking the web UI's theme bootstrap script and bundled fonts. -- Fix sessions failing to be created when the workspace directory is given through a symlink. -- Fix the CLI exiting unexpectedly when reading an image from the clipboard fails; it now falls back to pasting text. -- web: Fix completed background subagents losing their final output after a session reload. -- web: Fix Enter not confirming modal confirmation dialogs in dev builds. -- web: Fix a background subagent showing up as two identical rows in the agents dock panel during streaming. -- Fix the diagnostic log missing the actual error when the CLI exits unexpectedly. - -## 0.24.2 (2026-07-15) - -### Features - -- Add a builtin `/check-pythinker-code-docs` skill that automatically answers Pythinker Code product questions with official-docs sources. - -### Polish - -- Align `pythinker -p` behavior across engines: `print_background_mode` and `print_max_turns` now apply, and `/goal` runs stay alive until the goal finishes. -- `pythinker -p` now stays alive by default while background tasks are pending, with no effective wait or turn limit, and feeds each completion back to the agent. Set `print_background_mode = "exit"` or `"drain"` to restore the old exit-after-one-turn behavior. -- `pythinker -p` background tasks and subagents no longer time out by default (interactive mode is unchanged); restore limits with `[background] bash_task_timeout_s` or `[subagent] timeout_ms`. -- Subagent timeout now defaults to 2 hours everywhere; override with `[subagent] timeout_ms` or `PYTHINKER_SUBAGENT_TIMEOUT_MS`. -- The per-step LLM retry limit is raised from 3 to 10 attempts, so transient provider failures (429 / overload) are retried before a turn fails; tune with `loop_control.max_retries_per_step`. -- Workspaces now stay in sync: new sessions register automatically, missing workspaces are restored at startup, and removed ones stay removed. -- `pythinker web` now logs failed requests and key operations so daemon issues are easier to diagnose. -- web: AgentDynamicWorkflow cards now stay expanded while subagents are still running. -- web: Minimized plan review and question cards now use an upward chevron for expand. - -### Bug Fixes - -- web: Fix mobile layout on iOS, including the composer, safe areas, and toasts. -- Fix new sessions not opening in older CLI versions. -- Fix completion notifications firing early when a subagent finished while the main turn was still running. -- Fix the web UI showing the wrong CLI version. -- Fix Gemini tool call IDs colliding across turns and merging dynamic_workflow runs into one card. -- web: Show server error details when actions like stopping or archiving a session fail. -- web: Fix long responses stalling after the tab was backgrounded. -- web: Fix code block copy buttons over plain HTTP. -- web: Keep loaded sessions visible when the session list fails to reload. -- web: Restore the AgentDynamicWorkflow member list after a page refresh. -- web: Fix session titles not generating when the first message is a slash command. -- web: Show each message's actual send time after reloading a session. -- Fix several goal-mode issues around budgets and turn limits, pausing and resuming, crash recovery, final status messages, and invalid persisted goal records. -- Fix replaced goals being able to affect the new goal's budget, and reject subagent goals consistently. -- Correct the guidance shown when a goal cannot be paused or resumed. - -### Refactors - -- Rename the dynamic tool loading capability from `select_tools` to `dynamically_loaded_tools`; behavior is unchanged. - -## 0.24.1 (2026-07-14) - -### Bug Fixes - -- Fix Pythinker sessions getting stuck when preserved-thinking history contains an empty reasoning step. -- Fix built-in tools being unavailable when the model provider becomes ready after the session starts. -- Fix Thinking effort routing: non-Pythinker providers now preserve configured values, while Pythinker models validate runtime selections and fall back safely during model resolution. -- web: Align thinking-level handling with the CLI: submit the selected level verbatim instead of silently downgrading it, fall back to the model's own default when nothing was chosen or the model switches, and persist explicit picks as the default for new sessions. -- Preserve goal completion summaries and show untyped LLM errors without an internal error-code prefix in step interruption events. - -### Polish - -- web: Show just the level name (e.g. Max) in the model pill instead of "thinking: max". - -## 0.24.0 (2026-07-14) - -### Features - -- web: Add session export: run `/export` or pick Export session from a session's more menu to download the session and troubleshooting logs as a ZIP (limited to 64 MiB). -- Move foreground Bash commands that hit their timeout to the background instead of killing them, so long-running commands survive the timeout and report back on completion. Set `bash_auto_background_on_timeout = false` under `[background]` in config.toml to restore the kill-on-timeout behavior. - -### Polish - -- web: Refine goal mode controls with animated strip interactions, budget-aware progress, and design-system cancellation confirmation. -- On session close, background tasks are now asked to stop and given a grace period before being force-stopped. -- Rewrite repeated-tool-call reminders to redirect the agent toward a different action instead of prohibiting the call. -- Optimize the TaskOutput tool prompts to discourage blocking waits on background tasks. -- Send the pythinker-code-cli User-Agent on provider registry (api.json) and model catalog fetches, so registries can identify the client version. -- Log a warning when a skill fails to parse instead of silently dropping it, and fix skill scan results not being reported. - -### Bug Fixes - -- Prevent oversized image reads from poisoning sessions; sessions that already failed with request-too-large errors now recover automatically. -- Fix session fork losing everything except the conversation log: forked sessions now carry over media attachments, plan files, background task output, and cron tasks, and a failed fork no longer leaves a broken copy behind. -- web: Fix several session rendering glitches when reopening, reconnecting, or resyncing a session, including the context usage indicator dropping to 0, duplicate user message bubbles, and duplicated text in multi-step turns. -- web: Fix uploaded images failing to display when connecting to the server over a non-localhost address. -- web: Continue blocked goals after the user resumes them from the goal controls. -- web: Fix the AgentDynamicWorkflow member list disappearing after a page refresh while subagents are still running. -- web: Fix the goal card disappearing after a page refresh while a session goal is active. -- web: Fix the workspace picker menu sizing too narrowly for its content. -- web: Recover transient subagent rate limits without surfacing them as session errors. -- Fix Bash auto-detection on Windows failing when git comes from a native MSYS2 toolchain (ucrt64/clang64/clangarm64). -- Fix OAuth login hanging after browser authorization when the provider configuration changes during sign-in. -- Show the provider's actual rejection message instead of a misleading re-login prompt when an OAuth-managed model keeps returning 401 after a token refresh. -- Fix providers without a configured `base_url` being rejected: anthropic/openai and other protocol providers now fall back to their official default endpoints again. -- Fix MCP tools being unavailable on the first turn after session startup. -- Fix pasted media and images being dropped from `/skill` and plugin command arguments, and when steering with `Ctrl-S`. -- Fix empty reasoning blocks being dropped across providers, which broke multi-step tool calls. -- In auto permission mode, plan exits are now marked as auto-approved instead of user-reviewed, so the agent no longer mistakes the approval for a user signal to start executing. -- Fix background tasks being lost or wrongly marked as lost when resuming sessions. -- Fix server shutdown sometimes leaving a stale instance file behind. - -### Refactors - -- `pythinker web` now runs on the reworked agent engine by default. - -## 0.23.6 (2026-07-12) - -### Polish - -- web: Let wide Markdown tables grow beyond the reading column up to 1040px, scrolling horizontally inside the table when wider. -- web: Keep the server access token for up to 7 days across tab close and browser restarts, instead of asking for it again with every new tab. -- web: Add workspaces by typing an absolute path directly in the workspace picker's search box, with live validation and completion suggestions. -- web: Auto-enable the default thinking effort when switching to a model that supports effort levels in the web UI. -- Recognize the `support_efforts` and `default_effort` fields when importing a custom registry, so thinking effort levels are available for those models. -- Update the Pythinker Browser Extension install page link opened from the `/plugins` panel. -- Add a `subagent.timeout_ms` config option (or the `PYTHINKER_SUBAGENT_TIMEOUT_MS` env var) to control how long a single subagent may run before timing out; the default is raised from 30 minutes to 2 hours. -- Add a print-mode background policy: set `[background].print_background_mode = "steer"` to keep `pythinker -p` alive across background-task completions, so the main agent can be steered into follow-up turns. - -### Bug Fixes - -- web: Fix sessions getting stuck in a sending state after a reconnect; turns that finish while the connection is down now stop the spinner and let the next message send normally. -- web: Fix the first visit after starting or updating the web UI bouncing to the login page when the initial auth check fails; the connecting screen now stays up, shows the connection error, and retries. -- Keep `pythinker -p` runs alive after a turn ends while a goal is still active or a cron task is pending, so goal continuations and cron fires run their turns instead of being cut off when the main turn finishes. -- Treat a dismissed question prompt as the user choosing not to answer, instead of implicitly selecting the recommended option. -- web: Fix ReadMediaFile results rendering as plain tool cards instead of images after resuming or reloading a session. -- web: Fix the chat view jumping downward while scrolling through conversation history. -- web: Fix the model dropdown showing checkmarks on same-named models from other providers; the current model is now matched by its unique model id. -- web: Fix sidebar lag with many sessions by removing repeated session list scans during rendering. - -### Refactors - -- Rename the dynamic tool loading model capability from `select_tools` to `dynamically_loaded_tools`. - -## 0.23.5 (2026-07-10) - -### Polish - -- Retry provider 429, overload, and other transient errors more reliably, honoring the server Retry-After delay, and surface retries in `-p --output-format stream-json`. - -### Bug Fixes - -- Stop unsupported image formats (AVIF, BMP, TIFF, ICO, …) from breaking sessions at every entry point — including remote image URLs and images mislabeled by a tool — and recover an already-stuck session by dropping the offending image and retrying, so one such image can no longer make every later request fail. -- web: Fix the "Turn finished" desktop notification and completion sound firing twice per turn. -- web: Hide the internal image-compression note so it no longer renders as user message text. - -## 0.23.4 (2026-07-10) - -### Features - -- web: Add notifications when a tool needs approval, and improve notification reliability. - -### Polish - -- web: Polish the chat UI with Inter typography, localized labels, and tighter composer and menu styling. -- web: Polish the session sidebar layout, colors, icons, and typography. -- Display the Extra Usage (fuel pack) balance in the `/usage` and `/status` commands. -- Add a Pythinker Pythinker Browser Extension entry to the Official tab of the `/plugins` panel that opens the Pythinker Browser Extension install page in your browser. - -### Bug Fixes - -- Keep image-heavy sessions within provider request-size limits: oversized images (model-read and pasted, including WebP) are downscaled and compressed, HEIC/HEIF reads are refused with a platform-matched conversion command instead of poisoning the session, and an HTTP 413 request-too-large now recovers automatically — the request and `/compact` retry with older media replaced by text markers. The limits are configurable via `[image]` in `config.toml` (or `PYTHINKER_IMAGE_*` env vars), and each core keeps its own settings so reloading one client's config no longer changes another client's compression. -- Fix resuming sessions whose original working directory no longer exists. -- Fix prompt-mode goals so they run until completion and report invalid goal commands before sending prompts. -- web: Fix an occasional "another turn is active" error when sending the first message of a new conversation, and show a starting state while it is being sent. - -## 0.23.3 (2026-07-08) - -### Bug Fixes - -- Fix a misleading "OAuth login expired" message shown when a model is not available for the current account. - -## 0.23.2 (2026-07-08) - -### Features - -- Add the Vercel plugin to the bundled plugin marketplace. Run `/plugins` and select Vercel Plugin to install it. - -### Bug Fixes - -- Fix `pythinker -p` runs exiting with code 0 when a turn fails. -- Prevent autonomous goals from being paused by model-reported status updates. -- Count the turn that starts an autonomous goal toward its turn budget. -- Raise the image downscale cap from 2000px to 3000px, and fix swapped width/height for EXIF-rotated (portrait) photos in compression captions and media read notes so region readback coordinates map correctly. -- web: Fix the connection error toast lingering after the WebSocket reconnects when returning from the background. -- Fix console windows flashing on Windows each time a hook runs. - -### Polish - -- web: Redesign the scheduled reminder UI. -- web: Show session skills in the slash menu as `/skill:` so they are distinguishable from built-in commands; typing the bare skill name still works. -- web: The composer model switcher switches the active session's model as before and additionally bumps the global default model, so new sessions inherit the choice. -- web: Press Enter to confirm in archive and other confirmation dialogs. -- Tighten goal-mode guidance for blocked and complete status updates. -- Progressive tool disclosure (`select_tools`, experimental): compaction now discards the loaded tool schemas instead of re-injecting them, and the model re-selects the tools it still needs afterward. A from-memory call to a no-longer-loaded tool is rejected with guidance to select it first. No effect unless the `tool-select` experimental flag and a `select_tools`-capable model are active. - -### Refactors - -- web: Compile icons at build time so the bundled web UI only carries the icons it renders. - -## 0.23.1 (2026-07-07) - -### Bug Fixes - -- Fix `pythinker -p` abandoning background subagents that start late or run long, so their results reach the main agent. -- web: Recover chat streaming after a stale background-tab WebSocket instead of requiring a page refresh. -- Fix some third-party models (e.g. Opus 4.8) falling back to the family default max output tokens; an unrecognized minor now reuses the nearest earlier known version's limit. -- Honor explicit Anthropic `max_output_size` settings instead of clamping them to built-in ceilings. -- Stop showing tool-produced `` metadata in tool outputs; failed tools now show their own error text. -- Fix goal completion and blocked updates to produce one final user-facing outcome summary from the tool result. -- Fix goal startup and queue handling so failed starts restore permission mode and queued goals wait behind new user messages. -- Fix goal token budgets to count model completion tokens and stop without extra continuation steps when the budget is exhausted. -- Fix goal tools being unavailable to the main agent, and return clear messages for invalid goal-control calls. -- Respect the `--skills-dir` flag in interactive mode. -- web: Fix several slash commands and skills not working on the new-session screen: `/goal ` and slash skill activations (for example `/pre-changelog`) silently did nothing, and `/btw []` opened an empty side chat. - -### Polish - -- Preserve prior turns' thinking by default on the Anthropic provider (Claude and Pythinker's Anthropic-compatible mode), matching the Pythinker default. Disable with `[thinking] keep = "off"` or `PYTHINKER_MODEL_THINKING_KEEP=off`. -- Clarify the permission mode descriptions shown by `/permission`, `/auto`, and `/yolo`, and reorder `/auto` and `/yolo` in the command list. -- Show long-running goal wall-clock budget reminders in hours. -- Tighten goal-mode guidance so agents continue reasonable work across turns instead of ending goals prematurely. - -### Refactors - -- Record a per-request trace in the session wire log, so model requests can be reconstructed for debugging. - -## 0.23.0 (2026-07-06) - -### Features - -- web: Add an Archived sessions page in Settings to browse and restore archived sessions. Open Settings → Archived to find it. -- Add experimental on-demand tool loading (`select_tools`) under the `tool-select` flag: a supporting model loads MCP tools only when needed instead of sending all of them in every request, preserving the provider prompt cache. Off by default and only active on models that declare the `select_tools` capability. - -### Bug Fixes - -- Fix sessions that exist on disk but were missing from the session list or returned 404 on direct access, by rebuilding the session index at server startup. -- Fix the Bash and Edit tool cards collapsing, jumping, or flickering in height when results stream in or finish with short output, and visually separate the Bash command from its output. -- Fix the input box shifting upward after the slash command menu closes. -- Fix the edit approval preview shown by Ctrl+E to include surrounding context lines, matching the summary panel. -- Fix `@` file completion missing deeply nested files in large projects after adding extra workspace directories. -- web: Fix several web layout and animation glitches: the collapsed sidebar now hides correctly, the chat history no longer replays its entrance animation when opening a session, and tool components no longer jump the conversation when expanded or collapsed. -- web: Fix scheduled-reminder (cron) fires being hidden; they now show as notice cards in the chat. -- web: Fix the end of a reply staying missing after reopening a session. -- web: Fix queued media messages not loading back into the composer and keep attachments when undoing a message. -- web: Keep the composer toolbar from clipping its controls on narrow windows and phones, with the context ring staying visible at every width. -- web: Fix the font size setting so chat text, composer text, and sidebar text follow the selected size. -- web: Fix an almost-invisible composer input caret and a washed-out strikethrough on completed todos. -- web: Show the correct session search shortcut on Windows. -- Fix tool calling with Google Gemini models, including Gemini 3 thinking-signature round-trips across turns. - -### Polish - -- web: Replace the dynamic_workflow footer with a single inline tool card that shows live subagent progress and the aggregated result, and keep the dynamic_workflow progress bar stable after refresh. -- Show compaction summaries in the TUI after compaction. Press Ctrl+O to show or hide the summary. -- web: Render AskUserQuestion answers as a readable option list with the chosen option(s) highlighted, instead of raw JSON. -- web: Show available skills in the composer before a session is created. -- web: Add an Archived sessions entry to the mobile settings sheet and clarify the archive confirmation to mention restoring from Settings. -- web: Show the Pythinker icon and clearer titles in desktop notifications. -- web: Align the markdown diff code block with the design system: code text keeps the normal ink colour while the sign and a soft row background carry the change, matching the `~/diff` panel. -- web: Prevent chat text from hyphenating at line breaks and render code without font ligatures. -- web: Drop the stray left indent in the tool-call card body so expanded content aligns with the header. -- Feed AskUserQuestion answers back to the model as question text and option labels instead of positional ids, so the model no longer has to map them back. Question texts must now be unique per call and option labels unique per question; existing clients keep answering with option ids, so no client change is required. -- Keep prior reasoning across turns for Pythinker models by default when Thinking is on. Set `[thinking] keep = "off"` to disable. - -## 0.22.3 (2026-07-04) - -### Bug Fixes - -- Wait for background subagents to finish and respond to their results before exiting in `pythinker -p`, instead of ending the turn early. -- web: Fix uploaded videos failing to play in the web chat. -- Revert the recent TUI transcript rendering changes to the original upstream behavior and fix related rendering issues. - -### Polish - -- Add `--dangerous-bypass-auth` and `--keep-alive` flags to `pythinker server run`, so the server can run without a token on trusted networks and stay alive past the idle timeout. -- web: Add click-to-enlarge for images uploaded in the web chat. Click an image in a message to open it. - -## 0.22.2 (2026-07-03) - -### Bug Fixes - -- Fix sessions silently dropping later user messages after a turn was interrupted between a tool call and its result. -- Fix requests being rejected by strict providers when the model emits duplicate tool call ids. -- Fix `pythinker upgrade` failing on Windows with a spawn error when installing the new version. -- Fix duplicated transcript content appearing in scrollback during streaming. -- Fix compressed-image prompts leaking an internal `` compression note into the visible message and the session title. -- Keep automatic background updates from flashing a console window on Windows. - -### Polish - -- Have context-compaction notes capture a forward plan for the remaining work — upcoming steps, settled decisions, and foreseeable obstacles — instead of only the immediate next step, so the agent continues more coherently after auto-compaction. -- Enrich PATH from the user's login shell at startup, so shell commands find user-installed tools (e.g. Homebrew's `gh`) even when pythinker-code was launched without the full profile PATH. -- Promote the language-matching rule to a dedicated section in the system prompt, so replies and reasoning consistently follow the user's language through long English tool output, while repository artifacts keep project conventions. -- Add a TUI preference to keep rapid multi-line pastes from submitting line by line when bracketed paste is unavailable. Set `disable_paste_burst = true` in `tui.toml` to turn it off. -- Keep subagent cards at a stable height and show a live status spinner with a compact two-row activity window. -- In `pythinker -p` runs, wait for background subagents to finish before exiting when `background.keep_alive_on_exit` is enabled. Set `keep_alive_on_exit = true` to let concurrent background subagents complete. - -### Refactors - -- Record model response ids in session wire logs to make individual model requests easier to trace. - -## 0.22.1 (2026-07-02) - -### Bug Fixes - -- Fix TUI rendering bugs that caused the screen to go blank and the input box to disappear. -- Fix the TUI crashing when the terminal is resized to a very narrow width while the input contains CJK or emoji text. -- Fix the web UI becoming sluggish after opening many sessions. -- Clear the screen fully when starting a new session via /new, /clear, or a session switch. -- Fix web tooltips that could get stuck on screen when their trigger element is removed while open. -- Fix the sidebar session row shifting its title and status badges when hovered. -- Fix the session search dialog showing a horizontal scrollbar for long session titles or snippets. - -### Polish - -- Improve compaction handoff summaries for more reliable resumed sessions. They now keep the latest intent, key tool results, decisions, open questions, and context to re-check. -- Save shell commands to input history and recall them in bash mode. Press Up on an empty `!` prompt to browse previous shell commands. -- When large images are compressed, tell the model the original and delivered image details. Keep the original image available, and support cropped or full-resolution reads for fine details. -- Refresh the web UI icon set and unify the message copy and undo button hover states and tooltips. -- Let the web sidebar collapse an expanded workspace session list back to its first page. -- Trim redundant and incorrect tooltips in the web UI. -- Show an up arrow on the web composer send button. - -### Refactors - -- Remove the experimental micro compaction feature and its toggle from the experiments panel. -- Remove duplicate newline-shortcut handling from the prompt editor. - -## 0.22.0 (2026-07-02) - -### Features - -- Automatically compress oversized images before they reach the model, downsampling and re-encoding them to cut vision-token cost and avoid provider image-size errors. -- Add model alias overrides, letting you set model metadata under `[models."".overrides]` to override provider catalog refresh results. - -### Bug Fixes - -- Fix plan, dynamic_workflow, and goal modes being shared across sessions in the web UI; each session now keeps its own toggles. -- Fix the transcript jumping to the top when scrolling up through history during streaming output. -- Release pasted images and streaming timers once they are no longer shown, so memory stops growing in long sessions. -- Fix the terminal being left in raw mode with a hidden cursor and disabled flow control after a crash or abrupt exit. -- Fix an active workspace showing only its five most recent sessions on load, so it now keeps loading older sessions from the last 12 hours. -- Fix the Thinking-by-default setting not taking effect, so new sessions correctly start with thinking enabled. -- Fix spurious errors from the web question, approval, and task actions when the action was already complete, and add loading feedback so each click is acknowledged immediately. -- Show draft pull requests with a distinct draft status instead of displaying them as open. -- Hide the conversation outline when there is not enough room to expand its labels, so it no longer clips against the window edge. -- Hide the unsupported Off option in the /model thinking switcher for always-on models that already expose multiple effort levels. - -### Polish - -- Refresh the web UI with a new design system, including updated colors, typography, spacing, light and dark palettes, restyled tooltips, and subtle enter/exit and expand/collapse animations. -- Group consecutive tool calls into a collapsible stack with per-tool renderers, including diff line-count chips for edits and inline previews for image, video, and audio results. -- Improve session search with a Cmd/Ctrl+K palette that filters by title, workspace, and last prompt with highlighted matches. Press Cmd+K or Ctrl+K to open it. -- Show queued prompts inline below the running turn in the web chat, and split Stop into its own button so Send no longer interrupts. -- Show the conversation outline as one entry per user query that expands into a labeled list on hover. -- Replace the Explore and Native theme options with a single chat layout and a Blue or Black accent-color setting. -- Add workspace sorting by manual order or last-edited time, plus collapse-all and expand-all controls, to the sidebar. -- Show time, duration, connection, and stack details in web error and warning toasts. -- Use one consistent modal dialog for confirmations in the web UI (archive session, delete workspace, delete provider, undo message, and mode toggles). -- Reduce the default TUI transcript window to keep long sessions responsive. -- Reduce the web composer's default height for a more compact empty state, and fix ArrowUp recalling the previous message while editing a multi-line draft; ArrowUp now recalls only from the very start of the text and is disabled in the expanded editor. -- Remove the fade-out animation when undoing a message in the web chat. - -## 0.21.1 (2026-07-01) - -### Bug Fixes - -- Keep the waiting spinner visible while encrypted reasoning streams, fixing a blank spinner-less gap before the first response text appears. - -## 0.21.0 (2026-07-01) - -### Features - -- Plugins can now provide slash commands via a `commands` field in their manifest, registered as `:` and invoked with `$ARGUMENTS` expansion. -- Add Mermaid diagram rendering to the web chat. Fenced `mermaid` blocks in assistant responses now render as diagrams. KaTeX math and Mermaid diagram parsing also run in Web Workers to keep the UI responsive during live streaming. - -### Bug Fixes - -- Stop a malformed message history from permanently bricking a session on strict providers (Anthropic). The request is repaired before sending — orphaned tool calls are closed and empty/whitespace-only text blocks dropped — and if the provider still rejects its structure, it is resent once with a wire-compliant rebuild. -- Force-exit headless runs (`pythinker -p`) so a stray ref'd handle left over from the run can't keep a completed run alive until an external timeout, and bound prompt cleanup so a wedged shutdown step can't hang shutdown. -- Fix @ file mentions not opening when typed inside a slash command argument. -- Fix adding a workspace by path in the web UI failing silently when the daemon rejects the path; it now shows an error instead of a broken workspace. -- Fix duplicate workspaces showing in the web sidebar when the same folder is registered more than once. -- Fix the web workspace rename not persisting after a page refresh. - -### Polish - -- Add a double-Esc shortcut to open the undo selector. Press Esc twice while idle to undo. -- Show file path completions when typing `/` in shell mode (`!`). -- Always show the usage-data opt-out toggle in the web settings with a clearer label and description. - -### Refactors - -- Rework conversation compaction: - - Keep only recent user prompts plus a single user-role summary; drop assistant and tool messages. - - Repair tool_use/tool_result adjacency before sending, fixing a strict-provider HTTP 400 when a tool call and its result became non-adjacent. - - Merge consecutive user turns for strict providers (Gemini/Vertex), fixing an HTTP 400 ("roles must alternate") after compaction or when a turn is steered in right after a tool result. - - Micro-compaction now defaults off. -- Refactor the thinking effort system -- Add a server-side key-value store API for persisting web UI preferences to the user's data directory. - -## 0.20.3 (2026-06-30) - -### Bug Fixes - -- Fix provider error messages rendering as blank lines in the TUI when the server returns an HTML error page. -- Fix the web composer being hidden behind the mobile Safari toolbar and the page auto-zooming when the composer is focused. - -### Polish - -- Refresh provider model lists automatically in the background instead of only at startup, so newly available models appear without restarting. -- Glob now uses ripgrep, so it respects .gitignore by default, supports brace patterns, returns only files, and keeps partial results with a warning when some directories are unreadable. - -### Refactors - -- Align malformed tool call argument handling with schema validation fallback. - -## 0.20.2 (2026-06-29) - -### Features - -- Support the Anthropic-compatible protocol for Pythinker Code, including video input. -- Add a completion sound and question notifications to the web UI, with separate Settings toggles for completion notifications, question notifications, and sound. Question notifications default off so question text only reaches your desktop after you opt in. -- Add `PYTHINKER_CODE_CUSTOM_HEADERS` for custom outbound LLM request headers, and send the `User-Agent` header to non-Pythinker providers. Set `PYTHINKER_CODE_CUSTOM_HEADERS` to newline-separated `Name: Value` lines. -- Add an optional `exclude_empty` parameter to the session list API to omit sessions that have no messages. - -### Bug Fixes - -- Recover from provider 413 context overflows by compacting before retrying. -- Cap compaction output at 128k tokens by default to avoid provider `max_tokens` errors. -- Fix compaction ignoring the configured max output size. -- Fix unnecessary full-screen redraws when typing in the input box or toggling the slash panel. -- Keep unsent composer attachments scoped to their session in the web UI, so switching sessions no longer leaks them into another session's next message. -- Fix the web composer occasionally keeping typed text after sending the first message of a new session. -- Fix debug timing output lingering after undoing a turn. -- Fix working tips getting squeezed against the agent dynamic_workflow progress bar. - -### Polish - -- Rework the web ask-user-question card into a step-by-step wizard so multi-question navigation and the final Submit action are easier to see. -- In the bundled web UI, a new session is now created only when the first message is sent, so `+ New` without a workspace opens the composer instead of making an empty session. -- Restore each session's scroll position when switching back to it in the web UI. -- Keep the open side panel when switching between sessions in the web UI. -- Scope the web composer's up/down input history to the current session instead of sharing it across all sessions. -- In the bundled web UI, `/new` and `/clear` are now aliases that open the session onboarding composer and focus the input. iOS auto-zoom is prevented by keeping text inputs at 16px instead of disabling viewport scaling. -- Hide unused "New Session" entries from the web session list by default. -- Remove the `/sessions` slash command from the web UI; the sidebar already covers session browsing. -- Show the first five sessions per workspace in the web sidebar instead of ten. -- Replace the web composer attach button's plus icon with an image icon. - -### Refactors - -- Route Pythinker Code models on the Anthropic-compatible protocol through the beta Messages API. -- Upgrade web markdown renderer dependencies (katex, markstream-vue, shiki) for bug fixes and performance improvements. -- Add provider type and protocol attributes to turn and API error telemetry. - -## 0.20.1 (2026-06-26) - -### Features - -- Plugins now support declaring lifecycle hooks in `pythinker.plugin.json` to run scripts at specific stages. See [Hooks in Plugins](../customization/plugins.md#hooks-in-plugins). -- `/feedback` now supports attaching diagnostic logs and codebase context. -- Add the `pythinker update` command, equivalent to `pythinker upgrade`, for upgrading to the latest version. -- `pythinker web` adds the `--allowed-host ` option to add a specified Host to the DNS-rebinding allowlist; 403 errors now explain how to allow it via `--allowed-host` or `PYTHINKER_CODE_ALLOWED_HOSTS`, e.g. `pythinker web --allowed-host example.com`. - -### Bug Fixes - -- Fix pythinker server failing to start on Windows after the first run. -- Fix the Web UI opened by the `/web` command not signing in automatically; the terminal now prints the access token. -- Cap chat-completions providers' `max_tokens` to the remaining context window, avoiding context overflow and invalid parameter errors. - -### Polish - -- Optimize the default system prompt and built-in tool descriptions to stop the agent from blocking background tasks, unify tool guidance across profiles, and surface previously missing tool-result details (fetched-page mode, Grep match totals). -- Cache rendered message lines to keep the terminal responsive in long conversations. -- Retain only recent turns in the transcript and collapse older steps within each turn to keep long sessions responsive. -- Make the web chat input grow with its content and add an expandable editor for longer messages. -- Show the done / in progress / pending breakdown of hidden todos in the collapsed todo panel. - -## 0.20.0 (2026-06-26) - -### Features - -- Add shell mode to the TUI. Type `!` in the input box to enable it. For long-running commands, press Ctrl+B to move them to the background. For example, you can run `!gh auth login` to sign in to the GitHub CLI without opening a new terminal. -- Add a `--host` CLI option so `pythinker web --host` can expose the server to the internet, with hardened token authentication, rate limiting, and other security measures. -- Render LaTeX display math (`$$…$$`) in the web UI. - -### Bug Fixes - -- Fix a startup crash on Linux caused by an unhandled native clipboard error. -- Fix `pythinker web` and `/web` failing to start the background server daemon on Windows with `spawn EFTYPE` when the CLI is installed via npm/pnpm or run from source. The official single-binary install script was not affected. -- Fix the terminal window repeatedly losing focus on Linux Wayland, which broke IME input. -- Stop auto-dismissing questions in the web UI after 60 seconds so they wait for the user's answer. -- Fix explore subagents silently losing git context when git commands time out or the directory is not a repository. -- Fix Ctrl-C during compaction so it clears a pending editor draft first instead of cancelling immediately. -- Fix MCP server working directories when sessions are hosted by the web server. -- Fix duplicate session snapshot reloads in the bundled web UI during resync. -- Fix truncated skill descriptions missing an ellipsis in the model's skill listing. - -### Polish - -- Redesign `/plugins` as a single tabbed panel: **Installed** (manage installed plugins — toggle, remove, MCP, details, reload), **Official** (Pythinker-maintained marketplace plugins), **Third-party** (marketplace plugins from other publishers), and **Custom** (install straight from a GitHub URL, zip URL, or local path). Use `Tab` / `Shift-Tab` to switch tabs. -- Show a line-by-line diff when the agent edits or writes a file in the web chat. -- Show the plan body and approach choices in the plan review card when exiting plan mode in the web UI. -- Show the full accumulated progress of a subagent in its detail panel, with concise tool-call summaries instead of raw JSON. -- `/reload` now refreshes the assistant's view of plugin skills, so plugin changes take effect in the current session instead of requiring a new one. -- Replace silent AGENTS.md truncation with a visible warning in the TUI status bar and web UI. -- Add a confirmation prompt before installing third-party plugins. -- Show update badges on the `/plugins` Installed tab, where Enter now installs the available update and I opens plugin details. -- Add a copy button to user messages in the web chat. -- Preserve full tool output logs when previews are truncated and link background task completion notifications to saved output. -- Sync session title changes across all connected clients in server mode. -- Add Ctrl+U and Ctrl+D as page up and page down shortcuts in the task output viewer. -- Add a hint to the per-turn step limit error pointing users to the `loop_control.max_steps_per_turn` config option. -- Reduce streaming redraw cost for long assistant messages with code blocks. -- Page the web session list per workspace so the first screen no longer fetches every session up front. -- Keep the web session sidebar from re-rendering on every streaming token to improve rendering performance. -- Create missing parent directories automatically when writing a file. -- Improve the image paste hint. - -## 0.19.2 (2026-06-24) - -### Features - -- Keep drag-and-drop workspace reordering in the web sidebar, with sort order persisted locally; sessions now also float to the top of their group as soon as a new message arrives. -- Add an Alt+S shortcut in the model picker to switch the model for the current session only, without saving it as the default. -- Add a Ctrl+T shortcut to expand and collapse a truncated todo list. -- Add `-c` as a shorthand for `--continue`. - -### Bug Fixes - -- Fix yolo mode in the web app auto-approving plan reviews and sensitive file access. -- Fix resume not realigning a tool call that was interrupted mid-history. -- Fix the composer's ↑/↓ input-history recall doing nothing right after the first message of a new session. -- Fix stale rows occasionally leaving duplicate input boxes after tall content shrinks. -- Fix inline images being rendered as broken escape sequences in the transcript. -- Fix code blocks nested inside list items rendering blank in the web chat after a turn finishes generating. -- Fix the Tab key unexpectedly opening the file completion list. -- Fix clipboard copy actions in the web UI when served over plain HTTP. -- Fix the web question prompt missing the free-text Other option. -- Fix web chat stop actions so stale prompt ids fall back to cancelling the active session. - -### Polish - -- Read large text files in bounded memory and read tail lines without scanning whole files. -- Show the command in running Bash tool cards and allow expanding it with Ctrl+O before the result arrives. -- Allow the web sidebar and detail panel to be resized up to the available viewport width, keeping their resize handles reachable on narrow windows. -- Show subcommand suggestions after Tab-completing a slash command name. -- Show a transient footer hint when an image is detected in the clipboard, displaying the platform-appropriate paste shortcut. -- Persist the collapsed state of workspace groups in the web sidebar across page reloads. -- Add a development-mode indicator to the web sidebar for local development. -- Optimize the loading tips display. - -### Refactors - -- Reorganize the web app's components into area subdirectories (chat/settings/dialogs/mobile) and refresh the component path comments. -- Extract several composer pieces into reusable composables. -- Extract pure turn-rendering helpers out of the chat pane into their own module. -- Extract the beta conversation outline (table of contents) into its own component. -- Extract the workspace group rendering out of the sidebar into its own component. - -## 0.19.1 (2026-06-23) - -### Bug Fixes - -- Fix ACP editors such as Zed failing to start a new thread. -- Fix the web sidebar's unread dots getting out of sync across browser tabs. -- Clear all per-session state when a session is archived or removed, so archived sessions no longer leave orphaned data behind. - -### Refactors - -- Consolidate web client localStorage access and split the root state store and app shell into focused composables. - -## 0.19.0 (2026-06-22) - -### Features - -- Added the ability to add extra workspace directories: - - Use the `/add-dir ` command to add extra working directories to the current session, or remember them for the project. - - Use `pythinker --add-dir ` to add them on startup. - - Project-level local config is now managed in `.pythinker-code/local.toml`; we recommend adding it to your `.gitignore`. -- Allow long-running foreground commands and subagents to be moved into background tasks with `Ctrl+B`, and inspect them via the `/tasks` panel. - -### Bug Fixes - -- Surface provider safety-policy blocks instead of silently treating them as completed turns, and prevent the context token count from dropping to zero after a filtered response. -- Fix provider requests failing when restored conversation history contains empty text content blocks. -- Detect the real image format from file contents when reading media, so a mismatched filename extension no longer produces a data URL the model API rejects. -- Fix commands flashing an empty console window on Windows. -- Stop showing unread dots on cancelled or failed sessions in the web sidebar. - -### Polish - -- Speed up session snapshot loading with a direct disk reader and a request timeout safeguard, keeping the previous path as a legacy fallback. -- Show longer branch names in the web chat header and expose the full name on hover. -- Keep the web page title fixed instead of changing with the session or workspace name. -- Polish file mention UX. - -### Refactors - -- Unify image format detection when sniffing fails. -- Consolidate web client localStorage access and decouple appearance/notification state into dedicated modules. - -## 0.18.0 (2026-06-18) - -### Features - -- Add session filtering to the web sidebar, filtering by title and the last user prompt. -- Add scroll-up lazy loading for older messages in the web chat session view. -- Add an environment variable to cap AgentDynamicWorkflow concurrency during the initial ramp, so large dynamicWorkflows do not trip provider rate limits as easily. - -### Bug Fixes - -- Fix the web app only loading the 20 most recent sessions. -- Fix web slash skill selection sending immediately and allow slash search to match skill names by substring. -- Fix the highlighted web slash command not staying visible while navigating a long slash menu. -- Fix incorrect display after archiving the last session. -- Fix the web login slash command description to match the browser authorization flow. - -### Polish - -- Redesign the web OAuth login dialog so the order of steps is unambiguous. -- Show the current version in web settings. -- Allow long web slash command names and descriptions to wrap without overflowing the slash menu. -- Add `/reload` suggestion in plugin-change hints. - -## 0.17.1 (2026-06-17) - -### Bug Fixes - -- Fix the `pythinker web` command failing to start in the background. -- Stop the background local server from locking the directory it was started in. -- Prevent the web login dialog from closing when clicking the backdrop. - -### Polish - -- Group the default model dropdown in web settings by provider. - -## 0.17.0 (2026-06-17) - -### Features - -- Add Pythinker Code Web mode, which you can start with `pythinker web` or `/web` in the CLI, and continue sessions in a browser chat interface. - -### Bug Fixes - -- Show the underlying connection error when OAuth token refresh fails after internal retries, instead of prompting for login. Token refresh failures are no longer re-retried at the agent loop level. -- Restore the turn counter from persisted loop events on resume so post-resume turns no longer reuse turn ids that already appear in history. - -### Polish - -- Skip debug TPS when the output stream is too short to measure reliably. - -## 0.16.0 (2026-06-16) - -### Features - -- Add a built-in `pythinker vis` command that launches the session visualizer in your browser, pointed at your local sessions. Supports `--port`/`--host`, `--no-open`, and `pythinker vis ` deep-links. - -### Bug Fixes - -- Stop Anthropic-compatible providers from reading ambient Anthropic shell credentials and custom headers. -- Fix repeated compaction handling when context remains over the blocking threshold. -- Prevent session shutdown from resuming the agent when stopping background tasks. -- Project session replay ranges over rendered replay records instead of raw persisted records. -- Close wrapped output streams when buffered readers are destroyed. - -### Polish - -- Reduce the maximum height of the `/btw` side panel from half to one-third of the terminal. -- Polish queue pane styling. -- Add configurable banner display frequencies with local display state. - -### Refactors - -- Remove redundant LLM request logging context plumbing. - -## 0.15.0 (2026-06-15) - -### Features - -- Add an all-sessions picker view with name search, paginated browsing, and clipboard-ready resume commands for sessions in other working directories. -- Add support for legacy SSE MCP servers alongside stdio and streamable HTTP transports. - -### Bug Fixes - -- Recover resumed sessions when an interrupted tool call result was not recorded. -- Stop writing resume version markers into persisted agent metadata. -- Do not carry obsolete legacy loop, background, plan, yolo, or unknown experimental flags into migrated config files. -- Repair mismatched JSON Schema types emitted by Xcode 26.5 MCP server for PyModel compatibility. - -### Polish - -- Keep TUI components within narrow terminal widths by wrapping, compacting, or truncating lines that could exceed the render width. -- Prompt the CLI to show one brief same-language status sentence before non-trivial tool calls. -- Extend the same-language rule to the model's reasoning, so thinking follows the user's language while keeping code and technical terms in their original form. -- Read media files using header-detected types before falling back to media extensions. -- Prioritize clearing draft editor text before Ctrl-C cancels an active stream. -- Collapse hidden directories in the workspace prompt and explain how to inspect them. -- Include the skill's directory on the loaded-skill context block so the agent can locate a skill's bundled resources (scripts, templates) after it is invoked. -- Show the all-sessions toggle hint when the current working directory has no sessions. -- Clarify that compaction summaries must be emitted in the final answer. -- Clarify AGENTS.md prompt guidance and mark truncated instruction files. - -### Refactors - -- Resolve model capabilities through a static lookup instead of instantiating a temporary provider. -- Decouple agent skill access from session-specific registry implementations. -- Optimize the npm packaging system. - -## 0.14.3 (2026-06-14) - -### Polish - -- Refresh provider model metadata before opening the model picker. - -## 0.14.2 (2026-06-12) - -### Bug Fixes - -- Fix endless desktop notifications in iTerm2 by only sending terminal progress sequences to terminals that support them. -- Show completed and cancelled compaction records correctly when resuming a session. -- Drop invalid config.toml sections with a warning instead of failing to start. - -### Polish - -- Stream foreground Bash stdout and stderr while commands are still running. -- Allow `--auto`, `--yolo`, and `--plan` to be combined with `--session` or `--continue` by applying the requested mode to the resumed session. -- Qualify sub-skill names with their parent prefix and expose sub-skills as dotted slash commands in the TUI. -- Sync custom registry provider additions, removals, and rotated registry keys during startup refresh. - -## 0.14.1 (2026-06-12) - -### Bug Fixes - -- Cancel active turns during session shutdown so foreground shell commands do not outlive prompt-mode exits. -- Stop background tasks by default when sessions close. -- Prevent overlapping interactive agent requests from using the wrong active agent. -- Fix premature stream close errors when shell processes time out or are killed. -- Degrade unsupported audio/video to placeholder text and reattach tool result media instead of silently dropping them. -- Send OpenAI Responses system prompts as request instructions. -- Propagate configured execution environment overrides across spawned processes. -- Fix ACP file reads and edits for Windows workspaces opened through IDE clients. -- Require AgentDynamicWorkflow tool calls to run alone in a model response. - -### Polish - -- Add runtime support for dynamic MCP server updates, reference skills, replay timestamps, and Node file uploads. -- Add a YOLO choice when starting dynamic_workflow tasks from Manual mode. -- Polish builtin skills. -- Find slash commands by their aliases in autocomplete — typing `/clear` now suggests `new (clear)`. -- Wrap long command and skill descriptions in the autocomplete menu onto a second line instead of cutting them off. -- Display a tips banner below the welcome panel on startup. - -## 0.14.0 (2026-06-10) - -### Features - -- Add an `Interrupt` hook event that fires when the user interrupts a turn (e.g. pressing Esc), letting hooks observe the turn stopping instead of getting stuck on a working state. - -### Bug Fixes - -- Preserve image outputs from tools when using OpenAI-compatible chat completions. - -## 0.13.1 (2026-06-10) - -### Bug Fixes - -- Prevent forking sessions during active turns and consolidate wire protocol definitions into a shared internal package. -- Fix Pythinker Datasource to use the matching OAuth credentials and service endpoint for the active Pythinker Code environment. -- Fix goal marker text overflowing terminal width. - -### Polish - -- Add Claude Fable 5 support to the Anthropic provider. -- Add an interactive undo selector and clearer undo-limit messages. -- YOLO mode no longer asks before writing or editing files outside the working directory. -- Clarify active skill prompts so loaded skills are no longer represented as system reminders. -- Tighten file tool guidance to route incremental edits through Edit. - -## 0.13.0 (2026-06-10) - -### Features - -- Add custom color themes. Define your own palette as a JSON file in `~/.pythinker-code/themes/`, or generate one with the built-in `/custom-theme` skill command. -- Add `/import-from-cc-codex` to import selected Claude Code and Codex instructions, Skills, and MCP settings. -- Show available plugin updates in the marketplace. - -### Bug Fixes - -- Fix Windows builds and development launches that could fail when package binaries resolve to command shims. -- Fix device login to keep the URL and code visible when the browser cannot be opened. - -### Polish - -- Clarify grouped subagent progress with active status breakdowns and elapsed time. -- Truncate queued message display to a single line with ellipsis when it exceeds terminal width. - -## 0.12.1 (2026-06-09) - -### Bug Fixes - -- Allow obsolete experimental config entries to remain without blocking startup. -- Pass through xhigh reasoning effort for OpenAI-compatible chat completions requests. - -## 0.12.0 (2026-06-09) - -### Features - -- Add the `/dynamic_workflow` command for running agent dynamicWorkflows with live progress and rate-limit-aware retries. -- Make goals, background questions, and sub-skill discovery available without experimental opt-ins. -- Honor the standard `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` environment variables, including SOCKS proxies, for all outbound traffic. -- Support Homebrew installations. -- Enable micro compaction by default. Disable via `/experiments`. - -### Bug Fixes - -- Fix ACP slash skill routing, bootstrap context reads, file and permission edge cases, subagent event handling, and stale-file edit messaging. -- Fix goal resume behavior by restoring goal state from agent records. -- Fix thinking text and tool output display for subagents. -- Fix session workdir mismatch on Windows caused by inconsistent path separators. -- Fix the `/mcp` status panel border being broken by multi-line MCP server errors, which are now folded onto a single row. -- Detect Git Bash installed through Scoop and other Git shims on Windows. -- Show the underlying error when migration fails. -- Allow the startup session picker to exit with repeated Ctrl-C or Ctrl-D. - -### Polish - -- Remove the per-turn auto-compaction limit so long conversations can keep compacting instead of failing early. -- Improve goal mode outcome handling with follow-up messages, safer error pauses, and clearer TUI transcript display. -- Show full plan cards directly and remove the Plan card keyboard shortcut. -- Wrap long single-line shell commands in approval prompts so the full command remains visible. -- Rework file reference completion in the TUI. -- Load Pythinker-specific user Skills and global agent instructions from `PYTHINKER_CODE_HOME` when it is set. - -## 0.11.0 (2026-06-05) - -### Features - -- Add experimental sub-skill discovery gated by the `PYTHINKER_CODE_EXPERIMENTAL_SUB_SKILL` environment variable. Ships the `sub-skill` builtin bundle (`sub-skill.review`, `sub-skill.consolidate`) for inventorying and consolidating skills into hierarchical groups. -- Add the following environment variables: - - - `PYTHINKER_MODEL_TEMPERATURE`, `PYTHINKER_MODEL_TOP_P` — sampling parameters applied globally to any `pythinker` provider (not tied to `PYTHINKER_MODEL_NAME`). - - `PYTHINKER_MODEL_THINKING_KEEP` — PyModel preserved-thinking passthrough (`thinking.keep`), injected only while Thinking is on. - - `PYTHINKER_CODE_NO_AUTO_UPDATE` (legacy alias `PYTHINKER_CLI_NO_AUTO_UPDATE`) — fully disables the update preflight (no check, background install, or prompt). -- Show built-in skills as direct slash commands and group them ahead of external skill commands. - -### Bug Fixes - -- Fix slash command autocomplete so goal text can be submitted when the cursor is before existing text. -- Fix queued goals so failed promotion attempts do not lose or duplicate queued work. -- Fix upcoming-goal queue handling while editing or pasting queued goals. -- Ask before starting goals in YOLO mode so users can switch to Auto for unattended work. -- Show concise provider filtering errors when responses are blocked before visible output. -- Show "unknown command" instead of "too many arguments" when an invalid subcommand is entered. -- Clamp OpenAI Chat Completions `xhigh` and `max` thinking effort to `high` unless the model supports `xhigh` on `v1/chat/completions`. -- Preserve thinking effort when compacting long conversations. -- Refresh provider model metadata when capabilities change without model ID changes. - -### Polish - -- Show the upcoming-goal confirmation with the same accent treatment as goal lifecycle messages. -- Start upcoming goals immediately when there is no active goal to wait for. - Support multiline edits when managing upcoming goals. -- Use a fixed 30-minute timeout for subagents and show concise resume instructions when they time out. -- Highlight goal queue subcommands while typing slash commands. - -## 0.10.1 (2026-06-05) - -### Bug Fixes - -- Fix a crash when starting a goal in the TUI. - -## 0.10.0 (2026-06-04) - -### Features - -- Users now can prepare several goals for the agent to work on sequentially. The agent will pick up the next goal from the queue once the current goal is completed. Use `/goal next ` to queue a goal and `/goal next manage` to review and change the queue interactively. -- Add the built-in `update-config` skill — you can now have Pythinker edit its own config files. -- Add persistent experimental feature toggles and a TUI panel that applies confirmed changes by reloading the current session. -- Add `/reload` to reload the current session and apply updated config files, plus `/reload-tui` to reload only TUI preferences. -- Add a doctor command for validating Pythinker Code configuration files. - -### Bug Fixes - -- Normalize malformed Responses stream rate limit errors as provider rate limit failures. -- Keep managed OAuth credentials scoped to their configured authentication and API endpoints. -- Stop carrying active and queued goals into forked sessions. -- Fail early when Git Bash is missing on Windows before starting CLI sessions. -- Refresh the update target before showing foreground update prompts so the displayed version matches the install. -- Point session error diagnostics to the `/export-debug-zip` command. -- Set terminal tab titles without renaming the running process. - -### Polish - -- Start automatic background updates as soon as startup's fresh update check finds a newer version. -- Set the CLI process title to pythinker-code during startup. -- Lowercase the stale file content message in edit tool errors. - -### Refactors - -- Ensure Nix-packaged CLI builds can find ripgrep and fd. - -### Other - -- Document the Git Bash prerequisite for Windows installs. - -## 0.9.0 (2026-06-03) - -### Features - -- Add the `pythinker acp` subcommand: pythinker-code now speaks [Agent Client Protocol 0.23](https://agentclientprotocol.com/) over stdio so IDEs (Zed, JetBrains AI Chat, custom clients) can drive sessions directly — coverage matrix, Zed configuration and breaking pre-release notes are in [pythinker acp Subcommand Page](https://code.pythinker.com/pythinker-code/en/reference/pythinker-acp.html). -- Add `/btw` for side-channel conversations without steering the active main turn, and allow `/btw` to open the side-channel panel before entering a question. - -### Bug Fixes - -- Fix external editor (Ctrl+G) on Windows by removing `/bin/sh` dependency and using platform-aware shell quoting for temp file paths. -- Use the OpenAI completion token field required by newer Chat Completions models. -- Use configured model output limits for completion token caps. -- Fix goal budget tool schemas for OpenAI-compatible providers. -- Resume saved subagents lazily when they are accessed. - -### Polish - -- Unify the interaction and visuals across TUI dialogs and selectors. -- Log enabled experimental flags at startup. - -### Refactors - -- Allow SDK runtime creation to use a separate RPC client while preserving local CLI startup. - -## 0.8.0 (2026-06-02) - -### Features - -- Add experimental goal mode for longer tasks that need more than one turn. Turn it on with `PYTHINKER_CODE_EXPERIMENTAL_GOAL_COMMAND=1` before you start Pythinker. - - Use `/goal ` in the TUI when you want Pythinker to keep working on one task across turns. For example: - - ```text - /goal Fix the failing checkout test - ``` - - Pythinker shows the goal in the TUI and keeps progress visible while it works. Use `/goal status`, `/goal pause`, `/goal resume`, `/goal cancel`, and `/goal replace ` to manage the goal. This feature is still experimental. Try it and tell us what would make it more useful. -- Add `pythinker provider` CLI subcommand with `add`, `remove`, `list`, and `catalog list` / `catalog add` actions, so providers from a custom registry (api.json) or the public models.dev catalog can be imported and managed without launching the TUI. -- Add background structured questions so agents can continue while waiting for user answers. -- Add background automatic upgrades, which can be disabled in tui.toml. -- Add `/undo` slash command to withdraw the last prompt from conversation history, and keep replay records in sync when a prompt is undone. -- Add a `pythinker upgrade` command for manually checking and upgrade Pythinker Code CLI. -- Add approval lifecycle hook events for observing pending and completed permission prompts. -- Allow subagents to use custom tools registered on their parent agent. -- Allow glob searches to target explicit absolute paths outside the workspace. - -### Bug Fixes - -- Fix cross-provider replay failures from incompatible tool call IDs and unsigned Claude thinking history. -- Fix custom registry provider handling during re-import. Prevent loss of multi-provider entries and remove stale providers along with their model aliases and default model references. -- Fix tool output preview rendering: trim trailing empty lines, append ellipsis to multi-line Bash command headers, and truncate long single-line output by visual wrapped lines instead of raw newline count. -- Fix slash-activated skills not being recognized by the model due to missing system reminder wrapper. -- Fix a crash in the `/sessions` picker on very narrow terminals by clamping every rendered line to the terminal width. -- Normalize glob patterns before brace expansion to prevent incorrect path matching. -- Prevent modified keyboard release sequences from appearing after exiting the CLI. -- Fix Git Bash path detection on Windows by also searching `usr\bin\bash.exe` locations, which is where bash lives in many Git for Windows installations where `bin\bash.exe` does not exist. - -### Polish - -- Show MCP server summary in the welcome panel and add configuration hints in the /mcp command output. -- Point users to `/provider` instead of the removed `/connect` command in the welcome screen and the no-models-configured hint. -- Append the current todo list as markdown to compaction summaries before writing them to history. -- Show the full model name in the footer status bar instead of truncating the provider prefix. -- Remind the model to refresh TodoList during long-running tasks and strengthen TodoList progress-tracking guidance. -- Replace chalk named color with theme-aware hex in session-directory warning. - -### Refactors - -- Consolidate background task management under the agent background runtime. - -## 0.7.0 (2026-06-02) - -### Features - -- Add `/provider` command for managing AI providers, support custom registry imports, and introduce a tabbed model selector. It replaces the deprecated `/connect` command — use `/provider` instead. -- Render scheduled reminders distinctly in the TUI, expose cron fired events to SDK clients, and report cron fire times with local timezone offsets. -- Add `PYTHINKER_MODEL_ADAPTIVE_THINKING` (and a matching `adaptive_thinking` model-alias field) to force adaptive thinking (`thinking: { type: 'adaptive' }`) on or off, overriding the Anthropic model-name version inference. This lets custom-named compatible endpoints that back an adaptive-capable model opt in even when the model name does not encode a parseable Claude version. - -### Bug Fixes - -- Report truncated compaction summaries clearly and apply valid completion token budgets across supported providers. -- Fix glob pattern backslash escaping and include match count in truncation messages. - -### Polish - -- Clarify Kimi Platform API key login labels and prompt details. -- Polish a small TUI visual interaction. - -## 0.6.0 (2026-05-29) - -### Features - -- Add a `PYTHINKER_MODEL_*` environment-variable channel that lets you run Pythinker Code against a specific model (provider type, base URL, API key, context size, capabilities, and thinking settings) without editing `config.toml`. -- Install plugins directly from GitHub repository URLs, and surface each install's origin and trust level (pythinker-official, curated, third-party) in the plugin manager. - -### Bug Fixes - -- Show the real terminal status of background agents in the transcript so lost, failed, and killed ones no longer appear as completed, and include the resume agent id and recovery instructions in the failure notification so the model can resume reliably. -- Recover from provider model token limit errors during long conversations. -- Automatically retry when a model response stream is dropped mid-flight (a `terminated` error) instead of failing the turn. -- Handle context overflow errors consistently across provider responses. -- Back off failed compaction retries by a fixed slice of the model context window. -- Fix the native self-updater reporting a successful update when the install command actually failed. -- Project persisted hook and blocked prompt messages into model context. -- Keep blocked prompt hook conversations available to subsequent model turns. -- Fix footer leaking onto the terminal when resuming a non-existent session. -- Fix automatic ripgrep installation when temporary files are on another filesystem. - -### Polish - -- Remove the default per-turn step limit of 1000. Users can still set `max_steps_per_turn` in config to enforce a custom limit. -- Support querying sessions by sessionId or workDir in listSessions, and show a helpful cd command when resuming a session from a different working directory. -- Expand the footer's rotating tips to surface more commands and shortcuts, featuring newer and important ones more prominently. -- Improve the usage information display in the TUI. -- Restrict plugin trust badges to Pythinker-hosted plugin CDN URL patterns. -- Clarify subagent and background task stop messages as user-initiated. -- Align the datasource plugin with the generic two-tool workflow. - -### Refactors - -- Introduce `ModelProvider` interface and `SingleModelProvider` to decouple `Agent` from `ProviderManager`. -- Split `RuntimeConfig` into `Pyaos` and `ToolServices` and update all references accordingly. -- Slim the LLM diagnostic logs with fewer, more compact fields. -- Relocate shared tool service typing to the tool support layer. - -## 0.5.0 (2026-05-28) - -### Features - -- Add scheduled tasks: - - You can now ask the agent to remind you at a specific time, run a task on a recurring cron schedule (for example, check a deploy every 5 minutes or run a daily report every weekday at 9am), or come back on its own in a few minutes to continue what it was doing. - - Schedules use the standard 5-field cron syntax. - -- Add `/auto` slash command and `--auto` CLI flag for auto permission mode. -- Show file content and diff in Write and Edit approval prompts, and open them in a dedicated full-screen viewer on ctrl+e instead of expanding inline. - -### Bug Fixes - -- Fix compaction to handle edge cases where no messages are compactable and improve retry logic. -- Fix official datasource tools to preserve complete responses and write returned result files. -- Fix migration mapping the legacy `default_yolo` key to the dead `yolo` field instead of `default_permission_mode`. - -### Polish - -- Add a clickable changelog link to the update prompt. -- Show the full Bash command when expanding a Bash tool card with `ctrl+o`. The header still truncates long commands at 60 chars, but the expanded view now reveals the complete multi-line command above the output. -- Shorten the session title written to the terminal window/tab from 80 to 32 characters so long first messages and pasted content no longer stretch the tab bar past readable width. -- Cap the inline todo panel at five rows and show a `+N more` indicator so long task lists no longer fill the screen. -- Clarify plugin manager keyboard shortcuts and show plugin state changes inline. -- Report discovered plugin skills in plugin manager summaries. -- Offload large base64 media payloads from `wire.jsonl` into external blob files to reduce wire size and memory pressure during session replay. Includes an in-memory read-through cache on `BlobStore` so repeated rehydration avoids redundant disk reads. -- Wrap long question, body, and option text in the AskUserQuestion dialog instead of truncating with an ellipsis. The question prompt, body description, option label, option description, and submit-tab review entries now flow onto multiple lines with a hanging indent. - -### Refactors - -- Refactor TUI code structure. - -## 0.4.0 (2026-05-27) - -### Features - -- Add user-global plugin installation, interactive plugin management, plugin-provided skills, and plugin-owned MCP servers. -- Expand folded paste markers on second paste. -- Rework tool permissions: reads outside cwd no longer prompt, session approvals match the exact call, and path-based rules are case-insensitive. -- Add `/export-debug-zip` slash command to export the current session as a debug ZIP archive directly from the TUI. -- Add `/export-md` slash command to export the current session as a Markdown file. - -### Bug Fixes - -- Prevent the TUI from crashing when pull request lookup fails during startup. -- Fix thinking spinner leaking past turn end when an empty thinking delta creates an orphaned thinking component. -- Show the original session resume command after forking a session. -- Restrict plugin zip installs to manifests at the archive root or a single wrapper directory. -- Route session-tagged log entries exclusively to the session sink instead of duplicating them to the global sink. Consistently omit stable main-agent context keys from all session log lines that carry `agentId=main`. - -### Refactors - -- Refactor TUI resume replay logic. -- Use one retry classification for transient LLM failures across regular turns and compaction. - -### Other - -- Enhance `pythinker export` to include more diagnostic information in the manifest. - -## 0.3.0 (2026-05-26) - -### Features - -- `/logout` now opens a picker so you can choose which provider to log out of, instead of always logging out the one tied to the current model. The current provider is highlighted by default, so pressing Enter matches the previous behavior. The command is also available as `/disconnect`. -- The `openai` provider now works out of the box for OpenAI-compatible reasoner models: it auto-detects thinking fields in responses (`reasoning_content` / `reasoning_details` / `reasoning`) and auto-injects `reasoning_effort` when history contains prior thinking. DeepSeek, Qwen, One API and other gateway-fronted services no longer need a hand-set `reasoning_key`, which remains available as an explicit override for non-standard gateways. - -### Bug Fixes - -- Prevent running the `/model` and `/sessions` slash commands while streaming or compacting context. -- Preserve catalog-declared interleaved reasoning fields for OpenAI-compatible models configured through `/connect`. -- Fix API key input dialog showing a masked dot in empty state. -- Fix user skills in `~/.agents/` not being loaded. -- Restore real-time token display for running subagents in the TUI. -- Hide the todo panel on resume when all todos are already completed. -- Always emit a paired tool result when a tool returns a malformed or missing result, preventing the next request from failing with a missing tool_call_id error. -- Fix Plan mode session resets so new sessions no longer fail after plan review rejection and continue receiving events after setup errors. -- Exit promptly when the controlling terminal goes away. The TUI now handles `SIGHUP` / `SIGTERM` and stdout/stderr `EIO` / `EPIPE` / `ENOTCONN` errors, preventing leftover `pythinker` processes that pin a CPU core after the parent shell or multiplexer dies unexpectedly. -- Avoid overly small local completion caps that can truncate reasoning before summaries are produced. - -### Refactors - -- Make `AgentRecords` hold the `Agent` instance directly and inline the restore dispatch logic. - -### Other - -- Improve the Write tool UX. - -## 0.2.0 (2026-05-26) - -### Features - -- Add a `/connect` command that configures a provider and model from a model catalog. -- The `/connect` provider and model pickers now support type-to-search filtering, and long lists are paginated. The `/model` picker is also paginated when many models are configured. -- Add `Ctrl-J` as an additional shortcut for inserting new lines in the TUI prompt. -- Add wire record migration handling during session replay. -- Migrate user skills from `~/.pythinker/skills/` to `~/.pythinker-code/skills/` during the first-launch migration; existing target skills are kept. -- Emit session resume hint as a structured meta message in stream-json output format. - -### Bug Fixes - -- Report the macOS product version in OAuth device information instead of the Darwin kernel version. -- Correct the `X-Msh-Platform` header value to `pythinker_code_cli`. -- Clarify the prompt-mode error when no model is configured by pointing users to the login flow. -- Hide the empty current session from the sessions picker while keeping other empty sessions visible. -- Stop mentioning OAuth credentials in the migration UI — they are never migrated, so the previous "needs /login" notice misread as a failure. OAuth-only installs no longer trigger the migration screen. -- Surface API-provided error messages during feedback, usage, login, and model setup failures. -- Persist model selections from the terminal UI to the default configuration, and honor the configured default thinking state for new sessions. -- Retry compaction responses that do not contain a summary before updating conversation history. -- Avoid CPU spikes from large streamed tool arguments and coalesce high-frequency streaming UI updates. -- Resume sessions with a newer wire protocol version instead of failing. A warning is now shown in the TUI and records are replayed without migration. -- Warn tmux users when extended key settings may prevent modified Enter shortcuts from working. -- Let Pythinker requests use the remaining context window for completion tokens by default while keeping explicit environment limits as hard caps. - -### Refactors - -- Flatten tool call data by inlining tool names and arguments at the top level, and limit legacy record migration so it only rewrites matching tool call payloads. -- Move wire metadata handling into the record layer and keep persistence backends limited to storage operations. - -### Other - -- When no models are configured, `/model` and the welcome panel now point users to `/login` (for Pythinker) and `/connect` (for other providers). diff --git a/docs/reference/pythinker-command.md b/docs/reference/pythinker-command.md index 9b24fed6c..dc333f179 100644 --- a/docs/reference/pythinker-command.md +++ b/docs/reference/pythinker-command.md @@ -198,7 +198,7 @@ Generate a new persistent bearer token (written to `~/.pythinker-code/server.tok ### `pythinker install-app` -Print the Pythinker Code desktop app page and open it in the default browser, so you can download and install the desktop app without leaving the terminal. The URL follows the active region: `https://www.kimi.com/code` on the mainland region, `https://www.kimi.ai/code` on the global region. +Print the Pythinker Code desktop app page and open it in the default browser, so you can download and install the desktop app without leaving the terminal. The page is `https://www.pythinker.com/code`. ```sh pythinker install-app diff --git a/docs/reference/slash-commands.md b/docs/reference/slash-commands.md index 92c633b5c..8e59c2b41 100644 --- a/docs/reference/slash-commands.md +++ b/docs/reference/slash-commands.md @@ -42,7 +42,7 @@ Some commands are only available in the idle state. Executing these commands whi | `/copy` | — | Copy the last assistant message to the clipboard | No | | `/add-dir []` | — | Add an extra workspace directory to the current session. Run without a path (or with `list`) to list configured directories. When adding, choose whether to remember the directory for the project in `.pythinker-code/local.toml` | No | | `/web` | — | Open the current session in the web UI: pick a running server to connect to, or start a new foreground server after the TUI exits. See [`pythinker web`](./pythinker-command.md#pythinker-web) | Yes | -| `/desktop` | `/install-desktop` | Open the Pythinker Code desktop app page in your browser (URL follows the active region: `https://www.kimi.com/code` or `https://www.kimi.ai/code`). See [`pythinker install-app`](./pythinker-command.md#pythinker-install-app) | Yes | +| `/desktop` | `/install-desktop` | Open the Pythinker Code desktop app page in your browser (`https://www.pythinker.com/code`). See [`pythinker install-app`](./pythinker-command.md#pythinker-install-app) | Yes | ## Modes & Run Control diff --git a/package.json b/package.json index f595aa6ff..58664d333 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "build:plugin-marketplace": "pnpm -C apps/pythinker-code run build:plugin-marketplace", "dev:docs": "pnpm -C docs install --ignore-workspace && pnpm -C docs run dev", "typecheck": "pnpm run build:packages && pnpm -r --filter './packages/*' run typecheck && pnpm --filter @pymodel/pythinker-code run typecheck && pnpm --filter @pymodel/pythinker-web run typecheck && pnpm --filter @pymodel/pythinker-desktop run typecheck && pnpm --filter @pymodel/vis-server run typecheck && pnpm --filter @pymodel/vis-web run typecheck", - "lint": "node scripts/check-product-boundaries.mjs && node scripts/check-identity-freeze.mjs && node scripts/check-no-comments.mjs && oxlint --type-aware", + "lint": "node scripts/check-product-boundaries.mjs && node scripts/check-identity-freeze.mjs && node scripts/check-no-comments.mjs && node scripts/oxlint-coverage.mjs", "lint:fix": "pnpm run lint --fix", "lint:pkg": "pnpm --filter @pymodel/pythinker-code exec publint && npm_config_cache=${TMPDIR:-/tmp}/pythinker-code-npm-cache pnpm --filter @pymodel/pythinker-code exec attw --pack . --profile node16", "sherif": "sherif --ignore-package ./apps/vscode -i @agentclientprotocol/sdk", diff --git a/packages/agent-core-v2/src/features/skill/catalog/builtin/check-pythinker-code-docs.md b/packages/agent-core-v2/src/features/skill/catalog/builtin/check-pythinker-code-docs.md index dbbd69bcc..1921609d6 100644 --- a/packages/agent-core-v2/src/features/skill/catalog/builtin/check-pythinker-code-docs.md +++ b/packages/agent-core-v2/src/features/skill/catalog/builtin/check-pythinker-code-docs.md @@ -1,18 +1,18 @@ --- name: check-pythinker-code-docs -description: Answer questions about the Pythinker Code product using the official documentation — CLI usage, configuration, slash commands, features, membership and quota, API onboarding, third-party tool setup, and error codes. Use when the user asks how Pythinker Code works, how to set something up, or what a Pythinker Code error message means. +description: Answer questions about the Pythinker Code product using the official documentation — CLI usage, configuration, slash commands, features, and guides. Use when the user asks how Pythinker Code works, how to set something up, or how a documented feature behaves. --- # Check Pythinker Code docs (check-pythinker-code-docs) -Answer Pythinker Code **product** questions from the official documentation site, not from memory. This skill covers product usage ("how do I configure a provider", "what does this error mean", "how does membership quota work"); it is not for developing the Pythinker Code repository itself. +Answer Pythinker Code **product** questions from the official documentation site, not from memory. This skill covers product usage ("how do I configure a provider", "what does this error message mean", "how does Remote Control work"); it is not for developing the Pythinker Code repository itself. ## The single source of truth Official documentation (English): ``` -https://www.kimi.com/code/docs/en/ +https://code.pythinker.com/pythinker-code/en/ ``` Fetch pages with **FetchURL** before answering. All page links below are relative to this base. @@ -21,18 +21,14 @@ Fetch pages with **FetchURL** before answering. All page links below are relativ | Question topic | Page (relative to the base URL) | | --- | --- | -| What Pythinker Code is; Base URL / API Key; standard vs high-speed model; platform comparison | `./` (home overview) | -| Membership plans, quota and rate limits, fuel packs | `pythinker-code/membership.html` | -| Install / login / usage FAQ | `pythinker-code/faq.html` | -| Error codes and their meaning (e.g. 401 for high-speed model access) | `pythinker-code/error-reference.html` | -| Product news and recent changes | `pythinker-code/whats-new.html` | -| Community guidelines; contact and feedback | `pythinker-code/community-guidelines.html`, `pythinker-code/contact-and-feedback.html` | -| `config.toml` fields, providers/models, environment variables, data locations, config overrides | `pythinker-code-cli/configuration/` — `config-files.html`, `providers.html`, `env-vars.html`, `data-locations.html`, `overrides.html` | -| Skills, MCP, hooks, plugins, themes, agents/sub-agents, Pythinker Datasource | `pythinker-code-cli/customization/` — `skills.html`, `mcp.html`, `hooks.html`, `plugins.html`, `themes.html`, `agents.html`; Pythinker Datasource lives at `plugins.html#pythinker-datasource` | -| Getting started, sessions and context, goals, interaction and input, IDEs, migration, use cases | `pythinker-code-cli/guides/` — `getting-started.html`, `sessions.html`, `goals.html`, `interaction.html`, `ides.html`, `migration.html`, `use-cases.html` | -| Slash commands, keyboard shortcuts, builtin tools, `pythinker` command flags, ACP | `pythinker-code-cli/reference/` — `slash-commands.html`, `keyboard.html`, `tools.html`, `pythinker-command.html`, `pythinker-acp.html` | -| CLI changelog | `pythinker-code-cli/release-notes/changelog.html` | -| Using Pythinker Code in Claude Code and other third-party agents | `third-party-tools/other-coding-agents.html` | +| What Pythinker Code is; getting started; install and first run | `./` (home overview), `guides/getting-started.html` | +| Providers and models, API keys | `configuration/providers.html` | +| `config.toml` fields, environment variables, data locations, config overrides | `configuration/` — `config-files.html`, `env-vars.html`, `data-locations.html`, `overrides.html` | +| Skills, MCP, hooks, plugins, themes, agents/sub-agents | `customization/` — `skills.html`, `mcp.html`, `hooks.html`, `plugins.html`, `themes.html`, `agents.html` | +| Sessions and context, interaction and input, IDEs, use cases | `guides/` — `sessions.html`, `interaction.html`, `ides.html`, `use-cases.html` | +| Desktop app, Remote Control, browser web UI | `guides/` — `desktop.html`, `remote-control.html`, `web.html` | +| Slash commands, keyboard shortcuts, builtin tools, `pythinker` command flags, ACP, server API, release channels | `reference/` — `slash-commands.html`, `keyboard.html`, `tools.html`, `pythinker-command.html`, `pythinker-acp.html`, `server-api.html`, `release-channels.html` | +| CLI changelog | `release-notes/changelog.html` | If no row fits the question, fetch the docs home page and follow its navigation links. @@ -41,4 +37,4 @@ If no row fits the question, fetch the docs home page and follow its navigation 1. Pick the page from the table above. 2. **FetchURL the page before answering** — answer strictly from the fetched content, never from memory. 3. Cite the page link(s) you used at the end of the answer. -4. If the fetch fails or the docs do not cover the question, say so plainly: answer from what you already know, attach the docs entry link (`https://www.kimi.com/code/docs/en/`), and mark which parts you could not verify. **Never invent config keys, command names, model IDs, or product behaviors.** +4. If the fetch fails or the docs do not cover the question, say so plainly: answer from what you already know, attach the docs entry link (`https://code.pythinker.com/pythinker-code/en/`), and mark which parts you could not verify. **Never invent config keys, command names, model IDs, or product behaviors.** diff --git a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/context.jsonl b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/context.jsonl index 4549c3c02..88c33b2cc 100644 --- a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/context.jsonl +++ b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/context.jsonl @@ -1,9 +1,9 @@ {"role": "_checkpoint", "id": 0} -{"role": "user", "content": "帮我写一个示例函数,处理字符串清理和分块"} +{"role": "user", "content": "Write me an example function that handles string cleanup and chunking"} {"role": "_checkpoint", "id": 1} {"role":"user","content":"\n\nPlan mode is active. You are in a research and planning phase.\n\nIn plan mode, you should:\n1. Thoroughly explore the codebase using Glob, Grep, and ReadFile tools.\n2. Understand existing patterns and architecture.\n3. Design an implementation approach.\n4. Write your plan to the plan file using WriteFile.\n5. Use AskUserQuestion if you need to clarify approaches.\n6. When your plan is complete, call ExitPlanMode.\n\nRemember: DO NOT write or edit code files... [truncated]"} {"role": "_usage", "token_count": 9665} -{"role": "assistant", "content": [{"type": "think", "think": "用户想要写一个示例函数,处理字符串清理和分块。我先看看当前 diff 中有哪些相关代码可以参考。\n\n让我先检查一下当前的 git diff。", "encrypted": null}], "tool_calls": [{"type": "function", "id": "Shell:0", "function": {"name": "Shell", "arguments": "{\"command\": \"git diff --no-color\"}"}}]} +{"role": "assistant", "content": [{"type": "think", "think": "The user wants an example function that handles string cleanup and chunking. Let me first check the current diff for related code to reference.\n\nLet me check the current git diff first.", "encrypted": null}], "tool_calls": [{"type": "function", "id": "Shell:0", "function": {"name": "Shell", "arguments": "{\"command\": \"git diff --no-color\"}"}}]} {"role": "_usage", "token_count": 9722} {"role": "tool", "content": [{"type": "text", "text": "Command executed successfully."}, {"type": "text", "text": "diff --git a/example.py b/example.py\nindex 0000aaaa..1111bbbb 100644\n--- a/example.py\n+++ b/example.py\n@@ -1,12 +1,16 @@\n class Greeter:\n def __init__(self, name: str) -> None:\n self.name = name\n+ self.greetings: list[str] = []\n \n def greet(self) -> str:\n- return f\"Hello, {self.name}!\"\n+ msg = f\"Hello, {self.name}!\"\n+ self.greetings.append(msg)\n+ return msg\n \n- def farewell(self) -> str:\n- return f\"Goodbye, {self.name}!\"\n+ def farewell(self, formal: bool = False) -> str:\n+ prefix = \"Farewell\" if formal else \"Goodbye\"\n+ return f\"{prefix}, {self.name}!\"\n \ndiff --git a/example/utils.py b/example/utils.py\nindex 22223333..44445555 100644\n--- a/example/utils.py\n+++ b/example/utils.py\n@@ -10,16 +10,20 @@ def slugify(text: str) -> str:\n result = text.lower()\n result = result.replace(\" \", \"-\")\n- # Remove punctuation\n- result = \"\".join(c for c in result if c.isalnum() or c == \"-\")\n+ # Remove punctuation and collapse repeated dashes\n+ cleaned = \"\".join(c for c in result if c.isalnum() or c == \"-\")\n+ parts = [p for p in cleaned.split(\"-\") if p]\n+ result = \"-\".join(parts)\n return result\n \n \n def chunk(items: list[str], size: int) -> list[list[str]]:\n- if size <= 0:\n- raise ValueError(\"size must be positive\")\n- return [items[i:i + size] for i in range(0, len(items), size)]\n+ if size <= 0:\n+ raise ValueError(\"chunk size must be a positive integer\")\n+ if not items:\n+ return []\n+ return [items[i : i + size] for i in range(0, len(items), size)]\n \n \n def truncate(text: str, limit: int = 80) -> str:\n@@ -32,8 +36,12 @@ def truncate(text: str, limit: int = 80) -> str:\n return text\n return text[: limit - 1] + \"…\"\n \n+\n+def normalize_whitespace(text: str) -> str:\n+ return \" \".join(text.split())\n+\n diff --git a/example/cli.py b/example/cli.py\nindex 66667777..88889999 100644\n--- a/example/cli.py\n+++ b/example/cli.py\n@@ -5,12 +5,18 @@ from .utils import slugify, truncate\n \n def build_parser() -> argparse.ArgumentParser:\n parser = argparse.ArgumentParser(prog=\"example\")\n- parser.add_argument(\"--name\", default=\"world\")\n+ parser.add_argument(\"--name\", default=\"world\", help=\"who to greet\")\n+ parser.add_argument(\n+ \"--formal\",\n+ action=\"store_true\",\n+ help=\"use a formal farewell tone\",\n+ )\n return parser\n \n \n def main(argv: list[str] | None = None) -> int:\n parser = build_parser()\n args = parser.parse_args(argv)\n- print(f\"Hello, {args.name}!\")\n+ greeter = Greeter(args.name)\n+ print(greeter.greet())\n+ print(greeter.farewell(formal=args.formal))\n return 0\ndiff --git a/example/config.py b/example/config.py\nindex ccccdddd..eeeeffff 100644\n--- a/example/config.py\n+++ b/example/config.py\n@@ -1,15 +1,22 @@\n from dataclasses import dataclass, field\n+from pathlib import Path\n \n \n @dataclass\n class AppConfig:\n name: str = \"example\"\n verbose: bool = False\n- timeout: int = 30\n+ timeout_seconds: int = 30\n+ cache_dir: Path | None = None\n features: list[str] = field(default_factory=list)\n \n def is_feature_enabled(self, feature: str) -> bool:\n return feature in self.features\n+\n+ def with_feature(self, feature: str) -> \"AppConfig\":\n+ if feature in self.features:\n+ return self\n+ return AppConfig(**{**self.__dict__, \"features\": [*self.features, feature]})\n diff --git a/tests/test_example.py b/tests/test_example.py\nindex aabbccdd..eeff0011 100644\n--- a/tests/test_example.py\n+++ b/tests/test_example.py\n@@ -1,10 +1,22 @@\n from example import Greeter\n+from example.utils import slugify, chunk, normalize_whitespace\n+from example.config import AppConfig\n \n \n def test_greeter_greet() -> None:\n g = Greeter(\"Ada\")\n assert g.greet() == \"Hello, Ada!\"\n+ assert g.greetings == [\"Hello, Ada!\"]\n \n \n def test_greeter_farewell() -> None:\n g = Greeter(\"Ada\")\n assert g.farewell() == \"Goodbye, Ada!\"\n+ assert g.farewell(formal=True) == \"Farewell, Ada!\"\n+\n+\n+def test_slugify_collapses_dashes() -> None:\n+ assert slugify(\"hello world!!!\") == \"hello-world\"\n+ assert slugify(\"--abc--def--\") == \"abc-def\"\n+\n+\n+def test_config_with_feature_is_idempotent() -> None:\n+ cfg = AppConfig().with_feature(\"beta\").with_feature(\"beta\")\n+ assert cfg.features == [\"beta ... [truncated]"}], "tool_call_id": "Shell:0"} {"role": "_checkpoint", "id": 2} diff --git a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/state.json b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/state.json index 818625632..c043df19c 100644 --- a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/state.json +++ b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/state.json @@ -7,7 +7,7 @@ ] }, "additional_dirs": [], - "custom_title": "帮我创建一个 plan,来优化我当前 diff 中的代码", + "custom_title": "Create a plan for me to optimize the code in my current diff", "title_generated": false, "title_generate_attempts": 0, "plan_mode": false, diff --git a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/wire.jsonl b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/wire.jsonl index 03bb686c6..6c79c534c 100644 --- a/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/wire.jsonl +++ b/packages/migration-legacy/test/fixtures/legacy-protocol-1.3/wire.jsonl @@ -1,7 +1,7 @@ {"type": "metadata", "protocol_version": "1.3"} -{"timestamp": 1773128640.5874681, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "帮我写一个示例函数,处理字符串清理和分块"}]}}} +{"timestamp": 1773128640.5874681, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "Write me an example function that handles string cleanup and chunking"}]}}} {"timestamp": 1773128640.589089, "message": {"type": "StepBegin", "payload": {"n": 1}}} -{"timestamp": 1773128644.0387058, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "用户想要写一个示例函数,处理字符串清理和分块。我先看看当前 diff 中有哪些相关代码可以参考。\n\n让我先检查一下当前的 git diff。", "encrypted": null}}} +{"timestamp": 1773128644.0387058, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "The user wants an example function that handles string cleanup and chunking. Let me first check the current diff for related code to reference.\n\nLet me check the current git diff first.", "encrypted": null}}} {"timestamp": 1773128644.1119459, "message": {"type": "ToolCall", "payload": {"type": "function", "id": "Shell:0", "function": {"name": "Shell", "arguments": "{\"command\": \"git diff --no-color\"}"}, "extras": null}}} {"timestamp": 1773128644.112614, "message": {"type": "ApprovalRequest", "payload": {"id": "245bc43b-84ab-4db1-9e40-3f5508b0404f", "tool_call_id": "Shell:0", "sender": "Shell", "action": "run command", "description": "Run command `git diff --no-color`", "display": [{"type": "shell", "language": "bash", "command": "git diff --no-color"}]}}} {"timestamp": 1773128644.112929, "message": {"type": "StatusUpdate", "payload": {"context_usage": 0.03866, "context_tokens": 9665, "max_context_tokens": 250000, "token_usage": {"input_other": 4033, "output": 57, "input_cache_read": 5632, "input_cache_creation": 0}, "message_id": "chatcmpl-fixture-legacy-0001"}}} diff --git a/packages/migration-legacy/test/fixtures/multi-workdir/.pythinker/pythinker.json b/packages/migration-legacy/test/fixtures/multi-workdir/.pythinker/pythinker.json index fc0636ee3..bd5a81243 100644 --- a/packages/migration-legacy/test/fixtures/multi-workdir/.pythinker/pythinker.json +++ b/packages/migration-legacy/test/fixtures/multi-workdir/.pythinker/pythinker.json @@ -1,7 +1,7 @@ { "work_dirs": [ - { "path": "/proj-a", "kaos": "local", "last_session_id": null }, - { "path": "/proj-b", "kaos": "local", "last_session_id": null }, - { "path": "/proj-c", "kaos": "kaos-foo", "last_session_id": null } + { "path": "/proj-a", "pyaos": "local", "last_session_id": null }, + { "path": "/proj-b", "pyaos": "local", "last_session_id": null }, + { "path": "/proj-c", "pyaos": "pyaos-foo", "last_session_id": null } ] } diff --git a/packages/migration-legacy/test/fixtures/recent-protocol-1.10/context.jsonl b/packages/migration-legacy/test/fixtures/recent-protocol-1.10/context.jsonl index 0e96b405e..1071e5b7e 100644 --- a/packages/migration-legacy/test/fixtures/recent-protocol-1.10/context.jsonl +++ b/packages/migration-legacy/test/fixtures/recent-protocol-1.10/context.jsonl @@ -4,7 +4,7 @@ {"role": "_checkpoint", "id": 1} {"role":"assistant","content":[{"type":"think","think":"The user said \"hi\". This is a simple greeting. I should respond in a friendly, helpful manner. Since there's no specific task or question, I'll just greet them back and ask how I can help.","encrypted":null},{"type":"text","text":"Hello! How can I help you today?"}]} {"role": "_checkpoint", "id": 2} -{"role":"user","content":"探索这个项目"} +{"role":"user","content":"Explore this project"} {"role": "_checkpoint", "id": 3} -{"role":"assistant","content":[{"type":"think","think":"用户要求探索这个项目。让我先了解一下项目的整体结构,然后提供一些关键信息。\n\n从目录结构来看,这是一个示例 Python 项目。让我先查看一些关键文件来更好地理解项目。","encrypted":null}],"tool_calls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\"command\": \"find . -maxdepth 2 -type f -name \\\"*.md\\\" | grep -E \\\"(README|CHANGELOG|CONTRIBUTING|AGENTS)\\\" | sort\"}"}},{"type":"function","id":"Shell:1","function":{"name":"Shell","arguments":"{\"command\": \"ls -la pyproject.toml Makefile 2>/dev/null || echo \\\"No pyproject.toml or Makefile found\\\"\"}"}},{"type":"function","id":"Shell:2","function":{"name":"Shell","arguments":"{\"command\": \"git log --oneline -10 2>/dev/null || echo \\\"Not a git repo or no commits\\\"\"}"}}]} +{"role":"assistant","content":[{"type":"think","think":"The user asked me to explore this project. Let me first get an overview of the project structure, then provide the key facts.\n\nJudging by the directory layout, this is an example Python project. Let me look at a few key files to understand it better.","encrypted":null}],"tool_calls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\"command\": \"find . -maxdepth 2 -type f -name \\\"*.md\\\" | grep -E \\\"(README|CHANGELOG|CONTRIBUTING|AGENTS)\\\" | sort\"}"}},{"type":"function","id":"Shell:1","function":{"name":"Shell","arguments":"{\"command\": \"ls -la pyproject.toml Makefile 2>/dev/null || echo \\\"No pyproject.toml or Makefile found\\\"\"}"}},{"type":"function","id":"Shell:2","function":{"name":"Shell","arguments":"{\"command\": \"git log --oneline -10 2>/dev/null || echo \\\"Not a git repo or no commits\\\"\"}"}}]} {"role":"tool","content":[{"type":"text","text":"Command executed successfully."},{"type":"text","text":"./.pytest_cache/README.md\n./AGENTS.md\n./CHANGELOG.md\n./CONTRIBUTING.md\n./docs/AGENTS.md\n./README.md\n./tests_e2e/AGENTS.md\n"}],"tool_call_id":"Shell:0"} diff --git a/packages/migration-legacy/test/fixtures/recent-protocol-1.10/wire.jsonl b/packages/migration-legacy/test/fixtures/recent-protocol-1.10/wire.jsonl index b8fa65824..1692610dc 100644 --- a/packages/migration-legacy/test/fixtures/recent-protocol-1.10/wire.jsonl +++ b/packages/migration-legacy/test/fixtures/recent-protocol-1.10/wire.jsonl @@ -5,6 +5,6 @@ {"timestamp": 1778563951.826904, "message": {"type": "ContentPart", "payload": {"type": "text", "text": "Hello! How can I help you today?"}}} {"timestamp": 1778563951.827712, "message": {"type": "StatusUpdate", "payload": {"context_usage": null, "context_tokens": null, "max_context_tokens": null, "token_usage": null, "message_id": "chatcmpl-fixture-recent-0001", "plan_mode": false, "mcp_status": null}}} {"timestamp": 1778563951.8288698, "message": {"type": "TurnEnd", "payload": {}}} -{"timestamp": 1778563970.0091572, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "探索这个项目"}]}}} +{"timestamp": 1778563970.0091572, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "Explore this project"}]}}} {"timestamp": 1778563970.010856, "message": {"type": "StepBegin", "payload": {"n": 1}}} -{"timestamp": 1778563970.716398, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "用户要求探索这个项目。让我先了解一下项目的整体结构,然后提供一些关键信息。\n\n从目录结构来看,这是一个示例 Python 项目。让我先查看一些关键文件来更好地理解项目。", "encrypted": null}}} +{"timestamp": 1778563970.716398, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "The user asked me to explore this project. Let me first get an overview of the project structure, then provide the key facts.\n\nJudging by the directory layout, this is an example Python project. Let me look at a few key files to understand it better.", "encrypted": null}}} diff --git a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/context.jsonl b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/context.jsonl index d86a7278c..e38453b1a 100644 --- a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/context.jsonl +++ b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/context.jsonl @@ -1,10 +1,10 @@ {"role":"_system_prompt","content":"You are an AI agent. (system prompt elided in fixture)"} {"role": "_checkpoint", "id": 0} -{"role":"user","content":"你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上"} +{"role":"user","content":"Launch three sub agents; each one must run a few simple commands that require approval — run at least two"} {"role": "_checkpoint", "id": 1} {"role": "_usage", "token_count": 12095} -{"role":"assistant","content":[{"type":"think","think":"用户要求我启动三个 sub agent,每个 sub agent 都需要运行一些需要被批准的简单指令,并且要求运行两个以上。\n\n我需要使用 Agent 工具来创建三个 sub agent。每个 sub agent 都应该执行一些需要批准的操作,比如文件写入或 shell 命令。\n\n让我创建三个 sub agent,每个都执行不同的需要批准的任务:\n1. 第一个 sub agent:创建一个测试文件\n2. 第二个 sub agent:执行一个简单的 shell 命令\n3. 第三个 sub agent:创建另一个测试文件\n\n我需要并行启动这三个 sub agent。","encrypted":null},{"type":"text","text":"我来启动三个 sub agent,每个都会执行需要批准的简单指令。"}],"tool_calls":[{"type":"function","id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 1: 创建文件\", \"prompt\": \"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\n\\n1. 使用 WriteFile 工具在当前目录创建一个名为 `subagent1_test.txt` 的文件,内容为 \\\"Hello from Sub Agent 1!\\\"\\n\\n这个操作需要用户批准,请等待批准并完成操作。\\n\\n完成后,报告操作结果。\"}"}},{"type":"function","id":"tool_OtoGSYsrDQpEaav0hLYbXDhd","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 2: Shell 命令\", \"prompt\": \"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\n\\n1. 使用 Shell 工具执行命令 `echo \\\"Hello from Sub Agent 2!\\\" > subagent2_test.txt`\\n\\n这个操作需要用户批准,请等待批准并完成操作。\\n\\n完成后,报告操作结果。\"}"}},{"type":"function","id":"tool_zi4b3Pfbiia2kxPMUvjL4cVN","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 3: 创建文件\", \"prompt\": \"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\n\\n1. 使用 WriteFile 工具在当前目录创建一个名为 `subagent3_test.txt` 的文件,内容为 \\\"Hello from Sub Agent 3!\\\"\\n\\n这个操作需要用户批准,请等待批准并完成操作。\\n\\n完成后,报告操作结果。\"}"}}]} +{"role":"assistant","content":[{"type":"think","think":"The user asked me to launch three sub agents, each of which must run a few simple commands that require approval — at least two of them.\n\nI need the Agent tool to create three sub agents. Each sub agent should perform an action that requires approval, such as a file write or a shell command.\n\nLet me create three sub agents, each performing a different task that requires approval:\n1. Sub agent 1: create a test file\n2. Sub agent 2: run a simple shell command\n3. Sub agent 3: create another test file\n\nI need to launch these three sub agents in parallel.","encrypted":null},{"type":"text","text":"I am launching three sub agents, each running a simple command that requires approval."}],"tool_calls":[{"type":"function","id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 1: Create file\", \"prompt\": \"You are a test sub agent. Perform the following actions that require approval:\\n\\n1. Use the WriteFile tool to create a file named `subagent1_test.txt` in the current directory with the content \\\"Hello from Sub Agent 1!\\\"\\n\\nThis action requires user approval; wait for approval and then complete the action.\\n\\nWhen done, report the result of the operation.\"}"}},{"type":"function","id":"tool_OtoGSYsrDQpEaav0hLYbXDhd","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 2: Shell command\", \"prompt\": \"You are a test sub agent. Perform the following actions that require approval:\\n\\n1. Use the Shell tool to run the command `echo \\\"Hello from Sub Agent 2!\\\" > subagent2_test.txt`\\n\\nThis action requires user approval; wait for approval and then complete the action.\\n\\nWhen done, report the result of the operation.\"}"}},{"type":"function","id":"tool_zi4b3Pfbiia2kxPMUvjL4cVN","function":{"name":"Agent","arguments":"{\"description\": \"Sub agent 3: Create file\", \"prompt\": \"You are a test sub agent. Perform the following actions that require approval:\\n\\n1. Use the WriteFile tool to create a file named `subagent3_test.txt` in the current directory with the content \\\"Hello from Sub Agent 3!\\\"\\n\\nThis action requires user approval; wait for approval and then complete the action.\\n\\nWhen done, report the result of the operation.\"}"}}]} {"role": "_usage", "token_count": 12507} -{"role":"tool","content":"agent_id: ac1f74988\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n## 详细操作报告\n\n### 任务背景\n作为测试用的 sub agent,我被要求执行一个需要用户批准的文件写入操作。这是一个测试场景,旨在验证带批准流程的工具调用机制。\n\n### 执行过程\n\n#### 第一次尝试(被拒绝)\n- **操作**: 使用 `WriteFile` 工具创建文件 `subagent1_test.txt`\n- **预期内容**: `\"Hello from Sub Agent 1!\"`\n- **结果**: 用户拒绝了此操作\n- **用户反馈**: 要求将文件名改为 `kk`\n\n#### 第二次尝试(成功)\n- **操作**: 使用 `WriteFile` 工具创建文件 `kk`\n- **内容**: `\"Hello from Sub Agent 1!\"`\n- **结果**: ✅ 成功创建/覆盖文件\n- **文件大小**: 23 字节\n\n### 技术细节\n\n1. **工具使用**: `W... [truncated]","tool_call_id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8"} -{"role": "tool", "content": "agent_id: aa373b195\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n## 详细操作结果报告\n\n### 1. 任务概述\n\n作为测试用 sub agent,我成功执行了需要用户批准的 Shell 命令操作,创建了测试文件并验证了其内容完整性。\n\n### 2. 执行详情\n\n#### 2.1 命令信息\n- **命令类型**: Shell 命令执行\n- **具体命令**: `echo \"Hello from Sub Agent 2!\" > subagent2_test.txt`\n- **工作目录**: `/Users/example/proj/example-project`\n- **执行时间**: 2026-03-23T21:42:43+08:00(系统参考时间)\n\n#### 2.2 技术实现\n该命令使用了以下 Shell 特性:\n- `echo` 命令:输出字符串到标准输出\n- 重定向操作符 `>`:将标准输出重定向到文件\n- 如果文件不存在则... [truncated]", "tool_call_id": "tool_OtoGSYsrDQpEaav0hLYbXDhd"} -{"role": "tool", "content": "agent_id: aadd98577\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n**详细操作报告 - Sub Agent 3**\n\n---\n\n## 1. 任务概述\n\n本次任务要求以测试用的 sub agent 身份执行一个需要用户批准的操作:在指定目录创建一个包含特定内容的文本文件。\n\n---\n\n## 2. 操作执行详情\n\n### 2.1 第一次尝试\n\n| 属性 | 详情 |\n|------|------|\n| **目标文件路径** | `subagent3_test.txt` |\n| **请求内容** | `Hello from Sub Agent 3!` |\n| **操作类型** | WriteFile(覆盖模式,默认)|\n| **执行状态** | 被拒绝 |\n| **用户反馈** | \"用另一个文件名再试一次\" |\n\n**技术分析**:系统在第一次请求时触发了用户审批流程。用户拒绝了原始文件名 `subagent3_test.txt`,并明确要求使用另一个文件名再试。这表... [truncated]", "tool_call_id": "tool_zi4b3Pfbiia2kxPMUvjL4cVN"} +{"role":"tool","content":"agent_id: ac1f74988\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n## Detailed operation report\n\n### Task background\nAs a test sub agent, I was asked to perform a file write that requires user approval. This is a test scenario meant to exercise the tool-call approval flow.\n\n### Execution\n\n#### First attempt (rejected)\n- **Action**: Using `WriteFile` tool to create the file `subagent1_test.txt`\n- **Expected content**: `\"Hello from Sub Agent 1!\"`\n- **Result**: The user rejected this action\n- **User feedback**: Asked to rename the file to `kk`\n\n#### Second attempt (succeeded)\n- **Action**: Using `WriteFile` tool to create the file `kk`\n- **Content**: `\"Hello from Sub Agent 1!\"`\n- **Result**: ✅ File created/overwritten successfully\n- **File size**: 23 bytes\n\n### Technical details\n\n1. **Tool usage**: `W... [truncated]","tool_call_id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8"} +{"role": "tool", "content": "agent_id: aa373b195\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n## Detailed operation result report\n\n### 1. Task overview\n\nAs a test sub agent, I successfully ran the shell command that required user approval, created the test file, and verified its content.\n\n### 2. Execution details\n\n#### 2.1 Command information\n- **Command type**: Shell Command execution\n- **Exact command**: `echo \"Hello from Sub Agent 2!\" > subagent2_test.txt`\n- **Working directory**: `/Users/example/proj/example-project`\n- **Executed at**: 2026-03-23T21:42:43+08:00(system reference time)\n\n#### 2.2 Technical implementation\nThe command used the following shell features:\n- `echo` command: writes a string to standard output\n- redirection operator `>`: redirects standard output to a file\n- creates the file if it does not exist... [truncated]", "tool_call_id": "tool_OtoGSYsrDQpEaav0hLYbXDhd"} +{"role": "tool", "content": "agent_id: aadd98577\nresumed: false\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\n**Detailed operation report - Sub Agent 3**\n\n---\n\n## 1. Task overview\n\nThis task asked me, as a test sub agent, to perform one action that requires user approval: create a text file with specific content in a given directory.\n\n---\n\n## 2. Operation details\n\n### 2.1 First attempt\n\n| Property | Detail |\n|------|------|\n| **Target file path** | `subagent3_test.txt` |\n| **Requested content** | `Hello from Sub Agent 3!` |\n| **Operation type** | WriteFile(overwrite mode, default)|\n| **Status** | Rejected|\n| **User feedback** | \"Try again with a different file name\" |\n\n**Technical analysis**: The system triggered the user approval flow on the first request. The user rejected the original file name `subagent3_test.txt`, and explicitly asked to retry with a different file name. This... [truncated]", "tool_call_id": "tool_zi4b3Pfbiia2kxPMUvjL4cVN"} diff --git a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/state.json b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/state.json index 93c632372..b25a782db 100644 --- a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/state.json +++ b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/state.json @@ -5,7 +5,7 @@ "auto_approve_actions": [] }, "additional_dirs": [], - "custom_title": "你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上", + "custom_title": "Launch three sub agents; each one must run a few simple commands that require approval — run at least two", "title_generated": false, "title_generate_attempts": 1, "plan_mode": false, diff --git a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/wire.jsonl b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/wire.jsonl index 94330cbcc..ac731c427 100644 --- a/packages/migration-legacy/test/fixtures/with-subagent-collapsed/wire.jsonl +++ b/packages/migration-legacy/test/fixtures/with-subagent-collapsed/wire.jsonl @@ -1,10 +1,10 @@ {"type": "metadata", "protocol_version": "1.6"} -{"timestamp": 1774273363.887481, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上"}]}}} +{"timestamp": 1774273363.887481, "message": {"type": "TurnBegin", "payload": {"user_input": [{"type": "text", "text": "Launch three sub agents; each one must run a few simple commands that require approval — run at least two"}]}}} {"timestamp": 1774273363.888598, "message": {"type": "StepBegin", "payload": {"n": 1}}} -{"timestamp": 1774273371.364724, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "用户要求我启动三个 sub agent,每个 sub agent 都需要运行一些需要被批准的简单指令,并且要求运行两个以上。\n\n我需要使用 Agent 工具来创建三个 sub agent。每个 sub agent 都应该执行一些需要批准的操作,比如文件写入或 shell 命令。\n\n让我创建三个 sub agent,每个都执行不同的需要批准的任务:\n1. 第一个 sub agent:创建一个测试文件\n2. 第二个 sub agent:执行一个简单的 shell 命令\n3. 第三个 sub agent:创建另一个测试文件\n\n我需要并行启动这三个 sub agent。", "encrypted": null}}} -{"timestamp": 1774273371.87823, "message": {"type": "ContentPart", "payload": {"type": "text", "text": "我来启动三个 sub agent,每个都会执行需要批准的简单指令。"}}} -{"timestamp": 1774273375.4379919, "message": {"type": "ToolCall", "payload": {"type": "function", "id": "tool_Ct3NdbCjJ2Xbl7YzithE2Dy8", "function": {"name": "Agent", "arguments": "{\"description\": \"Sub agent 1: 创建文件\", \"prompt\": \"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\n\\n1. 使用 WriteFile 工具在当前目录创建一个名为 `subagent1_test.txt` 的文件,内容为 \\\"Hello from Sub Agent 1!\\\"\\n\\n这个操作需要用户批准,请等待批准并完成操作。\\n\\n完成后,报告操作结果。\"}"}, "extras": null}}} +{"timestamp": 1774273371.364724, "message": {"type": "ContentPart", "payload": {"type": "think", "think": "The user asked me to launch three sub agents, each of which must run a few simple commands that require approval — at least two of them.\n\nI need the Agent tool to create three sub agents. Each sub agent should perform an action that requires approval, such as a file write or a shell command.\n\nLet me create three sub agents, each performing a different task that requires approval:\n1. Sub agent 1: create a test file\n2. Sub agent 2: run a simple shell command\n3. Sub agent 3: create another test file\n\nI need to launch these three sub agents in parallel.", "encrypted": null}}} +{"timestamp": 1774273371.87823, "message": {"type": "ContentPart", "payload": {"type": "text", "text": "I am launching three sub agents, each running a simple command that requires approval."}}} +{"timestamp": 1774273375.4379919, "message": {"type": "ToolCall", "payload": {"type": "function", "id": "tool_Ct3NdbCjJ2Xbl7YzithE2Dy8", "function": {"name": "Agent", "arguments": "{\"description\": \"Sub agent 1: Create file\", \"prompt\": \"You are a test sub agent. Perform the following actions that require approval:\\n\\n1. Use the WriteFile tool to create a file named `subagent1_test.txt` in the current directory with the content \\\"Hello from Sub Agent 1!\\\"\\n\\nThis action requires user approval; wait for approval and then complete the action.\\n\\nWhen done, report the result of the operation.\"}"}, "extras": null}}} {"timestamp": 1774273375.475582, "message": {"type": "ToolCall", "payload": {"type": "function", "id": "tool_OtoGSYsrDQpEaav0hLYbXDhd", "function": {"name": "Agent", "arguments": "{\"description\": \""}, "extras": null}}} -{"timestamp": 1774273375.476424, "message": {"type": "SubagentEvent", "payload": {"parent_tool_call_id": "tool_Ct3NdbCjJ2Xbl7YzithE2Dy8", "agent_id": "ac1f74988", "subagent_type": "coder", "event": {"type": "TurnBegin", "payload": {"user_input": "你是一个测试用的 sub agent。请执行以下需要批准的操作:\n\n1. 使用 WriteFile 工具在当前目录创建一个名为 `subagent1_test.txt` 的文件,内容为 \"Hello from Sub Agent 1!\"\n\n这个操作需要用户批准,请等待批准并完成操作。\n\n完成后,报告操作结果。"}}}}} +{"timestamp": 1774273375.476424, "message": {"type": "SubagentEvent", "payload": {"parent_tool_call_id": "tool_Ct3NdbCjJ2Xbl7YzithE2Dy8", "agent_id": "ac1f74988", "subagent_type": "coder", "event": {"type": "TurnBegin", "payload": {"user_input": "You are a test sub agent. Perform the following actions that require approval:\n\n1. Use the WriteFile tool to create a file named `subagent1_test.txt` in the current directory with the content \"Hello from Sub Agent 1!\"\n\nThis action requires user approval; wait for approval and then complete the action.\n\nWhen done, report the result of the operation."}}}}} {"timestamp": 1774273375.4771452, "message": {"type": "SubagentEvent", "payload": {"parent_tool_call_id": "tool_Ct3NdbCjJ2Xbl7YzithE2Dy8", "agent_id": "ac1f74988", "subagent_type": "coder", "event": {"type": "StepBegin", "payload": {"n": 1}}}}} -{"timestamp": 1774273378.226876, "message": {"type": "ToolCallPart", "payload": {"arguments_part": "Sub agent 2: Shell 命令\", \"prompt\": \"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\n\\n1. 使用 Shell 工具执行命令 `echo \\\"Hello from Sub Agent 2!\\\" > subagent2_test.txt`\\n\\n这个操作需要用户批准,请等待批准并完成操作。\\n\\n完成后,"}}} +{"timestamp": 1774273378.226876, "message": {"type": "ToolCallPart", "payload": {"arguments_part": "Sub agent 2: Shell command\", \"prompt\": \"You are a test sub agent. Perform the following actions that require approval:\\n\\n1. Use the Shell tool to run the command `echo \\\"Hello from Sub Agent 2!\\\" > subagent2_test.txt`\\n\\nThis action requires user approval; wait for approval and then complete the action.\\n\\nWhen done, "}}} diff --git a/packages/migration-legacy/test/sessions/__snapshots__/fixtures.snapshot.test.ts.snap b/packages/migration-legacy/test/sessions/__snapshots__/fixtures.snapshot.test.ts.snap index be4dcb6ba..b164989d7 100644 --- a/packages/migration-legacy/test/sessions/__snapshots__/fixtures.snapshot.test.ts.snap +++ b/packages/migration-legacy/test/sessions/__snapshots__/fixtures.snapshot.test.ts.snap @@ -159,7 +159,7 @@ exports[`migration snapshot: legacy-protocol-1.3 > migration succeeds and matche "updatedAt": "".2852, "archived": true, "archivedAt": 1774779476844.07, - "title": "帮我创建一个 plan,来优化我当前 diff 中的代码", + "title": "Create a plan for me to optimize the code in my current diff", "titleKind": "custom", "isCustomTitle": true, "lastPrompt": "\\n\\nPlan mode is active. You are in a research and planning phase.\\n\\nIn plan mode, you should:\\n1. Thoroughly explore the codebase using Glob, Grep, and ReadFil", @@ -185,11 +185,11 @@ exports[`migration snapshot: legacy-protocol-1.3 > migration succeeds and matche } }", "wire": "{"type":"metadata","protocol_version":"1.0","created_at":} -{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"帮我写一个示例函数,处理字符串清理和分块"}],"origin":{"kind":"user"},"time":} -{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"帮我写一个示例函数,处理字符串清理和分块"}],"toolCalls":[]}} +{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"Write me an example function that handles string cleanup and chunking"}],"origin":{"kind":"user"},"time":} +{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"Write me an example function that handles string cleanup and chunking"}],"toolCalls":[]}} {"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"\\n\\nPlan mode is active. You are in a research and planning phase.\\n\\nIn plan mode, you should:\\n1. Thoroughly explore the codebase using Glob, Grep, and ReadFile tools.\\n2. Understand existing patterns and architecture.\\n3. Design an implementation approach.\\n4. Write your plan to the plan file using WriteFile.\\n5. Use AskUserQuestion if you need to clarify approaches.\\n6. When your plan is complete, call ExitPlanMode.\\n\\nRemember: DO NOT write or edit code files... [truncated]"}],"origin":{"kind":"user"},"time":} {"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"\\n\\nPlan mode is active. You are in a research and planning phase.\\n\\nIn plan mode, you should:\\n1. Thoroughly explore the codebase using Glob, Grep, and ReadFile tools.\\n2. Understand existing patterns and architecture.\\n3. Design an implementation approach.\\n4. Write your plan to the plan file using WriteFile.\\n5. Use AskUserQuestion if you need to clarify approaches.\\n6. When your plan is complete, call ExitPlanMode.\\n\\nRemember: DO NOT write or edit code files... [truncated]"}],"toolCalls":[]}} -{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"用户想要写一个示例函数,处理字符串清理和分块。我先看看当前 diff 中有哪些相关代码可以参考。\\n\\n让我先检查一下当前的 git diff。"}],"toolCalls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\\"command\\": \\"git diff --no-color\\"}"}}]}} +{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"The user wants an example function that handles string cleanup and chunking. Let me first check the current diff for related code to reference.\\n\\nLet me check the current git diff first."}],"toolCalls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\\"command\\": \\"git diff --no-color\\"}"}}]}} {"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"Command executed successfully."},{"type":"text","text":"diff --git a/example.py b/example.py\\nindex 0000aaaa..1111bbbb 100644\\n--- a/example.py\\n+++ b/example.py\\n@@ -1,12 +1,16 @@\\n class Greeter:\\n def __init__(self, name: str) -> None:\\n self.name = name\\n+ self.greetings: list[str] = []\\n \\n def greet(self) -> str:\\n- return f\\"Hello, {self.name}!\\"\\n+ msg = f\\"Hello, {self.name}!\\"\\n+ self.greetings.append(msg)\\n+ return msg\\n \\n- def farewell(self) -> str:\\n- return f\\"Goodbye, {self.name}!\\"\\n+ def farewell(self, formal: bool = False) -> str:\\n+ prefix = \\"Farewell\\" if formal else \\"Goodbye\\"\\n+ return f\\"{prefix}, {self.name}!\\"\\n \\ndiff --git a/example/utils.py b/example/utils.py\\nindex 22223333..44445555 100644\\n--- a/example/utils.py\\n+++ b/example/utils.py\\n@@ -10,16 +10,20 @@ def slugify(text: str) -> str:\\n result = text.lower()\\n result = result.replace(\\" \\", \\"-\\")\\n- # Remove punctuation\\n- result = \\"\\".join(c for c in result if c.isalnum() or c == \\"-\\")\\n+ # Remove punctuation and collapse repeated dashes\\n+ cleaned = \\"\\".join(c for c in result if c.isalnum() or c == \\"-\\")\\n+ parts = [p for p in cleaned.split(\\"-\\") if p]\\n+ result = \\"-\\".join(parts)\\n return result\\n \\n \\n def chunk(items: list[str], size: int) -> list[list[str]]:\\n- if size <= 0:\\n- raise ValueError(\\"size must be positive\\")\\n- return [items[i:i + size] for i in range(0, len(items), size)]\\n+ if size <= 0:\\n+ raise ValueError(\\"chunk size must be a positive integer\\")\\n+ if not items:\\n+ return []\\n+ return [items[i : i + size] for i in range(0, len(items), size)]\\n \\n \\n def truncate(text: str, limit: int = 80) -> str:\\n@@ -32,8 +36,12 @@ def truncate(text: str, limit: int = 80) -> str:\\n return text\\n return text[: limit - 1] + \\"…\\"\\n \\n+\\n+def normalize_whitespace(text: str) -> str:\\n+ return \\" \\".join(text.split())\\n+\\n diff --git a/example/cli.py b/example/cli.py\\nindex 66667777..88889999 100644\\n--- a/example/cli.py\\n+++ b/example/cli.py\\n@@ -5,12 +5,18 @@ from .utils import slugify, truncate\\n \\n def build_parser() -> argparse.ArgumentParser:\\n parser = argparse.ArgumentParser(prog=\\"example\\")\\n- parser.add_argument(\\"--name\\", default=\\"world\\")\\n+ parser.add_argument(\\"--name\\", default=\\"world\\", help=\\"who to greet\\")\\n+ parser.add_argument(\\n+ \\"--formal\\",\\n+ action=\\"store_true\\",\\n+ help=\\"use a formal farewell tone\\",\\n+ )\\n return parser\\n \\n \\n def main(argv: list[str] | None = None) -> int:\\n parser = build_parser()\\n args = parser.parse_args(argv)\\n- print(f\\"Hello, {args.name}!\\")\\n+ greeter = Greeter(args.name)\\n+ print(greeter.greet())\\n+ print(greeter.farewell(formal=args.formal))\\n return 0\\ndiff --git a/example/config.py b/example/config.py\\nindex ccccdddd..eeeeffff 100644\\n--- a/example/config.py\\n+++ b/example/config.py\\n@@ -1,15 +1,22 @@\\n from dataclasses import dataclass, field\\n+from pathlib import Path\\n \\n \\n @dataclass\\n class AppConfig:\\n name: str = \\"example\\"\\n verbose: bool = False\\n- timeout: int = 30\\n+ timeout_seconds: int = 30\\n+ cache_dir: Path | None = None\\n features: list[str] = field(default_factory=list)\\n \\n def is_feature_enabled(self, feature: str) -> bool:\\n return feature in self.features\\n+\\n+ def with_feature(self, feature: str) -> \\"AppConfig\\":\\n+ if feature in self.features:\\n+ return self\\n+ return AppConfig(**{**self.__dict__, \\"features\\": [*self.features, feature]})\\n diff --git a/tests/test_example.py b/tests/test_example.py\\nindex aabbccdd..eeff0011 100644\\n--- a/tests/test_example.py\\n+++ b/tests/test_example.py\\n@@ -1,10 +1,22 @@\\n from example import Greeter\\n+from example.utils import slugify, chunk, normalize_whitespace\\n+from example.config import AppConfig\\n \\n \\n def test_greeter_greet() -> None:\\n g = Greeter(\\"Ada\\")\\n assert g.greet() == \\"Hello, Ada!\\"\\n+ assert g.greetings == [\\"Hello, Ada!\\"]\\n \\n \\n def test_greeter_farewell() -> None:\\n g = Greeter(\\"Ada\\")\\n assert g.farewell() == \\"Goodbye, Ada!\\"\\n+ assert g.farewell(formal=True) == \\"Farewell, Ada!\\"\\n+\\n+\\n+def test_slugify_collapses_dashes() -> None:\\n+ assert slugify(\\"hello world!!!\\") == \\"hello-world\\"\\n+ assert slugify(\\"--abc--def--\\") == \\"abc-def\\"\\n+\\n+\\n+def test_config_with_feature_is_idempotent() -> None:\\n+ cfg = AppConfig().with_feature(\\"beta\\").with_feature(\\"beta\\")\\n+ assert cfg.features == [\\"beta ... [truncated]"}],"toolCalls":[],"toolCallId":"Shell:0"}} {"type":"turn.ended","agentId":"main","turnId":1,"reason":"completed","time":} {"type":"token_counting.measured","agentId":"main","length":4,"tokens":11286,"time":} @@ -209,7 +209,7 @@ exports[`migration snapshot: recent-protocol-1.10 > migration succeeds and match "title": "hi", "titleKind": "custom", "isCustomTitle": true, - "lastPrompt": "探索这个项目", + "lastPrompt": "Explore this project", "lastTurnReason": "completed", "agents": { "main": { @@ -236,9 +236,9 @@ exports[`migration snapshot: recent-protocol-1.10 > migration succeeds and match {"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"hi"}],"toolCalls":[]}} {"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"The user said \\"hi\\". This is a simple greeting. I should respond in a friendly, helpful manner. Since there's no specific task or question, I'll just greet them back and ask how I can help."},{"type":"text","text":"Hello! How can I help you today?"}],"toolCalls":[]}} {"type":"turn.ended","agentId":"main","turnId":0,"reason":"completed","time":} -{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"探索这个项目"}],"origin":{"kind":"user"},"time":} -{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"探索这个项目"}],"toolCalls":[]}} -{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"用户要求探索这个项目。让我先了解一下项目的整体结构,然后提供一些关键信息。\\n\\n从目录结构来看,这是一个示例 Python 项目。让我先查看一些关键文件来更好地理解项目。"}],"toolCalls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\\"command\\": \\"find . -maxdepth 2 -type f -name \\\\\\"*.md\\\\\\" | grep -E \\\\\\"(README|CHANGELOG|CONTRIBUTING|AGENTS)\\\\\\" | sort\\"}"}},{"type":"function","id":"Shell:1","function":{"name":"Shell","arguments":"{\\"command\\": \\"ls -la pyproject.toml Makefile 2>/dev/null || echo \\\\\\"No pyproject.toml or Makefile found\\\\\\"\\"}"}},{"type":"function","id":"Shell:2","function":{"name":"Shell","arguments":"{\\"command\\": \\"git log --oneline -10 2>/dev/null || echo \\\\\\"Not a git repo or no commits\\\\\\"\\"}"}}]}} +{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"Explore this project"}],"origin":{"kind":"user"},"time":} +{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"Explore this project"}],"toolCalls":[]}} +{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"The user asked me to explore this project. Let me first get an overview of the project structure, then provide the key facts.\\n\\nJudging by the directory layout, this is an example Python project. Let me look at a few key files to understand it better."}],"toolCalls":[{"type":"function","id":"Shell:0","function":{"name":"Shell","arguments":"{\\"command\\": \\"find . -maxdepth 2 -type f -name \\\\\\"*.md\\\\\\" | grep -E \\\\\\"(README|CHANGELOG|CONTRIBUTING|AGENTS)\\\\\\" | sort\\"}"}},{"type":"function","id":"Shell:1","function":{"name":"Shell","arguments":"{\\"command\\": \\"ls -la pyproject.toml Makefile 2>/dev/null || echo \\\\\\"No pyproject.toml or Makefile found\\\\\\"\\"}"}},{"type":"function","id":"Shell:2","function":{"name":"Shell","arguments":"{\\"command\\": \\"git log --oneline -10 2>/dev/null || echo \\\\\\"Not a git repo or no commits\\\\\\"\\"}"}}]}} {"type":"context.append_message","message":{"role":"tool","toolCallId":"Shell:1","content":[{"type":"text","text":"[tool result unavailable — session imported from pythinker-cli]"}],"toolCalls":[]}} {"type":"context.append_message","message":{"role":"tool","toolCallId":"Shell:2","content":[{"type":"text","text":"[tool result unavailable — session imported from pythinker-cli]"}],"toolCalls":[]}} {"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"Command executed successfully."},{"type":"text","text":"./.pytest_cache/README.md\\n./AGENTS.md\\n./CHANGELOG.md\\n./CONTRIBUTING.md\\n./docs/AGENTS.md\\n./README.md\\n./tests_e2e/AGENTS.md\\n"}],"toolCalls":[],"toolCallId":"Shell:0"}} @@ -344,10 +344,10 @@ exports[`migration snapshot: with-subagent-collapsed > migration succeeds and ma "createdAt": "", "updatedAt": "".7732, "archived": false, - "title": "你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上", + "title": "Launch three sub agents; each one must run a few simple commands that require approval — run at least two", "titleKind": "custom", "isCustomTitle": true, - "lastPrompt": "你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上", + "lastPrompt": "Launch three sub agents; each one must run a few simple commands that require approval — run at least two", "lastTurnReason": "completed", "agents": { "main": { @@ -370,12 +370,12 @@ exports[`migration snapshot: with-subagent-collapsed > migration succeeds and ma } }", "wire": "{"type":"metadata","protocol_version":"1.0","created_at":} -{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上"}],"origin":{"kind":"user"},"time":} -{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"你启动三个 sub agent,每个 sub,都要运行一些需要被批准的简单指令,运行两个以上"}],"toolCalls":[]}} -{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"用户要求我启动三个 sub agent,每个 sub agent 都需要运行一些需要被批准的简单指令,并且要求运行两个以上。\\n\\n我需要使用 Agent 工具来创建三个 sub agent。每个 sub agent 都应该执行一些需要批准的操作,比如文件写入或 shell 命令。\\n\\n让我创建三个 sub agent,每个都执行不同的需要批准的任务:\\n1. 第一个 sub agent:创建一个测试文件\\n2. 第二个 sub agent:执行一个简单的 shell 命令\\n3. 第三个 sub agent:创建另一个测试文件\\n\\n我需要并行启动这三个 sub agent。"},{"type":"text","text":"我来启动三个 sub agent,每个都会执行需要批准的简单指令。"}],"toolCalls":[{"type":"function","id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 1: 创建文件\\", \\"prompt\\": \\"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\\\n\\\\n1. 使用 WriteFile 工具在当前目录创建一个名为 \`subagent1_test.txt\` 的文件,内容为 \\\\\\"Hello from Sub Agent 1!\\\\\\"\\\\n\\\\n这个操作需要用户批准,请等待批准并完成操作。\\\\n\\\\n完成后,报告操作结果。\\"}"}},{"type":"function","id":"tool_OtoGSYsrDQpEaav0hLYbXDhd","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 2: Shell 命令\\", \\"prompt\\": \\"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\\\n\\\\n1. 使用 Shell 工具执行命令 \`echo \\\\\\"Hello from Sub Agent 2!\\\\\\" > subagent2_test.txt\`\\\\n\\\\n这个操作需要用户批准,请等待批准并完成操作。\\\\n\\\\n完成后,报告操作结果。\\"}"}},{"type":"function","id":"tool_zi4b3Pfbiia2kxPMUvjL4cVN","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 3: 创建文件\\", \\"prompt\\": \\"你是一个测试用的 sub agent。请执行以下需要批准的操作:\\\\n\\\\n1. 使用 WriteFile 工具在当前目录创建一个名为 \`subagent3_test.txt\` 的文件,内容为 \\\\\\"Hello from Sub Agent 3!\\\\\\"\\\\n\\\\n这个操作需要用户批准,请等待批准并完成操作。\\\\n\\\\n完成后,报告操作结果。\\"}"}}]}} -{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: ac1f74988\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n## 详细操作报告\\n\\n### 任务背景\\n作为测试用的 sub agent,我被要求执行一个需要用户批准的文件写入操作。这是一个测试场景,旨在验证带批准流程的工具调用机制。\\n\\n### 执行过程\\n\\n#### 第一次尝试(被拒绝)\\n- **操作**: 使用 \`WriteFile\` 工具创建文件 \`subagent1_test.txt\`\\n- **预期内容**: \`\\"Hello from Sub Agent 1!\\"\`\\n- **结果**: 用户拒绝了此操作\\n- **用户反馈**: 要求将文件名改为 \`kk\`\\n\\n#### 第二次尝试(成功)\\n- **操作**: 使用 \`WriteFile\` 工具创建文件 \`kk\`\\n- **内容**: \`\\"Hello from Sub Agent 1!\\"\`\\n- **结果**: ✅ 成功创建/覆盖文件\\n- **文件大小**: 23 字节\\n\\n### 技术细节\\n\\n1. **工具使用**: \`W... [truncated]"}],"toolCalls":[],"toolCallId":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8"}} -{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: aa373b195\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n## 详细操作结果报告\\n\\n### 1. 任务概述\\n\\n作为测试用 sub agent,我成功执行了需要用户批准的 Shell 命令操作,创建了测试文件并验证了其内容完整性。\\n\\n### 2. 执行详情\\n\\n#### 2.1 命令信息\\n- **命令类型**: Shell 命令执行\\n- **具体命令**: \`echo \\"Hello from Sub Agent 2!\\" > subagent2_test.txt\`\\n- **工作目录**: \`/Users/example/proj/example-project\`\\n- **执行时间**: 2026-03-23T21:42:43+08:00(系统参考时间)\\n\\n#### 2.2 技术实现\\n该命令使用了以下 Shell 特性:\\n- \`echo\` 命令:输出字符串到标准输出\\n- 重定向操作符 \`>\`:将标准输出重定向到文件\\n- 如果文件不存在则... [truncated]"}],"toolCalls":[],"toolCallId":"tool_OtoGSYsrDQpEaav0hLYbXDhd"}} -{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: aadd98577\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n**详细操作报告 - Sub Agent 3**\\n\\n---\\n\\n## 1. 任务概述\\n\\n本次任务要求以测试用的 sub agent 身份执行一个需要用户批准的操作:在指定目录创建一个包含特定内容的文本文件。\\n\\n---\\n\\n## 2. 操作执行详情\\n\\n### 2.1 第一次尝试\\n\\n| 属性 | 详情 |\\n|------|------|\\n| **目标文件路径** | \`subagent3_test.txt\` |\\n| **请求内容** | \`Hello from Sub Agent 3!\` |\\n| **操作类型** | WriteFile(覆盖模式,默认)|\\n| **执行状态** | 被拒绝 |\\n| **用户反馈** | \\"用另一个文件名再试一次\\" |\\n\\n**技术分析**:系统在第一次请求时触发了用户审批流程。用户拒绝了原始文件名 \`subagent3_test.txt\`,并明确要求使用另一个文件名再试。这表... [truncated]"}],"toolCalls":[],"toolCallId":"tool_zi4b3Pfbiia2kxPMUvjL4cVN"}} +{"type":"turn.prompt","agentId":"main","input":[{"type":"text","text":"Launch three sub agents; each one must run a few simple commands that require approval — run at least two"}],"origin":{"kind":"user"},"time":} +{"type":"context.append_message","message":{"role":"user","content":[{"type":"text","text":"Launch three sub agents; each one must run a few simple commands that require approval — run at least two"}],"toolCalls":[]}} +{"type":"context.append_message","message":{"role":"assistant","content":[{"type":"think","think":"The user asked me to launch three sub agents, each of which must run a few simple commands that require approval — at least two of them.\\n\\nI need the Agent tool to create three sub agents. Each sub agent should perform an action that requires approval, such as a file write or a shell command.\\n\\nLet me create three sub agents, each performing a different task that requires approval:\\n1. Sub agent 1: create a test file\\n2. Sub agent 2: run a simple shell command\\n3. Sub agent 3: create another test file\\n\\nI need to launch these three sub agents in parallel."},{"type":"text","text":"I am launching three sub agents, each running a simple command that requires approval."}],"toolCalls":[{"type":"function","id":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 1: Create file\\", \\"prompt\\": \\"You are a test sub agent. Perform the following actions that require approval:\\\\n\\\\n1. Use the WriteFile tool to create a file named \`subagent1_test.txt\` in the current directory with the content \\\\\\"Hello from Sub Agent 1!\\\\\\"\\\\n\\\\nThis action requires user approval; wait for approval and then complete the action.\\\\n\\\\nWhen done, report the result of the operation.\\"}"}},{"type":"function","id":"tool_OtoGSYsrDQpEaav0hLYbXDhd","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 2: Shell command\\", \\"prompt\\": \\"You are a test sub agent. Perform the following actions that require approval:\\\\n\\\\n1. Use the Shell tool to run the command \`echo \\\\\\"Hello from Sub Agent 2!\\\\\\" > subagent2_test.txt\`\\\\n\\\\nThis action requires user approval; wait for approval and then complete the action.\\\\n\\\\nWhen done, report the result of the operation.\\"}"}},{"type":"function","id":"tool_zi4b3Pfbiia2kxPMUvjL4cVN","function":{"name":"Agent","arguments":"{\\"description\\": \\"Sub agent 3: Create file\\", \\"prompt\\": \\"You are a test sub agent. Perform the following actions that require approval:\\\\n\\\\n1. Use the WriteFile tool to create a file named \`subagent3_test.txt\` in the current directory with the content \\\\\\"Hello from Sub Agent 3!\\\\\\"\\\\n\\\\nThis action requires user approval; wait for approval and then complete the action.\\\\n\\\\nWhen done, report the result of the operation.\\"}"}}]}} +{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: ac1f74988\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n## Detailed operation report\\n\\n### Task background\\nAs a test sub agent, I was asked to perform a file write that requires user approval. This is a test scenario meant to exercise the tool-call approval flow.\\n\\n### Execution\\n\\n#### First attempt (rejected)\\n- **Action**: Using \`WriteFile\` tool to create the file \`subagent1_test.txt\`\\n- **Expected content**: \`\\"Hello from Sub Agent 1!\\"\`\\n- **Result**: The user rejected this action\\n- **User feedback**: Asked to rename the file to \`kk\`\\n\\n#### Second attempt (succeeded)\\n- **Action**: Using \`WriteFile\` tool to create the file \`kk\`\\n- **Content**: \`\\"Hello from Sub Agent 1!\\"\`\\n- **Result**: ✅ File created/overwritten successfully\\n- **File size**: 23 bytes\\n\\n### Technical details\\n\\n1. **Tool usage**: \`W... [truncated]"}],"toolCalls":[],"toolCallId":"tool_Ct3NdbCjJ2Xbl7YzithE2Dy8"}} +{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: aa373b195\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n## Detailed operation result report\\n\\n### 1. Task overview\\n\\nAs a test sub agent, I successfully ran the shell command that required user approval, created the test file, and verified its content.\\n\\n### 2. Execution details\\n\\n#### 2.1 Command information\\n- **Command type**: Shell Command execution\\n- **Exact command**: \`echo \\"Hello from Sub Agent 2!\\" > subagent2_test.txt\`\\n- **Working directory**: \`/Users/example/proj/example-project\`\\n- **Executed at**: 2026-03-23T21:42:43+08:00(system reference time)\\n\\n#### 2.2 Technical implementation\\nThe command used the following shell features:\\n- \`echo\` command: writes a string to standard output\\n- redirection operator \`>\`: redirects standard output to a file\\n- creates the file if it does not exist... [truncated]"}],"toolCalls":[],"toolCallId":"tool_OtoGSYsrDQpEaav0hLYbXDhd"}} +{"type":"context.append_message","message":{"role":"tool","content":[{"type":"text","text":"agent_id: aadd98577\\nresumed: false\\nactual_subagent_type: coder\\nstatus: completed\\n\\n[summary]\\n**Detailed operation report - Sub Agent 3**\\n\\n---\\n\\n## 1. Task overview\\n\\nThis task asked me, as a test sub agent, to perform one action that requires user approval: create a text file with specific content in a given directory.\\n\\n---\\n\\n## 2. Operation details\\n\\n### 2.1 First attempt\\n\\n| Property | Detail |\\n|------|------|\\n| **Target file path** | \`subagent3_test.txt\` |\\n| **Requested content** | \`Hello from Sub Agent 3!\` |\\n| **Operation type** | WriteFile(overwrite mode, default)|\\n| **Status** | Rejected|\\n| **User feedback** | \\"Try again with a different file name\\" |\\n\\n**Technical analysis**: The system triggered the user approval flow on the first request. The user rejected the original file name \`subagent3_test.txt\`, and explicitly asked to retry with a different file name. This... [truncated]"}],"toolCalls":[],"toolCallId":"tool_zi4b3Pfbiia2kxPMUvjL4cVN"}} {"type":"turn.ended","agentId":"main","turnId":0,"reason":"completed","time":} {"type":"token_counting.measured","agentId":"main","length":5,"tokens":12507,"time":} ", diff --git a/packages/pyaos/test/local.test.ts b/packages/pyaos/test/local.test.ts index 513069303..09cdba268 100644 --- a/packages/pyaos/test/local.test.ts +++ b/packages/pyaos/test/local.test.ts @@ -341,7 +341,7 @@ describe('LocalPyaos', () => { }); describe('readText errors parameter (Python compat)', () => { - // Valid UTF-8 "中", invalid 0xff, valid UTF-8 "文". + // Valid UTF-8 "\u4E2D", invalid 0xff, valid UTF-8 "\u6587". const invalidBytes = Buffer.concat([ Buffer.from([0xe4, 0xb8, 0xad]), Buffer.from([0xff]), @@ -362,8 +362,8 @@ describe('LocalPyaos', () => { const content = await pyaos.readText(filePath, { errors: 'replace' }); expect(content).toContain('\uFFFD'); - expect(content).toContain('中'); - expect(content).toContain('文'); + expect(content).toContain('\u4E2D'); + expect(content).toContain('\u6587'); }); it('drops invalid bytes with errors="ignore"', async () => { @@ -371,7 +371,7 @@ describe('LocalPyaos', () => { await pyaos.writeBytes(filePath, invalidBytes); const content = await pyaos.readText(filePath, { errors: 'ignore' }); - expect(content).toBe('中文'); + expect(content).toBe('\u4E2D\u6587'); expect(content).not.toContain('\uFFFD'); }); diff --git a/scripts/oxlint-coverage.mjs b/scripts/oxlint-coverage.mjs new file mode 100644 index 000000000..28a1191b6 --- /dev/null +++ b/scripts/oxlint-coverage.mjs @@ -0,0 +1,48 @@ +import { spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// Wraps oxlint and fails when the file walk is neutralized. A parent +// .gitignore above a worktree (or any ignore rule covering the whole tree) +// makes oxlint exit 0 with zero files walked — a green result that checked +// nothing. The lint run itself keeps oxlint's native output (CI annotation +// format included); afterwards a small probe run with a forced plain format +// walks a known directory and must report a real file count. +const MIN_FILES = Number(process.env.LINT_MIN_FILES ?? '10'); +if (!Number.isFinite(MIN_FILES) || MIN_FILES <= 0) { + process.stderr.write(`oxlint coverage check failed: LINT_MIN_FILES must be a positive number, got ${String(process.env.LINT_MIN_FILES)}\n`); + process.exit(2); +} +const PROBE_DIR = 'scripts'; + +const repoBin = join(fileURLToPath(new URL('..', import.meta.url)), 'node_modules', '.bin', 'oxlint'); +const bin = existsSync(repoBin) ? repoBin : 'oxlint'; + +const fail = (message) => { + process.stderr.write(`oxlint coverage check failed: ${message}\n`); + process.exit(2); +}; + +const res = spawnSync(bin, ['--type-aware', ...process.argv.slice(2)], { stdio: 'inherit' }); +if (res.error !== undefined) fail(`could not run ${bin}: ${res.error.message}`); + +const probe = spawnSync(bin, ['--type-aware', '--format=default', PROBE_DIR], { + stdio: ['ignore', 'pipe', 'pipe'], + encoding: 'utf8', + maxBuffer: 512 * 1024 * 1024, +}); +if (probe.error !== undefined) fail(`coverage probe could not run: ${probe.error.message}`); + +const match = `${probe.stdout ?? ''}${probe.stderr ?? ''}`.match(/on (\d+) files? /); +const files = match === null ? Number.NaN : Number(match[1]); +if (!Number.isFinite(files) || files < MIN_FILES) { + const scanned = Number.isFinite(files) ? `${files}` : 'an unknown number of'; + fail( + `walked only ${scanned} files under ${PROBE_DIR}, minimum is ${MIN_FILES}. ` + + "The linter's file walk was likely neutralized (e.g. a parent .gitignore); " + + 'this run is not a real lint.', + ); +} + +process.exit(res.status ?? 1);