Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ update one package at `plugins/<github-username>/<plugin-id>/`.
the LICENSE holder. Update: open the existing package. Read `docs/package-contract.md` before
editing any manifest.
2. Read `docs/runtime.md` before editing `miniapp/node/*`. Read `docs/security.md` before the
Node code reads or writes files, spawns processes, or makes network requests.
Node code reads or writes files, spawns processes, or makes network requests. Read
`docs/client.md` before the page calls `window.mcode`, and `docs/agent.md` before declaring
`agentAccess`.
3. Write `README.md` (English; `README.zh-CN.md` optional): what it does, how to install and open
it, and the headings `## Tested environment` and `## Data & access` (files read/written,
network hosts, spawned processes, where state is stored).
network hosts, spawned processes, Agent turns, where state is stored). When the package uses a
feature with a minimum client version (`docs/package-contract.md`), name a MiniMax Code version
at or above it under `## Tested environment`.
4. Add one row to the table in both root READMEs.
5. Run `npm run check`. Done when it reports no errors and every warning is either fixed or
explained in the PR description.
Expand All @@ -28,12 +32,16 @@ update one package at `plugins/<github-username>/<plugin-id>/`.
- The Node entry is ESM with a named export `start(context)`. Resolve `start` only after the
listener accepts connections on `context.listen`. Return `{ dispose }`; `dispose` closes
everything the entry started: server, timers, child processes, streams, file handles.
- Bind only `context.listen.host` / `context.listen.port`. Serve `surface.path` plus your own
routes.
- A Mini App package is a MiniMax Plugin. Skills, MCP servers, hooks, host bindings, MCP endpoints,
and `hostConnectorAccess` may all be declared; `npm run check` validates their shape only, and the
Host validates them at install time. This repository documents the Mini App payload; treat
`context.hostConnector` and those capabilities as outside its scope.
- Bind only `context.listen.host` / `context.listen.port`, with a `node:http` server (or a framework
on top of it); `https`, `http2`, `tls`, raw `net`, or a child process on that port never becomes
ready. Serve `surface.path` plus your own routes, none under `/__mavis/`.
- A Mini App package is a MiniMax Plugin. Skills, subagents (`agents`), MCP servers, hooks, host
bindings, MCP endpoints, and `hostConnectorAccess` may all be declared; `npm run check` validates
their shape only, and the Host validates them at install time. This repository documents the Mini
App payload, the page APIs, and the Agent API; treat `context.hostConnector` and the other
capabilities as outside its scope.
- `agentAccess` gives an Agent with files, shell, and MCP. Declare it only when needed, and never
answer a `permission.requested` with `allowOnce` unless the user chose it.
- The runtime payload is exactly `miniapp/client` and `miniapp/node`. Keep tests and docs outside
`miniapp/`. Vendor third-party code inside the payload; `node_modules` is never committed.
- Paths are portable: ASCII, no symlinks, no `..`. `npm run check` enforces the full rule set.
Expand All @@ -43,13 +51,17 @@ update one package at `plugins/<github-username>/<plugin-id>/`.
## Review policy

Maintainers gate on three things: manifests pass `npm run check`; the README describes real
behaviour (files, network, processes, tested environment); the package cannot damage Host or user
data. Everything else is a suggestion.
behaviour (files, network, processes, Agent turns, tested environment); the package cannot damage
Host or user data. Everything else is a suggestion.

## Navigation

- `docs/package-contract.md` — layout, the three manifests, portable paths, limits
- `docs/runtime.md` — `start(context)`, `dispose`, logger, lifecycle
- `docs/security.md` — process boundary, state, writing outside `dataDir`, spawning, network
- `docs/package-contract.md` — layout, the three manifests, portable paths, limits, minimum client
versions
- `docs/runtime.md` — `start(context)`, `dispose`, logger, lifecycle, loopback authentication
- `docs/client.md` — page APIs: `message.append`, `agent`, `window`
- `docs/agent.md` — `agentAccess`, sessions, calls, events, errors
- `docs/security.md` — process boundary, state, writing outside `dataDir`, spawning, network, Agent
turns
- `examples/hello-miniapp/` — the copyable starting point
- `CONTRIBUTING.md` — fork, pull request, and license steps for people
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ English | [简体中文](CONTRIBUTING.zh-CN.md)
We welcome tools, games, and other interesting apps you have built for MiniMax Code.

