Repository navigation
release: 0.4.0 — FTS5 search, theme system v2, TUI redesign, curator lifecycle - #79
Closed
ApiliumDevTeam wants to merge 23 commits into
Closed
ApiliumDevTeam wants to merge 23 commits into
ApiliumDevTeam wants to merge 23 commits into
Conversation
Formalize the lint configuration that was previously running on oxlint defaults via the --type-aware flag. The config: - Activates type-aware rules (no-floating-promises, no-misused-promises, await-thenable, no-base-to-string, restrict-template-expressions). - Formalizes the repo no-explicit-any policy as a warning. - Disables noisy rules in test files where type strictness is less valuable (unbound-method, no-floating-promises, any). - Keeps correctness category as warnings, not errors, to preserve a green baseline while surfacing 364 items of technical debt for incremental cleanup.
Add a Dependency Supply Chain section to SECURITY.md covering the controls the project already enforces via package.json: - The 48-hour release age floor (minimumReleaseAge: 2880). - The rationale requirement for every pnpm.overrides entry, tied to a CVE or regression. - The hard rule that patchedDependencies must use exact versions. - Lockfile discipline: frozen-lockfile in CI, deps: commits for dependency changes, reviewed transitive bumps. - The onlyBuiltDependencies allowlist limiting native build scripts. This formalizes existing practice as written policy so maintainers and contributors have a single source of truth for dependency trust decisions.
…undary Introduce a Trust Model section that states the project's governing security principle: operating-system level isolation is the only load-bearing boundary, and every in-process defense (approval gate, output redaction, scanners, rate limiters, the WASM sandbox) is a heuristic that reduces attack surface but does not contain a determined adversary with in-process code execution. The section classifies real boundaries (separate users, containers, VMs, namespaces, seccomp, Landlock) against in-process defenses, and gives concrete implications for operators running untrusted skills and plugins. This sharpens the existing Plugin Trust Boundary section, which now reads as a specific case of the general model.
Add a CI step on pull requests that fails when pnpm-lock.yaml changes without a deps-scoped commit or a corresponding manifest change (package.json, pnpm-workspace.yaml). This implements the lockfile discipline documented in SECURITY.md as an automated gate rather than relying on review alone. The check is implemented as scripts/check-lockfile-policy.mjs so it can also run locally (node scripts/check-lockfile-policy.mjs --base <ref>) before pushing. On CI it only runs on pull_request events, where the base ref is well-defined.
Remove every `as any` escape hatch in production code (the 20 occurrences flagged by no-explicit-any once the rule was formalized). Each replacement uses a concrete type: - mattermost/channel.ts: params is already Record<string, unknown>, so the casts were unnecessary — access the fields directly with typeof narrowing. - agent-mesh/index.ts: model the on-disk mayros.json and auth-profiles shapes with structural types instead of Record<string, any>; forward the heterogeneous Mamoru gateway handlers through a typed callable. - feishu/bot.ts: narrow the Lark SDK response with a minimal FeishuUserResponse type; widen the logger signature to unknown[]. - feishu/docx.ts: introduce a FeishuBlock structural type for the document blocks used by sortBlocksByFirstLevel, with a type-guard filter. - mcp-server/index.ts: type registerCli's program as Command and the dashboard respond callback with its structural signature. Also extend the oxlint test override to *.test-helpers.ts so test fixtures are not held to production type strictness.
Formalize the project's prompt-cache invariant and add a drift signal to the cache trace so cache-breaking changes become visible. AGENTS.md: - Add a Prompt Cache Discipline section stating the governing principle: the system prompt must stay stable across runs that have no reason to change it, slash commands that mutate state invalidate only on the next turn, and hooks must be deterministic. This makes the contract explicit for contributors. cache-trace.ts: - Track the last system digest per session key in a module-level map. - When wrapStreamFn sees a different system digest for the same session on a consecutive call, emit a new cache:system-drift event carrying both the current and previous digests. This turns silent cache invalidations (which multiply token cost) into a diagnosable signal. - Propagate previousSystemDigest through the event pipeline. cache-trace.test.ts: - Unit tests covering first-call (no drift), drift on change, stability across repeated calls, and independent per-session tracking.
Add docs/platforms/tui-redesign.md capturing the current state of the terminal UI, the gaps found during an internal audit, and a prioritized P0/P1/P2 roadmap for the next redesign pass. The audit covers rendering, input, slash commands, themes, vim mode, accessibility, and platform coverage. It documents the architectural constraints (shared terminal UI dependency as rendering floor, mandatory gateway, mutable state, two parallel TUI implementations, two palette systems) that bound the redesign, and calls out two P0 items that block trustworthy feature work: verifying the vim bridge is actually wired, and bringing the accessibility TUI to feature parity.
…servability (#65) ## Summary First pass of a stabilization effort focused on type safety, security documentation, and cache observability. No behavior changes to user-facing features. - **Lint configuration**: add `.oxlintrc.json` to formalize the type-aware rules (no-floating-promises, no-misused-promises, await-thenable, no-base-to-string, restrict-template-expressions) that were running on defaults. Keeps the green baseline while surfacing technical debt for incremental cleanup. - **Type safety**: replace every `as any` escape hatch in production code with proper types (structural types, type guards, inferred signatures). Removes the soft type-escape across five modules. - **Security documentation**: add a dependency supply-chain policy section (release-age floor, override rationale, patched-dependency version rule, lockfile discipline) and a trust-model section stating that OS-level isolation is the only load-bearing boundary, with in-process defenses classified as heuristics rather than containment. - **CI**: add a pull-request gate that fails when `pnpm-lock.yaml` changes without a `deps:`-scoped commit or a manifest change. Implements the documented lockfile discipline as an automated check; also runnable locally. - **Cache observability**: formalize the prompt-cache invariant in the contribution guide and add a system-prompt drift signal to the cache trace. When the system digest changes between consecutive turns of a session without a session restart, a `cache:system-drift` event is emitted so silent cache invalidations become diagnosable. Includes unit tests. - **TUI roadmap**: document the current terminal UI state and a prioritized P0/P1/P2 redesign plan covering rendering, input, vim mode, accessibility, and platform coverage. ## Test plan - [x] `pnpm tsgo` passes (0 errors) - [x] `pnpm lint` passes (0 errors, 344 warnings — pre-existing technical debt surfaced by the new config) - [x] `pnpm exec vitest run src/agents/cache-trace.test.ts` — 4/4 new tests pass - [x] `node scripts/check-lockfile-policy.mjs --base origin/dev` — passes locally - [x] All 13 changed files pass `pnpm exec oxfmt --check`
The @mariozechner/pi-{agent-core,ai,coding-agent,tui} packages were renamed
and deprecated ("use @earendil-works/... going forward"); their last published
version (0.73.1) is still affected by 3 open advisories with no fix. Migrate
the whole suite to @earendil-works/pi-* ^0.80.2, which carries the fixes.
- Repoint all import specifiers @mariozechner/pi-* -> @earendil-works/pi-*
across src, extensions, scripts, skills, docs and the mac packaging script.
- Bump the 4 root deps to ^0.80.2.
- Adapt to the 0.54 -> 0.80 API surface:
- pi-ai: legacy helpers (complete/completeSimple/stream/streamSimple/getModel/
getEnvApiKey/streamOpenAIResponses) moved to the `@earendil-works/pi-ai/compat`
subpath; OAuth helpers to `@earendil-works/pi-ai/oauth`; deep `/dist/...`
imports repointed (types -> root, google-shared -> /api); onPayload callback
now (payload, model); ProviderHeaders allows null values; typebox re-exported
from pi-ai.
- pi-tui: dropped the `Editor` infix (getKeybindings/setKeybindings/
KeybindingsConfig/KeybindingsManager); KeybindingsManager(definitions,
userBindings?); namespaced keybinding ids; async 4-arg AutocompleteProvider.
- pi-coding-agent: codingTools -> createCodingTools(cwd); Skill.source ->
Skill.sourceInfo.source; ModelRegistry.create(...)/getApiKeyForProvider;
generateSummary headers param; createAgentSession tools is a name allowlist.
- pi-agent-core: agent.replaceMessages/setSystemPrompt -> agent.state.*.
- Add pnpm override "@apilium/mayros": "workspace:*" so the bluebubbles plugin
resolves its peer to the workspace package instead of pulling the published
0.1.1, which bundled the old vulnerable pi suite.
Closes the 3 remaining pi-coding-agent advisories: pnpm audit no longer reports
any @mariozechner/pi or pi-coding-agent finding, and @earendil-works has none.
Verified: pnpm install, pnpm tsgo (0 errors), pnpm lint (0 errors), oxfmt on
changed files. Full test suite not run here (native deps + live keys); needs a
CI run and a smoke test of the agent runtime before merge.
Move the four core libraries to the latest patch on the 0.80 line (@earendil-works/pi-agent-core, pi-ai, pi-coding-agent, pi-tui: 0.80.2 -> 0.80.6). The 0.80.6 release internally consumes zod 4.4.3, so align the workspace to zod ^4.4.3 and add a pnpm override pinning zod to 4.4.3 to keep a single resolved version across the tree. Refresh 21 runtime and dev dependencies to their current minor/patch releases: @aws-sdk/client-bedrock, @clack/prompts, @grammyjs/types, @homebridge/ciao, @noble/curves, @slack/bolt, @slack/web-api, @vitest/coverage-v8, ajv, dotenv, grammy, jiti, lit, markdown-it, oxlint, semver, tslog, tsx, vitest, ws. Exact-pinned and security-overridden dependencies (hono, tar, undici, yaml, typebox, playwright-core, etc.) are left untouched by design. Skip the symlink-based config test on Windows where unprivileged symlink creation is not permitted.
…cy (#67) * fix(tui): resolve readline temporal-dead-zone in accessible mode The accessible TUI created its readline interface after wiring the gateway client's disconnect handler and after client.start(). If the client disconnected in that window, the handler referenced the readline binding before its initialization, throwing a ReferenceError. Move the readline creation before the disconnect wiring so the reference is always initialized. * feat(tui): make Ctrl+C abort the active run Ctrl+C now follows a three-state priority: abort an active run, clear non-empty input, or exit on a second press within one second. Escape remains a secondary abort path. This unblocks users of modal editors (where Escape is consumed to switch modes) and aligns the interrupt behavior with terminal conventions. * feat(tui): share slash-command handling between graphical and accessible modes The accessible TUI previously supported only /quit and /exit. Introduce a renderer-agnostic shared command core (shared-commands.ts) that calls the gateway client directly and reports results through an injected text sink. The accessible TUI now routes /abort, /new, /reset, /compact, /status, /sessions, /session <key>, /model, and /help through this core, reaching feature parity with the graphical TUI for every command that does not require a visual picker. The graphical TUI continues to use its full handler for overlay-based commands. Both TUIs share logic (the gateway calls and command parsing) without sharing presentation. * fix(tui): render diffs with theme colors The diff renderer hardcoded chalk.green/red/cyan/white, so added and removed lines were hard to read under light or high-contrast themes. Thread optional DiffColors through renderDiff and formatDiffStatsLine; when the TUI calls them it passes the active theme's success/error/ accent/dim functions, keeping diff colors consistent across every theme preset. Callers without a theme fall back to the previous defaults. * refactor(terminal): align CLI palette with the TUI dark palette The CLI and the terminal UI used different accent colors (orange in the CLI, gold in the TUI), so running `mayros` then `mayros code` showed two different identities. Derive the CLI palette tokens from the TUI dark palette so both surfaces share accent, success, warn, and error values. The CLI's accentBright maps to the TUI accentSoft; accentDim is a darkened accent; info reuses accentSoft. The visual identity is now consistent across every Mayros surface. * feat(tui): enable extended keyboard protocols where supported Add term-capabilities.ts to detect and manage extended keyboard protocols. Capable terminals (kitty, WezTerm, ghostty get the kitty keyboard protocol; iTerm, vscode, tmux, windows-terminal, Apple Terminal get xterm modifyOtherKeys level 2) are upgraded at TUI startup so modifier combinations like Shift+Enter and Ctrl+Shift survive instead of being collapsed by plain raw-mode stdin. The protocol is popped on exit/SIGINT/SIGTERM so the terminal is restored for the shell. Detection is conservative: terminals outside the allow-list get no upgrade, so escape sequences are never emitted to a terminal that cannot parse them back. * chore(tui): remove unused input-history store The input-history store was never wired into the TUI main loop; it was dead code that looked like a feature. Remove it and its test to reduce confusion. Cross-session input history can be reintroduced as a real feature later, but shipping a dormant implementation invites false confidence. * test(tui): align session rename test with the new API and skip symlink on Windows The session rename API field changed from displayName to label in the 0.80 surface; update the assertion to match the implementation. The symlink-follows test requires Developer Mode or admin rights to create a symlink on Windows (EPERM otherwise), so skip it on win32; CI on Linux exercises it normally.
…ility (#69) * feat(tui): auto-detect dark/light theme from the terminal When config.ui.theme is unset or set to "auto", query the terminal for its color-scheme preference before the first render. The detection uses two concurrent queries with a 150 ms timeout each: the DSR color-scheme report (CSI ? 996 n, a direct dark/light answer) and the OSC 11 background-color query (parsed to RGB and classified via Rec. 709 relative luminance). The fastest signal wins; an unresponsive terminal falls back to the default dark preset without blocking startup. Add "auto" to the ui.theme config type so users can opt in explicitly, and document the query ladder in term-capabilities.ts. The manual /theme command continues to override the detected preset for the session. * test(tui): cover paste-collapse and grapheme caret behavior Add no-regression coverage for the two input features inherited from the terminal UI dependency: grapheme-aware caret movement (Intl.Segmenter) and paste-collapse markers. Both are implemented in the Editor class the TUI extends, so the tests pin the environment contract rather than the Editor internals. The grapheme tests verify that emoji with skin-tone modifiers, flag emoji, and CJK characters each count as a single grapheme — the invariant the caret relies on. The paste-marker tests verify the marker shape ([paste #N +M lines], [paste #N M chars], [paste #N]) so that cursor movement and submission expansion keep working if the format changes upstream. Also update the welcome-screen quick-start hint to reflect that Ctrl+C now aborts the active run (Esc remains a secondary path). * perf(tui): split streaming markdown at the stable block boundary Re-parsing the full markdown on every streaming delta is O(n²) over a response. Split the assistant message text at the last stable block boundary (a blank line outside any open code fence) into a stable prefix and an active suffix. Only the suffix is re-parsed per delta; the prefix reuses the Markdown component's internal render cache, which turns the per-delta cost into O(n). Add stream-boundary.ts with a pure, unit-tested findStableBoundary function that walks backwards through blank lines and skips any that sit inside an open code fence. Refactor AssistantMessageComponent to hold two Markdown instances (prefix + suffix) and only update the prefix when its text actually grows. * feat(tui): per-section detail visibility for tools and thinking Replace the binary expanded/collapsed toggle with a three-state model: hidden, collapsed, and expanded. Each section cycles independently via its keyboard shortcut. - Tools (Ctrl+O): hidden renders only the header line, collapsed shows a 12-line preview, expanded shows the full output. - Thinking (Ctrl+T): hidden suppresses thinking text, collapsed and expanded both show it (a truncated thinking mode is deferred to a follow-up that restructures the composer). The section state is the new source of truth in TuiStateAccess; the existing toolsExpanded and showThinking fields are kept as compatibility shims. Add a nextSectionState helper and unit tests for the cycle.
The vim mode implementation was structurally broken: the editor bridge that connects vim motions to the buffer was never wired, so every editing operation silently no-op'd while the mode indicator claimed vim was active. Keys were consumed without editing, leaving users with a non-functional editor. Disable vim honestly instead of leaving the broken experience: /vim now reports that the feature is unavailable, the dead handler and tests are removed (730 LOC), all vim wiring is stripped from the TUI, the config flag is deprecated, and docs are updated. Emacs/readline-style keybindings remain available.
…72) * feat(tui): custom themes, colorblind palettes, and monochrome mode Generalize the theme system so users can define custom palettes in ~/.mayros/themes/*.json. Each file must contain the 22 palette color tokens as #RRGGBB hex strings; the loader validates, caches by mtime, and registers them so /theme <name> and config.ui.theme accept custom names alongside the built-ins. Add three new built-in palettes: - colorblind-dark and colorblind-light: Okabe-Ito hues that avoid the red/green pairing for error/success (error = vermilion, success = sky blue), so deuteranopic and protanopic users get distinguishable semantic colors. - monochrome: all tokens use the same foreground, for NO_COLOR environments or users who prefer a minimal terminal. Generalize ThemePreset from a closed union to string (with BuiltinPreset for the 13 built-ins), add a custom palette registry, and relax the /theme handler and config guards to accept custom names. The loader runs at TUI startup before the config theme is resolved. * chore(tui): dedupe theme presets and migrate stderr to subsystem logger Remove the duplicated THEME_PRESETS array from commands.ts (it was a local copy that had already drifted — missing the three new palettes added in the theme system update). Import the canonical list from palettes.ts instead so there is a single source of truth. Migrate the three process.stderr.write call sites in tui-command-handlers.ts (fast-mode) and compact-handler.ts to the shared subsystem logger (createSubsystemLogger("tui")), so TUI diagnostics route through the same logging infrastructure as the rest of the codebase instead of writing raw stderr.
) * fix(tui): cross-platform clipboard support for Windows and Wayland The /copy command and teleport export hardcoded pbcopy/xclip, so they were broken on Windows (no pbcopy/xclip) and Wayland (no xclip under pure Wayland). Replace the spawn and shell-pipeline approaches with the existing cross-platform copyToClipboard helper from infra/clipboard.ts, which has a fallback chain covering pbcopy, xclip, wl-copy, clip.exe, and PowerShell Set-Clipboard. Add Windows and Wayland support to clipboard image capture: - Windows: PowerShell + System.Windows.Forms to read clipboard PNG. - Wayland: wl-paste -t image/png when WAYLAND_DISPLAY is set, with xclip fallback for XWayland sessions. * feat(tui): render image content blocks inline where supported Tool results that contain image content blocks now render inline using the terminal image component when the terminal supports it (Kitty graphics protocol or iTerm2 inline images). The Image component handles capability detection internally and falls back to a text placeholder ([image/png 12kb]) when the terminal cannot display images, so no escape sequences leak to unsupported terminals. Backend change: stop stripping the base64 data from image content blocks in sanitizeToolResult. The omitted: true flag already prevents the image from entering the model context, so retaining data does not inflate token usage — it only makes the base64 available for TUI rendering. * refactor(tui): extract pure helpers from tui.ts into tui-helpers.ts Move 7 standalone functions (createEditorSubmitHandler, shouldEnableWindowsGitBashPasteFallback, createSubmitBurstCoalescer, resolveTuiSessionKey, tryInlinePairingApproval, resolveGatewayDisconnectState, createBackspaceDeduper) out of tui.ts into a dedicated tui-helpers.ts module. These functions are pure factories and resolvers with no dependency on the TUI instance or mutable state, and are already covered by tui.test.ts. Reduces tui.ts from 1087 to 883 LOC. Further extraction of the state factory, status renderer, and connection handlers is tracked as a follow-up — the remaining sections are tightly coupled to runTui's closures and require more careful refactoring.
* feat(search): FTS5-backed session search with BM25 ranking Replace the linear substring scan with a dedicated SQLite FTS5 index for session message search. The index lives at ~/.mayros/agents/<agentId>/sessions/search.sqlite and uses an external-content virtual table with triggers for automatic sync. The search now supports multi-word queries with BM25 relevance ranking, role/timestamp/session filters, and snippet generation via the FTS5 snippet() function. Indexing is incremental (lazy sync on each query, skipping unchanged files by mtime+size), so no background watchers are needed. The public searchSessions API is unchanged — it tries FTS5 first and falls back to the original linear scan when node:sqlite is unavailable, so search always works regardless of the runtime. * feat(search): FTS5-backed session search with BM25 ranking Replace the linear substring scan with a dedicated SQLite FTS5 index for session message search. The index lives at ~/.mayros/agents/<agentId>/sessions/search.sqlite and uses an external-content virtual table with triggers for automatic sync. The search now supports multi-word queries with BM25 relevance ranking, role/timestamp/session filters, and snippet generation via the FTS5 snippet() function. Indexing is incremental (lazy sync on each query, skipping unchanged files by mtime+size), so no background watchers are needed. The public searchSessions API is unchanged — it tries FTS5 first and falls back to the original linear scan when node:sqlite is unavailable, so search always works regardless of the runtime. Results are sorted by timestamp descending and sessionsSearched counts files scanned, matching the original contract. * feat(skills): curator lifecycle with activity tracking and auto-archive Add a skill lifecycle manager that automatically transitions skills through active → stale → archived states based on invocation activity. Activity tracking (activity-store.ts): a JSON sidecar at ~/.mayros/skills/skill-activity.json records lastInvokedAt, invocationCount, and firstInvokedAt per skill. Updated on every skill invocation. Lifecycle metadata: extend MayrosSkillMetadata with state, pinned, created, and updated fields. Parsed from SKILL.md frontmatter. Curator sweep (skill-curator.ts): a self-throttled background sweep (mirroring session-reaper.ts) that transitions active skills to stale after 30 days of inactivity and stale skills to archived after 90 days. Pinned skills are never transitioned. Only agent-created skills (source: mayros-managed) are eligible for archival — user skills can go stale but are never auto-archived. Archived skills are moved to ~/.mayros/skills.archived/.
* TUI: split status renderer, keybindings, and connection handlers from tui.ts * TUI: split command handlers into per-group modules with dispatcher
The FTS5 session search module imported extractTextContent and search types from session-search.ts, while session-search.ts dynamically imported session-search-fts.ts — creating a circular dependency that deadlocked the bundler's module initialization order and crashed every CLI invocation (mayros --version, --help, all commands). Extract extractTextContent and the SearchOptions/SearchResult/ SearchSummary types into a shared session-search-types.ts module that neither file imports from the other. Verified: pnpm build succeeds, node mayros.mjs --version and --help both work, all 27 search tests pass.
Align all 54 extension package.json versions and changelogs with the root package via pnpm plugins:sync. Required for pnpm release:check to pass.
Write CHANGELOG entries for 0.3.0 through 0.4.0 (file was two minor versions behind). The 0.4.0 entry covers all 10 merged PRs: FTS5 search, theme system v2, inline images, cross-platform clipboard, per-section visibility, streaming markdown, skill curator, keyboard protocols, prompt cache drift detection, accessible TUI parity, agent stack migration, and the TUI modularization — plus the breaking changes (vim removal, scope migration, palette accent change). Update README: theme count (13 built-in + custom, not 3), add FTS5 search, colorblind/monochrome palettes, auto-theme detection, inline images, cross-platform clipboard, per-section visibility, and keyboard protocols to the feature list.
…olution, widen theme config Remove the disabled /vim command from the TUI help text and command list — a non-functional command in the help listing is confusing UX. Fix CORE_PACKAGE_NAME in version.ts from "mayros" to "@apilium/mayros" so that --version resolves correctly from package.json instead of relying solely on build-info.json as a fallback. Widen the config.ui.theme type from a closed union to string, so users can set any built-in preset (colorblind-dark, monochrome, etc.) or a custom theme name from ~/.mayros/themes/*.json in their config file.
|
This pull request has been automatically marked as stale due to inactivity. |
|
Closing due to inactivity. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 0.4.0
Major release with FTS5 session search, theme system v2 (custom themes, colorblind, monochrome), complete TUI redesign, skill curator lifecycle, and migration to the official agent stack.
Highlights
Search
Themes
~/.mayros/themes/*.json.Terminal UI
Skills
Agent stack
@earendil-works/*@0.80.6(official scope).Breaking changes
@earendil-works.Release preparation
/vimremoved from help, version resolution fixed, theme config widened.Test plan
pnpm build— succeedspnpm tsgo— 0 errorspnpm lint— 0 errors, 359 warningspnpm release:check— npm pack contents OKnode mayros.mjs --version— shows 0.4.0node mayros.mjs --help— lists all commands