Skip to content

feat(code-sdk): add the @minimax-ai/code-sdk TypeScript SDK - #553

Merged
hetaoBackend merged 10 commits into
mainfrom
feat/code-sdk
Oct 29, 2026
Merged

hetaoBackend merged 10 commits into
mainfrom
feat/code-sdk

Conversation

@hetaoBackend

@hetaoBackend hetaoBackend commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Thanks for your interest in contributing. For now, code and documentation pull requests are accepted only from repository collaborators. If you are not a collaborator, please open an issue so we can discuss your idea.

Change

Adds @minimax-ai/code-sdk (packages/code-sdk), a zero-dependency TypeScript SDK for driving the local MiniMax Code agent from Node.js. It is similar to the Codex TypeScript SDK. The SDK wraps the existing mcode exec --output-format stream-json contract and does not add a new execution protocol.

  • Execution model. Each thread.run() / thread.runStreamed() starts one mcode exec process, which runs one Turn. The prompt is written to stdin as {"prompt": ...}. A Thread only holds the Session id and continues it with --session.

  • API.

    • new MCode({ bin, dataDir, env, configPath }), startThread, resumeThread, run, runStreamed and thread.id.
    • run() resolves only on succeeded. runStreamed() delivers turn.failed as an ordinary event.
    • A concurrent Turn on the same Thread throws ThreadBusyError.
    • dataDir maps to MINIMAX_DATA_DIR.
  • CLI resolution. In order:

    1. the explicit bin option;
    2. an installed @minimax-ai/code;
    3. the nearest node_modules/.bin/mcode above the working directory, so any project-local CLI distribution works;
    4. mcode on PATH.

    On Windows, the official installer's versioned mcode.cmd and npm/pnpm .cmd / .ps1 shims are followed to their JS entry; the SDK never starts the CLI through a shell. The SDK runs a cached mcode --version check (minimum 0.7.0) and accepts only events with schemaVersion === 1.

  • Results and errors.

    • Items are merged by id. Identical repeated snapshots are dropped, and unfinished items are marked incomplete.
    • Errors are typed and mapped from the exec status and the exit codes 0/2/3/4/6/7/70/130/141.
    • Failures carry the observed sessionId.
  • Process handling.

    • AbortSignal sends SIGINT, then SIGTERM after 10s, then SIGKILL after another 5s.
    • timeoutMs maps to --timeout <n>ms. The SDK force-stops the CLI at timeoutMs + 30s, with a validated upper bound.
    • After exit, the SDK drains stdout/stderr for at most 2s, so grandchildren holding the pipes cannot stall a Turn.
    • Temporary prompt files are removed before a run settles.
    • Running CLI processes are cleaned up on a normal host exit.
  • Publication metadata.

    • Version 0.1.0, MIT, not private.
    • publishConfig pins https://registry.npmjs.org/ with public access. All release commands also pin --@minimax-ai:registry, because a scoped registry setting overrides publishConfig.
    • Published files: dist, src, README.md, CHANGELOG.md (npm adds package.json and LICENSE).
    • engines.node equals the CLI's >=22.19 <23 || >=24.2 <27, and a test enforces this.
    • @minimax-ai/code >=0.7.0 is an optional peer dependency.
  • Manual release support. packages/code-sdk/RELEASING.md documents the steps. pnpm --filter @minimax-ai/code-sdk run release:check:

    • checks for a clean, up-to-date main;
    • checks that the version matches the first CHANGELOG section and is not yet on npm;
    • builds, type-checks and tests;
    • runs npm pack, applies a tarball entry allowlist and scans every packed file for internal hosts, credentials, local user paths, other repository package paths and internal package names;
    • runs an import/require smoke test from the tarball;
    • prints, but does not run, the npm publish and read-back commands.

    --allow-non-main rehearses on a feature branch without printing a publish command.

  • Repository integration.

    • The package is added to pnpm-workspace.yaml and to release/extraction.json packageRoots.
    • pnpm gen:tsconfig was run.
    • The test files form a new code-sdk group in test/vitest-suites.json.
    • scripts/verify.mjs gains typecheck:code-sdk and test:code-sdk. Both are Linux/macOS only, outside the Windows contract.
    • The check:source internal-text and credential patterns move unchanged to scripts/lib/public-text-policy.mjs, so release:check reuses them.
    • The lockfile only gains the new importer.
    • README.md / README_ZH.md gain one entry-point row each.

The tests replay recorded, sanitized stream-json output through a fake mcode. test/cli-contract.test.ts imports the CLI's real JSONL projector (packages/tui/src/headless/events.ts) and exit-code policy, so changes on either side surface in the SDK suite.

  • PR labels: enhancement, cli.

