@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.
Files changed (186) hide show
  1. package/.turbo/turbo-build.log +126 -105
  2. package/AGENTS.md +3 -1
  3. package/CHANGELOG.md +29 -0
  4. package/README.md +46 -9
  5. package/dist/agent/agent.handler.d.ts +3 -0
  6. package/dist/agent/agent.handler.js +730 -1
  7. package/dist/agent/index.js +73 -72
  8. package/dist/agent.feature.js +179 -0
  9. package/dist/browser/agent/agent.handler.js +730 -1
  10. package/dist/browser/agent/index.js +73 -72
  11. package/dist/browser/agent.feature.js +179 -0
  12. package/dist/browser/docs/agent-console.docblock.js +11 -8
  13. package/dist/browser/docs/index.js +11 -8
  14. package/dist/browser/example.js +2 -3
  15. package/dist/browser/handlers/agent.handlers.js +1883 -2
  16. package/dist/browser/handlers/index.js +2142 -8
  17. package/dist/browser/index.js +3347 -2433
  18. package/dist/browser/presentations/index.js +49 -49
  19. package/dist/browser/run/index.js +818 -812
  20. package/dist/browser/run/run.handler.js +666 -1
  21. package/dist/browser/shared/index.js +293 -1
  22. package/dist/browser/shared/mock-runs.js +5 -0
  23. package/dist/browser/tool/index.js +331 -331
  24. package/dist/browser/tool/tool.handler.js +479 -3
  25. package/dist/browser/ui/AgentDashboard.js +1204 -319
  26. package/dist/browser/ui/AgentDashboard.visualizations.js +217 -0
  27. package/dist/browser/ui/AgentRunList.js +359 -127
  28. package/dist/browser/ui/hooks/index.js +468 -18
  29. package/dist/browser/ui/hooks/useAgentMutations.js +443 -8
  30. package/dist/browser/ui/hooks/useRunList.js +25 -10
  31. package/dist/browser/ui/index.js +1293 -390
  32. package/dist/browser/ui/renderers/agent-list.markdown.js +14 -5
  33. package/dist/browser/ui/renderers/dashboard.markdown.js +207 -36
  34. package/dist/browser/ui/renderers/index.js +245 -49
  35. package/dist/browser/ui/renderers/run-list.markdown.js +9 -4
  36. package/dist/browser/ui/renderers/tool-registry.markdown.js +15 -4
  37. package/dist/browser/ui/views/RunDataTable.js +326 -0
  38. package/dist/browser/ui/views/RunListView.js +359 -127
  39. package/dist/browser/ui/views/index.js +406 -174
  40. package/dist/browser/ui/views/run-data-table.columns.js +271 -0
  41. package/dist/browser/ui/views/run-list.shared.js +177 -0
  42. package/dist/browser/visualizations/catalog.js +134 -0
  43. package/dist/browser/visualizations/index.js +187 -0
  44. package/dist/browser/visualizations/selectors.js +181 -0
  45. package/dist/docs/agent-console.docblock.js +11 -8
  46. package/dist/docs/index.js +11 -8
  47. package/dist/example.js +2 -3
  48. package/dist/example.test.d.ts +1 -0
  49. package/dist/handlers/agent.handlers.d.ts +2 -0
  50. package/dist/handlers/agent.handlers.js +1883 -2
  51. package/dist/handlers/index.d.ts +1 -3
  52. package/dist/handlers/index.js +2142 -8
  53. package/dist/handlers/mock-handlers.test.d.ts +1 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +3347 -2433
  56. package/dist/node/agent/agent.handler.js +730 -1
  57. package/dist/node/agent/index.js +73 -72
  58. package/dist/node/agent.feature.js +179 -0
  59. package/dist/node/docs/agent-console.docblock.js +11 -8
  60. package/dist/node/docs/index.js +11 -8
  61. package/dist/node/example.js +2 -3
  62. package/dist/node/handlers/agent.handlers.js +1883 -2
  63. package/dist/node/handlers/index.js +2142 -8
  64. package/dist/node/index.js +3347 -2433
  65. package/dist/node/presentations/index.js +49 -49
  66. package/dist/node/run/index.js +818 -812
  67. package/dist/node/run/run.handler.js +666 -1
  68. package/dist/node/shared/index.js +293 -1
  69. package/dist/node/shared/mock-runs.js +5 -0
  70. package/dist/node/tool/index.js +331 -331
  71. package/dist/node/tool/tool.handler.js +479 -3
  72. package/dist/node/ui/AgentDashboard.js +1204 -319
  73. package/dist/node/ui/AgentDashboard.visualizations.js +217 -0
  74. package/dist/node/ui/AgentRunList.js +359 -127
  75. package/dist/node/ui/hooks/index.js +468 -18
  76. package/dist/node/ui/hooks/useAgentMutations.js +443 -8
  77. package/dist/node/ui/hooks/useRunList.js +25 -10
  78. package/dist/node/ui/index.js +1293 -390
  79. package/dist/node/ui/renderers/agent-list.markdown.js +14 -5
  80. package/dist/node/ui/renderers/dashboard.markdown.js +207 -36
  81. package/dist/node/ui/renderers/index.js +245 -49
  82. package/dist/node/ui/renderers/run-list.markdown.js +9 -4
  83. package/dist/node/ui/renderers/tool-registry.markdown.js +15 -4
  84. package/dist/node/ui/views/RunDataTable.js +326 -0
  85. package/dist/node/ui/views/RunListView.js +359 -127
  86. package/dist/node/ui/views/index.js +406 -174
  87. package/dist/node/ui/views/run-data-table.columns.js +271 -0
  88. package/dist/node/ui/views/run-list.shared.js +177 -0
  89. package/dist/node/visualizations/catalog.js +134 -0
  90. package/dist/node/visualizations/index.js +187 -0
  91. package/dist/node/visualizations/selectors.js +181 -0
  92. package/dist/presentations/index.js +49 -49
  93. package/dist/proof/index.d.ts +2 -0
  94. package/dist/proof/meetup-proof.d.ts +10 -0
  95. package/dist/proof/meetup-proof.runtime.d.ts +22 -0
  96. package/dist/proof/meetup-proof.scenario.d.ts +2 -0
  97. package/dist/proof/meetup-proof.suite.d.ts +1 -0
  98. package/dist/proof/meetup-proof.test.d.ts +1 -0
  99. package/dist/run/index.js +818 -812
  100. package/dist/run/run.handler.d.ts +4 -0
  101. package/dist/run/run.handler.js +666 -1
  102. package/dist/shared/demo-dashboard-data.d.ts +16 -0
  103. package/dist/shared/demo-runtime-seed.d.ts +17 -0
  104. package/dist/shared/demo-runtime.d.ts +8 -0
  105. package/dist/shared/demo-runtime.test.d.ts +1 -0
  106. package/dist/shared/index.d.ts +3 -0
  107. package/dist/shared/index.js +293 -1
  108. package/dist/shared/mock-runs.d.ts +4 -0
  109. package/dist/shared/mock-runs.js +5 -0
  110. package/dist/tool/index.js +331 -331
  111. package/dist/tool/tool.handler.d.ts +4 -1
  112. package/dist/tool/tool.handler.js +479 -3
  113. package/dist/ui/AgentDashboard.js +1204 -319
  114. package/dist/ui/AgentDashboard.sandbox.test.d.ts +1 -0
  115. package/dist/ui/AgentDashboard.visualizations.d.ts +4 -0
  116. package/dist/ui/AgentDashboard.visualizations.js +218 -0
  117. package/dist/ui/AgentRunList.js +359 -127
  118. package/dist/ui/hooks/index.js +468 -18
  119. package/dist/ui/hooks/useAgentMutations.js +443 -8
  120. package/dist/ui/hooks/useRunList.d.ts +8 -2
  121. package/dist/ui/hooks/useRunList.js +25 -10
  122. package/dist/ui/index.js +1293 -390
  123. package/dist/ui/renderers/agent-list.markdown.js +14 -5
  124. package/dist/ui/renderers/dashboard.markdown.js +207 -36
  125. package/dist/ui/renderers/index.js +245 -49
  126. package/dist/ui/renderers/run-list.markdown.js +9 -4
  127. package/dist/ui/renderers/tool-registry.markdown.d.ts +1 -1
  128. package/dist/ui/renderers/tool-registry.markdown.js +15 -4
  129. package/dist/ui/views/RunDataTable.d.ts +18 -0
  130. package/dist/ui/views/RunDataTable.js +327 -0
  131. package/dist/ui/views/RunListView.js +359 -127
  132. package/dist/ui/views/index.js +406 -174
  133. package/dist/ui/views/run-data-table.columns.d.ts +3 -0
  134. package/dist/ui/views/run-data-table.columns.js +272 -0
  135. package/dist/ui/views/run-list.shared.d.ts +14 -0
  136. package/dist/ui/views/run-list.shared.js +178 -0
  137. package/dist/visualizations/catalog.d.ts +10 -0
  138. package/dist/visualizations/catalog.js +135 -0
  139. package/dist/visualizations/index.d.ts +2 -0
  140. package/dist/visualizations/index.js +188 -0
  141. package/dist/visualizations/selectors.d.ts +3 -0
  142. package/dist/visualizations/selectors.js +182 -0
  143. package/dist/visualizations/selectors.test.d.ts +1 -0
  144. package/package.json +112 -10
  145. package/proofs/agent-console-meetup.replay.json +220 -0
  146. package/src/agent/agent.handler.ts +18 -1
  147. package/src/agent.feature.ts +3 -0
  148. package/src/docs/agent-console.docblock.ts +11 -8
  149. package/src/example.test.ts +75 -0
  150. package/src/example.ts +2 -3
  151. package/src/handlers/agent.handlers.ts +55 -2
  152. package/src/handlers/index.ts +18 -2
  153. package/src/handlers/mock-handlers.test.ts +77 -0
  154. package/src/index.ts +2 -0
  155. package/src/proof/index.ts +2 -0
  156. package/src/proof/meetup-proof.runtime.ts +196 -0
  157. package/src/proof/meetup-proof.scenario.ts +99 -0
  158. package/src/proof/meetup-proof.suite.ts +29 -0
  159. package/src/proof/meetup-proof.test.ts +28 -0
  160. package/src/proof/meetup-proof.ts +130 -0
  161. package/src/run/run.handler.ts +17 -1
  162. package/src/shared/demo-dashboard-data.ts +58 -0
  163. package/src/shared/demo-runtime-seed.ts +139 -0
  164. package/src/shared/demo-runtime.test.ts +169 -0
  165. package/src/shared/demo-runtime.ts +260 -0
  166. package/src/shared/index.ts +11 -0
  167. package/src/shared/mock-runs.ts +5 -0
  168. package/src/tool/tool.handler.ts +21 -4
  169. package/src/ui/AgentDashboard.sandbox.test.tsx +312 -0
  170. package/src/ui/AgentDashboard.tsx +4 -1
  171. package/src/ui/AgentDashboard.visualizations.tsx +35 -0
  172. package/src/ui/hooks/useAgentMutations.ts +19 -11
  173. package/src/ui/hooks/useRunList.ts +41 -9
  174. package/src/ui/renderers/agent-list.markdown.ts +31 -12
  175. package/src/ui/renderers/dashboard.markdown.ts +37 -42
  176. package/src/ui/renderers/run-list.markdown.ts +16 -8
  177. package/src/ui/renderers/tool-registry.markdown.ts +21 -9
  178. package/src/ui/views/RunDataTable.tsx +74 -0
  179. package/src/ui/views/RunListView.tsx +37 -111
  180. package/src/ui/views/run-data-table.columns.tsx +102 -0
  181. package/src/ui/views/run-list.shared.tsx +139 -0
  182. package/src/visualizations/catalog.ts +132 -0
  183. package/src/visualizations/index.ts +2 -0
  184. package/src/visualizations/selectors.test.ts +12 -0
  185. package/src/visualizations/selectors.ts +70 -0
  186. package/tsdown.config.js +17 -0
