@hachej/boring-workspace 0.1.13 → 0.1.16
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 +270 -42
- package/dist/CommandPalette-NOEOVkN2.js +5714 -0
- package/dist/{FileTree-BVfqs3rR.js → FileTree-Dl-qUAB0.js} +9 -9
- package/dist/MarkdownEditor-yc6mFsnI.js +533 -0
- package/dist/{WorkspaceLoadingState-BjZGQLS_.js → WorkspaceLoadingState-CSZfENWe.js} +145 -124
- package/dist/agent-tool-DEtfQPVB.d.ts +100 -0
- package/dist/app-front.d.ts +79 -67
- package/dist/app-front.js +253 -241
- package/dist/app-server.d.ts +17 -12
- package/dist/app-server.js +80 -10
- package/dist/{bootstrapServer-BRUqUpVW.d.ts → bootstrapServer-BreQ9QBc.d.ts} +8 -2
- package/dist/server.d.ts +10 -32
- package/dist/server.js +22 -127
- package/dist/shared.d.ts +1 -2
- package/dist/testing.d.ts +0 -63
- package/dist/testing.js +2248 -2401
- package/dist/workspace.css +1616 -974
- package/dist/workspace.d.ts +111 -450
- package/dist/workspace.js +417 -1635
- package/docs/INTERFACES.md +2 -2
- package/docs/PLUGIN_STRUCTURE.md +1 -1
- package/docs/plans/ASK_USER_QUESTIONS_PLUGIN_SPEC.md +131 -263
- package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +29 -27
- package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +12 -12
- package/docs/plans/PANE_TO_AGENT_CHAT_ACTIONS_SPEC.md +366 -0
- package/docs/plans/README.md +2 -0
- package/docs/plans/archive/PLUGIN_MODEL.md +14 -14
- package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +2 -3
- package/docs/plans/archive/WORKSPACE_V2_PLAN.md +1 -1
- package/package.json +3 -6
- package/dist/CommandPalette-Dme9em28.js +0 -5506
- package/dist/MarkdownEditor-CcCDF65H.js +0 -502
- package/dist/agent-tool-NvxKfist.d.ts +0 -28
- package/dist/explorer-DtLUnuah.d.ts +0 -129
|
@@ -320,7 +320,7 @@ createDataCatalogPreset({
|
|
|
320
320
|
});
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
Owner: `
|
|
323
|
+
Owner: the `@hachej/boring-data-catalog` package, not macro.
|
|
324
324
|
|
|
325
325
|
2. **Drag payload helper**
|
|
326
326
|
|
|
@@ -328,7 +328,7 @@ Owner: `dataCatalogPlugin`, not macro.
|
|
|
328
328
|
getDragPayload: createTextDragPayload("text/series-id", (row) => row.id);
|
|
329
329
|
```
|
|
330
330
|
|
|
331
|
-
Owner: future `
|
|
331
|
+
Owner: the future `@hachej/boring-data-explorer` package or `DataExplorer/adapters`.
|
|
332
332
|
|
|
333
333
|
3. **Facet config helper**
|
|
334
334
|
|
|
@@ -406,8 +406,8 @@ Also extract generic query helper:
|
|
|
406
406
|
toQueryString(params: Record<string, string | number | string[] | undefined>): string
|
|
407
407
|
```
|
|
408
408
|
|
|
409
|
-
Owner:
|
|
410
|
-
|
|
409
|
+
Owner: `@hachej/boring-data-explorer/adapters.ts` once created. Temporary owner can be
|
|
410
|
+
`@hachej/boring-data-explorer/front` adapter helpers.
|
|
411
411
|
|
|
412
412
|
Risk: don't overfit response shape. Keep mapper hooks explicit.
|
|
413
413
|
|
|
@@ -563,7 +563,7 @@ surfaceResolverOutputs(resolvers): PluginOutput[]
|
|
|
563
563
|
```
|
|
564
564
|
|
|
565
565
|
Owner: `front/registry/surfaceResolverHelpers.ts` or future
|
|
566
|
-
`
|
|
566
|
+
`@hachej/boring-data-explorer` surface helpers. Since these helpers are not explorer-only,
|
|
567
567
|
prefer `front/registry` or a new `front/surface` module.
|
|
568
568
|
|
|
569
569
|
### `data/macroSeriesData.ts`
|
|
@@ -662,7 +662,7 @@ createOpenSurfacePrompt({
|
|
|
662
662
|
});
|
|
663
663
|
```
|
|
664
664
|
|
|
665
|
-
Owner: workspace server UI-control or
|
|
665
|
+
Owner: workspace server UI-control or data catalog package server utilities.
|
|
666
666
|
|
|
667
667
|
3. **Provisioning type**
|
|
668
668
|
|
|
@@ -851,16 +851,16 @@ Better integration opportunities:
|
|
|
851
851
|
plugin composition.
|
|
852
852
|
3. `createRestExplorerAdapter()` for `/catalog` + `/facets` endpoints.
|
|
853
853
|
4. `createOpenSurfaceRowHandler()` for catalog row activation.
|
|
854
|
-
5.
|
|
855
|
-
`
|
|
854
|
+
5. The future `@hachej/boring-data-explorer` package owns explorer primitives and adapter helpers;
|
|
855
|
+
the `@hachej/boring-data-catalog` package composes them.
|
|
856
856
|
|
|
857
|
-
Do not make macro depend directly on a future `
|
|
858
|
-
`
|
|
857
|
+
Do not make macro depend directly on a future `@hachej/boring-data-explorer` package if
|
|
858
|
+
the `@hachej/boring-data-catalog` package can provide the right data-catalog specialization. Macro
|
|
859
859
|
should depend on the highest-level appropriate abstraction:
|
|
860
860
|
|
|
861
861
|
```txt
|
|
862
|
-
macro series catalog ->
|
|
863
|
-
macro custom project/tree explorer ->
|
|
862
|
+
macro series catalog -> data catalog package plugin/factory
|
|
863
|
+
macro custom project/tree explorer -> data explorer package plugin/factory
|
|
864
864
|
macro chart/deck panels -> macro-owned panels
|
|
865
865
|
```
|
|
866
866
|
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Pane to Agent Chat Actions Spec
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-05-08
|
|
4
|
+
|
|
5
|
+
## Summary
|
|
6
|
+
|
|
7
|
+
Add a first-class way for Workspace panes/plugins to send a message into the
|
|
8
|
+
active visible agent chat without bypassing `ChatPanel` and `useAgentChat()`.
|
|
9
|
+
|
|
10
|
+
This is separate from the `ask_user` Questions submit path. `ask_user` answers
|
|
11
|
+
resolve a blocked tool through the Questions browser -> server command channel.
|
|
12
|
+
Pane-to-agent chat actions are for user-triggered conversational messages such
|
|
13
|
+
as "Analyze this chart", "Explain this result", or "Ask the agent about this
|
|
14
|
+
pane".
|
|
15
|
+
|
|
16
|
+
## Problem
|
|
17
|
+
|
|
18
|
+
Today panes cannot cleanly send a message into the active agent session.
|
|
19
|
+
|
|
20
|
+
Current flow:
|
|
21
|
+
|
|
22
|
+
- `ChatPanel` owns `sendMessage` privately through `useAgentChat()`.
|
|
23
|
+
- `useAgentChat()` wraps AI SDK `useChat()` and posts to
|
|
24
|
+
`POST /api/v1/agent/chat` with `{ sessionId, message, model, thinkingLevel,
|
|
25
|
+
attachments }`.
|
|
26
|
+
- `ChatPanel.handleSubmit()` delegates to AI SDK `sendMessage()` and does not
|
|
27
|
+
expose that foreground-chat path to panes.
|
|
28
|
+
- While chat is submitted/streaming, the current composer behavior is owned by
|
|
29
|
+
`ChatPanel`; this plan does not invent a separate follow-up endpoint.
|
|
30
|
+
- `WorkspaceAgentFront` / `ChatPanelHost` wrap chat for stream data events,
|
|
31
|
+
artifact opening, and agent -> UI commands.
|
|
32
|
+
- Existing Workspace UI bridge is agent/server -> pane commands. It is not a
|
|
33
|
+
pane -> active chat message API.
|
|
34
|
+
|
|
35
|
+
A pane can technically call `/api/v1/agent/chat` directly, but that is the wrong
|
|
36
|
+
integration for foreground chat UX because it bypasses:
|
|
37
|
+
|
|
38
|
+
- visible user bubble state
|
|
39
|
+
- AI SDK `useChat()` state
|
|
40
|
+
- current session selection
|
|
41
|
+
- model/thinking-level selection
|
|
42
|
+
- request headers
|
|
43
|
+
- attachment handling and composer validation
|
|
44
|
+
- streaming/busy state
|
|
45
|
+
- history/persistence expectations
|
|
46
|
+
|
|
47
|
+
Direct HTTP is acceptable only for intentionally headless/background agent jobs,
|
|
48
|
+
not for "send this as a message in the current chat".
|
|
49
|
+
|
|
50
|
+
## Goals
|
|
51
|
+
|
|
52
|
+
- Let panes/plugins send a message into the active visible agent chat.
|
|
53
|
+
- Preserve `ChatPanel` as the owner of foreground chat send behavior.
|
|
54
|
+
- Preserve current session, model, thinking level, request headers, attachments,
|
|
55
|
+
visible bubbles, busy-state behavior, and persistence behavior.
|
|
56
|
+
- Expose a stable Workspace-facing hook/helper for panes.
|
|
57
|
+
- Support plugin/module-graph boundaries with an event fallback.
|
|
58
|
+
- Keep Workspace base front/shared code package-neutral where required; app/front
|
|
59
|
+
composition may import documented `@boring/agent/front` APIs.
|
|
60
|
+
|
|
61
|
+
## Non-goals
|
|
62
|
+
|
|
63
|
+
- Replacing `useAgentChat()` or changing the agent chat transport.
|
|
64
|
+
- Making panes call `/api/v1/agent/chat` directly for foreground chat.
|
|
65
|
+
- Implementing a new follow-up queue or `/followup` route in v1.
|
|
66
|
+
- Using this path for `ask_user` form submission.
|
|
67
|
+
- Background/headless agent jobs.
|
|
68
|
+
- Multi-agent routing or sending to arbitrary non-active sessions in v1.
|
|
69
|
+
|
|
70
|
+
## Architecture
|
|
71
|
+
|
|
72
|
+
Use both an imperative controller and a browser event helper:
|
|
73
|
+
|
|
74
|
+
1. `ChatPanel` exposes an `AgentChatController` that wraps its existing visible
|
|
75
|
+
composer send path.
|
|
76
|
+
2. Workspace app/front composition stores the active controller near
|
|
77
|
+
`WorkspaceAgentFront`.
|
|
78
|
+
3. Panes call a stable hook/helper such as `useAgentActions().sendMessage(...)`.
|
|
79
|
+
4. Plugins that cannot reliably share React context may call
|
|
80
|
+
`postAgentMessage(...)`, which dispatches a browser event caught by the same
|
|
81
|
+
Workspace app/front bridge and forwarded to the active controller.
|
|
82
|
+
|
|
83
|
+
## Export/API placement
|
|
84
|
+
|
|
85
|
+
- `AgentChatController`, `AgentChatControllerRegistration`, and
|
|
86
|
+
`AgentChatMessageInput` should be exported as **types** from
|
|
87
|
+
`@boring/agent/front`.
|
|
88
|
+
- `useAgentActions()`, `AgentActionsProvider`, and `postAgentMessage()` should be
|
|
89
|
+
exported from `@boring/workspace/app/front` because they are part of the
|
|
90
|
+
Workspace + Agent composition layer.
|
|
91
|
+
- Base Workspace front/shared code must not value-import `@boring/agent`.
|
|
92
|
+
- If a package-neutral Workspace type is needed, keep it structurally typed and
|
|
93
|
+
free of agent value imports.
|
|
94
|
+
|
|
95
|
+
Intended imports:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import type { AgentChatMessageInput } from "@boring/agent/front"
|
|
99
|
+
import { useAgentActions, postAgentMessage } from "@boring/workspace/app/front"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Contracts
|
|
103
|
+
|
|
104
|
+
### Agent chat controller
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
export interface AgentChatAttachmentInput {
|
|
108
|
+
filename?: string
|
|
109
|
+
mediaType: string
|
|
110
|
+
url: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface AgentChatMessageInput {
|
|
114
|
+
text: string
|
|
115
|
+
files?: AgentChatAttachmentInput[]
|
|
116
|
+
metadata?: Record<string, unknown>
|
|
117
|
+
allowSlashCommands?: boolean
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface AgentChatControllerRegistration {
|
|
121
|
+
controllerId: string
|
|
122
|
+
sessionId: string
|
|
123
|
+
controller: AgentChatController
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface AgentChatController {
|
|
127
|
+
sendAgentMessage(input: AgentChatMessageInput): Promise<void>
|
|
128
|
+
canSendAgentMessage(): boolean
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`ChatPanel` owns the implementation. It should expose the controller through a
|
|
133
|
+
prop such as:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
interface ChatPanelProps {
|
|
137
|
+
onControllerReady?: (registration: AgentChatControllerRegistration | null) => void
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Lifecycle rules:
|
|
142
|
+
|
|
143
|
+
- `ChatPanel` calls `onControllerReady(registration)` when mounted and ready.
|
|
144
|
+
- `ChatPanel` calls `onControllerReady(null)` on unmount or when the active
|
|
145
|
+
controller becomes invalid.
|
|
146
|
+
- Workspace stores the active controller by `controllerId` and `sessionId`.
|
|
147
|
+
- Unregister clears the active controller only when `controllerId` matches the
|
|
148
|
+
currently registered controller. An old unmount must not clear a newer active
|
|
149
|
+
controller.
|
|
150
|
+
- `ChatPanel` re-registers when `sessionId`, request headers, model,
|
|
151
|
+
thinking-level, or the underlying send callback changes.
|
|
152
|
+
- The controller method delegates to the same path as user composer submit.
|
|
153
|
+
- If chat is submitted/streaming, default v1 behavior rejects with
|
|
154
|
+
`AGENT_CHAT_BUSY` and `canSendAgentMessage()` returns false. Runtimes with an
|
|
155
|
+
explicit native follow-up capability may route busy-time user messages through
|
|
156
|
+
their documented follow-up path instead of rejecting.
|
|
157
|
+
- Workspace attention blockers override native follow-up. If a pending
|
|
158
|
+
`ask_user`/Questions blocker exists for the active session,
|
|
159
|
+
`canSendAgentMessage()` returns false and `sendAgentMessage()` rejects/blocks;
|
|
160
|
+
the message must not be converted into a hidden follow-up because the active
|
|
161
|
+
tool expects a structured form answer.
|
|
162
|
+
- It must not create a separate hidden chat stream.
|
|
163
|
+
|
|
164
|
+
### Promise semantics
|
|
165
|
+
|
|
166
|
+
`sendAgentMessage()` / `sendMessage()` promises resolve when the message is
|
|
167
|
+
accepted into the visible ChatPanel send path and local user-message handling has
|
|
168
|
+
completed. They do **not** wait for the assistant response stream to finish.
|
|
169
|
+
|
|
170
|
+
Promise rejection is for immediate failures only:
|
|
171
|
+
|
|
172
|
+
- invalid input
|
|
173
|
+
- no active controller
|
|
174
|
+
- stale controller
|
|
175
|
+
- chat busy/submitted/streaming
|
|
176
|
+
- synchronous failure while enqueueing/sending
|
|
177
|
+
|
|
178
|
+
Async model/network failures after the message is accepted surface through the
|
|
179
|
+
existing chat error UI and optional notifications. They are not guaranteed to
|
|
180
|
+
reject the original pane promise.
|
|
181
|
+
|
|
182
|
+
### Workspace agent actions
|
|
183
|
+
|
|
184
|
+
Workspace app/front exposes a provider/hook for panes:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
export interface AgentActions {
|
|
188
|
+
sendMessage(input: AgentChatMessageInput): Promise<void>
|
|
189
|
+
canSendMessage(): boolean
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function useAgentActions(): AgentActions
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Behavior:
|
|
196
|
+
|
|
197
|
+
- If no active controller exists, `sendMessage` rejects with
|
|
198
|
+
`AGENT_CHAT_CONTROLLER_UNAVAILABLE` and may show a workspace notification.
|
|
199
|
+
- If the active controller changes between call start and dispatch,
|
|
200
|
+
`sendMessage` rejects with `AGENT_CHAT_CONTROLLER_STALE`.
|
|
201
|
+
- If chat is busy/submitted/streaming, `sendMessage` rejects with
|
|
202
|
+
`AGENT_CHAT_BUSY`.
|
|
203
|
+
- `canSendMessage()` returns false when no active chat controller is registered
|
|
204
|
+
or when the active controller reports busy/unavailable.
|
|
205
|
+
- The hook sends to the active visible chat session only.
|
|
206
|
+
|
|
207
|
+
### Event fallback
|
|
208
|
+
|
|
209
|
+
For plugin/module graph safety, expose a same-window browser event helper:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
export const AGENT_MESSAGE_EVENT = "boring:agent-message"
|
|
213
|
+
|
|
214
|
+
export interface PostAgentMessageInput {
|
|
215
|
+
workspaceId?: string
|
|
216
|
+
text: string
|
|
217
|
+
metadata?: Record<string, unknown>
|
|
218
|
+
allowSlashCommands?: boolean
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function postAgentMessage(input: PostAgentMessageInput): boolean {
|
|
222
|
+
return globalThis.dispatchEvent(new CustomEvent(AGENT_MESSAGE_EVENT, { detail: input }))
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Event fallback is fire-and-forget. The boolean only means the browser event was
|
|
227
|
+
dispatched to at least one same-window listener; it is not delivery confirmation.
|
|
228
|
+
Use `useAgentActions()` when the caller needs promise feedback.
|
|
229
|
+
|
|
230
|
+
Workspace app/front listens for `AGENT_MESSAGE_EVENT`, validates the payload, and
|
|
231
|
+
forwards it to the active `AgentChatController`.
|
|
232
|
+
|
|
233
|
+
Event rules:
|
|
234
|
+
|
|
235
|
+
- Event fallback v1 is text + metadata only. Attachments are allowed through the
|
|
236
|
+
hook/controller path, not global events.
|
|
237
|
+
- The event is for trusted same-origin plugin code only. It is not a security
|
|
238
|
+
boundary.
|
|
239
|
+
- The event is same-window only and does not cross tabs by default.
|
|
240
|
+
- `workspaceId` routes events when multiple Workspace shells are mounted in one
|
|
241
|
+
window. If omitted and more than one active controller exists, Workspace
|
|
242
|
+
rejects/ignores the event as ambiguous and may show a notification.
|
|
243
|
+
- Invalid payloads are ignored with a developer-facing warning.
|
|
244
|
+
- If no controller is active, Workspace shows a non-fatal notification such as
|
|
245
|
+
"No active agent chat is available."
|
|
246
|
+
- Event sends are subject to the same busy-state rejection and debounce/rate
|
|
247
|
+
limiting as hook sends.
|
|
248
|
+
|
|
249
|
+
## Pane usage
|
|
250
|
+
|
|
251
|
+
Preferred React usage:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
function ChartPane({ chartId }: { chartId: string }) {
|
|
255
|
+
const agent = useAgentActions()
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<Button
|
|
259
|
+
disabled={!agent.canSendMessage()}
|
|
260
|
+
onClick={() =>
|
|
261
|
+
agent.sendMessage({
|
|
262
|
+
text: `Analyze this chart: ${chartId}`,
|
|
263
|
+
metadata: { source: "chart-pane", chartId },
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
>
|
|
267
|
+
Ask agent
|
|
268
|
+
</Button>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Event fallback:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
postAgentMessage({
|
|
277
|
+
text: "Explain the selected result",
|
|
278
|
+
metadata: { source: "results-pane" },
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Do not call `/api/v1/agent/chat` directly from panes for visible chat UX. Use the
|
|
283
|
+
hook or event helper so the visible chat owner remains `ChatPanel`.
|
|
284
|
+
|
|
285
|
+
## Error handling
|
|
286
|
+
|
|
287
|
+
Add or reuse stable Workspace/app-front error codes for:
|
|
288
|
+
|
|
289
|
+
- `AGENT_CHAT_CONTROLLER_UNAVAILABLE`
|
|
290
|
+
- `AGENT_CHAT_CONTROLLER_STALE`
|
|
291
|
+
- `AGENT_CHAT_BUSY`
|
|
292
|
+
- `AGENT_CHAT_MESSAGE_INVALID`
|
|
293
|
+
- `AGENT_CHAT_SEND_FAILED`
|
|
294
|
+
- `AGENT_CHAT_AMBIGUOUS_WORKSPACE`
|
|
295
|
+
|
|
296
|
+
Errors should be surfaced as non-fatal UI notifications and promise rejections
|
|
297
|
+
from `sendMessage()` when the caller uses the hook/controller path. Event path
|
|
298
|
+
errors are notification/log only.
|
|
299
|
+
|
|
300
|
+
## Security and validation
|
|
301
|
+
|
|
302
|
+
- Panes can only send to the active visible chat session in v1.
|
|
303
|
+
- The server remains responsible for authorization on chat requests; this plan
|
|
304
|
+
does not weaken existing auth.
|
|
305
|
+
- Client-side validation should use exported constants shared with the composer
|
|
306
|
+
where possible.
|
|
307
|
+
- Suggested v1 limits:
|
|
308
|
+
- `text.trim().length >= 1` unless files are present
|
|
309
|
+
- max message length matches server/chat composer limits
|
|
310
|
+
- max attachments: 20
|
|
311
|
+
- max attachment size: 5 MiB when size is knowable
|
|
312
|
+
- allowed attachment URL schemes: `blob:`, `data:`, or same-origin URLs only
|
|
313
|
+
- max attachment URL/string byte length to prevent giant data-url bypasses
|
|
314
|
+
- metadata is JSON-serializable, max depth 4, max serialized bytes 4096
|
|
315
|
+
- `metadata` is client-side advisory only in v1. It is not sent to the server or
|
|
316
|
+
agent unless the caller explicitly includes it in `text`.
|
|
317
|
+
- Event helper payloads must be runtime-validated before forwarding.
|
|
318
|
+
- Pane-originated messages bypass slash-command parsing by default. Set
|
|
319
|
+
`allowSlashCommands: true` only when the caller intentionally wants composer
|
|
320
|
+
slash-command behavior.
|
|
321
|
+
|
|
322
|
+
## Relationship to `ask_user`
|
|
323
|
+
|
|
324
|
+
This plan does not change `ask_user` answer submission.
|
|
325
|
+
|
|
326
|
+
- `ask_user` answer path:
|
|
327
|
+
`Questions pane -> Questions browser/server command -> AskUserCoordinator`
|
|
328
|
+
- pane-to-agent chat action path:
|
|
329
|
+
`pane -> AgentChatController -> ChatPanel/useAgentChat -> visible chat`
|
|
330
|
+
|
|
331
|
+
Do not use pane-to-agent chat actions to submit generated form answers. Use it
|
|
332
|
+
only when the user intentionally wants to send a conversational message to the
|
|
333
|
+
agent.
|
|
334
|
+
|
|
335
|
+
## Acceptance criteria
|
|
336
|
+
|
|
337
|
+
- `ChatPanel` exposes an `AgentChatController` lifecycle callback or equivalent
|
|
338
|
+
controller ref.
|
|
339
|
+
- Workspace app/front stores the active controller by `controllerId`/`sessionId`
|
|
340
|
+
and exposes `useAgentActions()`.
|
|
341
|
+
- `postAgentMessage()` event helper forwards text-only messages to the active
|
|
342
|
+
controller when unambiguous.
|
|
343
|
+
- Pane calls produce visible user messages in the active chat.
|
|
344
|
+
- Pane calls reject/disable while chat is submitted/streaming with
|
|
345
|
+
`AGENT_CHAT_BUSY` in v1.
|
|
346
|
+
- Pane calls preserve model/thinking-level/session/request-header behavior by
|
|
347
|
+
delegating to `ChatPanel` internals.
|
|
348
|
+
- Calling from a pane when no active controller exists fails gracefully with a
|
|
349
|
+
stable error/notification.
|
|
350
|
+
- Plugin author docs/templates mention: foreground chat uses
|
|
351
|
+
`useAgentActions()` / `postAgentMessage()`; direct HTTP is only for
|
|
352
|
+
background/headless jobs.
|
|
353
|
+
- Tests cover:
|
|
354
|
+
- controller registration/unregistration
|
|
355
|
+
- old unregister cannot clear newer controller
|
|
356
|
+
- session switch re-registers and stale controller rejects
|
|
357
|
+
- hook-based send
|
|
358
|
+
- event-based send
|
|
359
|
+
- busy/submitted/streaming rejection
|
|
360
|
+
- promise resolves on local accept/enqueue, not assistant completion
|
|
361
|
+
- unavailable controller path
|
|
362
|
+
- invalid event payload rejection
|
|
363
|
+
- multiple Workspace shells / ambiguous event routing
|
|
364
|
+
- attachment count/URL/size validation on hook path
|
|
365
|
+
- metadata size/depth validation
|
|
366
|
+
- slash-command bypass by default and opt-in behavior
|
package/docs/plans/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Active ownership records live in this folder.
|
|
4
4
|
|
|
5
|
+
- `ASK_USER_QUESTIONS_PLUGIN_SPEC.md` - blocking ask-user tool + Questions workspace plugin spec.
|
|
6
|
+
- `PANE_TO_AGENT_CHAT_ACTIONS_SPEC.md` - pane/plugin to active agent chat action bridge spec.
|
|
5
7
|
- `GENERIC_EXPLORER_PLUGIN_PLAN.md` - generic explorer plugin shape and front/plugin ownership audit.
|
|
6
8
|
- `MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md` - macro plugin audit for reusable explorer/surface/event helper extraction.
|
|
7
9
|
- `PLUGIN_OUTPUTS_ISOLATION_PLAN.md` - plugin ownership and isolation plan.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
> are superseded by generic `surface-resolver` outputs. Workspace core owns
|
|
12
12
|
> only the resolver registry and `openSurface` dispatch. Filesystem path/glob
|
|
13
13
|
> mapping lives in `plugins/filesystemPlugin/surfaceResolver.ts`; data catalog
|
|
14
|
-
> row-to-visualization mapping lives in
|
|
14
|
+
> row-to-visualization mapping now lives in `@hachej/boring-data-catalog`.
|
|
15
15
|
|
|
16
16
|
Prior status (v7.6): round-5 review patches — Step 0 sequencing (move workspace into v7.5 layout BEFORE Phase 1, not after); split plugin entrypoints (index.ts client + server.ts per plugin); strict type-only imports for cross-folder Plugin refs; cleanup pack (uiBridge dedup, EmptyFilePanel relocation, A/B parallelism tightened, TL;DR scrub, tsconfig excludes, pi-tools-migration catch-up). **Meta-rule: when files move, they go DIRECTLY to final v7.6 destinations — no intermediate placements.**
|
|
17
17
|
|
|
@@ -588,10 +588,10 @@ Use Fastify's `inject()` for in-process testing; no port binding.
|
|
|
588
588
|
Dropping `data: DataPaneConfig` from `<ChatCenteredShell>` removed
|
|
589
589
|
the one-liner ergonomics for hosts that just want a simple data
|
|
590
590
|
tab with their adapter (gemini P2). Restore them via the reusable
|
|
591
|
-
data catalog plugin factory exported from `@boring/
|
|
591
|
+
data catalog plugin factory now exported from `@hachej/boring-data-catalog/front`:
|
|
592
592
|
|
|
593
593
|
```ts
|
|
594
|
-
import { createDataCatalogPlugin } from "@boring/
|
|
594
|
+
import { createDataCatalogPlugin } from "@hachej/boring-data-catalog/front"
|
|
595
595
|
import { myAdapter } from "./adapter"
|
|
596
596
|
|
|
597
597
|
export const dataPlugin = createDataCatalogPlugin({
|
|
@@ -746,7 +746,7 @@ type changes. The plan does NOT delete `chatSuggestions` from
|
|
|
746
746
|
|---|---|---|
|
|
747
747
|
| **`filesystemPlugin`** | UI-only: a Files catalog (cmd palette); FileTree panel registration as `placement: 'left-tab'`; CodeEditor + MarkdownEditor panel registrations (with `filePatterns`). **No `agentTools` field** — file tools are harness substrate (see §"Tools belong with the harness, not the plugin"). | Hosts that want a chat-only UI (no file tree, no code editor opening on file click) can opt out. When excluded: file UI disappears; LLM file tools STAY (controlled separately by `disableDefaultFileTools` on `createAgentApp`). |
|
|
748
748
|
|
|
749
|
-
**v6 had a
|
|
749
|
+
**v6 had a data catalog plugin second default; v6.2 cuts it.**
|
|
750
750
|
Reason (codex round-3 P1): with `recentKind` cut and no other
|
|
751
751
|
filter, a generic "Data" tab couldn't unambiguously pick *which*
|
|
752
752
|
catalog to display when multiple plugins contribute catalogs (e.g.,
|
|
@@ -1269,7 +1269,7 @@ agent tools and catalogs but leave a dead Files tab in the UI.
|
|
|
1269
1269
|
Phase 1 step 5c retrofits `WorkbenchLeftPane` to query
|
|
1270
1270
|
`PanelRegistry` for `placement: 'left-tab'`, sorted by
|
|
1271
1271
|
registration order. `filesystemPlugin` contributes the Files tab;
|
|
1272
|
-
|
|
1272
|
+
the data catalog plugin contributes the Data tab.
|
|
1273
1273
|
`excludeDefaults: ['filesystem']` truly removes the tab.
|
|
1274
1274
|
|
|
1275
1275
|
(`'right-tab'` is reserved in the contract but no Phase 1 component
|
|
@@ -1533,7 +1533,7 @@ model**.
|
|
|
1533
1533
|
│ @boring/workspace │
|
|
1534
1534
|
│ • Plugin contract (definePlugin, factories) │
|
|
1535
1535
|
│ • Registries (Panel / Command / Catalog) │
|
|
1536
|
-
│ • Default plugins (filesystemPlugin
|
|
1536
|
+
│ • Default plugins (filesystemPlugin; data catalog is now external) │
|
|
1537
1537
|
│ • UI bridge core (moved from @boring/agent) │
|
|
1538
1538
|
│ • Substrate routes /api/v1/{ui,files,tree,files/search} │
|
|
1539
1539
|
│ • Event bus + WorkspaceEventMap │
|
|
@@ -1655,7 +1655,7 @@ packages/workspace/
|
|
|
1655
1655
|
│ │ └── defaults/
|
|
1656
1656
|
│ │ ├── filesystemPlugin.ts (imports filesystemAgentTools
|
|
1657
1657
|
│ │ │ from @boring/agent)
|
|
1658
|
-
│ │ └── dataCatalogPlugin.ts
|
|
1658
|
+
│ │ └── dataCatalogPlugin.ts (historical; now external package)
|
|
1659
1659
|
│ ├── registry/
|
|
1660
1660
|
│ │ ├── PanelRegistry.ts [EXISTS — retrofitted:
|
|
1661
1661
|
│ │ │ subscribable, path-aware
|
|
@@ -1878,7 +1878,7 @@ restructure; subsequent phases assume the v7.6 layout exists.
|
|
|
1878
1878
|
│ registered by createAgentApp via pi-tools-migration's │
|
|
1879
1879
|
│ buildFilesystemAgentTools(bundle) — NOT the plugin's job. │
|
|
1880
1880
|
│ │
|
|
1881
|
-
│ (v6 had
|
|
1881
|
+
│ (v6 had a data catalog plugin as a second default; v6.2 cuts │
|
|
1882
1882
|
│ it — plugins that want a workbench data tab contribute │
|
|
1883
1883
|
│ their own left-tab panel.) │
|
|
1884
1884
|
│ │
|
|
@@ -1930,7 +1930,7 @@ restructure; subsequent phases assume the v7.6 layout exists.
|
|
|
1930
1930
|
│ was the original justification for the retrofit. │
|
|
1931
1931
|
│ - Drop `data: DataPaneConfig` prop. Hosts that want a │
|
|
1932
1932
|
│ workbench data tab register their own left-tab panel │
|
|
1933
|
-
│ or compose
|
|
1933
|
+
│ or compose `createDataCatalogPlugin(opts)` from `@hachej/boring-data-catalog/front` │
|
|
1934
1934
|
│ / `appendDataCatalogOutputs(...)` helpers. │
|
|
1935
1935
|
│ - Drop `extraPanels` prop. Panels come from PanelRegistry; │
|
|
1936
1936
|
│ new optional `allowedPanels?: string[]` for gating. │
|
|
@@ -2056,7 +2056,7 @@ export const makeMacroServerPlugin = (): Plugin =>
|
|
|
2056
2056
|
component is `DataExplorer` configured with macro's adapter and
|
|
2057
2057
|
`onActivate` that calls `surface.openPanel({ component:
|
|
2058
2058
|
"chart-canvas", … })`. This replaces the v5 dataPaneConfig wiring
|
|
2059
|
-
without needing a default
|
|
2059
|
+
without needing a default data catalog plugin. The catalog
|
|
2060
2060
|
(`seriesCatalog`) stays separate — it's what powers the cmd palette
|
|
2061
2061
|
search; the panel is what shows the workbench browser.
|
|
2062
2062
|
|
|
@@ -2417,7 +2417,7 @@ apps/boring-macro-v2/src/plugin/ ← macroPlugin lives here
|
|
|
2417
2417
|
- `src/components/chat/` (whole folder; Phase A + C + G)
|
|
2418
2418
|
- Top-level `src/panes/{code-editor, markdown-editor, file-tree}/` — moved INTO
|
|
2419
2419
|
`src/plugin/defaults/filesystemPlugin/` (panes/sidebar split per role) by j9p7.9
|
|
2420
|
-
- `src/panes/data-catalog/` — orphaned (
|
|
2420
|
+
- `src/panes/data-catalog/` — orphaned (the data catalog plugin was cut in v6.2);
|
|
2421
2421
|
audit during j9p7.30: delete if no consumer, otherwise re-home as a primitive
|
|
2422
2422
|
in `components/` for plugin authors who want a generic data-tab implementation
|
|
2423
2423
|
- `src/panes/EmptyPane.tsx` (loose) — moved to `chrome/EmptyPane.tsx`
|
|
@@ -3417,7 +3417,7 @@ simple hosts.** A host that just wants "a data tab with my
|
|
|
3417
3417
|
adapter" had a one-liner; v6.2's "register your own left-tab
|
|
3418
3418
|
panel" makes it ~10 lines. **Fix:** use
|
|
3419
3419
|
`createDataCatalogPlugin` for standalone data catalog plugins, or
|
|
3420
|
-
`appendDataCatalogOutputs` when an app plugin needs to install
|
|
3420
|
+
`appendDataCatalogOutputs` from `@hachej/boring-data-catalog/front` when an app plugin needs to install
|
|
3421
3421
|
the data catalog as part of a domain plugin. Macro uses the latter
|
|
3422
3422
|
so chart/deck behavior stays in the macro app.
|
|
3423
3423
|
|
|
@@ -3474,7 +3474,7 @@ by `createWorkspaceAgentApp` rather than evaluated at module load.
|
|
|
3474
3474
|
`data` prop.** With `recentKind` cut and multiple registered
|
|
3475
3475
|
catalogs (filesystem's Files + macro's Series), nothing in the
|
|
3476
3476
|
spec said which one fills the generic Data tab. **Fix:** drop
|
|
3477
|
-
|
|
3477
|
+
the data catalog plugin from defaults entirely. There is no generic
|
|
3478
3478
|
Data tab; plugins that want a workbench data tab register their
|
|
3479
3479
|
own `placement: 'left-tab'` panel (e.g., macro's `macroSeriesPanel`
|
|
3480
3480
|
which internally renders DataExplorer with the macro adapter +
|
|
@@ -3516,7 +3516,7 @@ rolled back.
|
|
|
3516
3516
|
|
|
3517
3517
|
### Net impact (v6.1 → v6.2)
|
|
3518
3518
|
|
|
3519
|
-
- One default plugin removed (
|
|
3519
|
+
- One default plugin removed (the data catalog plugin; now external).
|
|
3520
3520
|
- Two factory shapes formalized (`createFilesystemAgentTools(deps)`
|
|
3521
3521
|
and `makeFilesystemPlugin(deps)`).
|
|
3522
3522
|
- Macro example honors client/server split.
|
|
@@ -87,7 +87,7 @@ src/
|
|
|
87
87
|
│ │ ├── defaultEditorPanels.ts ← MOVED from panes/ (3 apps consume via barrel — keep)
|
|
88
88
|
│ │ └── __tests__/
|
|
89
89
|
│ │
|
|
90
|
-
│ └──
|
|
90
|
+
│ └── (moved out) data catalog package: `@hachej/boring-data-catalog`
|
|
91
91
|
│
|
|
92
92
|
├── server/ (existing)
|
|
93
93
|
└── shared/ (existing)
|
|
@@ -108,8 +108,7 @@ plugin with hardcoded data.
|
|
|
108
108
|
|
|
109
109
|
`DataCatalog` belongs alongside `DataExplorer` in `front/components/` — both are
|
|
110
110
|
presentational peers. Move `panes/data-catalog/*` → `front/components/data-catalog/*`.
|
|
111
|
-
Apps install data catalog outputs through `createDataCatalogPlugin` or
|
|
112
|
-
`appendDataCatalogOutputs`.
|
|
111
|
+
Apps install data catalog outputs through `@hachej/boring-data-catalog/front` (`createDataCatalogPlugin` or `appendDataCatalogOutputs`).
|
|
113
112
|
|
|
114
113
|
---
|
|
115
114
|
|
|
@@ -21,7 +21,7 @@ Use this section as the live handoff ledger while executing this plan.
|
|
|
21
21
|
- Current source uses generic `surface-resolver` plugin outputs plus the
|
|
22
22
|
`openSurface` UI command. Filesystem path matching belongs to
|
|
23
23
|
`plugins/filesystemPlugin/surfaceResolver.ts`; data catalog row opening
|
|
24
|
-
belongs to `
|
|
24
|
+
belongs to the extracted `@hachej/boring-data-catalog` package.
|
|
25
25
|
|
|
26
26
|
### Pass 2 — Full milestone verification (2026-04-24)
|
|
27
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-workspace",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Workspace UI, plugin, and bridge package for composing chat, files, catalogs, editors, and app-specific panes.",
|
|
@@ -119,14 +119,12 @@
|
|
|
119
119
|
"fastify": "^5.3.3",
|
|
120
120
|
"lowlight": "^3.3.0",
|
|
121
121
|
"lucide-react": "^1.8.0",
|
|
122
|
-
"micromatch": "^4.0.8",
|
|
123
|
-
"radix-ui": "^1.4.3",
|
|
124
122
|
"react-arborist": "^3.4.0",
|
|
125
123
|
"tailwind-merge": "^2.0.0",
|
|
126
124
|
"zod": "^3.23.0",
|
|
127
125
|
"zustand": "^5.0.0",
|
|
128
|
-
"@hachej/boring-agent": "0.1.
|
|
129
|
-
"@hachej/boring-ui-kit": "0.1.
|
|
126
|
+
"@hachej/boring-agent": "0.1.16",
|
|
127
|
+
"@hachej/boring-ui-kit": "0.1.16"
|
|
130
128
|
},
|
|
131
129
|
"devDependencies": {
|
|
132
130
|
"@tailwindcss/postcss": "^4.0.0",
|
|
@@ -134,7 +132,6 @@
|
|
|
134
132
|
"@testing-library/jest-dom": "^6.9.1",
|
|
135
133
|
"@testing-library/react": "^16.3.2",
|
|
136
134
|
"@testing-library/user-event": "^14.6.1",
|
|
137
|
-
"@types/micromatch": "^4.0.10",
|
|
138
135
|
"@types/node": "^22.15.3",
|
|
139
136
|
"@types/react": "^19.0.0",
|
|
140
137
|
"@types/react-dom": "^19.0.0",
|