Validation

  • Checks run and results (local runs on revision 701cb82, macOS arm64, pnpm 9.12.0; the follow-up f441c7d was re-checked as noted below):
    • pnpm verify (full profile) on the committed, clean tree with official Node v24.22.1: passed, 17 gates, including the new typecheck:code-sdk and test:code-sdk. Skipped: test:windows (not applicable on macOS) and test:release-package (requires an npm release archive).
    • The same run with Homebrew Node v26.4.0 failed one existing TUI test unrelated to this change: update-runtime.test.ts, "heals receipts pinned to a Homebrew Cellar keg via the stable opt path". The test copies the current node binary into a fake Cellar, and Homebrew's Node dynamically links libnode.147.dylib, so the copy aborts (dyld: Library not loaded). It fails the same way when run alone, and this PR does not touch packages/tui.
    • pnpm test:code-sdk: 7 files, 60 tests passed. pnpm typecheck:code-sdk and pnpm --filter @minimax-ai/code-sdk run build passed.
    • pnpm check:source, pnpm check:tsconfig and pnpm test:release-tools (54 passed, 1 platform skip) passed.
    • ESLint with the repository rules on packages/code-sdk: 0 errors. The SDK is not part of the lint:tui gate.
    • pnpm --filter @minimax-ai/code-sdk run release:check -- --allow-non-main:
      • the tarball has 64 files, all inside the allowlist, with no private-text findings;
      • the import/require smoke test from the tarball passed;
      • @minimax-ai/[email protected] is confirmed absent from npmjs;
      • nothing was published.
    • Negative checks: a dirty tree makes release:check fail, and the scan rules flag sample other-package paths, /Users/… and C:\Users\… paths, and internal package names.
  • f441c7d addresses the CodeQL alerts raised on the first push (1 high: polynomial regex in the Windows argument quoting; 4 medium: shell command construction). It removes the cmd.exe fallback and its quoting entirely. After it: pnpm test:code-sdk (60 passed), pnpm typecheck:code-sdk, pnpm check:source, ESLint (0 errors). GitHub Checks on f441c7d: all passed, including CodeQL, verify on ubuntu/macos/windows with Node 24, install on Node 22.19.0/24.2.0/25/26 for ubuntu and macOS, verification and source-history-artifact.
  • Local end-to-end validation with live models (packed tarball of 46f8dbe, installed into fresh projects under a temporary directory and used through import '@minimax-ai/code-sdk'; macOS arm64, Node v26.4.0, npm 11.17.0; model minimax/MiniMax-M3.1-Flash; CLI: a local MCode CLI 0.7.0 preview build on PATH, and @minimax-ai/[email protected] from npm installed into the project). All of the following passed:
    • run() with tool calls, files and appendSystemPrompt: the agent read a file, listed the workspace, wrote out.txt (HELLO SDK) and answered from the attached log; items, usage and model were populated; thread.id was null before the first run and equal to the result's sessionId after it.
    • resumeThread(): same sessionId, context kept (the agent recalled the earlier file content without tools).
    • runStreamed(): the stream starts with exec.started and ends with exec.completed, sequence is strictly increasing, and the result status is succeeded.
    • A second run() on a busy Thread throws ThreadBusyError.
    • Cancelling run() during a long shell command throws MCodeAbortError (name === 'AbortError', exit 130, result.status === 'cancelled', sessionId set). Cancelling runStreamed() ends the stream in about 2.4 s with turn.failed (cancelled) and exec.completed (cancelled), as documented. No sleep or mcode exec processes were left behind in either case.
    • timeoutMs: 15000 around a 45 s shell command: MCodeRunError with status === 'timeout' and sessionId set, no leftover processes.
    • Error mapping without model calls: a missing bin gives MCodeNotFoundError; an invalid effort gives MCodeInvocationError (exit 2, with the CLI's list of levels); an empty dataDir gives MCodeRunError with category === 'config' (exit 3, sign-in prompt).
    • A wrapper bin that logs its argv confirmed the SDK passes --append-system-prompt-file=<file> with mode 600 and the exact text, and removes the temporary directory after the run.
    • Resolution of a project-local @minimax-ai/[email protected] with PATH restricted to system directories: a full Turn succeeded (out.txt written). A control project without the CLI fails with MCodeNotFoundError under the same PATH.
    • Found during this run and fixed in 2179002: npm 11 rejects --allow-scripts for project-scoped installs (EALLOWSCRIPTS); the README now documents the allowScripts entries required in the project's package.json.
    • Observed, not an SDK defect: the hosted models did not follow an appended system prompt rule (ending the answer with a marker). Passing the same text with the CLI's own --append-system-prompt behaves the same, and the SDK passes it through correctly (see the argv log above).
  • Performance: basic. The change adds a separate package and does not touch the CLI runtime paths covered by perf:full.
  • NOT RUN, platform limitations and live-service boundaries:
    • Windows: not run. Cancellation on Windows is a hard kill (documented). Windows shim resolution is covered only by pure-function tests. test:code-sdk is excluded from the Windows contract.
    • Live models: the offline tests and fixtures are not live-model acceptance; the live-model runs above cover one model on macOS only.
    • Not published to npm. The first publication follows RELEASING.md manually. @minimax-ai/[email protected] from npm was exercised for one Turn (above); 0.7.0 is accepted by the argument check but was not run with a model.
    • The host-exit cleanup path has no automated test.

Publication and contribution checks

  • I have permission to contribute these changes under the existing licenses applicable to the changed files/packages; imported material and its provenance are identified and existing notices are preserved.
  • No credentials, account data, real user content, internal source history or private review material is included.
  • Added/removed source files were reviewed before regenerating release/public-source.json; new tests are declared in test/vitest-suites.json where applicable.
  • Shared English/Chinese documentation and capability/verification records are updated where applicable. Mock/offline results are not described as live-service acceptance.

Maintainer handoff

Publication scope or license changes (if any):

  • New MIT package packages/code-sdk (37 files), intended for npm as @minimax-ai/code-sdk.
  • The recorded CLI fixtures were sanitized: account and organization identifiers, provider ids, local paths and user names were removed.
  • scripts/lib/public-text-policy.mjs holds the existing check:source patterns.

Shared-source port: not applicable. This is a public-only package: this repository is its single source, and source synchronization does not produce or update it (stated in packages/code-sdk/CONTRIBUTING.md).

Review fixes

Each item was reproduced first; the evidence is "before → after".

  • B1 — scoped registry overrode the publish target. A user npmrc with @minimax-ai:registry=<mirror> beat both publishConfig.registry and --registry. The npm lookup in release:check and the RELEASING read-back would then query the same wrong registry.
    • Fix: release:check first reads the effective @minimax-ai:registry and default registry, and fails with a fix-it hint if the scope points anywhere but npmjs. Every npm command it runs or prints, and every command in RELEASING.md (publish, view, dist-tags, install smoke, deprecate), passes both --registry=https://registry.npmjs.org/ and --@minimax-ai:registry=https://registry.npmjs.org/. RELEASING now says publishConfig only replaces the default registry.
    • Evidence: with an isolated userconfig, npm publish --dry-run --registry=npmjs went from Publishing to http://127.0.0.1:9/scoped/ to Publishing to https://registry.npmjs.org/ once the printed command was used. release:check now stops at "npm registry configuration" with @minimax-ai:registry is set to http://127.0.0.1:9/scoped/ ….
    • Regression test: test/release-check.test.ts runs the script with an isolated userconfig (no network access, no publication) and unit-tests the pinned flags.
  • N1 — official Windows installer layout. The installer's root mcode.cmd (SET /P MCODE_RELEASE=<"%~dp0current" + CALL "%~dp0releases\%MCODE_RELEASE%\.mcode-launcher.cmd", the same text packages/tui/src/update/versioned-prefix.ts writes) failed with MCodeNotFoundError.
    • Fix: the SDK follows current, validates the release key with the launcher's own rule, and parses .mcode-launcher.cmd to the JS entry. It still runs that entry with process.execPath, no shell. The pointer is re-read before every spawn, so a self-update between Turns is used.
    • Errors: no more mcode.exe; an installed but unfollowable launcher asks for bin instead of suggesting npm install.
    • Evidence: the review's simulated install went from MCodeNotFoundError … (or mcode.exe) … Install it with npm … to { kind: 'node', path: …\releases\0.7.1\…\cli.js, launcher: …\mcode.cmd }.
    • Tests: three new tests (platform: 'win32' on a generated layout), covering resolution via PATH and bin, re-resolution after current changes, and the error text. The README lists the supported Windows installations.
  • N2 — unnecessary check:source exemption. Removed.
    • Evidence: appending an internal URL to scripts/lib/public-text-policy.mjs went from "check passed" to scripts/lib/public-text-policy.mjs: internal source reference.
    • Regression test added in test/source-sync.test.mjs.
  • N3 — error table. The README now maps exec.completed results and exits without a result separately. Only exit 0 without a result is a protocol error; any other unknown exit code (for example 1) or a signal becomes an internal MCodeRunError. A test for exit 1 was added.
  • N5 — Windows shell in release:check. No tool is started through a shell any more. Node.js runs directly; on Windows, npm and pnpm run through their JS entries, and missing entries fail with a clear message. Unit tests cover the Windows invocation; it has not been run on a Windows machine.
  • N7. CONTRIBUTING.md states that this repository is the package's single source and that it is not produced by source synchronization.
  • Nits.
    • The minimum-version comment now records the public 0.7.0 verification.
    • CONTRIBUTING: both SDK gates run in the full and platform profiles.
    • README: describes require(esm) support instead of a CommonJS export condition.

Commits: d2506b3 (B1/N5), fe912d2 (N1), 616ac3c (N2), 46f8dbe (N3/N7/nits).

Checks on 46f8dbe:

  • pnpm test:code-sdk: 8 files, 69 tests.
  • pnpm typecheck:code-sdk, the package build, pnpm check:source and pnpm check:tsconfig: passed.
  • node --test test/source-sync.test.mjs: 55 passed, 1 platform skip.
  • ESLint on packages/code-sdk: 0 errors.
  • release:check -- --allow-non-main: passed; 64 files, no findings; ends with the rehearsal warning.
  • Full pnpm verify: passed, 17 gates, on official Node v24.22.1 (macOS arm64). Skipped: test:windows (not applicable) and test:release-package (needs an npm release archive).

Review (yujiachen-y) — 2b865af, 552c815

Addressed the second review pass on 2179002. Decision and fixes:

  • CLI resolution (maintainer decision). Chose option 1 (opt-in). resolveFromCwd defaults to false. Default order: explicit bin → @minimax-ai/code next to the SDK → mcode on PATH. With resolveFromCwd: true, cwd package.json / parent walk for node_modules/.bin/mcode runs before PATH. Untrusted-cwd risk documented in the README.
  • EMFILE / spawn error. Attach the 'error' listener immediately after spawn, before reading stdio, so spawn failures become typed SDK errors instead of crashing the host.
  • Ctrl+C / process group. CLI stays in the host process group (intentional). Cancel sends SIGTERM then SIGKILL after 60s; the SDK no longer sends SIGINT, so it does not collide with Ctrl+C consuming the CLI's one-shot handler. A second SIGINT (double Ctrl+C) can still kill the CLI before exec.completed — documented caveat.
  • Kill windows. Cancel: SIGTERM → SIGKILL after 60s. Timeout backstop: timeoutMs + 90s (60s shutdown + 30s startup slack). After exec.completed, lingering CLI is SIGKILLed after 5s.
  • MIN_CLI_VERSION. Set to 0.6.3; optional peer @minimax-ai/code >=0.6.3 so this repo's 0.6.5 CLI is accepted.
  • Contract test. Extended to parse argv and stdin via the CLI's applyExecCliContract / resolveTuiExecInvocation.
  • Local-path scan. Covers /Users/, /home/<name>/, /root/, /private/var/folders/, C:\Users\, and escaped C:\\Users\\.
  • Publish guard. prepublishOnly runs node scripts/release-tools.mjs, which always exits 1 from the package directory.
  • Temp prompt dirs. exit hook uses sync rmSync so mid-turn process.exit() still cleans $TMPDIR/mcode-sdk-*.
  • Release regression tests. Allowlist / path rules / rehearsal live in scripts/release-tools.mjs, covered by test/release-check.test.ts.
  • 552c815. Keeps published comments free of other package paths.

Local verification on Linux (Node 24.22.2, pnpm 9.12.0, mcode 0.6.5, model MiniMax-M3) at 552c815: full pnpm verify (16 gates; Windows/sandbox skipped on Linux), 81 code-sdk tests, and live-model e2e for run / resumeThread, AbortSignal cancel, timeoutMs, resolveFromCwd on/off, mid-turn process.exit temp cleanup, and npm publish --dry-run blocked by prepublishOnly.

Follow-ups (not in this PR)

  • N4: a Windows automated subset of the SDK tests.
  • N6: the CLI silently falling back to the default model for an unknown model id (separate CLI PR).
  • npm --provenance for SDK releases.

Add packages/code-sdk, a zero-dependency TypeScript SDK that drives the
local MiniMax Code agent from Node.js by wrapping
`mcode exec --output-format stream-json`: one CLI process per Turn, with a
Thread holding only the Session id and continuing it with --session.

- MCode / startThread / resumeThread / run / runStreamed, a ThreadBusy
  guard, prompts over stdin and system prompts through private temp files
- CLI resolution: explicit bin, an installed @minimax-ai/code, the nearest
  node_modules/.bin/mcode above the working directory, then PATH; a cached
  minimum-version check and schemaVersion 1 enforcement
- items merged by id, duplicate snapshots dropped, unfinished items marked
  incomplete; typed errors mapped from exec results and exit codes
- AbortSignal cancellation (SIGINT, SIGTERM, SIGKILL), a timeoutMs
  backstop, a bounded drain after exit and cleanup on normal host exit
- tests replay recorded, sanitized CLI output through a fake mcode; a
  contract test imports the CLI's JSONL projector and exit policy, and
  engines.node is pinned to the CLI's range
- npm publication metadata pinned to registry.npmjs.org, a manual
  RELEASING.md, and a release:check script that builds, tests, packs,
  scans the tarball and prints the publish command without running it

Repository integration: the package is registered in the workspace and
release/extraction.json, its tests form the code-sdk Vitest suite, and
verify gains typecheck:code-sdk and test:code-sdk (Linux and macOS). The
check:source text patterns move to scripts/lib/public-text-policy.mjs so
release:check reuses them.
A rehearsal on a branch other than an up-to-date main now ends with an
explicit warning instead of a publish command for a tarball that is not
release-ready.
@hetaoBackend hetaoBackend added enhancement New feature or request cli Standalone mcode: TUI, headless, ACP and source builds/tooling labels Oct 28, 2026
Comment thread packages/code-sdk/src/version.ts Fixed
Drop the Windows fallback that ran an unrecognized .cmd launcher through
cmd.exe with escaped arguments. The SDK now only runs a JS entry with
process.execPath or a native executable, and asks for an explicit bin
when a .cmd shim has no recognizable JS entry. This removes the shell
command construction and the backslash-heavy quoting regular expression
flagged by CodeQL.
publishConfig.registry and --registry only replace the default registry;
an @MiniMax-AI:registry setting in any npmrc still routed publish and the
read-back of this scoped package elsewhere, so a mistaken publish would
also read back as consistent.

release:check now reads the effective npm configuration and fails while
@MiniMax-AI:registry points anywhere but npmjs. Every npm command it runs
or prints, and every command in RELEASING.md, passes both --registry and
--@MiniMax-AI:registry for npmjs. Tools start without a shell; on Windows
npm and pnpm run through their JS entries so the smoke-test script and
temporary paths are not split by cmd.exe.
The installer and CLI self-update put a root mcode.cmd on PATH that reads
the current release pointer and calls releases\<release>\.mcode-launcher.cmd.
Resolve that layout to the release's JS entry and keep running it with
process.execPath, without a shell. The pointer is followed again before
every spawn, so a self-update between Turns is used.

When a launcher is installed but cannot be followed, ask for bin instead of
suggesting an npm install, and stop mentioning a nonexistent mcode.exe.
The policy patterns never match their own definitions, so the check:source
exemption for scripts/lib/public-text-policy.mjs only left a gap where an
internal address added to that file went unreported. Remove it and add a
regression test.
- README: map errors separately for exec results and for exits without a
  result (only exit 0 is a protocol error; unknown codes are internal run
  errors), describe require(esm) instead of a CommonJS export condition,
  and list the supported Windows installations.
- CONTRIBUTING: state that this repository is the package's only source,
  and that both SDK gates run in the full and platform profiles.
- Note that the public 0.7.0 CLI accepts the SDK's flags; test exit 1.
npm 11 rejects --allow-scripts for project-scoped installs (EALLOWSCRIPTS), so a project that installs @minimax-ai/code next to the SDK must approve the CLI's install scripts in package.json.

@yujiachen-y yujiachen-y left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted review (trial of a new review flow): needs maintainer decision

0 blocking · 1 needs a maintainer decision · 9 suggestions (P2, non-blocking), all posted as inline comments. Reviewed at 2179002, including the five commits pushed after f441c7d. Points the PR now declares (public-only single source, provenance as a follow-up) are not raised again.

Scope da22f11c..2179002d, new package packages/code-sdk plus repository wiring
Tier Large: touches release tooling, CI gates and child-process handling
Method Four parallel review passes (security and supply chain, CLI contract, process lifecycle, repository integration and release) plus a full read; the five later commits were reviewed as an increment. Every candidate was re-checked in a separate context that tried to refute it: 1 was refuted and 4 were downgraded.
Limits Static review. Five findings were reproduced with probes that load the SDK sources in memory or run the test fixtures. vitest, tsc and a live CLI were not run here, and Windows was not tested.

Needs a maintainer decision. .github/CODEOWNERS lists only the author, and docs/maintainers.md asks for a non-author approval, so this is for the reviewer the release coordinator assigns; the author does not need to answer it.

  • CLI resolution can execute an mcode from the host's current directory or a parent directory (src/cli-resolver.ts).

Suggestions (P2). Spawn failure such as EMFILE crashes the host · Ctrl+C in a terminal can kill the CLI before it finishes cancelling · kill windows are shorter than the CLI's shutdown budget · the minimum CLI version rejects the CLI built from this repository · the contract test does not cover argv or stdin · the release scan misses common local-path forms · first non-private workspace package without a publish guard · temporary system-prompt files left behind on process.exit() · no regression tests for the text scan and allowlist.

Claims from the PR description that do not fully hold
Other observations (no separate comment)
  • .agents/skills/testing-workflow/SKILL.md does not list the new gates, although CONTRIBUTING.md asks to update the relevant skill; pnpm verify still runs them.
  • checkCliVersion waits only for 'close' (packages/code-sdk/src/version.ts:94-106), so a wrapper that leaves a descendant holding the pipes would hang it. The official --version path starts no child processes.
  • release:check --offline on main still prints the publish command (packages/code-sdk/scripts/release-check.mjs:172-174).
  • Checked and not an issue: runtime startup redirects console to the log file, so migration log lines do not reach the stream-json stdout.

Comment thread packages/code-sdk/src/cli-resolver.ts Resolved
Comment thread packages/code-sdk/src/exec-process.ts Resolved
Comment thread packages/code-sdk/src/exec-process.ts Resolved
Comment thread packages/code-sdk/src/exec-process.ts Resolved
Comment thread packages/code-sdk/src/version.ts Resolved
Comment thread packages/code-sdk/package.json Resolved
Comment thread packages/code-sdk/src/exec-process.ts Resolved
cursoragent and others added 2 commits October 29, 2026 06:40
Search process.cwd() for the CLI only when resolveFromCwd is set.
Handle spawn failures without crashing the host, cancel with SIGTERM
and the CLI's 60s shutdown budget, and accept CLI 0.6.3 so this
repository's 0.6.5 build is supported. Release checks cover more
local paths, refuse a directory publish, and omit npm publish off main.

Co-authored-by: DanielWalnut <[email protected]>
The release text scan rejects packages/tui paths in the packed SDK. Describe
the CLI version floor and shutdown budget without naming those files.

Co-authored-by: DanielWalnut <[email protected]>
@hetaoBackend

Copy link
Copy Markdown
Collaborator Author

@yujiachen-y 你提的几点都改完并回复了(cwd 走了 opt-in),本地也跑过全量 pnpm verify 和真实模型的取消 / 超时 / resolveFromCwd 路径。方便的话再帮看一眼?

@yujiachen-y yujiachen-y left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted review, round 2: pass

Re-reviewed at 552c815, covering the increment since 2179002 (2b865af, 552c815). All 10 points from round 1 are addressed; each thread has a short verification note and is resolved. No new issues were found in the increment, and all 21 checks pass.

Extra checks beyond reading the diff:

  • npm 11.13: prepublishOnly runs for npm publish in the package directory and does not run for npm publish <tarball>, so the guard blocks only the unintended path.
  • The CLI keeps a once('SIGTERM') listener until runExec returns (packages/tui/src/cli/run-exec-command.ts:52, removed at :128), so the SDK's SIGTERM cancel is still handled as a cancel after a terminal Ctrl+C.
  • LOCAL_USER_PATH: all six target path forms match; nearby forms such as /homework/ do not.

Optional, non-blocking:

  • test/cli-contract.test.ts is still excluded from tsconfig.check.json.
  • The shared credential pattern does not match npm tokens, JWTs or AWS keys (tracked sources are covered by gitleaks).
  • Unchanged notes from round 1: .agents/skills/testing-workflow/SKILL.md does not list the new gates, and release:check --offline on main still prints the publish command.

Limits: static review plus the probes above; the SDK test suite was not run locally.

@yujiachen-y yujiachen-y left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving after verifying the round-2 fixes. Thanks for the quick turnaround.

@hetaoBackend
hetaoBackend merged commit 50448ab into main Oct 29, 2026
24 checks passed
@hetaoBackend
hetaoBackend deleted the feat/code-sdk branch October 29, 2026 14:33
POWERFULMOVES added a commit to POWERFULMOVES/PMOVES-minimax-code that referenced this pull request Oct 30, 2026
* fix: remove forced branching from bash guidance (#358)

Co-authored-by: minimax <[email protected]>

* feat: sync reviewed 0.5.5 runtime improvements (#367)

Preserve public privacy defaults and distribution boundaries while porting compaction, Bash timeout, memory output and update-proxy improvements. Record selective provenance without advancing the full source baseline.

Assisted-by: codex-cli reason:public-source-sync-0.5.5

* docs: publish feedback and private security contact channels (#360)

* docs: publish feedback and private security contact channels

* docs: correct MiniMax Agent X contact link

* fix(tui): distinguish Bash recaps and import models during onboarding (#369)

* fix: measure token speed over model generation time (#373)

* chore: bump version to 0.5.6 (#374)

* feat(config): add M3.1 Flash Preview to the fallback catalog (#375)

Include context window and effort controls in the first-run catalog while preserving existing managed snapshots.

Docs-Impact: Existing model selection workflow is unchanged.

Assisted-by: codex-cli reason:m31-flash-fallback-sync

* chore: bump version to 0.5.7 (#377)

* chore: bump version to 0.5.7

* test: drain stderr before nonzero Bash fixture exit

* fix(tui): display hook system messages without adding model context (#376)

* fix(tui): anchor restored sessions above inherited terminal history (#378)

* fix(tui): keep slow terminal output off the input loop (#379)

* chore: bump version to 0.5.8 (#380)

* feat: cascade explicit session stops to owned background work (#381)

Port the reviewed shared runtime behavior while retaining standalone composition. Keep conversation leave separate from explicit stop, suppress canceled task delivery, and track append activations through teardown.


Assisted-by: codex-cli reason:selective-runtime-port
Docs-Impact: document stop and session-leave behavior in docs/tui-capabilities.md

Co-authored-by: chenhao <[email protected]>

* fix: query structured Windows source volume properties (#383)

* feat(examples): add Pocket Pet demo and refresh README onboarding (#385)

* feat(examples): add reproducible Pocket Pet demo and refresh onboarding

* feat(examples): polish Pocket Pet interaction and demo

* fix(tui): dismiss stale terminal errors when a new turn starts (#388)

* fix(tui): preserve native scrollback during automatic updates (#389)

* fix(tui): preserve native scrollback during projection updates

* fix(tui): isolate transient overlays from native scrollback

* fix(tui): stabilize native history and diff overlay frames (#390)

* fix(tui): prevent idle transcript replay and diff overlay frames

* fix(tui): retain prompt identity and deferred main-buffer geometry

* test: pin the srt-macos probe PATH and scale the BYOK serial timeout budget (#397)

* test(sandbox): pin the probe PATH instead of inheriting the ambient one

The real sandbox-exec probes resolve their command through PATH, so
inheriting the caller's let any wrapper ahead of /bin decide what `rm`
means. On a machine that has run the agent, that is the recoverable-delete
shim MCode installs into agent shells: `rm` resolves to the shim, the shim
execs mavis-trash, the sandbox denies that trash move as a write outside
the policy under test, and a correct allow-probe exits 1. The suite then
reports a sandbox regression where the sandbox behaved correctly.

Pin all three probe environments to the system binaries the probes
actually use, matching the wrapper-argument test above them. This also
drops the unguarded `process.env.PATH` in wrapProfile, which had no
fallback and could hand a real sandbox spawn an undefined PATH.

Fixes #394.

* test(byok): scale the serial BYOK budget by platform

The first case is 30+ serial CLI spawns, each booting the whole runtime,
so its wall-clock cost tracks machine speed rather than the transport it
asserts. The flat 90s budget only held with roughly 1.5x headroom on an
idle machine, so a parallel build, a laptop under load, or a shared CI
runner turned a passing transport into a `testTimeoutFailure` that named a
product failure no assertion had actually observed.

Budget it the way smoke.test.mjs already budgets runtime startup: a base
allowance plus a Windows multiplier for slower process spawn. The test is
not slowed down by a larger ceiling; it only stops failing for reasons
unrelated to what it checks.

Fixes #395.

* feat: sync MiniMax Code 0.5.9 runtime and TUI changes (#398)

TUI
- Report active root-session background tasks as `background=N` in the
  `[V]` build-mode status line. Bash still owned by its foreground tool
  call is excluded, and the count holds at 1 after a turn settles until a
  fresh task list arrives. The documented minimum width is now 102 columns.

Runtime
- The MiniMax API-key route now shares the official model catalog with the
  managed route; the first-run catalog moves to `minimax-model-catalog.ts`
  with unchanged model definitions.
- Apply byte limits to large media and accumulated history for BYOK requests.
- Send the M3 thinking toggle as a thinking setting rather than a generic
  reasoning effort.
- Allow edit and rewind after an interrupt that left only a background
  reminder in canonical history.
- Read less data when listing session files and navigating long histories.
- Measure token output rate from the time events are observed.
- Add `worktreeRefreshBeforeCreate` (default on) to fetch the selected
  upstream before creating a worktree, and exclude nested roots from fork
  worktree fingerprints.
- Add Ghostty to the external editor catalog.
- Enable the Codex OAuth model settings entry by default.

Prompts
- Cron guidance now lives only in tool definitions; the legacy feature
  template is empty. Memory edits use the `memory` tool's `edit` operation.
- Clarify multimodal tool discovery in the mcode-tools reminder.

* chore: release MiniMax Code 0.5.9 (#399)

* Revert #390 and #389 to restore TUI scrollback baseline (#396)

* Revert "fix(tui): stabilize native history and diff overlay frames (#390)"

This reverts commit 1e136bfd57ede5452909a258cc06dae72d85d87d.

* Revert "fix(tui): preserve native scrollback during automatic updates (#389)"

This reverts commit 9b9c07bc6ba118cf89c8104f4a653f78231cc87d.

* fix(tui): sync 0.5.10 prompt and run recovery fixes (#410)

* chore: release MiniMax Code 0.5.10 (#411)

* fix: include hidden thinking in TPS timing (#413)

* fix: include hidden thinking in TPS timing

* docs: note thinking_start in first token timing comment

* test(windows): allow the NTFS source check to exceed the default timeout

---------

Co-authored-by: hetaoBackend <[email protected]>

* fix: omit empty tools from OpenAI compaction requests (#199)

* fix: omit empty tools on OpenAI compaction requests

* fix: negotiate missing tools with bounded compatibility recovery

* fix: simplify empty tools handling to omit absent definitions

* fix(tui): keep regular-mode history stable during live runs (#416)

Regular mode wrote rows into native scrollback as soon as they scrolled
off screen. When such a row later changed (a parallel tool finishing, a
streamed table widening, a list turning loose, a turn growing past the
projection fold, the welcome status badge flipping, or a stopped prompt
gaining its cancelled marker), the renderer cleared scrollback and
replayed the whole session from the welcome logo.

Only final rows now reach native history:

- The transcript reports how many leading rows later updates cannot
  change, including the committed blocks of a streaming Markdown reply.
- The chat layout keeps rows that may still change within one screen
  above the footer, showing their latest rows under a one-line notice
  when they do not fit.
- The regular-mode projection only appends; established turns are not
  re-folded or dropped.
- The welcome banner above a conversation is static; account notices
  that need action appear above the Composer.
- Stopping a run no longer marks its delivered prompt as cancelled; the
  marker stays reserved for prompts returned to the Composer.

To keep long sessions responsive, the transcript drops final rows far
above the screen and the engine rebases its retained state
(takeDiscardedRows) instead of rewriting history.

* feat: sync and release MiniMax Code 0.6.0 (#421)

Port the reviewed 0.6.0 runtime and TUI behavior into the standalone
distribution and bump the release version to 0.6.0.

- Allow /retry inside a /btw side conversation to resend its last message.
- Default to MiniMax-M3.1-Flash-Preview when no model has been selected.
- After an accepted Goal completion, keep the Turn open for one final reply
  that summarizes the result and deliverables; blocked proposals still end
  the Turn.
- Do not retry provider safety refusals, including on BYOK, and classify
  them as content_filter; keep Anthropic refusal details in the error.
- Retry TLS record verification failures before output like other
  transient network errors.
- Keep a post-compaction reminder at the tail of a continuation instead of
  aborting the provider call and recompacting on every retry.

* feat: sync and release MiniMax Code 0.6.1 (#422)

Allow /doctor and /feedback in a /btw side conversation so a failed side
response can be diagnosed or reported without leaving it. Neither command
mutates the parent or side Session. /quit stays blocked in the side view,
because leaving from there aborts only the side Turn and skips side
Session cleanup.

Bump the root and TUI source versions to 0.6.1.

* fix(tui): keep side-session Esc and main run timer consistent (#423)

In a /btw side conversation, an empty Escape no longer arms the
double-Escape /edit shortcut or shows its "Press Esc again to edit"
hint, since /edit is unavailable in side conversations. Escape still
interrupts a live side response.

Switching between the side and main views re-adopted the main Session's
live Turn at the switch time, which reset the Running/Loading timer and
shortened the settled Turn duration. A Turn start ledger now records the
earliest observed start (submission, session.start events including
hidden Sessions, and adoption), and both re-adoption and the activity
line use it.

Bump the root and TUI source versions to 0.6.2.

* chore(deps-dev): bump the tooling-minor-patch group across 1 directory with 6 updates (#363)

Bumps the tooling-minor-patch group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [prettier](https://github.com/prettier/prettier) | `3.9.6` | `3.9.9` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.13` | `4.23.15` |
| [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) | `5.5.5` | `5.5.6` |
| [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) | `18.3.1` | `18.4.0` |
| [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd) | `5.2.1` | `5.3.3` |
| [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) | `6.35.1` | `6.38.0` |



Updates `prettier` from 3.9.6 to 3.9.9
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.9)

Updates `tsx` from 4.23.13 to 4.23.15
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.13...v4.23.15)

Updates `eslint-plugin-prettier` from 5.5.5 to 5.5.6
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.5...v5.5.6)

Updates `dependency-cruiser` from 18.3.1 to 18.4.0
- [Release notes](https://github.com/sverweij/dependency-cruiser/releases)
- [Changelog](https://github.com/sverweij/dependency-cruiser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sverweij/dependency-cruiser/compare/v18.3.1...v18.4.0)

Updates `jscpd` from 5.2.1 to 5.3.3
- [Release notes](https://github.com/kucherenko/jscpd/releases)
- [Commits](https://github.com/kucherenko/jscpd/commits/v5.3.3/rust/jscpd)

Updates `knip` from 6.35.1 to 6.38.0
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/[email protected]/packages/knip)

---
updated-dependencies:
- dependency-name: dependency-cruiser
  dependency-version: 18.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: tooling-minor-patch
- dependency-name: eslint-plugin-prettier
  dependency-version: 5.5.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: tooling-minor-patch
- dependency-name: jscpd
  dependency-version: 5.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: tooling-minor-patch
- dependency-name: knip
  dependency-version: 6.37.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: tooling-minor-patch
- dependency-name: prettier
  dependency-version: 3.9.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: tooling-minor-patch
- dependency-name: tsx
  dependency-version: 4.23.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: tooling-minor-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(agent-core): bound hung model requests with first-event and idle timeouts (#428)

* fix: bound hung model requests so the retry path runs (#425)

A provider that accepts a request but never sends a response held the
turn for the transport default (undici headers timeout, ~300 s) before
the existing retry path ran. During that window the TUI showed Loading,
queued follow-ups, and /retry and /goal clear appeared to do nothing.

- agent-core: wrap every physical model request (each withLLMRetry
  attempt, agent and compaction scopes) in a stream watchdog. No first
  event within 120 s, or no event for 300 s after the first, aborts the
  attempt and ends the stream with a retryable "LLM request timed out"
  error (stopReason "error", not "aborted"), so BYOK and managed
  providers retry it. Caller aborts pass through unchanged. Bounds are
  configurable via LLMModelConfig.firstEventTimeoutMs /
  streamIdleTimeoutMs or MCODE_LLM_FIRST_EVENT_TIMEOUT_MS /
  MCODE_LLM_STREAM_IDLE_TIMEOUT_MS (0 disables).
- tui: /retry during a live run now says "Stop the running turn before
  using /retry." instead of "There is no failed response to retry";
  /goal clear and /goal pause during a live run say the current response
  keeps running and that Esc interrupts it.
- Tests: never-responding fake provider and real HTTP server that
  accepts but never answers; the attempt fails within the bound, retries
  and recovers, a fully hung turn fails and the next turn on the same
  runner completes. Document the bounds in docs/tui-capabilities.md.

* fix: keep prior first-response wait and stop reading abandoned streams (#425)

Review follow-up for the model request timeouts:

- Default the first-event bound to 300 s, matching undici's headers
  timeout that was the effective previous wait, so no request that used
  to succeed now times out. The idle bound stays at 300 s.
- Timeout errors name the setting that fired
  (MCODE_LLM_FIRST_EVENT_TIMEOUT_MS or MCODE_LLM_STREAM_IDLE_TIMEOUT_MS,
  or the host's per-model firstEventTimeoutMs/streamIdleTimeoutMs).
- When a bound fires, the wrapper stops waiting on the inner stream even
  if the provider ignores the abort: the pending read races a stop
  signal, and the inner iterator is released without being awaited.

Refs #425

---------

Co-authored-by: Tao He <[email protected]>

* fix(tui): defer rebuild after output-driven layout shrink (#429)

* fix(tui): keep scrolled-up readers in place on output-driven redraws (#426)

The regular-mode history reconstruction (L034) clears scrollback with
ED 3 and replays the document. xterm.js keeps its scrolled state while
the scrollback is rebuilt, so a reader who had scrolled up was moved to
line 0 whenever a resize settled (for example a fit addon reacting to a
layout change) or a transient layout shrank while a reply streamed.

Output-driven reconstructions now wait for the next user input, when
terminals scroll back to the bottom:

- TuiBase reports key and paste input through a protected onUserInput()
  hook; focus, size, mode and kitty-flag reports and key releases do not
  count.
- A settled resize replays history only if input arrived after the
  resize notification; otherwise it finishes as an in-place viewport
  redraw and the replay is deferred.
- A shrink in an unknown or changed layout pads like L038 unless input
  arrived within the last second (input-driven closes still restore
  rows immediately), and the replay is deferred.
- The next input after the deferral, or stop(), runs the deferred
  replay, so native history ends up exact and unique as before.

Changed historical text and size changes without a resize notification
still reconstruct immediately. Recorded as L047 in LOCAL_CHANGES.

* fix(tui): limit scroll preservation to output-driven layout shrink (#426)

Review follow-up:

- Resize again replays history as soon as it settles, as on main. The
  resize deferral is split out until the reporter's fit/resize behaviour
  is known; only the output-driven layout-shrink deferral remains (pad
  with blank rows, reconstruct on the next user input, immediately
  within one second of input).
- User input detection splits each chunk into control sequences instead
  of matching the whole chunk. Cursor position reports, OSC/DCS/APC
  strings and SGR mouse reports join focus, window, device and mode
  reports and key releases as non-input, and a key or paste that shares
  a chunk with reports still counts.
- Tests cover a tailing reader when output ends and the task list
  collapses, recent input reconstructing immediately, resize keeping its
  replay, each report kind, report-only and mixed chunks, and the replay
  before stop. L047 is updated accordingly.

Refs #426

---------

Co-authored-by: Tao He <[email protected]>

* fix(agent-core): cap images per request and recover sessions over the provider image limit (#431)

* fix(images): cap images per request and recover sessions over the provider limit

Inline images stay in persisted history and every request re-sends the
whole provider-visible history, so a long session eventually exceeded the
provider's per-request image limit ("Too many images in request: 31 > 30").
Every later request, /retry, /compact and a restarted session then failed
the same way, each after the full BYOK retry window, behind a generic
"temporarily unavailable / Run /retry" message.

- Request-time image ceiling: projectAgentMessagesForModel keeps the newest
  N images across user messages and tool results and replaces older ones
  with a short placeholder naming the original file. Stored history is
  never rewritten. N comes from capabilities.max_images_per_request
  (default 20; api.mistral.ai defaults to its documented 8). Agent turns,
  checkpoint requests and footprint estimates share the projection.
- Deterministic rejections are terminal: an image-count rejection, or an
  HTTP 400 invalid_request_error, is no longer retried by the BYOK
  retry-all path (timeouts, network, 408/429/5xx stay retryable). The TUI
  shows the upstream reason and suggests /compact or /new instead of /retry.
- Compaction fallback: when the checkpoint Provider rejects a candidate for
  too many images, compaction retries the same history with media replaced
  by text (Hvideo) and continues down the attachment-free ladder.

Refs #425

* fix(images): parse attachment reminder paths with a linear scan

CodeQL flagged the attribute regular expression as polynomial on
user-controlled message text (js/polynomial-redos). Parse the attachment
tags with indexOf instead and cover escaped paths and adversarial input.

Refs #425

---------

Co-authored-by: Tao He <[email protected]>

* Add opt-in lightweight context mode (#436)

* feat: add opt-in lightweight context mode

Co-authored-by: DanielWalnut <[email protected]>

* docs: clarify lightweight context boundary

Co-authored-by: DanielWalnut <[email protected]>

* fix: harden lightweight session behavior

Co-authored-by: DanielWalnut <[email protected]>

* fix: type lightweight turn guard and localize indicator

Co-authored-by: DanielWalnut <[email protected]>

* fix: import lightweight turn callback contract

Co-authored-by: DanielWalnut <[email protected]>

* fix: stop lightweight turns after unoffered tool calls

Co-authored-by: DanielWalnut <[email protected]>

* test: snapshot standard footer and forward tool guard

Co-authored-by: DanielWalnut <[email protected]>

* fix: forward lightweight stop policy to agent loop

Co-authored-by: DanielWalnut <[email protected]>

* fix: normalize unexpected lightweight tool calls

Co-authored-by: DanielWalnut <[email protected]>

* test: require clear lightweight tool fallback

Co-authored-by: DanielWalnut <[email protected]>

* test: cover resumed mode indicator in TUI

Co-authored-by: DanielWalnut <[email protected]>

* test: hydrate resumed session purpose in TUI

Co-authored-by: DanielWalnut <[email protected]>

* fix: preserve failed unexpected tool responses

Co-authored-by: DanielWalnut <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: DanielWalnut <[email protected]>

* feat: add launch-scoped system prompt overrides and fix Ask/Plan turn settlement (#444)

Add --system-prompt, --system-prompt-file, --append-system-prompt, and
--append-system-prompt-file to the interactive TUI, exec, exec review, and
acp. Replace and append are independent slots; each accepts inline text or a
file, and the slot written closest to the subcommand wins, so
`--system-prompt-file a.md exec --system-prompt "..."` uses the inline text
and never reads a.md. Files are read once at startup and a missing, empty, or
combined-form source fails the launch instead of running unpatched. Flags
placed before a subcommand that does not accept them are rejected rather
than silently ignored.

The overrides replace or extend the main Agent identity prompt and are held
in process memory only: they are never written to the Session or
configuration, survive the post-login restart, and do not reach Task child
Agents. Runtime rules, project instructions, Environment, Memory, and Skills
are still composed as before.

Also settle an ask_user turn as awaiting continuation. That turn ends with a
durable pending request rather than a final assistant message, so it now
settles as awaiting-user-continuation instead of a retryable EMPTY_RESPONSE
failure. The tool result and, when a fast reply arrives before the stream
drains, the pending request itself are both recognized; the generic event
match is narrowed to questionnaire.ask so a later dismiss or supersede is
not mistaken for a new request.

Bump the root and TUI source versions to 0.6.3.

* Fix locale-dependent capability tests (#443)

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: DanielWalnut <[email protected]>

* fix(cron): accept week unit in cron once --after durations (#435)

* fix(cron): accept week unit in cron once --after durations

`cron once --after 1w` was rejected with CRON_VALIDATION_ERROR even though
DURATION_UNIT_MS defines `w`, because both relative-duration parsers only
matched `ms|s|m|h|d`. Add `w` to the token regex in the shared agent-tools
parser and the local-runtime parser (keeping `ms` ahead of `m`), and replace
the local-runtime nested ternary with a unit table that includes weeks.

Single (`1w`) and compound (`1w2d`) week durations now resolve correctly;
existing units and invalid-input rejection are unchanged and covered by new
unit tests in both packages.

Fixes #433

Co-authored-by: smartworldarafath <[email protected]>

* fix(cron): parse cron once durations with an anchored token pattern

CodeQL flagged the `parseDurationMs` token regex (js/polynomial-redos):
matching the unanchored global pattern against `after` retries `\d+` from
every offset, so a long unit-less digit run takes quadratic time (about 2s
for 50k digits). Consume one start-anchored `<number><unit>` token at a time
instead, in both the agent-tools and local-runtime parsers. Accepted and
rejected inputs are unchanged; add a regression test for the digit-run case.

---------

Co-authored-by: Tao He <[email protected]>
Co-authored-by: smartworldarafath <[email protected]>

* fix(tui): defer rebuild when scrollback rows change without input (#434)

* fix(tui): defer rebuild when scrollback rows change without input (#426)

The regular-mode history reconstruction (L034) clears with ED 3 and replays
the document. xterm.js keeps its scrolled state while scrollback is erased,
so a reader who had scrolled up is moved to line 0. #429 deferred this for
an output-driven layout shrink; the reporter's jumps line up with the other
reconstruction paths, which still ran immediately without user input:

- text already in native history changed or was removed
  ("firstChanged < viewportTop"), for example the previous run duration
  note that TurnProjection removes when a queued turn starts;
- a shrink the L038 padding cannot absorb (changed history, overlays,
  images, or no rows left in the previous viewport);
- a footer taller than a small screen refreshing rows in scrollback.

Without user input in the last second these now repaint the screen in
place and mark the deferred replay; the next key, a settled resize replay
or stop() runs the L034 reconstruction as before. Changed history repaints
from the previous viewport origin so growth in the same frame still enters
native history; shrinks paint the document's last rows. Native scrollback
keeps stale, removed or duplicated rows above the screen until the next key.

PI_DEBUG_REDRAW lines now carry the pid (several sessions can share one
log), whether a reconstruction was deferred, and the first changed history
row with letters and digits masked.

Existing tests that asserted an immediate exact rebuild without input now
assert the deferred screen first and exact history after the next key. New
xterm cases cover a changed scrollback row, removal of the previous run
duration from a real append-only transcript, a shrink larger than a screen,
a five-row panel whose footer refreshes, repeated turn endings, and the
debug log. L047 is updated.

Refs #426

* test(tui): model input-driven closes/submits under deferred rebuild (#426)

The Tasks panel close and the typed /resume are user-driven; deliver the
input explicitly so their history rebuild runs before the measured steps.

Refs #426

* fix(tui): keep settled one-time feedback in transcript history (#426)

The previous run-duration note, finished ephemeral `!` shell blocks and
stale run errors were removed from the middle of the transcript when the
next turn started or ended. Their rows are final and can already sit in
native terminal scrollback, so the removal shifted every later row and
forced a history rebuild that moved a scrolled-up xterm.js reader to the
top whenever a turn started without a key press.

Dismiss such feedback only while it is still the live tail (nothing but
the next prompt, live cells or other feedback after it). Once settled
output follows, it stays in place as history. Retained turn-scoped cells
re-anchor after their own turn when a history refresh uses different
cell ids, so kept notes do not drift or stack up. The edit flow now
targets the most recent run duration.

Refs #426

* fix(tui): drop kept feedback of turns an edit removed (#426)

Run-duration notes, terminal errors and shell blocks kept in history
carry their turn id. When an edit removes later turns, a history refresh
could not anchor them to their turn and fell back to position-based
placement, leaving an isolated note in an unrelated spot. Drop such
feedback when its turn was projected before the refresh and is gone
after it (only when turn ids are comparable across the refresh). Rewind
reloads the projection from scratch and was not affected.

Also guard the deferred in-place repaint: if the document no longer
reaches the previous viewport, paint the tail instead of letting
fullRender fall back to an immediate ED 3.

Refs #426

---------

Co-authored-by: Tao He <[email protected]>

* fix(tui): exclude buffered bursts from the output rate at turn start (#454)

* chore: release MiniMax Code 0.6.4 (#447)

* chore: release MiniMax Code 0.6.5 (#458)

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: DanielWalnut <[email protected]>

* fix(tui): keep npm-prefix updates on the Node pinned by the receipt (#445)

* fix(tui): keep npm-prefix updates on the Node pinned by the receipt

The versioned npm-prefix update flow pins every release launcher to one
Node executable so the better-sqlite3 binding stays ABI-consistent, and
records that executable in install.json. The launcher runtime, however,
was taken from process.execPath: on macOS, execPath resolves Homebrew's
stable bin/node symlink into a versioned Cellar keg such as
Cellar/node/26.7.0/bin/node. After `brew upgrade` removes the old keg,
self-updated launchers exec a path that no longer exists and the CLI
fails to start with a bare "not found".

Reuse the nodeExecutable recorded in the install receipt when it is
still present, so launcher activation and the npm/npm validation child
processes stay on the pinned executable instead of re-resolving it
through process.execPath. Receipts without a usable nodeExecutable keep
the previous process.execPath behavior. Versioned launchers now also
print an actionable repair hint and exit 127 when their pinned Node is
missing, instead of a bare exec failure.

Homebrew same-major upgrades keep the ABI stable, so following the
formula-managed symlink is safe; a cross-major upgrade surfaces through
the existing SQLite validation and installer repair flow.

Assisted-by: pi

* fix(tui): heal Cellar-pinned Node receipts to the Homebrew opt path

Review follow-up for npm-prefix updates on Homebrew-managed Node.

Receipts written by earlier installers record the resolved Cellar keg
path, so simply preferring the receipt's nodeExecutable still breaks
after `brew upgrade` removes the keg. Canonicalize the recorded path at
receipt parse time: when it points into a versioned Cellar/node keg (or
a Homebrew-managed bin/node) and the formula's stable
opt/<formula>/bin/node resolves to the same executable, use the opt path
instead. The opt directory also keeps the update PATH prefix scoped to
the Node formula rather than all of <brew>/bin. Unusable entries
(missing, relative, non-file, non-executable) fall back to
process.execPath as before.

Also point the native SQLite startup diagnostic at rerunning the
installer for npm-prefix installs instead of an npm-global reinstall
command.

Assisted-by: pi

* fix(tui): match Homebrew opt node receipts explicitly

Review follow-up: the formula branch of HOMEBREW_MANAGED_NODE_PATH
was missing the opt/ literal, so it matched the nonexistent
<brew>/node/bin/node while real opt/<formula>/bin/node receipts never
matched. opt receipts are still returned unchanged either way, but the
pattern now recognizes them explicitly and the escaped linuxbrew
segment stops matching lookalike prefixes. Adds a pass-through test
for opt receipts.

Assisted-by: pi

* fix: schedule exec timeouts above the Node timer limit in chunks (#448)

Node clamps setTimeout delays above 2147483647 ms to 1 ms, so accepted
durations such as --timeout 600h cancelled a run almost immediately
while the forwarded execution deadline pointed weeks into the future.
Schedule the coordinator timeout as consecutive bounded chunks that
preserve the requested total delay, keeping parser acceptance of any
positive safe-integer duration.

Co-authored-by: minimax <agent@local>

* fix: keep --effort on the MiniMax API-key request (#461)

pi-ai sets output_config.effort from the selected thinking effort, but
patchThinkingPayload replaced the openplatform thinking variant and then
deleted output_config for every non-custom provider, so --effort never
reached the MiniMax open platform on the API-key path.

For minimax_api requests, keep the explicitly selected effort in
output_config the same way patchManagedThinkingPayload does on the managed
path. 'default', plain on/off models and other providers are unchanged.

* perf(runtime): trim per-turn reminders (budget thresholds, minute time, session id once) (#459)

P1-8: two reminders were spending tokens on every request/turn.

--timeout execution budget (request-only, appended per LLM request):
- Emit only on the first request, when remaining budget first drops to
  <=50%, <=25%, <=10% of the total, and on every request inside the final
  two minutes. Total budget is measured from hook creation (turn prep).
- Coarse text: whole minutes above 2 min, 10-second steps inside it.
- Drop the "since the previous model request" sentence.
- Admission rejection does not consume a threshold. Caller's cancellation
  timer stays authoritative; hook ordering constraint unchanged.

Per-turn <agent-context> system reminder (local runtime):
- date: Date#toString() without seconds (minute precision).
- The slim (turn >= 2) block omits YOUR SESSION ID when the provider-facing
  history since the latest compaction already contains a user message with
  this session's ID. First turn and the first turn after a compaction that
  dropped it still carry the ID. Full first-turn block and cloud scene are
  unchanged (flag absent => old behaviour).

* fix: stop retrying oversized requests and recover with one forced compaction (#460)

* fix: stop retrying oversized requests and recover with one forced compaction

A request the provider rejects as too large (HTTP 413, request_too_large,
the local request-body check, or a context-window overflow such as MiniMax
'context window exceeds limit (2013)') fails identically on every resend.
The API-key and custom-provider paths retried it five times.

- Classify these rejections as deterministic (isLLMRequestOversized), so
  withLLMRetry sends them once on every path.
- Add contextOverflowRecoveryHook to PiTurnHooks. When the main-agent
  request is rejected as too large before any output, the runner runs only
  these hooks once per turn with trigger 'context_overflow_recovery',
  persists the replacement and resends once; a second rejection is final.
- local-runtime-v2 registers the automatic compaction hook there; the
  trigger forces compaction and targets at most half of the rejected
  request, since the local estimate had admitted it.

* fix: surface the original rejection when overflow recovery throws

* fix(runtime): restore session ID after compaction and drop forced overflow compaction (#466)

* fix(runtime): drop forced compaction after oversized requests

Remove the one-shot compact-and-resend recovery that ran after a provider
rejected a main-agent request as too large. The resend bypassed the
normal before-LLM pipeline, so request-level transforms (screenshot caps,
session identity restoration) were not reapplied to it.

Oversized requests (HTTP 413, request_too_large, the local request-body
check, context-window overflow) are still classified as deterministic
rejections and are not resent unchanged.

Co-authored-by: Cursor <[email protected]>

* fix(runtime): restore the session ID after compaction drops it

The turn reminder omits `YOUR SESSION ID` while earlier history already
shows it. When automatic compaction summarizes that history away, the
model would lose its own session ID for the rest of the turn. A stateless
post-compaction hook now appends a hidden session identity reminder when
no visible user message or identity reminder after the latest compaction
carries the ID (before the current user message on initial compaction,
at the tail mid-turn). The preparation check counts that reminder too, so
the next turn does not repeat the ID.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>

* fix(tui): support SOCKS5 proxies and stop ALL_PROXY from crashing startup (#465)

* fix(tui): support SOCKS5 proxies and stop ALL_PROXY from crashing startup

Fixes #393. ALL_PROXY=socks5://... used to abort CLI startup because only
http:// and https:// proxy URLs were accepted.

- Route socks5://, socks5h://, and socks:// proxies through undici's
  Socks5ProxyAgent, which sends host names to the proxy for resolution.
  socks5h:// is rewritten to socks5:// with its credentials preserved.
  NO_PROXY hosts, including loopback, still connect directly.
- Ignore an unusable ALL_PROXY fallback (for example socks4:// or a
  malformed URL) with a single stderr warning instead of failing startup.
  Explicit invalid HTTP_PROXY/HTTPS_PROXY values are still rejected.
- Mask proxy credentials in warnings.
- Suppress only undici's one-time SOCKS5 ExperimentalWarning while the
  agent is constructed; other warnings are unaffected.
- Add unit tests and a loopback SOCKS5 integration test covering
  no-auth, username/password auth, domain-name CONNECT, and NO_PROXY.

* fix(tui): handle IPv6 SOCKS5 proxies and malformed proxy credentials

Follow-up to #393 review feedback.

- Dial SOCKS5 proxies at IPv6 literals such as socks5://[::1]:1080.
  undici passes the bracketed URL hostname to the socket connect, which
  failed with ENOTFOUND; a wrapped connector now strips the brackets.
- Validate the percent-encoding of proxy user names and passwords while
  classifying proxy settings. undici decodes them during agent
  construction and threw URIError, so a malformed ALL_PROXY made every
  command, including --help, exit with only "URI malformed". Such an
  ALL_PROXY is now ignored with a redacted warning; an explicit
  HTTP_PROXY/HTTPS_PROXY fails with an error naming the variable.
- Pass decoded SOCKS5 credentials as agent options and remove them from
  the proxy URL so undici does not decode them again.

* fix: update Codex catalog client version to 0.162.0 (#477)

* feat(tui): report program status with OSC 7501 (#471)

* feat(tui): report program status with OSC 7501

* fix(tui): report approval and question waits without build-mode

Interaction readback was gated on the build-mode status line item, so with
the default configuration a pending permission or question was reported as
working in both the terminal title and OSC 7501. The readback now always
feeds the status projection; build-mode only controls rendering the [V] line.

Missing login is derived once in the status projection as an error with
loginRequired, so the [V] line keeps state=error while the title shows
"Needs login" and OSC 7501 reports blocked:kind=auth from the same fact.

Add tui.programStatus (default true) to disable OSC 7501 reports, document
why reports are not wrapped for tmux passthrough, and reference protocol
revision 0.4.

* docs: correct stale source and npm version references to 0.6.3 / 0.6.9 (#487)

README, README_ZH, SECURITY, installation, open-source-status,
release-audit, releasing, telemetry, tui-capabilities and verification
still declared 0.4.12 while package.json is 0.6.3 and the published npm
package is 0.6.9. The version and evidence baseline table named
packages/tui/package.json as its source of truth while asserting a value
that file contradicted.

The data directory default was verified against the published 0.6.9
package before updating the references that name it.

Historical records are intentionally left unchanged: the reviewed 0.4.12
import and shared-source baseline in docs/verification.md, the
packages/tui CHANGELOG timelines, and release/extraction.json.

* docs(plugins): publish Plugin V1 format contract, schemas and a dependency-free public validator (#484)

* docs(community): add community on-call SLA and group layering docs

Register the two new community documents in the public source inventory so
check:source passes.

- docs/community/oncall.md: role split (chat vs GitHub), rotation windows,
  48h triage SLA, label hygiene rules, contribution-request rules, weekly
  feedback replay, reply templates, weekly metrics.
- docs/community/groups.md: three community layers and what belongs in each,
  the single entry point, movement rules, Insider external-disclosure rule,
  showcase wall and AMA format.

* docs(plugins): publish Plugin V1 format contract, schemas and public validator

External authors had no path to the Plugin package format: the contract,
the four JSON Schemas, the reference vectors and the validator all lived in
the internal marketplace repository, and the only onboarding tool
(plugin-creator) is private.

This publishes the contract into the public repository:

- docs/plugins/package-format-v1.md, host-bindings.md, showcases.md
- schemas/plugins/{plugin,mcp,app,host-binding,marketplace}-v1.schema.json
- test-vectors/plugins/content-digest-v1.{source,expected}.json
- tools/plugins/validate-plugin.mjs

The public validator runs the same code path an external author runs, and
check:source now calls it, so the published schemas and the validator cannot
drift apart. It uses only dependencies the repository already declares.

Catalog membership, cross-file capability closure, archive limits and
content_digest remain the responsibility of the official Marketplace
validator at submission time.

* chore(source): record published Plugin contract paths in the source inventory

* chore(source): drop temporary validator fixtures from the source inventory

* fix(plugins): make the public Plugin validator dependency-free

check:source also runs inside the exported source snapshot, which has no
node_modules, so importing a YAML parser made the gate fail there. The Skill
frontmatter subset the V1 contract requires is a flat scalar mapping, so it is
parsed inline instead.

* docs(plugins): add the external author onboarding guide and check:plugin entry

The contract was published without a way in. This adds docs/plugins/README.md
with the clone-to-submit path and a pnpm check:plugin script so an author can
run the same validation the CI runs.

* feat: support Google BYOK providers through Pi (#492)

* feat: add contextManagement.compactionTriggerAt to lower the auto-compaction trigger (#493)

Users can set an upper bound for the automatic context compaction trigger in
config.yaml, as a ratio of the context window in (0, 1] or as an absolute token
count above 1. The effective trigger is min(setting, built-in trigger), so the
setting can only start compaction earlier; there is no lower bound by design.
Invalid numbers such as 1.5 are logged once and ignored, and an unreadable
config falls back to the built-in trigger.

Refs #488

* feat(config): promote M3.1 Flash to the default delivered model (#509)

Replace the MiniMax-M3.1-Flash-Preview entry in the built-in MiniMax
fallback catalog with the generally available MiniMax-M3.1-Flash, and
point the managed preset default model at it. The model definition
(capabilities, context options, thinking efforts) is unchanged and
matches the remote managed snapshot for MiniMax-M3.1-Flash.

* docs(tui): add missing changelog entries for 0.5.0-0.6.5 (#511)

Record the user-visible changes of every public release from 0.5.0 to
0.6.5 in the English and Chinese TUI changelogs, which previously stopped
at 0.5.5 and skipped 0.5.0-0.5.4. Entries follow the commits contained in
each public tag. 0.5.5 gains the terminal title, notification, update
proxy and memory-output changes it shipped, and its date now matches the
GitHub release.

* fix(skills): don't wipe shared .builtin-skills; add @minimax-ai/code-acp launcher for ACP registry (#522)

* fix(local-runtime): refresh only owned built-in skills in shared dir

The seed wiped the whole .builtin-skills dir on a fingerprint mismatch.
That dir is shared between the desktop app and the CLI/ACP agent, so a
CLI start deleted desktop-only skills (mavis, create-agent, ...). Replace
only the dirs of skills this seed ships; per-skill rm failures (EPERM,
EACCES) are logged and retried next start instead of aborting.

* feat(acp): add @minimax-ai/code-acp launcher and lockstep publish job

@minimax-ai/code ships two bins (mcode, mcode-tools), so the ACP
registry's `npx @minimax-ai/code acp` cannot pick one and the registry
entry is stuck at 0.2.7. code-acp is a single-bin launcher that runs the
mcode bin of a pinned @minimax-ai/code. The opt-in publish-acp job stamps
and publishes it at the same version after @minimax-ai/code is on npm.

* feat(code-sdk): add the @minimax-ai/code-sdk TypeScript SDK (#553)

* feat(code-sdk): add the @minimax-ai/code-sdk TypeScript SDK

Add packages/code-sdk, a zero-dependency TypeScript SDK that drives the
local MiniMax Code agent from Node.js by wrapping
`mcode exec --output-format stream-json`: one CLI process per Turn, with a
Thread holding only the Session id and continuing it with --session.

- MCode / startThread / resumeThread / run / runStreamed, a ThreadBusy
  guard, prompts over stdin and system prompts through private temp files
- CLI resolution: explicit bin, an installed @minimax-ai/code, the nearest
  node_modules/.bin/mcode above the working directory, then PATH; a cached
  minimum-version check and schemaVersion 1 enforcement
- items merged by id, duplicate snapshots dropped, unfinished items marked
  incomplete; typed errors mapped from exec results and exit codes
- AbortSignal cancellation (SIGINT, SIGTERM, SIGKILL), a timeoutMs
  backstop, a bounded drain after exit and cleanup on normal host exit
- tests replay recorded, sanitized CLI output through a fake mcode; a
  contract test imports the CLI's JSONL projector and exit policy, and
  engines.node is pinned to the CLI's range
- npm publication metadata pinned to registry.npmjs.org, a manual
  RELEASING.md, and a release:check script that builds, tests, packs,
  scans the tarball and prints the publish command without running it

Repository integration: the package is registered in the workspace and
release/extraction.json, its tests form the code-sdk Vitest suite, and
verify gains typecheck:code-sdk and test:code-sdk (Linux and macOS). The
check:source text patterns move to scripts/lib/public-text-policy.mjs so
release:check reuses them.

* fix(code-sdk): omit the publish command from release:check rehearsals

A rehearsal on a branch other than an up-to-date main now ends with an
explicit warning instead of a publish command for a tarball that is not
release-ready.

* fix(code-sdk): never start the CLI through cmd.exe

Drop the Windows fallback that ran an unrecognized .cmd launcher through
cmd.exe with escaped arguments. The SDK now only runs a JS entry with
process.execPath or a native executable, and asks for an explicit bin
when a .cmd shim has no recognizable JS entry. This removes the shell
command construction and the backslash-heavy quoting regular expression
flagged by CodeQL.

* fix(code-sdk): pin the npmjs scope registry in release checks

publishConfig.registry and --registry only replace the default registry;
an @minimax-ai:registry setting in any npmrc still routed publish and the
read-back of this scoped package elsewhere, so a mistaken publish would
also read back as consistent.

release:check now reads the effective npm configuration and fails while
@minimax-ai:registry points anywhere but npmjs. Every npm command it runs
or prints, and every command in RELEASING.md, passes both --registry and
--@minimax-ai:registry for npmjs. Tools start without a shell; on Windows
npm and pnpm run through their JS entries so the smoke-test script and
temporary paths are not split by cmd.exe.

* fix(code-sdk): follow the official Windows installer launcher

The installer and CLI self-update put a root mcode.cmd on PATH that reads
the current release pointer and calls releases\<release>\.mcode-launcher.cmd.
Resolve that layout to the release's JS entry and keep running it with
process.execPath, without a shell. The pointer is followed again before
every spawn, so a self-update between Turns is used.

When a launcher is installed but cannot be followed, ask for bin instead of
suggesting an npm install, and stop mentioning a nonexistent mcode.exe.

* fix(scripts): scan the public text policy like any other source file

The policy patterns never match their own definitions, so the check:source
exemption for scripts/lib/public-text-policy.mjs only left a gap where an
internal address added to that file went unreported. Remove it and add a
regression test.

* docs(code-sdk): correct the error mapping and package notes

- README: map errors separately for exec results and for exits without a
  result (only exit 0 is a protocol error; unknown codes are internal run
  errors), describe require(esm) instead of a CommonJS export condition,
  and list the supported Windows installations.
- CONTRIBUTING: state that this repository is the package's only source,
  and that both SDK gates run in the full and platform profiles.
- Note that the public 0.7.0 CLI accepts the SDK's flags; test exit 1.

* docs(code-sdk): document npm allowScripts for project-local CLI installs

npm 11 rejects --allow-scripts for project-scoped installs (EALLOWSCRIPTS), so a project that installs @minimax-ai/code next to the SDK must approve the CLI's install scripts in package.json.

* fix(code-sdk): make cwd CLI lookup opt-in and address review

Search process.cwd() for the CLI only when resolveFromCwd is set.
Handle spawn failures without crashing the host, cancel with SIGTERM
and the CLI's 60s shutdown budget, and accept CLI 0.6.3 so this
repository's 0.6.5 build is supported. Release checks cover more
local paths, refuse a directory publish, and omit npm publish off main.

Co-authored-by: DanielWalnut <[email protected]>

* fix(code-sdk): keep published comments free of other package paths

The release text scan rejects packages/tui paths in the packed SDK. Describe
the CLI version floor and shutdown budget without naming those files.

Co-authored-by: DanielWalnut <[email protected]>

---------

Co-authored-by: DanielWalnut <[email protected]>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: AdhereZ <[email protected]>
Co-authored-by: minimax <[email protected]>
Co-authored-by: DanielWalnut <[email protected]>
Co-authored-by: AmsZuidas <[email protected]>
Co-authored-by: chenhao <[email protected]>
Co-authored-by: SaladDay <[email protected]>
Co-authored-by: hetaoBackend <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tao He <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: smartworldarafath <[email protected]>
Co-authored-by: Jiachen Yu <[email protected]>
Co-authored-by: minimax <agent@local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Standalone mcode: TUI, headless, ACP and source builds/tooling enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants