Skip to content

Commit 39b50b5

Browse files
authored
feat: graduate remote control, storage and the subagent model pool, and finish the tunnel stack (#306)
## Related Issue No tracked issue — this is an internal reconciliation pass. ## Problem Three experimental features were ready to graduate but still hid behind flags, the `${now}` system prompt variable was documented but no longer had a purpose, and the Remote Control tunnel stack was split between the CLI and the gateway with no way to toggle it while the server ran. Global search stayed broken after a corrupt write instead of recovering, and a background task that outlived its agent emitted stray events. ## What changed **Breaking** - Remote Control, session indexing and global search no longer need experimental flags. Session indexing and search move to a new `[database]` config section; both kill-switch environment variables lose their `_EXPERIMENTAL` prefix. - The subagent model pool is always on; `[secondary_model]` takes effect with no opt-in. - `${now}` is gone from custom system prompt templates. It renders verbatim as an unknown placeholder now, and the agent still receives the current date through the environment disclosure. **Features** - `GET` and `POST /api/v1/remote-control` start and stop Remote Control while the server runs, backed by an xstate manager with `off`/`starting`/`on`/`stopping` states. - New `packages/remote-control` workspace holds the tunnel client and its machine-wide single-instance lock, shared by the gateway and the CLI. Both `flake.nix` lists were updated; the `pnpmDeps` hash is unchanged. **Fixes** - The Remote Control banner's Local UI link carries the server token, so it opens without a second sign-in. - The tunnel gzips text, JSON, JavaScript, XML and SVG responses; it skips `206` and already-encoded bodies, drops the ETag when it compresses, and always sets `Vary: Accept-Encoding`. - Global search rebuilds its index instead of staying broken when stored data is corrupt or a write keeps failing. - A background task settling after its agent is torn down no longer emits task events. - The desktop updater falls back to the default macOS manifest when the channel argument is empty. Notable adaptations: `REMOTE_CONTROL_ALREADY_RUNNING` uses code `40939` because `40928` is already `FS_CONFLICT` here, and the late-settlement fix is placed after `tryAcquireQuiescence` rather than before the loop drain, because this codebase reworked `close()` into phase-based error collection. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [ ] I have linked a related issue (external PRs: the issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Remote Control is generally available through CLI, web commands, and `/remote-control`. * Added runtime start/stop controls through the server API. * Secondary-model routing is available without experimental opt-in. * Added database configuration for session indexing and global search. * Tunnel responses now support gzip compression. * **Bug Fixes** * Local UI links now open with the server token. * Search indexes recover from corruption and repeated failures. * Completed background tasks no longer emit stray events. * Improved macOS artifact finalization with the default manifest. * **Breaking Changes** * Removed the `${now}` variable from custom system prompt templates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 6a1181a commit 39b50b5

114 files changed

Lines changed: 3286 additions & 1845 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": major
3+
---
4+
5+
Remote Control is always available — `pythinker rc`, `pythinker web --remote-control` and `/remote-control` no longer need an experimental flag. Session indexing and global search move to the new `[database]` section: set `PYTHINKER_CODE_PERSISTENCE_MINIDB_READMODEL` (was `PYTHINKER_CODE_EXPERIMENTAL_PERSISTENCE_MINIDB_READMODEL`) and `PYTHINKER_CODE_SEARCH_WORKER` (was `PYTHINKER_CODE_EXPERIMENTAL_SEARCH_WORKER`), or `[database] base` and `[database] search` in `config.toml`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": major
3+
---
4+
5+
The subagent model pool is always available. Remove `PYTHINKER_CODE_EXPERIMENTAL_SECONDARY_MODEL` from your environment — it no longer does anything, and `[secondary_model]` takes effect with no opt-in.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
The Remote Control banner's Local UI link now carries the server token, so it opens without a second sign-in.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
The server can now start and stop Remote Control while it runs, through `GET` and `POST /api/v1/remote-control`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Remote Control now gzips text, JSON, JavaScript, XML and SVG responses over the tunnel.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": major
3+
---
4+
5+
Remove the `${now}` variable from custom system prompt templates. Delete `${now}` from your `SYSTEM.md` and agent files — the agent still receives the current date.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Global search now rebuilds its index instead of staying broken when the stored data is corrupt or a write keeps failing.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
A background task that finishes after its agent is closed no longer emits stray task events.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Adding an OpenAI-compatible provider requires **zero code changes** — just add
6767
| `packages/acp-server` | Agent Client Protocol host over engine v2 | Drives the engine through a `klient` memory-transport facade. |
6868
| `packages/pi-tui` | Vendored TUI library | Upstream fork with local divergences; tests run with `node --test`, not vitest. See its `AGENTS.md`. |
6969
| `packages/protocol` | Shared REST + WS protocol schemas | Envelope, error codes, pagination, WS-control types. |
70+
| `packages/remote-control` | Remote Control tunnel client | Registers this machine with a relay and forwards HTTP/WebSocket traffic to the local server, behind a machine-wide single-instance lock. Consumed by agent-gateway (the `/api/v1/remote-control` toggle) and the CLI (`pythinker web --remote-control`). |
7071

7172
The web bundle: `apps/pythinker-code/dist-web` is the committed, prebuilt bundle of `apps/pythinker-web` (built with `pnpm --filter @pymodel/pythinker-web run build` and copied via `scripts/copy-web-assets.mjs`). `apps/pythinker-code/scripts/check-web-assets.mjs` fails when the bundle is missing **or stale** (it compares a fingerprint of every `apps/pythinker-web` build input against the one recorded at copy time); it runs in pre-push, in the CLI `build`, and on `prepack`. Whenever you touch the web UI, run `pnpm run build:web` and commit the restaged bundle in the same change. `packages/server` and `packages/server-e2e` are empty leftover directories excluded from the workspace — not packages.
7273

apps/desktop/scripts/finalize-mac-artifacts.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,13 @@ export function finalizeMacArtifacts(options: FinalizeMacArtifactsOptions): void
153153
.sort()
154154
if (dmgs.length === 0) throw new Error(`No DMG artifacts found in ${options.distDir}`)
155155

156-
const manifestName = options.manifestName ?? DEFAULT_MAC_MANIFEST
156+
// The workflow always passes the channel manifest positionally, so an unresolved
157+
// channel output arrives as an empty string rather than a missing argument; `??`
158+
// would keep it and read the distribution directory itself.
159+
const manifestName =
160+
options.manifestName === undefined || options.manifestName === ''
161+
? DEFAULT_MAC_MANIFEST
162+
: options.manifestName
157163
const metadataPath = join(options.distDir, manifestName)
158164
let metadata = readFileSync(metadataPath, 'utf8')
159165
const credentialArgs = buildNotarytoolArguments(options.env)

0 commit comments

Comments
 (0)