micro-models-agent 2.4.0 → 2.8.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 CHANGED
@@ -4,6 +4,115 @@ 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.8.0] - 2026-09-26
8
+
9
+ ### Added
10
+
11
+ - **Provider context auto-detect** (`src/llm/provider-context.ts`, `src/llm/models-dev.ts`, `src/core/bootstrap.ts`, `src/core/agent.ts`): MMA now asks the active backend for the context window it actually serves and adopts it as the effective `contextWindow` (provider wins over config, in-memory only). Probe chain: LM Studio native (loaded instance, not card maximum) → Ollama native (`/api/ps` → `/api/show`) → llama.cpp `/props` (`n_ctx`) → OpenAI-compat `/models` metadata (`context_length`, vLLM `max_model_len`, OpenRouter `top_provider.context_length`) → models.dev catalog (last resort for OpenCode Zen/Go, whose own `/models` returns bare ids; slim `id → context` disk cache `~/.mma/models-dev-cache.json`, 24h TTL, fail-open). Applied at startup via `applyProbedContext` (late `agent.reconfigure` so budgets follow; skipped on manual override or after a turn ran) and in `Agent.reconfigure` on model/provider switches. `HostStateSnapshot.contextWindow` + `renderState` keep the web topbar in sync.
12
+ - **Active provider endpoint resolution** (`src/modules/providers/manager.ts`): new `activeProviderEndpoint()` resolves `{baseUrl, apiKey, type}` from `provider.active` in `entries` (fallback: legacy flat fields). The context probe, model autoload and reasoning-probe cache key used the stale flat fields after `/provider use` (observed: flat `localhost:1234` while `opencode-go` was active, so Zen was never probed).
13
+ - **LM Studio as a first-class provider** (`src/modules/providers/presets.ts`): new `lmstudio` builtin type (fixed `http://localhost:1234/v1` URL, no key, model list, local prompt-cache) on the same OpenAI-compatible transport, so native discovery travels with it instead of hiding inside a generic `openai-compat` entry. Wired everywhere a type matters: terminal/web setup wizards (port scan + `not-needed` key), web provider form presets and type suggestions, `mma provider add lmstudio`. The old `openai-compat`-typed local preset is retired (existing entries keep working).
14
+
15
+ ## [2.7.0] - 2026-09-25
16
+
17
+ ### Added
18
+
19
+ - **web streaming Markdown** (`src/modules/webui/assets/md.js`, `smd.js`): chat answers are now parsed incrementally by a vendored `streaming-markdown` (MIT) and rendered as real Markdown — headings, paragraphs, lists, task lists, blockquotes, rules, tables, links, images, inline code, bold/italic/strikethrough. Incremental block rendering replaces the old full re-render per chunk (O(n²)); half-open inline constructs (`**bo`) resolve without flicker.
20
+ - **syntax highlighting** (`src/modules/webui/assets/hljs.js`, `styles.css`): fenced code blocks are highlighted once, when the fence closes, with a vendored highlight.js (common build, MIT); token palette reuses the theme's semantic colors in dark and light.
21
+ - **code block chrome** (`md.js`): every fence gets a dim header with its lowercase language label and a copy button (`navigator.clipboard` with an `execCommand` fallback), enabled only once the block is complete; success shows `✓`.
22
+ - **per-message copy** (`app.js`): each assistant message/turn gets `copy` and `copy as markdown` actions. New i18n keys `web.code.copy/copied/copy_failed`, `web.msg.copy/copy_md` (en+ru).
23
+ - **new web assets/routes**: `/md.js`, `/smd.js`, `/hljs.js` (`WebUiAssets.mdJs/smdJs/hljsJs`, `STATIC_ASSETS`); `index.html` loads `hljs.js` (defer) and boots `app.js` as an ES module.
24
+ - **web settings — model/provider panel redesign** (`src/modules/webui/assets/app.js`, `styles.css`): the panel is split into titled sections **Model** (search + scrollable catalog, active marked) and **Providers** (add button in the section header, one entry per provider with an active badge, metadata line and `make active`/`edit`/`delete` chips). The duplicate "active provider" row is gone; the add/edit form opens inline with required fields first and optional context/priority/rpm/parallel collapsed under "advanced", plus type/base-URL suggestions. New i18n keys `web.settings.section_models/section_providers/provider_add_title/provider_edit_title/provider_advanced/provider_make_active/providers_empty` (en+ru). Design: `docs/specs/2026-09-25-webui-provider-settings-design.md`.
25
+ - **per-provider model memory** (`src/config/registry.ts`, `src/config/types.ts`): the model you last used with a provider is stored in `provider.models[name]` (written on every `model` change) and restored when you switch back via `provider.active` — the global `config.model` no longer silently carries over a model the new backend does not serve. Additive config field, no migration.
26
+ - **provider name guard** (`src/modules/providers/naming.ts`, `src/modules/webui/providers-api.ts`, CLI add): a new/renamed provider must not collide with another entry's `label` or `type`; conflicts are rejected (`409 name-conflict`, `cli.provider_name_conflict`) instead of silently editing the wrong entry. Web edits send `originalName` so an entry can keep its own name.
27
+ - **one-click provider presets in the settings form** (`src/modules/webui/providers-api.ts`, `assets/app.js`): `GET /api/providers` now returns `presets` (the builtin providers with their fixed base URLs, plus named local endpoints **LM Studio / Ollama / vLLM** that map to `openai-compat` with their well-known URLs) and the add/edit form shows a **preset** select that fills `type`, `label` and `baseUrl` in one click — neither hosted nor local providers require typing a URL by hand. New i18n keys `web.settings.provider_preset/provider_custom` (en+ru).
28
+ - **provider add/edit as a nested dialog** (`assets/index.html`, `app.js`, `styles.css`): adding or editing a provider now opens its own dialog **above the settings modal** (`#providerOverlay`, header + form, Esc / backdrop / ✕ to close, Tab focus-trap) instead of an inline block that was crammed into the panel; the form got roomier vertical spacing.
29
+
30
+ ### Changed
31
+
32
+ - **`app.js`** is now an ES module importing `./md.js`; `renderAssistantText` and the live `text` stream share one renderer, and `parseFences`/`renderTextSegment` are removed.
33
+ - **`feedHasContent`** now recognizes `.md` blocks.
34
+ - **web assets are served fresh per request** (`src/modules/webui/state.ts`, `module.ts`, `server.ts`): editing `src/modules/webui/assets/*` is now visible in the browser without restarting `mma web` (`WebUiState.assetSource` re-reads them on each static request; server `src/**/*.ts` changes still need a restart).
35
+
36
+ ### Fixed
37
+
38
+ - **model list now follows the active provider** (`src/modules/webui/module.ts`): the web model cache was built once at startup and never refreshed, so after switching provider the settings/selector still showed the previous provider's models. It is refreshed on every `reconfigure` (`makeReconfigure(..., after)`), so `/api/models` reflects the newly active provider.
39
+ - **adding a provider no longer drops the legacy config** (`src/modules/webui/providers-api.ts`): on a legacy single-provider config (`provider.type`/`baseUrl` without `entries`) the first add used to create `entries` from scratch and switch `active`, replacing the working provider. The legacy entry is now seeded into `entries[0]` and stays active.
40
+ - **project map ignores dev-only dirs/files** (`src/modules/indexer/walker.ts`): the walker now skips `_testing`, `.superpowers`, `.opencode`, `.fallow` (mirroring `.gitignore`), lockfiles (`package-lock.json`, `bun.lock`, `yarn.lock`, …), `.fallow*` files and vendored/minified bundles (`hljs.js`, `smd.js`, `*.min.js`). A dev checkout's map is no longer flooded (this repo: 860 → 774 files); the same filter now also gates the FS watcher.
41
+ - **removed a stray committed session export** (`session-ses_fdd1.md` at the repo root) and `.gitignore` now ignores `session-*.md`.
42
+
43
+ ## [2.6.0] - 2026-09-25
44
+
45
+ ### Added
46
+
47
+ - **web first-run setup wizard** (`src/modules/setup/{answers,apply,discovery}.ts`, `src/modules/webui/setup-api.ts`, `assets/setup.js`): `mma web` with a virgin config now opens a browser wizard (Language → Provider → API key → Model → Context → Security → Review) instead of booting on defaults. The terminal wizard (`src/cli/setup.ts`, `setup-order.ts`) re-exports the same shared core, so both frontends use one write path.
48
+ - **web setup API**: `GET /api/setup` (`required` + languages/providers/defaults), `POST /api/setup/{scan,models,test,locale}`, and `POST /api/setup` (validate → apply → persist → reconfigure → ungate). Emits an SSE `setup_done`.
49
+ - **web chat gating**: while `setupRequired`, `POST /api/message` and `/api/command` answer `409 setup_required` — server-enforced, not just hidden in the UI. A configured instance refuses a second `POST /api/setup` with `409 already_configured`.
50
+ - **web setup UI**: hidden full-screen `#setupGate` + `setup.js` (`window.MmaSetup.maybeOpen()`), `app.js` runs the wizard before chat boot, localized `web.setup.*` keys (en+ru), styles per `docs/design/ui/STYLE.md`.
51
+ - **system-prompt completion criteria** (`src/core/bootstrap.ts`): `buildSystemInfo` now carries a `DONE CONDITIONS` section (all plan steps done, artifacts exist with real content, runnable verification reports clean, then a short summary) and a `STOP AND ASK THE USER` section (ambiguity, unfound path, destructive action, stuck after 2+ approaches) — explicit stop/ask criteria for small models.
52
+ - **stuck failure classification** (`src/modules/execution/stuck-detector.ts`, `execution-plugin.ts`): `StuckDetector.classifyFailure()` maps the live stuck states to a stable `FailureType` (`timeout`/`repeat`/`error_loop`/`bash_flailing`/`read_only`) and `getStuckDiagnostic()` emits a compact `[STUCK: type=…, tool=…, count=…, iteration=…]` marker, now prefixed to the recovery `<system-summary>` so the model sees a machine-readable reason before the guidance.
53
+ - **session auto-naming** (`src/modules/session/auto-name.ts`, `src/core/agent.ts`): every session gets a deterministic name derived from the first user message, then an optional background LLM refinement (config `session.autoName`, 8s timeout, fail-open). `SessionMeta.nameSource` tracks provenance (`default`/`auto`/`manual`) and a manual rename is never overwritten.
54
+ - **counter-agent (in-loop critic)** (`src/modules/critic/`, `src/core/agent/critic-gate.ts`): an evidence-grounded, event-triggered LLM verifier wired into the single-agent loop next to the audit/hallucination gates. It runs on work mutations and before finish, advises on `concern`, performs a bounded repair on `critical` (deduped, capped by `critic.maxCalls`/`maxRepairCycles`), and fails open on error/timeout/parse failure. Verdicts are logged to `session.jsonl` (`critic_check`/`critic_verdict`/`critic_escalation`) and shown as one chat line at `ui.verbosity ≥ normal` (terminal `Renderer.criticVerdict`, web SSE `critic`; hidden in `quiet`, summary in `verbose`). Config lives in the new `critic` domain. The MoE path is untouched.
55
+ - **MoE + critic settings** (`src/config/registry.ts`, `src/modules/webui/assets/app.js`): `moe.enabled`, `moe.maxReplanCycles`, `critic.enabled`, `critic.maxCalls`, `critic.maxRepairCycles`, `critic.timeoutMs` are now settable via `/config` and a dedicated web settings **Agent** panel. The web session tab and sessions dropdown now pick up a rename immediately instead of waiting for the 15s session-list poll.
56
+
57
+ ### Changed
58
+
59
+ - **`mma web` virgin notice** (`src/cli/web-command.ts`): prints `cli.web_setup_required` (browser wizard opens) instead of the one-shot defaults warning.
60
+
61
+ ### Fixed
62
+
63
+ - **stale project-map cache** (`src/modules/indexer/cache.ts`, `walker.ts`): `index-cache.json` is now an envelope carrying a schema `version`, the absolute project `root` and a tree `signature` (SHA-1 over sorted `path\0size\0mtimeMs`, computed stat-only by `Indexer.scanSignature()`). `IndexCache.load()` recomputes the signature and rejects a cache whose version/root/signature differ, or a legacy raw-`IndexResult` cache, so a project map that no longer matches the tree can never reach the system prompt — the index is rebuilt instead.
64
+ - **reasoning overhead on trivial turns** (`src/config/defaults.ts`, `src/core/agent/reasoning-resolver.ts`): the auto-policy baseline now defaults to `"low"` (was an implicit `"medium"` resolver fallback), and an unset `baseline` falls back to the configured `reasoning.min` (`"low"` by default) instead of a hardcoded `"medium"`. Trivial Q&A turns no longer burn medium reasoning tokens; the policy still raises to `"high"` on hallucinations, audit rejections and plan creation.
65
+ - **`WebUiAssets.setupJs`** is optional (lightweight test fixtures stay valid) and `/setup.js` is served with `Cache-Control: no-store` like the other assets.
66
+
67
+ ### Security (audit waves B/C/D/E, severity 4)
68
+
69
+ - **symlink write escape** (`src/modules/security/path-validator.ts`): `isPathWritable` now runs the `escapesViaSymlink` check the read path always had — and canonicalizes the nearest existing ancestor, so creating a file under a symlinked/junctioned directory that points outside `baseDir` is blocked too. Write/delete/move can no longer escape the sandbox through a link.
70
+ - **command injection in lint-on-write** (`src/modules/plugins/builtin/lint-on-write.ts`): `bun build`/`node --check` now spawn with `shell: false` and an argv array instead of interpolating a model-supplied path into a shell string (`$(…)`, backticks, `%VAR%` no longer execute).
71
+ - **MCP tool registration race** (`src/modules/mcp/module.ts`): tool discovery is parallel and awaited before the bootstrap snapshots tool definitions (was fire-and-forget, so real MCP tools were never registered); a timed-out attempt is cancelled so its late success cannot register a server already marked failed.
72
+
73
+ ### Fixed (audit waves B/C/D/E, severity 4)
74
+
75
+ - **unvalidated `plan update` status** (`src/modules/execution/plan-actions.ts`): only `done|failed|skipped` are accepted; an arbitrary status (or `pending`) is rejected (`plan.bad_status`) instead of being persisted as an unknown state that never lets the plan complete or silently reopens a done step.
76
+ - **false auto-close of plan steps** (`src/modules/execution/module.ts`): `autoCloseEvidenceSteps` now requires delete-kind targets to be gone and create-kind files to exist with non-empty content, instead of marking empty/undeleted deliverables done and passing the final audit.
77
+ - **MoE subagent scope clobber** (`src/tools/subagent.ts`, `src/tools/executor.ts`): the parent scope is read from the shared executor (`getScope`) and restored with compare-and-restore, so parallel subtasks no longer reset isolation to `undefined` (unrestricted).
78
+ - **stuck/recovery detection gaps** (`src/modules/execution/execution-plugin.ts`, `stuck-detector.ts`, `moe-executor.ts`): bash-flailing and cross-tool consecutive failures are now injected into recovery and logged (were computed but never surfaced); the pre-plan path preserves loop history (a `reset()` each iteration had disabled read-only/repeat detection without a plan); a type/syntax error on a successful write is no longer cancelled by `recordToolSuccess` on the same call; escalation count survives trivial successes; per-file typecheck failures clear selectively instead of all at once; the MoE retry detector keeps a stable step id so errors accumulate across attempts.
79
+ - **secrets in session logs** (`src/core/session-logger.ts`): assistant content, reasoning, `system` and `error` entries are now redacted via `redactContextSecrets` (only `user`/`tool_result` were). Also `logSessionStart` checks for an existing `session_start` through the decrypt-aware store, so it no longer duplicates the entry under session encryption.
80
+ - **SSRF via redirects** (`src/modules/security/network-validator.ts`, `tools/{download-file,web-browse,web-fetch}.ts`): new `fetchValidated()` follows redirects manually and re-validates every hop (download/web_browse used default `follow`; web_fetch re-checked only one hop). Expanded/non-canonical IPv6 loopback and IPv4-mapped forms (`0:0:0:0:0:0:0:1`, `::ffff:0:7f00:1`, link-local/unique-local) are now recognized as private.
81
+ - **download_file write guards** (`src/tools/download-file.ts`): now honours the onboarding `writePathAllowlist` and the always-on protected-path check, so it can no longer overwrite `.env`/keys that every other write tool refuses.
82
+ - **memory tool hardening** (`src/modules/memory/store.ts`, `tools/{remember,recall}.ts`): memory categories are validated against the fixed list (a model-supplied traversal like `../../notes` is rejected) and a failed read/write returns `success:false` instead of a fake success.
83
+ - **mcp_call client leak** (`src/tools/mcp-call.ts`): the MCP client is disconnected in a `finally` even when `callTool` throws, so a failed call no longer orphans the stdio child process.
84
+ - **plan correctness** (`src/modules/execution/`): MoE selective re-plan with all subtasks already successful returns success instead of a bogus "possible cycle" failure; `verifyMoEManifest` resolves files/typecheck nested-aware (`findExistingFile`/`findTypecheckRoot`); plan-coverage uses an extension allow-list so `e.g`/`i.e` are not "missing files" while real `.css`/`.html` deliverables are checked; read-after-write overlap compares paths (not string prefixes) so `main.ts` vs `main.tsx` no longer conflicts; `applyAutoFixes` no longer mutates the caller's plan; `PlanStore` writes a unique temp file and logs a failed rename (two `mma` processes no longer clobber each other); `StepVerifier` kills the whole process tree on timeout.
85
+ - **test audit is evidence-based** (`src/modules/execution/auditor.ts`, `audit-runners.ts`): the final audit runs the tests whenever a test file exists, instead of deciding from step prose keywords (rule #10) — a plan with tests but no "test" wording is no longer approved without running them.
86
+ - **tool/robustness hardening (audit sev 2–3)** across `src/tools`, `src/modules` and `src/ui`:
87
+ - `read_file` clamps `offset` (0 no longer returned the last line) and an oversized `limit` (2000 cap); `process_log` clamps `tail` to its documented 300; `pipeline_run` tracks recursion depth on the shared executor and uses a per-invocation engine (no singleton clobber under parallel MoE waves).
88
+ - `syntax-validator` spawns `bun`/`node` without a shell (path injection) and accepts JSONC (comments/trailing commas) so a valid `tsconfig.json` is no longer rejected.
89
+ - command validator skips wrapper options (`nice`/`timeout`/`stdbuf`/`sudo …`) and versioned interpreters (`python3.11 -c`) — `nice rm file` no longer evades the blacklist.
90
+ - skills loader normalizes CRLF, so Windows-checkout skills load instead of silently disappearing; process registry caps a newline-less partial line; MCP stdio request ids are monotonic; plugin loader survives an unreadable entry; lint-on-write typecheck debounce is keyed by project root (monorepo writes no longer leak another project's errors).
91
+ - `mma provider use` persists the switch for multi-entry configs; line-editor completion drops the stale absolute frame anchor (candidate list no longer erased on DSR terminals); md-formatter flushes a pending table before a code fence.
92
+ - removed the dead `tools/scope-check.ts` duplicate (test now targets the real `security/path-validator`); `isInsideDir` no longer rejects names beginning with `..`.
93
+ - MCP reliability: sanitized tool-name collisions are disambiguated (was a bootstrap crash) and the prompt advertises the same sanitized `connect` name; concurrent SSE calls use a per-id pending map instead of one shared slot (no cross-talk / wrong response).
94
+ - removed the dead `PlanPersister` module; `PluginManager.runOnBeforeTool` now honours the string-return block contract like the tool executor.
95
+ - i18n: verifier typecheck/test messages, subagent/pipeline/mcp_call/chunk_query/meet_done tool output, plan summaries/test-run summaries and CLI status/metric lines now go through `t()` (en+ru), so RU sessions no longer mix English fragments.
96
+ - `read_file` no longer counts a phantom trailing line; sessions older than 30 days are actually archived (`rotateOldSessions` was dead code — now scheduled non-blocking at bootstrap); the UI locale is resolved before `createProgram()` so `--help`/typo hints respect `locale`; non-TTY `LineEditor` carries partial chunks instead of splitting them; `killTree` is asynchronous (no event-loop stall on Windows shutdown).
97
+
98
+ ## [2.5.0] - 2026-09-24
99
+
100
+ ### Added
101
+
102
+ - **web file viewer** (`src/modules/webui/file-api.ts`): `GET /api/file?path=<p>` serves a workspace text file for the chat file cards' Open link — resolved against the workspace (lexical + `realpath` guards, so symlinks/junctions cannot escape), protected paths (`.git/`, `.env*`, keys) refused, 512 KB cap, binary refused. UTF-16 LE/BE BOM and UTF-8 BOM are decoded; errors are human-readable `text/plain` (the endpoint opens as a browser tab).
103
+ - **web cookie auth** (`src/modules/webui/util.ts`, `server.ts`): authenticated requests set an `HttpOnly; SameSite=Strict` `mma_web_auth` cookie and `checkAuth` accepts it, so a plain `<a target="_blank">` navigation authenticates without the token in the URL.
104
+ - **web session persistence** (`assets/app.js`): the active session is mirrored into `?session=` (`history.replaceState`, token preserved) and restored on reload after the first state refresh.
105
+
106
+ ### Fixed
107
+
108
+ - **web reasoning text** (`src/modules/webui/forward.ts`, `history-api.ts`): ANSI escape sequences (`ESC[2m…`) are stripped from streamed/replayed text, meta and tool output — reasoning no longer renders as literal `\u001b[2m…` garbage on the model's own words.
109
+ - **web file card** (`assets/app.js`, `styles.css`): the card stacks vertically (path on its own line) and its `open` control is a real `<a target="_blank">` link that opens the file in a new tab.
110
+
111
+ ### Changed
112
+
113
+ - **dedicated reasoning channel** (`src/core/agent.ts`, `src/modules/webui/forward.ts`, `src/cli/repl.ts`, `src/cli/main.ts`): model reasoning now travels on its own `onReasoning` callback and SSE `reasoning` event (no longer pre-dimmed and no longer mixed into the generic `meta` channel); the terminal renders it via `renderer.reasoning`.
114
+ - **web reasoning collapsed by default** (`assets/app.js`): `thinking` blocks (live and replayed) start collapsed; generic `meta` (status, context stats, compaction, tool summaries, warnings) renders as always-visible `.metaln` lines instead of being folded into the reasoning block.
115
+
7
116
  ## [2.4.0] - 2026-09-24
8
117
 
9
118
  ### Added
package/README.md CHANGED
@@ -3,11 +3,12 @@
3
3
  [![npm version](https://img.shields.io/npm/v/micro-models-agent)](https://www.npmjs.com/package/micro-models-agent)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
5
 
6
- **MMA** — модульный AI-coding агент, оптимизированный для **небольших локальных языковых моделей** (9B параметров, 32K–64K контекст). Работает на ноутбуке, облачное API не требуется.
6
+ **MMA** — универсальный агент-харнесс, оптимизированный для **небольших локальных языковых моделей** (9B параметров, 32K–64K контекст). Работает на ноутбуке, облачное API не требуется. Это не «агент только для кода»: разработка, поиск в интернете, автоматизация, работа с API, умный дом, персональный ассистент — любой сценарий.
7
7
 
8
8
  ```bash
9
9
  npm install -g micro-models-agent
10
10
  mma "перепиши модуль авторизации на JWT"
11
+ mma "какая погода в Москве и собери сводку за неделю"
11
12
  ```
12
13
 
13
14
  Создан и протестирован с [Qwen3.5-9B](https://qwen.readthedocs.io/) через LM Studio / Ollama / llama.cpp.
@@ -16,7 +17,7 @@ mma "перепиши модуль авторизации на JWT"
16
17
 
17
18
  ## Зачем MMA?
18
19
 
19
- Большинство AI-агентов для кода (Devin, Cursor, Copilot) завязаны на облачные модели или дорогие API. MMA работает иначе:
20
+ Большинство агентов (Devin, Cursor, Copilot) завязаны на облачные модели или дорогие API — и предполагают, что задача про код. MMA работает иначе: это **общий** харнесс, где программирование — лишь один из сценариев.
20
21
 
21
22
  - **Создан для моделей 9B** — оптимизирован под Qwen3.5-9B на обычном железе
22
23
  - **Не требует облака** — полностью работает офлайн с любым OpenAI-совместимым бэкендом
@@ -32,16 +33,22 @@ mma "перепиши модуль авторизации на JWT"
32
33
  - **Управление бюджетом контекста** — динамический бюджет под модель, sliding-window compaction с извлечением фактов/решений/ошибок
33
34
  - **Детекция галлюцинаций** — фактическая (пути файлов), согласованности (откат решений), уверенности (короткие/повторяющиеся ответы)
34
35
  - **Гарантии выполнения** — авто-планирование через LLM (без keyword-эвристик), stuck-detection, off-track предупреждения, авто-продвижение плана
35
- - **Agent-Level MoE** — Router + экспертные сабагенты с фильтрацией инструментов, изолированным контекстом, файловым скоупом и топологическим параллельным выполнением
36
- - **33 инструмента** — файловая система, шелл, веб-поиск/fetch, браузер (Playwright), планирование, взаимодействие, сабагенты, MCP, пайплайны
37
- - **19 модулей** — скиллы, плагины, MCP-клиент, пайплайны, indexer проекта, постоянная память, контекст, сессии, браузер, выполнение, детекция галлюцинаций, апдейтер, профиль пользователя, LSP, сертификация моделей, безопасность, артефакты, процессы, ценообразование
36
+ - **Agent-Level MoE** — Router + экспертные сабагенты с фильтрацией инструментов, изолированным контекстом, файловым скоупом, топологическим параллельным выполнением, re-plan-циклом и машиночитаемой верификацией (`success_criteria`)
37
+ - **Инструменты** — ФС, шелл, веб-поиск/fetch, браузер (Playwright), планирование, память, сабагенты, MCP, пайплайны, LSP, процессы. Ядро (~26) + модульные `plan`/`todo`/`verify`/`lsp_check`/`project_map`/`set_thinking`/`scope_request`; `question`/`approve` включаются только в веб-режиме
38
+ - **24 модуля** — скиллы, плагины, MCP, пайплайны, indexer, память, контекст, сессии, браузер, выполнение, детекция галлюцинаций, апдейтер, профиль пользователя, LSP, сертификация моделей, безопасность, артефакты, процессы, ценообразование, онбординг, провайдеры, reasoning, setup, webui
39
+ - **Веб-режим `mma web`** — браузерный чат (SSE + REST) на том же ядре и сессиях, без TTY; первый запуск открывает мастер настройки прямо в браузере
40
+ - **Онбординг** — `mma setup` (мастер провайдера), `mma meet` («Познакомимся»: интервью в глобальную память), `mma agents init` (генерация/улучшение `AGENTS.md`)
38
41
  - **MCP-клиент** — подключение к любому MCP-серверу (stdio или SSE)
39
- - **Модуль безопасности** — включён по умолчанию (balanced): валидация bash-команд, path/network-политики, сканирование контента, шифрование сессий, audit-лог
42
+ - **Модуль безопасности** — включён по умолчанию (balanced): валидация bash-команд, path/network-политики (SSRF), сканирование контента, шифрование сессий, audit-лог; always-on защита путей (`.git/`, `.env*`, ключи)
40
43
  - **LSP-диагностика** — TypeScript/CSS/HTML серверы: проверка типов после каждой правки и по запросу
41
44
  - **YAML-пайплайны** — DAG-движок с параллельными волнами, зависимостями и повторными попытками
42
- - **Управление сессиями** — постоянные JSONL-сессии с REPL-командами
43
- - **Карта проекта** — автоматический обход файлов + извлечение экспортов + дисковый кэш
45
+ - **Промпт-кешинг** — cache-подсказки (llama.cpp / OpenAI / OpenRouter) + метрики hit-rate и экономии (`⟡` cache line)
46
+ - **Context log** — `--context-log` пишет весь уходящий в модель контекст в git-подобный diff (`context.diff`)
47
+ - **Управление сессиями** — постоянные JSONL-сессии, REPL-команды, `mma session export`
48
+ - **Карта проекта** — обход файлов + извлечение экспортов + дисковый кэш с валидацией свежести (signature)
49
+ - **Единый `/config`** — реестр настроек с валидацией + сахар `/model`, `/provider`, `/context`, `/reasoning`, `/verbose`
44
50
  - **i18n** — все строки интерфейса через `t()`, включены английский и русский
51
+ - **Вывод без гонок** — single-writer `OutputChannel`: никаких прямых `console.*`/stdout вне CLI и машинного JSON
45
52
  - **8K–120K контекст** — адаптируется под любое контекстное окно модели
46
53
  - **Без TUI** — минимальный CLI + REPL с markdown→ANSI форматированием
47
54
  - **llama.cpp / Jinja proven** — проверено на граничных случаях Jinja-шаблонов (system-first, streaming fallback, явный `stream: false`)
@@ -134,21 +141,29 @@ bun run mma model cert-status <name> # Статус сертификации
134
141
  bun run mma model cert-list # Все сертификации
135
142
  bun run mma model uncertify <name> # Отозвать сертификацию
136
143
  bun run mma provider list # Список провайдеров
144
+ bun run mma provider add local --url http://localhost:1234/v1 # Добавить провайдера
137
145
  bun run mma provider use opencode-zen # Хостед-провайдер (zen / go)
138
- bun run mma context # Токены/бюджет контекста
146
+ bun run mma context # Токены/бюджет контекста (--system/--reserve)
147
+ bun run mma map [summary|refresh|find] # Карта проекта
148
+ bun run mma usage # Баланс провайдера (OpenRouter /key, /credits)
139
149
  bun run mma security status # Текущая конфигурация безопасности
140
150
  bun run mma security policies # Доступные политики (strict/balanced/permissive)
141
151
  bun run mma security set-policy strict # Применить политику
142
152
  bun run mma plugins list # Загруженные плагины (--all — включая встроенные)
143
153
  bun run mma session list # Список сессий
144
154
  bun run mma session show <id> # Детали сессии
155
+ bun run mma session export <id> # Экспорт сессии (--format md|json|jsonl, --out -)
145
156
  bun run mma session delete <id> # Удалить сессию
157
+ bun run mma web [--port N] [--host H] [--token T] [--no-open] # Браузерный чат
146
158
 
147
159
  # Флаги одноразового запуска
148
160
  bun run mma "<prompt>" --json # Машиночитаемый JSON-результат
149
161
  bun run mma "<prompt>" -d <dir> # Рабочая директория агента
150
162
  bun run mma "<prompt>" --no-agents-md # Не грузить AGENTS.md в системный промпт
151
163
  bun run mma "<prompt>" --exit-on-complete # Выйти после первого финального ответа
164
+ bun run mma "<prompt>" --context-log # Записать контекст в <session>/context.diff
165
+ bun run mma "<prompt>" --verbose # Подробный вывод (уровень verbosity)
166
+ bun run mma "<prompt>" --reasoning <auto|low|medium|high|max>
152
167
  ```
153
168
 
154
169
  ### REPL
@@ -166,10 +181,16 @@ bun run dev
166
181
  | `/rename <name>` | | Переименовать текущую сессию |
167
182
  | `/delete <id>` | `/rm` | Удалить сессию |
168
183
  | `/model [name]` | | Показать/сменить модель |
169
- | `/provider` | | Показать/сменить провайдера |
184
+ | `/provider [list\|use <name>\|add …]` | | Провайдеры (список/переключить/добавить) |
170
185
  | `/status` | | Статус: модель, плагины, сессия, стоимость |
186
+ | `/config [<ключ> [<значение>]]` | | Единый реестр настроек (`/config reset <key>`) |
171
187
  | `/context` | `/ctx` | Токены и бюджет контекста |
172
- | `/reasoning` | | Переключить показ reasoning |
188
+ | `/reasoning [show\|hide\|level <...>]` | | Показ reasoning / уровень (`auto`/`low`/…/`max`) |
189
+ | `/verbose [quiet\|normal\|verbose]` | | Уровень детализации вывода |
190
+ | `/memory [get\|set\|unset\|find\|forget]` | | Просмотр/правка памяти |
191
+ | `/meet` | `/познакомимся` | Онбординг-интервью (пишет в глобальную память) |
192
+ | `/map [summary\|refresh\|find]` | | Карта проекта |
193
+ | `/export [md\|json\|jsonl]` | | Экспорт текущей сессии |
173
194
  | `/image <path\|url>` | | Прикрепить изображение (или Ctrl+V) |
174
195
  | `/run <cmd>` | | Выполнить shell-команду без агента |
175
196
  | `/sysprompt` | | Показать системный промпт |
@@ -178,11 +199,24 @@ bun run dev
178
199
  | `/plugins` | | Плагины (`--all` — все) |
179
200
  | `/lsp [status\|restart\|check <path>]` | | Диагностика LSP |
180
201
  | `/reload` | | Перечитать конфиг и модули |
181
- | `/clear` | | Очистить контекст сессии |
202
+ | `/clear` | | Очистить экран (контекст сессии сохраняется) |
182
203
  | `/exit` | | Выйти из REPL |
183
204
 
184
205
  Горячие клавиши: `Esc Esc` — прервать агента; `Ctrl+C` — выход; `Ctrl+V` — вставить изображение из буфера обмена; `Shift+Enter` — перенос строки.
185
206
 
207
+ ### Веб-режим (`mma web`)
208
+
209
+ Тот же `bootstrap()` / `Agent` / сессии, но в браузере (SSE + REST, без TTY):
210
+
211
+ ```bash
212
+ bun run mma web # откроет браузер
213
+ bun run mma web --port 8080 --host 127.0.0.1 --no-open
214
+ ```
215
+
216
+ - **Первый запуск** с пустым конфигом: мастер прямо в браузере (язык → провайдер → ключ → модель → контекст → безопасность → проверка). Чат закрыт, пока настройка не завершена (сервер отвечает `409 setup_required`).
217
+ - Панель настроек: модели/провайдеры, контекст, reasoning, память, скиллы, плагины/MCP, безопасность; slash-команды (`/help`, `/status`, `/config`, `/model`, `/provider`, `/memory`, …) работают и в браузере.
218
+ - Env: `MMA_WEBUI_PORT`, `MMA_WEBUI_HOST`, `MMA_WEBUI_TOKEN`, `MMA_WEBUI_OPEN=0`, `MMA_WEBUI_MAX_CLIENTS`.
219
+
186
220
  ### Разработка
187
221
 
188
222
  ```bash
@@ -190,27 +224,34 @@ bun run mma "почини страницу логина" # Запуск аг
190
224
  bun run dev # Watch-режим (авто-перезапуск при изменениях)
191
225
  bun run build:prod # Сборка для публикации
192
226
  bun test # Запуск тестов
193
- bun run typecheck # Проверка типов
227
+ bun run typecheck # Проверка типов (tsc --noEmit)
228
+ bun run lint # Линтер Biome
229
+ bun run quality # typecheck + lint + fallow (dead-code gate)
194
230
  ```
195
231
 
196
232
  ---
197
233
 
198
234
  ## Конфигурация
199
235
 
200
- 3-слойный конфиг: `defaults.ts` → `~/.mma/config.json` (глобальный) → `.mmrc` (проект) + переменные окружения.
236
+ 3-слойный конфиг: `defaults.ts` → `~/.mma/config/*.json` (глобальные домены; legacy `~/.mma/config.json` мигрируется автоматически, с бэкапом) → `.mmrc` (проект) + переменные окружения `MMA_*`.
201
237
 
202
- Ключевые опции (полный список в `src/config/defaults.ts`):
238
+ Ключевые опции (полный список в `src/config/defaults.ts`, просмотр/правка — `/config` в REPL или `mma config show`):
203
239
 
204
240
  | Опция | По умолчанию | Описание |
205
241
  |-------|-------------|----------|
206
242
  | `model` | `qwen/qwen3.5-9b` | Имя модели для бэкенда |
207
- | `provider.baseUrl` | `http://localhost:1234/v1` | URL OpenAI-совместимого API |
243
+ | `provider.baseUrl` | `http://localhost:1234/v1` | URL OpenAI-совместимого API (или `provider.entries[]` для failover) |
208
244
  | `contextWindow` | `32768` | Контекстное окно модели в токенах |
245
+ | `contextBudget.systemPrompt` | `0.10` | Доля окна под системный промпт |
246
+ | `contextBudget.responseReserve` | `0.15` | Резерв под ответ |
209
247
  | `autoPlan` | `true` | Авто-создание планов для многошаговых задач |
248
+ | `reasoning.mode` | `auto` | `auto` (политика) или фиксированный `none`…`max` |
249
+ | `reasoning.baseline` | `low` | Стартовый уровень в auto (растёт на сигналах) |
210
250
  | `maxToolIterations` | `1000` | Макс. вызовов инструментов за запуск |
211
251
  | `stuckThreshold` | `6` | Итераций без прогресса до stuck-detection |
212
252
  | `moe.enabled` | `false` | Включить Agent-Level MoE |
213
253
  | `security.enabled` | `true` | Модуль безопасности (balanced-политика по умолчанию) |
254
+ | `ui.verbosity` | `normal` | `quiet` / `normal` / `verbose` |
214
255
  | `locale` | `en` | Язык интерфейса (`en` / `ru`) |
215
256
  | `logLevel` | `info` | Уровень логирования |
216
257
 
@@ -236,29 +277,33 @@ bun run typecheck # Проверка типов
236
277
  ## Архитектура
237
278
 
238
279
  ```
239
- CLI (main.ts → commands.ts / repl.ts / setup.ts)
240
- → Core (agent.ts state machine → prompt-builder.ts)
241
- → LLM Layer (provider.ts → openai-compat.ts → response.ts → token-counter.ts)
242
- → Tools (registry.ts → executor.ts → 33 tools)
280
+ CLI (main.ts → commands.ts / repl.ts / setup.ts / web-command.ts)
281
+ → Core (agent.ts state machine → bootstrap.ts buildSystemInfo)
282
+ → LLM Layer (provider.ts → openai-compat.ts → cache-usage.ts → token-counter.ts)
283
+ → Tools (registry.ts → executor.ts → core tools + module-registered)
243
284
  → Modules (skills, plugins, mcp, pipelines, indexer, memory, context, session,
244
285
  hallucination, execution, updater, user-profile, browser, lsp,
245
- security, certification, artifacts, processes, pricing)
286
+ security, certification, artifacts, processes, pricing,
287
+ onboarding, providers, reasoning, setup, webui)
288
+ → Output (OutputBus → OutputChannel single writer; JSON/SSE sinks)
246
289
  ```
247
290
 
248
291
  ### Структура проекта
249
292
 
250
293
  ```
251
294
  src/
252
- ├── core/ # Цикл агента, сборщик промптов, типы
253
- ├── llm/ # Абстракция провайдера, OpenAI-совместимость, стриминг, подсчёт токенов
254
- ├── tools/ # 33 инструмента с реестром, исполнителем, скоуп-гардами
255
- ├── modules/ # 19 модулей (скиллы, плагины, mcp, пайплайны, indexer, память, контекст,
295
+ ├── core/ # Цикл агента, bootstrap/системный промпт, WebHost, типы
296
+ ├── llm/ # Абстракция провайдера, OpenAI-совместимость, стриминг, токены, кеш-метрики
297
+ ├── tools/ # Ядро инструментов (~26) + реестр, исполнитель, скоуп-гарды
298
+ ├── modules/ # 24 модуля (скиллы, плагины, mcp, пайплайны, indexer, память, контекст,
256
299
  │ # сессии, галлюцинации, выполнение, апдейтер, профиль, браузер, LSP,
257
- │ # безопасность, сертификация моделей, артефакты, процессы, ценообразование)
258
- ├── cli/ # Точка входа, команды, REPL, мастер настройки
259
- ├── config/ # 3-слойный конфиг: defaults → глобальный → проект
300
+ │ # безопасность, сертификация, артефакты, процессы, цены, онбординг,
301
+ │ # провайдеры, reasoning, setup, webui)
302
+ ├── output/ # Single-writer OutputChannel + OutputBus, JSON/SSE sinks
303
+ ├── cli/ # Точка входа, команды, REPL, мастера (setup), `mma web`
304
+ ├── config/ # 3-слойный конфиг: defaults → домены → проект
260
305
  ├── i18n/ # en.json + ru.json + t()
261
- ├── ui/ # Markdown→ANSI форматтер
306
+ ├── ui/ # Markdown→ANSI форматтер, renderer, line-editor
262
307
  └── logger/ # Структурированный логгер с уровнями и дочерними логгерами
263
308
  ```
264
309
 
@@ -281,8 +326,8 @@ src/
281
326
  | `bash` | Выполнение shell-команд |
282
327
  | `subagent` | Изолированный сабагент со скоупом |
283
328
  | `web_search` | Поиск в интернете |
284
- | `web_fetch` | Загрузка веб-страницы (15K символов, 15s таймаут) |
285
- | `web_browse` | JS-less HTTP fetch страницы (без движка браузера) |
329
+ | `web_fetch` | Загрузка веб-страницы → markdown (~5K символов, 15s таймаут, SSRF-safe редиректы) |
330
+ | `web_browse` | JS-less HTTP fetch страницы без движка (до 3K символов) |
286
331
  | `browser` | Браузер на Playwright (клик, ввод, скролл, скриншот) |
287
332
  | `plan` | Создание/обновление/отмена планов |
288
333
  | `todo` | Отслеживание задач |
@@ -295,12 +340,16 @@ src/
295
340
  | `download_file` | Скачать бинарный файл по URL на диск |
296
341
  | `process_list` / `process_log` / `process_kill` | Управление фоновыми процессами |
297
342
  | `remember` / `recall` | Постоянная память агента |
298
- | `attach_image` | Прикрепить изображение (файл/URL/буфер обмена) |
343
+ | `attach_image` | Прикрепить изображение (файл/URL/буфер обмена; в контекст — при vision-модели) |
299
344
  | `enable_tools` | Включить скрытые группы инструментов на ходу |
300
345
  | `lsp_check` | Диагностика LSP (TypeScript/CSS/HTML) |
301
346
  | `verify` | Верификация шагов плана |
347
+ | `session_info` | Сведения о текущей сессии (id, модель, контекст, сообщения) |
348
+ | `set_thinking` | Сменить уровень reasoning для следующих итераций |
349
+ | `scope_request` | Запросить расширение файлового скоупа сабагента |
350
+ | `question` / `approve` | Интерактивные меню (регистрируются только в веб-режиме `mma web`) |
302
351
 
303
- > Примечание: интерактивные тулзы `question`/`approve` выключены — модель задаёт вопросы текстом в ответе.
352
+ > Примечание: в терминале интерактивные тулзы `question`/`approve` не регистрируются — модель задаёт вопросы текстом. В `mma web` они работают через SSE + `POST /api/answer`.
304
353
 
305
354
  ---
306
355
 
@@ -336,11 +385,12 @@ bun run mma "<промпт>" --no-agents-md --exit-on-complete -d <путь-к-
336
385
  2. **Маленькие файлы, одна ответственность** — каждый файл делает одно дело
337
386
  3. **KISS state machine** — никаких god-объектов, никакой вложенности if-else в цикле агента
338
387
  4. **Изоляция ошибок плагинов** — падение одного плагина не останавливает другие
339
- 5. **Безопасность путей** — все файловые инструменты проверяют `targetPath.startsWith(baseDir)`
340
- 6. **TDD** — сначала тест, потом реализация, потом коммит
341
- 7. **Никаких захардкоженных строк** — весь текст через `t()` i18n
342
- 8. **Никакого keyword matching** — LLM решает когда планировать, не эвристики
343
- 9. **Без TUI** — минимальный CLI + REPL с markdown→ANSI
388
+ 5. **Безопасность путей** — файловые инструменты резолвят `realpath` (защита от symlink/junction вне `baseDir`) и блокируют always-on защищённые пути (`.git/`, `.env*`, ключи)
389
+ 6. **Single-writer вывод** — всё через `OutputChannel`; прямой stdout только в CLI и машинном JSON
390
+ 7. **TDD** — сначала тест, потом реализация, потом коммит
391
+ 8. **Никаких захардкоженных строк** — весь текст через `t()` i18n
392
+ 9. **Никакого keyword matching** — LLM решает когда планировать, не эвристики
393
+ 10. **Без TUI** — минимальный CLI + REPL с markdown→ANSI
344
394
 
345
395
  ---
346
396
 
package/bin/mma.mjs CHANGED
@@ -1,41 +1,41 @@
1
- #!/usr/bin/env node
2
- import { spawnSync } from "node:child_process";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { dirname, join } from "node:path";
5
-
6
- // MMA is designed to run under Bun: clipboard image paste relies on
7
- // `Bun.Image.fromClipboard()` (Node has no clipboard API), and on Windows a
8
- // bare `npx.cmd` shim can only be spawned reliably through Bun's child_process
9
- // (under Node it throws `spawn EINVAL`). The global npm shim (`mma` /
10
- // `mma.cmd`) launches this file under Node, so re-execute ourselves under Bun
11
- // when it is available. The `bun run mma` path already runs under Bun and
12
- // skips the respawn.
13
- const runningUnderBun =
14
- typeof globalThis.Bun !== "undefined" &&
15
- typeof globalThis.Bun?.version !== "undefined";
16
-
17
- if (process.env.MMA_DEBUG_RUNTIME) {
18
- console.error(
19
- "[mma] runtime=" +
20
- (runningUnderBun ? "bun " + globalThis.Bun.version : "node " + process.version),
21
- );
22
- }
23
-
24
- if (!runningUnderBun) {
25
- try {
26
- const res = spawnSync("bun", [process.argv[1], ...process.argv.slice(2)], {
27
- stdio: "inherit",
28
- windowsHide: true,
29
- });
30
- if (res.error) throw res.error;
31
- process.exit(res.status ?? 0);
32
- } catch {
33
- // `bun` is not on PATH — fall back to the Node import path (clipboard
34
- // paste and LSP will degrade, but the agent still works).
35
- }
36
- }
37
-
38
- const __dirname = dirname(fileURLToPath(import.meta.url));
39
- const main = join(__dirname, "..", "dist", "main.js");
40
-
41
- await import(pathToFileURL(main).href);
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { dirname, join } from "node:path";
5
+
6
+ // MMA is designed to run under Bun: clipboard image paste relies on
7
+ // `Bun.Image.fromClipboard()` (Node has no clipboard API), and on Windows a
8
+ // bare `npx.cmd` shim can only be spawned reliably through Bun's child_process
9
+ // (under Node it throws `spawn EINVAL`). The global npm shim (`mma` /
10
+ // `mma.cmd`) launches this file under Node, so re-execute ourselves under Bun
11
+ // when it is available. The `bun run mma` path already runs under Bun and
12
+ // skips the respawn.
13
+ const runningUnderBun =
14
+ typeof globalThis.Bun !== "undefined" &&
15
+ typeof globalThis.Bun?.version !== "undefined";
16
+
17
+ if (process.env.MMA_DEBUG_RUNTIME) {
18
+ console.error(
19
+ "[mma] runtime=" +
20
+ (runningUnderBun ? `bun ${globalThis.Bun.version}` : `node ${process.version}`),
21
+ );
22
+ }
23
+
24
+ if (!runningUnderBun) {
25
+ try {
26
+ const res = spawnSync("bun", [process.argv[1], ...process.argv.slice(2)], {
27
+ stdio: "inherit",
28
+ windowsHide: true,
29
+ });
30
+ if (res.error) throw res.error;
31
+ process.exit(res.status ?? 0);
32
+ } catch {
33
+ // `bun` is not on PATH — fall back to the Node import path (clipboard
34
+ // paste and LSP will degrade, but the agent still works).
35
+ }
36
+ }
37
+
38
+ const __dirname = dirname(fileURLToPath(import.meta.url));
39
+ const main = join(__dirname, "..", "dist", "main.js");
40
+
41
+ await import(pathToFileURL(main).href);