1. Fork this repository and add your complete plugin package under `plugins/<your-github-username>/<plugin-id>/`. Use a lowercase GitHub username for the author directory.
2. Start from `examples/hello-miniapp/`. Include the hidden `.minimax-plugin/` directory, `package.json`, `miniapp/`, and all required runtime assets. The plugin must work when copied on its own, without depending on other directories in this repository. The exact rules are in `docs/package-contract.md`, `docs/runtime.md`, and `docs/security.md`.
3. Add a short English `README.md`. You may also include `README.zh-CN.md` with links between the two versions. Explain what the app does, how to install and use it, tested client versions and operating systems, required configuration, file access, and network requests. Screenshots or GIFs are welcome.
2. Start from `examples/hello-miniapp/`. Include the hidden `.minimax-plugin/` directory, `package.json`, `miniapp/`, and all required runtime assets. The plugin must work when copied on its own, without depending on other directories in this repository. The exact rules are in `docs/package-contract.md`, `docs/runtime.md`, `docs/client.md`, `docs/agent.md`, and `docs/security.md`.
3. Add a short English `README.md`. You may also include `README.zh-CN.md` with links between the two versions. Explain what the app does, how to install and use it, tested client versions and operating systems, required configuration, file access, network requests, and any Agent turns it runs. Screenshots or GIFs are welcome. If the app uses a feature that needs a newer client, such as `agentAccess` or `surface.window`, name a MiniMax Code version at or above its minimum under `## Tested environment` (see "Minimum client version" in `docs/package-contract.md`).
4. Include a `LICENSE` you are entitled to use, and preserve required attribution for third-party code and assets.
5. Add an entry to the app tables in both root READMEs, run `npm run check` (Node.js 22 or later) until it reports no errors, and open a pull request.

Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
欢迎分享你为 MiniMax Code 制作的小工具、游戏和其他有趣作品。

1. Fork 本仓库,在 `plugins/<你的 GitHub username>/<plugin-id>/` 下放入完整插件包。用户名目录统一使用小写。
2. 从 `examples/hello-miniapp/` 复制开始。保留 `.minimax-plugin/` 隐藏目录、`package.json`、`miniapp/` 和运行所需资源。插件应能独立复制使用,不依赖仓库里的其他目录。具体规则见 `docs/package-contract.md`、`docs/runtime.md` 和 `docs/security.md`(英文)。
3. 添加简短的英文 `README.md`,可另附 `README.zh-CN.md` 并互相链接。说明用途、安装与使用方式、已验证的客户端版本和系统,以及需要的配置、文件访问或网络请求。欢迎附上截图或 GIF。
2. 从 `examples/hello-miniapp/` 复制开始。保留 `.minimax-plugin/` 隐藏目录、`package.json`、`miniapp/` 和运行所需资源。插件应能独立复制使用,不依赖仓库里的其他目录。具体规则见 `docs/package-contract.md`、`docs/runtime.md`、`docs/client.md`、`docs/agent.md` 和 `docs/security.md`(英文)。
3. 添加简短的英文 `README.md`,可另附 `README.zh-CN.md` 并互相链接。说明用途、安装与使用方式、已验证的客户端版本和系统,以及需要的配置、文件访问、网络请求和会运行的 Agent 任务。欢迎附上截图或 GIF。如果用到了需要较新客户端的能力(如 `agentAccess`、`surface.window`),请在 `## Tested environment` 中写明不低于最低要求的 MiniMax Code 版本(见 `docs/package-contract.md` 的 “Minimum client version”)。
4. 添加你有权使用的 `LICENSE`,保留第三方代码和素材要求的署名。
5. 在根目录中英文 README 的作品表格中各增加一行,运行 `npm run check`(需要 Node.js 22 或更高版本)直到没有 error,再提交 Pull Request。

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ The preview uses synthetic data. The app interface is currently in Chinese. See

### Requirements

Use a MiniMax Code desktop version that supports MiniApps. Check the app's README for tested client versions, operating systems, configuration, and known limitations. Compatibility is documented per app.
Use a MiniMax Code desktop version that supports MiniApps. Check the app's README for tested client versions, operating systems, configuration, and known limitations. Compatibility is documented per app. Apps that use newer capabilities, such as running Agent tasks or opening in their own window, need MiniMax Code 3.1.2 or later; older versions cannot install them.

### 1. Download

Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ MiniMax Code Agent 通过宿主 MCP 客户端与 MiniApp 协作。业务请求

### 使用要求

