@tonyclaw/agent-inspector 3.0.13 → 3.0.15

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 (46) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BRV9ztWd.js → CompareDrawer-UaIxykqz.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-DlpfcX56.js +106 -0
  4. package/.output/public/assets/{ReplayDialog-B4b6aD0q.js → ReplayDialog-BFFomU1m.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-ybH1l2Bp.js → RequestAnatomy-DKQJBt_c.js} +1 -1
  6. package/.output/public/assets/{ResponseView-DpPKgH-Y.js → ResponseView-D7yotmne.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-9f_rtMzz.js → StreamingChunkSequence-Da-6WNFG.js} +1 -1
  8. package/.output/public/assets/{_sessionId-gEmO-b2f.js → _sessionId-ByTXLS-w.js} +1 -1
  9. package/.output/public/assets/{index-JgHKqbP1.js → index-BrM8t5mc.js} +1 -1
  10. package/.output/public/assets/index-CGpPRty8.js +1 -0
  11. package/.output/public/assets/index-HRfrFVYw.css +1 -0
  12. package/.output/public/assets/{json-viewer-DFpIPmLo.js → json-viewer-DyjXQdVu.js} +1 -1
  13. package/.output/public/assets/{main-YvdO47OP.js → main-Zv_gjZMb.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +216 -202
  15. package/.output/server/{_sessionId-wSX-l7m2.mjs → _sessionId-GcFhWT0i.mjs} +3 -3
  16. package/.output/server/_ssr/{CompareDrawer-R4d8V0Rv.mjs → CompareDrawer-BV7dw1TN.mjs} +3 -3
  17. package/.output/server/_ssr/{ProxyViewerContainer-CDtJhGg8.mjs → ProxyViewerContainer-CcgfvaS4.mjs} +931 -393
  18. package/.output/server/_ssr/{ReplayDialog-BvmLERpW.mjs → ReplayDialog-B7sBKxIO.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-CH-oO6mY.mjs → RequestAnatomy-s_9Veje2.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-D7shbJw0.mjs → ResponseView-DsOFPCvF.mjs} +3 -3
  21. package/.output/server/_ssr/{StreamingChunkSequence-DB2YHGJj.mjs → StreamingChunkSequence-CyA1XtiF.mjs} +3 -3
  22. package/.output/server/_ssr/{index-CS4QLxuv.mjs → index-CDDRt201.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-HxOn73kS.mjs → json-viewer-BFV-xtRM.mjs} +3 -3
  25. package/.output/server/_ssr/{router-l4O3mQ4T.mjs → router-B2Q7vHPM.mjs} +321 -121
  26. package/.output/server/_tanstack-start-manifest_v-Dm_GmuRz.mjs +4 -0
  27. package/.output/server/index.mjs +73 -73
  28. package/README.md +24 -0
  29. package/package.json +1 -1
  30. package/src/components/ProxyViewer.tsx +470 -197
  31. package/src/components/ProxyViewerContainer.tsx +145 -17
  32. package/src/components/ecosystem/AgentLabDialog.tsx +221 -10
  33. package/src/components/proxy-viewer/ToolTraceEvents.tsx +36 -10
  34. package/src/components/proxy-viewer/TurnGroup.tsx +4 -1
  35. package/src/lib/ecosystemContract.ts +21 -1
  36. package/src/proxy/ecosystemRegistry.ts +52 -0
  37. package/src/proxy/ecosystemTasks.ts +95 -1
  38. package/src/proxy/formats/openai/codexMetadata.ts +61 -0
  39. package/src/proxy/formats/openai/handler.ts +8 -2
  40. package/src/routes/api/ecosystem.packages.ts +2 -1
  41. package/src/routes/api/ecosystem.recipes.$recipeId.run.ts +16 -0
  42. package/styles/globals.css +54 -0
  43. package/.output/public/assets/ProxyViewerContainer-BVsv8OFb.js +0 -106
  44. package/.output/public/assets/index-BIgsQvaF.css +0 -1
  45. package/.output/public/assets/index-CF3MPYb3.js +0 -1
  46. package/.output/server/_tanstack-start-manifest_v-Dckf_NHV.mjs +0 -4
@@ -5,6 +5,7 @@ import packageJson from "../../package.json";
5
5
  import type {
6
6
  EcosystemPackage,
7
7
  EcosystemPackageState,
8
+ EcosystemRecipe,
8
9
  EcosystemRunnerPreset,
9
10
  } from "../lib/ecosystemContract";
10
11
  import { npmSpawnCommand } from "./platformCommands";
@@ -106,6 +107,53 @@ export const RUNNER_PRESETS: EcosystemRunnerPreset[] = [
106
107
  },
107
108
  ];
