Connector: make the local agent a durable, installable machine (`chopin` CLI, persisted pairing, service, reconnect)
メンテナーはふだん 1 日以内に返信
@lavaman131 がすでに取り組んでいます。
2026年10月12日 から。
評価
この issue はまだ評価されていません。
説明
Problem
The connector in apps/connector works well while someone keeps a terminal open, but it isn't yet something a team can leave running. We (@lavaman131) plan to implement the steps below and want to agree on the shape first.
The current gaps:
- It needs Bun and curl. Setup downloads a script with
curl … /connector.jsand runs it withbun1.4.2 (docs/local-launcher.md,docs/local-experiments.md). After a deploy, the fix is to "rerun the whole command", because that downloads the connector again. Users get no installed executable, no version check, and nothing a service can point at. - Pairing doesn't survive a restart or sign-out. "The connector credential is held in memory and tied to your browser login … Reconnect after a server restart or logout." The cause is in the server code.
Connectionskeeps pending pairings, connections and tokens in in-memoryMaps (apps/server/src/experiments/connections.ts:50-59). EachConnectionstores the browsersessionId(connections.ts:11).revokeSessiondrops a connection when that browser session ends (connections.ts:228-230), androutes.ts:343resolves the browser session again on every connector call. As a result, every deploy needs a person back at the terminal to pair again. The Let Chopin build and prototype automatically on this laptop choice (autoRun) is also lost and asked again. - The connector doesn't reconnect. In
main.ts, thewait_for_workloop rethrows any error that isn't an abort (main.ts:135-136), so a network blip or server restart ends the process. During a run, a failed heartbeat aborts the agent (run.ts:80:.catch(() => abort.abort())). The server forgets a connection after 90 seconds of silence (CONNECTION_MS,connections.ts:38) and "fails its builds and rebuilds and interrupts its investigations immediately" when the connector disconnects. - Nothing installs or starts it. The docs say so: "Keep the connector running. Each browser Build action starts an agent process; automatically installing or starting the connector itself remains future work." (
docs/local-launcher.md:35-36, repeated at line 98-100 as "automatic connector startup … remain future work"). - One process serves one checkout. "One connector process serves one checkout" (
docs/local-experiments.md, Trouble connecting). Each checkout has its own lock, created inlockWorkspace. - Users can't choose the machine. The server picks "your idle connection for the repository, preferring the one that last ran work for this document" (
docs/local-launcher.md:51-53). The UI doesn't let anyone see or choose which machine a document or repository uses. - Planning and building need separate local processes. Under
HARNESS=remote, the Planner runs in a client attached over/planner-linkwith a GitHub bearer (docs/planner-link.md). Builds and investigations run through the connector over/connector/mcp. The same laptop needs two separate long-lived processes with two different credentials. Attaching also needs "the same account … signed in to Chopin in a browser, on this server, since the server last started" (sign-in-required), so a server restart breaks the Planner side too.
#521 makes a missing agent visible ("Start your local agent"). This issue is about keeping the agent from going missing.
Plan
Each step is meant to be one reviewable PR. Steps run in order unless a step says otherwise. The interactive connect flow and GET /connector.js keep working throughout.
-
Ship the connector as an installable
chopinCLI that needs no Bun or curl.- Compile
apps/connector/src/main.tswithbun build --compilefor linux-x64, linux-arm64, darwin-x64, darwin-arm64 and windows-x64. - Publish it on npm as one package per platform behind a small launcher package that picks the right one through
optionalDependencies(the esbuild pattern). That waynpm install -g <package>works with plain Node and no Bun. - Attach the same binaries to GitHub releases, with
install.shandinstall.ps1that install to a stable per-user path. - Make the stdio bridge work from a compiled binary. Today it is spawned as
process.execPath [resolve(process.argv[1]), "bridge"](run.ts:151-161,main.ts:128). In a compiled binary it has to be[process.execPath, "bridge"]. - Have the CLI send its version when it pairs or connects. On an incompatible version, the server refuses with a message naming the upgrade command, replacing "rerun the whole command".
- Keep
/connector.jsserved and documented as a fallback during the migration. - The service step depends on this step, because a service unit must point at a stable installed executable rather than
bun ~/.chopin/connector.js.
- Compile
-
Persist a revocable machine credential.
chopin connect [checkout] -- <ACP command>pairs once through the existing link and confirmation code. It stores a machine credential in the state directory (stateDirectory(), mode 0600) and stores the ACP command and checkout in a machine config.- On the server, keep the machine (owner, label, repositories,
autoRun, credential fingerprint) in storage instead of the in-memoryConnectionsmaps, and stop tying it to the browsersessionId. Reconnecting after a server restart, a deploy or a browser sign-out then needs no new pairing. - Add revocation in two places: a Disconnect machine action in the UI and
chopin disconnecton the machine. A revoked credential gets a distinct error code, and the CLI stops instead of retrying. - The per-call push-access recheck stays, but it must work without a live browser session (see Open questions).
-
Reconnect with backoff.
- Wrap the
wait_for_workloop in a reconnect loop. Retry transient failures (network errors, 5xx,connection-unavailableafter a restart) after 1, 2, 5, 10, 20 and 30 seconds, then every minute. - Stop only on revocation, lost repository access or an incompatible version.
- A failed heartbeat during a run no longer aborts the agent right away. Reporting is held and retried until the server's grace period for the run expires.
- Wrap the
-
Server-side liveness and drop handling.
- Measure liveness on the server side, as
/planner-linknow does with pings every 15 seconds and a 120-second timeout. Either keep the long poll and track last-heard per machine, or move the machine channel to a socket with pings. Then a connector killed without warning is noticed promptly, and a connector that is still alive isn't dropped. - When a machine drops, its runs move to a "machine offline" state within a grace period instead of failing at once. When the same machine reconnects, it reports which runs it still holds.
- A reconnect with the same machine credential replaces that machine's own stale connection immediately. It must not count toward the 8-connection limit (
connections.ts:95) or wait outCONNECTION_MS.
- Measure liveness on the server side, as
-
Several checkouts per machine, and
chopin status.- One
chopinprocess serves every checkout in its config, keeping onelockWorkspacelock per checkout.chopin connectin another checkout adds that checkout to the running machine instead of starting a second process. chopin statusprints:- the installed version and the version the running process uses;
- the server and the paired account;
- whether the credential is stored (never the credential itself);
- each checkout and repository, with its connection state and last contact;
- the current runs with their worktree paths;
- the log path.
- One
-
chopin service install|status|uninstall.- Linux: a systemd user unit with
Restart=always, enabled withsystemctl --user enable --now, plusloginctl enable-lingerso the machine runs at boot and after logout. When enabling linger needs an administrator, print the onesudocommand and install nothing. - macOS: a LaunchAgent with
RunAtLoadandKeepAlive, loaded withlaunchctl bootstrap gui/<uid>. - Windows: a per-user Task Scheduler task that restarts after failure. On Windows, stopping must ask the running process to stop through a file or pipe, because Task Scheduler kills the process with no signal.
- The unit runs the absolute path of the installed
chopinexecutable, and output is appended to a log under the state directory. service statusreportsrunning,stopped,not-installed, or "restart pending" when the installed version differs from the running one.uninstallstops the service gracefully and leaves the credential, config and worktrees in place.
- Linux: a systemd user unit with
-
Choose a machine per repository or per document in the UI.
- List the owner's machines: label, last seen, version, repositories and the
autoRunchoice. - Allow pinning a machine to a repository, with an optional override per document. Build, Run, rebuilds and spikes go to the pinned machine. If none is pinned, the current preference applies (the machine that last ran work for this document, then the owner's most recently active idle one).
- Another person's machine is never eligible, as today.
- List the owner's machines: label, last seen, version, repositories and the
-
Launch a Planner backend for
HARNESS=remotedocuments from the same machine.- The machine config can name a Planner backend command next to the ACP command.
- For each document assigned to the machine, the machine creates a separate worktree, starts that command there and attaches it over
/planner-link. When the link drops, it attaches again with the same backoff. - One paired machine then serves both planning and builds.
- A stale link left by this machine must not block its own reattach. Today a dead link holds the document as
planner-attached, refusing the same account, until the 120-second timeout and then the 2-minute grace period. The server should let the same machine replace its own dead link. At minimum, the client must recognise itself as the holder and retry quickly rather than wait minutes. - This step depends on step 2's credential decision for
sign-in-required.
Requirements carried into each step
-
Outbound only. The machine never listens on a public interface. The loopback bridge in
local-bridge.tsstays loopback. -
A drop pauses work and never answers. When a link or connection drops, the machine pauses its runs and keeps their worktrees. Open questions, Decisions and ACP permission requests are withdrawn and are never answered on anyone's behalf. Runs resume only through an explicit resume or a person's action after reattach. A service has no TTY, so permission requests stay declined as today (
run.ts:181) and are never auto-approved by the machine. -
Pairing survives restarts. A credential tied to a browser sign-in breaks on every deploy, so the machine credential must outlive server restarts, deploys and browser sign-out. It must still be revocable from both the UI and the CLI. It is stored at 0600 and never printed, logged or written into a unit file or plist.
-
The service PATH survives a reboot. The unit records the absolute
chopinpath and a PATH where the agent command andgitstill resolve after a reboot:- the directory of the resolved agent executable comes first;
- entries under
$XDG_RUNTIME_DIRor/run/user/<uid>are dropped; - per-shell version-manager directories (for example fnm's
fnm_multishells) are dropped.
service installchecks that the ACP command resolves under that PATH before it writes anything. On Windows, the task inherits the user's environment variables. -
A stale link from the same machine must not block reattach. This applies to connector connections and
/planner-linkalike, and to the local.lockfile.staleLockalready replaces a lock whose process is gone. -
Worktrees stay isolated. Each run keeps its own worktree under
runs/<id>(prepareWorkspace), and each Planner document gets its own worktree. Local edits are preserved. Spikes keepnoPushEnvironment().stopProcessesInstill runs when a job ends. -
No regressions. Every step keeps
bun run e2e e2e/implementation-launcher.e2e.ts e2e/connector.e2e.tspassing and adds tests for the behaviour it introduces: a restart without re-pairing, reconnect after a drop, revocation, and reattach past a stale link.
Open questions
-
Planner link vs ACP. Should the machine keep two channels (
/connector/mcpfor builds and investigations,/planner-linkfor the Planner), or should Planner turns move onto the machine's connector channel under one credential? With two channels, does/planner-linkaccept the machine credential in place of a GitHub bearer? -
Repository credential for a durable machine. Repository tools and push-access rechecks currently use the browser login's GitHub App token (
routes.ts:343, andsign-in-requiredon/planner-link). The options we see:- store a GitHub App user refresh token server-side for each machine;
- run a device-flow sign-in on the machine;
- persist browser sessions across restarts.
Which does the team prefer?
-
Packaging. Is
bun build --compileacceptable given the binary size? What should the npm package name and scope be? Should the binaries be code-signed and notarized for macOS and Windows? Should the CLI update itself, or only tell the user when the server needs a newer version? -
Running builds during a drop. Should a build keep running locally through a short drop and report on reconnect, or pause at once?
-
Permission prompts under a service. Should they stay declined, rely on the agent's own auto-approval flag after
--, or be routed to the owner in Chopin? -
Ownership. Who owns the CLI, its release pipeline and the service units after they land? Should they live in
apps/connectoror in a newapps/cli?
- 主要言語
- TypeScript
- スター
- 406
- フォーク
- 24
- 平均マージ
- 13時間 56分
- マージ済み PR(30日)
- 371
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートなし
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
githubnext/chopin のほかの issue
-
documentation triage/human-response
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
githubnext/chopin#149 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 3/5 半日 初心者へのやさしさ 45/100
githubnext/chopin#597 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 4/5 3〜5日 初心者へのやさしさ 30/100
githubnext/chopin#596 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 5/5 1週間以上 初心者へのやさしさ 12/100
githubnext/chopin#595 ·
メンテナーはふだん 1 日以内に返信
-
enhancement triage/human-response
難易度 4/5 3〜5日 初心者へのやさしさ 55/100
githubnext/chopin#203 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
githubnext/chopin の issue をすべて見る
似ている issue
-
useFragment does not require `variables` when the fragment's variables type has required fieldsオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
apollographql/apollo-client#13493 ·
メンテナーはふだん 1 日以内に返信
-
Custom hosts written as trailing-dot FQDNs (`host.local.`) aren't detected as local backends対応中かも @roboomp が今日担当しました。 オープンbug prio:p2 providers triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
can1357/oh-my-pi#15798 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
code-yeongyu/oh-my-openagent#10172 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
PDFCraftTool/pdfcraft#189 ·
-
Add the Yahoo chart API duplicate last-day row (yfinance#573) to incidents.md (about 1 hour)対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンdata docs good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 80/100
pacocartones/market-data-sanity-checker#160 ·
メンテナーはふだん 1 日以内に返信