@@ -0,0 +1,139 @@
1
+ import type { Agent, Run, RunMetrics, Tool } from '../handlers/agent.handlers';
2
+ import { MOCK_AGENTS } from './mock-agents';
3
+ import { MOCK_RUNS } from './mock-runs';
4
+ import { MOCK_TOOLS } from './mock-tools';
5
+
6
+ export const AGENT_CONSOLE_DEMO_ORGANIZATION_ID = 'demo-org';
7
+ export const AGENT_CONSOLE_DEMO_PROJECT_ID = 'agent-console-demo';
8
+
9
+ export type DemoAgentRecord = Agent & { slug: string };
10
+
11
+ export function slugify(name: string): string {
12
+ return name
13
+ .toLowerCase()
14
+ .trim()
15
+ .replace(/[^a-z0-9]+/g, '-')
16
+ .replace(/^-+|-+$/g, '');
17
+ }
18
+
19
+ export function cloneAgent(agent: DemoAgentRecord): Agent {
20
+ return { ...agent };
21
+ }
22
+
23
+ export function cloneTool(tool: Tool): Tool {
24
+ return { ...tool };
25
+ }
26
+
27
+ export function cloneRun(run: Run): Run {
28
+ return { ...run };
29
+ }
30
+
31
+ export function createDefaultNow(): () => Date {
32
+ let tick = 0;
33
+ const base = Date.parse('2026-03-20T09:00:00.000Z');
34
+ return () => new Date(base + tick++ * 60_000);
35
+ }
36
+
37
+ function normalizeProvider(provider: string): string {
38
+ return provider.toLowerCase();
39
+ }
40
+
41
+ function normalizeRunStatus(status: string): Run['status'] {
42
+ return status === 'IN_PROGRESS' ? 'RUNNING' : (status as Run['status']);
43
+ }
44
+
45
+ export function createSeedState(
46
+ projectId: string,
47
+ organizationId: string
48
+ ): {
49
+ agents: DemoAgentRecord[];
50
+ tools: Tool[];
51
+ runs: Run[];
52
+ } {
53
+ const agents = MOCK_AGENTS.map((agent) => ({
54
+ id: agent.id,
55
+ projectId,
56
+ organizationId,
57
+ name: agent.name,
58
+ slug: agent.slug,
59
+ description: agent.description,
60
+ modelProvider: normalizeProvider(agent.modelProvider),
61
+ modelName: agent.modelName,
62
+ systemPrompt: agent.systemPrompt,
63
+ temperature:
64
+ typeof agent.modelConfig?.temperature === 'number'
65
+ ? agent.modelConfig.temperature
66
+ : 0.4,
67
+ maxTokens: 8192,
68
+ status: agent.status,
69
+ createdAt: agent.createdAt,
70
+ updatedAt: agent.updatedAt,
71
+ }));
72
+
73
+ const tools = MOCK_TOOLS.map((tool) => ({
74
+ id: tool.id,
75
+ projectId,
76
+ organizationId,
77
+ name: tool.name,
78
+ description: tool.description,
79
+ version: tool.version,
80
+ category: tool.category,
81
+ status: tool.status,
82
+ inputSchema: JSON.stringify(tool.parametersSchema),
83
+ outputSchema: tool.outputSchema
84
+ ? JSON.stringify(tool.outputSchema)
85
+ : undefined,
86
+ endpoint:
87
+ typeof tool.implementationConfig?.url === 'string'
88
+ ? tool.implementationConfig.url
89
+ : undefined,
90
+ createdAt: tool.createdAt,
91
+ updatedAt: tool.updatedAt,
92
+ }));
93
+
94
+ const agentNames = new Map(
95
+ agents.map((agent) => [agent.id, agent.name] as const)
96
+ );
97
+ const runs = MOCK_RUNS.map((run) => ({
98
+ id: run.id,
99
+ projectId,
100
+ agentId: run.agentId,
101
+ agentName: agentNames.get(run.agentId) ?? run.agentName ?? 'Unknown agent',
102
+ status: normalizeRunStatus(run.status),
103
+ input: JSON.stringify(run.input),
104
+ output: run.output ? JSON.stringify(run.output) : undefined,
105
+ totalTokens: run.totalTokens,
106
+ promptTokens: run.promptTokens,
107
+ completionTokens: run.completionTokens,
108
+ estimatedCostUsd: run.estimatedCostUsd ?? 0,
109
+ durationMs: run.durationMs,
110
+ errorMessage: run.errorMessage,
111
+ queuedAt: run.queuedAt,
112
+ startedAt: run.startedAt,
113
+ completedAt: run.completedAt,
114
+ }));
115
+
116
+ return { agents, tools, runs };
117
+ }
118
+
119
+ export function summarizeRunMetrics(runs: Run[]): RunMetrics {
120
+ const totalRuns = runs.length;
121
+ const completedRuns = runs.filter((run) => run.status === 'COMPLETED').length;
122
+ const completedDurations = runs
123
+ .map((run) => run.durationMs)
124
+ .filter((duration): duration is number => typeof duration === 'number');
125
+
126
+ return {
127
+ totalRuns,
128
+ successRate: totalRuns === 0 ? 0 : completedRuns / totalRuns,
129
+ averageDurationMs:
130
+ completedDurations.length === 0
131
+ ? 0
132
+ : Math.round(
133
+ completedDurations.reduce((sum, duration) => sum + duration, 0) /
134
+ completedDurations.length
135
+ ),
136
+ totalTokens: runs.reduce((sum, run) => sum + run.totalTokens, 0),
137
+ totalCostUsd: runs.reduce((sum, run) => sum + run.estimatedCostUsd, 0),
138
+ };
139
+ }
@@ -0,0 +1,169 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import {
3
+ AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
4
+ AGENT_CONSOLE_DEMO_PROJECT_ID,
5
+ createAgentConsoleDemoHandlers,
6
+ } from './index';
7
+ import { MOCK_AGENTS } from './mock-agents';
8
+ import { MOCK_RUNS } from './mock-runs';
9
+ import { MOCK_TOOLS } from './mock-tools';
10
+
11
+ describe('createAgentConsoleDemoHandlers', () => {
12
+ it('lists seeded agents, tools, and runs', async () => {
13
+ const handlers = createAgentConsoleDemoHandlers({
14
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
15
+ });
16
+ const [agents, tools, runs] = await Promise.all([
17
+ handlers.listAgents({
18
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
19
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
20
+ limit: 10,
21
+ }),
22
+ handlers.listTools({
23
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
24
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
25
+ limit: 10,
26
+ }),
27
+ handlers.listRuns({
28
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
29
+ limit: 10,
30
+ }),
31
+ ]);
32
+
33
+ expect(agents.total).toBe(MOCK_AGENTS.length);
34
+ expect(tools.total).toBe(MOCK_TOOLS.length);
35
+ expect(runs.total).toBe(MOCK_RUNS.length);
36
+ expect(agents.items[0]?.updatedAt).toBeInstanceOf(Date);
37
+ expect(runs.items[0]?.queuedAt).toBeInstanceOf(Date);
38
+ });
39
+
40
+ it('creates agents and rejects duplicate names or slugs', async () => {
41
+ const handlers = createAgentConsoleDemoHandlers({
42
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
43
+ });
44
+ const created = await handlers.createAgent(
45
+ {
46
+ name: 'Paris Meetup Agent',
47
+ description: 'Deterministic meetup walkthrough agent',
48
+ systemPrompt: 'Stay deterministic.',
49
+ },
50
+ {
51
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
52
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
53
+ }
54
+ );
55
+
56
+ expect(created.id).toBe('agent-demo-5');
57
+ expect(created.name).toBe('Paris Meetup Agent');
58
+ expect(created.status).toBe('DRAFT');
59
+
60
+ await expect(
61
+ handlers.createAgent(
62
+ {
63
+ name: 'paris meetup agent',
64
+ },
65
+ {
66
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
67
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
68
+ }
69
+ )
70
+ ).rejects.toThrow('AGENT_NAME_OR_SLUG_EXISTS');
71
+ });
72
+
73
+ it('updates agent status transitions', async () => {
74
+ const handlers = createAgentConsoleDemoHandlers({
75
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
76
+ });
77
+ const created = await handlers.createAgent(
78
+ {
79
+ name: 'Status Transition Agent',
80
+ },
81
+ {
82
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
83
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
84
+ }
85
+ );
86
+
87
+ const active = await handlers.updateAgent({
88
+ id: created.id,
89
+ status: 'ACTIVE',
90
+ });
91
+ const paused = await handlers.updateAgent({
92
+ id: created.id,
93
+ status: 'PAUSED',
94
+ });
95
+
96
+ expect(active.status).toBe('ACTIVE');
97
+ expect(paused.status).toBe('PAUSED');
98
+ expect(paused.updatedAt.getTime()).toBeGreaterThan(
99
+ active.updatedAt.getTime()
100
+ );
101
+ });
102
+
103
+ it('executes active agents with deterministic run output', async () => {
104
+ const handlers = createAgentConsoleDemoHandlers({
105
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
106
+ });
107
+ const created = await handlers.createAgent(
108
+ {
109
+ name: 'Execution Agent',
110
+ systemPrompt: 'Reply deterministically.',
111
+ },
112
+ {
113
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
114
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
115
+ }
116
+ );
117
+ await handlers.updateAgent({ id: created.id, status: 'ACTIVE' });
118
+
119
+ const run = await handlers.executeAgent({
120
+ agentId: created.id,
121
+ message: 'Summarize the meetup flow.',
122
+ context: {
123
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
124
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
125
+ },
126
+ });
127
+
128
+ expect(run.id).toBe('run-demo-6');
129
+ expect(run.status).toBe('COMPLETED');
130
+ expect(run.output).toContain(
131
+ 'Deterministic demo reply from Execution Agent'
132
+ );
133
+ expect(run.totalTokens).toBe(run.promptTokens + run.completionTokens);
134
+ });
135
+
136
+ it('updates aggregate metrics after execution', async () => {
137
+ const handlers = createAgentConsoleDemoHandlers({
138
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
139
+ });
140
+ const before = await handlers.getRunMetrics({
141
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
142
+ });
143
+ const created = await handlers.createAgent(
144
+ {
145
+ name: 'Metrics Agent',
146
+ },
147
+ {
148
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
149
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
150
+ }
151
+ );
152
+ await handlers.updateAgent({ id: created.id, status: 'ACTIVE' });
153
+ await handlers.executeAgent({
154
+ agentId: created.id,
155
+ message: 'Verify metrics.',
156
+ context: {
157
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
158
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
159
+ },
160
+ });
161
+ const after = await handlers.getRunMetrics({
162
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
163
+ });
164
+
165
+ expect(after.totalRuns).toBe(before.totalRuns + 1);
166
+ expect(after.totalTokens).toBeGreaterThan(before.totalTokens);
167
+ expect(after.totalCostUsd).toBeGreaterThan(before.totalCostUsd);
168
+ });
169
+ });
@@ -0,0 +1,260 @@
1
+ import type {
2
+ Agent,
3
+ AgentHandlers,
4
+ CreateAgentInput,
5
+ ListAgentsInput,
6
+ ListAgentsOutput,
7
+ ListRunsInput,
8
+ ListRunsOutput,
9
+ ListToolsInput,
10
+ ListToolsOutput,
11
+ Run,
12
+ RunMetrics,
13
+ UpdateAgentInput,
14
+ } from '../handlers/agent.handlers';
15
+ import {
16
+ AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
17
+ cloneAgent,
18
+ cloneRun,
19
+ cloneTool,
20
+ createDefaultNow,
21
+ createSeedState,
22
+ type DemoAgentRecord,
23
+ slugify,
24
+ summarizeRunMetrics,
25
+ } from './demo-runtime-seed';
26
+
27
+ export interface AgentConsoleDemoRuntimeOptions {
28
+ projectId: string;
29
+ organizationId?: string;
30
+ now?: () => Date;
31
+ idFactory?: (kind: 'agent' | 'run', nextIndex: number) => string;
32
+ }
33
+
34
+ export function createAgentConsoleDemoHandlers(
35
+ options: AgentConsoleDemoRuntimeOptions
36
+ ): AgentHandlers {
37
+ const projectId = options.projectId;
38
+ const organizationId =
39
+ options.organizationId ?? AGENT_CONSOLE_DEMO_ORGANIZATION_ID;
40
+ const now = options.now ?? createDefaultNow();
41
+ const state = createSeedState(projectId, organizationId);
42
+ let agentIndex = state.agents.length + 1;
43
+ let runIndex = state.runs.length + 1;
44
+
45
+ const nextId = (kind: 'agent' | 'run') => {
46
+ const nextIndex = kind === 'agent' ? agentIndex++ : runIndex++;
47
+ return options.idFactory?.(kind, nextIndex) ?? `${kind}-demo-${nextIndex}`;
48
+ };
49
+
50
+ return {
51
+ async listAgents(input: ListAgentsInput): Promise<ListAgentsOutput> {
52
+ const filtered = state.agents
53
+ .filter((agent) => agent.projectId === input.projectId)
54
+ .filter(
55
+ (agent) =>
56
+ !input.organizationId ||
57
+ agent.organizationId === input.organizationId
58
+ )
59
+ .filter(
60
+ (agent) =>
61
+ !input.status ||
62
+ input.status === 'all' ||
63
+ agent.status === input.status
64
+ )
65
+ .filter((agent) => {
66
+ if (!input.search) return true;
67
+ const query = input.search.toLowerCase();
68
+ return (
69
+ agent.name.toLowerCase().includes(query) ||
70
+ (agent.description ?? '').toLowerCase().includes(query)
71
+ );
72
+ })
73
+ .sort(
74
+ (left, right) => right.updatedAt.getTime() - left.updatedAt.getTime()
75
+ );
76
+ const limit = input.limit ?? 20;
77
+ const offset = input.offset ?? 0;
78
+ const items = filtered.slice(offset, offset + limit).map(cloneAgent);
79
+ return {
80
+ items,
81
+ total: filtered.length,
82
+ hasMore: offset + items.length < filtered.length,
83
+ };
84
+ },
85
+ async getAgent(id: string) {
86
+ const agent = state.agents.find((item) => item.id === id);
87
+ return agent ? cloneAgent(agent) : null;
88
+ },
89
+ async createAgent(
90
+ input: CreateAgentInput,
91
+ context: { projectId: string; organizationId: string }
92
+ ): Promise<Agent> {
93
+ const name = input.name.trim();
94
+ const slug = slugify(name);
95
+ const duplicate = state.agents.find(
96
+ (agent) =>
97
+ agent.projectId === context.projectId &&
98
+ agent.organizationId === context.organizationId &&
99
+ (agent.slug === slug ||
100
+ agent.name.toLowerCase() === name.toLowerCase())
101
+ );
102
+ if (duplicate) throw new Error('AGENT_NAME_OR_SLUG_EXISTS');
103
+
104
+ const timestamp = now();
105
+ const agent: DemoAgentRecord = {
106
+ id: nextId('agent'),
107
+ projectId: context.projectId,
108
+ organizationId: context.organizationId,
109
+ name,
110
+ slug,
111
+ description: input.description,
112
+ modelProvider: input.modelProvider ?? 'openai',
113
+ modelName: input.modelName ?? 'gpt-5.4',
114
+ systemPrompt: input.systemPrompt,
115
+ temperature: input.temperature ?? 0.4,
116
+ maxTokens: input.maxTokens ?? 8192,
117
+ status: 'DRAFT',
118
+ createdAt: timestamp,
119
+ updatedAt: timestamp,
120
+ };
121
+ state.agents.unshift(agent);
122
+ return cloneAgent(agent);
123
+ },
124
+ async updateAgent(input: UpdateAgentInput): Promise<Agent> {
125
+ const agent = state.agents.find((item) => item.id === input.id);
126
+ if (!agent) throw new Error('AGENT_NOT_FOUND');
127
+ const nextName = input.name;
128
+ if (nextName && slugify(nextName) !== agent.slug) {
129
+ const duplicate = state.agents.find(
130
+ (item) =>
131
+ item.id !== agent.id &&
132
+ item.projectId === agent.projectId &&
133
+ item.organizationId === agent.organizationId &&
134
+ (item.slug === slugify(nextName) ||
135
+ item.name.toLowerCase() === nextName.toLowerCase())
136
+ );
137
+ if (duplicate) throw new Error('AGENT_NAME_OR_SLUG_EXISTS');
138
+ agent.slug = slugify(nextName);
139
+ agent.name = nextName;
140
+ }
141
+ if (input.description !== undefined)
142
+ agent.description = input.description;
143
+ if (input.status) agent.status = input.status;
144
+ agent.updatedAt = now();
145
+ return cloneAgent(agent);
146
+ },
147
+ async executeAgent(input: {
148
+ agentId: string;
149
+ message: string;
150
+ context?: { projectId: string; organizationId: string };
151
+ }): Promise<Run> {
152
+ const agent = state.agents.find((item) => item.id === input.agentId);
153
+ if (!agent) throw new Error('AGENT_NOT_FOUND');
154
+ if (agent.status !== 'ACTIVE') throw new Error('AGENT_NOT_ACTIVE');
155
+
156
+ const queuedAt = now();
157
+ const startedAt = now();
158
+ const completedAt = now();
159
+ const durationMs = 2200 + input.message.length * 12;
160
+ const promptTokens = 600 + input.message.length * 3;
161
+ const completionTokens = 240 + agent.name.length * 4;
162
+ const run: Run = {
163
+ id: nextId('run'),
164
+ projectId: input.context?.projectId ?? agent.projectId,
165
+ agentId: agent.id,
166
+ agentName: agent.name,
167
+ status: 'COMPLETED',
168
+ input: input.message,
169
+ output: `Deterministic demo reply from ${agent.name}: ${input.message}`,
170
+ totalTokens: promptTokens + completionTokens,
171
+ promptTokens,
172
+ completionTokens,
173
+ estimatedCostUsd: Number(
174
+ ((promptTokens + completionTokens) / 1_000_000).toFixed(4)
175
+ ),
176
+ durationMs,
177
+ queuedAt,
178
+ startedAt,
179
+ completedAt,
180
+ };
181
+ state.runs.unshift(run);
182
+ return cloneRun(run);
183
+ },
184
+ async listTools(input: ListToolsInput): Promise<ListToolsOutput> {
185
+ const filtered = state.tools
186
+ .filter((tool) => tool.projectId === input.projectId)
187
+ .filter(
188
+ (tool) =>
189
+ !input.organizationId ||
190
+ tool.organizationId === input.organizationId
191
+ )
192
+ .filter(
193
+ (tool) =>
194
+ !input.category ||
195
+ input.category === 'all' ||
196
+ tool.category === input.category
197
+ )
198
+ .filter(
199
+ (tool) =>
200
+ !input.status ||
201
+ input.status === 'all' ||
202
+ tool.status === input.status
203
+ )
204
+ .filter((tool) => {
205
+ if (!input.search) return true;
206
+ const query = input.search.toLowerCase();
207
+ return (
208
+ tool.name.toLowerCase().includes(query) ||
209
+ (tool.description ?? '').toLowerCase().includes(query)
210
+ );
211
+ })
212
+ .sort((left, right) => left.name.localeCompare(right.name));
213
+ const limit = input.limit ?? 50;
214
+ const offset = input.offset ?? 0;
215
+ const items = filtered.slice(offset, offset + limit).map(cloneTool);
216
+ return {
217
+ items,
218
+ total: filtered.length,
219
+ hasMore: offset + items.length < filtered.length,
220
+ };
221
+ },
222
+ async listRuns(input: ListRunsInput): Promise<ListRunsOutput> {
223
+ const filtered = state.runs
224
+ .filter((run) => run.projectId === input.projectId)
225
+ .filter((run) => !input.agentId || run.agentId === input.agentId)
226
+ .filter(
227
+ (run) =>
228
+ !input.status ||
229
+ input.status === 'all' ||
230
+ run.status === input.status
231
+ )
232
+ .sort(
233
+ (left, right) => right.queuedAt.getTime() - left.queuedAt.getTime()
234
+ );
235
+ const limit = input.limit ?? 20;
236
+ const offset = input.offset ?? 0;
237
+ const items = filtered.slice(offset, offset + limit).map(cloneRun);
238
+ return {
239
+ items,
240
+ total: filtered.length,
241
+ hasMore: offset + items.length < filtered.length,
242
+ };
243
+ },
244
+ async getRunMetrics(input: {
245
+ projectId: string;
246
+ agentId?: string;
247
+ startDate?: Date;
248
+ endDate?: Date;
249
+ }): Promise<RunMetrics> {
250
+ const filtered = state.runs.filter((run) => {
251
+ if (run.projectId !== input.projectId) return false;
252
+ if (input.agentId && run.agentId !== input.agentId) return false;
253
+ if (input.startDate && run.queuedAt < input.startDate) return false;
254
+ if (input.endDate && run.queuedAt > input.endDate) return false;
255
+ return true;
256
+ });
257
+ return summarizeRunMetrics(filtered);
258
+ },
259
+ };
260
+ }
@@ -1,6 +1,17 @@
1
1
  /**
2
2
  * Shared utilities and mock data.
3
3
  */
