@blackbelt-technology/pi-agent-dashboard 0.4.1 → 0.4.3
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/AGENTS.md +80 -32
- package/README.md +7 -3
- package/docs/architecture.md +361 -12
- package/package.json +7 -7
- package/packages/extension/package.json +7 -2
- package/packages/extension/src/__tests__/ask-user-schema-discriminator.test.ts +141 -0
- package/packages/extension/src/__tests__/ask-user-tool.test.ts +51 -7
- package/packages/extension/src/__tests__/multiselect-dashboard-routing.test.ts +203 -0
- package/packages/extension/src/__tests__/multiselect-polyfill.test.ts +92 -0
- package/packages/extension/src/__tests__/no-tui-multiselect-arm-regression.test.ts +81 -0
- package/packages/extension/src/__tests__/openspec-activity-detector.test.ts +37 -0
- package/packages/extension/src/__tests__/ui-decorators.test.ts +309 -0
- package/packages/extension/src/__tests__/ui-modules.test.ts +293 -0
- package/packages/extension/src/ask-user-tool.ts +165 -57
- package/packages/extension/src/bridge.ts +97 -4
- package/packages/extension/src/multiselect-decode.ts +40 -0
- package/packages/extension/src/multiselect-polyfill.ts +38 -8
- package/packages/extension/src/ui-modules.ts +272 -0
- package/packages/server/package.json +9 -3
- package/packages/server/src/__tests__/auto-attach.test.ts +61 -8
- package/packages/server/src/__tests__/browse-endpoint.test.ts +295 -19
- package/packages/server/src/__tests__/cli-bootstrap.test.ts +36 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +163 -0
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +315 -0
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +303 -0
- package/packages/server/src/__tests__/directory-service.test.ts +174 -0
- package/packages/server/src/__tests__/installed-package-enricher.test.ts +225 -0
- package/packages/server/src/__tests__/package-manager-wrapper-move.test.ts +414 -0
- package/packages/server/src/__tests__/package-routes.test.ts +136 -3
- package/packages/server/src/__tests__/package-source-helpers.test.ts +101 -0
- package/packages/server/src/__tests__/pending-attach-registry.test.ts +123 -0
- package/packages/server/src/__tests__/pending-resume-intent-registry.test.ts +138 -0
- package/packages/server/src/__tests__/pi-core-checker.test.ts +73 -30
- package/packages/server/src/__tests__/pi-gateway-consume-pending-attach.test.ts +112 -0
- package/packages/server/src/__tests__/post-install-openspec-refresh.test.ts +180 -0
- package/packages/server/src/__tests__/post-install-rescan.test.ts +134 -0
- package/packages/server/src/__tests__/proposal-attach-naming.test.ts +79 -0
- package/packages/server/src/__tests__/session-action-handler-spawn-with-attach.test.ts +108 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +55 -0
- package/packages/server/src/__tests__/session-order-reboot.test.ts +242 -0
- package/packages/server/src/__tests__/session-scanner.test.ts +44 -0
- package/packages/server/src/__tests__/subscription-handler.test.ts +40 -0
- package/packages/server/src/__tests__/translate-path-source.test.ts +77 -0
- package/packages/server/src/__tests__/ui-decorators-replay.test.ts +209 -0
- package/packages/server/src/__tests__/ui-modules-replay.test.ts +221 -0
- package/packages/server/src/browse.ts +118 -13
- package/packages/server/src/browser-gateway.ts +19 -0
- package/packages/server/src/browser-handlers/__tests__/session-meta-handler.test.ts +183 -0
- package/packages/server/src/browser-handlers/directory-handler.ts +7 -1
- package/packages/server/src/browser-handlers/handler-context.ts +15 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +29 -3
- package/packages/server/src/browser-handlers/session-meta-handler.ts +46 -12
- package/packages/server/src/browser-handlers/subscription-handler.ts +46 -1
- package/packages/server/src/cli.ts +5 -6
- package/packages/server/src/directory-service.ts +156 -15
- package/packages/server/src/event-wiring.ts +111 -10
- package/packages/server/src/installed-package-enricher.ts +143 -0
- package/packages/server/src/package-manager-wrapper.ts +305 -8
- package/packages/server/src/package-source-helpers.ts +104 -0
- package/packages/server/src/pending-attach-registry.ts +112 -0
- package/packages/server/src/pending-resume-intent-registry.ts +107 -0
- package/packages/server/src/pi-core-checker.ts +9 -14
- package/packages/server/src/pi-gateway.ts +14 -0
- package/packages/server/src/proposal-attach-naming.ts +47 -0
- package/packages/server/src/routes/file-routes.ts +29 -3
- package/packages/server/src/routes/package-routes.ts +72 -3
- package/packages/server/src/routes/plugin-config-routes.ts +129 -0
- package/packages/server/src/routes/system-routes.ts +2 -0
- package/packages/server/src/server.ts +339 -10
- package/packages/server/src/session-api.ts +30 -5
- package/packages/server/src/session-order-manager.ts +22 -0
- package/packages/server/src/session-scanner.ts +10 -1
- package/packages/shared/package.json +9 -2
- package/packages/shared/src/__tests__/browser-protocol-types.test.ts +59 -0
- package/packages/shared/src/__tests__/config-plugins.test.ts +68 -0
- package/packages/shared/src/__tests__/extension-ui-module-shape.test.ts +265 -0
- package/packages/shared/src/__tests__/no-raw-openspec-status-in-skills.test.ts +81 -0
- package/packages/shared/src/__tests__/openspec-design-evidence.test.ts +288 -0
- package/packages/shared/src/__tests__/openspec-effective-status-script.test.ts +174 -0
- package/packages/shared/src/__tests__/openspec-poller-design-override.test.ts +225 -0
- package/packages/shared/src/__tests__/openspec-poller-specs-override.test.ts +284 -0
- package/packages/shared/src/__tests__/openspec-specs-evidence.test.ts +144 -0
- package/packages/shared/src/__tests__/platform/is-appimage-self-hit.test.ts +164 -0
- package/packages/shared/src/__tests__/plugin-bridge-register-extended.test.ts +72 -0
- package/packages/shared/src/__tests__/plugin-bridge-register.test.ts +113 -0
- package/packages/shared/src/__tests__/plugin-config-update-protocol.test.ts +41 -0
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +123 -0
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +5 -1
- package/packages/shared/src/__tests__/spawn-session-attach-proposal.test.ts +47 -0
- package/packages/shared/src/__tests__/tool-registry-strategies-appimage.test.ts +118 -0
- package/packages/shared/src/browser-protocol.ts +110 -4
- package/packages/shared/src/config.ts +45 -0
- package/packages/shared/src/dashboard-plugin/index.ts +11 -0
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +58 -0
- package/packages/shared/src/dashboard-plugin/plugin-status.ts +26 -0
- package/packages/shared/src/dashboard-plugin/slot-props.ts +92 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +151 -0
- package/packages/shared/src/openspec-activity-detector.ts +18 -22
- package/packages/shared/src/openspec-design-evidence.ts +109 -0
- package/packages/shared/src/openspec-poller.ts +117 -3
- package/packages/shared/src/openspec-specs-evidence.ts +79 -0
- package/packages/shared/src/platform/binary-lookup.ts +96 -1
- package/packages/shared/src/plugin-bridge-register.ts +139 -0
- package/packages/shared/src/protocol.ts +56 -2
- package/packages/shared/src/recommended-extensions.ts +7 -1
- package/packages/shared/src/rest-api.ts +68 -3
- package/packages/shared/src/state-replay.ts +11 -1
- package/packages/shared/src/tool-registry/strategies.ts +17 -3
- package/packages/shared/src/types.ts +160 -0
|
@@ -23,9 +23,19 @@ import type { EventForwardMessage } from "./protocol.js";
|
|
|
23
23
|
* pi 0.69+, where the bridge sees `message_start` before pi has assigned
|
|
24
24
|
* the entry id. Replay has no such gap.
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* @param knownContextWindow Optional override for the context window size,
|
|
28
|
+
* typically `session.contextWindow` from `.meta.json` (which was persisted
|
|
29
|
+
* from a live `turn_end` event). When provided, it is used in place of the
|
|
30
|
+
* `inferContextWindow(modelId)` heuristic for every synthesized
|
|
31
|
+
* `stats_update` event. The heuristic ignores Sonnet's 1M variant and
|
|
32
|
+
* pins Claude to 200k, so passing the persisted value avoids a brief
|
|
33
|
+
* 200k flicker on reload before the next live `turn_end` arrives.
|
|
34
|
+
*/
|
|
26
35
|
export function replayEntriesAsEvents(
|
|
27
36
|
sessionId: string,
|
|
28
37
|
entries: any[],
|
|
38
|
+
knownContextWindow?: number,
|
|
29
39
|
): EventForwardMessage[] {
|
|
30
40
|
const messages: EventForwardMessage[] = [];
|
|
31
41
|
const openToolCalls = new Set<string>(); // track tool calls without results
|
|
@@ -86,7 +96,7 @@ export function replayEntriesAsEvents(
|
|
|
86
96
|
if (totalTokens && totalTokens > 0) {
|
|
87
97
|
statsData.contextUsage = {
|
|
88
98
|
tokens: totalTokens,
|
|
89
|
-
contextWindow: inferContextWindow(currentModel),
|
|
99
|
+
contextWindow: knownContextWindow ?? inferContextWindow(currentModel),
|
|
90
100
|
};
|
|
91
101
|
}
|
|
92
102
|
messages.push(makeEvent(sessionId, "stats_update", ts, statsData));
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { existsSync } from "node:fs";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
import path from "node:path";
|
|
14
|
-
import { ToolResolver } from "../platform/binary-lookup.js";
|
|
14
|
+
import { ToolResolver, isAppImageSelfHit } from "../platform/binary-lookup.js";
|
|
15
15
|
import { getManagedBin, getManagedDir } from "../managed-paths.js";
|
|
16
16
|
import * as npm from "../platform/npm.js";
|
|
17
17
|
import type { Strategy, StrategyCtx, StrategyResult } from "./types.js";
|
|
@@ -152,6 +152,17 @@ export function npmGlobalStrategy(
|
|
|
152
152
|
/**
|
|
153
153
|
* PATH search via `ToolResolver.which()`. This is the plain-old "is it
|
|
154
154
|
* on PATH" strategy and should appear last in most chains.
|
|
155
|
+
*
|
|
156
|
+
* Filters AppImage self-hits via `isAppImageSelfHit` — when the host
|
|
157
|
+
* runs as a Linux AppImage with `executableName: "pi-dashboard"`, the
|
|
158
|
+
* AppImage runtime prepends its squashfs mount to PATH, so the first
|
|
159
|
+
* `which pi-dashboard` hit can be the Electron launcher itself.
|
|
160
|
+
* Trusting that result spawns the Electron app recursively as if it
|
|
161
|
+
* were the dashboard CLI, which never opens the dashboard port and
|
|
162
|
+
* causes the loading screen to hang. Every tool registered via
|
|
163
|
+
* `whereStrategy` inherits this guard transparently.
|
|
164
|
+
*
|
|
165
|
+
* See change: fix-electron-appimage-cli-self-detection (D2).
|
|
155
166
|
*/
|
|
156
167
|
export function whereStrategy(binaryName: string, deps?: StrategyDeps): Strategy {
|
|
157
168
|
const { which } = d(deps);
|
|
@@ -159,8 +170,11 @@ export function whereStrategy(binaryName: string, deps?: StrategyDeps): Strategy
|
|
|
159
170
|
name: "where",
|
|
160
171
|
run(): StrategyResult {
|
|
161
172
|
const p = which(binaryName);
|
|
162
|
-
if (p) return { ok:
|
|
163
|
-
|
|
173
|
+
if (!p) return { ok: false, reason: `not found on PATH` };
|
|
174
|
+
if (isAppImageSelfHit(p)) {
|
|
175
|
+
return { ok: false, reason: `appimage-self-hit: ${p}` };
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, path: p };
|
|
164
178
|
},
|
|
165
179
|
};
|
|
166
180
|
}
|
|
@@ -61,8 +61,168 @@ export interface DashboardSession {
|
|
|
61
61
|
/** Timestamp when metrics were last received */
|
|
62
62
|
updatedAt: number;
|
|
63
63
|
};
|
|
64
|
+
/** Extension-declared UI modules (Phase 1: management-modal slot). */
|
|
65
|
+
uiModules?: ExtensionUiModule[];
|
|
66
|
+
/** Cached row data per `view.dataEvent` for table/grid views. Per-event item cap is enforced server-side. */
|
|
67
|
+
uiDataMap?: Record<string, unknown[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Phase-2 live in-page decorations (footer-segment, agent-metric, breadcrumb,
|
|
70
|
+
* gate, toast). Keyed by `${kind}:${namespace}:${id}`. Last-write-wins on
|
|
71
|
+
* upsert; explicit removal via `ext_ui_decorator { removed: true }` deletes
|
|
72
|
+
* the entry. See change: add-extension-ui-decorations.
|
|
73
|
+
*/
|
|
74
|
+
uiDecorators?: Record<string, DecoratorDescriptor>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Extension UI System (Phase 1: management-modal slot) ───────────
|
|
78
|
+
// Per `extension-ui-system` design + `add-extension-ui-modal` change.
|
|
79
|
+
// Field/type names match PR #15 verbatim so any later archival diff stays small.
|
|
80
|
+
|
|
81
|
+
export type UiViewKind = "table" | "grid" | "form";
|
|
82
|
+
|
|
83
|
+
export type UiFieldKind =
|
|
84
|
+
| "text"
|
|
85
|
+
| "number"
|
|
86
|
+
| "boolean"
|
|
87
|
+
| "select"
|
|
88
|
+
| "code"
|
|
89
|
+
| "datetime"
|
|
90
|
+
| "textarea";
|
|
91
|
+
|
|
92
|
+
export interface UiField {
|
|
93
|
+
/** Dot-path into row / form-state. */
|
|
94
|
+
key: string;
|
|
95
|
+
label: string;
|
|
96
|
+
kind: UiFieldKind;
|
|
97
|
+
/** For kind: "select". */
|
|
98
|
+
options?: string[];
|
|
99
|
+
placeholder?: string;
|
|
100
|
+
required?: boolean;
|
|
101
|
+
readOnly?: boolean;
|
|
102
|
+
/** Legacy alias for kind: "textarea". Prefer `kind: "textarea"`. */
|
|
103
|
+
multiline?: boolean;
|
|
104
|
+
/** Display-only: table column width. */
|
|
105
|
+
width?: string | number;
|
|
106
|
+
/** For kind: "code". Hint to syntax highlighter. */
|
|
107
|
+
language?: string;
|
|
64
108
|
}
|
|
65
109
|
|
|
110
|
+
export interface UiAction {
|
|
111
|
+
/** Action id, echoed back as the `action` field on the `ui_management` message. */
|
|
112
|
+
id: string;
|
|
113
|
+
label: string;
|
|
114
|
+
/** MDI icon key from `@mdi/js` (e.g. `"mdiCheckCircle"`). Unknown keys render no icon. */
|
|
115
|
+
icon?: string;
|
|
116
|
+
variant?: "primary" | "secondary" | "danger";
|
|
117
|
+
/** Event name re-emitted on the extension's `pi.events` bus when the action fires. */
|
|
118
|
+
event: string;
|
|
119
|
+
params?: Record<string, unknown>;
|
|
120
|
+
/** If present, dashboard mounts ConfirmDialog with this message before dispatching. */
|
|
121
|
+
confirm?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface UiSection {
|
|
125
|
+
id: string;
|
|
126
|
+
title?: string;
|
|
127
|
+
description?: string;
|
|
128
|
+
fields: UiField[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface UiView {
|
|
132
|
+
kind: UiViewKind;
|
|
133
|
+
/** Table/grid columns; form fields when no `sections` provided. */
|
|
134
|
+
fields?: UiField[];
|
|
135
|
+
/** For form view: grouped fields. Mutually exclusive with top-level `fields`. */
|
|
136
|
+
sections?: UiSection[];
|
|
137
|
+
/** Event name to request rows; required for `table`/`grid`. */
|
|
138
|
+
dataEvent?: string;
|
|
139
|
+
/** Unique-row field for `table`/`grid` (default: `"id"`). */
|
|
140
|
+
rowKey?: string;
|
|
141
|
+
/** Per-row actions for `table`/`grid`. */
|
|
142
|
+
rowActions?: UiAction[];
|
|
143
|
+
/** Shown when `items.length === 0`. */
|
|
144
|
+
emptyState?: string;
|
|
145
|
+
/** Top-of-modal toolbar actions. */
|
|
146
|
+
actions?: UiAction[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ExtensionUiModule {
|
|
150
|
+
/** Phase 1: only `"management-modal"`. */
|
|
151
|
+
kind: "management-modal";
|
|
152
|
+
/** Unique within the session. Last-write-wins on collision. */
|
|
153
|
+
id: string;
|
|
154
|
+
/** Exact slash command (case-sensitive). */
|
|
155
|
+
command: string;
|
|
156
|
+
title: string;
|
|
157
|
+
description?: string;
|
|
158
|
+
/** MDI icon key from `@mdi/js`. */
|
|
159
|
+
icon?: string;
|
|
160
|
+
/** Free-form group label (sidebar grouping in future). */
|
|
161
|
+
category?: string;
|
|
162
|
+
view: UiView;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Extension UI System (Phase 2: live in-page decorations) ──────
|
|
166
|
+
// Per `extension-ui-system` design + `add-extension-ui-decorations` change.
|
|
167
|
+
// Single discriminated union forwarded as one `ext_ui_decorator` message per
|
|
168
|
+
// descriptor. Cache key: `${kind}:${namespace}:${id}`. `namespace` MUST match
|
|
169
|
+
// `/^[a-z0-9-]+$/`; the bridge drops malformed namespaces with a warning.
|
|
170
|
+
|
|
171
|
+
export type DecoratorKind =
|
|
172
|
+
| "footer-segment"
|
|
173
|
+
| "agent-metric"
|
|
174
|
+
| "breadcrumb"
|
|
175
|
+
| "gate"
|
|
176
|
+
| "toast";
|
|
177
|
+
|
|
178
|
+
export interface FooterSegmentPayload {
|
|
179
|
+
text: string;
|
|
180
|
+
tooltip?: string;
|
|
181
|
+
/** MDI icon key from `@mdi/js`. Unknown keys render no icon. */
|
|
182
|
+
icon?: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface AgentMetricPayload {
|
|
186
|
+
/** Matches the agent id rendered by `FlowAgentCard`. */
|
|
187
|
+
agentId: string;
|
|
188
|
+
text: string;
|
|
189
|
+
tooltip?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface BreadcrumbStep {
|
|
193
|
+
id: string;
|
|
194
|
+
label: string;
|
|
195
|
+
status: "pending" | "active" | "done" | "error";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface BreadcrumbPayload {
|
|
199
|
+
steps: BreadcrumbStep[];
|
|
200
|
+
/** Step id of the currently-active step (overrides `status: "active"` selection). */
|
|
201
|
+
current?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface GatePayload {
|
|
205
|
+
/** Matches the flow id rendered in `FlowLaunchDialog`. */
|
|
206
|
+
flowId: string;
|
|
207
|
+
available: boolean;
|
|
208
|
+
/** Reason rendered as a tooltip when `available: false`. */
|
|
209
|
+
reason?: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface ToastPayload {
|
|
213
|
+
level: "info" | "success" | "warn" | "error";
|
|
214
|
+
message: string;
|
|
215
|
+
/** Auto-dismiss after this many ms. Default 5000; `0` = sticky. */
|
|
216
|
+
durationMs?: number;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export type DecoratorDescriptor =
|
|
220
|
+
| { kind: "footer-segment"; namespace: string; id: string; payload: FooterSegmentPayload }
|
|
221
|
+
| { kind: "agent-metric"; namespace: string; id: string; payload: AgentMetricPayload }
|
|
222
|
+
| { kind: "breadcrumb"; namespace: string; id: string; payload: BreadcrumbPayload }
|
|
223
|
+
| { kind: "gate"; namespace: string; id: string; payload: GatePayload }
|
|
224
|
+
| { kind: "toast"; namespace: string; id: string; payload: ToastPayload };
|
|
225
|
+
|
|
66
226
|
/** An event forwarded from a pi session */
|
|
67
227
|
export interface DashboardEvent {
|
|
68
228
|
eventType: string;
|