lollipopkit / lollipopkit/flutter_server_box
rewrite: the Agent on pi-agent-core, and get compaction
Nobody has claimed this yet.
- Dominant language
- Dart
- Stars
- 8.7k
- Forks
- 558
- Avg merge
- 4h 31m
- Merged PRs (30d)
- 129
Description
## Why
ServerBox's Agent is hand-written: `AskAiRepository` carries the protocol, `AgentSession` runs the turn state machine, and the tools are defined in `global_agent_tools.dart`. It works, and it is missing one thing — **compaction**.
Context management today is a window and truncation (#1464). The turn in progress is carried whole; everything before it shares 20k, with each earlier tool output shortened to 6k. What falls outside is gone. Nothing summarises older turns and feeds the summary back.
Measured against what #1464 asked for, that PR is mitigation rather than a fix. It stops a turn over the budget from erasing the whole history, which was an implementation defect. The report also asked to "keep a summary instead of forgetting entirely", and that part is not done. A long enough conversation still forgets; it just no longer falls off a cliff.
Adding real compaction is not one function. `AskAiConversationItem` needs a summary kind that both storage and replay understand, the trigger needs real token usage rather than a character estimate, and the model needs a way to retrieve the originals by id. At that point the hand-written Agent is the size of a small agent framework — so the question is worth asking before writing it: keep writing our own?
## Survey 1: Dart agent frameworks
Six installed, each compiled, read, and driven by a stub probe.
| Framework | Latest | Likes | Compiles | Conversation compaction | Originals kept | Transitive deps |
|---|---|---|---|---|---|---|
| dartantic_ai | 3.4.2 | 60 | yes | **none** | — | 73 |
| dart_agent_core | 2.1.5 | 10 | yes | LLM summary + episodic memory | retrievable | 63 |
| agents (MS port) | 2.0.0 | 1 | **no** | 5 strategy kinds, 1847 lines | unverified | 43 |
| adk_dart | 2026.9.11 | 6 | yes | token threshold + LLM summary | partial | 72 |
| akashi | 0.3.2 | 1 | yes | **none** | — | 52 |
| agentic | 1.5.3 | 3 | yes | **none** (document chunking only) | — | 98 |
`agents` does not compile. The error is in its own `a2a_ai_content_extensions.dart` — `file.mimeType` became nullable and the code did not follow. Installing the latest version on its own fails the same way.
`dart_agent_core` works. Driving its compressor with 12 turns of 36 messages (one tool call each, 2000 characters of output): `36 -> 8` messages, 30 originals moved into episodic memory, **zero orphan tool results**, and the first message is still a valid `UserMessage`.
So: the most established Dart option has no compaction, and of the three that do, one does not compile and the other two are weeks old with single-digit likes.
## Survey 2: pi runs in QuickJS
The plugin-system branch already has a QuickJS runtime (`crates/sbm_plugin`, rquickjs + quickjs-ng, 113 tests). That makes a third option available: **the Agent runs in JavaScript**.
`@earendil-works/pi-agent-core` 0.85.1, bundled with esbuild `--platform=neutral`:
- 1.1 MB single-file ESM, with **no `node:` imports left**
- loads in `qjs` in **0.07 s**, 149 exports
- `Agent` is a function, `AgentHarness` is reachable
- the whole compaction API is there: `compact` / `shouldCompact` / `prepareCompaction` / `generateSummary` / `generateSummaryWithUsage` / `createCompactionSummaryMessage` / `generateBranchSummary`
- `DEFAULT_COMPACTION_SETTINGS = {enabled: true, reserveTokens: 16384, keepRecentTokens: 20000}`
Three globals have to be shimmed: `process`, `fetch`, `TextEncoder`/`TextDecoder`.
`pi-ai` (a unified API over 15+ providers) reaches for `node:` in exactly two places, both dynamic imports with a fallback: one reads `~/.pi/auth.json` (we do not need it — credentials are ours), the other builds a user-agent string. Its own comment reads "Keep runtime OS loading browser-safe. A top-level runtime import of node:os breaks browser/Vite builds."
## Draft plan
Replace the Agent's core with `pi-agent-core`, running in the QuickJS runtime that already exists. The app keeps what is genuinely ours:
- the proposal card, the high-risk confirmation, and the two-reader risk verdict (#1467)
- `ensureExec` across the SSH and monitor transports
- the encrypted SQLite storage and its replay
- l10n and the whole Flutter surface
pi's tools have to be reimplemented. Its built-in `bash` / `read` / `write` / `edit` act on the local filesystem; ours have to go through `ensureExec` to reach a server over SSH or a monitor agent. `pi.registerTool` is where they attach.
What that buys: compaction, multiple providers, session branching, an event stream, and an upstream that is actively developed.
## To verify, by risk
1. **Streaming.** Does `sb.http.fetch` support streaming responses? pi's agent loop depends on streamed tokens. If it does not, either the host interface grows or the LLM request stays on the Dart side and only the state machine moves into JS — which is a much smaller gain.
2. **UI bridging.** pi emits a typed event stream (`agent_start` / `message_update` / `tool_execution_start` / `tool_result` / …) that has to map onto the current timeline and approval flow. The plugin system's "plugin returns JSON describing a surface" mechanism does not cover interaction at this density.
3. **Approval timing.** Tool execution has to suspend on the JS side until the user has answered on the Flutter side. pi has no built-in permission gate — upstream's position is that extensions provide it — so its tool execution point has to be confirmed suspendable.
4. **Storage shape.** pi's sessions are a JSONL tree (every entry carries `id`/`parentId`); ours are SQLite tables. Either migrate existing conversations or accept that old ones become read-only.
5. **Version stability.** 0.85.x, and the API is explicitly still moving. A pinning policy is needed, and once a 1.1 MB bundle ships in assets, upgrading it means shipping a build.
6. **Licence** not checked.
7. **App Store 2.5.2.** PLUGINS.md already records that interpretation alone does not guarantee clearing this rule. The Agent would run in the same runtime, so it carries the same risk.
8. **Size and cold start.** 0.07 s is the `qjs` command line on a desktop, not reading from assets and parsing on an iPhone.
## Not in scope
- No Dart agent framework, for the reasons in Survey 1.
- Built-in features (PVE, benchmark, process, services, BMC) stay in Dart, as PLUGINS.md already decided.
- Nothing changes in the current Agent until 1, 2 and 3 above are answered. Local fixes like #1467 continue as normal.
## References
- [earendil-works/pi](https://github.com/earendil-works/pi) · [pi.dev/docs](https://pi.dev/docs/latest/sdk)
- [dart_agent_core](https://pub.dev/packages/dart_agent_core) — the compaction design worth copying
- `PLUGINS.md` on the docs/plugin-system branch
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the existing Agent pieces named in the issue, especially AskAiRepository, AgentSession, global_agent_tools.dart, crates/sbm_plugin, and PLUGINS.md. Verify streaming, UI bridging, approval suspension, storage compatibility, licensing, and runtime risks before choosing an implementation; done means those risks have answers and a migration plan is agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter, javascript
- Domain
- developer-experience, mobile, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100