@contractspec/example.agent-console 3.7.7 → 3.8.2
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/.turbo/turbo-build.log +126 -105
- package/AGENTS.md +3 -1
- package/CHANGELOG.md +29 -0
- package/README.md +46 -9
- package/dist/agent/agent.handler.d.ts +3 -0
- package/dist/agent/agent.handler.js +730 -1
- package/dist/agent/index.js +73 -72
- package/dist/agent.feature.js +179 -0
- package/dist/browser/agent/agent.handler.js +730 -1
- package/dist/browser/agent/index.js +73 -72
- package/dist/browser/agent.feature.js +179 -0
- package/dist/browser/docs/agent-console.docblock.js +11 -8
- package/dist/browser/docs/index.js +11 -8
- package/dist/browser/example.js +2 -3
- package/dist/browser/handlers/agent.handlers.js +1883 -2
- package/dist/browser/handlers/index.js +2142 -8
- package/dist/browser/index.js +3347 -2433
- package/dist/browser/presentations/index.js +49 -49
- package/dist/browser/run/index.js +818 -812
- package/dist/browser/run/run.handler.js +666 -1
- package/dist/browser/shared/index.js +293 -1
- package/dist/browser/shared/mock-runs.js +5 -0
- package/dist/browser/tool/index.js +331 -331
- package/dist/browser/tool/tool.handler.js +479 -3
- package/dist/browser/ui/AgentDashboard.js +1204 -319
- package/dist/browser/ui/AgentDashboard.visualizations.js +217 -0
- package/dist/browser/ui/AgentRunList.js +359 -127
- package/dist/browser/ui/hooks/index.js +468 -18
- package/dist/browser/ui/hooks/useAgentMutations.js +443 -8
- package/dist/browser/ui/hooks/useRunList.js +25 -10
- package/dist/browser/ui/index.js +1293 -390
- package/dist/browser/ui/renderers/agent-list.markdown.js +14 -5
- package/dist/browser/ui/renderers/dashboard.markdown.js +207 -36
- package/dist/browser/ui/renderers/index.js +245 -49
- package/dist/browser/ui/renderers/run-list.markdown.js +9 -4
- package/dist/browser/ui/renderers/tool-registry.markdown.js +15 -4
- package/dist/browser/ui/views/RunDataTable.js +326 -0
- package/dist/browser/ui/views/RunListView.js +359 -127
- package/dist/browser/ui/views/index.js +406 -174
- package/dist/browser/ui/views/run-data-table.columns.js +271 -0
- package/dist/browser/ui/views/run-list.shared.js +177 -0
- package/dist/browser/visualizations/catalog.js +134 -0
- package/dist/browser/visualizations/index.js +187 -0
- package/dist/browser/visualizations/selectors.js +181 -0
- package/dist/docs/agent-console.docblock.js +11 -8
- package/dist/docs/index.js +11 -8
- package/dist/example.js +2 -3
- package/dist/example.test.d.ts +1 -0
- package/dist/handlers/agent.handlers.d.ts +2 -0
- package/dist/handlers/agent.handlers.js +1883 -2
- package/dist/handlers/index.d.ts +1 -3
- package/dist/handlers/index.js +2142 -8
- package/dist/handlers/mock-handlers.test.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3347 -2433
- package/dist/node/agent/agent.handler.js +730 -1
- package/dist/node/agent/index.js +73 -72
- package/dist/node/agent.feature.js +179 -0
- package/dist/node/docs/agent-console.docblock.js +11 -8
- package/dist/node/docs/index.js +11 -8
- package/dist/node/example.js +2 -3
- package/dist/node/handlers/agent.handlers.js +1883 -2
- package/dist/node/handlers/index.js +2142 -8
- package/dist/node/index.js +3347 -2433
- package/dist/node/presentations/index.js +49 -49
- package/dist/node/run/index.js +818 -812
- package/dist/node/run/run.handler.js +666 -1
- package/dist/node/shared/index.js +293 -1
- package/dist/node/shared/mock-runs.js +5 -0
- package/dist/node/tool/index.js +331 -331
- package/dist/node/tool/tool.handler.js +479 -3
- package/dist/node/ui/AgentDashboard.js +1204 -319
- package/dist/node/ui/AgentDashboard.visualizations.js +217 -0
- package/dist/node/ui/AgentRunList.js +359 -127
- package/dist/node/ui/hooks/index.js +468 -18
- package/dist/node/ui/hooks/useAgentMutations.js +443 -8
- package/dist/node/ui/hooks/useRunList.js +25 -10
- package/dist/node/ui/index.js +1293 -390
- package/dist/node/ui/renderers/agent-list.markdown.js +14 -5
- package/dist/node/ui/renderers/dashboard.markdown.js +207 -36
- package/dist/node/ui/renderers/index.js +245 -49
- package/dist/node/ui/renderers/run-list.markdown.js +9 -4
- package/dist/node/ui/renderers/tool-registry.markdown.js +15 -4
- package/dist/node/ui/views/RunDataTable.js +326 -0
- package/dist/node/ui/views/RunListView.js +359 -127
- package/dist/node/ui/views/index.js +406 -174
- package/dist/node/ui/views/run-data-table.columns.js +271 -0
- package/dist/node/ui/views/run-list.shared.js +177 -0
- package/dist/node/visualizations/catalog.js +134 -0
- package/dist/node/visualizations/index.js +187 -0
- package/dist/node/visualizations/selectors.js +181 -0
- package/dist/presentations/index.js +49 -49
- package/dist/proof/index.d.ts +2 -0
- package/dist/proof/meetup-proof.d.ts +10 -0
- package/dist/proof/meetup-proof.runtime.d.ts +22 -0
- package/dist/proof/meetup-proof.scenario.d.ts +2 -0
- package/dist/proof/meetup-proof.suite.d.ts +1 -0
- package/dist/proof/meetup-proof.test.d.ts +1 -0
- package/dist/run/index.js +818 -812
- package/dist/run/run.handler.d.ts +4 -0
- package/dist/run/run.handler.js +666 -1
- package/dist/shared/demo-dashboard-data.d.ts +16 -0
- package/dist/shared/demo-runtime-seed.d.ts +17 -0
- package/dist/shared/demo-runtime.d.ts +8 -0
- package/dist/shared/demo-runtime.test.d.ts +1 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +293 -1
- package/dist/shared/mock-runs.d.ts +4 -0
- package/dist/shared/mock-runs.js +5 -0
- package/dist/tool/index.js +331 -331
- package/dist/tool/tool.handler.d.ts +4 -1
- package/dist/tool/tool.handler.js +479 -3
- package/dist/ui/AgentDashboard.js +1204 -319
- package/dist/ui/AgentDashboard.sandbox.test.d.ts +1 -0
- package/dist/ui/AgentDashboard.visualizations.d.ts +4 -0
- package/dist/ui/AgentDashboard.visualizations.js +218 -0
- package/dist/ui/AgentRunList.js +359 -127
- package/dist/ui/hooks/index.js +468 -18
- package/dist/ui/hooks/useAgentMutations.js +443 -8
- package/dist/ui/hooks/useRunList.d.ts +8 -2
- package/dist/ui/hooks/useRunList.js +25 -10
- package/dist/ui/index.js +1293 -390
- package/dist/ui/renderers/agent-list.markdown.js +14 -5
- package/dist/ui/renderers/dashboard.markdown.js +207 -36
- package/dist/ui/renderers/index.js +245 -49
- package/dist/ui/renderers/run-list.markdown.js +9 -4
- package/dist/ui/renderers/tool-registry.markdown.d.ts +1 -1
- package/dist/ui/renderers/tool-registry.markdown.js +15 -4
- package/dist/ui/views/RunDataTable.d.ts +18 -0
- package/dist/ui/views/RunDataTable.js +327 -0
- package/dist/ui/views/RunListView.js +359 -127
- package/dist/ui/views/index.js +406 -174
- package/dist/ui/views/run-data-table.columns.d.ts +3 -0
- package/dist/ui/views/run-data-table.columns.js +272 -0
- package/dist/ui/views/run-list.shared.d.ts +14 -0
- package/dist/ui/views/run-list.shared.js +178 -0
- package/dist/visualizations/catalog.d.ts +10 -0
- package/dist/visualizations/catalog.js +135 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +188 -0
- package/dist/visualizations/selectors.d.ts +3 -0
- package/dist/visualizations/selectors.js +182 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +112 -10
- package/proofs/agent-console-meetup.replay.json +220 -0
- package/src/agent/agent.handler.ts +18 -1
- package/src/agent.feature.ts +3 -0
- package/src/docs/agent-console.docblock.ts +11 -8
- package/src/example.test.ts +75 -0
- package/src/example.ts +2 -3
- package/src/handlers/agent.handlers.ts +55 -2
- package/src/handlers/index.ts +18 -2
- package/src/handlers/mock-handlers.test.ts +77 -0
- package/src/index.ts +2 -0
- package/src/proof/index.ts +2 -0
- package/src/proof/meetup-proof.runtime.ts +196 -0
- package/src/proof/meetup-proof.scenario.ts +99 -0
- package/src/proof/meetup-proof.suite.ts +29 -0
- package/src/proof/meetup-proof.test.ts +28 -0
- package/src/proof/meetup-proof.ts +130 -0
- package/src/run/run.handler.ts +17 -1
- package/src/shared/demo-dashboard-data.ts +58 -0
- package/src/shared/demo-runtime-seed.ts +139 -0
- package/src/shared/demo-runtime.test.ts +169 -0
- package/src/shared/demo-runtime.ts +260 -0
- package/src/shared/index.ts +11 -0
- package/src/shared/mock-runs.ts +5 -0
- package/src/tool/tool.handler.ts +21 -4
- package/src/ui/AgentDashboard.sandbox.test.tsx +312 -0
- package/src/ui/AgentDashboard.tsx +4 -1
- package/src/ui/AgentDashboard.visualizations.tsx +35 -0
- package/src/ui/hooks/useAgentMutations.ts +19 -11
- package/src/ui/hooks/useRunList.ts +41 -9
- package/src/ui/renderers/agent-list.markdown.ts +31 -12
- package/src/ui/renderers/dashboard.markdown.ts +37 -42
- package/src/ui/renderers/run-list.markdown.ts +16 -8
- package/src/ui/renderers/tool-registry.markdown.ts +21 -9
- package/src/ui/views/RunDataTable.tsx +74 -0
- package/src/ui/views/RunListView.tsx +37 -111
- package/src/ui/views/run-data-table.columns.tsx +102 -0
- package/src/ui/views/run-list.shared.tsx +139 -0
- package/src/visualizations/catalog.ts +132 -0
- package/src/visualizations/index.ts +2 -0
- package/src/visualizations/selectors.test.ts +12 -0
- package/src/visualizations/selectors.ts +70 -0
- package/tsdown.config.js +17 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { afterEach, beforeAll, describe, expect, it } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
type TemplateDefinition,
|
|
4
|
+
TemplateRuntimeContext,
|
|
5
|
+
type TemplateRuntimeContextValue,
|
|
6
|
+
} from '@contractspec/lib.example-shared-ui';
|
|
7
|
+
import Window from 'happy-dom/lib/window/Window.js';
|
|
8
|
+
import { act } from 'react';
|
|
9
|
+
import { createRoot, type Root } from 'react-dom/client';
|
|
10
|
+
import type { AgentHandlers } from '../handlers/agent.handlers';
|
|
11
|
+
import {
|
|
12
|
+
AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
13
|
+
createAgentConsoleDemoHandlers,
|
|
14
|
+
} from '../shared';
|
|
15
|
+
import { AgentDashboard } from './AgentDashboard';
|
|
16
|
+
|
|
17
|
+
const PROJECT_ID = 'agent-console-sandbox-smoke';
|
|
18
|
+
const TEMPLATE: TemplateDefinition = {
|
|
19
|
+
id: 'agent-console',
|
|
20
|
+
name: 'Agent Console',
|
|
21
|
+
description: 'Deterministic sandbox smoke test template.',
|
|
22
|
+
category: 'ai',
|
|
23
|
+
complexity: 'intermediate',
|
|
24
|
+
icon: '🤖',
|
|
25
|
+
features: ['agents', 'runs', 'tools', 'metrics'],
|
|
26
|
+
tags: ['sandbox', 'smoke'],
|
|
27
|
+
schema: { models: ['Agent', 'Run', 'Tool'], contracts: [] },
|
|
28
|
+
components: {
|
|
29
|
+
list: 'AgentListView',
|
|
30
|
+
detail: 'RunListView',
|
|
31
|
+
form: 'CreateAgentModal',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
beforeAll(() => {
|
|
36
|
+
const windowInstance = new Window({
|
|
37
|
+
url: 'https://sandbox.contractspec.local/sandbox',
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(windowInstance, 'SyntaxError', {
|
|
40
|
+
value: SyntaxError,
|
|
41
|
+
configurable: true,
|
|
42
|
+
});
|
|
43
|
+
Object.assign(globalThis, {
|
|
44
|
+
window: windowInstance,
|
|
45
|
+
document: windowInstance.document,
|
|
46
|
+
navigator: windowInstance.navigator,
|
|
47
|
+
HTMLElement: windowInstance.HTMLElement,
|
|
48
|
+
HTMLButtonElement: windowInstance.HTMLButtonElement,
|
|
49
|
+
HTMLInputElement: windowInstance.HTMLInputElement,
|
|
50
|
+
HTMLTextAreaElement: windowInstance.HTMLTextAreaElement,
|
|
51
|
+
Node: windowInstance.Node,
|
|
52
|
+
Event: windowInstance.Event,
|
|
53
|
+
MouseEvent: windowInstance.MouseEvent,
|
|
54
|
+
KeyboardEvent: windowInstance.KeyboardEvent,
|
|
55
|
+
MutationObserver: windowInstance.MutationObserver,
|
|
56
|
+
getComputedStyle: windowInstance.getComputedStyle.bind(windowInstance),
|
|
57
|
+
requestAnimationFrame: (callback: FrameRequestCallback) =>
|
|
58
|
+
setTimeout(() => callback(Date.now()), 0),
|
|
59
|
+
cancelAnimationFrame: (id: number) => clearTimeout(id),
|
|
60
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
document.body.innerHTML = '';
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function createContextValue(
|
|
69
|
+
handlers: AgentHandlers
|
|
70
|
+
): TemplateRuntimeContextValue<{ agent: AgentHandlers }> {
|
|
71
|
+
return {
|
|
72
|
+
template: TEMPLATE,
|
|
73
|
+
runtime: {},
|
|
74
|
+
installer: {
|
|
75
|
+
install: async () => {},
|
|
76
|
+
saveToStudio: async () => ({ projectId: PROJECT_ID, status: 'saved' }),
|
|
77
|
+
},
|
|
78
|
+
client: {} as never,
|
|
79
|
+
templateId: TEMPLATE.id,
|
|
80
|
+
projectId: PROJECT_ID,
|
|
81
|
+
engine: {} as never,
|
|
82
|
+
fetchData: async () => ({ data: null }),
|
|
83
|
+
handlers: { agent: handlers },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function renderDashboard() {
|
|
88
|
+
const container = document.createElement('div');
|
|
89
|
+
document.body.append(container);
|
|
90
|
+
const root: Root = createRoot(container);
|
|
91
|
+
const handlers = createAgentConsoleDemoHandlers({
|
|
92
|
+
projectId: PROJECT_ID,
|
|
93
|
+
organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
94
|
+
idFactory: (() => {
|
|
95
|
+
const counters = { agent: 0, run: 0 };
|
|
96
|
+
return (kind: 'agent' | 'run') => `${kind}-sandbox-${++counters[kind]}`;
|
|
97
|
+
})(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await act(async () => {
|
|
101
|
+
root.render(
|
|
102
|
+
<TemplateRuntimeContext.Provider value={createContextValue(handlers)}>
|
|
103
|
+
<AgentDashboard />
|
|
104
|
+
</TemplateRuntimeContext.Provider>
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { container, root };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function waitFor(assertion: () => boolean, timeoutMs = 3000) {
|
|
112
|
+
const startedAt = Date.now();
|
|
113
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
114
|
+
if (assertion()) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
await act(async () => {
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
throw new Error('Timed out waiting for dashboard state.');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function findButton(container: HTMLElement, label: string) {
|
|
125
|
+
return [...container.getElementsByTagName('button')].find((element) =>
|
|
126
|
+
element.textContent?.includes(label)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function findAgentCard(container: HTMLElement, name: string) {
|
|
131
|
+
return [...container.getElementsByTagName('*')].find(
|
|
132
|
+
(element) =>
|
|
133
|
+
element.getAttribute('role') === 'button' &&
|
|
134
|
+
element.textContent?.includes(name)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function getReactProp<T>(element: Element, propName: string): T | undefined {
|
|
139
|
+
const record = element as unknown as Record<string, unknown>;
|
|
140
|
+
const reactPropsKey = Object.keys(record).find((key) =>
|
|
141
|
+
key.startsWith('__reactProps$')
|
|
142
|
+
);
|
|
143
|
+
if (!reactPropsKey) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const props = record[reactPropsKey];
|
|
147
|
+
if (typeof props !== 'object' || props === null || !(propName in props)) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const value = (props as Record<string, unknown>)[propName];
|
|
151
|
+
return value as T | undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function click(element: Element | null | undefined) {
|
|
155
|
+
if (!element) {
|
|
156
|
+
throw new Error('Expected clickable element.');
|
|
157
|
+
}
|
|
158
|
+
const onPress = getReactProp<(() => void) | undefined>(element, 'onPress');
|
|
159
|
+
const onClick = getReactProp<((event: MouseEvent) => void) | undefined>(
|
|
160
|
+
element,
|
|
161
|
+
'onClick'
|
|
162
|
+
);
|
|
163
|
+
await act(async () => {
|
|
164
|
+
if (typeof onPress === 'function') {
|
|
165
|
+
onPress();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (typeof onClick === 'function') {
|
|
169
|
+
onClick(new MouseEvent('click', { bubbles: true }));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if ('click' in element && typeof element.click === 'function') {
|
|
173
|
+
element.click();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
element.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function fill(selector: string, value: string) {
|
|
181
|
+
const element = document.querySelector<
|
|
182
|
+
HTMLInputElement | HTMLTextAreaElement
|
|
183
|
+
>(selector);
|
|
184
|
+
if (!element) {
|
|
185
|
+
throw new Error(`Missing form field ${selector}.`);
|
|
186
|
+
}
|
|
187
|
+
const valueSetter = Object.getOwnPropertyDescriptor(
|
|
188
|
+
Object.getPrototypeOf(element),
|
|
189
|
+
'value'
|
|
190
|
+
)?.set;
|
|
191
|
+
if (!valueSetter) {
|
|
192
|
+
throw new Error(`Missing value setter for ${selector}.`);
|
|
193
|
+
}
|
|
194
|
+
const onChange = getReactProp<
|
|
195
|
+
(event: {
|
|
196
|
+
target: { value: string };
|
|
197
|
+
currentTarget: { value: string };
|
|
198
|
+
}) => void
|
|
199
|
+
>(element, 'onChange');
|
|
200
|
+
await act(async () => {
|
|
201
|
+
valueSetter.call(element, value);
|
|
202
|
+
onChange?.({
|
|
203
|
+
target: { value },
|
|
204
|
+
currentTarget: { value },
|
|
205
|
+
});
|
|
206
|
+
if (!onChange) {
|
|
207
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
208
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
describe('AgentDashboard sandbox smoke', () => {
|
|
214
|
+
it('loads seeded state, renders tabs, creates an agent, and executes a run', async () => {
|
|
215
|
+
const { container, root } = await renderDashboard();
|
|
216
|
+
await waitFor(
|
|
217
|
+
() => container.textContent?.includes('AI Agent Console') === true
|
|
218
|
+
);
|
|
219
|
+
await waitFor(
|
|
220
|
+
() =>
|
|
221
|
+
container.textContent?.includes(
|
|
222
|
+
'Affichage de 1 à 3 sur 5 résultats'
|
|
223
|
+
) === true
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
expect(container.textContent).toContain('Runs');
|
|
227
|
+
expect(container.textContent).toContain('Agents');
|
|
228
|
+
expect(container.textContent).toContain('Tools');
|
|
229
|
+
expect(container.textContent).toContain('Metrics');
|
|
230
|
+
expect(container.textContent).toContain('Run History');
|
|
231
|
+
|
|
232
|
+
await click(findButton(container, 'Tools'));
|
|
233
|
+
await waitFor(
|
|
234
|
+
() => container.textContent?.includes('Total Tools') === true
|
|
235
|
+
);
|
|
236
|
+
await click(findButton(container, 'Metrics'));
|
|
237
|
+
await waitFor(
|
|
238
|
+
() => container.textContent?.includes('Usage Analytics') === true
|
|
239
|
+
);
|
|
240
|
+
await click(findButton(container, 'Agents'));
|
|
241
|
+
await waitFor(() => container.textContent?.includes('Total: 4') === true);
|
|
242
|
+
|
|
243
|
+
await click(findButton(container, 'New Agent'));
|
|
244
|
+
await waitFor(
|
|
245
|
+
() => document.body.textContent?.includes('Create New Agent') === true
|
|
246
|
+
);
|
|
247
|
+
await fill('#agent-name', 'Paris Meetup UI Agent');
|
|
248
|
+
await fill(
|
|
249
|
+
'#agent-description',
|
|
250
|
+
'Smoke test agent for the sandbox walkthrough.'
|
|
251
|
+
);
|
|
252
|
+
await click(findButton(document.body, 'Create Agent'));
|
|
253
|
+
await click(findButton(container, 'Runs'));
|
|
254
|
+
await waitFor(
|
|
255
|
+
() =>
|
|
256
|
+
container.textContent?.includes(
|
|
257
|
+
'Affichage de 1 à 3 sur 5 résultats'
|
|
258
|
+
) === true
|
|
259
|
+
);
|
|
260
|
+
await click(findButton(container, 'Agents'));
|
|
261
|
+
await waitFor(
|
|
262
|
+
() => container.textContent?.includes('Paris Meetup UI Agent') === true
|
|
263
|
+
);
|
|
264
|
+
await waitFor(() => container.textContent?.includes('Total: 5') === true);
|
|
265
|
+
|
|
266
|
+
const agentCard = findAgentCard(container, 'Paris Meetup UI Agent');
|
|
267
|
+
await click(agentCard);
|
|
268
|
+
await waitFor(
|
|
269
|
+
() => document.body.textContent?.includes('Activate Agent') === true
|
|
270
|
+
);
|
|
271
|
+
await click(findButton(document.body, 'Activate Agent'));
|
|
272
|
+
await click(findButton(container, 'Runs'));
|
|
273
|
+
await waitFor(
|
|
274
|
+
() => container.textContent?.includes('Run History') === true
|
|
275
|
+
);
|
|
276
|
+
await click(findButton(container, 'Agents'));
|
|
277
|
+
await waitFor(
|
|
278
|
+
() =>
|
|
279
|
+
findAgentCard(
|
|
280
|
+
container,
|
|
281
|
+
'Paris Meetup UI Agent'
|
|
282
|
+
)?.textContent?.includes('ACTIVE') === true
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const activeAgentCard = findAgentCard(container, 'Paris Meetup UI Agent');
|
|
286
|
+
await click(activeAgentCard);
|
|
287
|
+
await waitFor(
|
|
288
|
+
() => document.body.textContent?.includes('Execute Agent') === true
|
|
289
|
+
);
|
|
290
|
+
await click(findButton(document.body, 'Execute Agent'));
|
|
291
|
+
await waitFor(
|
|
292
|
+
() => document.body.textContent?.includes('Message *') === true
|
|
293
|
+
);
|
|
294
|
+
await fill('#execute-message', 'Summarize the meetup smoke test.');
|
|
295
|
+
await click(findButton(document.body, 'Execute'));
|
|
296
|
+
|
|
297
|
+
await click(findButton(container, 'Runs'));
|
|
298
|
+
await waitFor(
|
|
299
|
+
() =>
|
|
300
|
+
container.textContent?.includes(
|
|
301
|
+
'Affichage de 1 à 3 sur 6 résultats'
|
|
302
|
+
) === true
|
|
303
|
+
);
|
|
304
|
+
await waitFor(
|
|
305
|
+
() => container.textContent?.includes('Paris Meetup UI Agent') === true
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
await act(async () => {
|
|
309
|
+
root.unmount();
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
* - ExecuteAgentCommand -> Execute agent via modal
|
|
18
18
|
*/
|
|
19
19
|
import { useCallback, useMemo, useState } from 'react';
|
|
20
|
+
import { AgentVisualizationOverview } from './AgentDashboard.visualizations';
|
|
20
21
|
import { type Agent, useAgentList } from './hooks/useAgentList';
|
|
21
22
|
import { useAgentMutations } from './hooks/useAgentMutations';
|
|
22
23
|
import { type RunMetrics, useRunList } from './hooks/useRunList';
|
|
@@ -34,7 +35,7 @@ export function AgentDashboard() {
|
|
|
34
35
|
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
|
|
35
36
|
const [isAgentActionsOpen, setIsAgentActionsOpen] = useState(false);
|
|
36
37
|
|
|
37
|
-
const { metrics, refetch: refetchRuns } = useRunList();
|
|
38
|
+
const { data: runData, metrics, refetch: refetchRuns } = useRunList();
|
|
38
39
|
const { refetch: refetchAgents } = useAgentList();
|
|
39
40
|
|
|
40
41
|
const mutations = useAgentMutations({
|
|
@@ -115,6 +116,8 @@ export function AgentDashboard() {
|
|
|
115
116
|
))}
|
|
116
117
|
</StatCardGroup>
|
|
117
118
|
|
|
119
|
+
<AgentVisualizationOverview runs={runData?.items ?? []} />
|
|
120
|
+
|
|
118
121
|
{/* Navigation Tabs */}
|
|
119
122
|
<nav className="flex gap-1 rounded-lg bg-muted p-1" role="tablist">
|
|
120
123
|
{tabs.map((tab) => (
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
VisualizationCard,
|
|
5
|
+
VisualizationGrid,
|
|
6
|
+
} from '@contractspec/lib.design-system';
|
|
7
|
+
import { createAgentVisualizationItems } from '../visualizations';
|
|
8
|
+
import type { Run } from './hooks/useRunList';
|
|
9
|
+
|
|
10
|
+
export function AgentVisualizationOverview({ runs }: { runs: Run[] }) {
|
|
11
|
+
const items = createAgentVisualizationItems(runs);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<section className="space-y-3">
|
|
15
|
+
<div>
|
|
16
|
+
<h3 className="font-semibold text-lg">Operational Visualizations</h3>
|
|
17
|
+
<p className="text-muted-foreground text-sm">
|
|
18
|
+
Contract-backed charts derived from recent run activity.
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
<VisualizationGrid>
|
|
22
|
+
{items.map((item) => (
|
|
23
|
+
<VisualizationCard
|
|
24
|
+
key={item.key}
|
|
25
|
+
data={item.data}
|
|
26
|
+
description={item.description}
|
|
27
|
+
height={item.height}
|
|
28
|
+
spec={item.spec}
|
|
29
|
+
title={item.title}
|
|
30
|
+
/>
|
|
31
|
+
))}
|
|
32
|
+
</VisualizationGrid>
|
|
33
|
+
</section>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
Run,
|
|
16
16
|
UpdateAgentInput,
|
|
17
17
|
} from '../../handlers/agent.handlers';
|
|
18
|
+
import { AGENT_CONSOLE_DEMO_ORGANIZATION_ID } from '../../shared/demo-runtime-seed';
|
|
18
19
|
|
|
19
20
|
export interface MutationState<T> {
|
|
20
21
|
loading: boolean;
|
|
@@ -27,6 +28,13 @@ export interface UseAgentMutationsOptions {
|
|
|
27
28
|
onError?: (error: Error) => void;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
function normalizeMutationError(
|
|
32
|
+
error: unknown,
|
|
33
|
+
fallbackMessage: string
|
|
34
|
+
): Error {
|
|
35
|
+
return error instanceof Error ? error : new Error(fallbackMessage);
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
export function useAgentMutations(options: UseAgentMutationsOptions = {}) {
|
|
31
39
|
const { handlers, projectId } = useTemplateRuntime<{
|
|
32
40
|
agent: AgentHandlers;
|
|
@@ -60,17 +68,16 @@ export function useAgentMutations(options: UseAgentMutationsOptions = {}) {
|
|
|
60
68
|
try {
|
|
61
69
|
const result = await agent.createAgent(input, {
|
|
62
70
|
projectId,
|
|
63
|
-
organizationId:
|
|
71
|
+
organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
64
72
|
});
|
|
65
73
|
setCreateState({ loading: false, error: null, data: result });
|
|
66
74
|
options.onSuccess?.();
|
|
67
75
|
return result;
|
|
68
76
|
} catch (err) {
|
|
69
|
-
const error =
|
|
70
|
-
err instanceof Error ? err : new Error('Failed to create agent');
|
|
77
|
+
const error = normalizeMutationError(err, 'Failed to create agent');
|
|
71
78
|
setCreateState({ loading: false, error, data: null });
|
|
72
79
|
options.onError?.(error);
|
|
73
|
-
|
|
80
|
+
throw error;
|
|
74
81
|
}
|
|
75
82
|
},
|
|
76
83
|
[agent, projectId, options]
|
|
@@ -88,11 +95,10 @@ export function useAgentMutations(options: UseAgentMutationsOptions = {}) {
|
|
|
88
95
|
options.onSuccess?.();
|
|
89
96
|
return result;
|
|
90
97
|
} catch (err) {
|
|
91
|
-
const error =
|
|
92
|
-
err instanceof Error ? err : new Error('Failed to update agent');
|
|
98
|
+
const error = normalizeMutationError(err, 'Failed to update agent');
|
|
93
99
|
setUpdateState({ loading: false, error, data: null });
|
|
94
100
|
options.onError?.(error);
|
|
95
|
-
|
|
101
|
+
throw error;
|
|
96
102
|
}
|
|
97
103
|
},
|
|
98
104
|
[agent, options]
|
|
@@ -141,17 +147,19 @@ export function useAgentMutations(options: UseAgentMutationsOptions = {}) {
|
|
|
141
147
|
const result = await agent.executeAgent({
|
|
142
148
|
agentId: input.agentId,
|
|
143
149
|
message: input.message,
|
|
144
|
-
context: {
|
|
150
|
+
context: {
|
|
151
|
+
projectId,
|
|
152
|
+
organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
153
|
+
},
|
|
145
154
|
});
|
|
146
155
|
setExecuteState({ loading: false, error: null, data: result });
|
|
147
156
|
options.onSuccess?.();
|
|
148
157
|
return result;
|
|
149
158
|
} catch (err) {
|
|
150
|
-
const error =
|
|
151
|
-
err instanceof Error ? err : new Error('Failed to execute agent');
|
|
159
|
+
const error = normalizeMutationError(err, 'Failed to execute agent');
|
|
152
160
|
setExecuteState({ loading: false, error, data: null });
|
|
153
161
|
options.onError?.(error);
|
|
154
|
-
|
|
162
|
+
throw error;
|
|
155
163
|
}
|
|
156
164
|
},
|
|
157
165
|
[agent, projectId, options]
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';
|
|
8
|
+
import type { ContractTableSort } from '@contractspec/lib.presentation-runtime-core';
|
|
8
9
|
import { useCallback, useEffect, useState } from 'react';
|
|
9
10
|
import type {
|
|
10
11
|
AgentHandlers,
|
|
@@ -22,6 +23,9 @@ export interface UseRunListOptions {
|
|
|
22
23
|
agentId?: string;
|
|
23
24
|
status?: Run['status'] | 'all';
|
|
24
25
|
limit?: number;
|
|
26
|
+
pageIndex?: number;
|
|
27
|
+
pageSize?: number;
|
|
28
|
+
sorting?: ContractTableSort[];
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export function useRunList(options: UseRunListOptions = {}) {
|
|
@@ -34,7 +38,11 @@ export function useRunList(options: UseRunListOptions = {}) {
|
|
|
34
38
|
const [metrics, setMetrics] = useState<RunMetrics | null>(null);
|
|
35
39
|
const [loading, setLoading] = useState(true);
|
|
36
40
|
const [error, setError] = useState<Error | null>(null);
|
|
37
|
-
const [
|
|
41
|
+
const [internalPageIndex, setInternalPageIndex] = useState(0);
|
|
42
|
+
|
|
43
|
+
const pageSize = options.pageSize ?? options.limit ?? 20;
|
|
44
|
+
const pageIndex = options.pageIndex ?? internalPageIndex;
|
|
45
|
+
const [sort] = options.sorting ?? [];
|
|
38
46
|
|
|
39
47
|
const fetchData = useCallback(async () => {
|
|
40
48
|
setLoading(true);
|
|
@@ -46,14 +54,21 @@ export function useRunList(options: UseRunListOptions = {}) {
|
|
|
46
54
|
projectId,
|
|
47
55
|
agentId: options.agentId,
|
|
48
56
|
status: options.status === 'all' ? undefined : options.status,
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
sortBy: sort?.id as
|
|
58
|
+
| 'queuedAt'
|
|
59
|
+
| 'totalTokens'
|
|
60
|
+
| 'durationMs'
|
|
61
|
+
| 'estimatedCostUsd'
|
|
62
|
+
| 'status'
|
|
63
|
+
| 'agentName'
|
|
64
|
+
| undefined,
|
|
65
|
+
sortDirection: sort ? (sort.desc ? 'desc' : 'asc') : undefined,
|
|
66
|
+
limit: pageSize,
|
|
67
|
+
offset: pageIndex * pageSize,
|
|
51
68
|
}),
|
|
52
69
|
agent.getRunMetrics({
|
|
53
70
|
projectId,
|
|
54
71
|
agentId: options.agentId,
|
|
55
|
-
startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), // 30 days ago
|
|
56
|
-
endDate: new Date(),
|
|
57
72
|
}),
|
|
58
73
|
]);
|
|
59
74
|
setData(runsResult);
|
|
@@ -63,20 +78,37 @@ export function useRunList(options: UseRunListOptions = {}) {
|
|
|
63
78
|
} finally {
|
|
64
79
|
setLoading(false);
|
|
65
80
|
}
|
|
66
|
-
}, [
|
|
81
|
+
}, [
|
|
82
|
+
agent,
|
|
83
|
+
pageIndex,
|
|
84
|
+
pageSize,
|
|
85
|
+
projectId,
|
|
86
|
+
options.agentId,
|
|
87
|
+
options.status,
|
|
88
|
+
sort?.desc,
|
|
89
|
+
sort?.id,
|
|
90
|
+
]);
|
|
67
91
|
|
|
68
92
|
useEffect(() => {
|
|
69
93
|
fetchData();
|
|
70
94
|
}, [fetchData]);
|
|
71
95
|
|
|
96
|
+
const hasControlledPagination = options.pageIndex !== undefined;
|
|
97
|
+
|
|
72
98
|
return {
|
|
73
99
|
data,
|
|
74
100
|
metrics,
|
|
75
101
|
loading,
|
|
76
102
|
error,
|
|
77
|
-
page,
|
|
103
|
+
page: pageIndex + 1,
|
|
104
|
+
pageIndex,
|
|
105
|
+
pageSize,
|
|
78
106
|
refetch: fetchData,
|
|
79
|
-
nextPage:
|
|
80
|
-
|
|
107
|
+
nextPage: hasControlledPagination
|
|
108
|
+
? undefined
|
|
109
|
+
: () => setInternalPageIndex((current) => current + 1),
|
|
110
|
+
prevPage: hasControlledPagination
|
|
111
|
+
? undefined
|
|
112
|
+
: () => setInternalPageIndex((current) => Math.max(0, current - 1)),
|
|
81
113
|
};
|
|
82
114
|
}
|
|
@@ -5,13 +5,20 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
9
|
+
AGENT_CONSOLE_DEMO_PROJECT_ID,
|
|
10
|
+
createAgentConsoleDemoHandlers,
|
|
11
|
+
} from '@contractspec/example.agent-console/shared';
|
|
11
12
|
import type { PresentationSpec } from '@contractspec/lib.contracts-spec/presentations';
|
|
12
13
|
import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
interface AgentListItem {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
status: string;
|
|
19
|
+
modelProvider: string;
|
|
20
|
+
modelName: string;
|
|
21
|
+
}
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* Markdown renderer for agent-console.agent.list presentation
|
|
@@ -22,7 +29,7 @@ export const agentListMarkdownRenderer: PresentationRenderer<{
|
|
|
22
29
|
body: string;
|
|
23
30
|
}> = {
|
|
24
31
|
target: 'markdown',
|
|
25
|
-
render: async (desc: PresentationSpec) => {
|
|
32
|
+
render: async (desc: PresentationSpec, ctx) => {
|
|
26
33
|
// Only handle AgentListView
|
|
27
34
|
if (
|
|
28
35
|
desc.source.type !== 'component' ||
|
|
@@ -31,12 +38,24 @@ export const agentListMarkdownRenderer: PresentationRenderer<{
|
|
|
31
38
|
throw new Error('agentListMarkdownRenderer: not AgentListView');
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
const data: {
|
|
42
|
+
items: AgentListItem[];
|
|
43
|
+
total: number;
|
|
44
|
+
hasMore: boolean;
|
|
45
|
+
} = Array.isArray(ctx?.data)
|
|
46
|
+
? {
|
|
47
|
+
items: ctx.data as AgentListItem[],
|
|
48
|
+
total: ctx.data.length,
|
|
49
|
+
hasMore: false,
|
|
50
|
+
}
|
|
51
|
+
: await createAgentConsoleDemoHandlers({
|
|
52
|
+
projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
|
|
53
|
+
}).listAgents({
|
|
54
|
+
projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
|
|
55
|
+
organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
|
|
56
|
+
limit: 50,
|
|
57
|
+
offset: 0,
|
|
58
|
+
});
|
|
40
59
|
|
|
41
60
|
// Generate markdown
|
|
42
61
|
const lines: string[] = [
|
|
@@ -51,7 +70,7 @@ export const agentListMarkdownRenderer: PresentationRenderer<{
|
|
|
51
70
|
];
|
|
52
71
|
|
|
53
72
|
// Group by status
|
|
54
|
-
const byStatus: Record<string,
|
|
73
|
+
const byStatus: Record<string, AgentListItem[]> = {};
|
|
55
74
|
for (const agent of data.items) {
|
|
56
75
|
const status = agent.status;
|
|
57
76
|
if (byStatus[status]) {
|