需要支持 MiniApp 的 MiniMax Code 桌面端版本。各作品的 README 会说明已验证的客户端版本、操作系统、所需配置和已知限制,兼容性以具体作品为准。
需要支持 MiniApp 的 MiniMax Code 桌面端版本。各作品的 README 会说明已验证的客户端版本、操作系统、所需配置和已知限制,兼容性以具体作品为准。使用较新能力(如运行 Agent 任务、在独立窗口中打开)的作品需要 MiniMax Code 3.1.2 或更高版本,旧版本无法安装。

### 1. 下载

Expand Down
150 changes: 150 additions & 0 deletions docs/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Mini App Agent API

Verified against MiniMax Code 3.1.2. Earlier clients reject packages that declare `agentAccess`.

A Mini App can run Agent turns from its Node entry (`context.agent`) or its page
(`window.mcode.miniapp.agent`). The Agent keeps its full capabilities: files, shell, Skills, and MCP,
including this Mini App's own MCP tools. Declare it only when the Mini App genuinely needs an Agent
at runtime; a page that only prepares text for the user to send should use
`window.mcode.miniapp.message.append` instead (see `docs/client.md`).

## Grant

```json
{
"agentAccess": ["session.run"],
"agentConcurrency": 4
}
```

- `agentAccess` in `miniapp/miniapp.json` is the only grant. Absent or `[]` means no access:
`context.agent` is absent and page calls reject with `not_permitted`.
- `agentConcurrency` (1–32, default 4) caps how many turns of this Mini App run at once. Each session
runs one turn at a time. Further `run` / `answer` calls wait in order, up to four times
`agentConcurrency`; beyond that they fail with `busy`.
- Changing either field restarts the Mini App process when the package is updated.
- State in the README `## Data & access` that the Mini App runs Agent turns and what it asks them
to do (see `docs/security.md`).

## Sessions

Each Mini App owns its sessions and can continue only those. They are hidden from the user's
session list.

| | Page-initiated new session | Node-initiated new session |
| --- | --- | --- |
| Agent | The agent of the chat the Mini App is open in; the primary agent when it is not open in a chat. | The primary agent. |
| Working directory | That chat's project directory when it has one; otherwise this Mini App's own Agent directory. | This Mini App's own Agent directory. |
| Model | A snapshot of that chat's model and thinking level; the runtime default otherwise. | The runtime default. |

- Pass `agent` (a `name` from `agents()`) to `create` or to the first `run` to start the session as
another agent. The alias `main` is rejected. A session never changes its agent; passing `agent`
when continuing is `INVALID_ARGUMENTS` / `invalid_request`.
- A continued session keeps its agent and directory. Its model is fixed at creation and does not
follow later changes in the chat; pass `model` to `run` or `answer` to switch it first.
- `create({ agent? })` makes a session without running a turn and returns `{ sessionId }`. A page can
create a session bound to its chat and hand the `sessionId` to its Node code to continue there.

## Calls

Node calls take an options object `{ signal?, onEvent? }`; page calls take `{ onEvent? }` and use
`requestId` with `cancel(requestId)` instead of a signal.

| Call | Result |
| --- | --- |
| `run({ text, sessionId?, model?, agent? })` (page also `requestId?`) | `{ sessionId, turnId?, model?, status, text?, error?, pendingQuestion? }` when the turn ends. `status` is `finished`, `error`, or `aborted`; `text` is the final reply. `text` input is at most 100,000 UTF-16 code units. |
| `answer({ sessionId, answers, model? })` with `requestId` (Node) or `questionId` (page) naming the questionnaire | Same as `run`, for the continuation turn. |
| `reply({ sessionId, requestId, decision })` | Decides a waiting `permission.requested`; `decision` is `allowOnce` or `deny`. There is no "always allow". |
| `models()` | `{ models, default, host? }`: the models the user can use, the runtime default, and (page only) the chat's current model. `default` and `host` can be passed as `model` directly. |
| `agents()` | `{ agents }`: each `{ name, displayName, primary }`. |
| `create({ agent? })` | `{ sessionId }`, without running a turn. |
| `open(sessionId)` (page only) | Shows the session as a tab in the side panel of the chat the Mini App is open in, where the user can read it and answer its cards. Idempotent. |
| `cancel(requestId)` (page only) | Cancels that call; unknown or finished ids do nothing. |

- A model is `{ providerId, modelId, variant?, thinking?: { effort } }`. Build it from a `models()`
entry; pick `variant` from `variants` and `effort` from `thinking.efforts`. `variant: ''` means
thinking is off and must be passed as is.
- Call `context.agent` from request handlers, never inside `start(context)`.
- Node allows 16 outstanding `run` / `answer` / `models` / `agents` / `create` requests (shared with
Host Connector requests) plus 16 for `reply`, so a full queue can still answer approvals.

