@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
package/dist/app-server.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PiPackageSource, CreateAgentAppOptions } from '@hachej/boring-agent/server';
|
|
2
2
|
export { PiPackageSource as WorkspacePiPackageSource } from '@hachej/boring-agent/server';
|
|
3
3
|
import { FastifyInstance } from 'fastify';
|
|
4
|
-
import { S as ServerBootstrapOptions, W as
|
|
5
|
-
export { C as ComposeServerPluginsOptions,
|
|
6
|
-
import './agent-tool-
|
|
4
|
+
import { S as ServerBootstrapOptions, c as createInMemoryBridge, W as WorkspaceServerPlugin, a as WorkspaceProvisioningContribution, b as WorkspaceRouteContribution } from './bootstrapServer-BreQ9QBc.js';
|
|
5
|
+
export { C as ComposeServerPluginsOptions, d as composeServerPlugins, e as defineServerPlugin } from './bootstrapServer-BreQ9QBc.js';
|
|
6
|
+
import './agent-tool-DEtfQPVB.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Standalone workspace + agent Fastify composition.
|
|
@@ -21,27 +21,32 @@ interface WorkspaceAgentResourceLoaderOptions {
|
|
|
21
21
|
type WorkspaceAgentCreateOptions = Omit<CreateAgentAppOptions, "resourceLoaderOptions"> & {
|
|
22
22
|
resourceLoaderOptions?: WorkspaceAgentResourceLoaderOptions;
|
|
23
23
|
};
|
|
24
|
+
interface WorkspaceAgentServerPluginContext {
|
|
25
|
+
workspaceRoot: string;
|
|
26
|
+
bridge: ReturnType<typeof createInMemoryBridge>;
|
|
27
|
+
}
|
|
28
|
+
type WorkspaceAgentServerPluginFactory = (context: WorkspaceAgentServerPluginContext) => WorkspaceServerPlugin;
|
|
24
29
|
interface CreateWorkspaceAgentServerOptions extends WorkspaceAgentCreateOptions, Pick<ServerBootstrapOptions, "plugins" | "defaults" | "excludeDefaults"> {
|
|
30
|
+
pluginFactories?: WorkspaceAgentServerPluginFactory[];
|
|
25
31
|
provisionWorkspace?: boolean;
|
|
26
32
|
workspaceProvisioning?: {
|
|
27
33
|
force?: boolean;
|
|
28
34
|
};
|
|
29
|
-
/**
|
|
30
|
-
* Whether exec_ui should stat-check file paths against the workspaceRoot
|
|
31
|
-
* before queueing the command. Defaults to true only for local host-backed
|
|
32
|
-
* modes (direct/local). Remote sandbox modes should leave this false because
|
|
33
|
-
* workspace files may not exist on the host server running Fastify.
|
|
34
|
-
*/
|
|
35
35
|
validateUiPaths?: boolean;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
interface WorkspaceAgentServerPluginCollection {
|
|
39
39
|
provisioningContributions: WorkspaceProvisioningContribution[];
|
|
40
40
|
routeContributions: WorkspaceRouteContribution[];
|
|
41
|
+
preservedUiStateKeys: string[];
|
|
41
42
|
agentOptions: Pick<WorkspaceAgentCreateOptions, "extraTools" | "systemPromptAppend" | "resourceLoaderOptions">;
|
|
42
43
|
}
|
|
43
|
-
interface CollectWorkspaceAgentServerPluginsOptions extends Pick<
|
|
44
|
+
interface CollectWorkspaceAgentServerPluginsOptions extends Pick<ServerBootstrapOptions, "plugins" | "defaults" | "excludeDefaults"> {
|
|
45
|
+
workspaceRoot?: string;
|
|
46
|
+
systemPromptAppend?: string;
|
|
47
|
+
resourceLoaderOptions?: WorkspaceAgentResourceLoaderOptions;
|
|
44
48
|
}
|
|
49
|
+
declare function buildWorkspaceContextPrompt(): string;
|
|
45
50
|
declare function collectWorkspaceAgentServerPlugins(opts?: CollectWorkspaceAgentServerPluginsOptions): WorkspaceAgentServerPluginCollection;
|
|
46
51
|
declare function provisionWorkspaceAgentServer(opts: {
|
|
47
52
|
workspaceRoot: string;
|
|
@@ -50,4 +55,4 @@ declare function provisionWorkspaceAgentServer(opts: {
|
|
|
50
55
|
}): Promise<void>;
|
|
51
56
|
declare function createWorkspaceAgentServer(opts?: CreateWorkspaceAgentServerOptions): Promise<FastifyInstance>;
|
|
52
57
|
|
|
53
|
-
export { type CollectWorkspaceAgentServerPluginsOptions, type CreateWorkspaceAgentServerOptions, type WorkspaceAgentResourceLoaderOptions, type WorkspaceAgentServerPluginCollection, WorkspaceProvisioningContribution, WorkspaceRouteContribution, collectWorkspaceAgentServerPlugins, createWorkspaceAgentServer, provisionWorkspaceAgentServer };
|
|
58
|
+
export { type CollectWorkspaceAgentServerPluginsOptions, type CreateWorkspaceAgentServerOptions, type WorkspaceAgentResourceLoaderOptions, type WorkspaceAgentServerPluginCollection, type WorkspaceAgentServerPluginContext, type WorkspaceAgentServerPluginFactory, WorkspaceProvisioningContribution, WorkspaceRouteContribution, WorkspaceServerPlugin, buildWorkspaceContextPrompt, collectWorkspaceAgentServerPlugins, createWorkspaceAgentServer, provisionWorkspaceAgentServer };
|
package/dist/app-server.js
CHANGED
|
@@ -5,7 +5,54 @@ import {
|
|
|
5
5
|
provisionRuntimeWorkspace,
|
|
6
6
|
resolveMode
|
|
7
7
|
} from "@hachej/boring-agent/server";
|
|
8
|
-
import { join } from "path";
|
|
8
|
+
import { join as join2 } from "path";
|
|
9
|
+
|
|
10
|
+
// src/server/boringSystemPrompt.ts
|
|
11
|
+
import { existsSync, readFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
function resolveDocsPath() {
|
|
16
|
+
const override = process.env.BORING_DOCS_PATH;
|
|
17
|
+
if (override) return override;
|
|
18
|
+
const candidates = [
|
|
19
|
+
join(__dirname, "../docs"),
|
|
20
|
+
// dist/server.js → packages/workspace/docs/
|
|
21
|
+
join(__dirname, "../../docs")
|
|
22
|
+
// src/server/*.ts → packages/workspace/docs/
|
|
23
|
+
];
|
|
24
|
+
return candidates.find(existsSync) ?? null;
|
|
25
|
+
}
|
|
26
|
+
function readDocOrFallback(docsPath, name) {
|
|
27
|
+
const file = join(docsPath, name);
|
|
28
|
+
try {
|
|
29
|
+
return existsSync(file) ? readFileSync(file, "utf-8").trim() : "";
|
|
30
|
+
} catch {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function buildBoringSystemPrompt() {
|
|
35
|
+
const docsPath = resolveDocsPath();
|
|
36
|
+
const intro = `You are an expert agent operating inside boring-ui, an open-source workspace for building agent-powered products. You help users by reading files, executing commands, editing code, and opening workspace panels.`;
|
|
37
|
+
if (!docsPath) {
|
|
38
|
+
return intro;
|
|
39
|
+
}
|
|
40
|
+
const plugins = readDocOrFallback(docsPath, "plugins.md");
|
|
41
|
+
const panels = readDocOrFallback(docsPath, "panels.md");
|
|
42
|
+
const bridge = readDocOrFallback(docsPath, "bridge.md");
|
|
43
|
+
const sections = [
|
|
44
|
+
plugins && `<boring-ui-docs topic="plugin-system">
|
|
45
|
+
${plugins}
|
|
46
|
+
</boring-ui-docs>`,
|
|
47
|
+
panels && `<boring-ui-docs topic="panel-components">
|
|
48
|
+
${panels}
|
|
49
|
+
</boring-ui-docs>`,
|
|
50
|
+
bridge && `<boring-ui-docs topic="ui-bridge">
|
|
51
|
+
${bridge}
|
|
52
|
+
</boring-ui-docs>`
|
|
53
|
+
].filter(Boolean);
|
|
54
|
+
return [intro, ...sections].join("\n\n");
|
|
55
|
+
}
|
|
9
56
|
|
|
10
57
|
// src/server/bridge/createInMemoryBridge.ts
|
|
11
58
|
var MAX_PENDING_COMMANDS = 1e3;
|
|
@@ -222,11 +269,11 @@ function createExecUiTool(uiBridge, opts = {}) {
|
|
|
222
269
|
" \u2014 Open a plugin-owned target through the workspace",
|
|
223
270
|
" surface resolver registry. Use this when a plugin",
|
|
224
271
|
" defines the mapping from domain target to panel",
|
|
225
|
-
" component, for example a
|
|
272
|
+
" component, for example a catalog row.",
|
|
226
273
|
" Example: {kind:'openSurface', params:{",
|
|
227
|
-
" kind:'
|
|
274
|
+
" kind:'my-plugin.open-row',",
|
|
228
275
|
" target:'orders_daily',",
|
|
229
|
-
" meta:{catalogId:'
|
|
276
|
+
" meta:{catalogId:'my-plugin'}}}",
|
|
230
277
|
"",
|
|
231
278
|
" closePanel params: { id: string }",
|
|
232
279
|
" closeWorkbenchLeftPane params: {}",
|
|
@@ -395,7 +442,11 @@ function uiRoutes(app, opts, done) {
|
|
|
395
442
|
async (request, reply) => {
|
|
396
443
|
const body = request.body;
|
|
397
444
|
const bridge = await resolveBridge(request);
|
|
398
|
-
await bridge.
|
|
445
|
+
const current = await bridge.getState() ?? {};
|
|
446
|
+
const preserved = Object.fromEntries(
|
|
447
|
+
(opts.preserveStateKeys ?? []).filter((key) => !(key in body.state) && key in current).map((key) => [key, current[key]])
|
|
448
|
+
);
|
|
449
|
+
await bridge.setState({ ...body.state, ...preserved });
|
|
399
450
|
return reply.code(204).send();
|
|
400
451
|
}
|
|
401
452
|
);
|
|
@@ -608,6 +659,11 @@ function validateServerPlugin(plugin) {
|
|
|
608
659
|
if (plugin.routes !== void 0 && typeof plugin.routes !== "function") {
|
|
609
660
|
fail(plugin.id, "routes must be a Fastify plugin function when provided");
|
|
610
661
|
}
|
|
662
|
+
if (plugin.preservedUiStateKeys !== void 0) {
|
|
663
|
+
if (!Array.isArray(plugin.preservedUiStateKeys) || plugin.preservedUiStateKeys.some((key) => typeof key !== "string" || key.length === 0)) {
|
|
664
|
+
fail(plugin.id, "preservedUiStateKeys must be a non-empty string array when provided");
|
|
665
|
+
}
|
|
666
|
+
}
|
|
611
667
|
if (plugin.provisioning !== void 0) {
|
|
612
668
|
validateProvisioning(plugin.id, plugin.provisioning);
|
|
613
669
|
}
|
|
@@ -663,6 +719,10 @@ function composeServerPlugins(options) {
|
|
|
663
719
|
...options.plugins.map((plugin) => plugin.routes),
|
|
664
720
|
options.routes
|
|
665
721
|
]);
|
|
722
|
+
const preservedUiStateKeys = [.../* @__PURE__ */ new Set([
|
|
723
|
+
...options.plugins.flatMap((plugin) => plugin.preservedUiStateKeys ?? []),
|
|
724
|
+
...options.preservedUiStateKeys ?? []
|
|
725
|
+
])];
|
|
666
726
|
return defineServerPlugin({
|
|
667
727
|
id: options.id,
|
|
668
728
|
...options.label !== void 0 ? { label: options.label } : {},
|
|
@@ -670,7 +730,8 @@ function composeServerPlugins(options) {
|
|
|
670
730
|
...systemPrompt ? { systemPrompt } : {},
|
|
671
731
|
...agentTools.length > 0 ? { agentTools } : {},
|
|
672
732
|
...provisioning ? { provisioning } : {},
|
|
673
|
-
...routes ? { routes } : {}
|
|
733
|
+
...routes ? { routes } : {},
|
|
734
|
+
...preservedUiStateKeys.length > 0 ? { preservedUiStateKeys } : {}
|
|
674
735
|
});
|
|
675
736
|
}
|
|
676
737
|
|
|
@@ -702,13 +763,15 @@ function bootstrapServer(options) {
|
|
|
702
763
|
const piPackages = collectPiPackages(finalPlugins);
|
|
703
764
|
const provisioningContributions = finalPlugins.filter((p) => p.provisioning).map((p) => ({ id: p.id, provisioning: p.provisioning }));
|
|
704
765
|
const routeContributions = finalPlugins.filter((p) => p.routes).map((p) => ({ id: p.id, routes: p.routes }));
|
|
766
|
+
const preservedUiStateKeys = [...new Set(finalPlugins.flatMap((p) => p.preservedUiStateKeys ?? []))];
|
|
705
767
|
return {
|
|
706
768
|
registered: finalPlugins.map((p) => p.id),
|
|
707
769
|
systemPromptAppend,
|
|
708
770
|
piPackages,
|
|
709
771
|
agentTools,
|
|
710
772
|
provisioningContributions,
|
|
711
|
-
routeContributions
|
|
773
|
+
routeContributions,
|
|
774
|
+
preservedUiStateKeys
|
|
712
775
|
};
|
|
713
776
|
}
|
|
714
777
|
|
|
@@ -728,12 +791,13 @@ function collectWorkspaceAgentServerPlugins(opts = {}) {
|
|
|
728
791
|
plugins: opts.plugins,
|
|
729
792
|
excludeDefaults: opts.excludeDefaults
|
|
730
793
|
});
|
|
731
|
-
const workspaceSkillsDir =
|
|
794
|
+
const workspaceSkillsDir = join2(workspaceRoot, ".agents", "skills");
|
|
732
795
|
const callerAdditional = opts.resourceLoaderOptions?.additionalSkillPaths ?? [];
|
|
733
796
|
const callerPiPackages = opts.resourceLoaderOptions?.piPackages ?? [];
|
|
734
797
|
return {
|
|
735
798
|
provisioningContributions: result.provisioningContributions,
|
|
736
799
|
routeContributions: result.routeContributions,
|
|
800
|
+
preservedUiStateKeys: result.preservedUiStateKeys,
|
|
737
801
|
agentOptions: {
|
|
738
802
|
extraTools: result.agentTools,
|
|
739
803
|
systemPromptAppend: [opts.systemPromptAppend, result.systemPromptAppend].filter(Boolean).join("\n\n") || void 0,
|
|
@@ -762,7 +826,11 @@ async function createWorkspaceAgentServer(opts = {}) {
|
|
|
762
826
|
const uiTools = createWorkspaceUiTools(bridge, {
|
|
763
827
|
workspaceRoot: validateUiPaths ? workspaceRoot : void 0
|
|
764
828
|
});
|
|
765
|
-
const
|
|
829
|
+
const factoryPlugins = opts.pluginFactories?.map((factory) => factory({ workspaceRoot, bridge })) ?? [];
|
|
830
|
+
const pluginCollection = collectWorkspaceAgentServerPlugins({
|
|
831
|
+
...opts,
|
|
832
|
+
plugins: [...opts.plugins ?? [], ...factoryPlugins]
|
|
833
|
+
});
|
|
766
834
|
if (opts.provisionWorkspace !== false) {
|
|
767
835
|
await provisionWorkspaceAgentServer({
|
|
768
836
|
workspaceRoot,
|
|
@@ -781,17 +849,19 @@ async function createWorkspaceAgentServer(opts = {}) {
|
|
|
781
849
|
],
|
|
782
850
|
systemPromptAppend: [
|
|
783
851
|
workspaceFsCapability === "strong" ? buildWorkspaceContextPrompt() : void 0,
|
|
852
|
+
buildBoringSystemPrompt(),
|
|
784
853
|
pluginCollection.agentOptions.systemPromptAppend
|
|
785
854
|
].filter(Boolean).join("\n\n") || void 0,
|
|
786
855
|
resourceLoaderOptions: pluginCollection.agentOptions.resourceLoaderOptions
|
|
787
856
|
});
|
|
788
|
-
await app.register(uiRoutes, { bridge });
|
|
857
|
+
await app.register(uiRoutes, { bridge, preserveStateKeys: pluginCollection.preservedUiStateKeys });
|
|
789
858
|
for (const { routes } of pluginCollection.routeContributions) {
|
|
790
859
|
await app.register(routes);
|
|
791
860
|
}
|
|
792
861
|
return app;
|
|
793
862
|
}
|
|
794
863
|
export {
|
|
864
|
+
buildWorkspaceContextPrompt,
|
|
795
865
|
collectWorkspaceAgentServerPlugins,
|
|
796
866
|
composeServerPlugins,
|
|
797
867
|
createWorkspaceAgentServer,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { U as UiBridge, A as AgentTool } from './agent-tool-DEtfQPVB.js';
|
|
1
2
|
import { PiPackageSource, RuntimeProvisioningContribution } from '@hachej/boring-agent/server';
|
|
2
3
|
import { FastifyPluginAsync } from 'fastify';
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
declare function createInMemoryBridge(): UiBridge;
|
|
4
6
|
|
|
5
7
|
interface WorkspaceServerPlugin {
|
|
6
8
|
id: string;
|
|
@@ -15,6 +17,8 @@ interface WorkspaceServerPlugin {
|
|
|
15
17
|
agentTools?: AgentTool[];
|
|
16
18
|
provisioning?: RuntimeProvisioningContribution;
|
|
17
19
|
routes?: FastifyPluginAsync;
|
|
20
|
+
/** UI state keys owned by this plugin that browser state PUTs must not overwrite. */
|
|
21
|
+
preservedUiStateKeys?: string[];
|
|
18
22
|
}
|
|
19
23
|
declare class ServerPluginError extends Error {
|
|
20
24
|
constructor(message: string);
|
|
@@ -31,6 +35,7 @@ interface ComposeServerPluginsOptions {
|
|
|
31
35
|
agentTools?: AgentTool[];
|
|
32
36
|
provisioning?: RuntimeProvisioningContribution;
|
|
33
37
|
routes?: FastifyPluginAsync;
|
|
38
|
+
preservedUiStateKeys?: string[];
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Compose a server plugin from smaller server plugin fragments. Child
|
|
@@ -60,7 +65,8 @@ interface ServerBootstrapResult {
|
|
|
60
65
|
agentTools: AgentTool[];
|
|
61
66
|
provisioningContributions: WorkspaceProvisioningContribution[];
|
|
62
67
|
routeContributions: WorkspaceRouteContribution[];
|
|
68
|
+
preservedUiStateKeys: string[];
|
|
63
69
|
}
|
|
64
70
|
declare function bootstrapServer(options: ServerBootstrapOptions): ServerBootstrapResult;
|
|
65
71
|
|
|
66
|
-
export { type ComposeServerPluginsOptions as C, type ServerBootstrapOptions as S, type
|
|
72
|
+
export { type ComposeServerPluginsOptions as C, type ServerBootstrapOptions as S, type WorkspaceServerPlugin as W, type WorkspaceProvisioningContribution as a, type WorkspaceRouteContribution as b, createInMemoryBridge as c, composeServerPlugins as d, defineServerPlugin as e, type ServerBootstrapResult as f, ServerPluginError as g, bootstrapServer as h, validateServerPlugin as v };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
export { C as CommandResult, a as UiCommand, b as UiState } from './explorer-DtLUnuah.js';
|
|
1
|
+
export { C as ComposeServerPluginsOptions, S as ServerBootstrapOptions, f as ServerBootstrapResult, g as ServerPluginError, a as WorkspaceProvisioningContribution, b as WorkspaceRouteContribution, W as WorkspaceServerPlugin, h as bootstrapServer, d as composeServerPlugins, c as createInMemoryBridge, e as defineServerPlugin, v as validateServerPlugin } from './bootstrapServer-BreQ9QBc.js';
|
|
3
2
|
import { FastifyRequest, FastifyInstance } from 'fastify';
|
|
4
|
-
import { A as AgentTool } from './agent-tool-
|
|
5
|
-
|
|
6
|
-
export { C as ComposeServerPluginsOptions, S as ServerBootstrapOptions, e as ServerBootstrapResult, f as ServerPluginError, W as WorkspaceProvisioningContribution, a as WorkspaceRouteContribution, g as bootstrapServer, c as composeServerPlugins, d as defineServerPlugin, v as validateServerPlugin } from './bootstrapServer-BRUqUpVW.js';
|
|
3
|
+
import { U as UiBridge, A as AgentTool } from './agent-tool-DEtfQPVB.js';
|
|
4
|
+
export { C as CommandResult, a as UiCommand, b as UiState } from './agent-tool-DEtfQPVB.js';
|
|
7
5
|
export { PiPackageSource as WorkspacePiPackageSource } from '@hachej/boring-agent/server';
|
|
8
6
|
|
|
9
|
-
declare function createInMemoryBridge(): UiBridge;
|
|
10
|
-
|
|
11
7
|
interface UiRoutesOptions {
|
|
12
8
|
bridge?: UiBridge;
|
|
13
9
|
getBridge?: (request: FastifyRequest) => UiBridge | Promise<UiBridge>;
|
|
10
|
+
/**
|
|
11
|
+
* Server/plugin-owned state slots preserved across browser full-state PUTs.
|
|
12
|
+
* Browser UI snapshots are replace-style for normal workspace state, but
|
|
13
|
+
* these slots are published out-of-band by server plugins.
|
|
14
|
+
*/
|
|
15
|
+
preserveStateKeys?: string[];
|
|
14
16
|
}
|
|
15
17
|
declare function uiRoutes(app: FastifyInstance, opts: UiRoutesOptions, done: (err?: Error) => void): void;
|
|
16
18
|
|
|
@@ -57,28 +59,4 @@ declare function createExecUiTool(uiBridge: UiBridge, opts?: ExecUiToolOptions):
|
|
|
57
59
|
*/
|
|
58
60
|
declare function createWorkspaceUiTools(uiBridge: UiBridge, opts?: ExecUiToolOptions): AgentTool[];
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
name?: string;
|
|
62
|
-
label?: string;
|
|
63
|
-
adapter: ExplorerAdapter;
|
|
64
|
-
defaultLimit?: number;
|
|
65
|
-
maxLimit?: number;
|
|
66
|
-
}
|
|
67
|
-
interface DataCatalogSkillOptions {
|
|
68
|
-
label?: string;
|
|
69
|
-
toolName?: string;
|
|
70
|
-
surfaceKind?: string;
|
|
71
|
-
guidance?: string;
|
|
72
|
-
}
|
|
73
|
-
interface DataCatalogServerPluginOptions extends DataCatalogAgentToolOptions, DataCatalogSkillOptions {
|
|
74
|
-
id?: string;
|
|
75
|
-
}
|
|
76
|
-
declare function formatDataCatalogSearchResult(query: string, result: SearchResult): string;
|
|
77
|
-
declare function createDataCatalogAgentTool(options: DataCatalogAgentToolOptions): AgentTool;
|
|
78
|
-
declare function createDataCatalogSkillPrompt(options?: DataCatalogSkillOptions): string;
|
|
79
|
-
declare function createDataCatalogServerPlugin(options: DataCatalogServerPluginOptions): WorkspaceServerPlugin & {
|
|
80
|
-
agentTools: AgentTool[];
|
|
81
|
-
systemPrompt: string;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export { type DataCatalogAgentToolOptions, type DataCatalogServerPluginOptions, type DataCatalogSkillOptions, UiBridge, type UiRoutesOptions, WorkspaceServerPlugin, createDataCatalogAgentTool, createDataCatalogServerPlugin, createDataCatalogSkillPrompt, createExecUiTool, createGetUiStateTool, createInMemoryBridge, createWorkspaceUiTools, formatDataCatalogSearchResult, uiRoutes };
|
|
62
|
+
export { UiBridge, type UiRoutesOptions, createExecUiTool, createGetUiStateTool, createWorkspaceUiTools, uiRoutes };
|
package/dist/server.js
CHANGED
|
@@ -93,7 +93,11 @@ function uiRoutes(app, opts, done) {
|
|
|
93
93
|
async (request, reply) => {
|
|
94
94
|
const body = request.body;
|
|
95
95
|
const bridge = await resolveBridge(request);
|
|
96
|
-
await bridge.
|
|
96
|
+
const current = await bridge.getState() ?? {};
|
|
97
|
+
const preserved = Object.fromEntries(
|
|
98
|
+
(opts.preserveStateKeys ?? []).filter((key) => !(key in body.state) && key in current).map((key) => [key, current[key]])
|
|
99
|
+
);
|
|
100
|
+
await bridge.setState({ ...body.state, ...preserved });
|
|
97
101
|
return reply.code(204).send();
|
|
98
102
|
}
|
|
99
103
|
);
|
|
@@ -333,11 +337,11 @@ function createExecUiTool(uiBridge, opts = {}) {
|
|
|
333
337
|
" \u2014 Open a plugin-owned target through the workspace",
|
|
334
338
|
" surface resolver registry. Use this when a plugin",
|
|
335
339
|
" defines the mapping from domain target to panel",
|
|
336
|
-
" component, for example a
|
|
340
|
+
" component, for example a catalog row.",
|
|
337
341
|
" Example: {kind:'openSurface', params:{",
|
|
338
|
-
" kind:'
|
|
342
|
+
" kind:'my-plugin.open-row',",
|
|
339
343
|
" target:'orders_daily',",
|
|
340
|
-
" meta:{catalogId:'
|
|
344
|
+
" meta:{catalogId:'my-plugin'}}}",
|
|
341
345
|
"",
|
|
342
346
|
" closePanel params: { id: string }",
|
|
343
347
|
" closeWorkbenchLeftPane params: {}",
|
|
@@ -599,6 +603,11 @@ function validateServerPlugin(plugin) {
|
|
|
599
603
|
if (plugin.routes !== void 0 && typeof plugin.routes !== "function") {
|
|
600
604
|
fail(plugin.id, "routes must be a Fastify plugin function when provided");
|
|
601
605
|
}
|
|
606
|
+
if (plugin.preservedUiStateKeys !== void 0) {
|
|
607
|
+
if (!Array.isArray(plugin.preservedUiStateKeys) || plugin.preservedUiStateKeys.some((key) => typeof key !== "string" || key.length === 0)) {
|
|
608
|
+
fail(plugin.id, "preservedUiStateKeys must be a non-empty string array when provided");
|
|
609
|
+
}
|
|
610
|
+
}
|
|
602
611
|
if (plugin.provisioning !== void 0) {
|
|
603
612
|
validateProvisioning(plugin.id, plugin.provisioning);
|
|
604
613
|
}
|
|
@@ -654,6 +663,10 @@ function composeServerPlugins(options) {
|
|
|
654
663
|
...options.plugins.map((plugin) => plugin.routes),
|
|
655
664
|
options.routes
|
|
656
665
|
]);
|
|
666
|
+
const preservedUiStateKeys = [.../* @__PURE__ */ new Set([
|
|
667
|
+
...options.plugins.flatMap((plugin) => plugin.preservedUiStateKeys ?? []),
|
|
668
|
+
...options.preservedUiStateKeys ?? []
|
|
669
|
+
])];
|
|
657
670
|
return defineServerPlugin({
|
|
658
671
|
id: options.id,
|
|
659
672
|
...options.label !== void 0 ? { label: options.label } : {},
|
|
@@ -661,7 +674,8 @@ function composeServerPlugins(options) {
|
|
|
661
674
|
...systemPrompt ? { systemPrompt } : {},
|
|
662
675
|
...agentTools.length > 0 ? { agentTools } : {},
|
|
663
676
|
...provisioning ? { provisioning } : {},
|
|
664
|
-
...routes ? { routes } : {}
|
|
677
|
+
...routes ? { routes } : {},
|
|
678
|
+
...preservedUiStateKeys.length > 0 ? { preservedUiStateKeys } : {}
|
|
665
679
|
});
|
|
666
680
|
}
|
|
667
681
|
|
|
@@ -693,145 +707,26 @@ function bootstrapServer(options) {
|
|
|
693
707
|
const piPackages = collectPiPackages(finalPlugins);
|
|
694
708
|
const provisioningContributions = finalPlugins.filter((p) => p.provisioning).map((p) => ({ id: p.id, provisioning: p.provisioning }));
|
|
695
709
|
const routeContributions = finalPlugins.filter((p) => p.routes).map((p) => ({ id: p.id, routes: p.routes }));
|
|
710
|
+
const preservedUiStateKeys = [...new Set(finalPlugins.flatMap((p) => p.preservedUiStateKeys ?? []))];
|
|
696
711
|
return {
|
|
697
712
|
registered: finalPlugins.map((p) => p.id),
|
|
698
713
|
systemPromptAppend,
|
|
699
714
|
piPackages,
|
|
700
715
|
agentTools,
|
|
701
716
|
provisioningContributions,
|
|
702
|
-
routeContributions
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// src/plugins/dataCatalogPlugin/shared/constants.ts
|
|
707
|
-
var DATA_CATALOG_PLUGIN_ID = "data-catalog";
|
|
708
|
-
var DATA_CATALOG_DEFAULT_TOOL_NAME = "query_data_catalog";
|
|
709
|
-
var DATA_CATALOG_ROW_SURFACE_KIND = "data-catalog.open-row";
|
|
710
|
-
|
|
711
|
-
// src/plugins/dataCatalogPlugin/server/index.ts
|
|
712
|
-
function textResult(text, details) {
|
|
713
|
-
return { content: [{ type: "text", text }], details };
|
|
714
|
-
}
|
|
715
|
-
function errorResult(text) {
|
|
716
|
-
return { content: [{ type: "text", text }], isError: true };
|
|
717
|
-
}
|
|
718
|
-
function clampLimit(value, fallback, max) {
|
|
719
|
-
const numeric = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
720
|
-
if (!Number.isFinite(numeric)) return fallback;
|
|
721
|
-
return Math.max(1, Math.min(max, Math.floor(numeric)));
|
|
722
|
-
}
|
|
723
|
-
function normalizeLimitOptions(options) {
|
|
724
|
-
const rawMax = options.maxLimit ?? 50;
|
|
725
|
-
const maxLimit = typeof rawMax === "number" && Number.isFinite(rawMax) ? Math.max(1, Math.floor(rawMax)) : 50;
|
|
726
|
-
const rawDefault = options.defaultLimit ?? 20;
|
|
727
|
-
const defaultLimit = typeof rawDefault === "number" && Number.isFinite(rawDefault) ? Math.max(1, Math.min(maxLimit, Math.floor(rawDefault))) : Math.min(20, maxLimit);
|
|
728
|
-
return { defaultLimit, maxLimit };
|
|
729
|
-
}
|
|
730
|
-
function formatBadge(row) {
|
|
731
|
-
const parts = [
|
|
732
|
-
row.leading?.code,
|
|
733
|
-
...(row.trailing ?? []).map((badge) => badge.code),
|
|
734
|
-
row.meta
|
|
735
|
-
].filter(Boolean);
|
|
736
|
-
return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
|
|
737
|
-
}
|
|
738
|
-
function formatDataCatalogSearchResult(query, result) {
|
|
739
|
-
if (result.items.length === 0) {
|
|
740
|
-
return `No ${query ? `results for "${query}"` : "catalog results"}.`;
|
|
741
|
-
}
|
|
742
|
-
const lines = result.items.map((row) => {
|
|
743
|
-
const subtitle = row.subtitle ? ` \u2014 ${row.subtitle}` : "";
|
|
744
|
-
return `${row.id}: ${row.title}${subtitle}${formatBadge(row)}`;
|
|
745
|
-
});
|
|
746
|
-
const total = Number.isFinite(result.total) ? result.total : result.items.length;
|
|
747
|
-
return `Found ${total} results (showing ${result.items.length}):
|
|
748
|
-
|
|
749
|
-
${lines.join("\n")}`;
|
|
750
|
-
}
|
|
751
|
-
function createDataCatalogAgentTool(options) {
|
|
752
|
-
const name = options.name ?? DATA_CATALOG_DEFAULT_TOOL_NAME;
|
|
753
|
-
const label = options.label ?? "data catalog";
|
|
754
|
-
const { defaultLimit, maxLimit } = normalizeLimitOptions(options);
|
|
755
|
-
return {
|
|
756
|
-
name,
|
|
757
|
-
description: `Search the ${label}. Use this before opening data visualizations or asking for a specific dataset.`,
|
|
758
|
-
parameters: {
|
|
759
|
-
type: "object",
|
|
760
|
-
properties: {
|
|
761
|
-
query: {
|
|
762
|
-
type: "string",
|
|
763
|
-
description: "Search keywords for datasets, series, tables, or metrics."
|
|
764
|
-
},
|
|
765
|
-
limit: {
|
|
766
|
-
type: "number",
|
|
767
|
-
description: `Maximum number of results. Default ${defaultLimit}, max ${maxLimit}.`,
|
|
768
|
-
minimum: 1,
|
|
769
|
-
maximum: maxLimit
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
required: ["query"],
|
|
773
|
-
additionalProperties: false
|
|
774
|
-
},
|
|
775
|
-
async execute(params, ctx) {
|
|
776
|
-
const query = String(params.query ?? "").trim();
|
|
777
|
-
if (!query) return errorResult("query is required");
|
|
778
|
-
const limit = clampLimit(params.limit, defaultLimit, maxLimit);
|
|
779
|
-
try {
|
|
780
|
-
const result = await options.adapter.search({
|
|
781
|
-
query,
|
|
782
|
-
filters: {},
|
|
783
|
-
limit,
|
|
784
|
-
offset: 0,
|
|
785
|
-
signal: ctx.abortSignal
|
|
786
|
-
});
|
|
787
|
-
return textResult(formatDataCatalogSearchResult(query, result), result);
|
|
788
|
-
} catch (error) {
|
|
789
|
-
return errorResult(error instanceof Error ? error.message : String(error));
|
|
790
|
-
}
|
|
791
|
-
}
|
|
717
|
+
routeContributions,
|
|
718
|
+
preservedUiStateKeys
|
|
792
719
|
};
|
|
793
720
|
}
|
|
794
|
-
function createDataCatalogSkillPrompt(options = {}) {
|
|
795
|
-
const label = options.label ?? "data catalog";
|
|
796
|
-
const toolName = options.toolName ?? DATA_CATALOG_DEFAULT_TOOL_NAME;
|
|
797
|
-
const surfaceKind = options.surfaceKind ?? DATA_CATALOG_ROW_SURFACE_KIND;
|
|
798
|
-
const guidance = options.guidance?.trim();
|
|
799
|
-
return [
|
|
800
|
-
"## Data Catalog Plugin",
|
|
801
|
-
"",
|
|
802
|
-
`Use \`${toolName}\` to search the ${label} before referencing datasets, series, tables, or metrics.`,
|
|
803
|
-
`When you need to show a catalog row to the user, use the workspace UI bridge \`openSurface\` command with \`{ kind: '${surfaceKind}', target: row.id, meta: { catalogId, row } }\` so the client plugin resolver chooses the panel.`,
|
|
804
|
-
guidance ? "" : void 0,
|
|
805
|
-
guidance || void 0
|
|
806
|
-
].filter((line) => line !== void 0).join("\n");
|
|
807
|
-
}
|
|
808
|
-
function createDataCatalogServerPlugin(options) {
|
|
809
|
-
const tool = createDataCatalogAgentTool(options);
|
|
810
|
-
return defineServerPlugin({
|
|
811
|
-
id: options.id ?? DATA_CATALOG_PLUGIN_ID,
|
|
812
|
-
label: options.label ?? "Data Catalog",
|
|
813
|
-
agentTools: [tool],
|
|
814
|
-
systemPrompt: createDataCatalogSkillPrompt({
|
|
815
|
-
label: options.label,
|
|
816
|
-
toolName: tool.name,
|
|
817
|
-
surfaceKind: options.surfaceKind,
|
|
818
|
-
guidance: options.guidance
|
|
819
|
-
})
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
721
|
export {
|
|
823
722
|
ServerPluginError,
|
|
824
723
|
bootstrapServer,
|
|
825
724
|
composeServerPlugins,
|
|
826
|
-
createDataCatalogAgentTool,
|
|
827
|
-
createDataCatalogServerPlugin,
|
|
828
|
-
createDataCatalogSkillPrompt,
|
|
829
725
|
createExecUiTool,
|
|
830
726
|
createGetUiStateTool,
|
|
831
727
|
createInMemoryBridge,
|
|
832
728
|
createWorkspaceUiTools,
|
|
833
729
|
defineServerPlugin,
|
|
834
|
-
formatDataCatalogSearchResult,
|
|
835
730
|
uiRoutes,
|
|
836
731
|
validateServerPlugin
|
|
837
732
|
};
|
package/dist/shared.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export { C as CommandResult,
|
|
1
|
+
export { A as AgentTool, C as CommandResult, J as JSONSchema, T as ToolExecContext, c as ToolResult, U as UiBridge, a as UiCommand, b as UiState } from './agent-tool-DEtfQPVB.js';
|
|
2
2
|
import { ComponentType } from 'react';
|
|
3
3
|
import { DockviewPanelApi, DockviewApi } from 'dockview-react';
|
|
4
|
-
export { A as AgentTool, J as JSONSchema, T as ToolExecContext, a as ToolResult } from './agent-tool-NvxKfist.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Shared panel and command types — no runtime deps beyond React/dockview.
|
package/dist/testing.d.ts
CHANGED
|
@@ -8,12 +8,6 @@ import { ReactNode } from 'react';
|
|
|
8
8
|
import { RenderOptions } from '@testing-library/react';
|
|
9
9
|
import { RenderResult } from '@testing-library/react';
|
|
10
10
|
|
|
11
|
-
declare type Badge = {
|
|
12
|
-
/** 1–4 char mono code rendered as a chip. */
|
|
13
|
-
code: string;
|
|
14
|
-
tooltip?: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
11
|
/**
|
|
18
12
|
* Land on the app with a clean localStorage so persistence-sensitive tests
|
|
19
13
|
* start from defaults. Pre-opens the workbench surface unless told
|
|
@@ -135,8 +129,6 @@ export declare interface CreateMockRegistryOptions {
|
|
|
135
129
|
capabilities?: Record<string, boolean>;
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
export declare function createMockSeriesAdapter(): ExplorerAdapter;
|
|
139
|
-
|
|
140
132
|
export declare function createMockSessions(opts?: CreateMockSessionsOptions): MockSessionsStore;
|
|
141
133
|
|
|
142
134
|
export declare interface CreateMockSessionsOptions {
|
|
@@ -146,8 +138,6 @@ export declare interface CreateMockSessionsOptions {
|
|
|
146
138
|
activeId?: string;
|
|
147
139
|
}
|
|
148
140
|
|
|
149
|
-
export declare function createMockTablesAdapter(): ExplorerAdapter;
|
|
150
|
-
|
|
151
141
|
declare interface DynamicPaneConfig {
|
|
152
142
|
id: string;
|
|
153
143
|
component: string;
|
|
@@ -155,39 +145,6 @@ declare interface DynamicPaneConfig {
|
|
|
155
145
|
title?: string;
|
|
156
146
|
}
|
|
157
147
|
|
|
158
|
-
declare type ExplorerAdapter = {
|
|
159
|
-
search(args: SearchArgs): Promise<SearchResult>;
|
|
160
|
-
/** Optional. When omitted, the explorer renders flat (no facet popover). */
|
|
161
|
-
fetchFacets?(args: FacetsArgs): Promise<Facets>;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
declare type ExplorerRow = {
|
|
165
|
-
id: string;
|
|
166
|
-
title: string;
|
|
167
|
-
/** Optional muted second line (truncates with title). */
|
|
168
|
-
subtitle?: string;
|
|
169
|
-
/** Group key — must match one of the facet values for `groupBy`. */
|
|
170
|
-
group?: string;
|
|
171
|
-
/** Leading mono chip (e.g. type code, frequency). */
|
|
172
|
-
leading?: Badge;
|
|
173
|
-
/** Trailing mono chips for status flags (e.g. [D] derived, [LIVE]). */
|
|
174
|
-
trailing?: Badge[];
|
|
175
|
-
/** Right-aligned plain text for numeric metadata (e.g. "1.2M", "2.4s"). */
|
|
176
|
-
meta?: string;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
declare type Facets = Record<string, FacetValue[]>;
|
|
180
|
-
|
|
181
|
-
declare type FacetsArgs = {
|
|
182
|
-
filters: Record<string, string[]>;
|
|
183
|
-
signal?: AbortSignal;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
declare type FacetValue = {
|
|
187
|
-
value: string;
|
|
188
|
-
count: number;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
148
|
export declare interface MockBridgeState {
|
|
192
149
|
openPanels: PanelState[];
|
|
193
150
|
activeFile: string | null;
|
|
@@ -363,26 +320,6 @@ export declare type RenderPaneResult = RenderResult & {
|
|
|
363
320
|
registry: PanelRegistry;
|
|
364
321
|
};
|
|
365
322
|
|
|
366
|
-
declare type SearchArgs = {
|
|
367
|
-
query: string;
|
|
368
|
-
filters: Record<string, string[]>;
|
|
369
|
-
/** Scope to a single group's value (only set when paginating inside a group). */
|
|
370
|
-
group?: {
|
|
371
|
-
key: string;
|
|
372
|
-
value: string;
|
|
373
|
-
};
|
|
374
|
-
limit: number;
|
|
375
|
-
offset: number;
|
|
376
|
-
signal?: AbortSignal;
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
declare type SearchResult = {
|
|
380
|
-
items: ExplorerRow[];
|
|
381
|
-
/** Total count for the current scope (query + filters + optional group). */
|
|
382
|
-
total: number;
|
|
383
|
-
hasMore: boolean;
|
|
384
|
-
};
|
|
385
|
-
|
|
386
323
|
declare interface SessionItem {
|
|
387
324
|
id: string;
|
|
388
325
|
title: string;
|