feat(agent): interactive TUI launcher + identity + TOOLS.md (#35)

- scripts/agent.sh <name>: launches interactive pi TUI in the container
  with contracts + optional mission + agent identity + named session +
  optional workspace/tools; the Mosaic alternative to vanilla pi
- pi adapter: MOSAIC_INTERACTIVE branch (clean TUI, no -p, no initial
  prompt); headless exec rebuilt via positional args (no word-splitting
  on the request); MOSAIC_AGENT_NAME optional in headless
- loader: AGENT IDENTITY section when the launcher names the agent
- compose: fixed command removed (request defaults live in run-agent.sh);
  MOSAIC_INTERACTIVE/MOSAIC_AGENT_NAME passthrough
- docs/TOOLS.md: full on-demand tool reference; AGENTS.md routes to it
- RELEASE -> 0.0.8 (container change); build verified

Closes #35
This commit is contained in:
2026-09-03 11:24:56 -05:00
parent 0273a84549
commit 7db4c5c2ed
17 changed files with 1028 additions and 20 deletions
+83
View File
@@ -0,0 +1,83 @@
# unslop-hook
Mechanical AI-tell enforcement for pi seats. Anti-drift gate for the writing
standard in SYSTEM.md / ms-unslop: prose distribution alone decays over long
sessions; this check cannot forget.
- `lists.json`: committed machine source for every list the checker enforces:
words, phrases, punct rules, regex patterns, density thresholds. Each entry
carries provenance (`ms-unslop:<pattern id>` or `system-md`), the mention
convention, and the documented divergence of the density gate from
SYSTEM.md's outright em-dash ban. Edit lists here, not in code.
- `unslop-check.js`: dependency-free checker (node CLI + module) driven by
lists.json. Loads and schema-validates the lists on first use and hard-fails
closed: empty, unparseable, or invalid lists throw. Detects banned vocabulary,
chatbot/sycophancy phrases, filler phrases, em/en dashes, curly quotes,
`not just X but Y`. Strips fenced and inline code first, so quoted code is
never flagged. Exit 0 clean, 1 violations, 2 gate broken (lists unreadable,
never a clean verdict).
- `extension.ts`: pi extension. `message_end` checks finalized assistant text
and notifies the operator (TUI/RPC). `before_agent_start` reads the most
recent assistant reply from the session file and, if it carries tells,
injects a correction notice the model sees on its next turn. `/unslop`
reports session stats. Violation state lives in the session file, so the
injection path survives restart, resume, fork, and reload (an in-memory
pending flag was measured dead across print-mode turns, 2026-08-19). A
broken lists.json fails closed: checks stop, `broken_lists` /
`skipped_broken` events log the reason, operator notified once, seat keeps
running.
- `test-unslop-check.js`: unit tests with red and green controls.
## Use
```bash
node test-unslop-check.js # suite
node unslop-check.js <file> # CLI check
UNSLOP_LISTS=<path> node unslop-check.js <file> # alt lists location
pi -e ~/.mosaic/tools/unslop-hook/extension.ts # ad-hoc load
# deploy: copy dir to ~/.pi/agent/extensions/unslop-hook/ or seat .pi
# equivalent, or list it in settings.json "extensions"
```
Env: `MOSAIC_UNSLOP_HOOK=0` disables. `MOSAIC_UNSLOP_LOG=<path>` appends JSONL
events (loaded / flagged / notice_injected / checked / broken_lists /
skipped_broken) for headless evidence. `UNSLOP_LISTS=<path>` overrides the
lists.json location for both CLI and extension.
## Verified here (2026-08-19)
- Unit suite 24/24 (8 behavioral, 11 loader/CLI, 5 review follow-up), red and
green controls both exercised, including exit-2 on broken lists and on an
unreadable input file (S3).
- CLI: slop file exit 1, clean file exit 0, broken lists exit 2 with the fault
named on stderr.
- Extension, healthy path (print mode, zai/glm-5.3:low): startup probe loads
lists.json, reply checked clean.
- Extension, broken-lists path (print mode): `broken_lists` at startup,
`skipped_broken` per turn, seat survives, reply still delivered.
- Earlier live evidence (pre-C1, inline lists): forced-slop turn flagged;
fresh-process follow-up injected the notice and the reply came back clean;
full TUI trial (notify line, injection, /unslop stats) on session vision-unslop.
- Log evidence in session scratchpad.
## Limits
- `/unslop` command not tested headless (print mode has no command surface);
it is a thin stats wrapper.
- En dash flag fires on typographic ranges too (23); acceptable for fleet
prose, revisit if it noisifies technical writing.
- Notice injection is a nudger, not a blocker. Output already streamed to the
user stays as-is; correction lands on the next turn.
- A broken lists.json latches for the session: repairing the file mid-session
does not revive checks until the seat restarts. Acceptable for an advisory
gate (review S1).
- The fail-closed operator notification requires a UI. Print-mode sessions
log `skipped_broken` but notify nobody (review S2).
- The word/phrase lists are the mechanical subset of ms-unslop only, keyed to
pattern ids in lists.json. Style judgments (voice, rhythm, structure) stay in
the skill, not the gate.
## Promotion path
Stack issue (A4): checker shared as the single source for a matching Claude
Code Stop-hook script; lists versioned beside SYSTEM.md contract text.