@runtypelabs/cli 2.23.1 → 2.24.1

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 (2) hide show
  1. package/dist/index.js +278 -200
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -549,7 +549,7 @@ import { RuntypeApiError } from "@runtypelabs/sdk";
549
549
  // src/lib/sdk-client.ts
550
550
  import { createClient } from "@runtypelabs/sdk";
551
551
 
552
- // ../shared/dist/chunk-MYXAZOFG.mjs
552
+ // ../shared/dist/chunk-6FWWIIGE.mjs
553
553
  var travrseConfig = {
554
554
  name: "Travrse",
555
555
  nameLower: "travrse",
@@ -573,7 +573,7 @@ var runtypeConfig = {
573
573
  name: "Runtype",
574
574
  nameLower: "runtype",
575
575
  tagline: "Your AI Product Platform",
576
- description: "Build and deploy AI experiences faster than ever. Powerful API for developers, intuitive dashboard for teams.",
576
+ description: "The fastest way to ship agentic products. Prototype in minutes, prove it with evals, and ship to every surface on our cloud or yours.",
577
577
  url: "https://www.runtype.com",
578
578
  website: "https://runtype.com",
579
579
  domain: "runtype.com",
@@ -15566,7 +15566,7 @@ function resolveFallback(parsed, context, tracking) {
15566
15566
  }
15567
15567
  var templateEngine = new SimpleTemplateEngine();
15568
15568
 
15569
- // ../shared/dist/chunk-MMDRPM6Q.mjs
15569
+ // ../shared/dist/chunk-ONVX3P3J.mjs
15570
15570
  var tokensSchema = external_exports.object({ input: external_exports.number(), output: external_exports.number() });
15571
15571
  var wireStopReasonSchema = external_exports.enum([
15572
15572
  "end_turn",
@@ -15904,7 +15904,17 @@ var unifiedAwaitEventSchema = external_exports.object({
15904
15904
  parameters: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
15905
15905
  awaitedAt: external_exports.string().optional(),
15906
15906
  origin: external_exports.enum(["webmcp", "sdk"]).optional(),
15907
- pageOrigin: external_exports.string().optional()
15907
+ pageOrigin: external_exports.string().optional(),
15908
+ // Discriminates an AUTO-RESUMING durable pause (`crawl_pending` /
15909
+ // `durable_poll`, resumed server-side by the crawl / wait-until
15910
+ // Workflow) from a CLIENT-RESOLVABLE local-tool / WebMCP await (absent), so a
15911
+ // streaming consumer does not render a "resume" prompt for a pause that
15912
+ // resumes itself. Mirrors the JSON `pausedReason` discriminator
15913
+ // (execution-pipeline.ts); the server-resumed pauses carry `crawlId` /
15914
+ // `stepId` instead of the tool fields. UX context; never a control signal.
15915
+ awaitReason: external_exports.string().optional(),
15916
+ crawlId: external_exports.string().optional(),
15917
+ stepId: external_exports.string().optional()
15908
15918
  });
15909
15919
  var unifiedErrorEventSchema = external_exports.object({
15910
15920
  ...unifiedBase,
@@ -16400,7 +16410,17 @@ var flowAwaitEventSchema = external_exports.object({
16400
16410
  parameters: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
16401
16411
  awaitedAt: external_exports.string(),
16402
16412
  origin: external_exports.enum(["webmcp", "sdk"]).optional(),
16403
- pageOrigin: external_exports.string().optional()
16413
+ pageOrigin: external_exports.string().optional(),
16414
+ // Discriminates an AUTO-RESUMING durable pause (`crawl_pending` /
16415
+ // `durable_poll`, resumed server-side by the crawl / wait-until
16416
+ // Workflow) from a CLIENT-RESOLVABLE local-tool / WebMCP await (absent). The
16417
+ // server-resumed pauses carry `crawlId` / `stepId` instead of the tool fields.
16418
+ // Open `z.string()` (not an enum) for forward-compat — kept in lockstep with
16419
+ // the JSON `pausedReason` discriminator in execution-pipeline.ts. UX context;
16420
+ // never a control signal.
16421
+ awaitReason: external_exports.string().optional(),
16422
+ crawlId: external_exports.string().optional(),
16423
+ stepId: external_exports.string().optional()
16404
16424
  });
16405
16425
  var stepStartEventSchema = external_exports.object({
16406
16426
  ...baseFlowEvent,
@@ -33327,7 +33347,7 @@ var CORE_BUILTIN_TOOLS_REGISTRY = [
33327
33347
  {
33328
33348
  id: "exa",
33329
33349
  name: "Exa",
33330
- description: "Search the web using neural and keyword algorithms. Returns relevant content with optional summaries, highlights, and full text.",
33350
+ description: "Search the web using neural and keyword algorithms. Returns relevant content with optional summaries, highlights, and full text. Tuned for retrieval and not blocked by anti-bot defenses, so prefer this over the raw fetch-url/crawl flow steps for research against sites that block datacenter traffic.",
33331
33351
  category: BuiltInToolCategory.WEB_SEARCH,
33332
33352
  providers: [BuiltInToolProvider.MULTI],
33333
33353
  parametersSchema: {
@@ -33402,7 +33422,7 @@ var CORE_BUILTIN_TOOLS_REGISTRY = [
33402
33422
  {
33403
33423
  id: "firecrawl",
33404
33424
  name: "Firecrawl",
33405
- description: "Scrape and extract content from a URL as markdown. Provide a url and optional formats array.",
33425
+ description: "Scrape and extract content from a URL as markdown. Provide a url and optional formats array. Handles rendering and anti-bot measures, so prefer this over a raw HTTP fetch when a site blocks, rate-limits, or challenges datacenter (non-human) traffic.",
33406
33426
  category: BuiltInToolCategory.WEB_SCRAPING,
33407
33427
  providers: [BuiltInToolProvider.MULTI],
33408
33428
  parametersSchema: {
@@ -37591,39 +37611,46 @@ var BUILT_IN_GRADER_IDS = [
37591
37611
  "rightTone",
37592
37612
  "safeToSend"
37593
37613
  ];
37614
+ var graderSeveritySchema = external_exports.enum(["gate", "soft"]);
37615
+ var severityFields = { severity: graderSeveritySchema.optional() };
37594
37616
  var checkGraderSchema = external_exports.discriminatedUnion("kind", [
37595
37617
  external_exports.object({
37596
37618
  kind: external_exports.literal("contains"),
37597
37619
  value: external_exports.string(),
37598
- caseSensitive: external_exports.boolean().optional()
37620
+ caseSensitive: external_exports.boolean().optional(),
37621
+ ...severityFields
37599
37622
  }),
37600
37623
  external_exports.object({
37601
37624
  kind: external_exports.literal("not_contains"),
37602
37625
  value: external_exports.string(),
37603
- caseSensitive: external_exports.boolean().optional()
37626
+ caseSensitive: external_exports.boolean().optional(),
37627
+ ...severityFields
37604
37628
  }),
37605
37629
  // Exact/normalized match against `case.expected.text`.
37606
- external_exports.object({ kind: external_exports.literal("matches_expected") }),
37630
+ external_exports.object({ kind: external_exports.literal("matches_expected"), ...severityFields }),
37607
37631
  external_exports.object({
37608
37632
  kind: external_exports.literal("regex"),
37609
37633
  pattern: external_exports.string(),
37610
- flags: external_exports.string().optional()
37634
+ flags: external_exports.string().optional(),
37635
+ ...severityFields
37611
37636
  }),
37612
- external_exports.object({ kind: external_exports.literal("valid_json") }),
37637
+ external_exports.object({ kind: external_exports.literal("valid_json"), ...severityFields }),
37613
37638
  external_exports.object({
37614
37639
  kind: external_exports.literal("json_field"),
37615
37640
  path: external_exports.string(),
37616
37641
  equals: external_exports.unknown().optional(),
37617
- exists: external_exports.boolean().optional()
37642
+ exists: external_exports.boolean().optional(),
37643
+ ...severityFields
37618
37644
  }),
37619
37645
  external_exports.object({
37620
37646
  kind: external_exports.literal("length"),
37621
37647
  minChars: external_exports.number().int().nonnegative().optional(),
37622
- maxChars: external_exports.number().int().nonnegative().optional()
37648
+ maxChars: external_exports.number().int().nonnegative().optional(),
37649
+ ...severityFields
37623
37650
  }),
37624
- external_exports.object({ kind: external_exports.literal("latency"), maxMs: external_exports.number().int().positive() }),
37651
+ external_exports.object({ kind: external_exports.literal("latency"), maxMs: external_exports.number().int().positive(), ...severityFields }),
37625
37652
  // Today's implicit "success" made explicit: the case produced output without erroring.
37626
- external_exports.object({ kind: external_exports.literal("no_error") }),
37653
+ external_exports.object({ kind: external_exports.literal("no_error"), ...severityFields }),
37627
37654
  // -------------------------------------------------------------------------
37628
37655
  // Trace checks — deterministic, free, pure assertions over the run's
37629
37656
  // EXECUTION TRACE (which tools/steps ran, in what order, whether it
@@ -37641,25 +37668,30 @@ var checkGraderSchema = external_exports.discriminatedUnion("kind", [
37641
37668
  input: external_exports.unknown().optional(),
37642
37669
  output: external_exports.unknown().optional(),
37643
37670
  isError: external_exports.boolean().optional(),
37644
- times: external_exports.number().int().positive().optional()
37671
+ times: external_exports.number().int().positive().optional(),
37672
+ ...severityFields
37645
37673
  }),
37646
37674
  // No tool named `name` was called.
37647
- external_exports.object({ kind: external_exports.literal("not_called_tool"), name: external_exports.string().min(1) }),
37675
+ external_exports.object({ kind: external_exports.literal("not_called_tool"), name: external_exports.string().min(1), ...severityFields }),
37648
37676
  // The run made no tool calls at all.
37649
- external_exports.object({ kind: external_exports.literal("used_no_tools") }),
37677
+ external_exports.object({ kind: external_exports.literal("used_no_tools"), ...severityFields }),
37650
37678
  // The run made at most `max` tool calls.
37651
- external_exports.object({ kind: external_exports.literal("max_tool_calls"), max: external_exports.number().int().nonnegative() }),
37679
+ external_exports.object({
37680
+ kind: external_exports.literal("max_tool_calls"),
37681
+ max: external_exports.number().int().nonnegative(),
37682
+ ...severityFields
37683
+ }),
37652
37684
  // `tools` appears as an ordered SUBSEQUENCE of the tool-call names (other
37653
37685
  // calls may interleave; relative order of the listed tools must hold).
37654
- external_exports.object({ kind: external_exports.literal("tool_order"), tools: external_exports.array(external_exports.string()).min(1) }),
37686
+ external_exports.object({ kind: external_exports.literal("tool_order"), tools: external_exports.array(external_exports.string()).min(1), ...severityFields }),
37655
37687
  // A step named (or typed) `name` ran.
37656
- external_exports.object({ kind: external_exports.literal("ran_step"), name: external_exports.string().min(1) }),
37688
+ external_exports.object({ kind: external_exports.literal("ran_step"), name: external_exports.string().min(1), ...severityFields }),
37657
37689
  // `steps` appears as an ordered SUBSEQUENCE of the steps that ran.
37658
- external_exports.object({ kind: external_exports.literal("step_order"), steps: external_exports.array(external_exports.string()).min(1) }),
37690
+ external_exports.object({ kind: external_exports.literal("step_order"), steps: external_exports.array(external_exports.string()).min(1), ...severityFields }),
37659
37691
  // The run completed (finished without erroring and was not left paused).
37660
- external_exports.object({ kind: external_exports.literal("completed") }),
37692
+ external_exports.object({ kind: external_exports.literal("completed"), ...severityFields }),
37661
37693
  // Total run cost was within `maxUsd` (US dollars).
37662
- external_exports.object({ kind: external_exports.literal("cost"), maxUsd: external_exports.number().positive() })
37694
+ external_exports.object({ kind: external_exports.literal("cost"), maxUsd: external_exports.number().positive(), ...severityFields })
37663
37695
  ]);
37664
37696
  var aiGraderSchema = external_exports.object({
37665
37697
  kind: external_exports.literal("ai"),
@@ -37671,7 +37703,8 @@ var aiGraderSchema = external_exports.object({
37671
37703
  /** Defaults to a cheap routed model (e.g. claude-haiku-4-5) at execution time. */
37672
37704
  model: external_exports.string().optional(),
37673
37705
  /** Pass cutoff for the 1-5 scale. */
37674
- threshold: external_exports.number().min(1).max(5).optional()
37706
+ threshold: external_exports.number().min(1).max(5).optional(),
37707
+ ...severityFields
37675
37708
  });
37676
37709
  var graderConfigSchema = external_exports.union([checkGraderSchema, aiGraderSchema]);
37677
37710
  var gradersSchema = external_exports.array(graderConfigSchema);
@@ -42133,10 +42166,10 @@ var FLOW_STEP_TYPE_METADATA = {
42133
42166
  configHints: "vectorsSource, destination, idTemplate, outputVariable"
42134
42167
  },
42135
42168
  crawl: {
42136
- description: "Crawl a website and extract content from pages.",
42169
+ description: "Crawl a website with Cloudflare Browser Rendering and extract page content as markdown. Renders the SEED page fully; at depth >= 1 it enumerates child links WITHOUT rendering them (1 rendered seed record + N empty-markdown child records) and follows only ABSOLUTE link hrefs (relative links like item?id= are never followed). Writes { crawlId, status, result, resultPages[] } to outputVariable \u2014 flatten resultPages[].records[] (each { url, status, markdown?, metadata? }) to read every page. GOTCHA: the URL query string is DROPPED (https://site/item?id=42 crawls https://site/item) \u2014 for query-string URLs, JSON APIs, SPAs, or single detail/comment pages use fetch-url or the builtin:firecrawl scrape tool instead (firecrawl preserves query strings). ASYNC by default: with no streaming consumer (MCP run_flow/dispatch) the flow durably PAUSES while a Workflow polls; if the crawl exceeds completionTimeoutMs (default 120000 = 2 min) the step SILENTLY resumes with output = null. For real crawls raise completionTimeoutMs (e.g. 600000) and set pollIntervalMs (e.g. 5000), or set asyncCrawl:false to poll inline within the request (good for smoke tests).",
42137
42170
  category: "integration",
42138
42171
  isPrompt: false,
42139
- configHints: "url, limit, depth, outputVariable"
42172
+ configHints: 'url (required; query string is dropped), outputVariable (required), depth (0 = seed-only render, recommended), limit, formats (e.g. ["markdown"]), render, options ({ includePatterns, excludePatterns, allowSubdomains, allowExternalLinks, ignoreSitemap, sitemapOnly }), waitForSelector, completionTimeoutMs (default 120000; raise for slow crawls), pollIntervalMs, asyncCrawl (false = synchronous inline polling), errorHandling ({ onError: "continue"|"fail" } OBJECT, not a bare string), defaultValue. Pattern for "index + detail/comment pages": crawl the index with depth 0, transform-data to extract the links/ids from result markdown, then an agent prompt step (mode: "agent") with tools.toolIds ["builtin:firecrawl"] to fetch each query-string detail URL.'
42140
42173
  },
42141
42174
  "paginate-api": {
42142
42175
  description: "Fetch paginated data from an API endpoint across multiple pages.",
@@ -42854,6 +42887,7 @@ ${orthogonalLines.join("\n")}
42854
42887
  - **Web search (provider-native, inline citations)** \u2192 see Provider-Native Search section above
42855
42888
  - **Web search (standalone, AI-powered)** \u2192 \`builtin:exa\`
42856
42889
  - **Simple web scraping to markdown** \u2192 \`builtin:firecrawl\`
42890
+ - **Research/scraping a site that blocks bots** \u2192 prefer \`builtin:firecrawl\` or \`builtin:exa\` over the raw \`fetch-url\`/\`crawl\` flow steps. Those steps request from datacenter IPs flagged as non-human traffic, so sites behind a WAF/anti-bot service often return a 403, CAPTCHA, or empty body. A mixed approach (fetch first, fall back to Firecrawl/Exa on a block) is the most reliable.
42857
42891
  - **Rendered web pages, screenshots, structured extraction, or link harvesting** \u2192 \`builtin:browser:markdown\`, \`builtin:browser:snapshot\`, \`builtin:browser:json\`, \`builtin:browser:scrape\`, \`builtin:browser:links\` (stateless, one-shot \u2014 cheap, no session overhead)
42858
42892
  - **Autonomous browsing (just want the outcome \u2014 extract data, complete a form, find info on a page)** \u2192 \`builtin:browser:agent\` with a natural-language \`goal\` plus \`startUrl\` (fresh browser) or \`sessionId\` (continue an existing session). Stagehand handles observe/act/extract; you get back text or, with an \`outputSchema\`, structured JSON. Try this BEFORE reaching for the manual session tools below.
42859
42893
  - **Interactive browsing (precise control over individual actions)** \u2192 \`builtin:browser:open\` to start a session, then \`builtin:browser:click\`, \`builtin:browser:type\`, \`builtin:browser:scroll\`, \`builtin:browser:session-screenshot\`, \`builtin:browser:evaluate\`, \`builtin:browser:navigate\`, and \`builtin:browser:close\` when done. Session tools maintain cookies and page state across calls but cost more than stateless tools \u2014 prefer stateless tools when a single page read suffices.
@@ -42919,6 +42953,7 @@ Tools are added to prompt steps via \`tools: { toolIds: ["<id>"] }\`. The AI mod
42919
42953
  - **Web search (provider-native, inline citations)** \u2192 see the Provider-Native Search section
42920
42954
  - **Web search (standalone, AI-powered)** \u2192 \`builtin:exa\`
42921
42955
  - **Simple web scraping to markdown** \u2192 \`builtin:firecrawl\`
42956
+ - **Research/scraping a site that blocks bots** \u2192 prefer \`builtin:firecrawl\` or \`builtin:exa\` over the raw \`fetch-url\`/\`crawl\` flow steps. Those steps request from datacenter IPs flagged as non-human traffic, so sites behind a WAF/anti-bot service often return a 403, CAPTCHA, or empty body. A mixed approach (fetch first, fall back to Firecrawl/Exa on a block) is the most reliable.
42922
42957
  - **Rendered web pages, screenshots, structured extraction, or link harvesting** \u2192 \`builtin:browser:markdown\`, \`builtin:browser:snapshot\`, \`builtin:browser:json\`, \`builtin:browser:scrape\`, \`builtin:browser:links\` (stateless, one-shot)
42923
42958
  - **Autonomous browsing (just want the outcome)** \u2192 \`builtin:browser:agent\` with a natural-language \`goal\` plus \`startUrl\` or \`sessionId\` \u2014 try this BEFORE the manual session tools
42924
42959
  - **Interactive browsing (precise control)** \u2192 \`builtin:browser:open\`, then \`builtin:browser:click\` / \`type\` / \`scroll\` / \`session-screenshot\` / \`evaluate\` / \`navigate\`, and \`builtin:browser:close\` when done
@@ -43939,6 +43974,15 @@ var RuntimeProxyToolConfigSchema = external_exports.object({
43939
43974
  toolCallStrategy: external_exports.enum(["auto", "required", "none"]).optional(),
43940
43975
  onToolError: external_exports.enum(["fail", "continue", "retry"]).optional()
43941
43976
  });
43977
+ var RuntimeProxyProviderTransportSchema = external_exports.object({
43978
+ mixlayer: external_exports.object({
43979
+ modelApi: external_exports.enum(["chat", "responses"]).optional(),
43980
+ responsesTransport: external_exports.enum(["http", "websocket"]).optional()
43981
+ }).optional(),
43982
+ openai: external_exports.object({
43983
+ responsesTransport: external_exports.enum(["http", "websocket"]).optional()
43984
+ }).optional()
43985
+ });
43942
43986
  var RuntimeProxyRequestSchema = external_exports.object({
43943
43987
  model: external_exports.string().min(1),
43944
43988
  prompt: external_exports.string().default(""),
@@ -43955,6 +43999,7 @@ var RuntimeProxyRequestSchema = external_exports.object({
43955
43999
  seed: external_exports.number().int().optional(),
43956
44000
  tools: external_exports.array(RuntimeProxyToolSchema).optional(),
43957
44001
  providerTools: external_exports.array(RuntimeProxyProviderToolSchema).optional(),
44002
+ providerTransport: RuntimeProxyProviderTransportSchema.optional(),
43958
44003
  toolConfiguration: RuntimeProxyToolConfigSchema.optional()
43959
44004
  }).optional().default({})
43960
44005
  });
@@ -63990,185 +64035,191 @@ function buildJUnitXml(suites) {
63990
64035
 
63991
64036
  // src/commands/eval.ts
63992
64037
  var evalCommand = new Command20("eval").description("Manage evaluations");
63993
- evalCommand.command("submit").description("Submit an eval batch").requiredOption("-f, --flow <id>", "Flow ID to evaluate").requiredOption("-r, --records <file>", "JSON file with record IDs").option("-n, --name <name>", "Eval batch name").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
63994
- const apiKey = await ensureAuth();
63995
- if (!apiKey) return;
63996
- let recordIds;
63997
- try {
63998
- const content = readFileSync16(options.records, "utf-8");
63999
- const parsed = JSON.parse(content);
64000
- recordIds = Array.isArray(parsed) ? parsed : parsed.recordIds || parsed.records || [];
64001
- } catch (error51) {
64002
- const message = error51 instanceof Error ? error51.message : "Unknown error";
64003
- console.error(chalk27.red(`Failed to read records file: ${message}`));
64004
- process.exit(1);
64005
- return;
64006
- }
64007
- const client = createCliClient(apiKey);
64008
- if (!isTTY(options) || options.json) {
64038
+ evalCommand.command("submit").description("Submit an eval batch").requiredOption("-f, --flow <id>", "Flow ID to evaluate").requiredOption("-r, --records <file>", "JSON file with record IDs").option("-n, --name <name>", "Eval batch name").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(
64039
+ async (options) => {
64040
+ const apiKey = await ensureAuth();
64041
+ if (!apiKey) return;
64042
+ let recordIds;
64009
64043
  try {
64010
- const data = await client.post("/eval/submit", {
64011
- flowId: options.flow,
64012
- recordIds,
64013
- name: options.name
64014
- });
64015
- if (options.json) {
64016
- printJson(data);
64017
- } else {
64018
- console.log(chalk27.green("Eval submitted"));
64019
- console.log(` Batch ID: ${chalk27.green(data.id)}`);
64020
- if (data.name) console.log(` Name: ${data.name}`);
64021
- console.log(` Status: ${data.status}`);
64022
- console.log(` Records: ${data.totalRecords}`);
64023
- if (data.groupId) console.log(` Group: ${data.groupId}`);
64024
- }
64044
+ const content = readFileSync16(options.records, "utf-8");
64045
+ const parsed = JSON.parse(content);
64046
+ recordIds = Array.isArray(parsed) ? parsed : parsed.recordIds || parsed.records || [];
64025
64047
  } catch (error51) {
64026
64048
  const message = error51 instanceof Error ? error51.message : "Unknown error";
64027
- console.error(chalk27.red("Failed to submit eval"));
64028
- console.error(chalk27.red(message));
64049
+ console.error(chalk27.red(`Failed to read records file: ${message}`));
64029
64050
  process.exit(1);
64051
+ return;
64030
64052
  }
64031
- return;
64032
- }
64033
- const App = () => {
64034
- const [loading, setLoading] = useState36(true);
64035
- const [success2, setSuccess] = useState36(null);
64036
- const [error51, setError] = useState36(null);
64037
- const [resultNode, setResultNode] = useState36(void 0);
64038
- useEffect30(() => {
64039
- const run2 = async () => {
64040
- try {
64041
- const data = await client.post("/eval/submit", {
64042
- flowId: options.flow,
64043
- recordIds,
64044
- name: options.name
64045
- });
64046
- const fields = [
64047
- { label: "Batch ID", value: data.id, color: "green" }
64048
- ];
64049
- if (data.name) fields.push({ label: "Name", value: data.name });
64050
- fields.push({ label: "Status", value: data.status });
64051
- fields.push({ label: "Records", value: data.totalRecords });
64052
- if (data.groupId) fields.push({ label: "Group", value: data.groupId });
64053
- setResultNode(React19.createElement(EntityCard, { fields }));
64054
- setSuccess(true);
64055
- setLoading(false);
64056
- } catch (err) {
64057
- setError(err instanceof Error ? err : new Error(String(err)));
64058
- setSuccess(false);
64059
- setLoading(false);
64060
- }
64061
- };
64062
- run2();
64063
- }, []);
64064
- return React19.createElement(MutationResult, {
64065
- loading,
64066
- loadingLabel: `Submitting eval with ${recordIds.length} records...`,
64067
- success: success2,
64068
- successMessage: "Eval submitted",
64069
- error: error51,
64070
- result: resultNode
64071
- });
64072
- };
64073
- const { waitUntilExit } = render19(React19.createElement(App));
64074
- await waitUntilExit();
64075
- });
64076
- evalCommand.command("list").description("List eval batches").option("--flow <id>", "Filter by flow ID").option("--limit <n>", "Limit results", "20").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
64077
- const apiKey = await ensureAuth();
64078
- if (!apiKey) return;
64079
- const client = createCliClient(apiKey);
64080
- const params = { limit: options.limit };
64081
- if (options.flow) params.flowId = options.flow;
64082
- if (!isTTY(options) || options.json) {
64083
- try {
64084
- const data = await client.get("/eval/batches", params);
64085
- if (options.json) {
64086
- printJson(data);
64087
- } else {
64088
- const batches = data.data ?? [];
64089
- if (batches.length === 0) {
64090
- console.log(chalk27.gray("No eval batches found"));
64091
- return;
64092
- }
64093
- console.log(chalk27.cyan("Eval Batches:"));
64094
- for (const batch of batches) {
64095
- const name = batch.name || batch.id;
64096
- const progress = batch.totalRecords ? `${batch.completedRecords ?? 0}/${batch.totalRecords}` : "";
64097
- const statusColor = batch.status === "completed" ? "green" : "yellow";
64098
- console.log(
64099
- ` ${chalk27.green(batch.id)} ${name} ${chalk27[statusColor](`[${batch.status}]`)} ${chalk27.gray(progress)}`
64100
- );
64101
- }
64102
- const total = getTotalCount(data.pagination);
64103
- if (total !== void 0) {
64104
- console.log(chalk27.dim(`
64105
- Total: ${total} batches`));
64053
+ const client = createCliClient(apiKey);
64054
+ if (!isTTY(options) || options.json) {
64055
+ try {
64056
+ const data = await client.post("/eval/submit", {
64057
+ flowId: options.flow,
64058
+ recordIds,
64059
+ name: options.name
64060
+ });
64061
+ if (options.json) {
64062
+ printJson(data);
64063
+ } else {
64064
+ console.log(chalk27.green("Eval submitted"));
64065
+ console.log(` Batch ID: ${chalk27.green(data.id)}`);
64066
+ if (data.name) console.log(` Name: ${data.name}`);
64067
+ console.log(` Status: ${data.status}`);
64068
+ console.log(` Records: ${data.totalRecords}`);
64069
+ if (data.groupId) console.log(` Group: ${data.groupId}`);
64106
64070
  }
64071
+ } catch (error51) {
64072
+ const message = error51 instanceof Error ? error51.message : "Unknown error";
64073
+ console.error(chalk27.red("Failed to submit eval"));
64074
+ console.error(chalk27.red(message));
64075
+ process.exit(1);
64107
64076
  }
64108
- } catch (error51) {
64109
- const message = error51 instanceof Error ? error51.message : "Unknown error";
64110
- console.error(chalk27.red("Failed to fetch eval batches"));
64111
- console.error(chalk27.red(message));
64112
- process.exit(1);
64077
+ return;
64113
64078
  }
64114
- return;
64079
+ const App = () => {
64080
+ const [loading, setLoading] = useState36(true);
64081
+ const [success2, setSuccess] = useState36(null);
64082
+ const [error51, setError] = useState36(null);
64083
+ const [resultNode, setResultNode] = useState36(void 0);
64084
+ useEffect30(() => {
64085
+ const run2 = async () => {
64086
+ try {
64087
+ const data = await client.post("/eval/submit", {
64088
+ flowId: options.flow,
64089
+ recordIds,
64090
+ name: options.name
64091
+ });
64092
+ const fields = [{ label: "Batch ID", value: data.id, color: "green" }];
64093
+ if (data.name) fields.push({ label: "Name", value: data.name });
64094
+ fields.push({ label: "Status", value: data.status });
64095
+ fields.push({ label: "Records", value: data.totalRecords });
64096
+ if (data.groupId) fields.push({ label: "Group", value: data.groupId });
64097
+ setResultNode(React19.createElement(EntityCard, { fields }));
64098
+ setSuccess(true);
64099
+ setLoading(false);
64100
+ } catch (err) {
64101
+ setError(err instanceof Error ? err : new Error(String(err)));
64102
+ setSuccess(false);
64103
+ setLoading(false);
64104
+ }
64105
+ };
64106
+ run2();
64107
+ }, []);
64108
+ return React19.createElement(MutationResult, {
64109
+ loading,
64110
+ loadingLabel: `Submitting eval with ${recordIds.length} records...`,
64111
+ success: success2,
64112
+ successMessage: "Eval submitted",
64113
+ error: error51,
64114
+ result: resultNode
64115
+ });
64116
+ };
64117
+ const { waitUntilExit } = render19(React19.createElement(App));
64118
+ await waitUntilExit();
64115
64119
  }
64116
- const App = () => {
64117
- const [loading, setLoading] = useState36(true);
64118
- const [items, setItems] = useState36(null);
64119
- const [total, setTotal] = useState36(void 0);
64120
- const [error51, setError] = useState36(null);
64121
- useEffect30(() => {
64122
- const run2 = async () => {
64123
- try {
64124
- const data = await client.get("/eval/batches", params);
64125
- setItems(data.data ?? []);
64126
- setTotal(getTotalCount(data.pagination));
64127
- setLoading(false);
64128
- } catch (err) {
64129
- setError(err instanceof Error ? err : new Error(String(err)));
64130
- setLoading(false);
64120
+ );
64121
+ evalCommand.command("list").description("List eval batches").option("--flow <id>", "Filter by flow ID").option("--limit <n>", "Limit results", "20").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(
64122
+ async (options) => {
64123
+ const apiKey = await ensureAuth();
64124
+ if (!apiKey) return;
64125
+ const client = createCliClient(apiKey);
64126
+ const params = { limit: options.limit };
64127
+ if (options.flow) params.flowId = options.flow;
64128
+ if (!isTTY(options) || options.json) {
64129
+ try {
64130
+ const data = await client.get("/eval/batches", params);
64131
+ if (options.json) {
64132
+ printJson(data);
64133
+ } else {
64134
+ const batches = data.data ?? [];
64135
+ if (batches.length === 0) {
64136
+ console.log(chalk27.gray("No eval batches found"));
64137
+ return;
64138
+ }
64139
+ console.log(chalk27.cyan("Eval Batches:"));
64140
+ for (const batch of batches) {
64141
+ const name = batch.name || batch.id;
64142
+ const progress = batch.totalRecords ? `${batch.completedRecords ?? 0}/${batch.totalRecords}` : "";
64143
+ const statusColor = batch.status === "completed" ? "green" : "yellow";
64144
+ console.log(
64145
+ ` ${chalk27.green(batch.id)} ${name} ${chalk27[statusColor](`[${batch.status}]`)} ${chalk27.gray(progress)}`
64146
+ );
64147
+ }
64148
+ const total = getTotalCount(data.pagination);
64149
+ if (total !== void 0) {
64150
+ console.log(chalk27.dim(`
64151
+ Total: ${total} batches`));
64152
+ }
64131
64153
  }
64132
- };
64133
- run2();
64134
- }, []);
64135
- return React19.createElement(DataList, {
64136
- title: "Eval Batches",
64137
- items,
64138
- error: error51,
64139
- loading,
64140
- total,
64141
- emptyMessage: "No eval batches found",
64142
- renderCard: (item) => {
64143
- const b = item;
64144
- const name = b.name || b.id;
64145
- const progress = b.totalRecords ? `${b.completedRecords ?? 0}/${b.totalRecords}` : "";
64146
- const statusColor = b.status === "completed" ? "green" : "yellow";
64147
- return React19.createElement(
64148
- Text34,
64149
- { color: statusColor },
64150
- ` ${b.id} ${name} [${b.status}] ${progress}`
64151
- );
64154
+ } catch (error51) {
64155
+ const message = error51 instanceof Error ? error51.message : "Unknown error";
64156
+ console.error(chalk27.red("Failed to fetch eval batches"));
64157
+ console.error(chalk27.red(message));
64158
+ process.exit(1);
64152
64159
  }
64153
- });
64154
- };
64155
- const { waitUntilExit } = render19(React19.createElement(App));
64156
- await waitUntilExit();
64157
- });
64160
+ return;
64161
+ }
64162
+ const App = () => {
64163
+ const [loading, setLoading] = useState36(true);
64164
+ const [items, setItems] = useState36(null);
64165
+ const [total, setTotal] = useState36(void 0);
64166
+ const [error51, setError] = useState36(null);
64167
+ useEffect30(() => {
64168
+ const run2 = async () => {
64169
+ try {
64170
+ const data = await client.get("/eval/batches", params);
64171
+ setItems(data.data ?? []);
64172
+ setTotal(getTotalCount(data.pagination));
64173
+ setLoading(false);
64174
+ } catch (err) {
64175
+ setError(err instanceof Error ? err : new Error(String(err)));
64176
+ setLoading(false);
64177
+ }
64178
+ };
64179
+ run2();
64180
+ }, []);
64181
+ return React19.createElement(DataList, {
64182
+ title: "Eval Batches",
64183
+ items,
64184
+ error: error51,
64185
+ loading,
64186
+ total,
64187
+ emptyMessage: "No eval batches found",
64188
+ renderCard: (item) => {
64189
+ const b = item;
64190
+ const name = b.name || b.id;
64191
+ const progress = b.totalRecords ? `${b.completedRecords ?? 0}/${b.totalRecords}` : "";
64192
+ const statusColor = b.status === "completed" ? "green" : "yellow";
64193
+ return React19.createElement(
64194
+ Text34,
64195
+ { color: statusColor },
64196
+ ` ${b.id} ${name} [${b.status}] ${progress}`
64197
+ );
64198
+ }
64199
+ });
64200
+ };
64201
+ const { waitUntilExit } = render19(React19.createElement(App));
64202
+ await waitUntilExit();
64203
+ }
64204
+ );
64158
64205
  evalCommand.command("results <id>").description("Get eval batch results").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
64159
64206
  const apiKey = await ensureAuth();
64160
64207
  if (!apiKey) return;
64161
64208
  const client = createCliClient(apiKey);
64162
64209
  if (!isTTY(options) || options.json) {
64163
64210
  try {
64164
- const data = await client.get(`/eval/${id}/results`);
64211
+ const data = await client.get(
64212
+ `/eval/${id}/results`
64213
+ );
64165
64214
  if (options.json) {
64166
64215
  printJson(data);
64167
64216
  } else {
64168
64217
  if (data.batch) {
64169
64218
  console.log(chalk27.cyan(`Eval: ${data.batch.name || data.batch.id}`));
64170
64219
  console.log(` Status: ${data.batch.status}`);
64171
- console.log(` Progress: ${data.batch.completedRecords ?? 0}/${data.batch.totalRecords ?? 0}`);
64220
+ console.log(
64221
+ ` Progress: ${data.batch.completedRecords ?? 0}/${data.batch.totalRecords ?? 0}`
64222
+ );
64172
64223
  console.log();
64173
64224
  }
64174
64225
  const results = data.data ?? [];
@@ -64201,19 +64252,28 @@ evalCommand.command("results <id>").description("Get eval batch results").option
64201
64252
  useEffect30(() => {
64202
64253
  const run2 = async () => {
64203
64254
  try {
64204
- const data = await client.get(`/eval/${id}/results`);
64255
+ const data = await client.get(
64256
+ `/eval/${id}/results`
64257
+ );
64205
64258
  const results = data.data ?? [];
64206
64259
  const fields = [];
64207
64260
  if (data.batch) {
64208
64261
  fields.push({ label: "Eval", value: data.batch.name || data.batch.id });
64209
64262
  fields.push({ label: "Status", value: data.batch.status });
64210
- fields.push({ label: "Progress", value: `${data.batch.completedRecords ?? 0}/${data.batch.totalRecords ?? 0}` });
64263
+ fields.push({
64264
+ label: "Progress",
64265
+ value: `${data.batch.completedRecords ?? 0}/${data.batch.totalRecords ?? 0}`
64266
+ });
64211
64267
  }
64212
64268
  fields.push({ label: "Results", value: results.length });
64213
64269
  if (results.length > 0) {
64214
64270
  const completed = results.filter((r) => r.status === "completed").length;
64215
64271
  const avgScore = results.filter((r) => r.score !== void 0).reduce((sum, r) => sum + (r.score ?? 0), 0) / (results.filter((r) => r.score !== void 0).length || 1);
64216
- fields.push({ label: "Completed", value: `${completed}/${results.length}`, color: "green" });
64272
+ fields.push({
64273
+ label: "Completed",
64274
+ value: `${completed}/${results.length}`,
64275
+ color: "green"
64276
+ });
64217
64277
  if (results.some((r) => r.score !== void 0)) {
64218
64278
  fields.push({ label: "Avg Score", value: avgScore.toFixed(2) });
64219
64279
  }
@@ -64309,6 +64369,13 @@ function printSuiteResult(rootDir, outcome) {
64309
64369
  if (testCase.errored) {
64310
64370
  console.log(chalk27.red(` \u2717 errored: ${testCase.outputExcerpt.slice(0, 200)}`));
64311
64371
  }
64372
+ } else {
64373
+ for (const outcomeItem of testCase.outcomes.filter(
64374
+ (o) => !o.passed && o.severity === "soft"
64375
+ )) {
64376
+ const reason = outcomeItem.reasoning ? `: ${outcomeItem.reasoning}` : "";
64377
+ console.log(chalk27.yellow(` \u26A0 soft ${outcomeItem.kind}${reason}`));
64378
+ }
64312
64379
  }
64313
64380
  }
64314
64381
  }
@@ -64323,7 +64390,12 @@ function toJUnitSuite(outcome) {
64323
64390
  }))
64324
64391
  };
64325
64392
  }
64326
- evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval suites (**/*.eval.ts) as a CI gate (exit 0 pass / 1 fail / 2 config)").option("--strict", "Fail on soft-threshold misses too (no-op until severity lands)").option("--virtual", "Run inline without persisting a suite/batch to the dashboard").option("--junit <path>", "Write JUnit XML results to <path>").option("--url <api>", "Override the API base URL (e.g. staging)").option("--cwd <dir>", "Directory to discover *.eval.ts under (default: current directory)").action(
64393
+ evalCommand.command("run [idOrDirPrefix]").description(
64394
+ "Run code-colocated eval suites (**/*.eval.ts) as a CI gate (exit 0 pass / 1 fail / 2 config)"
64395
+ ).option(
64396
+ "--strict",
64397
+ "Fail the exit code on soft grader misses too (default: soft misses are reported but do not fail)"
64398
+ ).option("--virtual", "Run inline without persisting a suite/batch to the dashboard").option("--junit <path>", "Write JUnit XML results to <path>").option("--url <api>", "Override the API base URL (e.g. staging)").option("--cwd <dir>", "Directory to discover *.eval.ts under (default: current directory)").action(
64327
64399
  async (idOrDirPrefix, options) => {
64328
64400
  const apiKey = await ensureAuth();
64329
64401
  if (!apiKey) {
@@ -64361,11 +64433,6 @@ evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval
64361
64433
  } else {
64362
64434
  loaded = await loadAll(allFiles);
64363
64435
  }
64364
- if (options.strict) {
64365
- console.log(
64366
- chalk27.gray("Note: --strict has no effect yet (grader severity lands in a later increment).")
64367
- );
64368
- }
64369
64436
  const client = createCliClient(apiKey, options.url);
64370
64437
  const outcomes = [];
64371
64438
  for (const { file: file2, def } of loaded) {
@@ -64373,16 +64440,25 @@ evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval
64373
64440
  try {
64374
64441
  let result;
64375
64442
  if (runVirtual) {
64376
- result = await client.post("/eval/run", { definition: def });
64377
- } else {
64378
- const ensured = await client.post("/eval/ensure", {
64379
- name: def.name,
64380
- definition: def
64443
+ result = await client.post("/eval/run", {
64444
+ definition: def,
64445
+ strict: options.strict ?? false
64381
64446
  });
64447
+ } else {
64448
+ const ensured = await client.post(
64449
+ "/eval/ensure",
64450
+ {
64451
+ name: def.name,
64452
+ definition: def
64453
+ }
64454
+ );
64382
64455
  if (!ensured.suiteId) {
64383
64456
  throw new Error(`ensure did not return a suiteId (result: ${ensured.result})`);
64384
64457
  }
64385
- result = await client.post("/eval/run", { suiteId: ensured.suiteId });
64458
+ result = await client.post("/eval/run", {
64459
+ suiteId: ensured.suiteId,
64460
+ strict: options.strict ?? false
64461
+ });
64386
64462
  }
64387
64463
  outcomes.push({ file: file2, definition: def, result });
64388
64464
  printSuiteResult(rootDir, { file: file2, definition: def, result });
@@ -64400,7 +64476,9 @@ evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval
64400
64476
  writeFileSync6(outPath, xml, "utf-8");
64401
64477
  console.log(chalk27.gray(`JUnit results written to ${options.junit}`));
64402
64478
  } catch (error51) {
64403
- failConfig(`Failed to write JUnit report: ${error51 instanceof Error ? error51.message : String(error51)}`);
64479
+ failConfig(
64480
+ `Failed to write JUnit report: ${error51 instanceof Error ? error51.message : String(error51)}`
64481
+ );
64404
64482
  }
64405
64483
  }
64406
64484
  const failedSuites = outcomes.filter((o) => !o.result.passed);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/cli",
3
- "version": "2.23.1",
3
+ "version": "2.24.1",
4
4
  "description": "Command-line interface for Runtype AI platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "rosie-skills": "0.8.1",
25
25
  "yaml": "^2.9.0",
26
26
  "@runtypelabs/ink-components": "0.3.4",
27
- "@runtypelabs/sdk": "5.5.0",
27
+ "@runtypelabs/sdk": "5.7.0",
28
28
  "@runtypelabs/terminal-animations": "0.2.1"
29
29
  },
30
30
  "devDependencies": {
@@ -39,7 +39,7 @@
39
39
  "tsx": "^4.7.1",
40
40
  "typescript": "^6.0.3",
41
41
  "vitest": "^4.1.0",
42
- "@runtypelabs/shared": "1.42.5"
42
+ "@runtypelabs/shared": "1.43.1"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=22.0.0"