108
109
 
110
+ export const LAB_RECIPES: EcosystemRecipe[] = [
111
+ {
112
+ id: "inspector-mcp-smoke",
113
+ title: "Inspector MCP smoke",
114
+ stage: "Validate",
115
+ packageId: "eval-harness",
116
+ description: "Check that the running Inspector exposes the MCP tools eval-harness expects.",
117
+ command: "npx @tonyclaw/eval-harness check-inspector-mcp",
118
+ requiresSession: false,
119
+ runnable: true,
120
+ },
121
+ {
122
+ id: "session-evidence-smoke",
123
+ title: "Session evidence smoke",
124
+ stage: "Capture",
125
+ packageId: "eval-harness",
126
+ description:
127
+ "Create a lightweight Inspector evidence run from the latest captured logs or selected session.",
128
+ command:
129
+ 'npx @tonyclaw/eval-harness inspector-smoke --title "TonyClaw Lab smoke" --latest-log-limit 5',
130
+ requiresSession: false,
131
+ runnable: true,
132
+ },
133
+ {
134
+ id: "runner-presets-json",
135
+ title: "Runner presets JSON",
136
+ stage: "Connect",
137
+ packageId: "eval-harness",
138
+ description: "Export runner adapter baselines as machine-readable JSON for CI or docs.",
139
+ command: "npx @tonyclaw/eval-harness runner-presets --format json",
140
+ requiresSession: false,
141
+ runnable: true,
142
+ },
143
+ {
144
+ id: "local-smoke-batch",
145
+ title: "Local smoke batch",
146
+ stage: "Evaluate",
147
+ packageId: "eval-harness",
148
+ description:
149
+ "Run one short local Jenkins-like evaluation using explicit project/work directories.",
150
+ command:
151
+ "npx @tonyclaw/eval-harness run-once --smoke --project-source <project> --work-source <work> --count 1 --parallel 1",
152
+ requiresSession: false,
153
+ runnable: false,
154
+ },
155
+ ];
156
+
109
157
  const NPM_TIMEOUT_MS = 4000;
110
158
 
111
159
  function runNpm(args: readonly string[]): Promise<NpmResult> {
@@ -242,3 +290,7 @@ export async function listEcosystemPackages(): Promise<EcosystemPackage[]> {
242
290
  export function findEcosystemPackage(packageId: string): PackageDefinition | null {
243
291
  return ECOSYSTEM_PACKAGES.find((definition) => definition.id === packageId) ?? null;
244
292
  }
293
+
294
+ export function findEcosystemRecipe(recipeId: string): EcosystemRecipe | null {
295
+ return LAB_RECIPES.find((recipe) => recipe.id === recipeId) ?? null;
296
+ }
@@ -6,13 +6,14 @@ import type {
6
6
  EcosystemTaskAction,
7
7
  EcosystemTaskStatus,
8
8
  } from "../lib/ecosystemContract";
9
- import { findEcosystemPackage } from "./ecosystemRegistry";
9
+ import { findEcosystemPackage, findEcosystemRecipe } from "./ecosystemRegistry";
10
10
  import { npmCommand, npmSpawnCommand } from "./platformCommands";
11
11
 
12
12
  const MAX_TASKS = 50;
13
13
  const OUTPUT_LIMIT = 40_000;
14
14
  const INSTALL_TIMEOUT_MS = 120_000;
15
15
  const HELP_TIMEOUT_MS = 30_000;
16
+ const RECIPE_TIMEOUT_MS = 90_000;
16
17
 
17
18
  const tasks = new Map<string, EcosystemTask>();
18
19
 
@@ -47,7 +48,32 @@ function packageActionArgs(action: EcosystemTaskAction, npmName: string): string
47
48
  return ["exec", "--yes", npmName, "--", "--help"];
48
49
  case "runner-presets":
49
50
  return ["exec", "--yes", "@tonyclaw/eval-harness", "--", "runner-presets"];
51
+ case "recipe":
52
+ return ["exec", "--yes", npmName, "--", "--help"];
53
+ }
54
+ }
55
+
56
+ function recipeActionArgs(recipeId: string): string[] | null {
57
+ const execPrefix = ["exec", "--yes", "@tonyclaw/eval-harness", "--"];
58
+ switch (recipeId) {
59
+ case "inspector-mcp-smoke":
60
+ return [...execPrefix, "check-inspector-mcp"];
61
+ case "session-evidence-smoke":
62
+ return [
63
+ ...execPrefix,
64
+ "inspector-smoke",
65
+ "--title",
66
+ "TonyClaw Lab smoke",
67
+ "--latest-log-limit",
68
+ "5",
69
+ ];
70
+ case "runner-presets-json":
71
+ return [...execPrefix, "runner-presets", "--format", "json"];
72
+ case "local-smoke-batch":
73
+ return null;
50
74
  }
75
+
76
+ return null;
51
77
  }
