@hachej/boring-workspace 0.1.17 → 0.1.18
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/README.md +36 -34
- package/dist/{FileTree-Dvaud3jU.js → FileTree-DHVB9rpk.js} +15 -15
- package/dist/{MarkdownEditor-sLkqTXDj.js → MarkdownEditor-L1KDH0bM.js} +1 -1
- package/dist/{WorkspaceLoadingState-zLzh1tGc.js → WorkspaceLoadingState-DYDxUYnx.js} +114 -110
- package/dist/WorkspaceProvider-CDPaAO5u.js +5971 -0
- package/dist/app-front.d.ts +94 -107
- package/dist/app-front.js +243 -233
- package/dist/app-server.d.ts +130 -15
- package/dist/app-server.js +1569 -304
- package/dist/{bootstrapServer-BreQ9QBc.d.ts → createInMemoryBridge-BDxDzihm.d.ts} +11 -26
- package/dist/manifest-CyNNdfYz.d.ts +58 -0
- package/dist/plugin.d.ts +199 -0
- package/dist/plugin.js +300 -0
- package/dist/server.d.ts +239 -4
- package/dist/server.js +901 -78
- package/dist/shared.d.ts +4 -112
- package/dist/surface-COYagY2m.d.ts +111 -0
- package/dist/testing.d.ts +19 -1
- package/dist/testing.js +2 -2
- package/dist/{agent-tool-DEtfQPVB.d.ts → ui-bridge-Gfh1MMgl.d.ts} +30 -30
- package/dist/workspace.css +36 -0
- package/dist/workspace.d.ts +165 -120
- package/dist/workspace.js +330 -377
- package/docs/INTERFACES.md +9 -9
- package/docs/PLUGIN_STRUCTURE.md +39 -145
- package/docs/PLUGIN_SYSTEM.md +355 -0
- package/docs/README.md +6 -1
- package/docs/plans/README.md +1 -0
- package/docs/plans/archive/HOT_RELOADABLE_AGENT_PLUGINS_PLAN.md +218 -0
- package/docs/plans/archive/RELOAD_PLUGGABILITY_PLAN.md +174 -0
- package/docs/plans/archive/UNIFIED_PLUGIN_SYSTEM_PLAN.md +769 -0
- package/package.json +11 -5
- package/dist/CommandPalette-CJHuTJlD.js +0 -5716
- package/docs/bridge.md +0 -135
- package/docs/panels.md +0 -102
- package/docs/plugins.md +0 -158
- /package/docs/plans/{MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md → archive/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md} +0 -0
package/docs/INTERFACES.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Last updated: 2026-05-02
|
|
4
4
|
|
|
5
|
-
`@boring
|
|
5
|
+
`@hachej/boring-workspace` is a workspace UI and bridge package. The app shell owns
|
|
6
6
|
auth, routing, application persistence, and the concrete chat component.
|
|
7
7
|
Workspace owns layout runtime, layout preferences, plugin registries, bridge
|
|
8
8
|
commands, and default workspace plugins.
|
|
@@ -19,16 +19,17 @@ commands, and default workspace plugins.
|
|
|
19
19
|
and no agent package imports.
|
|
20
20
|
- `src/app/` hosts front/server composition helpers such as
|
|
21
21
|
`WorkspaceAgentFront` and `createWorkspaceAgentServer`, where workspace app
|
|
22
|
-
code may compose with documented `@boring
|
|
22
|
+
code may compose with documented `@hachej/boring-agent/server` APIs.
|
|
23
23
|
|
|
24
24
|
## Core Contracts
|
|
25
25
|
|
|
26
|
-
- Plugin
|
|
27
|
-
-
|
|
28
|
-
|
|
26
|
+
- Plugin contributions: `src/shared/plugins/frontFactory.ts`
|
|
27
|
+
- Front plugins are authored with `definePlugin({ panels, leftTabs, commands,
|
|
28
|
+
catalogs, bindings, providers, surfaceResolvers })`. Agent tools belong to
|
|
29
|
+
Pi/server runtime paths, not front plugin contributions.
|
|
29
30
|
- Surface opening: `src/shared/types/surface.ts`
|
|
30
31
|
- `SurfaceOpenRequest { kind, target, meta }` is resolved by plugin
|
|
31
|
-
|
|
32
|
+
surface resolvers into panel openings.
|
|
32
33
|
- UI bridge: `src/shared/ui-bridge.ts`
|
|
33
34
|
- Agents and servers post `UiCommand` values. The front-end dispatches them
|
|
34
35
|
against the workspace runtime.
|
|
@@ -42,8 +43,7 @@ commands, and default workspace plugins.
|
|
|
42
43
|
- Server plugins: `src/server/plugins`
|
|
43
44
|
- `defineServerPlugin()` validates tools, routes, provisioning, and native Pi
|
|
44
45
|
package declarations.
|
|
45
|
-
- `
|
|
46
|
-
- `piPackages` are passed to `@boring/agent` as in-memory Pi settings, so
|
|
46
|
+
- `piPackages` are passed to `@hachej/boring-agent` as in-memory Pi settings, so
|
|
47
47
|
workspace adapters can depend on native Pi packages without requiring
|
|
48
48
|
Boring-specific exports from those packages.
|
|
49
49
|
|
|
@@ -54,5 +54,5 @@ commands, and default workspace plugins.
|
|
|
54
54
|
compatibility layer.
|
|
55
55
|
- Use `openSurface` for domain targets that need resolver selection.
|
|
56
56
|
- Use `openPanel` only when the caller intentionally names the concrete panel.
|
|
57
|
-
- Front/shared workspace code does not value-import `@boring
|
|
57
|
+
- Front/shared workspace code does not value-import `@hachej/boring-agent`; app/server
|
|
58
58
|
composition may import documented agent server APIs.
|
package/docs/PLUGIN_STRUCTURE.md
CHANGED
|
@@ -1,162 +1,56 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Plugin Structure
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canonical quick reference for boring-ui plugin layouts. For the full current
|
|
4
|
+
contract, see [`PLUGIN_SYSTEM.md`](./PLUGIN_SYSTEM.md). For future hosted/runtime
|
|
5
|
+
architecture, see the repo-level
|
|
6
|
+
[`docs/runtime-plugin-v2-hot-reload-plan.md`](../../../docs/runtime-plugin-v2-hot-reload-plan.md).
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
ownership clarity, not ceremony: plugin domain behavior stays in the plugin,
|
|
7
|
-
and workspace core stays a generic host.
|
|
8
|
+
## Generated/runtime plugin
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
to.
|
|
10
|
+
Use the workspace-local CLI from inside the agent/runtime workspace:
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
then delete the files the plugin does not need.
|
|
16
|
-
|
|
17
|
-
```txt
|
|
18
|
-
<plugin>/
|
|
19
|
-
front/
|
|
20
|
-
index.tsx # front plugin factory and public front exports
|
|
21
|
-
panels.tsx # panel definitions
|
|
22
|
-
catalogs.ts # catalog outputs/config helpers
|
|
23
|
-
surfaceResolver.ts # openSurface target -> panel resolution
|
|
24
|
-
bindings.tsx # React side-effect bindings, if needed
|
|
25
|
-
data/ # plugin-owned client data API/hooks/cache
|
|
26
|
-
server/
|
|
27
|
-
index.ts # server plugin factory and public server exports
|
|
28
|
-
tools.ts # agent tools, if needed
|
|
29
|
-
routes.ts # server routes, if needed
|
|
30
|
-
shared/
|
|
31
|
-
constants.ts # plugin id, catalog ids, surface kinds
|
|
32
|
-
types.ts # platform-neutral shared types, if needed
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Use only the files a plugin actually needs. Large component families may live
|
|
36
|
-
in subfolders such as `file-tree/`, `code-editor/`, or `empty-file-panel/`.
|
|
37
|
-
|
|
38
|
-
## Ownership Rules
|
|
39
|
-
|
|
40
|
-
- `front/index.tsx` composes front outputs; large behavior belongs in focused files.
|
|
41
|
-
- `server/index.ts` composes server outputs with `defineServerPlugin()`.
|
|
42
|
-
- Shared files must stay platform-neutral. Do not import plugin `front/` or
|
|
43
|
-
`server/` code from `shared/`.
|
|
44
|
-
- Domain search belongs in `front/catalogs.ts`.
|
|
45
|
-
- Domain open behavior belongs in `front/surfaceResolver.ts`.
|
|
46
|
-
- Domain event names live in `shared/events.ts` when both layers need the
|
|
47
|
-
contract; event names must be keyed by plugin id.
|
|
48
|
-
- Plugin data clients/hooks live in `front/data/`, not package `front/data`.
|
|
49
|
-
- Server prompts/tools/routes/provisioning live under `server/`, not mixed
|
|
50
|
-
with client code.
|
|
51
|
-
- Workspace core may host registries, providers, event transport, and bridge
|
|
52
|
-
dispatch only. It must not hardcode plugin panel ids or plugin domain rules.
|
|
53
|
-
- Catalog selection and plugin-owned routing should prefer `openSurface`.
|
|
54
|
-
`openPanel` remains available for explicit app-level panel opens.
|
|
55
|
-
- Executable agent tools should be server plugin contributions. The legacy
|
|
56
|
-
front `agentTools` field remains for migration only.
|
|
57
|
-
|
|
58
|
-
## Composed Plugins
|
|
59
|
-
|
|
60
|
-
Use `composePlugins()` when a front plugin is easier to build from smaller
|
|
61
|
-
front fragments. The composed plugin flattens child panels, commands,
|
|
62
|
-
catalogs, bindings, and outputs into one normal `WorkspaceFrontPlugin`.
|
|
63
|
-
|
|
64
|
-
Default behavior adopts child ownership to the parent plugin id. Use
|
|
65
|
-
`adoptOutputs: false` only when registry ownership must stay attached to the
|
|
66
|
-
child fragment for diagnostics or selective unregister behavior.
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
const macroPlugin = composePlugins({
|
|
70
|
-
id: "boring-macro",
|
|
71
|
-
label: "Macro",
|
|
72
|
-
plugins: [macroPanelsPlugin, macroSurfacesPlugin, macroSeriesExplorerPlugin],
|
|
73
|
-
})
|
|
12
|
+
```bash
|
|
13
|
+
boring-ui scaffold-plugin <name>
|
|
14
|
+
boring-ui verify-plugin <name>
|
|
74
15
|
```
|
|
75
16
|
|
|
76
|
-
|
|
77
|
-
child tools, prompt text, pi package declarations, provisioning, and routes
|
|
78
|
-
into one normal `WorkspaceServerPlugin`.
|
|
17
|
+
Default shape:
|
|
79
18
|
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
})
|
|
19
|
+
```txt
|
|
20
|
+
.pi/extensions/<name>/
|
|
21
|
+
package.json # boring.front and/or pi.*; no boring.server by default
|
|
22
|
+
front/index.tsx # default-export definePlugin({ ... })
|
|
23
|
+
README.md
|
|
86
24
|
```
|
|
87
25
|
|
|
88
|
-
|
|
26
|
+
Generated plugins are hot-reloaded with `/reload` for front/Pi resources. They
|
|
27
|
+
should stay route-free: no `server/index.ts`, no Fastify routes, and no dynamic
|
|
28
|
+
backend registration.
|
|
89
29
|
|
|
90
|
-
|
|
91
|
-
workspace adapter plugin that wraps the pi package and optionally adds front
|
|
92
|
-
integration.
|
|
30
|
+
## App/internal publishable package plugin
|
|
93
31
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
extension functions that call `pi.registerCommand(...)`. Workspace adapters
|
|
97
|
-
should adapt to that shape.
|
|
98
|
-
|
|
99
|
-
Declare native pi package dependencies on the server plugin:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
defineServerPlugin({
|
|
103
|
-
id: "markdown-preview",
|
|
104
|
-
piPackages: ["npm:pi-markdown-preview@0.9.7"],
|
|
105
|
-
})
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Workspace passes these declarations to `@boring/agent` as in-memory Pi
|
|
109
|
-
settings. This enables Pi's native package loader without mutating
|
|
110
|
-
`.pi/settings.json`.
|
|
32
|
+
Use [`packages/cli/templates/plugin`](../../../packages/cli/templates/plugin/) as the reference
|
|
33
|
+
shape when building a trusted package composed by an app shell:
|
|
111
34
|
|
|
112
35
|
```txt
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
server/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
index.tsx # defineFrontPlugin()
|
|
121
|
-
panels.tsx # workspace-native preview panel
|
|
122
|
-
surfaceResolver.ts # markdown-preview.open -> panel resolution
|
|
36
|
+
plugins/<name>/
|
|
37
|
+
package.json # boring.front + optional boring.server
|
|
38
|
+
src/front/index.ts # definePlugin({ ... })
|
|
39
|
+
src/server/index.ts # defineServerPlugin({ ... }) when needed
|
|
40
|
+
src/shared/* # browser-safe shared constants/types
|
|
41
|
+
tsup.config.ts
|
|
42
|
+
vitest.config.ts
|
|
123
43
|
```
|
|
124
44
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
prompt should teach the model to use workspace `openSurface` when a Boring app
|
|
129
|
-
is present, even if the underlying pi package also provides terminal/browser
|
|
130
|
-
slash commands.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
## Current Plugins
|
|
134
|
-
|
|
135
|
-
- `packages/workspace/src/plugins/filesystemPlugin`
|
|
136
|
-
- Data catalog package: `@hachej/boring-data-catalog` (`plugins/data-catalog/src`)
|
|
137
|
-
- `apps/workspace-playground/src/plugins/playgroundDataCatalog`
|
|
138
|
-
- Macro plugin example: `hachej/boring-macro` (`src/plugins/macro`)
|
|
139
|
-
|
|
140
|
-
## Invariants
|
|
141
|
-
|
|
142
|
-
Run:
|
|
143
|
-
|
|
144
|
-
```sh
|
|
145
|
-
pnpm --filter @boring/workspace run lint:plugin-invariants
|
|
146
|
-
```
|
|
45
|
+
App/internal plugins may expose boot-time server contributions such as routes,
|
|
46
|
+
agent tools, system prompts, provisioning, and Pi resources. Server changes
|
|
47
|
+
require restarting the workspace process.
|
|
147
48
|
|
|
148
|
-
|
|
49
|
+
## Import boundaries
|
|
149
50
|
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
the wrong layer boundary.
|
|
157
|
-
- TypeScript source files directly under a plugin root instead of
|
|
158
|
-
`front/`, `server/`, or `shared/`.
|
|
159
|
-
- Plugin-domain imports from production `workspace/src/front/chrome`, `events`,
|
|
160
|
-
and `hooks`.
|
|
161
|
-
- Legacy plugin file names such as `catalog.ts`, `surfaceTargets.ts`,
|
|
162
|
-
root-level `client.ts`, or root-level `server.ts`.
|
|
51
|
+
- Front plugin code imports from `@hachej/boring-workspace/plugin`.
|
|
52
|
+
- Trusted server plugin code imports from `@hachej/boring-workspace/server`.
|
|
53
|
+
- App shells use `@hachej/boring-workspace/app/front` and
|
|
54
|
+
`@hachej/boring-workspace/app/server`.
|
|
55
|
+
- Runtime/generated plugins should avoid broad host/workspace internals and use
|
|
56
|
+
documented primitives only.
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Plugin / Agent Layer
|
|
2
|
+
|
|
3
|
+
Normative spec for `@hachej/boring-workspace`'s current plugin + agent
|
|
4
|
+
layer. Code comments and tests cite section numbers in this file (for
|
|
5
|
+
example `PLUGIN_SYSTEM.md §4.5`), so keep headings stable when editing.
|
|
6
|
+
|
|
7
|
+
This document describes the implementation as it exists now. Historical
|
|
8
|
+
implementation plans live under `packages/workspace/docs/plans/archive/`.
|
|
9
|
+
For future generated/hosted runtime-plugin architecture, see the repo-level
|
|
10
|
+
`docs/runtime-plugin-v2-hot-reload-plan.md`.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
1. [Glossary](#1-glossary)
|
|
15
|
+
2. [End-to-end behaviour](#2-end-to-end-behaviour)
|
|
16
|
+
3. [Architecture](#3-architecture)
|
|
17
|
+
4. [Public API](#4-public-api)
|
|
18
|
+
5. [Key algorithms](#5-key-algorithms)
|
|
19
|
+
6. [Gotchas](#6-gotchas)
|
|
20
|
+
7. [Non-goals](#7-non-goals)
|
|
21
|
+
8. [Risk register](#8-risk-register)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Glossary
|
|
26
|
+
|
|
27
|
+
| Term | Definition |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| **App/internal plugin** | Trusted package composed by the app at boot. May export `boring.server`, Fastify routes, agent tools, providers, catalogs, and domain APIs. Server changes require restart/redeploy. |
|
|
30
|
+
| **Runtime/generated plugin** | Workspace-local plugin under `.pi/extensions/<id>/`, usually produced by `boring-ui scaffold-plugin`. It is hot-loaded for front/Pi resources, but must not rely on dynamic backend routes. |
|
|
31
|
+
| **Boring plugin package** | Node package with `package.json#boring` and/or `package.json#pi`. App-default packages are declared in `package.json#boring.defaultPluginPackages` or passed to `createWorkspaceAgentServer`. |
|
|
32
|
+
| **Boring front factory** | Default export of `boring.front`: `(api: BoringFrontAPI) => void | Promise<void>`. Usually created with `definePlugin({ ... })`. |
|
|
33
|
+
| **Workspace server plugin** | Trusted boot-time server contribution returned by `defineServerPlugin({ ... })` or a compatible object. May include routes, tools, system prompt, Pi resources, and provisioning. |
|
|
34
|
+
| **Asset manager** | `BoringPluginAssetManager`. Scans plugin dirs, computes signatures, tracks revisions, emits `boring.plugin.{load,unload,error}` events, and backs `/api/v1/agent-plugins`. |
|
|
35
|
+
| **Revision** | Per-plugin monotonic integer. It bumps on signature changes and is appended to browser front imports for cache busting. |
|
|
36
|
+
| **Surface resolver** | Front contribution that maps a typed request such as `open-path` to a panel id/title/params. File opens should route through this path. |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. End-to-end behaviour
|
|
41
|
+
|
|
42
|
+
### App/internal package plugin
|
|
43
|
+
|
|
44
|
+
1. App installs or references a plugin package.
|
|
45
|
+
2. App declares it through `defaultPluginPackages`, `appPackageJsonPath`, or
|
|
46
|
+
explicit `plugins` passed to `createWorkspaceAgentServer`.
|
|
47
|
+
3. Workspace resolves the package at boot.
|
|
48
|
+
4. `boring.front` is exposed through the asset manager and front hot-load
|
|
49
|
+
path in dev.
|
|
50
|
+
5. `boring.server` is imported and boot-composed once; routes and agent
|
|
51
|
+
tools are registered with the Fastify/agent process.
|
|
52
|
+
6. Server changes require process restart. `/reload` may diagnose the drift,
|
|
53
|
+
but it does not rewire routes/tools in-place.
|
|
54
|
+
|
|
55
|
+
### Runtime/generated plugin
|
|
56
|
+
|
|
57
|
+
1. Agent/user runs the workspace-local CLI:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
boring-ui scaffold-plugin <name>
|
|
61
|
+
boring-ui verify-plugin <name>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. The plugin lives under `.pi/extensions/<name>/`.
|
|
65
|
+
3. `/reload` scans plugin manifests, refreshes Pi resources, and emits SSE
|
|
66
|
+
load/unload/error events.
|
|
67
|
+
4. Browser dynamic-imports `boring.front` with revision + salt query params
|
|
68
|
+
and atomically replaces that plugin's registry entries.
|
|
69
|
+
5. Previous working UI remains live when a front import/register fails.
|
|
70
|
+
6. Generated plugins should omit `boring.server`; backend-like work should use
|
|
71
|
+
Pi extensions/tools today and future brokered sandbox tools/RPC later.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. Architecture
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
App package.json / createWorkspaceAgentServer options
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
resolve default plugin package dirs + explicit plugin entries
|
|
82
|
+
│
|
|
83
|
+
├─ bootstrapServer(...) trusted boot-time server plugins
|
|
84
|
+
│ ├─ routes Fastify app.register at boot
|
|
85
|
+
│ ├─ agentTools passed to createAgentApp at boot
|
|
86
|
+
│ ├─ systemPromptAppend static prompt addendum
|
|
87
|
+
│ ├─ pi packages/skills/exts static Pi resources
|
|
88
|
+
│ └─ provisioning runtime workspace materialization
|
|
89
|
+
│
|
|
90
|
+
├─ BoringPluginAssetManager manifest scan + signatures + SSE
|
|
91
|
+
│ ├─ /api/v1/agent-plugins
|
|
92
|
+
│ ├─ /api/v1/agent-plugins/events
|
|
93
|
+
│ └─ /api/v1/agent-plugins/:id/error
|
|
94
|
+
│
|
|
95
|
+
└─ createAgentApp(...)
|
|
96
|
+
├─ beforeReload: asset scan + diagnostics + caller hook
|
|
97
|
+
├─ systemPromptDynamic: plugin `pi.systemPrompt`
|
|
98
|
+
└─ pi.getHotReloadableResources: plugin skills/extensions/packages
|
|
99
|
+
|
|
100
|
+
Browser WorkspaceAgentFront
|
|
101
|
+
│
|
|
102
|
+
└─ useAgentPluginHotReload
|
|
103
|
+
├─ EventSource to /api/v1/agent-plugins/events
|
|
104
|
+
├─ dynamic import(frontUrl?v=<revision>&t=<salt>)
|
|
105
|
+
└─ registry.replaceByPluginId(...) per output kind
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Subpath intent:
|
|
109
|
+
|
|
110
|
+
| Subpath | Audience | Contents |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `/plugin` | Front plugin authors | `definePlugin`, front API/types, manifest validators, panel/surface contracts. |
|
|
113
|
+
| `/server` | Trusted server plugin authors/hosts | `defineServerPlugin`, server plugin types, asset manager helpers. |
|
|
114
|
+
| `/app/server` | App shells | `createWorkspaceAgentServer` and orchestration options. |
|
|
115
|
+
| `/app/front` | App shells | `WorkspaceAgentFront`. |
|
|
116
|
+
| `/shared` | Runtime-agnostic contracts | Shared types only. |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 4. Public API
|
|
121
|
+
|
|
122
|
+
### 4.1 Plugin `package.json`
|
|
123
|
+
|
|
124
|
+
```jsonc
|
|
125
|
+
{
|
|
126
|
+
"name": "my-plugin",
|
|
127
|
+
"version": "0.1.0",
|
|
128
|
+
"boring": {
|
|
129
|
+
"label": "My Plugin",
|
|
130
|
+
"front": "front/index.tsx",
|
|
131
|
+
"server": "server/index.ts" // app/internal only; omit for generated plugins
|
|
132
|
+
},
|
|
133
|
+
"pi": {
|
|
134
|
+
"systemPrompt": "Short guidance for the agent.",
|
|
135
|
+
"extensions": ["agent/index.ts"],
|
|
136
|
+
"skills": ["skills/my-skill"],
|
|
137
|
+
"packages": ["."]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Rules:
|
|
143
|
+
|
|
144
|
+
- `boring.front`, `boring.server`, `pi.extensions`, and `pi.skills` are safe
|
|
145
|
+
relative paths contained by the package root.
|
|
146
|
+
- `boring.server: true` is invalid. Use a string path or omit it.
|
|
147
|
+
- Plugin id is derived from `package.json#name`; `boring.id` is rejected.
|
|
148
|
+
- Runtime/generated plugins should include `boring.front` and/or `pi.*` and
|
|
149
|
+
omit `boring.server`.
|
|
150
|
+
|
|
151
|
+
### 4.2 App `package.json`
|
|
152
|
+
|
|
153
|
+
```jsonc
|
|
154
|
+
{
|
|
155
|
+
"boring": {
|
|
156
|
+
"defaultPluginPackages": [
|
|
157
|
+
"@hachej/boring-ask-user",
|
|
158
|
+
"./src/plugins/playgroundDataCatalog"
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Relative entries resolve against the app package.json when
|
|
165
|
+
`appPackageJsonPath` is supplied. Explicit `defaultPluginPackages` passed to
|
|
166
|
+
`createWorkspaceAgentServer` are merged with manifest entries.
|
|
167
|
+
|
|
168
|
+
### 4.3 Front authoring (`@hachej/boring-workspace/plugin`)
|
|
169
|
+
|
|
170
|
+
Use the declarative object form:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { definePlugin } from "@hachej/boring-workspace/plugin"
|
|
174
|
+
|
|
175
|
+
export default definePlugin({
|
|
176
|
+
id: "my-plugin",
|
|
177
|
+
label: "My Plugin",
|
|
178
|
+
panels: [
|
|
179
|
+
{
|
|
180
|
+
id: "my-plugin.panel",
|
|
181
|
+
label: "My Panel",
|
|
182
|
+
placement: "center",
|
|
183
|
+
component: MyPanel,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
commands: [
|
|
187
|
+
{ id: "my-plugin.open", title: "Open My Plugin", panelId: "my-plugin.panel" },
|
|
188
|
+
],
|
|
189
|
+
surfaceResolvers: [myResolver],
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The legacy positional form `definePlugin(id, factory, options?)` is not
|
|
194
|
+
supported. For advanced composition, use `setup(api) { ... }` inside the
|
|
195
|
+
config object.
|
|
196
|
+
|
|
197
|
+
### 4.4 Server authoring (`@hachej/boring-workspace/server`)
|
|
198
|
+
|
|
199
|
+
Trusted app/internal packages may export a server plugin:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { defineServerPlugin } from "@hachej/boring-workspace/server"
|
|
203
|
+
|
|
204
|
+
export default defineServerPlugin({
|
|
205
|
+
id: "my-plugin",
|
|
206
|
+
systemPrompt: "Use My Plugin when ...",
|
|
207
|
+
agentTools: [tool],
|
|
208
|
+
routes: async (app) => {
|
|
209
|
+
app.get("/api/my-plugin/health", async () => ({ ok: true }))
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This is boot-time composition. Routes and tools are not hot-wired into a
|
|
215
|
+
running Fastify/agent process by `/reload`.
|
|
216
|
+
|
|
217
|
+
### 4.5 Hot-reload coverage and partial-failure tolerance
|
|
218
|
+
|
|
219
|
+
| Surface | Runtime `.pi/extensions` | App/internal package plugin | Notes |
|
|
220
|
+
| --- | --- | --- | --- |
|
|
221
|
+
| `pi.systemPrompt` | `/reload`, next turn | `/reload` in dev when discovered; static in production if hot reload disabled | Appended by `systemPromptDynamic`. |
|
|
222
|
+
| `pi.extensions` | `/reload` through Pi session reload | `/reload` when discovered as package resources | File paths are re-read from manifest. |
|
|
223
|
+
| `pi.skills` / `pi.packages` | `/reload` through dynamic resource getter | `/reload` in dev; boot snapshot when `pluginHotReload=false` | `verify-plugin` checks declared local skill paths. |
|
|
224
|
+
| `boring.front` panels/commands/catalogs/surface resolvers | `/reload` + SSE + browser dynamic import | `/reload` in dev when front URL is served by the app/Vite | Previous version stays live on import/register failure. |
|
|
225
|
+
| `boring.server` routes/agentTools | Not supported for generated plugins | Boot-time only | `/reload` can warn `requiresRestart`. Restart process to apply. |
|
|
226
|
+
| Providers/bindings from hot-loaded front factories | Skipped | Static composition only | Dynamic provider tree mounting is intentionally not implemented yet. |
|
|
227
|
+
|
|
228
|
+
Partial-failure rule: a failed plugin scan/import/register must not abort the
|
|
229
|
+
whole reload. Healthy plugins still update; failing plugins emit diagnostics,
|
|
230
|
+
write/read `.error` state, and keep their previous live UI where possible.
|
|
231
|
+
|
|
232
|
+
### 4.6 CLI authoring path
|
|
233
|
+
|
|
234
|
+
Generated plugin authoring uses the provisioned workspace-local CLI:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
boring-ui scaffold-plugin <name>
|
|
238
|
+
boring-ui verify-plugin <name>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Do not teach agents to copy `packages/cli/templates/plugin` for generated runtime
|
|
242
|
+
plugins. That template is an app/internal publishable package example.
|
|
243
|
+
|
|
244
|
+
### 4.7 `pluginHotReload`
|
|
245
|
+
|
|
246
|
+
`createWorkspaceAgentServer({ pluginHotReload })` controls runtime plugin
|
|
247
|
+
reload endpoints and dynamic Pi/package refresh.
|
|
248
|
+
|
|
249
|
+
- `true` (default): registers `/api/boring.reload`, keeps
|
|
250
|
+
`/api/v1/agent-plugins/events` active, and refreshes discovered plugin Pi
|
|
251
|
+
resources on `/api/v1/agent/reload`.
|
|
252
|
+
- `false`: static discovery/listing remains available, but package resources
|
|
253
|
+
are snapped once at boot and the developer reload endpoint is disabled.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 5. Key algorithms
|
|
258
|
+
|
|
259
|
+
### 5.1 Manifest scan
|
|
260
|
+
|
|
261
|
+
`scanBoringPlugins(pluginDirs)` discovers plugin package roots, validates
|
|
262
|
+
manifest shape, resolves contained entry paths, detects duplicate ids, and
|
|
263
|
+
returns `BoringServerPluginManifest[]` plus preflight diagnostics.
|
|
264
|
+
|
|
265
|
+
### 5.2 Signatures and revisions
|
|
266
|
+
|
|
267
|
+
`BoringPluginAssetManager` hashes manifest fields, front/server file
|
|
268
|
+
signatures, relevant front/server/shared directories, and Pi resource paths.
|
|
269
|
+
When a signature changes, the plugin revision increments and a load/unload/error
|
|
270
|
+
event is emitted.
|
|
271
|
+
|
|
272
|
+
### 5.3 Front import cache busting
|
|
273
|
+
|
|
274
|
+
Browser imports append both revision and salt:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import(`${frontUrl}?v=${revision}&t=${Date.now()}`)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The salt avoids stale Vite/browser module graph reuse across repeated reloads
|
|
281
|
+
or dev-server restarts.
|
|
282
|
+
|
|
283
|
+
### 5.4 Atomic registry replacement
|
|
284
|
+
|
|
285
|
+
Front factories are first captured into an in-memory API. The captured panels,
|
|
286
|
+
commands, catalogs, and surface resolvers are then installed with
|
|
287
|
+
`replaceByPluginId` per registry. DockView and registry subscribers should not
|
|
288
|
+
see an intermediate empty state.
|
|
289
|
+
|
|
290
|
+
### 5.5 Server drift warnings
|
|
291
|
+
|
|
292
|
+
When a plugin had a server entry in a previous revision and that server file
|
|
293
|
+
changes, the load event carries `requiresRestart: ["routes", "agentTools"]`.
|
|
294
|
+
The chat reload response also includes restart warnings so users know `/reload`
|
|
295
|
+
updated front/Pi resources but not boot-wired server code.
|
|
296
|
+
|
|
297
|
+
### 5.6 Path containment
|
|
298
|
+
|
|
299
|
+
Manifest paths are lexically safe relative paths and are checked with realpath
|
|
300
|
+
containment so symlink escapes are rejected. Runtime workspace provisioning
|
|
301
|
+
template targets are also constrained to remain inside the workspace root.
|
|
302
|
+
|
|
303
|
+
### 5.7 Output ownership and collision detection
|
|
304
|
+
|
|
305
|
+
Plugin-owned outputs carry `pluginId`. Hot reload uses that ownership to replace
|
|
306
|
+
only the current plugin's outputs. Cross-plugin id collisions are rejected with
|
|
307
|
+
`PLUGIN_OUTPUT_ID_COLLISION`; intra-plugin duplicate output ids are rejected
|
|
308
|
+
while capturing the front factory.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 6. Gotchas
|
|
313
|
+
|
|
314
|
+
1. `/reload` is the runtime plugin refresh boundary. Vite HMR should not
|
|
315
|
+
directly hot-update `.pi/extensions` modules into the host tree.
|
|
316
|
+
2. Native `EventSource` cannot send Authorization headers. When bearer auth is
|
|
317
|
+
required and no token-query fallback exists, front plugin hot reload is
|
|
318
|
+
disabled rather than silently unauthenticated.
|
|
319
|
+
3. Dynamic providers/bindings are not mounted for hot-loaded runtime plugins;
|
|
320
|
+
use static app composition for provider trees.
|
|
321
|
+
4. `boring.server` in a runtime plugin may verify as a valid file path but it is
|
|
322
|
+
not dynamically registered by `/reload`.
|
|
323
|
+
5. The asset manager is scan/hash/emit. Server route/tool import and
|
|
324
|
+
composition happen through app/server orchestration.
|
|
325
|
+
6. Keep generated plugins route-free; use Pi extensions/tools and workspace file
|
|
326
|
+
APIs instead.
|
|
327
|
+
7. Use surface resolvers for file visualizers. Do not hard-code extension logic
|
|
328
|
+
in the file tree.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 7. Non-goals
|
|
333
|
+
|
|
334
|
+
- Hot-registering Fastify routes or static `agentTools` from generated plugins
|
|
335
|
+
into a live server process.
|
|
336
|
+
- Loading untrusted hosted plugin JavaScript directly into the host React tree.
|
|
337
|
+
- Replacing app/internal domain APIs (for example Macro routes) with runtime
|
|
338
|
+
plugin RPC for purity.
|
|
339
|
+
- Dynamic provider/binding trees for runtime hot-loaded plugins in this PR.
|
|
340
|
+
- Marketplace signing/provenance/permissions. Those belong to the next runtime
|
|
341
|
+
plugin architecture phase.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 8. Risk register
|
|
346
|
+
|
|
347
|
+
| Risk | Current mitigation |
|
|
348
|
+
| --- | --- |
|
|
349
|
+
| Broken generated front import | Browser import/register error is surfaced; previous version remains live. |
|
|
350
|
+
| Stale server code after `/reload` | Restart warnings for server-path drift; docs state boot-time-only semantics. |
|
|
351
|
+
| Bad plugin blocks all reloads | Partial-failure tolerance; diagnostics per plugin. |
|
|
352
|
+
| Path escape via manifest entries | Safe relative path validation + realpath containment. |
|
|
353
|
+
| Path escape via runtime provisioning templates | Template targets are constrained to workspace-root descendants. |
|
|
354
|
+
| Duplicate output ids | Cross-plugin and intra-plugin collision checks. |
|
|
355
|
+
| Agent writes plugin into wrong cwd | Workspace-local `boring-ui` shim exports `BORING_AGENT_WORKSPACE_ROOT`; verifier prints scanned path. |
|
package/docs/README.md
CHANGED
|
@@ -5,8 +5,13 @@ Older implementation plans live in `docs/plans/archive/`.
|
|
|
5
5
|
|
|
6
6
|
## Current References
|
|
7
7
|
|
|
8
|
+
- `PLUGIN_SYSTEM.md` - current normative spec for the plugin / agent layer:
|
|
9
|
+
package manifest fields, public API for front + server authoring,
|
|
10
|
+
hot-reload coverage table, prompt-location guidance, and key algorithms.
|
|
11
|
+
Code cites it as `Per PLUGIN_SYSTEM.md §X`.
|
|
12
|
+
- `PLUGIN_STRUCTURE.md` - quick layout guide for generated/runtime plugins vs
|
|
13
|
+
app/internal publishable package plugins.
|
|
8
14
|
- `INTERFACES.md` - package boundaries and public abstractions.
|
|
9
|
-
- `PLUGIN_STRUCTURE.md` - plugin folder shape and invariant scans.
|
|
10
15
|
- `plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md` - latest plugin ownership plan.
|
|
11
16
|
- `plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md` - UI bridge ownership decision.
|
|
12
17
|
- `plans/archive/` - superseded implementation plans.
|
package/docs/plans/README.md
CHANGED
|
@@ -4,6 +4,7 @@ Active ownership records live in this folder.
|
|
|
4
4
|
|
|
5
5
|
- `ASK_USER_QUESTIONS_PLUGIN_SPEC.md` - blocking ask-user tool + Questions workspace plugin spec.
|
|
6
6
|
- `PANE_TO_AGENT_CHAT_ACTIONS_SPEC.md` - pane/plugin to active agent chat action bridge spec.
|
|
7
|
+
- `HOT_RELOADABLE_AGENT_PLUGINS_PLAN.md` - current package-json plugin, reload, Pi, and front-factory architecture.
|
|
7
8
|
- `GENERIC_EXPLORER_PLUGIN_PLAN.md` - generic explorer plugin shape and front/plugin ownership audit.
|
|
8
9
|
- `MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md` - macro plugin audit for reusable explorer/surface/event helper extraction.
|
|
9
10
|
- `PLUGIN_OUTPUTS_ISOLATION_PLAN.md` - plugin ownership and isolation plan.
|