## Interaction

Two interactions block a turn. The Mini App renders them itself, or lets the Host do it with the
page's `open(sessionId)`:

- `permission.requested` → `reply`: a tool call waits for approval.
- `question.requested` → `answer`: the turn paused on a questionnaire (a question to the user, a
feature enable, or a plan review). The same questionnaire is in `result.pendingQuestion`, so a run
without `onEvent` can render it too. Call `answer` only after the paused `run` has resolved; an
earlier `answer` fails with `busy`.

A permission request waits only while someone can answer it: the run that owns the session passed
`onEvent`, or the session is open in a chat side panel through `open`. Otherwise the Host denies it
and tells the model that nobody can answer. A run that passes `onEvent` must handle
`permission.requested`, or it waits until it is cancelled or the page closes; there is no timeout.
When the Agent delegates to a child session, the child's requests arrive with the child's
`sessionId` and `turnId`; reply with those and do not filter events by the root turn.

```js
async function runWithApproval(agent, text, confirmTool, askUser) {
const onEvent = (event) => {
if (event.type !== 'permission.requested') return;
void confirmTool(event.toolName, event.reason)
.then((allowed) =>
agent.reply({ sessionId: event.sessionId, requestId: event.requestId, decision: allowed ? 'allowOnce' : 'deny' }),
)
.catch(() => {}); // request_not_found: the run was cancelled meanwhile
};
let result = await agent.run({ text }, { onEvent });
while (result.pendingQuestion) {
const answers = await askUser(result.pendingQuestion.request);
// Node names the questionnaire `requestId`; the page names it `questionId`.
result = await agent.answer(
{ sessionId: result.sessionId, requestId: result.pendingQuestion.requestId, answers },
{ onEvent },
);
}
return result;
}
```

`answers` holds one `{ stepId, selectedOptionIds, selectedOther, otherText?, skipped? }` per
questionnaire step.

## Events

`onEvent` receives events in this vocabulary. Every event except `run.queued` carries `sessionId`.
Ignore any `type` you do not know; new types may be added.

| Event | Meaning |
| --- | --- |
| `run.queued` | All slots are taken; `position` is the call's 1-based place in line. Sent once, before the turn. |
| `turn.started` | The turn began; `model` is the model it runs with. |
| `text.delta` | The next piece of the streaming reply. |
| `message.committed` | One complete assistant message; a turn can have several. |
| `tool.started` / `tool.finished` | The Agent called tool `name`; `ok` is `false` when it failed. |
| `permission.requested` | Tool `toolName` waits for `reply`; carries `requestId`, `reason`, `toolInput?`. |
| `question.requested` | The turn paused on a questionnaire; carries `requestId`, `mode`, `request`. |
| `turn.finished` | Always last; same `status`, `text?`, `error?` as the result. |

## Errors

Node errors carry `code` and `retryable`; page errors are an `Error` whose `message` is the code.

| Node `code` | Page `message` | Meaning |
| --- | --- | --- |
| `NOT_PERMITTED` | `not_permitted` | No `agentAccess`, or (page `open`) the Mini App is not open in a chat. |
| `BUSY` | `busy` | The session already runs a turn, or the queue is full. Retryable. |
| `SESSION_NOT_FOUND` | `session_not_found` | Not a session this Mini App owns. |
| `INVALID_ARGUMENTS` | `invalid_request` | Malformed input, an unusable model, or `agent` on an existing session. |
| `AGENT_UNAVAILABLE` | `runtime_unavailable` | Nothing was started; retry later. Also returned to a page or process left over from before a restart. |
| `REQUEST_CANCELLED` | `cancelled` | Cancelled before anything ran. |
| `AGENT_OUTCOME_UNKNOWN` | `result_unknown` | The turn may have run. Never retry automatically; check the session first. |
| `REQUEST_NOT_FOUND` | `request_not_found` | The permission request or questionnaire is unknown or already settled. |

A cancelled turn resolves with `status: "aborted"` only when the Host confirmed the abort. A process
restart cancels in-flight turns, and reloading or closing the page cancels the page's turns.

The type declarations are in `examples/hello-miniapp/miniapp/node/miniapp-api.ts` (Node) and
`examples/hello-miniapp/miniapp/client/client-api.ts` (page).
Loading
Loading