my-durable 0.0.0-stage → 0.0.2
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/.env.example +7 -0
- package/LICENSE +21 -0
- package/README.md +328 -2
- package/ai_docs/durable_pi/announcement.md +860 -0
- package/ai_docs/durable_pi/changelog.md +51 -0
- package/ai_docs/durable_pi/chord-delta-findings.md +362 -0
- package/ai_docs/durable_pi/coding-agent-integration.md +70 -0
- package/ai_docs/durable_pi/pico-v5-chord-usage.md +391 -0
- package/ai_docs/durable_pi/pico-v5-handoff.md +921 -0
- package/ai_docs/durable_pi/readme.md +617 -0
- package/ai_docs/durable_pi/sources.md +22 -0
- package/ai_docs/durable_pi/spec.md +4696 -0
- package/ai_docs/durable_pi/vacation-planner.md +57 -0
- package/ai_docs/pi/chord.md +268 -0
- package/ai_docs/pi/cli-integration.md +110 -0
- package/ai_docs/pi/cli.md +325 -0
- package/ai_docs/pi/codemode.md +211 -0
- package/ai_docs/pi/compaction.md +467 -0
- package/ai_docs/pi/configuration.md +51 -0
- package/ai_docs/pi/containerization.md +187 -0
- package/ai_docs/pi/custom-provider.md +177 -0
- package/ai_docs/pi/environment-variables.md +102 -0
- package/ai_docs/pi/extensions.md +278 -0
- package/ai_docs/pi/how-pi-works.md +53 -0
- package/ai_docs/pi/index.md +43 -0
- package/ai_docs/pi/json.md +230 -0
- package/ai_docs/pi/keybindings.md +198 -0
- package/ai_docs/pi/llama-cpp.md +118 -0
- package/ai_docs/pi/mcp.md +274 -0
- package/ai_docs/pi/message-types.md +265 -0
- package/ai_docs/pi/models.md +209 -0
- package/ai_docs/pi/overview.md +166 -0
- package/ai_docs/pi/packages.md +133 -0
- package/ai_docs/pi/pi-agent-core.md +568 -0
- package/ai_docs/pi/pi-ai.md +1942 -0
- package/ai_docs/pi/prompt-templates.md +63 -0
- package/ai_docs/pi/providers.md +204 -0
- package/ai_docs/pi/quickstart.md +140 -0
- package/ai_docs/pi/readme.md +99 -0
- package/ai_docs/pi/rpc-commands.md +864 -0
- package/ai_docs/pi/rpc-extension-ui.md +204 -0
- package/ai_docs/pi/rpc.md +197 -0
- package/ai_docs/pi/sdk.md +151 -0
- package/ai_docs/pi/security.md +103 -0
- package/ai_docs/pi/session-format.md +297 -0
- package/ai_docs/pi/sessions.md +70 -0
- package/ai_docs/pi/settings.md +173 -0
- package/ai_docs/pi/shell-aliases.md +97 -0
- package/ai_docs/pi/skills.md +97 -0
- package/ai_docs/pi/slash-commands.md +64 -0
- package/ai_docs/pi/sources.md +57 -0
- package/ai_docs/pi/terminal-setup.md +223 -0
- package/ai_docs/pi/termux.md +121 -0
- package/ai_docs/pi/themes.md +140 -0
- package/ai_docs/pi/tmux.md +59 -0
- package/ai_docs/pi/toolkit.md +168 -0
- package/ai_docs/pi/tui.md +129 -0
- package/ai_docs/pi/usage.md +98 -0
- package/ai_docs/pi/virtual-models.md +118 -0
- package/ai_docs/pi/windows.md +71 -0
- package/apps/dpi-backend/.nvmrc +1 -0
- package/apps/dpi-backend/API.md +99 -0
- package/apps/dpi-backend/README.md +119 -0
- package/apps/dpi-backend/deploy/backend.env.example +9 -0
- package/apps/dpi-backend/deploy/dpi-backend.plist +26 -0
- package/apps/dpi-backend/deploy/dpi-backend.service +22 -0
- package/apps/dpi-backend/package.json +33 -0
- package/apps/dpi-backend/prompts/one_sentence.md +3 -0
- package/apps/dpi-backend/prompts/smart_ass_opus_fix.md +169 -0
- package/apps/dpi-backend/scripts/check-contract.mjs +151 -0
- package/apps/dpi-backend/scripts/migrate-playground.ts +66 -0
- package/apps/dpi-backend/scripts/record-nested-fixture.mts +154 -0
- package/apps/dpi-backend/scripts/token.ts +57 -0
- package/apps/dpi-backend/src/addons.ts +139 -0
- package/apps/dpi-backend/src/auth.ts +20 -0
- package/apps/dpi-backend/src/config.ts +54 -0
- package/apps/dpi-backend/src/contract-check.ts +118 -0
- package/apps/dpi-backend/src/errors.ts +50 -0
- package/apps/dpi-backend/src/extensions.ts +510 -0
- package/apps/dpi-backend/src/faux.ts +80 -0
- package/apps/dpi-backend/src/history.ts +165 -0
- package/apps/dpi-backend/src/idempotency.ts +20 -0
- package/apps/dpi-backend/src/jev.ts +43 -0
- package/apps/dpi-backend/src/jobs.ts +790 -0
- package/apps/dpi-backend/src/main.ts +76 -0
- package/apps/dpi-backend/src/model-catalog.ts +180 -0
- package/apps/dpi-backend/src/pairing-files.ts +142 -0
- package/apps/dpi-backend/src/pairing.ts +459 -0
- package/apps/dpi-backend/src/routes.ts +217 -0
- package/apps/dpi-backend/src/shared-state.ts +85 -0
- package/apps/dpi-backend/src/store.ts +1240 -0
- package/apps/dpi-backend/src/streams.ts +256 -0
- package/apps/dpi-backend/src/tunnel.ts +955 -0
- package/apps/dpi-backend/tsconfig.json +13 -0
- package/apps/dpi-playground/README.md +83 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-CFGc7lHT.js +77 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-DFdKpwm_.css +1 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/anthropic.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/deepseek.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/durable-pi.svg +14 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/gemini.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/grok.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/kimi.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/lab.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/meta.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/minimax.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/openai.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/qwen.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/xiaomi.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/zai.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/index.html +16 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/index.html +15 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/package.json +5 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/anthropic.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/deepseek.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/durable-pi.svg +14 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/gemini.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/grok.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/kimi.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/lab.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/meta.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/minimax.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/openai.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/qwen.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/xiaomi.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/zai.png +0 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/App.vue +180 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/commands.ts +119 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/composables.ts +129 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/hub.ts +40 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/icon-path.ts +7 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/ids.ts +2 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/jobs.ts +80 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/link-policy.ts +16 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/markdown-policy.ts +115 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/markdown.ts +60 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair-close.ts +23 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair-handoff.ts +56 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair.ts +166 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/qrcode.ts +323 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/signature.ts +146 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/store.ts +373 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/client/stream.ts +183 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/AgentView.vue +342 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Composer.vue +198 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ContextBar.vue +254 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DeviceCredentialsDialog.vue +40 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DeviceIcon.vue +56 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DirBrowser.vue +95 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ExtensionsPanel.vue +96 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/HelpDialog.vue +69 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Inspector.vue +425 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/JobCard.vue +221 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/MobileNav.vue +39 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ModelPicker.vue +71 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/NetworkSidebar.vue +236 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/NewSessionDialog.vue +114 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/OfflinePanel.vue +128 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/PairDeviceWizard.vue +433 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/PromptDialog.vue +89 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/StateEditor.vue +179 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TasksPanel.vue +280 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Toast.vue +11 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TokenManager.vue +175 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TopBar.vue +114 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TranscriptItem.vue +152 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TreeRow.vue +195 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TunnelStatusBar.vue +107 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/extensions.css +105 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/jobs.css +655 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/main.ts +22 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/network.css +675 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/src/style.css +3203 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/tsconfig.json +16 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/vite.config.ts +14 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/package.json +5 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/audit.ts +406 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/client-ip.ts +72 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/credentials.ts +265 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/guard.ts +43 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/http-errors.ts +156 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/identity.ts +60 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/limiter.ts +165 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/main.ts +460 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/monitor.ts +320 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/mux.ts +277 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/pairing.ts +816 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/proxy.ts +128 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/rotation.ts +583 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/seed.ts +11 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/transport.ts +108 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel-token.ts +11 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel-transport.ts +560 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel.ts +873 -0
- package/apps/dpi-playground/apps/dpi-playground-hub/tsconfig.json +13 -0
- package/apps/dpi-playground/package.json +37 -0
- package/apps/dpi-sdk/README.md +74 -0
- package/apps/dpi-sdk/package.json +20 -0
- package/apps/dpi-sdk/scripts/net.ts +73 -0
- package/apps/dpi-sdk/src/api.ts +631 -0
- package/apps/dpi-sdk/src/app-state.ts +111 -0
- package/apps/dpi-sdk/src/client.ts +330 -0
- package/apps/dpi-sdk/src/errors.ts +16 -0
- package/apps/dpi-sdk/src/jobs.ts +99 -0
- package/apps/dpi-sdk/src/models.ts +472 -0
- package/apps/dpi-sdk/src/node/chunk.ts +488 -0
- package/apps/dpi-sdk/src/node/config.ts +175 -0
- package/apps/dpi-sdk/src/node/drivers.ts +402 -0
- package/apps/dpi-sdk/src/node/ops.ts +114 -0
- package/apps/dpi-sdk/src/node/private-dir.ts +48 -0
- package/apps/dpi-sdk/src/node/registry-migration.ts +549 -0
- package/apps/dpi-sdk/src/node/registry.ts +390 -0
- package/apps/dpi-sdk/src/node/sse-writer.ts +151 -0
- package/apps/dpi-sdk/src/protocol/backoff.ts +31 -0
- package/apps/dpi-sdk/src/protocol/limits.ts +199 -0
- package/apps/dpi-sdk/src/protocol/pairing.ts +311 -0
- package/apps/dpi-sdk/src/protocol/rest-routes.ts +105 -0
- package/apps/dpi-sdk/src/protocol/tunnel.ts +331 -0
- package/apps/dpi-sdk/src/sse.ts +117 -0
- package/apps/dpi-sdk/src/transcript.ts +291 -0
- package/apps/dpi-sdk/tsconfig.json +13 -0
- package/apps/dpi-sdk/w +20 -0
- package/apps/dpi-tui/README.md +54 -0
- package/apps/dpi-tui/bin/dpi-tui.mjs +5 -0
- package/apps/dpi-tui/package.json +20 -0
- package/apps/dpi-tui/src/app.ts +433 -0
- package/apps/dpi-tui/src/commands.ts +151 -0
- package/apps/dpi-tui/src/main.ts +122 -0
- package/apps/dpi-tui/src/pairing.ts +287 -0
- package/apps/dpi-tui/src/pickers.ts +238 -0
- package/apps/dpi-tui/src/render.ts +164 -0
- package/apps/dpi-tui/src/theme.ts +57 -0
- package/apps/dpi-tui/tsconfig.json +14 -0
- package/bin/device.mjs +5 -0
- package/bin/hub.mjs +5 -0
- package/bin/tui.mjs +5 -0
- package/deploy/Caddyfile +120 -0
- package/docs/verification.md +102 -0
- package/dpi-network.example.yaml +62 -0
- package/images/01_durable_agent_network.svg +185 -0
- package/images/02_pi_made_durable.svg +204 -0
- package/images/03_restart_mid_tool.svg +168 -0
- package/images/07_one_api_every_client.svg +123 -0
- package/images/08_device_session_agent.svg +123 -0
- package/images/09_compact_keep_history.svg +143 -0
- package/images/10_shared_app_state.svg +177 -0
- package/images/11_durable_jobs_wait.svg +141 -0
- package/images/12_tools_change_mid_run.svg +231 -0
- package/images/13_hook_blocks_tool.svg +122 -0
- package/images/14_fork_at_any_entry.svg +119 -0
- package/images/15_web_client.png +0 -0
- package/images/16_terminal_client.svg +43 -0
- package/images/17_devices_are_data.svg +182 -0
- package/images/18_should_every_agent_be_durable.svg +118 -0
- package/images/19_sqlite_on_every_device.svg +155 -0
- package/images/20_admission_and_effects.svg +134 -0
- package/justfile +33 -0
- package/package.json +41 -4
- package/scripts/verify.mjs +210 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
> Source: [packages/coding-agent/src/experimental/vacation/README.md](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/src/experimental/vacation/README.md)
|
|
2
|
+
> Retrieved with Firecrawl: 2026-10-05. Repository snapshot: `b78e6a9085343ec0f308c3d377da72528b4cf7ee`.
|
|
3
|
+
> Relative documentation links point to local copies where available, otherwise to the source snapshot.
|
|
4
|
+
|
|
5
|
+
# vacation
|
|
6
|
+
|
|
7
|
+
A durable vacation planning agent with a TUI, built on `@earendil-works/pi-durable`. It is a copy of the durable coding
|
|
8
|
+
agent in [`../durable`](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/src/experimental/durable) with the coding tools and pi's coding prompt replaced by a vacation planner. It only
|
|
9
|
+
looks like a coding agent because it reuses pi's interactive TUI components.
|
|
10
|
+
|
|
11
|
+
From a pi checkout, after `npm install` and `npm run build`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node packages/coding-agent/src/experimental/vacation/main.ts
|
|
15
|
+
node packages/coding-agent/src/experimental/vacation/main.ts --continue
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Without a build, preload the source resolver so the workspace packages load from `src` instead of `dist`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node --import ./packages/coding-agent/src/experimental/source-resolver.ts packages/coding-agent/src/experimental/vacation/main.ts
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
A new session starts with pi's default model and thinking level from `settings.json`. `--continue` opens the newest
|
|
25
|
+
session for the current directory. Sessions live under
|
|
26
|
+
`~/.pi/agent/experimental/vacation-sessions/<cwd-hash>/<session>/session.sqlite`. Log in with pi itself; credentials are
|
|
27
|
+
shared.
|
|
28
|
+
|
|
29
|
+
## What it shows
|
|
30
|
+
|
|
31
|
+
- **Background subagent:** the `research` tool starts a subagent in its own conversation and returns at once. A
|
|
32
|
+
background task delivers the request and posts the subagent's report back to the main conversation as a new message,
|
|
33
|
+
so you keep chatting with the main agent while the research runs.
|
|
34
|
+
- **Parallel durable tool calls:** the subagent's `search` calls run in parallel, each as its own task in the task
|
|
35
|
+
panel. The results are canned; each topic takes a different time.
|
|
36
|
+
- **Recovery:** `search` is safe to rerun. Kill the process while a search runs and start it again with `--continue`:
|
|
37
|
+
finished searches are kept, the unfinished one runs again, and the report still arrives. Request IDs keep a restart
|
|
38
|
+
from sending the request or the report twice.
|
|
39
|
+
- **Everything else from the durable coding agent:** steering, follow-ups, `/agents`, `/compact`, `/tasks`, `/model`.
|
|
40
|
+
|
|
41
|
+
## Try it
|
|
42
|
+
|
|
43
|
+
1. "Plan a weekend in Vienna for two. Hand the research to a subagent."
|
|
44
|
+
2. While the searches run, ask the main agent something else.
|
|
45
|
+
3. Ctrl+C while only the trains search is left, then start again with `--continue`.
|
|
46
|
+
4. `/agents` to watch or steer the subagent, `/agents` again to go back to main.
|
|
47
|
+
|
|
48
|
+
## Layout
|
|
49
|
+
|
|
50
|
+
| file | role |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `vacation.ts` | the vacation extensions: `search`, `research`, the report task, and the prompt |
|
|
53
|
+
| `main.ts` | arguments, open, run, close |
|
|
54
|
+
| `sessions.ts` | session directories and the lock |
|
|
55
|
+
| `harness-setup.ts` | HTTP setup, settings, the registry, the initial model |
|
|
56
|
+
| `runtime.ts` | Harness and the plain `DurableView` and `DurableController` |
|
|
57
|
+
| `tui.ts` | rendering with pi's interactive components |
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
> Source: [packages/chord/README.md](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/chord/README.md)
|
|
2
|
+
> Retrieved with Firecrawl: 2026-10-05. Repository snapshot: `b78e6a9085343ec0f308c3d377da72528b4cf7ee`.
|
|
3
|
+
> Relative documentation links point to local copies where available, otherwise to the source snapshot.
|
|
4
|
+
|
|
5
|
+
# @earendil-works/chord
|
|
6
|
+
|
|
7
|
+
Chord is an application-composition runtime for systems assembled from
|
|
8
|
+
plugins/extensions. It provides facets, services, replicated state, and a
|
|
9
|
+
pluggable remote-service boundary. It is developed as a standalone package in
|
|
10
|
+
the Pi monorepo, but it is not a Pi package: it does not depend on any other Pi
|
|
11
|
+
workspace package and can be used by unrelated applications.
|
|
12
|
+
|
|
13
|
+
## What Chord is for
|
|
14
|
+
|
|
15
|
+
A single application feature may need to run in several environments: for
|
|
16
|
+
example, an agent worker, a terminal UI, and a remote WebUI. Chord provides the
|
|
17
|
+
generic machinery to write such extensions in a way that is both delightful for
|
|
18
|
+
humans as well as agents.
|
|
19
|
+
|
|
20
|
+
The design has a few connected pieces:
|
|
21
|
+
|
|
22
|
+
- **Plugins** are synchronous setup units that declare the services they provide
|
|
23
|
+
and require. After every plugin has declared its shape, a host validates the
|
|
24
|
+
complete dependency graph, binds services, activates providers before consumers,
|
|
25
|
+
and disposes resources in reverse dependency order. These units are called
|
|
26
|
+
*facets*.
|
|
27
|
+
|
|
28
|
+
- **Facets** are parts of a plugin. Each facet is bundled up separately and runs
|
|
29
|
+
in the process or environment where it's supposed to run. You can use facets
|
|
30
|
+
to split a plugin into separate pieces that need to be loaded into different
|
|
31
|
+
processes and environments (think backend, browser, TUI etc.)
|
|
32
|
+
|
|
33
|
+
- **Services** are typed, stable tokens with either one provider (**singleton**)
|
|
34
|
+
or dynamic keyed instances (**keyed**). A service can be process-local, with
|
|
35
|
+
an unrestricted JavaScript contract, or remotely exposable. Consumers retain a
|
|
36
|
+
stable facade while a provider disconnects or is replaced.
|
|
37
|
+
|
|
38
|
+
- **Replicated state** exposes authoritative state to local and remote
|
|
39
|
+
connected consumers. Producers publish atomic overlay transactions with
|
|
40
|
+
`change(context, callback)`; consumers receive complete immutable values. Draft
|
|
41
|
+
proxies exist only during the callback and become unusable afterward. Preparation
|
|
42
|
+
materializes a structurally shared immutable candidate and one exact decoded
|
|
43
|
+
operation batch, while each remote client/state stream owns independent path-codec
|
|
44
|
+
state. Replicas become unready on disconnect or replacement until rehydrated.
|
|
45
|
+
|
|
46
|
+
- **Delta tracking** records and coalesces operations over tracked plain JSON.
|
|
47
|
+
It preserves common string and array operations, supports durable base
|
|
48
|
+
batches, and validates untrusted operations as they are applied. Batches
|
|
49
|
+
guarantee convergence but are not canonical or necessarily minimal.
|
|
50
|
+
|
|
51
|
+
- **Remote service sources** advertise services available outside a facet host
|
|
52
|
+
and open bindings for the services its facets require. Bindings carry logical
|
|
53
|
+
calls and subscriptions through an application-supplied adapter. Chord
|
|
54
|
+
requires strict-JSON arguments, results, snapshots, updates, and catalogues,
|
|
55
|
+
but does not prescribe framing, routing, transport, or an application wire
|
|
56
|
+
envelope. `JsonRepresentation<T>` derives a wire-safe type for application data
|
|
57
|
+
with unknown payloads, while `isJsonValue()` validates received values at an
|
|
58
|
+
adapter boundary. Symmetric RPC peers are planned as one optional
|
|
59
|
+
implementation of this boundary.
|
|
60
|
+
|
|
61
|
+
- **Context** Chord provides a Go-like context system for cancellation and
|
|
62
|
+
invocation-scoped application values. Applications can carry permissions or
|
|
63
|
+
telemetry through those values without Chord depending on either.
|
|
64
|
+
|
|
65
|
+
The current runtime exports service tokens, singleton and keyed providers,
|
|
66
|
+
remote bindings, replicated state, facet hosts, and facet loaders from
|
|
67
|
+
`@earendil-works/chord`. Import public types and general runtime APIs from the
|
|
68
|
+
package root. Context constants and functions live in
|
|
69
|
+
`@earendil-works/chord/context` because their generic names should not pollute
|
|
70
|
+
the root API.
|
|
71
|
+
Chord-owned identifiers use the `chord.*` namespace and its reserved service
|
|
72
|
+
prefix is `$chord.*`.
|
|
73
|
+
|
|
74
|
+
## Remote service adapters
|
|
75
|
+
|
|
76
|
+
Chord owns its transport-independent service wire grammar. Consumer adapters
|
|
77
|
+
use `createServiceCatalogueCall()`, `createServiceSubscribeCall()`, and
|
|
78
|
+
`createServiceUnsubscribeCall()` for `$chord.service` control calls.
|
|
79
|
+
`createRemoteServiceEndpoint()` handles those calls for one provider consumer,
|
|
80
|
+
including subscription activation and cleanup. `parseServiceCall()`,
|
|
81
|
+
`parseServiceCatalogue()`, and the decoded/wire snapshot and update parsers
|
|
82
|
+
validate Chord semantics after an adapter has established a strict-JSON
|
|
83
|
+
boundary. `RemoteServiceErrorCode` and `REMOTE_SERVICE_ERROR_CODES` define the
|
|
84
|
+
service errors that may cross that boundary.
|
|
85
|
+
|
|
86
|
+
Replicated state operations use one `createServiceStateEncoder()` at the
|
|
87
|
+
provider side and one `createServiceStateDecoder()` at the consumer side for
|
|
88
|
+
each subscription. Those registries create an independent Delta path dictionary
|
|
89
|
+
for every instance/member state and reset it on replacement, unavailability,
|
|
90
|
+
close, or fresh hydration. Applications may place these values inside any
|
|
91
|
+
routing, request, response, or event envelope; Chord does not prescribe that
|
|
92
|
+
outer protocol.
|
|
93
|
+
|
|
94
|
+
Provider subscriptions atomically capture a snapshot and buffer subsequent
|
|
95
|
+
updates until activation. Activation and reentrant publication use the same FIFO.
|
|
96
|
+
The provider retains at most 100 pending updates per subscription, excluding the
|
|
97
|
+
running delivery. Adding update 101 replaces the entire pending queue with
|
|
98
|
+
`{ type: "reset", snapshot }`: a current full subscription snapshot whose state
|
|
99
|
+
members contain `[["r", value]]` and their new sequence baselines. It also captures
|
|
100
|
+
current instance membership, so discarded spawn/close/replacement events cannot
|
|
101
|
+
leave stale instances behind. Live keyed generations retain their existing handles.
|
|
102
|
+
The reset carries the context of the publication that triggered overflow.
|
|
103
|
+
|
|
104
|
+
Consumers and codecs must handle this explicit reset before accepting subsequent
|
|
105
|
+
ordinary deltas. A root operation in an ordinary `state` update does **not** permit
|
|
106
|
+
a sequence gap. Resets restart the subscription's path dictionaries, and later
|
|
107
|
+
deltas must be contiguous from each reset baseline. Transport adapters must
|
|
108
|
+
preserve snapshot/reset/update order; they must not drop encoded delta batches or
|
|
109
|
+
assume their own asynchronous queues are bounded by the provider's queue.
|
|
110
|
+
|
|
111
|
+
## Tracking JSON deltas
|
|
112
|
+
|
|
113
|
+
Import the standalone transactional tracker from `@earendil-works/chord/delta`:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { applyImmutable, track } from "@earendil-works/chord/delta";
|
|
117
|
+
|
|
118
|
+
const tracker = track({ output: "", count: 0 });
|
|
119
|
+
const change = tracker.beginChange();
|
|
120
|
+
change.state.output += "done\n";
|
|
121
|
+
change.state.count += 1;
|
|
122
|
+
const prepared = change.prepare();
|
|
123
|
+
|
|
124
|
+
tracker.adopt(prepared);
|
|
125
|
+
const replica = applyImmutable(prepared.base, prepared.ops);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`tracker.value` is always the latest adopted immutable revision. Preparation does
|
|
129
|
+
not change authority; adoption validates the preparation and swaps the root pointer.
|
|
130
|
+
Assigned containers are copied by value and unchanged subtrees may be shared between
|
|
131
|
+
revisions.
|
|
132
|
+
|
|
133
|
+
The tracker uses trusted immutable ownership rather than defensive copying or
|
|
134
|
+
freezing. `track(initial)`, `prepareReplace(value)`, `replicatedState(initial)`, and
|
|
135
|
+
`replace(context, value)` take ownership of alias-free strict-JSON roots without
|
|
136
|
+
walking them. Callers must not mutate transferred or published data. Values placed
|
|
137
|
+
through drafts are already copied, so that walk also rejects non-strict JSON before
|
|
138
|
+
the draft changes. Published values are not frozen. In-process loopback consumers
|
|
139
|
+
may share their containers with the provider; mutating a consumed value violates
|
|
140
|
+
the contract and can corrupt authority. Clone or serialize at any mutable trust
|
|
141
|
+
boundary.
|
|
142
|
+
|
|
143
|
+
Replicated state provides the same model through a callback:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const initial = { output: "", count: 0 };
|
|
147
|
+
const status = env.replicatedState(initial); // transfers ownership of initial
|
|
148
|
+
status.change(context, (draft) => {
|
|
149
|
+
draft.output += "done\n";
|
|
150
|
+
draft.count += 1;
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A successful `change()` publishes exactly one atomic revision. If its callback
|
|
155
|
+
throws, the original value and sequence remain unchanged. Draft handles become
|
|
156
|
+
unusable when the callback returns. Chord emits string append and front-truncate
|
|
157
|
+
operations, array splices and permutations, sets, and deletes; large edit sets may
|
|
158
|
+
fold into a complete replacement. Remote connection plumbing encodes each batch
|
|
159
|
+
independently for every client/state pairing.
|
|
160
|
+
|
|
161
|
+
### Public state subscriptions
|
|
162
|
+
|
|
163
|
+
`state.subscribe(async (value, context, delivery) => { ... })` serializes callbacks
|
|
164
|
+
independently for each subscription. Hydration starts immediately when a value is
|
|
165
|
+
available, and its returned promise must settle before updates start. Synchronous
|
|
166
|
+
callbacks still run synchronously. Read the captured `value` inside an asynchronous
|
|
167
|
+
callback: `state.value` may already refer to a later revision.
|
|
168
|
+
|
|
169
|
+
Each subscription retains at most 100 pending complete-value deliveries, excluding
|
|
170
|
+
the running callback. On overflow, only the newest pending value, context, and
|
|
171
|
+
delivery metadata are retained; a not-yet-started initial hydration is preserved.
|
|
172
|
+
Thus public delivery sequences may skip. This is a frame-count policy, not a byte
|
|
173
|
+
limit. Internal exact-operation subscriptions remain synchronous and receive every
|
|
174
|
+
revision, independently of slow public callbacks.
|
|
175
|
+
|
|
176
|
+
The returned unsubscribe function immediately discards pending work and prevents
|
|
177
|
+
new callbacks. It neither aborts nor waits for the running callback. Synchronous
|
|
178
|
+
throws and promise rejections are observed, reported, and do not stop other
|
|
179
|
+
subscriptions or subsequent callbacks. Attached sources and remote bindings use
|
|
180
|
+
their `onError` handler; local mutable states (and attached sources without a
|
|
181
|
+
handler) report failures by throwing in a microtask. Unsubscribing does not hide a
|
|
182
|
+
later rejection from the running callback.
|
|
183
|
+
|
|
184
|
+
The standalone [Delta guide](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/chord/src/delta/README.md) defines the complete ownership,
|
|
185
|
+
lifecycle, operation, and replica contracts.
|
|
186
|
+
|
|
187
|
+
## Bundling and loading facets
|
|
188
|
+
|
|
189
|
+
`@earendil-works/chord/bundler` uses esbuild to turn ESM or TypeScript application
|
|
190
|
+
entries into independent, content-addressed CommonJS files. The package-level API
|
|
191
|
+
reads plugin identity and build configuration from `package.json`, then applies
|
|
192
|
+
facet path conventions supplied by the host application:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"name": "@example/my-plugin",
|
|
197
|
+
"version": "1.0.0",
|
|
198
|
+
"type": "module",
|
|
199
|
+
"peerDependencies": {
|
|
200
|
+
"@earendil-works/chord": "^0.84.4"
|
|
201
|
+
},
|
|
202
|
+
"chord": {
|
|
203
|
+
"facets": {
|
|
204
|
+
"worker": "./src/custom-worker.ts",
|
|
205
|
+
"presentation": false
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { bundleFacetPackage } from "@earendil-works/chord/bundler";
|
|
213
|
+
|
|
214
|
+
await bundleFacetPackage({
|
|
215
|
+
packagePath: "/path/to/my-plugin",
|
|
216
|
+
outdir: "/application-owned/plugin-builds/my-plugin",
|
|
217
|
+
defaultFacets: {
|
|
218
|
+
worker: "src/worker.ts",
|
|
219
|
+
presentation: "src/presentation.ts",
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Existing conventional files become entries unless `chord.facets` overrides or
|
|
225
|
+
disables them. Peer dependencies are externalized and resolved against the host
|
|
226
|
+
when loading. Chord never installs dependencies or runs package lifecycle
|
|
227
|
+
scripts. `bundleFacets()` remains available as the lower-level API for callers
|
|
228
|
+
that already have explicit plugin identity and entry mappings.
|
|
229
|
+
|
|
230
|
+
The output directory contains one `.cjs` file per entry plus
|
|
231
|
+
`chord-facets.json`. Load one application-selected entry through the Node-only
|
|
232
|
+
loader:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { createFacetBundleLoader } from "@earendil-works/chord/node";
|
|
236
|
+
|
|
237
|
+
const loader = createFacetBundleLoader({
|
|
238
|
+
manifestPath: "/application-owned/plugin-builds/my-plugin/chord-facets.json",
|
|
239
|
+
entry: "worker",
|
|
240
|
+
resolveExternal: (specifier) => import.meta.resolve(specifier),
|
|
241
|
+
});
|
|
242
|
+
const loaded = await loader.load();
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Each `load()` verifies SHA-256 integrity and compiles the CommonJS body directly
|
|
246
|
+
with `node:vm` instead of putting the plugin into Node's CommonJS or ESM module
|
|
247
|
+
cache. Externals are resolved by the host and loaded through a restricted
|
|
248
|
+
`require`; esbuild lowers dynamic imports so they use the same path. Disposing a
|
|
249
|
+
retired generation releases the loader's facet references, making its compiled
|
|
250
|
+
code eligible for garbage collection once plugin-owned resources are also gone.
|
|
251
|
+
|
|
252
|
+
For transport to another Node host, `readFacetBundleArtifact()` packages one
|
|
253
|
+
verified manifest entry with its source, and `createFacetBundleArtifactLoader()`
|
|
254
|
+
materializes fresh temporary generations while resolving externals against the
|
|
255
|
+
receiving host.
|
|
256
|
+
|
|
257
|
+
To reload, load a candidate, pass its facets to `FacetHost.reload()`, dispose the
|
|
258
|
+
candidate on failure, and dispose the retired `LoadedFacets` only after a
|
|
259
|
+
successful cutover. The host activates and validates the candidate while the
|
|
260
|
+
currently active providers remain routed, then replaces each singleton directly
|
|
261
|
+
without an unavailable interval. Stable service handles therefore do not become
|
|
262
|
+
disconnected during an ordinary reload. Keyed instances
|
|
263
|
+
remain incarnation-specific and replacements receive fresh generations. The
|
|
264
|
+
bundler writes a complete temporary directory before replacing the previous
|
|
265
|
+
output, so loaders do not observe partially built generations.
|
|
266
|
+
|
|
267
|
+
See [PLANNING.md](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/chord/PLANNING.md) for the broader RPC and generation-loading
|
|
268
|
+
architecture.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
> Source: [packages/coding-agent/docs/cli-integration.md](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/docs/cli-integration.md)
|
|
2
|
+
> Retrieved with Firecrawl: 2026-10-05. Repository snapshot: `b78e6a9085343ec0f308c3d377da72528b4cf7ee`.
|
|
3
|
+
> Relative documentation links point to local copies where available, otherwise to the source snapshot.
|
|
4
|
+
|
|
5
|
+
# CLI Integration
|
|
6
|
+
|
|
7
|
+
By default, running `pi` opens the interactive terminal interface. When input or output is piped or redirected, Pi uses print mode instead. You can also select print, JSON, or RPC mode explicitly for scripts and applications.
|
|
8
|
+
|
|
9
|
+
All four modes use the same agent, sessions, resources, and tools. The mode determines how input enters Pi, how output is exposed, and whether the process remains available for more commands.
|
|
10
|
+
|
|
11
|
+
The SDK is not a CLI mode. It embeds the agent directly in a Node.js or Bun process. See the [SDK](sdk.md) when direct TypeScript access is preferable to a process boundary.
|
|
12
|
+
|
|
13
|
+
## Choose a mode
|
|
14
|
+
|
|
15
|
+
| Mode | Interface | Lifetime | Use it when |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| Interactive | Terminal UI | Until the user exits | A person is working with Pi directly |
|
|
18
|
+
| Print | Final text on stdout | One invocation | A script needs the final assistant response |
|
|
19
|
+
| JSON | JSONL events on stdout | One invocation | A process needs structured progress from a run |
|
|
20
|
+
| RPC | JSONL commands, responses, and events | Long-lived | A process needs bidirectional control |
|
|
21
|
+
|
|
22
|
+
CLI options still select the working directory, model, tools, resources, and session persistence independently of the mode. See [Command Line](cli.md) for the complete startup options.
|
|
23
|
+
|
|
24
|
+
## Print to stdout
|
|
25
|
+
|
|
26
|
+
Print mode runs the supplied prompts, writes the final assistant text to stdout, and exits:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pi --print "Summarize the changes in this repository"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use print mode when only the final text is needed, including command substitution, pipelines, and one-shot jobs. Intermediate events are not exposed.
|
|
33
|
+
|
|
34
|
+
Print mode writes errors to stderr. A final assistant response with an `error` or `aborted` stop reason produces a nonzero exit status.
|
|
35
|
+
|
|
36
|
+
When no mode is selected explicitly, non-TTY stdin or stdout also selects print mode. This allows piped input and output without adding `--print`.
|
|
37
|
+
|
|
38
|
+
## Stream JSON events
|
|
39
|
+
|
|
40
|
+
JSON mode writes a session header followed by agent and session events as newline-delimited JSON:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pi --mode json "Review this repository" > events.jsonl
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is structured event output, not a single JSON result or a constraint on the format of the model’s response.
|
|
47
|
+
|
|
48
|
+
All prompts are supplied when the process starts. The process streams events for that run and then exits; it does not accept later commands.
|
|
49
|
+
|
|
50
|
+
A failed or aborted assistant response appears in the event stream but does not by itself produce a nonzero exit status. Inspect the events when success or failure matters. Pi still exits nonzero if the invocation throws an error.
|
|
51
|
+
|
|
52
|
+
Streaming `message_update` records contain deltas rather than a growing message snapshot. Assemble live output from the delta events, then replace it with the authoritative message from `message_end`.
|
|
53
|
+
|
|
54
|
+
`agent_end` can be followed by automatic recovery or queued work. `agent_settled` marks the end of automatic work for the current run.
|
|
55
|
+
|
|
56
|
+
Stdout is reserved for JSONL. Diagnostics and application logging are written to stderr. See [JSON Event Stream](json.md) for framing, event shapes, and reconstruction rules.
|
|
57
|
+
|
|
58
|
+
## Control Pi with RPC
|
|
59
|
+
|
|
60
|
+
RPC mode keeps Pi running while another process sends commands and receives responses and events:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pi --mode rpc --no-session
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Commands are JSON objects written to stdin. Responses and events are JSON objects written to stdout. Every record occupies one line.
|
|
67
|
+
|
|
68
|
+
Add an `id` to commands that need correlation. The matching response repeats that ID. Events generally have no command ID because they describe session activity rather than one request.
|
|
69
|
+
|
|
70
|
+
A successful `prompt` response means the prompt was accepted, queued, or handled. It does not mean the run completed. Continue consuming events through `agent_settled` when completion matters.
|
|
71
|
+
|
|
72
|
+
RPC commands can change models, inspect state, manage sessions, run shell commands, and answer extension UI requests.
|
|
73
|
+
|
|
74
|
+
Extension dialogs form a request-response subprotocol. Other extension UI updates are notifications that a client may display or ignore. TUI-only extension capabilities are unavailable or degraded outside interactive mode.
|
|
75
|
+
|
|
76
|
+
For Node.js or TypeScript integrations, prefer `RpcClient` from `@earendil-works/pi-coding-agent`. It starts a Pi RPC child process, correlates requests, exposes typed command methods, and delivers session events to listeners.
|
|
77
|
+
|
|
78
|
+
The [RPC client example](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/examples/rpc-client.ts) sends one prompt, streams text and tool activity, waits for `agent_settled`, and shuts down the child process. It is included in the repository’s TypeScript checks.
|
|
79
|
+
|
|
80
|
+
`RpcClient.promptAndWait()` installs its event listener before sending the prompt, avoiding a race with fast completions. For separate operations, subscribe before calling `prompt()` and call `waitForIdle()` only while a run is active.
|
|
81
|
+
|
|
82
|
+
The client requires a path to a runnable Pi CLI. The repository example points at `dist/cli.js`, so the package must be built before that example runs from a checkout.
|
|
83
|
+
|
|
84
|
+
If you are building a client without `RpcClient`, start with [RPC Protocol](rpc.md), then use [RPC Commands](rpc-commands.md) and [JSON Event Stream](json.md) as the wire references.
|
|
85
|
+
|
|
86
|
+
## Fork and rebrand Pi
|
|
87
|
+
|
|
88
|
+
A source fork can change the CLI name and configuration directory through `package.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"piConfig": {
|
|
93
|
+
"name": "my-agent",
|
|
94
|
+
"configDir": ".my-agent"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Change the top-level `bin` field to set the executable name. These settings affect the CLI banner, configuration paths, and derived environment variable names.
|
|
100
|
+
|
|
101
|
+
## Examples and references
|
|
102
|
+
|
|
103
|
+
- [RPC client](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/examples/rpc-client.ts): typed Node.js integration
|
|
104
|
+
- [RPC extension UI](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/examples/rpc-extension-ui.ts): custom terminal client with extension dialogs
|
|
105
|
+
- [Command Line](cli.md): startup options and mode selection
|
|
106
|
+
- [JSON Event Stream](json.md): JSON event reference
|
|
107
|
+
- [RPC Protocol](rpc.md): RPC lifecycle, framing, errors, and shutdown
|
|
108
|
+
- [RPC Commands](rpc-commands.md): command and response reference
|
|
109
|
+
- [RPC Extension UI](rpc-extension-ui.md): extension interaction subprotocol
|
|
110
|
+
- [SDK examples](https://github.com/earendil-works/pi/blob/b78e6a9085343ec0f308c3d377da72528b4cf7ee/packages/coding-agent/examples/sdk): in-process TypeScript integrations
|