@agimon-ai/po-web-dashboard 0.0.0-alpha.4 → 0.0.0-alpha.6
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/dist/views.cjs +1 -0
- package/dist/views.d.mts +2 -2
- package/dist/views.mjs +2 -2
- package/dist/web/features/activity/ContextPanel.cjs +183 -147
- package/dist/web/features/activity/ContextPanel.cjs.map +1 -1
- package/dist/web/features/activity/ContextPanel.d.mts +23 -0
- package/dist/web/features/activity/ContextPanel.d.mts.map +1 -1
- package/dist/web/features/activity/ContextPanel.mjs +185 -150
- package/dist/web/features/activity/ContextPanel.mjs.map +1 -1
- package/package.json +5 -5
package/dist/views.cjs
CHANGED
|
@@ -61,6 +61,7 @@ exports.Composer = require_Composer.Composer;
|
|
|
61
61
|
exports.ComposerPrompt = require_ComposerPrompt.ComposerPrompt;
|
|
62
62
|
exports.ContextItemDialog = require_ContextItemDialog.ContextItemDialog;
|
|
63
63
|
exports.ContextPanel = require_ContextPanel.ContextPanel;
|
|
64
|
+
exports.ContextPanelView = require_ContextPanel.ContextPanelView;
|
|
64
65
|
exports.ContributedSettings = require_ContributedSettings.ContributedSettings;
|
|
65
66
|
exports.DevProxyTargets = require_DevProxyTargets.DevProxyTargets;
|
|
66
67
|
exports.DialogOverlay = require_DialogOverlay.DialogOverlay;
|
package/dist/views.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import { QrCode, QrCodeProps } from "./web/components/QrCode.mjs";
|
|
|
5
5
|
import { SessionMcp, SessionMcpTools } from "./web/components/SessionMcp.mjs";
|
|
6
6
|
import { ActivityDock } from "./web/features/activity/ActivityDock.mjs";
|
|
7
7
|
import { ContextItemDialog, ContextItemDialogProps } from "./web/features/activity/ContextItemDialog.mjs";
|
|
8
|
-
import { ContextPanel } from "./web/features/activity/ContextPanel.mjs";
|
|
8
|
+
import { ContextPanel, ContextPanelView } from "./web/features/activity/ContextPanel.mjs";
|
|
9
9
|
import { DockTabs } from "./web/features/activity/DockTabs.mjs";
|
|
10
10
|
import { RefusedCard } from "./web/features/connection/RefusedCard.mjs";
|
|
11
11
|
import { DialogOverlay } from "./web/features/dialogs/DialogOverlay.mjs";
|
|
@@ -53,4 +53,4 @@ import { SettingsPanelHost } from "./web/features/settings/SettingsPanelHost.mjs
|
|
|
53
53
|
import { TemplateSettings } from "./web/features/settings/TemplateSettings.mjs";
|
|
54
54
|
import { TunnelSettings } from "./web/features/settings/TunnelSettings.mjs";
|
|
55
55
|
import { TopBar } from "./web/features/status/TopBar.mjs";
|
|
56
|
-
export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextItemDialogProps, ContextPanel, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MascotMarkProps, MentionPreviewAsset, MentionPreviews, MessageFileLinkHandler, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QrCodeProps, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SandboxWorkspacesProps, SelectionBar, SessionActivity, SessionActivityView, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TrustedDeviceListViewProps, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
|
|
56
|
+
export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextItemDialogProps, ContextPanel, ContextPanelView, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MascotMarkProps, MentionPreviewAsset, MentionPreviews, MessageFileLinkHandler, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QrCodeProps, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SandboxWorkspacesProps, SelectionBar, SessionActivity, SessionActivityView, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TrustedDeviceListViewProps, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
|
package/dist/views.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { PluginSurface } from "./web/components/PluginSurface.mjs";
|
|
|
4
4
|
import { QrCode } from "./web/components/QrCode.mjs";
|
|
5
5
|
import { SessionMcp, SessionMcpTools } from "./web/components/SessionMcp.mjs";
|
|
6
6
|
import { ContextItemDialog } from "./web/features/activity/ContextItemDialog.mjs";
|
|
7
|
-
import { ContextPanel } from "./web/features/activity/ContextPanel.mjs";
|
|
7
|
+
import { ContextPanel, ContextPanelView } from "./web/features/activity/ContextPanel.mjs";
|
|
8
8
|
import { DockTabs } from "./web/features/activity/DockTabs.mjs";
|
|
9
9
|
import { ActivityDock } from "./web/features/activity/ActivityDock.mjs";
|
|
10
10
|
import { RefusedCard } from "./web/features/connection/RefusedCard.mjs";
|
|
@@ -53,4 +53,4 @@ import { SettingsMenu } from "./web/features/settings/SettingsMenu.mjs";
|
|
|
53
53
|
import { RepositoryWorkspace } from "./web/features/settings/RepositoryWorkspace.mjs";
|
|
54
54
|
import { SettingsPanelHost } from "./web/features/settings/SettingsPanelHost.mjs";
|
|
55
55
|
import { TopBar } from "./web/features/status/TopBar.mjs";
|
|
56
|
-
export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextPanel, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MentionPreviewAsset, MentionPreviews, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SelectionBar, SessionActivity, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
|
|
56
|
+
export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextPanel, ContextPanelView, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MentionPreviewAsset, MentionPreviews, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SelectionBar, SessionActivity, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
|
|
@@ -9,27 +9,9 @@ let _agimon_ai_po_core_contextApi = require("@agimon-ai/po-core/contextApi");
|
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
let _agimon_ai_po_web_components = require("@agimon-ai/po-web-components");
|
|
11
11
|
//#region src/web/features/activity/ContextPanel.tsx
|
|
12
|
-
/** Short enough to sit in a fixed column without wrapping the tool's own name. */
|
|
13
|
-
const SOURCE_LABEL = {
|
|
14
|
-
extension: "ext",
|
|
15
|
-
mcp: "mcp",
|
|
16
|
-
plugin: "plug",
|
|
17
|
-
core: "core"
|
|
18
|
-
};
|
|
19
|
-
/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */
|
|
20
12
|
function tokens(value) {
|
|
21
13
|
return value === null ? "-" : `~${value.toLocaleString()}`;
|
|
22
14
|
}
|
|
23
|
-
/**
|
|
24
|
-
* What the session is carrying.
|
|
25
|
-
*
|
|
26
|
-
* Every tool and skill in here is spending context before the first message,
|
|
27
|
-
* so the surface is built around the one question worth asking of it: what is
|
|
28
|
-
* this costing, and what do I switch off to stop paying it. Modes head the
|
|
29
|
-
* groups because a mode is what you switch; packages head the rows inside them
|
|
30
|
-
* because a package is what you add or remove. It reads only; switching stays
|
|
31
|
-
* with the composer chips that already own it.
|
|
32
|
-
*/
|
|
33
15
|
function withoutMcpInventory(groups) {
|
|
34
16
|
return groups.map((group) => {
|
|
35
17
|
const mcp = group.items.filter((item) => item.source === "mcp");
|
|
@@ -57,192 +39,246 @@ function ContextPanel() {
|
|
|
57
39
|
}
|
|
58
40
|
const warnings = (0, _agimon_ai_po_core_contextApi.parseContextToolWarnings)(statuses[_agimon_ai_po_core_contextApi.CONTEXT_TOOL_WARNINGS_STATUS_KEY]);
|
|
59
41
|
const groups = context ? require_contextComposition.projectedGroups(context) : require_contextComposition.contextGroups(statuses, widgets, projection);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
42
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextPanelView, {
|
|
43
|
+
groups: withoutMcpInventory(groups),
|
|
44
|
+
total: require_contextComposition.totalTokens(groups),
|
|
45
|
+
idle: require_contextComposition.inactiveTotal(groups),
|
|
46
|
+
systemPrompt: context?.systemPrompt,
|
|
47
|
+
estimator: context?.estimator,
|
|
48
|
+
warnings,
|
|
49
|
+
onSelect: setTarget,
|
|
50
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PluginSurface.PluginSurface, {
|
|
51
|
+
slot: require_pluginRegistry.HOST_SLOTS.context,
|
|
52
|
+
sessionId
|
|
53
|
+
})
|
|
54
|
+
}, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ContextItemDialog.ContextItemDialog, {
|
|
55
|
+
sessionId,
|
|
56
|
+
target,
|
|
57
|
+
warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
|
|
58
|
+
onClose: () => setTarget(null)
|
|
59
|
+
})] });
|
|
60
|
+
}
|
|
61
|
+
/** Presentation shared with the deterministic design stories. Totals never depend on the search. */
|
|
62
|
+
function ContextPanelView({ groups, total, idle, systemPrompt, estimator = "gpt-tokenizer", warnings = {}, onSelect, children }) {
|
|
63
|
+
const [search, setSearch] = (0, react.useState)("");
|
|
64
|
+
const query = search.trim().toLowerCase();
|
|
65
|
+
const matches = (value) => value.toLowerCase().includes(query);
|
|
66
|
+
const visibleGroups = groups.filter((group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)));
|
|
63
67
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
64
68
|
"data-testid": "context-panel",
|
|
65
69
|
className: "flex min-h-0 min-w-0 flex-1 flex-col",
|
|
66
70
|
children: [
|
|
71
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
72
|
+
className: "shrink-0 space-y-3 p-3",
|
|
73
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
74
|
+
className: "flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3",
|
|
75
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
76
|
+
className: "min-w-0 flex-1",
|
|
77
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
78
|
+
className: "text-sm text-po-dim",
|
|
79
|
+
children: "Active context"
|
|
80
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
81
|
+
className: "flex flex-wrap items-baseline gap-2",
|
|
82
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
83
|
+
"data-testid": "context-total",
|
|
84
|
+
className: "text-3xl font-semibold text-po-hi tabular-nums",
|
|
85
|
+
children: tokens(total)
|
|
86
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
87
|
+
className: "text-sm text-po-dim",
|
|
88
|
+
children: "tokens"
|
|
89
|
+
})]
|
|
90
|
+
})]
|
|
91
|
+
}), idle > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
92
|
+
className: "shrink-0 border-l border-po-border pl-3",
|
|
93
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
94
|
+
className: "text-sm text-po-dim",
|
|
95
|
+
children: "Inactive"
|
|
96
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
97
|
+
"data-testid": "context-inactive",
|
|
98
|
+
className: "text-lg font-medium text-po-dim tabular-nums",
|
|
99
|
+
children: `+${tokens(idle)}`
|
|
100
|
+
})]
|
|
101
|
+
}) : null]
|
|
102
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
103
|
+
className: "relative",
|
|
104
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.SearchIcon, {
|
|
105
|
+
"aria-hidden": true,
|
|
106
|
+
className: "pointer-events-none absolute top-2 left-3 size-4 text-po-dim"
|
|
107
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.Input, {
|
|
108
|
+
type: "search",
|
|
109
|
+
"aria-label": "Find in context",
|
|
110
|
+
placeholder: "Find in context",
|
|
111
|
+
value: search,
|
|
112
|
+
onChange: (event) => setSearch(event.target.value),
|
|
113
|
+
className: "w-full pl-9 placeholder:text-po-dim"
|
|
114
|
+
})]
|
|
115
|
+
})]
|
|
116
|
+
}),
|
|
67
117
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
68
118
|
"data-testid": "context-scroll",
|
|
69
|
-
className: "min-h-0 flex-1 overflow-y-auto",
|
|
70
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
71
|
-
slot: require_pluginRegistry.HOST_SLOTS.context,
|
|
72
|
-
sessionId
|
|
73
|
-
}), groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
|
|
119
|
+
className: "min-h-0 flex-1 overflow-y-auto px-3 pb-3",
|
|
120
|
+
children: [children, groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
|
|
74
121
|
"data-testid": "context-empty",
|
|
75
122
|
className: "px-4 py-5",
|
|
76
|
-
title: "
|
|
77
|
-
description: "
|
|
78
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
title: "No composition yet",
|
|
124
|
+
description: "The session reports its major mode, minor modes, and domains once it has started."
|
|
125
|
+
}) : visibleGroups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
|
|
126
|
+
title: "No matches",
|
|
127
|
+
description: "Try a mode, package, tool, or skill name."
|
|
128
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
129
|
+
className: "overflow-hidden rounded-lg border border-po-border-soft",
|
|
130
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
131
|
+
className: "flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim",
|
|
132
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Loaded context" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Est. tokens" })]
|
|
133
|
+
}), visibleGroups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextGroupView, {
|
|
81
134
|
group,
|
|
135
|
+
query,
|
|
82
136
|
warnings,
|
|
83
|
-
onSelect
|
|
84
|
-
}, `${group.kind}:${group.id}`))
|
|
137
|
+
onSelect
|
|
138
|
+
}, `${group.kind}:${group.id}:${query ? "search" : "browse"}`))]
|
|
85
139
|
})]
|
|
86
140
|
}),
|
|
87
141
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
88
|
-
className: "shrink-0 border-t border-po-border px-4 py-3",
|
|
89
|
-
children: [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
owner: "session"
|
|
99
|
-
}),
|
|
100
|
-
className: "flex-row items-center justify-between gap-2 rounded-none px-0 py-px",
|
|
101
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
102
|
-
className: "text-sm text-po-dim",
|
|
103
|
-
children: context.systemPrompt.stage === "base" ? "system prompt (base)" : "system prompt"
|
|
104
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
105
|
-
className: "text-sm text-po-dim",
|
|
106
|
-
children: tokens(context.systemPrompt.tokens)
|
|
107
|
-
})]
|
|
108
|
-
}) : null,
|
|
109
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
110
|
-
className: "flex items-center justify-between",
|
|
111
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
112
|
-
className: "text-sm font-semibold text-po-text",
|
|
113
|
-
children: "active"
|
|
114
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
115
|
-
"data-testid": "context-total",
|
|
116
|
-
className: "text-sm font-semibold text-po-text",
|
|
117
|
-
children: tokens(total)
|
|
118
|
-
})]
|
|
142
|
+
className: "shrink-0 space-y-2 border-t border-po-border px-4 py-3",
|
|
143
|
+
children: [systemPrompt ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
|
|
144
|
+
variant: "ghost",
|
|
145
|
+
size: "card",
|
|
146
|
+
"data-testid": "context-system-prompt",
|
|
147
|
+
title: "Read the system prompt this session is running under",
|
|
148
|
+
onClick: () => onSelect({
|
|
149
|
+
itemKind: "prompt",
|
|
150
|
+
name: "system",
|
|
151
|
+
owner: "session"
|
|
119
152
|
}),
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
]
|
|
136
|
-
}),
|
|
137
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ContextItemDialog.ContextItemDialog, {
|
|
138
|
-
sessionId,
|
|
139
|
-
target,
|
|
140
|
-
warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
|
|
141
|
-
onClose: () => setTarget(null)
|
|
153
|
+
className: "flex-row items-center justify-between gap-2 px-0 py-1",
|
|
154
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
155
|
+
className: "text-sm text-po-text",
|
|
156
|
+
children: systemPrompt.stage === "base" ? "System prompt (base)" : "System prompt"
|
|
157
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
158
|
+
className: "shrink-0 text-sm text-po-text tabular-nums",
|
|
159
|
+
children: tokens(systemPrompt.tokens)
|
|
160
|
+
})]
|
|
161
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
162
|
+
className: "text-sm leading-relaxed text-po-dim",
|
|
163
|
+
children: [
|
|
164
|
+
"Token counts are estimates.",
|
|
165
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
|
|
166
|
+
`${estimator} BPE · Not a billed total`
|
|
167
|
+
]
|
|
168
|
+
})]
|
|
142
169
|
})
|
|
143
170
|
]
|
|
144
171
|
});
|
|
145
172
|
}
|
|
146
|
-
|
|
147
|
-
function ink(active) {
|
|
148
|
-
return active ? "text-po-hi" : "text-po-dim";
|
|
149
|
-
}
|
|
150
|
-
function ContextGroupView({ group, warnings, onSelect }) {
|
|
173
|
+
function ContextGroupView({ group, query, warnings, onSelect }) {
|
|
151
174
|
const groupActive = group.items.length === 0 || group.items.some((item) => item.active);
|
|
152
|
-
|
|
175
|
+
const groupMatches = group.label.toLowerCase().includes(query);
|
|
176
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
177
|
+
open: true,
|
|
153
178
|
"data-testid": `context-group-${group.id}`,
|
|
154
179
|
"data-kind": group.kind,
|
|
155
180
|
"data-active": groupActive,
|
|
156
|
-
className: "
|
|
157
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
158
|
-
className: "flex items-center gap-2 px-
|
|
181
|
+
className: "group/mode border-b border-po-border-soft last:border-b-0",
|
|
182
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
183
|
+
className: "flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
|
|
159
184
|
children: [
|
|
160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
185
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.ChevronRightIcon, {
|
|
161
186
|
"aria-hidden": true,
|
|
162
|
-
className: "
|
|
163
|
-
children: "#"
|
|
187
|
+
className: "size-3 shrink-0 text-po-dim group-open/mode:rotate-90"
|
|
164
188
|
}),
|
|
165
189
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
166
|
-
className: `min-w-0 flex-1 truncate text-
|
|
190
|
+
className: `min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? "text-po-hi" : "text-po-dim"}`,
|
|
167
191
|
children: group.label
|
|
168
192
|
}),
|
|
169
193
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
170
|
-
className: "
|
|
194
|
+
className: "rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
|
|
171
195
|
children: group.kind
|
|
172
196
|
}),
|
|
173
197
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
174
198
|
"data-testid": `context-subtotal-${group.id}`,
|
|
175
|
-
className: "w-
|
|
199
|
+
className: "w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums",
|
|
176
200
|
children: tokens(group.tokens)
|
|
177
201
|
})
|
|
178
202
|
]
|
|
179
203
|
}), group.items.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
180
204
|
"data-testid": `context-pending-${group.id}`,
|
|
181
|
-
className: "px-
|
|
182
|
-
children: group.detail || "
|
|
205
|
+
className: "px-3 pb-3 text-sm text-po-dim",
|
|
206
|
+
children: group.detail || "No tools or skills reported"
|
|
183
207
|
}) : require_contextComposition.ownersOf(group).map((owner) => {
|
|
208
|
+
const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);
|
|
209
|
+
const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));
|
|
210
|
+
if (items.length === 0) return null;
|
|
184
211
|
const ownerActive = owner.items.some((item) => item.active);
|
|
185
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
213
|
+
open: query ? true : owner.items.length > 1,
|
|
214
|
+
className: "group/owner border-t border-po-border-soft",
|
|
215
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
188
216
|
"data-testid": `context-owner-${owner.owner}`,
|
|
189
217
|
"data-active": ownerActive,
|
|
190
|
-
title: owner.owner
|
|
191
|
-
className: "flex items-center gap-2 px-
|
|
218
|
+
title: `${owner.owner} (${owner.source})`,
|
|
219
|
+
className: "flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
|
|
192
220
|
children: [
|
|
221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.ChevronRightIcon, {
|
|
222
|
+
"aria-hidden": true,
|
|
223
|
+
className: "size-3 shrink-0 text-po-dim group-open/owner:rotate-90"
|
|
224
|
+
}),
|
|
193
225
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
194
|
-
className: `min-w-0 flex-1 truncate text-base font-semibold text-po-hi`,
|
|
226
|
+
className: `min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? "text-po-hi" : "text-po-dim"}`,
|
|
195
227
|
children: require_contextComposition.ownerLabel(owner.owner)
|
|
196
228
|
}),
|
|
197
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
229
|
+
owner.source !== "extension" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
198
230
|
className: "text-sm text-po-dim",
|
|
199
|
-
children:
|
|
200
|
-
}),
|
|
231
|
+
children: owner.source
|
|
232
|
+
}) : null,
|
|
201
233
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
202
|
-
className: "w-
|
|
234
|
+
className: "w-16 shrink-0 text-right text-sm text-po-dim tabular-nums",
|
|
203
235
|
children: tokens(owner.tokens)
|
|
204
236
|
})
|
|
205
237
|
]
|
|
206
|
-
}),
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("abbr", {
|
|
219
|
-
title: item.itemKind,
|
|
220
|
-
className: `w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? "text-po-violet" : "text-po-faint"}`,
|
|
221
|
-
children: item.itemKind === "skill" ? "S" : "T"
|
|
222
|
-
}),
|
|
223
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
224
|
-
className: `min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`,
|
|
225
|
-
children: item.name
|
|
238
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
239
|
+
className: "mb-2 ml-6 border-l border-po-border pl-2",
|
|
240
|
+
children: items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
|
|
241
|
+
variant: "ghost",
|
|
242
|
+
size: "card",
|
|
243
|
+
"data-testid": `context-row-${item.name}`,
|
|
244
|
+
"data-active": item.active,
|
|
245
|
+
title: item.name,
|
|
246
|
+
onClick: () => onSelect({
|
|
247
|
+
itemKind: item.itemKind,
|
|
248
|
+
name: item.name,
|
|
249
|
+
owner: item.owner
|
|
226
250
|
}),
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
"
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
251
|
+
className: "flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
254
|
+
className: "shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
|
|
255
|
+
children: item.itemKind
|
|
256
|
+
}),
|
|
257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
258
|
+
className: `min-w-0 flex-1 truncate text-base ${item.active ? "text-po-hi" : "text-po-dim"}`,
|
|
259
|
+
children: item.name
|
|
260
|
+
}),
|
|
261
|
+
item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("abbr", {
|
|
262
|
+
"data-testid": `context-warning-${item.name}`,
|
|
263
|
+
"aria-label": `${item.name} has warnings`,
|
|
264
|
+
title: "Warnings: open tool details for origins",
|
|
265
|
+
className: "shrink-0 text-sm font-semibold text-po-yellow no-underline",
|
|
266
|
+
children: "!"
|
|
267
|
+
}) : null,
|
|
268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
269
|
+
title: item.active ? void 0 : "Not sent to the model; costs nothing until switched on",
|
|
270
|
+
className: `w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? "text-po-text" : "text-po-dim"}`,
|
|
271
|
+
children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
|
|
272
|
+
})
|
|
273
|
+
]
|
|
274
|
+
}, `${item.itemKind}:${item.name}`))
|
|
275
|
+
})]
|
|
241
276
|
}, owner.owner);
|
|
242
277
|
})]
|
|
243
278
|
});
|
|
244
279
|
}
|
|
245
280
|
//#endregion
|
|
246
281
|
exports.ContextPanel = ContextPanel;
|
|
282
|
+
exports.ContextPanelView = ContextPanelView;
|
|
247
283
|
|
|
248
284
|
//# sourceMappingURL=ContextPanel.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextPanel.cjs","names":["useActiveSession","useActiveSessionMeta","useState","parseContextToolWarnings","CONTEXT_TOOL_WARNINGS_STATUS_KEY","projectedGroups","contextGroups","totalTokens","inactiveTotal","PluginSurface","HOST_SLOTS","EmptyState","Button","ContextItemDialog","ownersOf","ownerLabel"],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, EmptyState } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n type ContextItemSource,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\n/** The row a reader clicked, which is all the detail request is made of. */\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\n/** Short enough to sit in a fixed column without wrapping the tool's own name. */\nconst SOURCE_LABEL: Record<ContextItemSource, string> = {\n extension: 'ext',\n mcp: 'mcp',\n plugin: 'plug',\n core: 'core',\n};\n\n/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\n/**\n * What the session is carrying.\n *\n * Every tool and skill in here is spending context before the first message,\n * so the surface is built around the one question worth asking of it: what is\n * this costing, and what do I switch off to stop paying it. Modes head the\n * groups because a mode is what you switch; packages head the rows inside them\n * because a package is what you add or remove. It reads only; switching stays\n * with the composer chips that already own it.\n */\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n // The runtime's grouping wins once it arrives; the status line only has to\n // carry the surface until the session has reported its inventory.\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n const total = totalTokens(groups);\n const idle = inactiveTotal(groups);\n const visibleGroups = withoutMcpInventory(groups);\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto\">\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"no composition yet\"\n description=\"the session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : (\n <div className=\"flex flex-col\">\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}`}\n group={group}\n warnings={warnings}\n onSelect={setTarget}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0 border-t border-po-border px-4 py-3\">\n {/* Above the totals rather than inside them. The prompt carries every\n tool's prose and every skill's listing, which the group subtotals\n have already counted, so the two figures overlap and adding them\n would bill the same text twice. */}\n {context?.systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"read the system prompt this session is running under\"\n onClick={() => setTarget({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 rounded-none px-0 py-px\"\n >\n <span className=\"text-sm text-po-dim\">\n {context.systemPrompt.stage === 'base' ? 'system prompt (base)' : 'system prompt'}\n </span>\n <span className=\"text-sm text-po-dim\">{tokens(context.systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-po-text\">active</span>\n <span data-testid=\"context-total\" className=\"text-sm font-semibold text-po-text\">\n {tokens(total)}\n </span>\n </div>\n {idle > 0 ? (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-po-dim\">inactive</span>\n <span data-testid=\"context-inactive\" className=\"text-sm text-po-dim\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n <span className=\"text-sm leading-relaxed text-po-dim\">\n {`estimated · ${context?.estimator ?? 'gpt-tokenizer'} BPE · not a billed total`}\n </span>\n </div>\n\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </div>\n );\n}\n\n/** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */\nfunction ink(active: boolean): string {\n return active ? 'text-po-hi' : 'text-po-dim';\n}\n\nfunction ContextGroupView({\n group,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n // A group that reported rows is active when any of them is; one still pending keeps the normal head.\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n return (\n <div\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"flex flex-col gap-1 border-b border-po-border-soft px-3 py-3\"\n >\n {/* The same head the activity dock uses, for the same reason: the glyph\n says \"section\", so a mode never reads as one more row among the tools\n it brought with it. */}\n <div className=\"flex items-center gap-2 px-1\">\n <span aria-hidden className=\"text-sm font-semibold text-po-faint\">\n #\n </span>\n <span className={`min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`}>{group.label}</span>\n <span className=\"inline-block text-sm text-po-dim first-letter:uppercase\">{group.kind}</span>\n <span data-testid={`context-subtotal-${group.id}`} className=\"w-14 text-right text-sm text-po-dim\">\n {tokens(group.tokens)}\n </span>\n </div>\n\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-1 text-sm text-po-dim\">\n {group.detail || 'no tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <div key={owner.owner} className=\"flex flex-col\">\n {/* The package heads its own rows, so the source tag sits here\n rather than repeating on every tool that shares it. */}\n <div\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={owner.owner}\n className=\"flex items-center gap-2 px-1 pt-1\"\n >\n <span className={`min-w-0 flex-1 truncate text-base font-semibold text-po-hi`}>\n {ownerLabel(owner.owner)}\n </span>\n <span className=\"text-sm text-po-dim\">{SOURCE_LABEL[owner.source]}</span>\n <span className=\"w-14 text-right text-sm text-po-dim\">{tokens(owner.tokens)}</span>\n </div>\n {owner.items.map((item) => (\n // A row is a question as much as a figure: what am I paying for.\n // The answer is too large to have travelled with the panel, so\n // the row asks for it rather than carrying it.\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-none py-px pr-1 pl-4\"\n >\n <abbr\n title={item.itemKind}\n className={`w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? 'text-po-violet' : 'text-po-faint'}`}\n >\n {item.itemKind === 'skill' ? 'S' : 'T'}\n </abbr>\n <span className={`min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"warnings: open tool details for origins\"\n className=\"shrink-0 text-xs font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'not sent to the model; costs nothing until switched on'}\n className={`w-14 text-right text-sm ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n );\n })\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContextPanel.cjs","names":["useActiveSession","useActiveSessionMeta","useState","parseContextToolWarnings","CONTEXT_TOOL_WARNINGS_STATUS_KEY","projectedGroups","contextGroups","totalTokens","inactiveTotal","PluginSurface","HOST_SLOTS","ContextItemDialog","SearchIcon","Input","EmptyState","Button","ChevronRightIcon","ownersOf","ownerLabel"],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from '@agimon-ai/po-web-components';\nimport { type ReactNode, useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n return (\n <>\n <ContextPanelView\n key={sessionId}\n groups={withoutMcpInventory(groups)}\n total={totalTokens(groups)}\n idle={inactiveTotal(groups)}\n systemPrompt={context?.systemPrompt}\n estimator={context?.estimator}\n warnings={warnings}\n onSelect={setTarget}\n >\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n </ContextPanelView>\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </>\n );\n}\n\n/** Presentation shared with the deterministic design stories. Totals never depend on the search. */\nexport function ContextPanelView({\n groups,\n total,\n idle,\n systemPrompt,\n estimator = 'gpt-tokenizer',\n warnings = {},\n onSelect,\n children,\n}: {\n groups: readonly ContextGroup[];\n total: number | null;\n idle: number;\n systemPrompt?: { stage: string; tokens: number } | null;\n estimator?: string;\n warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n children?: ReactNode;\n}) {\n const [search, setSearch] = useState('');\n const query = search.trim().toLowerCase();\n const matches = (value: string) => value.toLowerCase().includes(query);\n const visibleGroups = groups.filter(\n (group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)),\n );\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div className=\"shrink-0 space-y-3 p-3\">\n <div className=\"flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-po-dim\">Active context</div>\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span data-testid=\"context-total\" className=\"text-3xl font-semibold text-po-hi tabular-nums\">\n {tokens(total)}\n </span>\n <span className=\"text-sm text-po-dim\">tokens</span>\n </div>\n </div>\n {idle > 0 ? (\n <div className=\"shrink-0 border-l border-po-border pl-3\">\n <div className=\"text-sm text-po-dim\">Inactive</div>\n <span data-testid=\"context-inactive\" className=\"text-lg font-medium text-po-dim tabular-nums\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"relative\">\n <SearchIcon aria-hidden className=\"pointer-events-none absolute top-2 left-3 size-4 text-po-dim\" />\n <Input\n type=\"search\"\n aria-label=\"Find in context\"\n placeholder=\"Find in context\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n className=\"w-full pl-9 placeholder:text-po-dim\"\n />\n </div>\n </div>\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto px-3 pb-3\">\n {children}\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"No composition yet\"\n description=\"The session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : visibleGroups.length === 0 ? (\n <EmptyState title=\"No matches\" description=\"Try a mode, package, tool, or skill name.\" />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-po-border-soft\">\n <div className=\"flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim\">\n <span>Loaded context</span>\n <span>Est. tokens</span>\n </div>\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}:${query ? 'search' : 'browse'}`}\n group={group}\n query={query}\n warnings={warnings}\n onSelect={onSelect}\n />\n ))}\n </div>\n )}\n </div>\n <div className=\"shrink-0 space-y-2 border-t border-po-border px-4 py-3\">\n {/* Prompt tokens overlap the group subtotals. Do not add them to the active total. */}\n {systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"Read the system prompt this session is running under\"\n onClick={() => onSelect({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 px-0 py-1\"\n >\n <span className=\"text-sm text-po-text\">\n {systemPrompt.stage === 'base' ? 'System prompt (base)' : 'System prompt'}\n </span>\n <span className=\"shrink-0 text-sm text-po-text tabular-nums\">{tokens(systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <p className=\"text-sm leading-relaxed text-po-dim\">\n Token counts are estimates.\n <br />\n {`${estimator} BPE · Not a billed total`}\n </p>\n </div>\n </div>\n );\n}\n\nfunction ContextGroupView({\n group,\n query,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n query: string;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n const groupMatches = group.label.toLowerCase().includes(query);\n return (\n <details\n open\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"group/mode border-b border-po-border-soft last:border-b-0\"\n >\n <summary className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\">\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/mode:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {group.label}\n </span>\n <span className=\"rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {group.kind}\n </span>\n <span\n data-testid={`context-subtotal-${group.id}`}\n className=\"w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums\"\n >\n {tokens(group.tokens)}\n </span>\n </summary>\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-3 pb-3 text-sm text-po-dim\">\n {group.detail || 'No tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);\n const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));\n if (items.length === 0) return null;\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <details\n key={owner.owner}\n open={query ? true : owner.items.length > 1}\n className=\"group/owner border-t border-po-border-soft\"\n >\n <summary\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={`${owner.owner} (${owner.source})`}\n className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\"\n >\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/owner:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {ownerLabel(owner.owner)}\n </span>\n {owner.source !== 'extension' ? <span className=\"text-sm text-po-dim\">{owner.source}</span> : null}\n <span className=\"w-16 shrink-0 text-right text-sm text-po-dim tabular-nums\">\n {tokens(owner.tokens)}\n </span>\n </summary>\n <div className=\"mb-2 ml-6 border-l border-po-border pl-2\">\n {items.map((item) => (\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n title={item.name}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2\"\n >\n <span className=\"shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {item.itemKind}\n </span>\n <span className={`min-w-0 flex-1 truncate text-base ${item.active ? 'text-po-hi' : 'text-po-dim'}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"Warnings: open tool details for origins\"\n className=\"shrink-0 text-sm font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'Not sent to the model; costs nothing until switched on'}\n className={`w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n </details>\n );\n })\n )}\n </details>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
|
+
import { ContextGroup } from "../../lib/contextComposition.mjs";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { ContextItemKind, ContextToolWarning } from "@agimon-ai/po-core/contextApi";
|
|
1
4
|
//#region src/web/features/activity/ContextPanel.d.ts
|
|
5
|
+
interface ItemTarget {
|
|
6
|
+
itemKind: ContextItemKind;
|
|
7
|
+
name: string;
|
|
8
|
+
owner: string;
|
|
9
|
+
}
|
|
2
10
|
export declare function ContextPanel(): import("react").JSX.Element;
|
|
11
|
+
/** Presentation shared with the deterministic design stories. Totals never depend on the search. */
|
|
12
|
+
export declare function ContextPanelView({ groups, total, idle, systemPrompt, estimator, warnings, onSelect, children }: {
|
|
13
|
+
groups: readonly ContextGroup[];
|
|
14
|
+
total: number | null;
|
|
15
|
+
idle: number;
|
|
16
|
+
systemPrompt?: {
|
|
17
|
+
stage: string;
|
|
18
|
+
tokens: number;
|
|
19
|
+
} | null;
|
|
20
|
+
estimator?: string;
|
|
21
|
+
warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;
|
|
22
|
+
onSelect: (target: ItemTarget) => void;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
3
25
|
//#endregion
|
|
26
|
+
export {};
|
|
4
27
|
//# sourceMappingURL=ContextPanel.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextPanel.d.mts","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"mappings":";
|
|
1
|
+
{"version":3,"file":"ContextPanel.d.mts","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"mappings":";;;;UAwBU;EACR,UAAU;EACV;EACA;;wBAqBc,gCAAY,IAAA;;wBAyCZ,mBACd,QACA,OACA,MACA,cACA,WACA,UACA,UACA;EAEA,iBAAiB;EACjB;EACA;EACA;IAAiB;IAAe;;EAChC;EACA,WAAW,SAAS,wBAAwB;EAC5C,WAAW,QAAQ;EACnB,WAAW;oBACZ,IAAA"}
|
|
@@ -6,30 +6,12 @@ import { PluginSurface } from "../../components/PluginSurface.mjs";
|
|
|
6
6
|
import { ContextItemDialog } from "./ContextItemDialog.mjs";
|
|
7
7
|
import { useState } from "react";
|
|
8
8
|
import { CONTEXT_TOOL_WARNINGS_STATUS_KEY, parseContextToolWarnings } from "@agimon-ai/po-core/contextApi";
|
|
9
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import { Button, EmptyState } from "@agimon-ai/po-web-components";
|
|
9
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from "@agimon-ai/po-web-components";
|
|
11
11
|
//#region src/web/features/activity/ContextPanel.tsx
|
|
12
|
-
/** Short enough to sit in a fixed column without wrapping the tool's own name. */
|
|
13
|
-
const SOURCE_LABEL = {
|
|
14
|
-
extension: "ext",
|
|
15
|
-
mcp: "mcp",
|
|
16
|
-
plugin: "plug",
|
|
17
|
-
core: "core"
|
|
18
|
-
};
|
|
19
|
-
/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */
|
|
20
12
|
function tokens(value) {
|
|
21
13
|
return value === null ? "-" : `~${value.toLocaleString()}`;
|
|
22
14
|
}
|
|
23
|
-
/**
|
|
24
|
-
* What the session is carrying.
|
|
25
|
-
*
|
|
26
|
-
* Every tool and skill in here is spending context before the first message,
|
|
27
|
-
* so the surface is built around the one question worth asking of it: what is
|
|
28
|
-
* this costing, and what do I switch off to stop paying it. Modes head the
|
|
29
|
-
* groups because a mode is what you switch; packages head the rows inside them
|
|
30
|
-
* because a package is what you add or remove. It reads only; switching stays
|
|
31
|
-
* with the composer chips that already own it.
|
|
32
|
-
*/
|
|
33
15
|
function withoutMcpInventory(groups) {
|
|
34
16
|
return groups.map((group) => {
|
|
35
17
|
const mcp = group.items.filter((item) => item.source === "mcp");
|
|
@@ -57,192 +39,245 @@ function ContextPanel() {
|
|
|
57
39
|
}
|
|
58
40
|
const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);
|
|
59
41
|
const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
42
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ContextPanelView, {
|
|
43
|
+
groups: withoutMcpInventory(groups),
|
|
44
|
+
total: totalTokens(groups),
|
|
45
|
+
idle: inactiveTotal(groups),
|
|
46
|
+
systemPrompt: context?.systemPrompt,
|
|
47
|
+
estimator: context?.estimator,
|
|
48
|
+
warnings,
|
|
49
|
+
onSelect: setTarget,
|
|
50
|
+
children: /* @__PURE__ */ jsx(PluginSurface, {
|
|
51
|
+
slot: HOST_SLOTS.context,
|
|
52
|
+
sessionId
|
|
53
|
+
})
|
|
54
|
+
}, sessionId), /* @__PURE__ */ jsx(ContextItemDialog, {
|
|
55
|
+
sessionId,
|
|
56
|
+
target,
|
|
57
|
+
warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
|
|
58
|
+
onClose: () => setTarget(null)
|
|
59
|
+
})] });
|
|
60
|
+
}
|
|
61
|
+
/** Presentation shared with the deterministic design stories. Totals never depend on the search. */
|
|
62
|
+
function ContextPanelView({ groups, total, idle, systemPrompt, estimator = "gpt-tokenizer", warnings = {}, onSelect, children }) {
|
|
63
|
+
const [search, setSearch] = useState("");
|
|
64
|
+
const query = search.trim().toLowerCase();
|
|
65
|
+
const matches = (value) => value.toLowerCase().includes(query);
|
|
66
|
+
const visibleGroups = groups.filter((group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)));
|
|
63
67
|
return /* @__PURE__ */ jsxs("div", {
|
|
64
68
|
"data-testid": "context-panel",
|
|
65
69
|
className: "flex min-h-0 min-w-0 flex-1 flex-col",
|
|
66
70
|
children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: "shrink-0 space-y-3 p-3",
|
|
73
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: "flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3",
|
|
75
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
76
|
+
className: "min-w-0 flex-1",
|
|
77
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
78
|
+
className: "text-sm text-po-dim",
|
|
79
|
+
children: "Active context"
|
|
80
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
81
|
+
className: "flex flex-wrap items-baseline gap-2",
|
|
82
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
83
|
+
"data-testid": "context-total",
|
|
84
|
+
className: "text-3xl font-semibold text-po-hi tabular-nums",
|
|
85
|
+
children: tokens(total)
|
|
86
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
87
|
+
className: "text-sm text-po-dim",
|
|
88
|
+
children: "tokens"
|
|
89
|
+
})]
|
|
90
|
+
})]
|
|
91
|
+
}), idle > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
92
|
+
className: "shrink-0 border-l border-po-border pl-3",
|
|
93
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
94
|
+
className: "text-sm text-po-dim",
|
|
95
|
+
children: "Inactive"
|
|
96
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
97
|
+
"data-testid": "context-inactive",
|
|
98
|
+
className: "text-lg font-medium text-po-dim tabular-nums",
|
|
99
|
+
children: `+${tokens(idle)}`
|
|
100
|
+
})]
|
|
101
|
+
}) : null]
|
|
102
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
103
|
+
className: "relative",
|
|
104
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, {
|
|
105
|
+
"aria-hidden": true,
|
|
106
|
+
className: "pointer-events-none absolute top-2 left-3 size-4 text-po-dim"
|
|
107
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
108
|
+
type: "search",
|
|
109
|
+
"aria-label": "Find in context",
|
|
110
|
+
placeholder: "Find in context",
|
|
111
|
+
value: search,
|
|
112
|
+
onChange: (event) => setSearch(event.target.value),
|
|
113
|
+
className: "w-full pl-9 placeholder:text-po-dim"
|
|
114
|
+
})]
|
|
115
|
+
})]
|
|
116
|
+
}),
|
|
67
117
|
/* @__PURE__ */ jsxs("div", {
|
|
68
118
|
"data-testid": "context-scroll",
|
|
69
|
-
className: "min-h-0 flex-1 overflow-y-auto",
|
|
70
|
-
children: [/* @__PURE__ */ jsx(
|
|
71
|
-
slot: HOST_SLOTS.context,
|
|
72
|
-
sessionId
|
|
73
|
-
}), groups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
119
|
+
className: "min-h-0 flex-1 overflow-y-auto px-3 pb-3",
|
|
120
|
+
children: [children, groups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
74
121
|
"data-testid": "context-empty",
|
|
75
122
|
className: "px-4 py-5",
|
|
76
|
-
title: "
|
|
77
|
-
description: "
|
|
78
|
-
}) : /* @__PURE__ */ jsx(
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
title: "No composition yet",
|
|
124
|
+
description: "The session reports its major mode, minor modes, and domains once it has started."
|
|
125
|
+
}) : visibleGroups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
126
|
+
title: "No matches",
|
|
127
|
+
description: "Try a mode, package, tool, or skill name."
|
|
128
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
129
|
+
className: "overflow-hidden rounded-lg border border-po-border-soft",
|
|
130
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
131
|
+
className: "flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim",
|
|
132
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Loaded context" }), /* @__PURE__ */ jsx("span", { children: "Est. tokens" })]
|
|
133
|
+
}), visibleGroups.map((group) => /* @__PURE__ */ jsx(ContextGroupView, {
|
|
81
134
|
group,
|
|
135
|
+
query,
|
|
82
136
|
warnings,
|
|
83
|
-
onSelect
|
|
84
|
-
}, `${group.kind}:${group.id}`))
|
|
137
|
+
onSelect
|
|
138
|
+
}, `${group.kind}:${group.id}:${query ? "search" : "browse"}`))]
|
|
85
139
|
})]
|
|
86
140
|
}),
|
|
87
141
|
/* @__PURE__ */ jsxs("div", {
|
|
88
|
-
className: "shrink-0 border-t border-po-border px-4 py-3",
|
|
89
|
-
children: [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
owner: "session"
|
|
99
|
-
}),
|
|
100
|
-
className: "flex-row items-center justify-between gap-2 rounded-none px-0 py-px",
|
|
101
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
102
|
-
className: "text-sm text-po-dim",
|
|
103
|
-
children: context.systemPrompt.stage === "base" ? "system prompt (base)" : "system prompt"
|
|
104
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
105
|
-
className: "text-sm text-po-dim",
|
|
106
|
-
children: tokens(context.systemPrompt.tokens)
|
|
107
|
-
})]
|
|
108
|
-
}) : null,
|
|
109
|
-
/* @__PURE__ */ jsxs("div", {
|
|
110
|
-
className: "flex items-center justify-between",
|
|
111
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
112
|
-
className: "text-sm font-semibold text-po-text",
|
|
113
|
-
children: "active"
|
|
114
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
115
|
-
"data-testid": "context-total",
|
|
116
|
-
className: "text-sm font-semibold text-po-text",
|
|
117
|
-
children: tokens(total)
|
|
118
|
-
})]
|
|
142
|
+
className: "shrink-0 space-y-2 border-t border-po-border px-4 py-3",
|
|
143
|
+
children: [systemPrompt ? /* @__PURE__ */ jsxs(Button, {
|
|
144
|
+
variant: "ghost",
|
|
145
|
+
size: "card",
|
|
146
|
+
"data-testid": "context-system-prompt",
|
|
147
|
+
title: "Read the system prompt this session is running under",
|
|
148
|
+
onClick: () => onSelect({
|
|
149
|
+
itemKind: "prompt",
|
|
150
|
+
name: "system",
|
|
151
|
+
owner: "session"
|
|
119
152
|
}),
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
]
|
|
136
|
-
}),
|
|
137
|
-
/* @__PURE__ */ jsx(ContextItemDialog, {
|
|
138
|
-
sessionId,
|
|
139
|
-
target,
|
|
140
|
-
warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
|
|
141
|
-
onClose: () => setTarget(null)
|
|
153
|
+
className: "flex-row items-center justify-between gap-2 px-0 py-1",
|
|
154
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
155
|
+
className: "text-sm text-po-text",
|
|
156
|
+
children: systemPrompt.stage === "base" ? "System prompt (base)" : "System prompt"
|
|
157
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
158
|
+
className: "shrink-0 text-sm text-po-text tabular-nums",
|
|
159
|
+
children: tokens(systemPrompt.tokens)
|
|
160
|
+
})]
|
|
161
|
+
}) : null, /* @__PURE__ */ jsxs("p", {
|
|
162
|
+
className: "text-sm leading-relaxed text-po-dim",
|
|
163
|
+
children: [
|
|
164
|
+
"Token counts are estimates.",
|
|
165
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
166
|
+
`${estimator} BPE · Not a billed total`
|
|
167
|
+
]
|
|
168
|
+
})]
|
|
142
169
|
})
|
|
143
170
|
]
|
|
144
171
|
});
|
|
145
172
|
}
|
|
146
|
-
|
|
147
|
-
function ink(active) {
|
|
148
|
-
return active ? "text-po-hi" : "text-po-dim";
|
|
149
|
-
}
|
|
150
|
-
function ContextGroupView({ group, warnings, onSelect }) {
|
|
173
|
+
function ContextGroupView({ group, query, warnings, onSelect }) {
|
|
151
174
|
const groupActive = group.items.length === 0 || group.items.some((item) => item.active);
|
|
152
|
-
|
|
175
|
+
const groupMatches = group.label.toLowerCase().includes(query);
|
|
176
|
+
return /* @__PURE__ */ jsxs("details", {
|
|
177
|
+
open: true,
|
|
153
178
|
"data-testid": `context-group-${group.id}`,
|
|
154
179
|
"data-kind": group.kind,
|
|
155
180
|
"data-active": groupActive,
|
|
156
|
-
className: "
|
|
157
|
-
children: [/* @__PURE__ */ jsxs("
|
|
158
|
-
className: "flex items-center gap-2 px-
|
|
181
|
+
className: "group/mode border-b border-po-border-soft last:border-b-0",
|
|
182
|
+
children: [/* @__PURE__ */ jsxs("summary", {
|
|
183
|
+
className: "flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
|
|
159
184
|
children: [
|
|
160
|
-
/* @__PURE__ */ jsx(
|
|
185
|
+
/* @__PURE__ */ jsx(ChevronRightIcon, {
|
|
161
186
|
"aria-hidden": true,
|
|
162
|
-
className: "
|
|
163
|
-
children: "#"
|
|
187
|
+
className: "size-3 shrink-0 text-po-dim group-open/mode:rotate-90"
|
|
164
188
|
}),
|
|
165
189
|
/* @__PURE__ */ jsx("span", {
|
|
166
|
-
className: `min-w-0 flex-1 truncate text-
|
|
190
|
+
className: `min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? "text-po-hi" : "text-po-dim"}`,
|
|
167
191
|
children: group.label
|
|
168
192
|
}),
|
|
169
193
|
/* @__PURE__ */ jsx("span", {
|
|
170
|
-
className: "
|
|
194
|
+
className: "rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
|
|
171
195
|
children: group.kind
|
|
172
196
|
}),
|
|
173
197
|
/* @__PURE__ */ jsx("span", {
|
|
174
198
|
"data-testid": `context-subtotal-${group.id}`,
|
|
175
|
-
className: "w-
|
|
199
|
+
className: "w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums",
|
|
176
200
|
children: tokens(group.tokens)
|
|
177
201
|
})
|
|
178
202
|
]
|
|
179
203
|
}), group.items.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
180
204
|
"data-testid": `context-pending-${group.id}`,
|
|
181
|
-
className: "px-
|
|
182
|
-
children: group.detail || "
|
|
205
|
+
className: "px-3 pb-3 text-sm text-po-dim",
|
|
206
|
+
children: group.detail || "No tools or skills reported"
|
|
183
207
|
}) : ownersOf(group).map((owner) => {
|
|
208
|
+
const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);
|
|
209
|
+
const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));
|
|
210
|
+
if (items.length === 0) return null;
|
|
184
211
|
const ownerActive = owner.items.some((item) => item.active);
|
|
185
|
-
return /* @__PURE__ */ jsxs("
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
return /* @__PURE__ */ jsxs("details", {
|
|
213
|
+
open: query ? true : owner.items.length > 1,
|
|
214
|
+
className: "group/owner border-t border-po-border-soft",
|
|
215
|
+
children: [/* @__PURE__ */ jsxs("summary", {
|
|
188
216
|
"data-testid": `context-owner-${owner.owner}`,
|
|
189
217
|
"data-active": ownerActive,
|
|
190
|
-
title: owner.owner
|
|
191
|
-
className: "flex items-center gap-2 px-
|
|
218
|
+
title: `${owner.owner} (${owner.source})`,
|
|
219
|
+
className: "flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
|
|
192
220
|
children: [
|
|
221
|
+
/* @__PURE__ */ jsx(ChevronRightIcon, {
|
|
222
|
+
"aria-hidden": true,
|
|
223
|
+
className: "size-3 shrink-0 text-po-dim group-open/owner:rotate-90"
|
|
224
|
+
}),
|
|
193
225
|
/* @__PURE__ */ jsx("span", {
|
|
194
|
-
className: `min-w-0 flex-1 truncate text-base font-semibold text-po-hi`,
|
|
226
|
+
className: `min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? "text-po-hi" : "text-po-dim"}`,
|
|
195
227
|
children: ownerLabel(owner.owner)
|
|
196
228
|
}),
|
|
197
|
-
/* @__PURE__ */ jsx("span", {
|
|
229
|
+
owner.source !== "extension" ? /* @__PURE__ */ jsx("span", {
|
|
198
230
|
className: "text-sm text-po-dim",
|
|
199
|
-
children:
|
|
200
|
-
}),
|
|
231
|
+
children: owner.source
|
|
232
|
+
}) : null,
|
|
201
233
|
/* @__PURE__ */ jsx("span", {
|
|
202
|
-
className: "w-
|
|
234
|
+
className: "w-16 shrink-0 text-right text-sm text-po-dim tabular-nums",
|
|
203
235
|
children: tokens(owner.tokens)
|
|
204
236
|
})
|
|
205
237
|
]
|
|
206
|
-
}),
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
/* @__PURE__ */ jsx("abbr", {
|
|
219
|
-
title: item.itemKind,
|
|
220
|
-
className: `w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? "text-po-violet" : "text-po-faint"}`,
|
|
221
|
-
children: item.itemKind === "skill" ? "S" : "T"
|
|
222
|
-
}),
|
|
223
|
-
/* @__PURE__ */ jsx("span", {
|
|
224
|
-
className: `min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`,
|
|
225
|
-
children: item.name
|
|
238
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
239
|
+
className: "mb-2 ml-6 border-l border-po-border pl-2",
|
|
240
|
+
children: items.map((item) => /* @__PURE__ */ jsxs(Button, {
|
|
241
|
+
variant: "ghost",
|
|
242
|
+
size: "card",
|
|
243
|
+
"data-testid": `context-row-${item.name}`,
|
|
244
|
+
"data-active": item.active,
|
|
245
|
+
title: item.name,
|
|
246
|
+
onClick: () => onSelect({
|
|
247
|
+
itemKind: item.itemKind,
|
|
248
|
+
name: item.name,
|
|
249
|
+
owner: item.owner
|
|
226
250
|
}),
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
"
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
251
|
+
className: "flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsx("span", {
|
|
254
|
+
className: "shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
|
|
255
|
+
children: item.itemKind
|
|
256
|
+
}),
|
|
257
|
+
/* @__PURE__ */ jsx("span", {
|
|
258
|
+
className: `min-w-0 flex-1 truncate text-base ${item.active ? "text-po-hi" : "text-po-dim"}`,
|
|
259
|
+
children: item.name
|
|
260
|
+
}),
|
|
261
|
+
item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ jsx("abbr", {
|
|
262
|
+
"data-testid": `context-warning-${item.name}`,
|
|
263
|
+
"aria-label": `${item.name} has warnings`,
|
|
264
|
+
title: "Warnings: open tool details for origins",
|
|
265
|
+
className: "shrink-0 text-sm font-semibold text-po-yellow no-underline",
|
|
266
|
+
children: "!"
|
|
267
|
+
}) : null,
|
|
268
|
+
/* @__PURE__ */ jsx("span", {
|
|
269
|
+
title: item.active ? void 0 : "Not sent to the model; costs nothing until switched on",
|
|
270
|
+
className: `w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? "text-po-text" : "text-po-dim"}`,
|
|
271
|
+
children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
|
|
272
|
+
})
|
|
273
|
+
]
|
|
274
|
+
}, `${item.itemKind}:${item.name}`))
|
|
275
|
+
})]
|
|
241
276
|
}, owner.owner);
|
|
242
277
|
})]
|
|
243
278
|
});
|
|
244
279
|
}
|
|
245
280
|
//#endregion
|
|
246
|
-
export { ContextPanel };
|
|
281
|
+
export { ContextPanel, ContextPanelView };
|
|
247
282
|
|
|
248
283
|
//# sourceMappingURL=ContextPanel.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextPanel.mjs","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, EmptyState } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n type ContextItemSource,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\n/** The row a reader clicked, which is all the detail request is made of. */\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\n/** Short enough to sit in a fixed column without wrapping the tool's own name. */\nconst SOURCE_LABEL: Record<ContextItemSource, string> = {\n extension: 'ext',\n mcp: 'mcp',\n plugin: 'plug',\n core: 'core',\n};\n\n/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\n/**\n * What the session is carrying.\n *\n * Every tool and skill in here is spending context before the first message,\n * so the surface is built around the one question worth asking of it: what is\n * this costing, and what do I switch off to stop paying it. Modes head the\n * groups because a mode is what you switch; packages head the rows inside them\n * because a package is what you add or remove. It reads only; switching stays\n * with the composer chips that already own it.\n */\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n // The runtime's grouping wins once it arrives; the status line only has to\n // carry the surface until the session has reported its inventory.\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n const total = totalTokens(groups);\n const idle = inactiveTotal(groups);\n const visibleGroups = withoutMcpInventory(groups);\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto\">\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"no composition yet\"\n description=\"the session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : (\n <div className=\"flex flex-col\">\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}`}\n group={group}\n warnings={warnings}\n onSelect={setTarget}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0 border-t border-po-border px-4 py-3\">\n {/* Above the totals rather than inside them. The prompt carries every\n tool's prose and every skill's listing, which the group subtotals\n have already counted, so the two figures overlap and adding them\n would bill the same text twice. */}\n {context?.systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"read the system prompt this session is running under\"\n onClick={() => setTarget({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 rounded-none px-0 py-px\"\n >\n <span className=\"text-sm text-po-dim\">\n {context.systemPrompt.stage === 'base' ? 'system prompt (base)' : 'system prompt'}\n </span>\n <span className=\"text-sm text-po-dim\">{tokens(context.systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-po-text\">active</span>\n <span data-testid=\"context-total\" className=\"text-sm font-semibold text-po-text\">\n {tokens(total)}\n </span>\n </div>\n {idle > 0 ? (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-po-dim\">inactive</span>\n <span data-testid=\"context-inactive\" className=\"text-sm text-po-dim\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n <span className=\"text-sm leading-relaxed text-po-dim\">\n {`estimated · ${context?.estimator ?? 'gpt-tokenizer'} BPE · not a billed total`}\n </span>\n </div>\n\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </div>\n );\n}\n\n/** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */\nfunction ink(active: boolean): string {\n return active ? 'text-po-hi' : 'text-po-dim';\n}\n\nfunction ContextGroupView({\n group,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n // A group that reported rows is active when any of them is; one still pending keeps the normal head.\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n return (\n <div\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"flex flex-col gap-1 border-b border-po-border-soft px-3 py-3\"\n >\n {/* The same head the activity dock uses, for the same reason: the glyph\n says \"section\", so a mode never reads as one more row among the tools\n it brought with it. */}\n <div className=\"flex items-center gap-2 px-1\">\n <span aria-hidden className=\"text-sm font-semibold text-po-faint\">\n #\n </span>\n <span className={`min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`}>{group.label}</span>\n <span className=\"inline-block text-sm text-po-dim first-letter:uppercase\">{group.kind}</span>\n <span data-testid={`context-subtotal-${group.id}`} className=\"w-14 text-right text-sm text-po-dim\">\n {tokens(group.tokens)}\n </span>\n </div>\n\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-1 text-sm text-po-dim\">\n {group.detail || 'no tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <div key={owner.owner} className=\"flex flex-col\">\n {/* The package heads its own rows, so the source tag sits here\n rather than repeating on every tool that shares it. */}\n <div\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={owner.owner}\n className=\"flex items-center gap-2 px-1 pt-1\"\n >\n <span className={`min-w-0 flex-1 truncate text-base font-semibold text-po-hi`}>\n {ownerLabel(owner.owner)}\n </span>\n <span className=\"text-sm text-po-dim\">{SOURCE_LABEL[owner.source]}</span>\n <span className=\"w-14 text-right text-sm text-po-dim\">{tokens(owner.tokens)}</span>\n </div>\n {owner.items.map((item) => (\n // A row is a question as much as a figure: what am I paying for.\n // The answer is too large to have travelled with the panel, so\n // the row asks for it rather than carrying it.\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-none py-px pr-1 pl-4\"\n >\n <abbr\n title={item.itemKind}\n className={`w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? 'text-po-violet' : 'text-po-faint'}`}\n >\n {item.itemKind === 'skill' ? 'S' : 'T'}\n </abbr>\n <span className={`min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"warnings: open tool details for origins\"\n className=\"shrink-0 text-xs font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'not sent to the model; costs nothing until switched on'}\n className={`w-14 text-right text-sm ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n );\n })\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContextPanel.mjs","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from '@agimon-ai/po-web-components';\nimport { type ReactNode, useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n return (\n <>\n <ContextPanelView\n key={sessionId}\n groups={withoutMcpInventory(groups)}\n total={totalTokens(groups)}\n idle={inactiveTotal(groups)}\n systemPrompt={context?.systemPrompt}\n estimator={context?.estimator}\n warnings={warnings}\n onSelect={setTarget}\n >\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n </ContextPanelView>\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </>\n );\n}\n\n/** Presentation shared with the deterministic design stories. Totals never depend on the search. */\nexport function ContextPanelView({\n groups,\n total,\n idle,\n systemPrompt,\n estimator = 'gpt-tokenizer',\n warnings = {},\n onSelect,\n children,\n}: {\n groups: readonly ContextGroup[];\n total: number | null;\n idle: number;\n systemPrompt?: { stage: string; tokens: number } | null;\n estimator?: string;\n warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n children?: ReactNode;\n}) {\n const [search, setSearch] = useState('');\n const query = search.trim().toLowerCase();\n const matches = (value: string) => value.toLowerCase().includes(query);\n const visibleGroups = groups.filter(\n (group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)),\n );\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div className=\"shrink-0 space-y-3 p-3\">\n <div className=\"flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-po-dim\">Active context</div>\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span data-testid=\"context-total\" className=\"text-3xl font-semibold text-po-hi tabular-nums\">\n {tokens(total)}\n </span>\n <span className=\"text-sm text-po-dim\">tokens</span>\n </div>\n </div>\n {idle > 0 ? (\n <div className=\"shrink-0 border-l border-po-border pl-3\">\n <div className=\"text-sm text-po-dim\">Inactive</div>\n <span data-testid=\"context-inactive\" className=\"text-lg font-medium text-po-dim tabular-nums\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"relative\">\n <SearchIcon aria-hidden className=\"pointer-events-none absolute top-2 left-3 size-4 text-po-dim\" />\n <Input\n type=\"search\"\n aria-label=\"Find in context\"\n placeholder=\"Find in context\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n className=\"w-full pl-9 placeholder:text-po-dim\"\n />\n </div>\n </div>\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto px-3 pb-3\">\n {children}\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"No composition yet\"\n description=\"The session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : visibleGroups.length === 0 ? (\n <EmptyState title=\"No matches\" description=\"Try a mode, package, tool, or skill name.\" />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-po-border-soft\">\n <div className=\"flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim\">\n <span>Loaded context</span>\n <span>Est. tokens</span>\n </div>\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}:${query ? 'search' : 'browse'}`}\n group={group}\n query={query}\n warnings={warnings}\n onSelect={onSelect}\n />\n ))}\n </div>\n )}\n </div>\n <div className=\"shrink-0 space-y-2 border-t border-po-border px-4 py-3\">\n {/* Prompt tokens overlap the group subtotals. Do not add them to the active total. */}\n {systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"Read the system prompt this session is running under\"\n onClick={() => onSelect({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 px-0 py-1\"\n >\n <span className=\"text-sm text-po-text\">\n {systemPrompt.stage === 'base' ? 'System prompt (base)' : 'System prompt'}\n </span>\n <span className=\"shrink-0 text-sm text-po-text tabular-nums\">{tokens(systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <p className=\"text-sm leading-relaxed text-po-dim\">\n Token counts are estimates.\n <br />\n {`${estimator} BPE · Not a billed total`}\n </p>\n </div>\n </div>\n );\n}\n\nfunction ContextGroupView({\n group,\n query,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n query: string;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n const groupMatches = group.label.toLowerCase().includes(query);\n return (\n <details\n open\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"group/mode border-b border-po-border-soft last:border-b-0\"\n >\n <summary className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\">\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/mode:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {group.label}\n </span>\n <span className=\"rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {group.kind}\n </span>\n <span\n data-testid={`context-subtotal-${group.id}`}\n className=\"w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums\"\n >\n {tokens(group.tokens)}\n </span>\n </summary>\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-3 pb-3 text-sm text-po-dim\">\n {group.detail || 'No tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);\n const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));\n if (items.length === 0) return null;\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <details\n key={owner.owner}\n open={query ? true : owner.items.length > 1}\n className=\"group/owner border-t border-po-border-soft\"\n >\n <summary\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={`${owner.owner} (${owner.source})`}\n className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\"\n >\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/owner:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {ownerLabel(owner.owner)}\n </span>\n {owner.source !== 'extension' ? <span className=\"text-sm text-po-dim\">{owner.source}</span> : null}\n <span className=\"w-16 shrink-0 text-right text-sm text-po-dim tabular-nums\">\n {tokens(owner.tokens)}\n </span>\n </summary>\n <div className=\"mb-2 ml-6 border-l border-po-border pl-2\">\n {items.map((item) => (\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n title={item.name}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2\"\n >\n <span className=\"shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {item.itemKind}\n </span>\n <span className={`min-w-0 flex-1 truncate text-base ${item.active ? 'text-po-hi' : 'text-po-dim'}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"Warnings: open tool details for origins\"\n className=\"shrink-0 text-sm font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'Not sent to the model; costs nothing until switched on'}\n className={`w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n </details>\n );\n })\n )}\n </details>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-web-dashboard",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.6",
|
|
4
4
|
"description": "Shared browser state, actions and optional views for Po dashboard templates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
65
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
66
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
64
|
+
"@agimon-ai/po-core": "0.0.0-alpha.6",
|
|
65
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.6",
|
|
66
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.6",
|
|
67
67
|
"@earendil-works/chord": "1.2.0",
|
|
68
68
|
"@simplewebauthn/browser": "^14.0.0",
|
|
69
69
|
"@tanstack/react-router": "1.170.44",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"qrcode-generator": "^2.0.4"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.
|
|
76
|
+
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.6",
|
|
77
77
|
"@types/node": "26.6.5",
|
|
78
78
|
"@types/react": "19.3.0",
|
|
79
79
|
"@types/react-dom": "19.3.0",
|