@animalabs/connectome-host 0.3.10 → 0.4.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 +81 -0
- package/HEADLESS-FLEET-PLAN.md +1 -1
- package/UNIFIED-TREE-PLAN.md +2 -0
- package/docs/AGENT-ONBOARDING.md +9 -8
- package/docs/DEPLOYMENTS.md +2 -2
- package/docs/DEV-ENVIRONMENT.md +28 -26
- package/docs/LOCUS-ROUTING-DESIGN.md +8 -2
- package/package.json +4 -4
- package/src/commands.ts +50 -4
- package/src/framework-agent-config.ts +3 -0
- package/src/framework-strategy.ts +3 -0
- package/src/index.ts +9 -0
- package/src/modules/channel-mode-module.ts +9 -6
- package/src/modules/subscription-gc-module.ts +163 -34
- package/src/modules/tts-relay-module.ts +481 -0
- package/src/recipe.ts +58 -1
- package/src/tui.ts +363 -104
- package/test/commands-checkpoint-restore.test.ts +118 -0
- package/test/subscription-gc-module.test.ts +156 -1
- package/test/tui-quit-confirm.test.ts +42 -0
- package/test/tui-short-agent-name.test.ts +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,88 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **context-manager ^0.6.0** — the fatal coverage invariant: a compile
|
|
8
|
+
refuses (`OverBudgetError` / `UncoveredDropError`) rather than shipping a
|
|
9
|
+
context with silently-dropped messages, and recall-pair pricing includes
|
|
10
|
+
reasoning carriers (fixes the permanent compile wedge / silent middle loss
|
|
11
|
+
on carrier-bearing stores). Default `overBudgetGraceRatio` is now 0.02.
|
|
12
|
+
- **agent-framework ^0.7.0** — host-side recovery for context refusals: the
|
|
13
|
+
OverBudget drain breaker also kicks for `UncoveredDropError`, and a
|
|
14
|
+
`context-refusal` ops alert fires immediately (fleet-watch) with the
|
|
15
|
+
recovery knobs named. Plus the context-settings preview surface and the
|
|
16
|
+
workspace read cap.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `compressionMaxTokens` recipe passthrough — cap compression output for
|
|
21
|
+
models with low output ceilings (2c78936).
|
|
22
|
+
|
|
3
23
|
## Unreleased
|
|
4
24
|
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **TUI bug sweep** (#64): operator-safety and observability fixes.
|
|
28
|
+
- `/quit` confirm no longer treats arbitrary input as consent — only an
|
|
29
|
+
explicit `y`/`yes` (or re-typed `/quit`) kills fleet children, `d`
|
|
30
|
+
detaches, anything else cancels; a typed-through message is restored to
|
|
31
|
+
the input (paste referents intact) instead of discarded. Ctrl+C now goes
|
|
32
|
+
through the same confirmation; a second Ctrl+C force-quits.
|
|
33
|
+
- `/checkpoint` records the message position and `/restore` branches back
|
|
34
|
+
to it (previously restored to the branch head — rolling back nothing);
|
|
35
|
+
repeat restores at the same position are a no-op, and an unreachable
|
|
36
|
+
position degrades to the branch head with an explicit note.
|
|
37
|
+
- Session switch fully resets TUI observability state (tree aggregator,
|
|
38
|
+
stream subscriptions, per-agent caches) — fleet subtrees no longer
|
|
39
|
+
freeze after `/session switch`.
|
|
40
|
+
- Memory: peek logs / transcripts / scrollback capped, and detached
|
|
41
|
+
renderables are `destroy()`ed so their native text buffers are actually
|
|
42
|
+
freed (the fleet view leaked one buffer per line per 500ms repaint).
|
|
43
|
+
- Agent-name resolution is exact (`shortAgentName`, fork `-d{depth}`
|
|
44
|
+
scheme included) instead of substring matching that cross-wired agents
|
|
45
|
+
with prefix-overlapping names; peek tails no longer clip the newest
|
|
46
|
+
lines; fleet-view kill/restart failures are surfaced; per-round context
|
|
47
|
+
size (`ctx:`) and session totals (`Σ`) are separate status segments;
|
|
48
|
+
synesthete summaries moved off the render path and back off 30s after
|
|
49
|
+
a failed call instead of retrying at 2 Hz.
|
|
50
|
+
- Smaller UX: peek works on finished subagents (final runtime shown),
|
|
51
|
+
fork `done` summaries always print a chat line, Esc/Ctrl+B work from
|
|
52
|
+
the fleet view, paste placeholders survive `]` in the pasted text,
|
|
53
|
+
`/help` documents `/find` and `/branchto`, `/clear` with arguments
|
|
54
|
+
clears.
|
|
55
|
+
|
|
56
|
+
### Docs
|
|
57
|
+
|
|
58
|
+
- Synced stale documentation with the current build: repos marked public
|
|
59
|
+
(AGENT-ONBOARDING), `forking-knowledge-miner` → `connectome-host`
|
|
60
|
+
naming, webui default port corrected to 7340, DEV-ENVIRONMENT
|
|
61
|
+
branch/version table refreshed (all feature branches merged),
|
|
62
|
+
LOCUS-ROUTING and both root plan docs marked implemented.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- **Tool-bloat reduction**: subscription-gc's `set_channel_idle_limit` /
|
|
67
|
+
`list_channel_idle_limits` tools folded into `agent_settings` as the
|
|
68
|
+
`channel_idle_limits` field (per-entry merge; number / `"off"` /
|
|
69
|
+
`"default"`-or-null to clear), following the reasoning-controls
|
|
70
|
+
precedent. The old tool names remain routable (undeclared), so agent
|
|
71
|
+
muscle memory keeps working; agents just no longer carry the two extra
|
|
72
|
+
tool schemas. `get` also reports read-only `channel_idle_default`,
|
|
73
|
+
`channel_idle_counters`, and `channel_idle_pinned`, preserving what
|
|
74
|
+
`list_channel_idle_limits` exposed. Updates are all-or-nothing: a patch
|
|
75
|
+
with any invalid entry applies none of its entries.
|
|
76
|
+
- **GC pins split from agent overrides**: ChannelModeModule now holds
|
|
77
|
+
debounced channels open via an internal `pin_channel_idle_limit` verb
|
|
78
|
+
and a separate pins layer, instead of writing an `"off"` override.
|
|
79
|
+
Consequences: a blanket `agent_settings reset` clears only agent-set
|
|
80
|
+
limits — it can no longer silently re-enable auto-close on a channel in
|
|
81
|
+
debounced mode — and a pre-existing agent override now survives a
|
|
82
|
+
debounced→mentions round-trip rather than being reset to default.
|
|
83
|
+
(Pins persisted by earlier builds as `"off"` overrides stay agent-level
|
|
84
|
+
until the next mode change re-asserts them as pins.)
|
|
85
|
+
|
|
5
86
|
## 0.3.10 — 2026-07-21
|
|
6
87
|
|
|
7
88
|
### Added
|
package/HEADLESS-FLEET-PLAN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Headless Daemon Mode & Fleet Orchestration — Plan
|
|
2
2
|
|
|
3
|
-
Status:
|
|
3
|
+
Status: implemented (all phases shipped — see [UNIFIED-TREE-PLAN.md](./UNIFIED-TREE-PLAN.md) "Implementation outcome") • Authors: conversation between @tengro and Claude, 2026-04-21
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
package/UNIFIED-TREE-PLAN.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Unified Tree Plan: Fleet Children as Subagents in the TUI
|
|
2
2
|
|
|
3
|
+
**Status: shipped** — all phases landed; see "Implementation outcome" below. The per-phase headers preserve the plan as written.
|
|
4
|
+
|
|
3
5
|
Goal: collapse the two-paradigm UI (`SubagentModule`'s in-process tree vs. `FleetModule`'s flat child list) into a single tree-rendering paradigm where fleet children appear and behave like subagents — same readouts (context tokens, phase, tool-call count), same unfold-children affordance, same row component.
|
|
4
6
|
|
|
5
7
|
Companion to [HEADLESS-FLEET-PLAN.md](./HEADLESS-FLEET-PLAN.md), which defines the underlying IPC.
|
package/docs/AGENT-ONBOARDING.md
CHANGED
|
@@ -22,7 +22,7 @@ A connectome agent is:
|
|
|
22
22
|
- an **`.env`** with secrets and per-deploy config;
|
|
23
23
|
- a **chronicle** (`data/`) — the event-sourced memory store, optionally seeded by importing
|
|
24
24
|
a prior conversation;
|
|
25
|
-
- run by the **connectome-host** (`
|
|
25
|
+
- run by the **connectome-host** (`bun src/index.ts <recipe> --headless`);
|
|
26
26
|
- under a **systemd --user** service, with a sibling **terminal-sessions** daemon for the shell tool.
|
|
27
27
|
|
|
28
28
|
It reaches the model through **membrane** (Anthropic-format adapter; can target a gateway via
|
|
@@ -164,10 +164,10 @@ shell. First, the **hard prerequisites** (install / confirm before anything else
|
|
|
164
164
|
or their box owner if unsure):
|
|
165
165
|
- **git**, **Bun** (`curl -fsSL https://bun.sh/install | bash`), **node ≥ 20** (via `nvm`);
|
|
166
166
|
- a **Rust toolchain** (`rustup`) — `chronicle` has a native (napi) component built from Rust;
|
|
167
|
-
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
- the `anima-research` / `antra-tess` GitHub repos. These are **public** (as of 2026-07;
|
|
168
|
+
they previously required org access), and the `@animalabs/*` npm packages install without
|
|
169
|
+
auth. If a `git clone` / `bun install` fails on access, check whether the repo moved
|
|
170
|
+
rather than assuming a permissions gate.
|
|
171
171
|
|
|
172
172
|
The repos (clone all as siblings under one dir, canonically `~/connectome-local/`):
|
|
173
173
|
|
|
@@ -203,7 +203,7 @@ errors and logs `[webui] listening`. Then continue with the per-agent install di
|
|
|
203
203
|
|
|
204
204
|
**Pick non-colliding ports.** Every agent on the box needs a unique:
|
|
205
205
|
- **shell-daemon port** (`SESSION_SERVER_PORT`, default 3100)
|
|
206
|
-
- **webui port** (default
|
|
206
|
+
- **webui port** (module default 7340; binds `0.0.0.0` and requires `basicAuth` unless set to loopback)
|
|
207
207
|
|
|
208
208
|
Check what's taken (`ss -tlnp`) and pick the next pair (e.g. 3101 / 7343).
|
|
209
209
|
|
|
@@ -381,7 +381,7 @@ Two `systemd --user` units in `~/.config/systemd/user/`:
|
|
|
381
381
|
|
|
382
382
|
1. **`terminal-sessions.service`** — the shell daemon, `--host localhost --headless --token
|
|
383
383
|
${SESSION_SERVER_TOKEN}`, env `SESSION_SERVER_PORT`. `enable` + `start` it first.
|
|
384
|
-
2. **`<agent>-agent.service`** — `bun .../
|
|
384
|
+
2. **`<agent>-agent.service`** — `bun .../connectome-host/src/index.ts
|
|
385
385
|
.../recipes/<agent>.json --headless`, `EnvironmentFile=...env`, `Environment=PATH=<node bin>:<bun bin>:...`,
|
|
386
386
|
`After=/Wants=terminal-sessions.service`, `Restart=always`.
|
|
387
387
|
|
|
@@ -472,7 +472,8 @@ robust fallback strategy.
|
|
|
472
472
|
|
|
473
473
|
## Appendix — quick reference
|
|
474
474
|
|
|
475
|
-
- **Run host:** `bun
|
|
475
|
+
- **Run host:** `bun connectome-host/src/index.ts <recipe.json> --headless` (legacy
|
|
476
|
+
checkouts may still name the directory `forking-knowledge-miner`)
|
|
476
477
|
- **Reach loopback webui:** `ssh -L <port>:localhost:<port> <login-user>@<box>` → `http://localhost:<port>`
|
|
477
478
|
- **Live makeup:** `GET /debug/context/makeup` (basic auth) — segments + exact total tokens
|
|
478
479
|
- **Compiled context:** `GET /debug/context`
|
package/docs/DEPLOYMENTS.md
CHANGED
|
@@ -9,9 +9,9 @@ end-to-end, see [`AGENT-ONBOARDING.md`](./AGENT-ONBOARDING.md).
|
|
|
9
9
|
|
|
10
10
|
## TL;DR
|
|
11
11
|
|
|
12
|
-
An agent deployment is the Connectome stack (`
|
|
12
|
+
An agent deployment is the Connectome stack (`connectome-host` +
|
|
13
13
|
`agent-framework` + MCPL servers) plus a per-agent install dir. Agents run
|
|
14
|
-
**headless** (`bun
|
|
14
|
+
**headless** (`bun connectome-host/src/index.ts <recipe> --headless`),
|
|
15
15
|
each as its own OS user, supervised by launchd (macOS) or systemd-user (Linux).
|
|
16
16
|
|
|
17
17
|
> **Model integrity matters.** Each agent has a *correct* model and must stay on
|
package/docs/DEV-ENVIRONMENT.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Connectome Dev Environment — Setup Guide
|
|
2
2
|
|
|
3
|
-
**Status:** Working notes
|
|
3
|
+
**Status:** Working notes. Snapshot of the dev layout as of 2026-07-22 (originally 2026-05-30; all feature branches in the original table have since merged to `main`).
|
|
4
4
|
**Goal:** Reproduce the current development state — every component as a local git
|
|
5
5
|
checkout, wired together so the host runs against editable source.
|
|
6
6
|
|
|
@@ -24,17 +24,22 @@ that holds a recipe + `.env` + chronicle data and references the code by absolut
|
|
|
24
24
|
|
|
25
25
|
All cloned as siblings under `~/connectome-local/`:
|
|
26
26
|
|
|
27
|
-
| Dir | GitHub repo | Branch | Ver | Role |
|
|
27
|
+
| Dir | GitHub repo | Branch | Ver (2026-07-22) | Role |
|
|
28
28
|
|---|---|---|---|---|
|
|
29
|
-
| `forking-knowledge-miner` | `anima-research/connectome-host` | `
|
|
30
|
-
| `agent-framework` | `anima-research/agent-framework` | `
|
|
31
|
-
| `discord-mcpl` | `anima-research/discord-mcpl` | `main` | 0.1.
|
|
32
|
-
| `heartbeat-mcpl` | `anima-research/heartbeat-mcpl` | `main` | 0.1.
|
|
33
|
-
| `terminal-sessions-mcp` | `antra-tess/terminal-sessions-mcp` ⚠️ | `main` | 1.0
|
|
34
|
-
| `membrane` | `antra-tess/membrane` ⚠️ | `
|
|
35
|
-
| `context-manager` | `anima-research/context-manager` | `
|
|
36
|
-
| `chronicle` | `anima-research/chronicle` | `main` | 0.
|
|
37
|
-
| `mcpl-core-ts` | `anima-research/mcpl-core-ts` | `main` | 0.1
|
|
29
|
+
| `forking-knowledge-miner` | `anima-research/connectome-host` | `main` | 0.3.10 | the host app (run via **bun**) |
|
|
30
|
+
| `agent-framework` | `anima-research/agent-framework` | `main` | 0.6.10 | host runtime: gate, MCPL orchestration, locus routing, `think` |
|
|
31
|
+
| `discord-mcpl` | `anima-research/discord-mcpl` | `main` | 0.1.4 | Discord surface (MCPL server) |
|
|
32
|
+
| `heartbeat-mcpl` | `anima-research/heartbeat-mcpl` | `main` | 0.1.3 | periodic self-wake (MCPL server) |
|
|
33
|
+
| `terminal-sessions-mcp` | `antra-tess/terminal-sessions-mcp` ⚠️ | `main` | 1.6.0 | shell: session daemon (ws://localhost:3100) + per-agent MCP stdio frontend |
|
|
34
|
+
| `membrane` | `antra-tess/membrane` ⚠️ | `main` | 0.5.74 | LLM client lib — **single shared instance required** |
|
|
35
|
+
| `context-manager` | `anima-research/context-manager` | `main` | 0.5.14 | context compilation / autobiographical memory |
|
|
36
|
+
| `chronicle` | `anima-research/chronicle` | `main` | 0.2.7 | record / chronicle store |
|
|
37
|
+
| `mcpl-core-ts` | `anima-research/mcpl-core-ts` | `main` | 0.2.1 | MCPL protocol types |
|
|
38
|
+
|
|
39
|
+
> Versions drift; treat the column as a dated snapshot. The published npm
|
|
40
|
+
> releases now track `main` closely (typically within a patch), so the
|
|
41
|
+
> stock `bun install` path is sufficient for host-level work — use the
|
|
42
|
+
> checkout layout below when editing the libraries themselves.
|
|
38
43
|
|
|
39
44
|
> ⚠️ **Org note:** `terminal-sessions-mcp` and `membrane` still live under the
|
|
40
45
|
> personal `antra-tess` org, not `anima-research`. Consider migrating them for
|
|
@@ -51,19 +56,15 @@ All cloned as siblings under `~/connectome-local/`:
|
|
|
51
56
|
```bash
|
|
52
57
|
mkdir -p ~/connectome-local && cd ~/connectome-local
|
|
53
58
|
|
|
54
|
-
git clone -
|
|
55
|
-
|
|
56
|
-
git clone -
|
|
57
|
-
|
|
58
|
-
git clone
|
|
59
|
-
git clone
|
|
60
|
-
git clone
|
|
61
|
-
git clone -
|
|
62
|
-
|
|
63
|
-
git clone -b feat/adaptive-resolution \
|
|
64
|
-
git@github.com:anima-research/context-manager.git
|
|
65
|
-
git clone -b main git@github.com:anima-research/chronicle.git
|
|
66
|
-
git clone -b main git@github.com:anima-research/mcpl-core-ts.git
|
|
59
|
+
git clone git@github.com:anima-research/connectome-host.git forking-knowledge-miner
|
|
60
|
+
git clone git@github.com:anima-research/agent-framework.git
|
|
61
|
+
git clone git@github.com:anima-research/discord-mcpl.git
|
|
62
|
+
git clone git@github.com:anima-research/heartbeat-mcpl.git
|
|
63
|
+
git clone git@github.com:antra-tess/terminal-sessions-mcp.git
|
|
64
|
+
git clone git@github.com:antra-tess/membrane.git
|
|
65
|
+
git clone git@github.com:anima-research/context-manager.git
|
|
66
|
+
git clone git@github.com:anima-research/chronicle.git
|
|
67
|
+
git clone git@github.com:anima-research/mcpl-core-ts.git
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
---
|
|
@@ -82,8 +83,9 @@ terminal-sessions-mcp
|
|
|
82
83
|
|
|
83
84
|
In each: `npm install && npm run build` (build script is `tsc`).
|
|
84
85
|
|
|
85
|
-
> `discord-mcpl` depends on `@
|
|
86
|
-
> (
|
|
86
|
+
> `discord-mcpl` now depends on `@animalabs/mcpl-core` as a regular npm
|
|
87
|
+
> dependency (the old `file:../mcpl-core-ts` path dep is gone). A sibling
|
|
88
|
+
> `mcpl-core-ts` checkout is only needed when changing mcpl-core itself.
|
|
87
89
|
|
|
88
90
|
---
|
|
89
91
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Conversational Locus & Output Routing — Design Note
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
4
|
-
|
|
3
|
+
**Status:** Implemented — the locus now lives in `agent-framework`
|
|
4
|
+
(`src/mcpl/channel-registry.ts`; `routeSpeech` fires at turn completion in
|
|
5
|
+
`framework.ts`) and has grown beyond this note (global-locus fallback for
|
|
6
|
+
heartbeats, fork/home-channel semantics). Kept as design rationale.
|
|
7
|
+
**Date:** 2026-05-29 (status updated 2026-07-21)
|
|
5
8
|
**Context:** Surfaced while wiring periodic heartbeats for an agent instance
|
|
6
9
|
running headless on a Linux VPS.
|
|
7
10
|
|
|
@@ -95,6 +98,9 @@ discord-mcpl patch.
|
|
|
95
98
|
|
|
96
99
|
## Bugs found in passing (separate fixes, worth upstreaming)
|
|
97
100
|
|
|
101
|
+
*(Update 2026-07-21: #3 has been fixed upstream — `agent-framework`'s
|
|
102
|
+
feature-set-manager now normalizes array→record on ingest.)*
|
|
103
|
+
|
|
98
104
|
1. **discord-mcpl `connect()` race** — resolved on `login()` (before gateway
|
|
99
105
|
READY), so `registerDiscordChannels` enumerated an empty `guilds.cache`.
|
|
100
106
|
*Fixed*: `connect()` now awaits the `ready` event. (Not yet
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animalabs/connectome-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "General-purpose agent TUI host with recipe-based configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"postinstall": "test -d web && npm --prefix web install && npm --prefix web run build || true"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@animalabs/agent-framework": "^0.
|
|
17
|
+
"@animalabs/agent-framework": "^0.7.0",
|
|
18
18
|
"@animalabs/chronicle": "^0.2.0",
|
|
19
|
-
"@animalabs/context-manager": "^0.
|
|
20
|
-
"@animalabs/membrane": "^0.5.
|
|
19
|
+
"@animalabs/context-manager": "^0.6.0",
|
|
20
|
+
"@animalabs/membrane": "^0.5.75",
|
|
21
21
|
"@opentui/core": "^0.1.82"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
package/src/commands.ts
CHANGED
|
@@ -151,7 +151,9 @@ export function handleCommand(command: string, app: AppContext): CommandResult {
|
|
|
151
151
|
{ text: ' /restore <name> Restore to checkpoint', style: 'system' },
|
|
152
152
|
{ text: ' /branches List Chronicle branches', style: 'system' },
|
|
153
153
|
{ text: ' /checkout <name> Switch to branch', style: 'system' },
|
|
154
|
-
{ text: ' /history
|
|
154
|
+
{ text: ' /history [n] Show state transitions (last n)', style: 'system' },
|
|
155
|
+
{ text: ' /find <text> Search messages for text', style: 'system' },
|
|
156
|
+
{ text: ' /branchto <msgId> Branch from a specific message', style: 'system' },
|
|
155
157
|
{ text: ' /mcp list List MCPL servers', style: 'system' },
|
|
156
158
|
{ text: ' /mcp add <id> <cmd> Add/overwrite server', style: 'system' },
|
|
157
159
|
{ text: ' /mcp remove <id> Remove a server', style: 'system' },
|
|
@@ -747,7 +749,13 @@ function handleCheckpoint(app: AppContext, name?: string): CommandResult {
|
|
|
747
749
|
if (!cm) return { lines: [{ text: 'No agent context manager.', style: 'system' }] };
|
|
748
750
|
|
|
749
751
|
const branch = cm.currentBranch();
|
|
750
|
-
|
|
752
|
+
// A checkpoint is a *position*, not just a branch: record the id of the
|
|
753
|
+
// last message so /restore can branch back to this exact point even after
|
|
754
|
+
// the branch head has moved on. branchName alone would restore to the
|
|
755
|
+
// branch HEAD — i.e. not roll anything back.
|
|
756
|
+
const { messages } = cm.queryMessages({});
|
|
757
|
+
const messageId = messages.length > 0 ? messages[messages.length - 1]!.id : undefined;
|
|
758
|
+
app.branchState.checkpoints.set(name, { branchName: branch.name, messageId });
|
|
751
759
|
|
|
752
760
|
return { lines: [{ text: `Checkpoint "${name}" saved at branch ${branch.name} (head: ${branch.head}).`, style: 'system' }] };
|
|
753
761
|
}
|
|
@@ -774,12 +782,50 @@ function handleRestore(app: AppContext, name?: string): CommandResult {
|
|
|
774
782
|
const cm = getAgentCM(app.framework);
|
|
775
783
|
if (!cm) return { lines: [{ text: 'No agent context manager.', style: 'system' }] };
|
|
776
784
|
|
|
777
|
-
|
|
785
|
+
// Restore to the recorded *position*. If the checkpoint captured a message
|
|
786
|
+
// id, branch at that message (same machinery as /undo) — switching to the
|
|
787
|
+
// stored branch name alone would land on its head, which by now may
|
|
788
|
+
// include everything the user wanted to roll back. Checkpoints from before
|
|
789
|
+
// the messageId field (or taken on an empty branch) fall back to the head.
|
|
790
|
+
let target = point.branchName;
|
|
791
|
+
let exact = false;
|
|
792
|
+
if (point.messageId) {
|
|
793
|
+
// A checkpoint is a POSITION, so position equality is the whole
|
|
794
|
+
// "already there" test. Comparing branch names too would kill the guard
|
|
795
|
+
// after the first restore (you'd be on restore-{name}-{ts}, never the
|
|
796
|
+
// original branch again) and every repeat /restore would mint another
|
|
797
|
+
// branch pointing at the same message.
|
|
798
|
+
const { messages } = cm.queryMessages({});
|
|
799
|
+
const atCheckpoint = messages.length > 0
|
|
800
|
+
&& messages[messages.length - 1]!.id === point.messageId;
|
|
801
|
+
if (atCheckpoint) {
|
|
802
|
+
return { lines: [{ text: `Already at checkpoint "${name}".`, style: 'system' }] };
|
|
803
|
+
}
|
|
804
|
+
try {
|
|
805
|
+
// branchAt returns the new branch's NAME (the only thing switchBranch accepts).
|
|
806
|
+
target = cm.branchAt(point.messageId, `restore-${name}-${Date.now()}`);
|
|
807
|
+
exact = true;
|
|
808
|
+
// The record follows the position: keep the newest branch that holds
|
|
809
|
+
// the checkpoint message as the fallback for future restores.
|
|
810
|
+
app.branchState.checkpoints.set(name, { branchName: target, messageId: point.messageId });
|
|
811
|
+
} catch {
|
|
812
|
+
// branchAt resolves ids against the CURRENT branch's view of the log;
|
|
813
|
+
// after e.g. /undo the checkpoint message may not be reachable from
|
|
814
|
+
// here. Degrade to the checkpoint's branch head (the pre-messageId
|
|
815
|
+
// behavior) rather than failing the restore outright.
|
|
816
|
+
target = point.branchName;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
778
819
|
|
|
779
820
|
// switchBranch is async — strategy reinit needs to complete before next op.
|
|
780
821
|
const asyncWork = Promise.resolve(cm.switchBranch(target))
|
|
781
822
|
.then(() => ({
|
|
782
|
-
lines: [
|
|
823
|
+
lines: [
|
|
824
|
+
{ text: `Restored to checkpoint "${name}" (branch: ${target}).`, style: 'system' as const },
|
|
825
|
+
...(point.messageId && !exact
|
|
826
|
+
? [{ text: ' (note: exact checkpoint position unreachable from the current branch — restored to the branch head instead)', style: 'system' as const }]
|
|
827
|
+
: []),
|
|
828
|
+
],
|
|
783
829
|
branchChanged: true,
|
|
784
830
|
}))
|
|
785
831
|
.catch(err => ({
|
|
@@ -55,5 +55,8 @@ export function buildFrameworkAgentConfig(
|
|
|
55
55
|
...(recipe.agent.sameRoundThinkTextPolicy !== undefined
|
|
56
56
|
? { sameRoundThinkTextPolicy: recipe.agent.sameRoundThinkTextPolicy }
|
|
57
57
|
: {}),
|
|
58
|
+
...(recipe.agent.proseRouting !== undefined
|
|
59
|
+
? { proseRouting: recipe.agent.proseRouting }
|
|
60
|
+
: {}),
|
|
58
61
|
};
|
|
59
62
|
}
|
|
@@ -67,6 +67,9 @@ export function buildFrameworkStrategy(
|
|
|
67
67
|
headWindowTokens: strategyConfig?.headWindowTokens ?? 4000,
|
|
68
68
|
recentWindowTokens: strategyConfig?.recentWindowTokens ?? 30000,
|
|
69
69
|
compressionModel: strategyConfig?.compressionModel ?? model,
|
|
70
|
+
...(strategyConfig?.compressionMaxTokens !== undefined
|
|
71
|
+
? { compressionMaxTokens: strategyConfig.compressionMaxTokens }
|
|
72
|
+
: {}),
|
|
70
73
|
autoTickOnNewMessage: true,
|
|
71
74
|
maxMessageTokens: strategyConfig?.maxMessageTokens ?? 10000,
|
|
72
75
|
...(strategyType === 'frontdesk' ? { timeZone } : {}),
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ import { ChannelModeModule } from './modules/channel-mode-module.js';
|
|
|
46
46
|
import { WebUiModule } from './modules/web-ui-module.js';
|
|
47
47
|
import { ObserversModule } from './modules/observers-module.js';
|
|
48
48
|
import { McplAdminModule } from './modules/mcpl-admin-module.js';
|
|
49
|
+
import { TtsRelayModule } from './modules/tts-relay-module.js';
|
|
49
50
|
import { loadMcplServers, applyAgentOverlay, DEFAULT_CONFIG_PATH, DEFAULT_AGENT_OVERLAY_PATH } from './mcpl-config.js';
|
|
50
51
|
import { SessionManager } from './session-manager.js';
|
|
51
52
|
import { resolveAgentName } from './agent-name.js';
|
|
@@ -357,6 +358,14 @@ async function createFramework(
|
|
|
357
358
|
moduleInstances.push(new ObserversModule({ path: observersPath }));
|
|
358
359
|
}
|
|
359
360
|
|
|
361
|
+
// TTS relay tap — opt-in per recipe. Pure trace-bus consumer: mirrors the
|
|
362
|
+
// agent's streaming generation to a melodeus-tts-relay /bot endpoint and
|
|
363
|
+
// trims posted messages on voice interruptions. See modules.ttsRelay in
|
|
364
|
+
// recipe.ts for the config contract (url/token validated at recipe load).
|
|
365
|
+
if (modules.ttsRelay) {
|
|
366
|
+
moduleInstances.push(new TtsRelayModule(modules.ttsRelay));
|
|
367
|
+
}
|
|
368
|
+
|
|
360
369
|
// Extension-registered modules. Instantiated last so built-in modules keep
|
|
361
370
|
// their historical positions; each factory gets the minimal runtime context
|
|
362
371
|
// plus its declaring extension's config blob.
|
|
@@ -200,11 +200,13 @@ export class ChannelModeModule implements Module {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// 3. Pin subscription-gc off so a chatty channel doesn't auto-close
|
|
203
|
-
// itself back out of debounced mode.
|
|
203
|
+
// itself back out of debounced mode. The pin layer sits above
|
|
204
|
+
// agent-level overrides, so agent_settings reset/update can't undo it
|
|
205
|
+
// and any pre-existing override survives the mode round-trip.
|
|
204
206
|
steps.push(
|
|
205
|
-
await this.callToolStep('gc-
|
|
207
|
+
await this.callToolStep('gc-pin', `${this.gcModuleName}--pin_channel_idle_limit`, {
|
|
206
208
|
channelId,
|
|
207
|
-
|
|
209
|
+
pinned: true,
|
|
208
210
|
}),
|
|
209
211
|
);
|
|
210
212
|
|
|
@@ -225,11 +227,12 @@ export class ChannelModeModule implements Module {
|
|
|
225
227
|
// 2. Close ambient traffic through the generic host lifecycle.
|
|
226
228
|
steps.push(await this.callToolStep('close', 'channel_close', { channelId, serverId: this.serverId }));
|
|
227
229
|
|
|
228
|
-
// 3.
|
|
230
|
+
// 3. Release the pin; the channel's agent override (if any) or the
|
|
231
|
+
// default limit applies again.
|
|
229
232
|
steps.push(
|
|
230
|
-
await this.callToolStep('gc-
|
|
233
|
+
await this.callToolStep('gc-unpin', `${this.gcModuleName}--pin_channel_idle_limit`, {
|
|
231
234
|
channelId,
|
|
232
|
-
|
|
235
|
+
pinned: false,
|
|
233
236
|
}),
|
|
234
237
|
);
|
|
235
238
|
|