52
78
 
53
79
  function packageActionTimeout(action: EcosystemTaskAction): number {
@@ -58,6 +84,8 @@ function packageActionTimeout(action: EcosystemTaskAction): number {
58
84
  case "help":
59
85
  case "runner-presets":
60
86
  return HELP_TIMEOUT_MS;
87
+ case "recipe":
88
+ return RECIPE_TIMEOUT_MS;
61
89
  }
62
90
  }
63
91
 
@@ -101,6 +129,7 @@ export function startEcosystemTask(
101
129
  id: randomUUID(),
102
130
  packageId: definition.id,
103
131
  npmName: definition.npmName,
132
+ recipeId: null,
104
133
  action,
105
134
  status: "queued",
106
135
  command: taskCommand(args),
@@ -151,3 +180,68 @@ export function startEcosystemTask(
151
180
 
152
181
  return task;
153
182
  }
183
+
184
+ export function startEcosystemRecipeTask(recipeId: string): EcosystemTask | null {
185
+ const recipe = findEcosystemRecipe(recipeId);
186
+ if (recipe === null) return null;
187
+ if (!recipe.runnable) return null;
188
+ const definition = findEcosystemPackage(recipe.packageId);
189
+ if (definition === null) return null;
190
+ const args = recipeActionArgs(recipe.id);
191
+ if (args === null) return null;
192
+
193
+ const task: EcosystemTask = {
194
+ id: randomUUID(),
195
+ packageId: definition.id,
196
+ npmName: definition.npmName,
197
+ recipeId: recipe.id,
198
+ action: "recipe",
199
+ status: "queued",
200
+ command: taskCommand(args),
201
+ output: "",
202
+ createdAt: nowIso(),
203
+ startedAt: null,
204
+ finishedAt: null,
205
+ exitCode: null,
206
+ error: null,
207
+ };
208
+ rememberTask(task);
209
+
210
+ const spawnCommand = npmSpawnCommand(args);
211
+ const child = spawn(spawnCommand.command, spawnCommand.args, {
212
+ windowsHide: true,
213
+ shell: false,
214
+ stdio: ["ignore", "pipe", "pipe"],
215
+ });
216
+ const timeout = setTimeout(() => {
217
+ child.kill();
218
+ appendTaskOutput(task, `\nTimed out after ${String(RECIPE_TIMEOUT_MS / 1000)}s.\n`);
219
+ }, RECIPE_TIMEOUT_MS);
220
+
221
+ task.status = "running";
222
+ task.startedAt = nowIso();
223
+ appendTaskOutput(task, `$ ${task.command}\n`);
224
+
225
+ child.stdout.on("data", (chunk: Buffer) => {
226
+ appendTaskOutput(task, chunk.toString("utf8"));
227
+ });
228
+ child.stderr.on("data", (chunk: Buffer) => {
229
+ appendTaskOutput(task, chunk.toString("utf8"));
230
+ });
231
+ child.on("error", (error) => {
232
+ clearTimeout(timeout);
233
+ appendTaskOutput(task, `\n${error.message}\n`);
234
+ completeTask(task, "failed", null, error.message);
235
+ });
236
+ child.on("close", (code) => {
237
+ clearTimeout(timeout);
238
+ if (task.status === "failed") return;
239
+ if (code === 0) {
240
+ completeTask(task, "succeeded", code, null);
241
+ return;
242
+ }
243
+ completeTask(task, "failed", code, `Command exited with code ${String(code ?? "unknown")}`);
244
+ });
245
+
246
+ return task;
247
+ }
@@ -0,0 +1,61 @@
1
+ type CodexClientMetadata = {
2
+ sessionId: string | null;
3
+ threadId: string | null;
4
+ turnId: string | null;
5
+ installationId: string | null;
6
+ windowId: string | null;
7
+ };
8
+
9
+ const EMPTY_CODEX_CLIENT_METADATA: CodexClientMetadata = {
10
+ sessionId: null,
11
+ threadId: null,
12
+ turnId: null,
13
+ installationId: null,
14
+ windowId: null,
15
+ };
16
+
17
+ function readObjectField(value: unknown, field: string): unknown {
18
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
19
+ return Object.getOwnPropertyDescriptor(value, field)?.value;
20
+ }
21
+
22
+ function readStringField(value: unknown, field: string): string | null {
23
+ const fieldValue = readObjectField(value, field);
24
+ return typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : null;
25
+ }
26
+
27
+ function parseNestedTurnMetadata(value: unknown): unknown {
28
+ if (typeof value !== "string" || value.length === 0) return null;
29
+ try {
30
+ return JSON.parse(value);
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ export function extractCodexClientMetadata(rawRequest: unknown): CodexClientMetadata {
37
+ const clientMetadata = readObjectField(rawRequest, "client_metadata");
38
+ if (clientMetadata === undefined) return EMPTY_CODEX_CLIENT_METADATA;
39
+
40
+ const turnMetadata = parseNestedTurnMetadata(
41
+ readObjectField(clientMetadata, "x-codex-turn-metadata"),
42
+ );
43
+
44
+ return {
45
+ sessionId:
46
+ readStringField(clientMetadata, "session_id") ?? readStringField(turnMetadata, "session_id"),
47
+ threadId:
48
+ readStringField(clientMetadata, "thread_id") ?? readStringField(turnMetadata, "thread_id"),
49
+ turnId: readStringField(clientMetadata, "turn_id") ?? readStringField(turnMetadata, "turn_id"),
50
+ installationId:
51
+ readStringField(clientMetadata, "x-codex-installation-id") ??
52
+ readStringField(turnMetadata, "installation_id"),
53
+ windowId:
54
+ readStringField(clientMetadata, "x-codex-window-id") ??
55
+ readStringField(turnMetadata, "window_id"),
56
+ };
57
+ }
58
+
59
+ export function extractCodexSessionId(rawRequest: unknown): string | null {
60
+ return extractCodexClientMetadata(rawRequest).sessionId;
61
+ }
@@ -7,6 +7,7 @@ import {
7
7
  parseOpenAIResponse,
8
8
  } from "./schemas";
9
9
  import { extractOpenAIStream } from "./stream";
10
+ import { extractCodexSessionId } from "./codexMetadata";
10
11
 
11
12
  function emptyTokens(): TokenUsage {
12
13
  return {
@@ -17,6 +18,11 @@ function emptyTokens(): TokenUsage {
17
18
  };
18
19
  }
19
20
 
21
+ function headerSessionId(headers: Headers | undefined): string | null {
22
+ if (headers === undefined) return null;
23
+ return headers.get("x-agent-inspector-session-id") ?? headers.get("x-session-affinity");
24
+ }
25
+
20
26
  export const OpenAIFormatHandler: FormatHandler = {
21
27
  format: "openai",
22
28
 
@@ -27,14 +33,14 @@ export const OpenAIFormatHandler: FormatHandler = {
27
33
  if (result.success) {
28
34
  return {
29
35
  model: result.data.model,
30
- sessionId: headers?.get("x-session-affinity") ?? null,
36
+ sessionId: extractCodexSessionId(json) ?? headerSessionId(headers),
31
37
  };
32
38
  }
33
39
  const responsesResult = OpenAIResponsesRequestSchema.safeParse(json);
34
40
  if (responsesResult.success) {
35
41
  return {
36
42
  model: responsesResult.data.model,
37
- sessionId: headers?.get("x-session-affinity") ?? null,
43
+ sessionId: extractCodexSessionId(json) ?? headerSessionId(headers),
38
44
  };
39
45
  }
40
46
  return null;
@@ -1,5 +1,5 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { listEcosystemPackages, RUNNER_PRESETS } from "../../proxy/ecosystemRegistry";
2
+ import { LAB_RECIPES, listEcosystemPackages, RUNNER_PRESETS } from "../../proxy/ecosystemRegistry";
3
3
 
4
4
  export const Route = createFileRoute("/api/ecosystem/packages")({
5
5
  server: {
@@ -9,6 +9,7 @@ export const Route = createFileRoute("/api/ecosystem/packages")({
9
9
  checkedAt: new Date().toISOString(),
10
10
  packages: await listEcosystemPackages(),
11
11
  runnerPresets: RUNNER_PRESETS,
12
+ recipes: LAB_RECIPES,
12
13
  });
13
14
  },
14
15
  },
@@ -0,0 +1,16 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { startEcosystemRecipeTask } from "../../proxy/ecosystemTasks";
3
+
4
+ export const Route = createFileRoute("/api/ecosystem/recipes/$recipeId/run")({
5
+ server: {
6
+ handlers: {
7
+ POST: ({ params }: { params: { recipeId: string } }) => {
8
+ const task = startEcosystemRecipeTask(params.recipeId);
9
+ if (task === null) {
10
+ return Response.json({ error: "Unknown ecosystem recipe" }, { status: 404 });
11
+ }
12
+ return Response.json({ task });
13
+ },
14
+ },
15
+ },
16
+ });
@@ -44,6 +44,8 @@
44
44
  --animate-crab-appear: crab-appear 300ms ease-out;
45
45
  --animate-crab-settle: crab-settle 400ms ease-in-out;
46
46
  --animate-crab-piano-pop: crab-piano-pop 400ms cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
47
+ --animate-crab-short-leg-body: crab-short-leg-body 720ms ease-in-out infinite;
48
+ --animate-crab-short-leg-step: crab-short-leg-step 360ms ease-in-out infinite;
47
49
  }
48
50
 
49
51
  :root {
@@ -165,10 +167,42 @@
165
167
  }
166
168
  }
167
169
 
170
+ @keyframes crab-short-leg-body {
171
+ 0%,
172
+ 100% {
173
+ transform: translateY(0) rotate(-1deg);
174
+ }
175
+ 25% {
176
+ transform: translateY(1px) rotate(1.5deg);
177
+ }
178
+ 50% {
179
+ transform: translateY(-1px) rotate(-1.5deg);
180
+ }
181
+ 75% {
182
+ transform: translateY(1px) rotate(1deg);
183
+ }
184
+ }
185
+
186
+ @keyframes crab-short-leg-step {
187
+ 0%,
188
+ 100% {
189
+ transform: translateY(0) scaleY(0.72) rotate(var(--crab-leg-rest, 0deg));
190
+ opacity: 0.42;
191
+ }
192
+ 50% {
193
+ transform: translateY(3px) scaleY(1.15) rotate(var(--crab-leg-step, 0deg));
194
+ opacity: 0.9;
195
+ }
196
+ }
197
+
168
198
  @media (prefers-reduced-motion: reduce) {
169
199
  .animate-crab-piano-pop {
170
200
  animation: none !important;
171
201
  }
202
+ .floating-crab-body,
203
+ .floating-crab-leg {
204
+ animation: none !important;
205
+ }
172
206
  }
173
207
 
174
208
  [data-deep-link-highlight="true"] {
@@ -205,6 +239,26 @@
205
239
  }
206
240
 
207
241
  @layer utilities {
242
+ .floating-crab-body {
243
+ animation: var(--animate-crab-short-leg-body);
244
+ transform-origin: 50% 74%;
245
+ }
246
+
247
+ .floating-crab-leg {
248
+ animation: var(--animate-crab-short-leg-step);
249
+ transform-origin: 50% 0%;
250
+ }
251
+
252
+ .floating-crab-leg-left {
253
+ --crab-leg-rest: -10deg;
254
+ --crab-leg-step: 14deg;
255
+ }
256
+
257
+ .floating-crab-leg-right {
258
+ --crab-leg-rest: 10deg;
259
+ --crab-leg-step: -14deg;
260
+ }
261
+
208
262
  .inspector-scrollbar {
209
263
  scrollbar-color: color-mix(in srgb, var(--muted-foreground) 32%, transparent) transparent;
210
264
  scrollbar-width: thin;