micro-models-agent 2.36.0 → 2.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +104 -57
- package/dist/assets/app.js +1 -1
- package/dist/assets/js/feed/sidebar.js +1 -1
- package/dist/assets/js/footer.js +1 -1
- package/dist/assets/js/loader.js +1 -1
- package/dist/assets/js/settings/controls.js +1 -1
- package/dist/assets/styles.css +1 -1
- package/dist/i18n/en.json +1 -1
- package/dist/i18n/ru.json +1 -1
- package/dist/main.js +416 -339
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,43 @@ All notable changes to Micro Models Agent (MMA) will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.37.0] - 2026-10-09
|
|
8
|
+
|
|
9
|
+
`mma serve` now describes its own API: a live OpenAPI 3.1 document plus a
|
|
10
|
+
human-readable contract page, so an integrator learns the endpoints from the
|
|
11
|
+
running service instead of from the repository. Auto-update reached `mma web`,
|
|
12
|
+
where it had never run, and the one dead updater flag is gone. README refreshed
|
|
13
|
+
across the board — features, tools, modules, CLI/REPL surfaces and config keys
|
|
14
|
+
had drifted behind the code.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Auto-update in `mma web`** (`src/modules/updater/factory.ts`, `cli/web-command.ts`, `webui/assets/js/loader.ts`, `js/footer.ts`): the web UI used to skip the updater entirely, so the four `updater.*` toggles in Settings → Advanced were dead. `createUpdater()` is now the single construction point for both surfaces; web starts the same fire-and-forget npm check and reports through a new SSE `update` event (`phase: available|installed|failed`), which makes **MMART react** — a new sparkly `update` face state that settles back to idle after a few seconds. The persistent half is `HostStateSnapshot.updateAvailable`, rendered as a green `↑ <latest>` chip in the footer next to the installed `mma <version>`, so the notice survives a reconnect and a freshly opened tab. The check still never blocks the server bind, and the checkout safety guard (`isDevEntryPath`) still refuses to `npm install -g` from a dev tree.
|
|
19
|
+
- **`exitOnComplete` no longer disables the updater** (`cli/main.ts`): that flag exists to decide who owns stdin, but the updater never touches stdin (a `fetch` and an `execFile`), so gating it on that flag was a false coupling.
|
|
20
|
+
- **Self-describing service contract** (`src/modules/service/openapi/`, `api/docs-api.ts`, `http/routes.ts`, `cli/service-command.ts`): `GET /v1/openapi.json` serves an OpenAPI 3.1 document of Agent Service API v1 (Swagger UI, Redoc, Postman/Insomnia, client generation), and `GET /v1/docs` serves the same contract as a self-contained HTML page — quickstart cURL, endpoint table with response codes, SSE events and error types. Both URLs are printed on `mma serve` startup (`cli.serve_docs`, en+ru) and both are exempt from the bearer check like `/v1/health` and `/v1/ready`: they describe the public API and expose no tenant data. The page is offline-first (no CDN), with an auth header in the quickstart only when a token is actually required. Single source of truth: the `OPERATIONS` table feeds the OpenAPI document, the docs page, and `tests/service/openapi.test.ts`, whose guard fails when a route exists without a description (or vice versa). Root `security: [{bearerAuth: []}]` appears only when a token is configured; `info.version` is `1.0.0+mma-<runtime version>`, so the spec can never drift from the build.
|
|
21
|
+
- **Spec ↔ reality conformance suite** (`tests/service/contract.test.ts`, `tests/service/contract-helpers.ts`): a hand-written spec rots silently, so every response the service returns is now checked against it in both directions — declared status codes (happy, error, queue-full, cancel-terminal, validation), payload shape (no undocumented fields, all required fields present, valid enums), declared response headers (`Location`/`Retry-After` on the `202` submit), content types, the auth matrix per operation (protected routes reject a missing/wrong token and accept the right one; open routes stay open), RFC 9457 error bodies, SSE frames against `EventEnvelope`, and the webhook body against `JobObject`. A guard also scans the runtime sources and fails if an emitted event type is missing from the enum, or a declared type is never emitted.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Oversized request bodies no longer kill the service** (`src/modules/service/http/respond.ts`, `api/sessions-api.ts`, `api/tasks-api.ts`): every `mma serve` handler read its JSON body without a guard, so a body over the 1 MiB cap produced an unhandled rejection — the integration got a socket error instead of a status code, and under Node the process could go down on an ordinary bad request. Bodies are now read through `readJsonBodyOrProblem`, which answers `413` (or `400` on a broken stream) as RFC 9457 Problem Details and destroys the socket only after the response is flushed — the same approach `mma web` already used.
|
|
26
|
+
- **Unhandled handler errors return `500 internal`** (`src/modules/service/server.ts`): the request dispatcher now wraps the route table, so any escaping exception becomes a Problem Details response and a warning log instead of a dropped connection. A long-running service must not die from one malformed request.
|
|
27
|
+
- **Webhook signature documented as conditional** (`src/modules/service/openapi/schemas.ts`, `docs/serve-mode.md`, `docs/agents/service-api.md`): `X-Mma-Signature` is sent only when `service.webhookSecret` is configured — an unsigned callback was documented as signed, which would have had integrators trusting unsigned payloads. The docs and the schema description now say so explicitly.
|
|
28
|
+
- **Dead contract entries removed**: the `error` event type was declared in `EventEnvelope.type` but never emitted, and `POST /v1/sessions/:id/tasks` returned `400` on an empty `input` without declaring it. Both are now guarded by tests (see below), and the missing `400`/`413` responses are declared.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **`updater.autoInstall` notice now says how to enable it** (`i18n` `updater.auto_install_hint`, en+ru): the default is off, so an "update available" line without a next step read like a dead end. The message now points at `/config updater.autoInstall true` alongside the manual command.
|
|
33
|
+
- **Docs:** the Auto-Update section of `docs/agents/modules.md` described `checkOnStart` and `autoInstall` as defaulting to `true` — both have been `false` since 1.x — and claimed the check only ran in the terminal. Now accurate, including the config file location (`~/.mma/updater.json`), the web surface, and the dev guard.
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- **`updater.checkOnStart`** (`config/types.ts`, `config/defaults.ts`, `config/registry-extra.ts`, `cli/main.ts`, web settings, both dictionaries): the flag was declared in config, the registry, i18n and the web settings panel, and read nowhere — `runLoop()` checked unconditionally. `enabled: false` already means "never check" and `intervalMs: 0` already means "check once per launch", so both states it could express were already spelled. An existing `~/.mma/updater.json` keeps the field; it is simply ignored. Guards in `tests/webui-config-extra.test.ts` keep the panel and the dictionaries free of it.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- **README refreshed** (features, tools table, module/tool counts, CLI subcommands, REPL command table, web mode, config keys, architecture and project structure): removed the phantom `scope_request` tool, added `define_capability` / `process_wait`, `mma serve`, `mma plugins …`, `mma security …`, `/security`, `/lsp doctor`, `contextAuto`, `critic.enabled`, `service.port`, the `service` module, and pointed first-time users at the new `/v1/docs` + `/v1/openapi.json` endpoints.
|
|
42
|
+
- **Docs:** a discovery section in `docs/agents/service-api.md` and `docs/serve-mode.md`; auth column added to the endpoint table.
|
|
43
|
+
|
|
7
44
|
## [2.36.0] - 2026-10-08
|
|
8
45
|
|
|
9
46
|
`mma web` fixes from a live two-tab session (a background session's stream
|
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ Most agents (Devin, Cursor, Copilot) are glued to cloud models or pricey APIs
|
|
|
23
23
|
|
|
24
24
|
- **Tuned for 9B models** — designed around Qwen3.5-9B running on ordinary hardware
|
|
25
25
|
- **Cloud-optional by design** — works fully offline against any OpenAI-compatible backend
|
|
26
|
-
- **Thrives in a tight context** —
|
|
27
|
-
- **Catches hallucinations** — a 3-stage validation pipeline spots the lies small models love to tell
|
|
26
|
+
- **Thrives in a tight context** — calibrated compaction (v2 policy) keeps 32K models productive, with a tool ledger so the agent stops re-reading what it already read
|
|
27
|
+
- **Catches hallucinations** — a 3-stage validation pipeline spots the lies small models love to tell, plus artifact/web/file claims and an in-loop critic
|
|
28
28
|
- **Agent-Level MoE** — hierarchical task decomposition lets small models ship big multi-step work
|
|
29
29
|
|
|
30
30
|
---
|
|
@@ -34,34 +34,37 @@ Most agents (Devin, Cursor, Copilot) are glued to cloud models or pricey APIs
|
|
|
34
34
|
**The agent loop**
|
|
35
35
|
|
|
36
36
|
- **6-state agent loop** (`INIT → THINK → ACT → OBSERVE → OUTPUT → ERROR`) — clean, predictable, debuggable
|
|
37
|
-
- **Context budget management** — per-model budgets with sliding-window compaction that preserves facts, decisions, and errors
|
|
38
|
-
- **
|
|
37
|
+
- **Context budget management** — per-model budgets with sliding-window compaction that preserves facts, decisions, and errors, keeps a `[Tool ledger]` of what was already read, and calibrates its token estimate against the provider's real `prompt_tokens`
|
|
38
|
+
- **Honest-stop guards** — failed tool batches, read-only loops and retry storms stop the loop with a truthful status instead of letting a small model grind (honest stop, read-only budget, `retry_loop_stopped`)
|
|
39
|
+
- **Hallucination detection** — fact checks (file paths), consistency checks (decision rollbacks), confidence checks, plus claim verification: "created file X" / "output says Y" / "the price is Z" are checked against real tool output
|
|
39
40
|
- **Execution guarantees** — LLM-driven auto-planning (no keyword heuristics), stuck detection, off-track warnings, automatic plan advancement
|
|
40
41
|
|
|
41
42
|
**Orchestration & scale**
|
|
42
43
|
|
|
43
44
|
- **Agent-Level MoE** — a router plus expert subagents with tool filtering, isolated context, file scoping, topological parallel execution, a re-plan cycle, and machine-readable verification (`success_criteria`)
|
|
44
45
|
- **YAML pipelines** — a DAG engine with parallel waves, dependencies, and retries
|
|
45
|
-
- **Project map** —
|
|
46
|
+
- **Project map** — symbol index (definitions, outlines, signatures), PageRank-ranked map under a token budget, gitignore-aware incremental cache, LSP fallback for symbol lookup
|
|
46
47
|
|
|
47
48
|
**Tools & integrations**
|
|
48
49
|
|
|
49
|
-
- **
|
|
50
|
-
- **
|
|
50
|
+
- **~37 core tools** — filesystem, shell, web search/fetch/browse, browser (Playwright), planning, memory, subagents, MCP, pipelines, LSP, processes, images; plus modular `plan`/`todo`/`verify`/`lsp_check`/`project_map`/`set_thinking`/`define_capability`, and `question`/`approve` in web mode
|
|
51
|
+
- **26 modules** — skills, plugins, MCP, pipelines, indexer, memory, context, sessions, browser, execution, critic, hallucination detection, updater, user profile, LSP, model certification, security, artifacts, processes, pricing, onboarding, providers, reasoning, setup, webui, service
|
|
51
52
|
- **MCP client** — connect to any MCP server (stdio or SSE)
|
|
52
|
-
- **LSP diagnostics** — TypeScript/CSS/HTML servers: type checks after every edit and
|
|
53
|
+
- **LSP diagnostics** — TypeScript/CSS/HTML servers: type checks after every edit, on demand, and `/lsp doctor` explains why a server isn't working
|
|
54
|
+
- **Plugin registry** — plugins can ship their own tools; the agent can draft its own capability with `define_capability` (written to a file, inert until you approve it)
|
|
53
55
|
|
|
54
56
|
**Experience**
|
|
55
57
|
|
|
56
|
-
- **Web mode `mma web`** — a browser chat (SSE + REST) on the same core and sessions, no TTY needed; first run opens the setup wizard right in the browser
|
|
58
|
+
- **Web mode `mma web`** — a browser chat (SSE + REST) on the same core and sessions, no TTY needed; first run opens the setup wizard right in the browser. Sidebar tabs: Session, Files (workspace tree + viewer), LSP, Trace (live per-session event stream), Plugins, Map. Parallel turns per session, tab persistence, activity rail, MMART the loader face
|
|
59
|
+
- **Service mode `mma serve`** — the same agent as a headless HTTP service with a versioned contract (Agent Service API v1): sessions, tasks, SSE, webhooks, and a live OpenAPI 3.1 spec at `/v1/openapi.json`
|
|
57
60
|
- **Onboarding** — `mma setup` (provider wizard), `mma meet` ("Let's get acquainted": an interview written to global memory), `mma agents init` (generate/improve `AGENTS.md`)
|
|
58
|
-
- **Unified `/config`** — a validated settings registry plus sugar commands `/model`, `/provider`, `/context`, `/reasoning`, `/verbose`
|
|
61
|
+
- **Unified `/config`** — a validated settings registry (~110 keys) plus sugar commands `/model`, `/provider`, `/context`, `/reasoning`, `/verbose`, `/security`
|
|
59
62
|
- **Prompt caching** — cache hints (llama.cpp / OpenAI / OpenRouter) with hit-rate and savings metrics (the `⟡` cache line)
|
|
60
63
|
- **Context log** — `--context-log` writes the full outgoing model context to a git-style diff (`context.diff`)
|
|
61
|
-
- **Session management** — persistent JSONL sessions, REPL commands, `mma session export`
|
|
64
|
+
- **Session management** — persistent JSONL sessions, auto-naming, REPL commands, `mma session export`, `mma session prune`
|
|
62
65
|
- **i18n** — every UI string goes through `t()`, with English and Russian bundled
|
|
63
66
|
- **Race-free output** — single-writer `OutputChannel`: no stray `console.*`/stdout outside the CLI and machine JSON
|
|
64
|
-
- **Flexible context** — adapts to any model window from 8K to
|
|
67
|
+
- **Flexible context** — adapts to any model window from 8K to 1M; the window is auto-detected from the provider unless you pin it
|
|
65
68
|
- **No TUI** — a minimal CLI + REPL with markdown→ANSI formatting
|
|
66
69
|
- **llama.cpp / Jinja proven** — tested against tricky Jinja-template edge cases (system-first, streaming fallback, explicit `stream: false`)
|
|
67
70
|
|
|
@@ -119,19 +122,28 @@ Point MMA at any OpenAI-compatible endpoint. With LM Studio:
|
|
|
119
122
|
### 2. Run the setup wizard
|
|
120
123
|
|
|
121
124
|
```bash
|
|
122
|
-
bun run mma setup
|
|
125
|
+
mma setup # or: bun run mma setup (from a source checkout)
|
|
123
126
|
```
|
|
124
127
|
|
|
125
|
-
It scans local ports, finds a model, tests the connection, and writes your config. Legacy alias: `
|
|
128
|
+
It scans local ports, finds a model, tests the connection, and writes your config. Legacy alias: `mma init`. Running `mma web` on an empty config gives you the same wizard in the browser.
|
|
126
129
|
|
|
127
130
|
### 3. Use the agent
|
|
128
131
|
|
|
132
|
+
Four modes over the same agent, sessions and tools — pick by who is driving:
|
|
133
|
+
|
|
129
134
|
```bash
|
|
130
|
-
#
|
|
131
|
-
|
|
135
|
+
mma "build an Express REST API and add tests" # one-shot
|
|
136
|
+
mma # interactive REPL
|
|
137
|
+
mma web # browser chat
|
|
138
|
+
mma serve # HTTP service for your own programs
|
|
139
|
+
```
|
|
132
140
|
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
### 4. Want the API contract?
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
mma serve &
|
|
145
|
+
curl -s localhost:7331/v1/openapi.json | head # OpenAPI 3.1 spec
|
|
146
|
+
open http://localhost:7331/v1/docs # human-readable page
|
|
135
147
|
```
|
|
136
148
|
|
|
137
149
|
---
|
|
@@ -149,7 +161,8 @@ bun run mma init # Legacy alias of mma setup (provider wiz
|
|
|
149
161
|
bun run mma meet # "Let's get acquainted" — interview, writes to memory
|
|
150
162
|
bun run mma agents init # Generate/improve AGENTS.md for the workspace
|
|
151
163
|
bun run mma config set model qwen/qwen3.5-9b
|
|
152
|
-
bun run mma config show
|
|
164
|
+
bun run mma config show # Secrets redacted (--show-secrets opts out)
|
|
165
|
+
bun run mma config migrate # Migrate a legacy ~/.mma/config.json
|
|
153
166
|
bun run mma model list # Available models (✔ = certified)
|
|
154
167
|
bun run mma model use qwen3.5-9b # Switch model
|
|
155
168
|
bun run mma model certify <name> # Certify a model against the backend
|
|
@@ -158,19 +171,27 @@ bun run mma model cert-list # All certifications
|
|
|
158
171
|
bun run mma model uncertify <name> # Revoke a certification
|
|
159
172
|
bun run mma provider list # List providers
|
|
160
173
|
bun run mma provider add local --url http://localhost:1234/v1 # Add a provider
|
|
161
|
-
bun run mma provider use opencode-zen #
|
|
174
|
+
bun run mma provider use opencode-zen # Switch the active provider
|
|
175
|
+
bun run mma provider check # Probe the active provider
|
|
162
176
|
bun run mma context # Context tokens/budget (--system/--reserve)
|
|
163
|
-
bun run mma map [summary|refresh|find] # Project map
|
|
164
177
|
bun run mma usage # Provider balance (OpenRouter /key, /credits)
|
|
178
|
+
bun run mma map [summary|refresh|find] # Project map
|
|
165
179
|
bun run mma security status # Current security configuration
|
|
166
180
|
bun run mma security policies # Available policies (strict/balanced/permissive)
|
|
167
181
|
bun run mma security set-policy strict # Apply a policy
|
|
182
|
+
bun run mma security audit-stats # Audit log counters
|
|
168
183
|
bun run mma plugins list # Loaded plugins (--all includes built-in)
|
|
184
|
+
bun run mma plugins show <name> # Plugin details (tools, permissions, failures)
|
|
185
|
+
bun run mma plugins enable|disable <name>
|
|
186
|
+
bun run mma plugins drafts [approve|revoke] # Agent-written capability drafts
|
|
187
|
+
bun run mma plugins reload
|
|
169
188
|
bun run mma session list # List sessions
|
|
170
189
|
bun run mma session show <id> # Session details
|
|
171
190
|
bun run mma session export <id> # Export a session (--format md|json|jsonl, --out -)
|
|
172
191
|
bun run mma session delete <id> # Delete a session
|
|
192
|
+
bun run mma session prune # Drop old sessions
|
|
173
193
|
bun run mma web [--port N] [--host H] [--token T] [--no-open] # Browser chat
|
|
194
|
+
bun run mma serve [--port N] [--host H] [--token T] # HTTP service (Agent Service API v1)
|
|
174
195
|
|
|
175
196
|
# One-shot flags
|
|
176
197
|
bun run mma "<prompt>" --json # Machine-readable JSON result
|
|
@@ -190,33 +211,35 @@ bun run dev
|
|
|
190
211
|
|
|
191
212
|
| Command | Aliases | Description |
|
|
192
213
|
|---------|---------|-------------|
|
|
193
|
-
| `/help` | | Show help |
|
|
214
|
+
| `/help [<command>]` | | Show help (usage, aliases, examples per command) |
|
|
194
215
|
| `/sessions` | `/ls` | List sessions (* = active) |
|
|
195
216
|
| `/new <name>` | `/create` | Create a new session |
|
|
196
|
-
| `/resume <id\|name>` | `/switch
|
|
217
|
+
| `/resume <id\|name>` | `/switch` | Switch to a session |
|
|
197
218
|
| `/rename <name>` | | Rename the current session |
|
|
198
219
|
| `/delete <id>` | `/rm` | Delete a session |
|
|
199
220
|
| `/model [name]` | | Show/switch model |
|
|
200
|
-
| `/provider [list\|use <name>\|add …]` | | Providers (list/switch/add) |
|
|
201
|
-
| `/status` | | Status: model, plugins, session, cost |
|
|
202
|
-
| `/config [<key> [<value>]]` | | Unified settings registry (`/config reset <key
|
|
203
|
-
| `/
|
|
221
|
+
| `/provider [list\|use <name>\|add …]` | | Providers (list/switch/add from the REPL) |
|
|
222
|
+
| `/status` | | Status: model, providers, plugins, session, cost |
|
|
223
|
+
| `/config [<key> [<value>]]` | | Unified settings registry (`/config reset <key>\|--all`) |
|
|
224
|
+
| `/security [status\|<key> [value]]` | | Security summary or a single key |
|
|
225
|
+
| `/context [N\|system 10%\|reserve 0.2]` | `/ctx` | Context window and budget |
|
|
204
226
|
| `/reasoning [show\|hide\|level <...>]` | | Reasoning display / level (`auto`/`low`/…/`max`) |
|
|
205
227
|
| `/verbose [quiet\|normal\|verbose]` | | Output verbosity level |
|
|
206
|
-
| `/memory [get\|set\|unset\|find\|forget]` | | View/edit memory |
|
|
228
|
+
| `/memory [get\|set\|unset\|find\|forget]` | | View/edit global memory |
|
|
207
229
|
| `/meet` | `/познакомимся` | Onboarding interview (writes to global memory) |
|
|
208
|
-
| `/map [summary\|refresh\|find]` | | Project map |
|
|
230
|
+
| `/map [summary\|refresh\|find]` | | Project map (symbol/outline lookup) |
|
|
209
231
|
| `/export [md\|json\|jsonl]` | | Export the current session |
|
|
210
|
-
| `/image <path\|url>` | | Attach an image (or Ctrl+V) |
|
|
232
|
+
| `/image <path\|url>` | `/img` | Attach an image (or Ctrl+V) |
|
|
211
233
|
| `/run <cmd>` | | Run a shell command without the agent |
|
|
212
|
-
| `/sysprompt` | | Show the system prompt |
|
|
213
|
-
| `/wizard` | | Setup wizard |
|
|
234
|
+
| `/sysprompt [--full]` | | Show the system prompt |
|
|
235
|
+
| `/wizard` | `/setup` | Setup wizard |
|
|
236
|
+
| `/init` | | Deprecated alias of `/wizard` |
|
|
214
237
|
| `/skill <name>` | | Load a skill |
|
|
215
|
-
| `/plugins` | |
|
|
216
|
-
| `/lsp [status\|restart\|check <path
|
|
238
|
+
| `/plugins [list\|show\|enable\|disable\|reload]` | | Plugin registry and drafts |
|
|
239
|
+
| `/lsp [status\|restart\|check <path>\|doctor]` | | LSP diagnostics |
|
|
217
240
|
| `/reload` | | Reload config and modules |
|
|
218
241
|
| `/clear` | | Clear the screen (session context is preserved) |
|
|
219
|
-
| `/exit` | | Exit the REPL |
|
|
242
|
+
| `/exit` | `/quit`, `/q` | Exit the REPL |
|
|
220
243
|
|
|
221
244
|
**Hotkeys:** `Esc Esc` — interrupt the agent; `Ctrl+C` — quit; `Ctrl+V` — paste an image from the clipboard; `Shift+Enter` — newline.
|
|
222
245
|
|
|
@@ -230,7 +253,9 @@ bun run mma web --port 8080 --host 127.0.0.1 --no-open
|
|
|
230
253
|
```
|
|
231
254
|
|
|
232
255
|
- **First run** with an empty config: the wizard opens right in the browser (language → provider → key → model → context → security → review). Chat stays locked until setup is complete (the server answers `409 setup_required`).
|
|
233
|
-
-
|
|
256
|
+
- **Real parallelism** — every tab is its own session with its own agent and context; turns run concurrently, and a background session streams into its own tab. Tabs survive a reload.
|
|
257
|
+
- **Sidebar** — Session (plan · usage · environment), Files (lazy workspace tree + read-only viewer, click a row to attach the path to your prompt), LSP, Trace (live per-session event stream with details), Plugins, Map (project index).
|
|
258
|
+
- Settings panel: models/providers, context (incl. `auto` from the provider), reasoning, memory, skills, plugins/MCP, security, network (incl. Agent Service API). Slash commands (`/help`, `/status`, `/config`, `/model`, `/provider`, `/memory`, …) work in the browser too, and config writes land in the open panel immediately.
|
|
234
259
|
- Env: `MMA_WEBUI_PORT`, `MMA_WEBUI_HOST`, `MMA_WEBUI_TOKEN`, `MMA_WEBUI_OPEN=0`, `MMA_WEBUI_MAX_CLIENTS`.
|
|
235
260
|
|
|
236
261
|
### Service mode (`mma serve`)
|
|
@@ -242,9 +267,14 @@ bun run mma serve # 127.0.0.1:7331
|
|
|
242
267
|
bun run mma serve --port 8080 --host 0.0.0.0 --token "$SECRET"
|
|
243
268
|
```
|
|
244
269
|
|
|
270
|
+
- **Discoverable contract** — no hunting through the repo:
|
|
271
|
+
- `GET /v1/openapi.json` — machine-readable **OpenAPI 3.1** spec (feed it to Swagger UI, Redoc, Postman/Insomnia, or generate a client);
|
|
272
|
+
- `GET /v1/docs` — the same contract as a human-readable page (quickstart cURL, endpoint table, SSE events, error types), self-contained HTML that works offline.
|
|
273
|
+
- Both are printed on startup and served without a bearer token — they describe the public API, they don't expose tenant data.
|
|
245
274
|
- **1 process = 1 tenant** (silo): its own `MMA_CONFIG_DIR`, workspace, memory. A control plane maps tenants to processes; the runtime does not know about neighbours.
|
|
246
275
|
- **Auth:** `Authorization: Bearer <token>` for the internal control-plane call (`service.token`, mandatory for non-loopback). The runtime is **not** meant to be exposed to the internet directly.
|
|
247
276
|
- Endpoints: `/v1/sessions` CRUD, `/v1/sessions/:id/tasks` (jobs), `/v1/tasks/:jobId` (+`/cancel`, `/answer`), `/v1/sessions/:id/events` (SSE), `/v1/health`, `/v1/ready`.
|
|
277
|
+
- Task = a turn over a session: `job` (autonomous, strict `toolAllowlist`) or `interactive` (questions over SSE). Pull (`GET /v1/tasks/:jobId`) is the source of truth; SSE and HMAC-signed webhooks are optimizations. Errors are RFC 9457 Problem Details with stable `type` URIs.
|
|
248
278
|
- Config: `service.port`, `service.host`, `service.token`, `service.resultRetentionDays`, `service.idempotencyTtlHours`, `service.syncWaitMs`, `service.maxQueueLength`, `service.callbackTimeoutMs`, `service.webhookSecret`. Env: `MMA_SERVICE_PORT`, `MMA_SERVICE_HOST`, `MMA_SERVICE_TOKEN`.
|
|
249
279
|
- Full reference: [docs/agents/service-api.md](./docs/agents/service-api.md) · guide: [docs/serve-mode.md](./docs/serve-mode.md) · design: [docs/specs/2026-10-08-agent-service-api-design.md](./docs/specs/2026-10-08-agent-service-api-design.md).
|
|
250
280
|
|
|
@@ -276,8 +306,10 @@ Key options (full list in `src/config/defaults.ts`; view/edit via `/config` in t
|
|
|
276
306
|
| Option | Default | Description |
|
|
277
307
|
|--------|---------|-------------|
|
|
278
308
|
| `model` | `qwen/qwen3.5-9b` | Model name for the backend |
|
|
279
|
-
| `provider.baseUrl` | `http://localhost:1234/v1` | OpenAI-compatible API URL
|
|
309
|
+
| `provider.baseUrl` | `http://localhost:1234/v1` | OpenAI-compatible API URL of the active provider |
|
|
310
|
+
| `provider.entries[]` | — | Named providers (multi-provider setup); `provider.active` holds the active entry's name |
|
|
280
311
|
| `contextWindow` | `32768` | Model context window in tokens |
|
|
312
|
+
| `contextAuto` | `true` | Take the window from the provider; writing a value pins it |
|
|
281
313
|
| `contextBudget.systemPrompt` | `0.10` | Share of the window for the system prompt |
|
|
282
314
|
| `contextBudget.responseReserve` | `0.15` | Reserve for the response |
|
|
283
315
|
| `autoPlan` | `true` | Auto-create plans for multi-step tasks |
|
|
@@ -285,12 +317,16 @@ Key options (full list in `src/config/defaults.ts`; view/edit via `/config` in t
|
|
|
285
317
|
| `reasoning.baseline` | `low` | Starting level in auto (grows on signals) |
|
|
286
318
|
| `maxToolIterations` | `1000` | Max tool calls per run |
|
|
287
319
|
| `stuckThreshold` | `6` | Iterations without progress before stuck detection |
|
|
288
|
-
| `moe.enabled` | `false` | Enable Agent-Level MoE |
|
|
320
|
+
| `moe.enabled` | `false` | Enable Agent-Level MoE (orchestrator falls back to the active model) |
|
|
321
|
+
| `critic.enabled` | `true` | In-loop critic: verifies the final answer before it's accepted |
|
|
289
322
|
| `security.enabled` | `true` | Security module (balanced policy by default) |
|
|
323
|
+
| `service.port` | `7331` | Port for `mma serve` |
|
|
290
324
|
| `ui.verbosity` | `normal` | `quiet` / `normal` / `verbose` |
|
|
291
325
|
| `locale` | `en` | UI language (`en` / `ru`) |
|
|
292
326
|
| `logLevel` | `info` | Logging level |
|
|
293
327
|
|
|
328
|
+
Everything else — ~110 keys across context budget, reasoning, MoE, critic, plugins, images, service, providers, webui — is reachable through `/config`, `mma config show` and web Settings → … (`config show` redacts secrets unless `--show-secrets`).
|
|
329
|
+
|
|
294
330
|
### Agent-Level MoE
|
|
295
331
|
|
|
296
332
|
```json
|
|
@@ -313,34 +349,43 @@ Key options (full list in `src/config/defaults.ts`; view/edit via `/config` in t
|
|
|
313
349
|
## Architecture
|
|
314
350
|
|
|
315
351
|
```
|
|
316
|
-
CLI (main.ts → commands.ts / repl.ts /
|
|
352
|
+
CLI (main.ts → commands.ts / repl.ts / service-command.ts / web-command.ts / setup.ts)
|
|
317
353
|
→ Core (agent.ts state machine → bootstrap.ts buildSystemInfo)
|
|
318
354
|
→ LLM Layer (provider.ts → openai-compat.ts → cache-usage.ts → token-counter.ts)
|
|
319
355
|
→ Tools (registry.ts → executor.ts → core tools + module-registered)
|
|
320
356
|
→ Modules (skills, plugins, mcp, pipelines, indexer, memory, context, session,
|
|
321
|
-
hallucination, execution, updater, user-profile, browser, lsp,
|
|
357
|
+
hallucination, execution, critic, updater, user-profile, browser, lsp,
|
|
322
358
|
security, certification, artifacts, processes, pricing,
|
|
323
|
-
onboarding, providers, reasoning, setup, webui)
|
|
359
|
+
onboarding, providers, reasoning, setup, webui, service)
|
|
324
360
|
→ Output (OutputBus → OutputChannel single writer; JSON/SSE sinks)
|
|
325
361
|
```
|
|
326
362
|
|
|
363
|
+
Three hosts over the same core: the terminal REPL (`cli/repl.ts`), the browser
|
|
364
|
+
(`core/web-host.ts` + `modules/webui/`), and the HTTP service
|
|
365
|
+
(`modules/service/`) — each with its own transport, none reimplementing the loop.
|
|
366
|
+
|
|
327
367
|
### Project structure
|
|
328
368
|
|
|
329
369
|
```
|
|
330
370
|
src/
|
|
331
371
|
├── core/ # Agent loop, bootstrap/system prompt, WebHost, types
|
|
332
372
|
├── llm/ # Provider abstraction, OpenAI compatibility, streaming, tokens, cache metrics
|
|
333
|
-
├── tools/ # Tool core (~
|
|
334
|
-
├── modules/ #
|
|
335
|
-
│ # sessions, hallucination, execution, updater, profile, browser, LSP,
|
|
373
|
+
├── tools/ # Tool core (~37) + registry, executor, scope guards
|
|
374
|
+
├── modules/ # 26 modules (skills, plugins, mcp, pipelines, indexer, memory, context,
|
|
375
|
+
│ # sessions, hallucination, execution, critic, updater, profile, browser, LSP,
|
|
336
376
|
│ # security, certification, artifacts, processes, pricing, onboarding,
|
|
337
|
-
│ # providers, reasoning, setup, webui)
|
|
377
|
+
│ # providers, reasoning, setup, webui, service)
|
|
338
378
|
├── output/ # Single-writer OutputChannel + OutputBus, JSON/SSE sinks
|
|
339
|
-
├── cli/ # Entry point, commands, REPL, wizards (setup), `mma web`
|
|
379
|
+
├── cli/ # Entry point, commands, REPL, wizards (setup), `mma web`, `mma serve`
|
|
340
380
|
├── config/ # 3-layer config: defaults → domains → project
|
|
341
381
|
├── i18n/ # en.json + ru.json + t()
|
|
342
382
|
├── ui/ # Markdown→ANSI formatter, renderer, line-editor
|
|
343
383
|
└── logger/ # Structured logger with levels and child loggers
|
|
384
|
+
|
|
385
|
+
docs/
|
|
386
|
+
├── serve-mode.md # Integrator guide for `mma serve`
|
|
387
|
+
├── design/ui/STYLE.md # Canonical chat-output style (single source of truth)
|
|
388
|
+
└── agents/ # Architecture, code style, tools, modules, execution, testing, service API
|
|
344
389
|
```
|
|
345
390
|
|
|
346
391
|
---
|
|
@@ -353,39 +398,39 @@ src/
|
|
|
353
398
|
| `write_file` | Create/overwrite, creating directories as needed |
|
|
354
399
|
| `edit_file` | Find-and-replace in existing files |
|
|
355
400
|
| `glob` | Search by glob patterns |
|
|
356
|
-
| `grep` | Search file contents
|
|
401
|
+
| `grep` | Search file contents (ripgrep → grep → built-in JS fallback) |
|
|
357
402
|
| `list_dir` | List directory contents |
|
|
358
403
|
| `create_dir` | Create a directory (recursively) |
|
|
359
404
|
| `delete_file` | Delete a file or empty directory |
|
|
360
405
|
| `move_file` | Move/rename a file or directory |
|
|
361
406
|
| `file_info` | File/directory metadata |
|
|
362
|
-
| `bash` | Run shell commands |
|
|
363
|
-
| `subagent` | Isolated, scoped subagent |
|
|
407
|
+
| `bash` | Run shell commands (foreground → background process) |
|
|
408
|
+
| `subagent` | Isolated, scoped subagent (runs in parallel within one batch) |
|
|
364
409
|
| `web_search` | Web search |
|
|
365
|
-
| `web_fetch` | Fetch a web page → markdown (
|
|
366
|
-
| `web_browse` | JS-less HTTP page fetch without an engine
|
|
410
|
+
| `web_fetch` | Fetch a web page → markdown (SSRF-safe, main-content extraction) |
|
|
411
|
+
| `web_browse` | JS-less HTTP page fetch without an engine |
|
|
367
412
|
| `browser` | Playwright browser (click, type, scroll, screenshot) |
|
|
368
413
|
| `plan` | Create/update/cancel plans |
|
|
369
414
|
| `todo` | Task tracking |
|
|
415
|
+
| `verify` | Verify plan steps |
|
|
370
416
|
| `load_skill` | Load a skill at runtime |
|
|
371
417
|
| `pipeline_run` | Run a YAML pipeline |
|
|
372
418
|
| `mcp_call` | Call an MCP server tool |
|
|
373
419
|
| `search_history` | Search session history |
|
|
374
|
-
| `project_map` | Query the project map (summary, refresh, find) |
|
|
420
|
+
| `project_map` | Query the project map (summary, refresh, find, symbol, outline) |
|
|
375
421
|
| `chunk_query` | Process large texts in parallel chunks |
|
|
376
422
|
| `download_file` | Download a binary file by URL to disk |
|
|
377
|
-
| `process_list` / `process_log` / `process_kill` | Manage background processes |
|
|
423
|
+
| `process_list` / `process_log` / `process_wait` / `process_kill` | Manage background processes |
|
|
378
424
|
| `remember` / `recall` | Persistent agent memory |
|
|
379
|
-
| `attach_image` | Attach an image (file/URL/clipboard;
|
|
425
|
+
| `attach_image` | Attach an image (file/URL/clipboard; queued into the next user message) |
|
|
380
426
|
| `enable_tools` | Enable hidden tool groups on the fly |
|
|
381
427
|
| `lsp_check` | LSP diagnostics (TypeScript/CSS/HTML) |
|
|
382
|
-
| `verify` | Verify plan steps |
|
|
383
428
|
| `session_info` | Current session info (id, model, context, messages) |
|
|
384
429
|
| `set_thinking` | Change the reasoning level for upcoming iterations |
|
|
385
|
-
| `
|
|
430
|
+
| `define_capability` | Draft a new capability (plugin tool) — inert until you approve it |
|
|
386
431
|
| `question` / `approve` | Interactive menus (registered only in web mode `mma web`) |
|
|
387
432
|
|
|
388
|
-
> Note: in the terminal, the interactive tools `question`/`approve` are not registered — the model asks questions as plain text. In `mma web` they work via SSE + `POST /api/answer`.
|
|
433
|
+
> Note: in the terminal, the interactive tools `question`/`approve` are not registered — the model asks questions as plain text. In `mma web` they work via SSE + `POST /api/answer`; in `mma serve` they are available in `interactive` tasks via `POST /v1/tasks/:jobId/answer`.
|
|
389
434
|
|
|
390
435
|
---
|
|
391
436
|
|
|
@@ -427,6 +472,8 @@ bun run mma "<prompt>" --no-agents-md --exit-on-complete -d <sandbox-path>
|
|
|
427
472
|
8. **No hardcoded strings** — all text through `t()` i18n
|
|
428
473
|
9. **No keyword matching** — the LLM decides when to plan, not heuristics
|
|
429
474
|
10. **No TUI** — a minimal CLI + REPL with markdown→ANSI
|
|
475
|
+
11. **Handler maps, not if-chains** — REPL/CLI/HTTP subcommands dispatch through a `Record<string, handler>`
|
|
476
|
+
12. **One source of truth per contract** — HTTP routes, the OpenAPI spec and the docs page all render from the same operation table, with a guard test
|
|
430
477
|
|
|
431
478
|
---
|
|
432
479
|
|
package/dist/assets/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{msgs as V,inp as v,connStatus as _,setIcon as A,icon as R}from"./js/dom.js";import{api as X,token as q}from"./js/api.js";import{I18N as Y,t as a,loadI18n as Z}from"./js/i18n.js";import{tabs as f,session as u,settings as m,hooks as S}from"./js/store.js";import{routeState as $,mergeUsage as ee,storeSnapshot as P,applyCounters as B,shouldHydrateAutoOpen as te}from"./js/state-sync.js";import{onText as ne,onReasoning as se,onMeta as oe,onPhase as ie,onToolStart as ae,onToolEnd as re,onTurnEnd as de,onErrorEvent as ce,onCommandOutput as le,onLog as fe,onPlan as ue,onQuestion as pe,onRetract as be,showEmptyFeed as y,renderPlan as me,renderUsage as F,renderEnvironment as he,renderWorkspace as ge,feedHasContent as we,loadHistory as ve}from"./js/feed.js";import{openTab as E,updateTabName as Se,renderSessionsDropdown as I,reflectBusy as ye,loadSessions as H,loadCommands as Ee,setTabStatus as w,reconcileTabStatuses as Ie,readSavedTabs as Le}from"./js/session/tabs.js";import{renderSelectors as L,renderSettings as xe,loadConfig as M,loadModels as Te,loadProviders as Ce,loadMemory as ke,loadSkills as _e,loadPlugins as Ae,applyTheme as Re,currentTheme as qe,applyFontSize as Pe,setConnStatus as x,setStaleBar as N}from"./js/settings.js";import{initRail as Be,setPlan as O,railEvent as Fe,applyRailI18n as He}from"./js/rail.js";import{initSidebar as Me,sidebarSessionChanged as Ne,sidebarMapState as Oe,sidebarTraceEvent as T,sidebarTurnEnd as Ue,sidebarBusy as U,sidebarMapIndicator as We,sidebarPullLog as De,sidebarSessionId as je,setSessionLoading as ze,sidebarLspState as Ge,sidebarLspEvent as Je,applySidebarI18n as W,initFiles as Qe,sidebarFilesState as Ke,sidebarFilesChanged as D,sidebarPluginsState as Ve,sidebarPluginsEvent as Xe,initSidebarDrawer as Ye}from"./js/sidebar.js";import{insertIntoDraft as Ze}from"./js/session/composer.js";import{initCursor as $e,createFace as et,setFaceState as j,loaderStateFor as tt,setGreetResolver as nt}from"./js/loader.js";let h=null,C=0;function p(n){C++,j(h,n)}function st(){const n=document.getElementById("composerFace");n&&(h=et("idle","sm"),n.replaceChildren(h))}document.querySelectorAll("[data-collapse]").forEach(n=>{const e=n.querySelector(".arrow");e&&(e.textContent="",e.appendChild(R("chevron-down"))),n.addEventListener("click",()=>{const i=n.nextElementSibling;if(!i)return;const o=i.classList.toggle("collapsed");n.setAttribute("aria-expanded",o?"false":"true");const l=n.querySelector(".arrow");l&&(l.textContent="",l.appendChild(R(o?"chevron-right":"chevron-down")))})});let k=-1;function g(){const n=f.tabs.find(l=>l.id===f.activeTabId)||null,e=n&&n.state||{};u.host=e;const i=e.sessionId||n&&n.id||null;u.activeSessionId=i,f.tabs.length||y(),me(e.plan),O(u.busy&&e?e.plan:null),F(e),typeof e.contextWindow=="number"&&m.config&&m.config.contextWindow!==e.contextWindow&&(m.config.contextWindow=e.contextWindow,L()),he(e),Oe(e),We(e),Ge(),Ke(e),Ve();const o=typeof e.filesRevision=="number"?e.filesRevision:void 0;if(o!==void 0&&o!==k){const l=k===-1;k=o,!l&&o>0&&D()}Ne(i),ge(e),I(),ze(!1),N(!!e.i18nStale)}function ot(n){const e=$(n);ye(),Ie(),n.state&&typeof n.state.sessionId=="string"&&Se(n.state.sessionId,n.state.sessionName),e&&g()}async function z(n){const e=n?"/api/state?session="+encodeURIComponent(n):"/api/state",i=await X(e);return i.ok&&i.data?i.data:null}async function G(n){const e=f.tabs.find(o=>o.id===n);if(!e)return;if(e.state){f.activeTabId===n&&g();return}const i=await z(n);i&&(P(i.state),f.activeTabId===n&&(B(i),g()))}async function J(){const n=f.activeTabId,e=await z(n);if(!e||f.activeTabId!==n)return;const i=e.state;if(!n&&i&&i.sessionId&&!f.tabs.some(o=>o.id===i.sessionId)){const o=te(u.sessions,i.sessionId);E(i.sessionId,i.sessionName||String(i.sessionId),"session",o)}P(i),B(e),g()}S.ensureTabState=G,S.renderActive=g;function it(){for(const e of f.tabs)e.kind==="session"&&(e.state=null,G(e.id));J(),H();const n=f.tabs.find(e=>e.id===f.activeTabId);n&&n.kind==="session"&&!we(n.feed)&&(n.historyLoaded=!1,ve(n))}function at(){const n="/events"+(q?"?token="+encodeURIComponent(q):""),e=new EventSource(n);let i=!1;e.onopen=()=>{i&&(i=!1,x(!1),it())},e.onerror=t=>{t&&typeof t.data=="string"||(i=!0,x(!0))};const o=t=>{try{return JSON.parse(t.data)}catch{return null}},l=t=>!t.sessionId||t.sessionId===je(),d=[["text",t=>ne(t.chunk||"",t.sessionId)],["meta",t=>oe(t.chunk||"",t.sessionId,t.kind)],["reasoning",t=>se(t.chunk||"",t.sessionId)],["phase",t=>ie(t.phase,t.sessionId)],["tool_start",ae],["tool_end",re],["plan",ue],["question",pe],["turn_end",t=>{de(t),l(t)&&(Ue(t.turnSeq),U(!1),De())}],["log",fe],["retract",t=>be(t.reason||"",t.sessionId)]];for(const[t,s]of d)e.addEventListener(t,c=>{const b=o(c);b&&s(b)});for(const t of["tool_start","tool_end","text","plan"])e.addEventListener(t,s=>{const c=o(s);c&&(t==="plan"?O(c):Fe(t,c.name))});const r=[["tool_start","tool_start"],["tool_end","tool_end"],["text","text"],["question","question"],["error","error"]];for(const[t,s]of r)e.addEventListener(t,c=>{const b=o(c);b&&p(tt({type:s,name:typeof b.name=="string"?b.name:""}))});e.addEventListener("phase",t=>{const s=o(t),c=s&&typeof s.phase=="string"?s.phase:"";p(c==="composing"?"type":c==="tool"?"run":"think")}),e.addEventListener("reasoning",()=>p("think")),e.addEventListener("plan",()=>p("plan")),e.addEventListener("turn_end",t=>{const s=o(t);p(s&&s.success===!1&&!s.interrupted?"error":"done");const c=C;window.setTimeout(()=>{c===C&&j(h,"idle")},1500)}),e.addEventListener("retract",()=>p("error")),e.addEventListener("index",()=>p("find")),e.addEventListener("setup_done",()=>p("done"));for(const t of["text","reasoning","phase","tool_start","plan"])e.addEventListener(t,s=>{const c=o(s);c&&c.sessionId&&w(c.sessionId,"working")});e.addEventListener("question",t=>{const s=o(t);s&&s.sessionId&&w(s.sessionId,"waiting")}),e.addEventListener("turn_end",t=>{const s=o(t);s&&s.sessionId&&w(s.sessionId,s.success===!1&&!s.interrupted?"failed":"idle")});for(const t of["tool_start","tool_end","phase","plan","question","meta","log","retract","index"])e.addEventListener(t,s=>{const c=o(s);c&&l(c)&&T(t,c)});e.addEventListener("text",t=>{const s=o(t);s&&l(s)&&T("text",s)}),e.addEventListener("lsp",t=>{const s=o(t);s&&Je(s)}),e.addEventListener("files",()=>D()),e.addEventListener("plugins",()=>Xe()),e.addEventListener("config",()=>{M()}),e.addEventListener("command",t=>{const s=o(t);s&&typeof s.text=="string"&&le(s.text,s.sessionId)}),e.addEventListener("state",t=>{const s=o(t);s&&(U(!!s.busy),ot(s))}),e.addEventListener("sessions",t=>{const s=o(t);s&&Array.isArray(s.sessions)&&(u.sessions=s.sessions,I())}),e.addEventListener("usage",t=>{const s=o(t);if(!s||!s.usage||typeof s.usage!="object")return;const c=s.usageSessionId||s.sessionId||null;ee(c,s.usage)&&F(u.host)}),e.addEventListener("error",t=>{if(!t.data)return;const s=o(t);s&&(s.sessionId&&w(s.sessionId,"failed"),ce(s),T("error",s))})}function Q(){document.documentElement.lang=Y.locale;const n=(d,r)=>{const t=document.getElementById(d);t&&(t.textContent=r)};n("planHeaderLabel",a("web.sidebar.plan")),n("usageHeaderLabel",a("web.sidebar.session")+" · "+a("web.sidebar.usage")),n("envHeaderLabel",a("web.sidebar.env")),n("settingsTitle",a("web.settings.title")),n("sessActiveHeader",a("web.sessions.active")),n("sessRecentHeader",a("web.sessions.recent")),n("hintEnter",a("web.input.enter")),n("hintShift",a("web.input.shift_enter")),n("hintEsc",a("web.input.esc")),n("hintAttach",a("web.input.attach")),n("bootCap",a("web.loader.boot")),nt(()=>a("web.loader.greet"));const e=h?.querySelector(".mdl-tip");e&&(e.textContent=a("web.loader.greet"));const i={newChat:["plus",a("web.topbar.new_chat")],sessBtn:["menu",a("web.topbar.all_sessions")],toggleSidebar:["panel",a("web.topbar.sidebar")],openSettings:["settings",a("web.topbar.settings")],attachBtn:["image",a("web.attach.title")],closeSettings:["x",a("web.topbar.close")],sessNew:["plus",a("web.sessions.new")],sessRefresh:["refresh",a("web.sessions.refresh")],sessPrev:["chevron-left",a("web.sessbar.prev")],sessNext:["chevron-right",a("web.sessbar.next")],sessClose:["x",a("web.topbar.close")]};Object.keys(i).forEach(d=>{const r=document.getElementById(d);if(!r)return;const[t,s]=i[d];A(r,t,d==="closeSettings"||d==="sessNew"||d==="sessRefresh"?s:null),r.title=s,r.setAttribute("aria-label",s)});const o=document.getElementById("settingsIcon");o&&A(o,"settings",null),v&&(v.placeholder=a("web.input.placeholder"),v.setAttribute("aria-label",a("web.input.placeholder"))),document.querySelectorAll("#setMenu .mi").forEach(d=>{const r=d.dataset.p;r&&(d.textContent=a("web.settings."+r))});const l=document.getElementById("setSearch");l&&(l.setAttribute("placeholder",a("web.settings.search_placeholder")),l.setAttribute("aria-label",a("web.settings.search_placeholder"))),document.querySelectorAll(".content-col .panel").forEach(d=>{const r=d.querySelector("h3"),t=d.id.replace(/^p-/,"");r&&t&&(r.textContent=a("web.settings."+t))}),L(),I(),He(),W(),V.querySelector(".empty")&&y(),_&&!_.hidden&&x(!0),N(void 0,!0)}function rt(n){const e=Le(),i=r=>u.sessions.some(t=>t.id===r),o=r=>{const t=u.sessions.find(s=>s.id===r);return t&&t.name?String(t.name):r},l=(n&&i(n)?n:e.active&&i(e.active)?e.active:e.ids.find(i))||null,d=[];n&&i(n)&&d.push(n),l&&!d.includes(l)&&d.push(l);for(const r of e.ids)i(r)&&!d.includes(r)&&d.push(r);if(d.length){E(d[0],o(d[0]),"session",!0);for(const r of d.slice(1))E(r,o(r),"session",!1,!1)}}async function dt(){$e();const n=new URLSearchParams(location.search).get("session");if(window.MmaSetup)try{if(await window.MmaSetup.maybeOpen()){K();return}}catch{}try{await Z(),await Ee(),Re(qe()),Pe(),Q(),y(),L(),xe(),await M(),Be(m.config&&m.config.ui?m.config.ui.activityRail:void 0),st(),Me(),Ye(),W(),Qe({insertPath:e=>Ze("`"+e+"` ")}),await Te(),await Ce(),await H(),await ke(),await _e(),await Ae(),rt(n);try{await J()}catch(e){console.error("initial refresh failed",e)}}finally{K()}at()}function K(){const n=document.getElementById("bootSplash");!n||n.hidden||(n.classList.add("hide"),window.setTimeout(()=>{n.hidden=!0},500))}S.applyStaticI18n=Q,dt();
|
|
1
|
+
import{msgs as V,inp as S,connStatus as A,setIcon as R,icon as q}from"./js/dom.js";import{api as X,token as P}from"./js/api.js";import{I18N as Y,t as a,loadI18n as Z}from"./js/i18n.js";import{tabs as f,session as p,settings as m,hooks as y}from"./js/store.js";import{routeState as $,mergeUsage as ee,storeSnapshot as B,applyCounters as F,shouldHydrateAutoOpen as te}from"./js/state-sync.js";import{onText as ne,onReasoning as se,onMeta as oe,onPhase as ie,onToolStart as ae,onToolEnd as re,onTurnEnd as de,onErrorEvent as ce,onCommandOutput as le,onLog as fe,onPlan as ue,onQuestion as pe,onRetract as be,showEmptyFeed as E,renderPlan as me,renderUsage as H,renderEnvironment as he,renderWorkspace as we,feedHasContent as ge,loadHistory as ve}from"./js/feed.js";import{openTab as I,updateTabName as Se,renderSessionsDropdown as L,reflectBusy as ye,loadSessions as M,loadCommands as Ee,setTabStatus as v,reconcileTabStatuses as Ie,readSavedTabs as Le}from"./js/session/tabs.js";import{renderSelectors as x,renderSettings as xe,loadConfig as N,loadModels as Te,loadProviders as Ce,loadMemory as ke,loadSkills as _e,loadPlugins as Ae,applyTheme as Re,currentTheme as qe,applyFontSize as Pe,setConnStatus as T,setStaleBar as O}from"./js/settings.js";import{initRail as Be,setPlan as U,railEvent as Fe,applyRailI18n as He}from"./js/rail.js";import{initSidebar as Me,sidebarSessionChanged as Ne,sidebarMapState as Oe,sidebarTraceEvent as C,sidebarTurnEnd as Ue,sidebarBusy as W,sidebarMapIndicator as We,sidebarPullLog as De,sidebarSessionId as je,setSessionLoading as ze,sidebarLspState as Ge,sidebarLspEvent as Je,applySidebarI18n as D,initFiles as Qe,sidebarFilesState as Ke,sidebarFilesChanged as j,sidebarPluginsState as Ve,sidebarPluginsEvent as Xe,initSidebarDrawer as Ye}from"./js/sidebar.js";import{insertIntoDraft as Ze}from"./js/session/composer.js";import{initCursor as $e,createFace as et,setFaceState as k,loaderStateFor as tt,setGreetResolver as nt}from"./js/loader.js";let h=null,w=0;function u(s){w++,k(h,s)}function st(){const s=document.getElementById("composerFace");s&&(h=et("idle","sm"),s.replaceChildren(h))}document.querySelectorAll("[data-collapse]").forEach(s=>{const e=s.querySelector(".arrow");e&&(e.textContent="",e.appendChild(q("chevron-down"))),s.addEventListener("click",()=>{const i=s.nextElementSibling;if(!i)return;const o=i.classList.toggle("collapsed");s.setAttribute("aria-expanded",o?"false":"true");const l=s.querySelector(".arrow");l&&(l.textContent="",l.appendChild(q(o?"chevron-right":"chevron-down")))})});let _=-1;function g(){const s=f.tabs.find(l=>l.id===f.activeTabId)||null,e=s&&s.state||{};p.host=e;const i=e.sessionId||s&&s.id||null;p.activeSessionId=i,f.tabs.length||E(),me(e.plan),U(p.busy&&e?e.plan:null),H(e),typeof e.contextWindow=="number"&&m.config&&m.config.contextWindow!==e.contextWindow&&(m.config.contextWindow=e.contextWindow,x()),he(e),Oe(e),We(e),Ge(),Ke(e),Ve();const o=typeof e.filesRevision=="number"?e.filesRevision:void 0;if(o!==void 0&&o!==_){const l=_===-1;_=o,!l&&o>0&&j()}Ne(i),we(e),L(),ze(!1),O(!!e.i18nStale)}function ot(s){const e=$(s);ye(),Ie(),s.state&&typeof s.state.sessionId=="string"&&Se(s.state.sessionId,s.state.sessionName),e&&g()}async function z(s){const e=s?"/api/state?session="+encodeURIComponent(s):"/api/state",i=await X(e);return i.ok&&i.data?i.data:null}async function G(s){const e=f.tabs.find(o=>o.id===s);if(!e)return;if(e.state){f.activeTabId===s&&g();return}const i=await z(s);i&&(B(i.state),f.activeTabId===s&&(F(i),g()))}async function J(){const s=f.activeTabId,e=await z(s);if(!e||f.activeTabId!==s)return;const i=e.state;if(!s&&i&&i.sessionId&&!f.tabs.some(o=>o.id===i.sessionId)){const o=te(p.sessions,i.sessionId);I(i.sessionId,i.sessionName||String(i.sessionId),"session",o)}B(i),F(e),g()}y.ensureTabState=G,y.renderActive=g;function it(){for(const e of f.tabs)e.kind==="session"&&(e.state=null,G(e.id));J(),M();const s=f.tabs.find(e=>e.id===f.activeTabId);s&&s.kind==="session"&&!ge(s.feed)&&(s.historyLoaded=!1,ve(s))}function at(){const s="/events"+(P?"?token="+encodeURIComponent(P):""),e=new EventSource(s);let i=!1;e.onopen=()=>{i&&(i=!1,T(!1),it())},e.onerror=t=>{t&&typeof t.data=="string"||(i=!0,T(!0))};const o=t=>{try{return JSON.parse(t.data)}catch{return null}},l=t=>!t.sessionId||t.sessionId===je(),d=[["text",t=>ne(t.chunk||"",t.sessionId)],["meta",t=>oe(t.chunk||"",t.sessionId,t.kind)],["reasoning",t=>se(t.chunk||"",t.sessionId)],["phase",t=>ie(t.phase,t.sessionId)],["tool_start",ae],["tool_end",re],["plan",ue],["question",pe],["turn_end",t=>{de(t),l(t)&&(Ue(t.turnSeq),W(!1),De())}],["log",fe],["retract",t=>be(t.reason||"",t.sessionId)]];for(const[t,n]of d)e.addEventListener(t,c=>{const b=o(c);b&&n(b)});for(const t of["tool_start","tool_end","text","plan"])e.addEventListener(t,n=>{const c=o(n);c&&(t==="plan"?U(c):Fe(t,c.name))});const r=[["tool_start","tool_start"],["tool_end","tool_end"],["text","text"],["question","question"],["error","error"]];for(const[t,n]of r)e.addEventListener(t,c=>{const b=o(c);b&&u(tt({type:n,name:typeof b.name=="string"?b.name:""}))});e.addEventListener("phase",t=>{const n=o(t),c=n&&typeof n.phase=="string"?n.phase:"";u(c==="composing"?"type":c==="tool"?"run":"think")}),e.addEventListener("reasoning",()=>u("think")),e.addEventListener("plan",()=>u("plan")),e.addEventListener("turn_end",t=>{const n=o(t);u(n&&n.success===!1&&!n.interrupted?"error":"done");const c=w;window.setTimeout(()=>{c===w&&k(h,"idle")},1500)}),e.addEventListener("retract",()=>u("error")),e.addEventListener("index",()=>u("find")),e.addEventListener("setup_done",()=>u("done")),e.addEventListener("update",t=>{const n=o(t);if(n&&n.phase==="failed"){u("error");return}u("update");const c=w;window.setTimeout(()=>{c===w&&k(h,"idle")},4e3)});for(const t of["text","reasoning","phase","tool_start","plan"])e.addEventListener(t,n=>{const c=o(n);c&&c.sessionId&&v(c.sessionId,"working")});e.addEventListener("question",t=>{const n=o(t);n&&n.sessionId&&v(n.sessionId,"waiting")}),e.addEventListener("turn_end",t=>{const n=o(t);n&&n.sessionId&&v(n.sessionId,n.success===!1&&!n.interrupted?"failed":"idle")});for(const t of["tool_start","tool_end","phase","plan","question","meta","log","retract","index"])e.addEventListener(t,n=>{const c=o(n);c&&l(c)&&C(t,c)});e.addEventListener("text",t=>{const n=o(t);n&&l(n)&&C("text",n)}),e.addEventListener("lsp",t=>{const n=o(t);n&&Je(n)}),e.addEventListener("files",()=>j()),e.addEventListener("plugins",()=>Xe()),e.addEventListener("config",()=>{N()}),e.addEventListener("command",t=>{const n=o(t);n&&typeof n.text=="string"&&le(n.text,n.sessionId)}),e.addEventListener("state",t=>{const n=o(t);n&&(W(!!n.busy),ot(n))}),e.addEventListener("sessions",t=>{const n=o(t);n&&Array.isArray(n.sessions)&&(p.sessions=n.sessions,L())}),e.addEventListener("usage",t=>{const n=o(t);if(!n||!n.usage||typeof n.usage!="object")return;const c=n.usageSessionId||n.sessionId||null;ee(c,n.usage)&&H(p.host)}),e.addEventListener("error",t=>{if(!t.data)return;const n=o(t);n&&(n.sessionId&&v(n.sessionId,"failed"),ce(n),C("error",n))})}function Q(){document.documentElement.lang=Y.locale;const s=(d,r)=>{const t=document.getElementById(d);t&&(t.textContent=r)};s("planHeaderLabel",a("web.sidebar.plan")),s("usageHeaderLabel",a("web.sidebar.session")+" · "+a("web.sidebar.usage")),s("envHeaderLabel",a("web.sidebar.env")),s("settingsTitle",a("web.settings.title")),s("sessActiveHeader",a("web.sessions.active")),s("sessRecentHeader",a("web.sessions.recent")),s("hintEnter",a("web.input.enter")),s("hintShift",a("web.input.shift_enter")),s("hintEsc",a("web.input.esc")),s("hintAttach",a("web.input.attach")),s("bootCap",a("web.loader.boot")),nt(()=>a("web.loader.greet"));const e=h?.querySelector(".mdl-tip");e&&(e.textContent=a("web.loader.greet"));const i={newChat:["plus",a("web.topbar.new_chat")],sessBtn:["menu",a("web.topbar.all_sessions")],toggleSidebar:["panel",a("web.topbar.sidebar")],openSettings:["settings",a("web.topbar.settings")],attachBtn:["image",a("web.attach.title")],closeSettings:["x",a("web.topbar.close")],sessNew:["plus",a("web.sessions.new")],sessRefresh:["refresh",a("web.sessions.refresh")],sessPrev:["chevron-left",a("web.sessbar.prev")],sessNext:["chevron-right",a("web.sessbar.next")],sessClose:["x",a("web.topbar.close")]};Object.keys(i).forEach(d=>{const r=document.getElementById(d);if(!r)return;const[t,n]=i[d];R(r,t,d==="closeSettings"||d==="sessNew"||d==="sessRefresh"?n:null),r.title=n,r.setAttribute("aria-label",n)});const o=document.getElementById("settingsIcon");o&&R(o,"settings",null),S&&(S.placeholder=a("web.input.placeholder"),S.setAttribute("aria-label",a("web.input.placeholder"))),document.querySelectorAll("#setMenu .mi").forEach(d=>{const r=d.dataset.p;r&&(d.textContent=a("web.settings."+r))});const l=document.getElementById("setSearch");l&&(l.setAttribute("placeholder",a("web.settings.search_placeholder")),l.setAttribute("aria-label",a("web.settings.search_placeholder"))),document.querySelectorAll(".content-col .panel").forEach(d=>{const r=d.querySelector("h3"),t=d.id.replace(/^p-/,"");r&&t&&(r.textContent=a("web.settings."+t))}),x(),L(),He(),D(),V.querySelector(".empty")&&E(),A&&!A.hidden&&T(!0),O(void 0,!0)}function rt(s){const e=Le(),i=r=>p.sessions.some(t=>t.id===r),o=r=>{const t=p.sessions.find(n=>n.id===r);return t&&t.name?String(t.name):r},l=(s&&i(s)?s:e.active&&i(e.active)?e.active:e.ids.find(i))||null,d=[];s&&i(s)&&d.push(s),l&&!d.includes(l)&&d.push(l);for(const r of e.ids)i(r)&&!d.includes(r)&&d.push(r);if(d.length){I(d[0],o(d[0]),"session",!0);for(const r of d.slice(1))I(r,o(r),"session",!1,!1)}}async function dt(){$e();const s=new URLSearchParams(location.search).get("session");if(window.MmaSetup)try{if(await window.MmaSetup.maybeOpen()){K();return}}catch{}try{await Z(),await Ee(),Re(qe()),Pe(),Q(),E(),x(),xe(),await N(),Be(m.config&&m.config.ui?m.config.ui.activityRail:void 0),st(),Me(),Ye(),D(),Qe({insertPath:e=>Ze("`"+e+"` ")}),await Te(),await Ce(),await M(),await ke(),await _e(),await Ae(),rt(s);try{await J()}catch(e){console.error("initial refresh failed",e)}}finally{K()}at()}function K(){const s=document.getElementById("bootSplash");!s||s.hidden||(s.classList.add("hide"),window.setTimeout(()=>{s.hidden=!0},500))}y.applyStaticI18n=Q,dt();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{usageEl as
|
|
1
|
+
import{usageEl as p,envEl as v,wsLine as U,el as s,icon as x,attachTooltip as h}from"../dom.js";import{I18N as k,t as a,pluralCount as C}from"../i18n.js";import{feed as w,settings as B}from"../store.js";import{renderFooter as E}from"../footer.js";import{fmtTokens as b}from"../format.js";import{copyText as L}from"../../md.js";function f(e,i,n){const t=s("div","kv");return t.appendChild(s("span","",e)),t.appendChild(s("b",n||"",i)),t}function _(e){return[["seg-system","web.usage.part_system",e.system],["seg-tools","web.usage.part_tools",e.tools],["seg-history","web.usage.part_history",e.history],["seg-reserve","web.usage.part_reserve",e.reserve],["seg-free","web.usage.part_free",e.free]]}function S(e,i,n,t){const o=document.createDocumentFragment();return o.appendChild(s("i","ctip-dot "+e)),o.appendChild(s("span","ctip-label",a(i))),o.appendChild(s("b","",b(n))),o.appendChild(s("span","ctip-pct",Math.round(n/t*100)+"%")),o}const M=new Map;function T(e,i,n){const t=M.get(i);if(M.set(i,n),t===void 0||t===n){e.style.flexGrow=String(n);return}e.style.flexGrow=String(t);const o=()=>{e.style.flexGrow=String(n)};typeof requestAnimationFrame=="function"?requestAnimationFrame(o):o()}function W(e,i,n){const t=s("div","bar segbar"),o=e.contextBreakdown;if(!o||typeof o.window!="number"||o.window<=0){const l=s("i");return l.style.flex="0 0 "+Math.min(100,Math.round(i/n*100))+"%",t.appendChild(l),t}const r=Math.max(o.window,o.used+o.reserve);for(const[l,d,c]of _(o)){if(!c||c<=0)continue;const u=s("i",l);u.style.flexBasis="0",T(u,l,c),u.tabIndex=0,u.setAttribute("aria-label",[a(d),b(c),Math.round(c/r*100)+"%"].join(" · ")),h(u,()=>S(l,d,c,r)),t.appendChild(u)}return t}function F(e){const i=s("div","seglegend"),n=e.contextBreakdown;if(!n)return i;const t=Math.max(n.window,n.used+n.reserve);for(const[o,r,l]of _(n)){if(!l||l<=0)continue;const d=s("span","segl-item");d.tabIndex=0,d.setAttribute("aria-label",[a(r),b(l),Math.round(l/t*100)+"%"].join(" · ")),d.appendChild(s("i","segl-dot "+o)),d.appendChild(s("span","segl-label",a(r))),d.appendChild(s("b","",b(l))),h(d,()=>S(o,r,l,t)),i.appendChild(d)}return i}function I(e){const i=s("div","kv");i.appendChild(s("span","",a("web.usage.session")));const n=s("span","kv-val");if(!e)n.appendChild(s("b","","—"));else{n.appendChild(s("b","",e));const t=s("button","copy-btn");t.type="button",t.title=a("web.usage.copy_id"),t.setAttribute("aria-label",a("web.usage.copy_id")),t.appendChild(x("copy")),t.addEventListener("click",()=>{L(e).then(o=>{t.replaceChildren(x(o?"check":"copy")),setTimeout(()=>t.replaceChildren(x("copy")),1500)})}),n.appendChild(t)}return i.appendChild(n),i}export function renderUsage(e){const i=B.config||{},n=JSON.stringify([e.sessionId,e.model,e.contextUsed,e.contextLimit,e.contextWindow,e.cacheHitRate,e.costUsd,e.compactionCount,e.contextQuality,e.contextProbe,e.contextBreakdown,i.model,i.contextWindow,k.locale]);if(n===w.lastUsageSig)return;w.lastUsageSig=n,p.textContent="";const t=e.sessionId?String(e.sessionId):"";p.appendChild(I(t)),p.appendChild(f(a("web.usage.model"),e.model||i.model||"—"));const o=typeof e.contextUsed=="number"?e.contextUsed:null,r=typeof e.contextLimit=="number"?e.contextLimit:i.contextWindow||null;p.appendChild(f(a("web.usage.context"),o!=null&&r?b(o)+" / "+b(r):"—")),o!=null&&r&&(p.appendChild(W(e,o,r)),p.appendChild(F(e)));const l=typeof e.cacheHitRate=="number"?e.cacheHitRate:null;p.appendChild(f(a("web.usage.cache"),l!=null?Math.round(l*100)+"%":"—",l!=null?"":"dim")),p.appendChild(f(a("web.usage.cost"),typeof e.costUsd=="number"?"$"+e.costUsd.toFixed(4):"—")),p.appendChild(f(a("web.usage.compactions"),"⟳ "+(e.compactionCount||0),e.compactionCount?"wn":"dim"));const d=typeof e.contextQuality=="number"?e.contextQuality:null;p.appendChild(f(a("web.usage.quality"),d!=null?Math.round(d)+"%":"—",d!=null?"":"dim"));let c="—",u="dim";const m=e.contextProbe;if(m&&typeof m.actual=="number"){const y=b(m.actual),g=e.contextWindow||i.contextWindow||null;g&&m.actual<g?(c=a("web.usage.probe_low",{actual:y,configured:b(g)}),u="wn"):(c=a("web.usage.probe_loaded",{actual:y}),u="")}p.appendChild(f(a("web.usage.probe"),c,u))}export function renderWorkspace(e){E(U,{workspace:e.workspace,version:e.version,moeActive:e.moeActive===!0,updateAvailable:e.updateAvailable||void 0,dirLabel:a("web.env.dir"),updateTitle:a("web.update.chip_title",{latest:e.updateAvailable?.latest??""})},s)}function A(e,i){const n=s("div","env-item");return n.appendChild(s("div","env-k",e)),i.forEach(t=>{const o=s("div","env-v",t);o.title=t,n.appendChild(o)}),n}export function renderEnvironment(e){const i=!!(e.instructions&&e.instructions.disabled),n=e.instructions&&!i?e.instructions.files||[]:[],t=e.projectMap||null,o=JSON.stringify([i,n,t,k.locale]);o!==w.lastEnvSig&&(w.lastEnvSig=o,v.textContent="",v.appendChild(A(a("web.env.agents"),i?[a("web.env.agents_disabled")]:n.length?n:[a("web.env.agents_none")])),v.appendChild(A(a("web.env.map"),t&&t.indexed?[[C("web.env.files",t.files),C("web.env.symbols",t.symbols),C("web.env.tokens",t.tokens)].join(" · ")]:[a("web.env.map_not_indexed")])))}
|
package/dist/assets/js/footer.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export function renderFooter(
|
|
1
|
+
export function renderFooter(e,p,i){const{workspace:n="",version:d="",moeActive:o=!1,updateAvailable:s=void 0,dirLabel:r,updateTitle:c=""}=p,a=`${n} ${d} ${o} ${s?.latest??""}`;if(e.dataset.key!==a){if(e.dataset.key=a,e.textContent="",e.hidden=!n&&!d&&!o&&!s,n){e.appendChild(i("ws-k",r));const t=i("ws-v",n);t.title=n,e.appendChild(t)}if(o){const t=i("ws-moe","MoE");t.title="MoE is orchestrating this turn",e.appendChild(t)}if(s){const t=i("ws-upd",`↑ ${s.latest}`);t.title=c,e.appendChild(t)}if(d){const t=i("ws-ver",`mma ${d}`);t.title=`micro-models-agent ${d}`,e.appendChild(t)}}}
|
package/dist/assets/js/loader.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const m={idle:"c-dim track poke blink",think:"c-active st-think blink",read:"c-recon st-read",find:"c-recon st-find squint",web:"c-recon st-web squint",edit:"c-mutate st-edit narrow",type:"c-active st-type narrow",create:"c-mutate st-create",run:"c-exec st-run",tests:"c-exec st-tests",compact:"c-active st-compact",plan:"c-active st-plan",ask:"c-warn st-ask wide",done:"c-ok st-done happy celebrate",error:"c-err st-error sad",stop:"c-dim st-stop sleep",queued:"c-dim st-queued blink",boot:"c-dim st-boot",load:"c-dim st-load"},_={read_file:"read",list_dir:"read",file_info:"read",load_skill:"read",session_info:"read",grep:"find",glob:"find",project_map:"find",chunk_query:"find",history:"find",recall:"find",search_history:"find",web_search:"web",web_fetch:"web",download_file:"web",web_browse:"web",write_file:"create",create_dir:"create",define_capability:"create",remember:"create",edit_file:"edit",move_file:"edit",delete_file:"edit",attach_image:"edit",bash:"run",pipeline_run:"run",process_wait:"run",process_kill:"run",browser:"run",subagent:"run",mcp_call:"run",enable_tools:"run",plan:"plan",todo:"plan",verify:"plan",lsp_check:"plan",set_thinking:"think",approve:"ask",question:"ask",meet_done:"done"};export function toolState(e){return _[e]||"run"}export function loaderStateFor(e){switch(e.type){case"turn_end":return e.success===!1?"error":"done";case"error":case"retract":return"error";case"question":return"ask";case"text":return"type";case"tool_start":case"tool_end":return toolState(e.name||"");case"index":return"find";case"setup_done":return"done";case"usage":case"state":case"config":case"files":case"sessions":case"plugins":case"log":return"idle";default:return"think"}}export const MMART_NAME="MMART";let w=()=>`Привет! Я ${MMART_NAME}`;export function setGreetResolver(e){w=e}function r(e,n,t){const s=document.createElement(e);return n&&(s.className=n),t!=null&&(s.textContent=t),s}export function createFace(e="think",n=null){const t=r("span");t.setAttribute("aria-hidden","true"),t.dataset.size=n||"";const s=r("span","eyes");s.appendChild(r("i","eye left")),s.appendChild(r("i","eye right"));const o=r("span","orb");o.appendChild(r("i"));const a=r("span","keys");for(let i=0;i<4;i++)a.appendChild(r("i"));const c=r("span","dust");for(let i=0;i<3;i++)c.appendChild(r("i"));return t.append(s,o,a,r("span","grow"),r("span","fly"),c,r("span","q","?"),r("span","spark","✦"),r("span","star"),r("span","mdl-tip")),setFaceState(t,e),t}export function setFaceState(e,n){if(!e)return;const t=e.dataset.size||"";e.className="mdl"+(t?" "+t:"")+" "+m[n],e.dataset.state=n,m[n].includes("track")||(e.style.setProperty("--ex","0px"),e.style.setProperty("--ey","0px"),e.style.setProperty("--tilt","0deg"))}const p=new WeakMap;function b(e){const n=e.querySelector(".mdl-tip");if(!n)return;n.textContent=w(),e.classList.add("show-tip");const t=p.get(e);t!==void 0&&window.clearTimeout(t),p.set(e,window.setTimeout(()=>{e.classList.remove("show-tip"),p.delete(e)},2200))}function u(e){return Array.from(document.querySelectorAll(e))}function x(e,n){for(const t of u(".mdl.track")){const s=t.getBoundingClientRect(),o=s.left+s.width/2,a=s.top+s.height/2,c=e-o,i=n-a,
|
|
1
|
+
const m={idle:"c-dim track poke blink",think:"c-active st-think blink",read:"c-recon st-read",find:"c-recon st-find squint",web:"c-recon st-web squint",edit:"c-mutate st-edit narrow",type:"c-active st-type narrow",create:"c-mutate st-create",run:"c-exec st-run",tests:"c-exec st-tests",compact:"c-active st-compact",plan:"c-active st-plan",ask:"c-warn st-ask wide",done:"c-ok st-done happy celebrate",error:"c-err st-error sad",stop:"c-dim st-stop sleep",queued:"c-dim st-queued blink",boot:"c-dim st-boot",load:"c-dim st-load",update:"c-ok st-update happy"},_={read_file:"read",list_dir:"read",file_info:"read",load_skill:"read",session_info:"read",grep:"find",glob:"find",project_map:"find",chunk_query:"find",history:"find",recall:"find",search_history:"find",web_search:"web",web_fetch:"web",download_file:"web",web_browse:"web",write_file:"create",create_dir:"create",define_capability:"create",remember:"create",edit_file:"edit",move_file:"edit",delete_file:"edit",attach_image:"edit",bash:"run",pipeline_run:"run",process_wait:"run",process_kill:"run",browser:"run",subagent:"run",mcp_call:"run",enable_tools:"run",plan:"plan",todo:"plan",verify:"plan",lsp_check:"plan",set_thinking:"think",approve:"ask",question:"ask",meet_done:"done"};export function toolState(e){return _[e]||"run"}export function loaderStateFor(e){switch(e.type){case"turn_end":return e.success===!1?"error":"done";case"error":case"retract":return"error";case"question":return"ask";case"text":return"type";case"tool_start":case"tool_end":return toolState(e.name||"");case"index":return"find";case"setup_done":return"done";case"update":return"update";case"usage":case"state":case"config":case"files":case"sessions":case"plugins":case"log":return"idle";default:return"think"}}export const MMART_NAME="MMART";let w=()=>`Привет! Я ${MMART_NAME}`;export function setGreetResolver(e){w=e}function r(e,n,t){const s=document.createElement(e);return n&&(s.className=n),t!=null&&(s.textContent=t),s}export function createFace(e="think",n=null){const t=r("span");t.setAttribute("aria-hidden","true"),t.dataset.size=n||"";const s=r("span","eyes");s.appendChild(r("i","eye left")),s.appendChild(r("i","eye right"));const o=r("span","orb");o.appendChild(r("i"));const a=r("span","keys");for(let i=0;i<4;i++)a.appendChild(r("i"));const c=r("span","dust");for(let i=0;i<3;i++)c.appendChild(r("i"));return t.append(s,o,a,r("span","grow"),r("span","fly"),c,r("span","q","?"),r("span","spark","✦"),r("span","star"),r("span","mdl-tip")),setFaceState(t,e),t}export function setFaceState(e,n){if(!e)return;const t=e.dataset.size||"";e.className="mdl"+(t?" "+t:"")+" "+m[n],e.dataset.state=n,m[n].includes("track")||(e.style.setProperty("--ex","0px"),e.style.setProperty("--ey","0px"),e.style.setProperty("--tilt","0deg"))}const p=new WeakMap;function b(e){const n=e.querySelector(".mdl-tip");if(!n)return;n.textContent=w(),e.classList.add("show-tip");const t=p.get(e);t!==void 0&&window.clearTimeout(t),p.set(e,window.setTimeout(()=>{e.classList.remove("show-tip"),p.delete(e)},2200))}function u(e){return Array.from(document.querySelectorAll(e))}function x(e,n){for(const t of u(".mdl.track")){const s=t.getBoundingClientRect(),o=s.left+s.width/2,a=s.top+s.height/2,c=e-o,i=n-a,d=Math.hypot(c,i)||1,f=s.width*.16,l=Math.min(1,d/140),y=c/d,k=i/d;t.style.setProperty("--ex",(y*f*l).toFixed(1)+"px"),t.style.setProperty("--ey",(k*f*l).toFixed(1)+"px"),t.style.setProperty("--tilt",(y*6*l).toFixed(2)+"deg")}}function g(){for(const e of u(".mdl.track"))e.style.setProperty("--ex","0px"),e.style.setProperty("--ey","0px"),e.style.setProperty("--tilt","0deg")}function v(e){const n=e.target;return!n||typeof n.closest!="function"?null:n.closest(".mdl.poke")}let h=!1;export function initCursor(){if(h||typeof window>"u")return;h=!0,window.addEventListener("mousemove",n=>x(n.clientX,n.clientY)),window.addEventListener("mouseleave",g);let e=null;window.addEventListener("click",n=>{const t=u(".mdl.poke");if(t.length){for(const o of t)o.classList.remove("blink-once"),o.offsetWidth,o.classList.add("blink-once");e!==null&&window.clearTimeout(e),e=window.setTimeout(()=>{for(const o of t)o.classList.remove("blink-once");e=null},130)}const s=v(n);s&&b(s)})}
|