@a5c-ai/babysitter-paperclip 0.0.2-staging.29187771

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/BABYSITTER.md ADDED
@@ -0,0 +1,46 @@
1
+ # Babysitter Paperclip Plugin
2
+
3
+ ## Build
4
+
5
+ ```bash
6
+ cd plugins/babysitter-paperclip
7
+ npm run build
8
+ ```
9
+
10
+ Build uses esbuild (configured in `esbuild.config.mjs`). Output goes to `dist/`.
11
+
12
+ ## Dev
13
+
14
+ ```bash
15
+ cd plugins/babysitter-paperclip
16
+ npm run dev
17
+ ```
18
+
19
+ Starts the Paperclip plugin dev server on port 4177 with hot reload for UI components.
20
+
21
+ ## Test
22
+
23
+ ```bash
24
+ cd plugins/babysitter-paperclip
25
+ npm run test
26
+ ```
27
+
28
+ Uses vitest. Test files follow the `*.test.ts` convention in `__tests__/` directories.
29
+
30
+ ## Key Files
31
+
32
+ | File | Role |
33
+ |------|------|
34
+ | `src/worker.ts` | Worker entry point -- event handlers, data handlers, action handlers, stream handler, tool registration |
35
+ | `src/manifest.ts` | Plugin manifest -- capabilities, event subscriptions, UI slots, settings schema |
36
+ | `src/babysitter-bridge.ts` | CLI bridge -- typed wrappers around `babysitter` CLI commands (`run:create`, `run:iterate`, `task:post`, etc.) |
37
+ | `src/delegating-adapter.ts` | Harness detection -- three-tier detection of underlying AI harness from Paperclip agent metadata |
38
+ | `src/types.ts` | Shared types -- `TrackedRun`, `PendingBreakpoint`, `RunsOverview`, `RunDetail`, `HarnessDetectionResult`, `ADAPTER_TYPE_MAP` |
39
+ | `src/ui/` | React UI components -- `BabysitterDashboard`, `RunDetailTab`, `BreakpointApproval`, `BabysitterSidebar` |
40
+
41
+ ## Dependencies
42
+
43
+ - `@paperclipai/plugin-sdk` (peer, >=0.1.0) -- Paperclip plugin SDK
44
+ - `@a5c-ai/babysitter-sdk` (workspace) -- Babysitter SDK for types and utilities
45
+ - `react` / `react-dom` (dev, ^19.0.0) -- UI component rendering
46
+ - `esbuild` (dev) -- Build tooling
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # @a5c-ai/babysitter-paperclip
2
+
3
+ Babysitter orchestration plugin for Paperclip AI. Integrates Babysitter's
4
+ deterministic, event-sourced orchestration with Paperclip's multi-agent
5
+ platform, providing run lifecycle management, breakpoint approval workflows,
6
+ and real-time observability through Paperclip's UI slot system.
7
+
8
+ ## Installation
9
+
10
+ Install the Babysitter SDK CLI globally:
11
+
12
+ ```bash
13
+ npm install -g @a5c-ai/babysitter-sdk
14
+ ```
15
+
16
+ Then install the plugin from the monorepo:
17
+
18
+ ```bash
19
+ cd babysitter
20
+ npm install
21
+ npm run build --workspace=@a5c-ai/babysitter-paperclip
22
+ ```
23
+
24
+ In Paperclip, register the plugin by pointing to the built `dist/` output or
25
+ by adding `@a5c-ai/babysitter-paperclip` to your Paperclip workspace plugin
26
+ list.
27
+
28
+ ## Configuration
29
+
30
+ Settings are declared in the plugin manifest and configurable through
31
+ Paperclip's plugin settings UI:
32
+
33
+ | Setting | Type | Default | Description |
34
+ |---------|------|---------|-------------|
35
+ | `runsDir` | string | `.a5c/runs` | Directory where babysitter run data is stored |
36
+ | `autoIterate` | boolean | `true` | Automatically iterate runs when effects are resolved |
37
+ | `maxIterations` | number | `256` | Maximum orchestration iterations per run |
38
+ | `breakpointTimeout` | number | `3600000` | Time to wait for breakpoint approval (ms, default 1 hour) |
39
+
40
+ ## Architecture Overview
41
+
42
+ ### Delegating Adapter Model
43
+
44
+ Paperclip wraps multiple AI harnesses (Claude Code, Codex, Gemini CLI, Cursor,
45
+ GitHub Copilot, etc.). The plugin detects which underlying harness each agent
46
+ uses through a three-tier detection system:
47
+
48
+ 1. **Agent metadata (high confidence)** -- Maps the Paperclip agent
49
+ `adapterType` field to a babysitter harness name. Known mappings:
50
+
51
+ | Paperclip adapterType | Babysitter harness |
52
+ |-----------------------|-------------------|
53
+ | `claude_local` | `claude-code` |
54
+ | `codex_local` | `codex` |
55
+ | `gemini_local` | `gemini-cli` |
56
+ | `cursor_local` | `cursor` |
57
+ | `github_copilot` | `github-copilot` |
58
+ | `opencode_local` | `opencode` |
59
+ | `pi_local` | `pi` |
60
+ | `omp_local` | `oh-my-pi` |
61
+
62
+ 2. **Environment variable probing (medium confidence)** -- Checks for known
63
+ harness signatures: `CLAUDE_CODE_SESSION`, `CODEX_SESSION`,
64
+ `GEMINI_CLI_SESSION`, `CURSOR_SESSION`, etc.
65
+
66
+ 3. **Plugin config fallback (medium confidence)** -- Uses the `defaultHarness`
67
+ setting. If nothing matches, defaults to `claude-code` with low confidence.
68
+
69
+ ### Worker
70
+
71
+ The worker (`src/worker.ts`) is the server-side entry point. It registers:
72
+
73
+ - **Event handlers** for `agent.run.started`, `agent.run.finished`,
74
+ `agent.run.failed`, and `agent.run.cancelled` lifecycle events
75
+ - **Data handlers** for `runs-overview`, `run-detail`, and
76
+ `pending-breakpoints` queries
77
+ - **Action handlers** for `approve-breakpoint`, `reject-breakpoint`, and
78
+ `create-run` operations
79
+ - **Stream handler** for `subscribe-run-events` (real-time journal events)
80
+ - **Tool** `babysitter-status` -- available to agents for checking run state
81
+
82
+ All babysitter operations go through the CLI bridge (`src/babysitter-bridge.ts`)
83
+ which shells out to the `babysitter` CLI with `--json` output.
84
+
85
+ ### UI Components
86
+
87
+ The plugin registers three UI slots in Paperclip:
88
+
89
+ | Slot Type | Component | Description |
90
+ |-----------|-----------|-------------|
91
+ | `dashboardWidget` | `BabysitterDashboard` | Active runs overview with pending breakpoint badge count |
92
+ | `detailTab` | `RunDetailTab` | Journal timeline view with breakpoint approval/rejection forms (scoped to agent entities) |
93
+ | `sidebarPanel` | `BabysitterSidebar` | Compact run status sidebar |
94
+
95
+ ## Event Flow
96
+
97
+ ```
98
+ Paperclip Platform
99
+ +----------------------------------------------------------+
100
+ | |
101
+ | Agent starts run |
102
+ | | |
103
+ | v |
104
+ | agent.run.started -----> Worker: detectHarness() |
105
+ | | |
106
+ | v |
107
+ | Store harness |
108
+ | detection in |
109
+ | plugin state |
110
+ +----------------------------------------------------------+
111
+ |
112
+ | (babysitter CLI bridge)
113
+ v
114
+ +----------------------------------------------------------+
115
+ | Babysitter Orchestration |
116
+ | |
117
+ | run:create ---> run:iterate ---> Pending effects? |
118
+ | | |
119
+ | +---------+-------+-------+ |
120
+ | | | | |
121
+ | breakpoint task sleep |
122
+ | | | | |
123
+ +------------------------|---------+---------------|-------+
124
+ | |
125
+ +------------|-------------------------+
126
+ | v
127
+ +-----------|-------------------------------------------------+
128
+ | | Paperclip UI |
129
+ | v |
130
+ | Worker streams pending breakpoint to UI |
131
+ | | |
132
+ | v |
133
+ | RunDetailTab renders approval form |
134
+ | BabysitterDashboard shows badge count |
135
+ | | |
136
+ | v |
137
+ | User clicks Approve / Reject |
138
+ | | |
139
+ | v |
140
+ | Action handler: approve-breakpoint / reject-breakpoint |
141
+ | | |
142
+ +-------|----------------------------------------------------- +
143
+ |
144
+ | (babysitter CLI bridge)
145
+ v
146
+ +----------------------------------------------------------+
147
+ | babysitter task:post <runDir> <effectId> |
148
+ | --status ok |
149
+ | --value-inline '{"approved": true}' (approve) |
150
+ | --value-inline '{"approved": false, (reject) |
151
+ | "feedback": "..."}' |
152
+ | |
153
+ | EFFECT_RESOLVED appended to journal |
154
+ | Next iteration replays with resolved effect |
155
+ +----------------------------------------------------------+
156
+ ```
157
+
158
+ ### Breakpoint Flow (Critical Detail)
159
+
160
+ Both approval and rejection use `--status ok` when posting via `task:post`.
161
+ The distinction is in the value payload:
162
+
163
+ - **Approve**: `{ approved: true, response: "..." }`
164
+ - **Reject**: `{ approved: false, feedback: "..." }`
165
+
166
+ This matches babysitter's breakpoint contract where `BreakpointResult.approved`
167
+ controls the process branch, not the task status. Never use `--status error`
168
+ for rejections.
169
+
170
+ When `autoIterate` is enabled (default), the worker automatically calls
171
+ `run:iterate` after resolving a breakpoint so the process continues without
172
+ manual intervention.
173
+
174
+ ## Plugin Events
175
+
176
+ The plugin emits custom events on the Paperclip event bus:
177
+
178
+ | Event | When |
179
+ |-------|------|
180
+ | `plugin.babysitter.run.created` | A new babysitter run is created via the `create-run` action |
181
+ | `plugin.babysitter.breakpoint.requested` | A breakpoint effect is pending approval |
182
+ | `plugin.babysitter.breakpoint.resolved` | A breakpoint is approved or rejected |
183
+
184
+ ## Troubleshooting
185
+
186
+ ### "babysitter" CLI not found
187
+
188
+ The plugin shells out to the `babysitter` CLI. Ensure it is installed globally
189
+ and available on `PATH`:
190
+
191
+ ```bash
192
+ npm install -g @a5c-ai/babysitter-sdk
193
+ babysitter version
194
+ ```
195
+
196
+ ### Runs directory not found
197
+
198
+ The default `runsDir` is `.a5c/runs` relative to the workspace. If your
199
+ project uses a different path, update the plugin setting or set the
200
+ `BABYSITTER_RUNS_DIR` environment variable.
201
+
202
+ ### Harness detection shows "fallback" / low confidence
203
+
204
+ The plugin could not determine which underlying harness the agent uses. Either:
205
+ 1. Set the agent's `adapterType` field in Paperclip to one of the known values
206
+ (e.g., `claude_local`, `codex_local`)
207
+ 2. Set the `defaultHarness` plugin config to your preferred harness name
208
+ 3. Ensure the harness CLI sets its expected environment variables
209
+
210
+ ### Breakpoint times out
211
+
212
+ The default breakpoint timeout is 1 hour (3,600,000 ms). Increase the
213
+ `breakpointTimeout` setting if your workflow requires longer approval windows.
214
+
215
+ ### State cache mismatch
216
+
217
+ If run status appears stale, rebuild the state cache:
218
+
219
+ ```bash
220
+ babysitter run:rebuild-state .a5c/runs/<runId>
221
+ ```
222
+
223
+ ### Journal corruption
224
+
225
+ Repair a corrupted journal:
226
+
227
+ ```bash
228
+ babysitter run:repair-journal .a5c/runs/<runId>
229
+ ```
@@ -0,0 +1,12 @@
1
+ import { createPluginBundlerPresets } from "@paperclipai/plugin-sdk/bundlers";
2
+
3
+ const presets = createPluginBundlerPresets({ uiEntry: "src/ui/index.tsx" });
4
+
5
+ // Worker bundle
6
+ await presets.esbuild.worker();
7
+
8
+ // Manifest bundle
9
+ await presets.esbuild.manifest();
10
+
11
+ // UI bundle
12
+ await presets.esbuild.ui();
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@a5c-ai/babysitter-paperclip",
3
+ "version": "0.0.2-staging.29187771",
4
+ "description": "Babysitter orchestration plugin for Paperclip AI",
5
+ "type": "module",
6
+ "main": "dist/worker.js",
7
+ "scripts": {
8
+ "build": "node esbuild.config.mjs",
9
+ "dev": "paperclip-plugin-dev-server --root . --ui-dir dist/ui --port 4177",
10
+ "test": "vitest run",
11
+ "deploy": "npm publish --access public",
12
+ "deploy:staging": "npm publish --access public --tag staging"
13
+ },
14
+ "peerDependencies": {
15
+ "@paperclipai/plugin-sdk": ">=0.1.0"
16
+ },
17
+ "dependencies": {
18
+ "@a5c-ai/babysitter-sdk": "0.0.187-staging.29187771"
19
+ },
20
+ "devDependencies": {
21
+ "@paperclipai/plugin-sdk": "latest",
22
+ "esbuild": "^0.21.0",
23
+ "react": "^19.0.0",
24
+ "react-dom": "^19.0.0",
25
+ "@types/react": "^19.0.0",
26
+ "typescript": "^5.5.0",
27
+ "vitest": "^3.0.0"
28
+ }
29
+ }
@@ -0,0 +1,85 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
2
+ import { detectHarness, mapAdapterType } from "../delegating-adapter";
3
+
4
+ describe("detectHarness", () => {
5
+ // Save and clear env vars that trigger Tier 2 detection
6
+ const envKeys = [
7
+ "CLAUDE_CODE_SESSION", "CLAUDE_CODE_ENTRYPOINT",
8
+ "CODEX_SESSION", "CODEX_HOME",
9
+ "GEMINI_CLI_SESSION", "GOOGLE_GENAI_API_KEY",
10
+ "CURSOR_SESSION",
11
+ ];
12
+ const savedEnv: Record<string, string | undefined> = {};
13
+
14
+ beforeEach(() => {
15
+ for (const key of envKeys) {
16
+ savedEnv[key] = process.env[key];
17
+ delete process.env[key];
18
+ }
19
+ });
20
+
21
+ afterEach(() => {
22
+ for (const key of envKeys) {
23
+ if (savedEnv[key] !== undefined) {
24
+ process.env[key] = savedEnv[key];
25
+ } else {
26
+ delete process.env[key];
27
+ }
28
+ }
29
+ });
30
+
31
+ test("Tier 1: maps known Paperclip adapter types to babysitter harness names", () => {
32
+ expect(detectHarness("claude_local")).toEqual({
33
+ harnessName: "claude-code",
34
+ detectionTier: "agent-metadata",
35
+ confidence: "high",
36
+ });
37
+ expect(detectHarness("codex_local")).toEqual({
38
+ harnessName: "codex",
39
+ detectionTier: "agent-metadata",
40
+ confidence: "high",
41
+ });
42
+ expect(detectHarness("gemini_local")).toEqual({
43
+ harnessName: "gemini-cli",
44
+ detectionTier: "agent-metadata",
45
+ confidence: "high",
46
+ });
47
+ });
48
+
49
+ test("Tier 3: falls back to plugin config when adapter type unknown", () => {
50
+ expect(detectHarness("unknown_adapter", { defaultHarness: "cursor" })).toEqual({
51
+ harnessName: "cursor",
52
+ detectionTier: "config",
53
+ confidence: "medium",
54
+ });
55
+ });
56
+
57
+ test("Tier 4: defaults to claude-code when no detection succeeds", () => {
58
+ expect(detectHarness(undefined)).toEqual({
59
+ harnessName: "claude-code",
60
+ detectionTier: "fallback",
61
+ confidence: "low",
62
+ });
63
+ });
64
+
65
+ test("handles undefined adapter type with no config", () => {
66
+ expect(detectHarness(undefined, {})).toEqual({
67
+ harnessName: "claude-code",
68
+ detectionTier: "fallback",
69
+ confidence: "low",
70
+ });
71
+ });
72
+ });
73
+
74
+ describe("mapAdapterType", () => {
75
+ test("maps known types", () => {
76
+ expect(mapAdapterType("claude_local")).toBe("claude-code");
77
+ expect(mapAdapterType("pi_local")).toBe("pi");
78
+ expect(mapAdapterType("omp_local")).toBe("oh-my-pi");
79
+ });
80
+
81
+ test("returns undefined for unknown types", () => {
82
+ expect(mapAdapterType("unknown")).toBeUndefined();
83
+ expect(mapAdapterType("")).toBeUndefined();
84
+ });
85
+ });
@@ -0,0 +1,29 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { ADAPTER_TYPE_MAP } from "../types";
3
+
4
+ describe("ADAPTER_TYPE_MAP", () => {
5
+ test("maps all known Paperclip adapter types", () => {
6
+ expect(ADAPTER_TYPE_MAP).toEqual({
7
+ claude_local: "claude-code",
8
+ codex_local: "codex",
9
+ gemini_local: "gemini-cli",
10
+ cursor_local: "cursor",
11
+ github_copilot: "github-copilot",
12
+ opencode_local: "opencode",
13
+ pi_local: "pi",
14
+ omp_local: "oh-my-pi",
15
+ });
16
+ });
17
+
18
+ test("covers all expected harnesses", () => {
19
+ const harnesses = Object.values(ADAPTER_TYPE_MAP);
20
+ expect(harnesses).toContain("claude-code");
21
+ expect(harnesses).toContain("codex");
22
+ expect(harnesses).toContain("gemini-cli");
23
+ expect(harnesses).toContain("cursor");
24
+ expect(harnesses).toContain("github-copilot");
25
+ expect(harnesses).toContain("opencode");
26
+ expect(harnesses).toContain("pi");
27
+ expect(harnesses).toContain("oh-my-pi");
28
+ });
29
+ });