4
+
5
+ export {
6
+ type AgentConsoleDashboardData,
7
+ getAgentConsoleDashboardData,
8
+ getFallbackAgentConsoleDashboardData,
9
+ } from './demo-dashboard-data';
10
+ export { createAgentConsoleDemoHandlers } from './demo-runtime';
11
+ export {
12
+ AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
13
+ AGENT_CONSOLE_DEMO_PROJECT_ID,
14
+ } from './demo-runtime-seed';
4
15
  export { MOCK_AGENTS } from './mock-agents';
5
16
  export { MOCK_RUNS } from './mock-runs';
6
17
  export { MOCK_TOOLS } from './mock-tools';
@@ -5,6 +5,7 @@ export const MOCK_RUNS = [
5
5
  {
6
6
  id: 'run-1',
7
7
  organizationId: 'demo-org',
8
+ projectId: 'demo-project',
8
9
  agentId: 'agent-1',
9
10
  agentName: 'Customer Support Bot',
10
11
  userId: 'user-1',
@@ -27,6 +28,7 @@ export const MOCK_RUNS = [
27
28
  {
28
29
  id: 'run-2',
29
30
  organizationId: 'demo-org',
31
+ projectId: 'demo-project',
30
32
  agentId: 'agent-2',
31
33
  agentName: 'Code Review Assistant',
32
34
  userId: 'user-2',
@@ -44,6 +46,7 @@ export const MOCK_RUNS = [
44
46
  {
45
47
  id: 'run-3',
46
48
  organizationId: 'demo-org',
49
+ projectId: 'demo-project',
47
50
  agentId: 'agent-1',
48
51
  agentName: 'Customer Support Bot',
49
52
  userId: 'user-1',
@@ -65,6 +68,7 @@ export const MOCK_RUNS = [
65
68
  {
66
69
  id: 'run-4',
67
70
  organizationId: 'demo-org',
71
+ projectId: 'demo-project',
68
72
  agentId: 'agent-3',
69
73
  agentName: 'Data Analyst',
70
74
  userId: 'user-3',
@@ -86,6 +90,7 @@ export const MOCK_RUNS = [
86
90
  {
87
91
  id: 'run-5',
88
92
  organizationId: 'demo-org',
93
+ projectId: 'demo-project',
89
94
  agentId: 'agent-2',
90
95
  agentName: 'Code Review Assistant',
91
96
  userId: 'user-2',
@@ -1,7 +1,26 @@
1
1
  /**
2
2
  * Mock handlers for Tool contracts.
3
3
  */
4
+
4
5
  import { MOCK_TOOLS } from '../shared/mock-tools';
6
+ import {
7
+ CreateToolCommand,
8
+ GetToolQuery,
9
+ ListToolsQuery,
10
+ TestToolCommand,
11
+ UpdateToolCommand,
12
+ } from './tool.operation';
13
+
14
+ const TOOL_HANDLER_CONTRACTS = [
15
+ CreateToolCommand,
16
+ GetToolQuery,
17
+ ListToolsQuery,
18
+ TestToolCommand,
19
+ UpdateToolCommand,
20
+ ] as const;
21
+ void TOOL_HANDLER_CONTRACTS;
22
+
23
+ let nextMockToolId = MOCK_TOOLS.length + 1;
5
24
 
6
25
  export interface ListToolsInput {
7
26
  organizationId: string;
@@ -107,7 +126,7 @@ export async function mockCreateToolHandler(input: {
107
126
  );
108
127
  if (exists) throw new Error('SLUG_EXISTS');
109
128
  return {
110
- id: `tool-${Date.now()}`,
129
+ id: `tool-${nextMockToolId++}`,
111
130
  name: input.name,
112
131
  slug: input.slug,
113
132
  status: 'DRAFT' as const,
@@ -142,13 +161,11 @@ export async function mockTestToolHandler(input: {
142
161
  const tool = MOCK_TOOLS.find((t) => t.id === input.toolId);
143
162
  if (!tool) throw new Error('TOOL_NOT_FOUND');
144
163
 
145
- // Simulate tool execution
146
- const startTime = Date.now();
147
164
  await new Promise((resolve) => setTimeout(resolve, 100));
148
165
 
149
166
  return {
150
167
  success: true,
151
168
  output: { result: 'Test successful', input: input.testInput },
152
- durationMs: Date.now() - startTime,
169
+ durationMs: 100,
153
170
  };
154
171
  }