@wayai/cli 0.3.47 → 0.3.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -343,6 +343,16 @@ var init_api_client = __esm({
343
343
  if (opts?.limit) params.set("limit", String(opts.limit));
344
344
  return this.request("GET", `/api/evals/sessions/${sessionId}/runs?${params.toString()}`);
345
345
  }
346
+ /** Delete a single eval session (cascades runs/results/conversations server-side). */
347
+ async deleteEvalSession(hubId, sessionId) {
348
+ const params = new URLSearchParams({ hub_id: hubId });
349
+ return this.request("DELETE", `/api/evals/sessions/${sessionId}?${params.toString()}`);
350
+ }
351
+ /** Delete ALL eval sessions for a hub (each cascades server-side). */
352
+ async deleteAllEvalSessions(hubId) {
353
+ const params = new URLSearchParams({ hub_id: hubId });
354
+ return this.request("DELETE", `/api/evals/sessions?${params.toString()}`);
355
+ }
346
356
  async listScenarioSets(hubId) {
347
357
  const params = new URLSearchParams({ hub_id: hubId });
348
358
  return this.request("GET", `/api/evals/scenario-sets?${params.toString()}`);
@@ -5031,6 +5041,22 @@ function slugifyKebab(input, maxLength = 64) {
5031
5041
  if (typeof input !== "string") return "";
5032
5042
  return foldDiacritics(input).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").slice(0, maxLength).replace(/^-|-$/g, "");
5033
5043
  }
5044
+ function ensureUniqueSlug(slug, used) {
5045
+ if (!used.has(slug)) {
5046
+ used.add(slug);
5047
+ return slug;
5048
+ }
5049
+ for (let i = 2; i < 1e6; i++) {
5050
+ const suffix = `_${i}`;
5051
+ const baseLen = Math.max(1, 50 - suffix.length);
5052
+ const candidate = `${slug.slice(0, baseLen)}${suffix}`;
5053
+ if (!used.has(candidate)) {
5054
+ used.add(candidate);
5055
+ return candidate;
5056
+ }
5057
+ }
5058
+ throw new Error(`slug collision space exhausted for "${slug}"`);
5059
+ }
5034
5060
  function slugifySkillName(name) {
5035
5061
  return slugifyKebab(name, 64);
5036
5062
  }
@@ -9508,6 +9534,7 @@ function writeHubFolder(hubFolder, payload) {
9508
9534
  }
9509
9535
  writeResourceFiles(hubFolder, payload.resources || []);
9510
9536
  writeEvalYamlFiles(hubFolder, payload.evals || []);
9537
+ writeJourneyYamlFiles(hubFolder, payload.journeys || []);
9511
9538
  }
9512
9539
  function buildYamlPayload(payload) {
9513
9540
  const result = {
@@ -9634,6 +9661,60 @@ function cleanEvalOrphans(evalsDir, writtenRelPaths) {
9634
9661
  }
9635
9662
  }
9636
9663
  }
9664
+ function buildJourneyYamlObject(journeyEntry, slug) {
9665
+ const out = {};
9666
+ if (journeyEntry.id) out.id = journeyEntry.id;
9667
+ if (journeyEntry.name !== slug) out.name = journeyEntry.name;
9668
+ out.agent = journeyEntry.agent;
9669
+ if (journeyEntry.agent_id) out.agent_id = journeyEntry.agent_id;
9670
+ if (journeyEntry.enabled === false) {
9671
+ out.enabled = false;
9672
+ }
9673
+ if (journeyEntry.evaluator_instructions) {
9674
+ out.evaluator_instructions = journeyEntry.evaluator_instructions;
9675
+ }
9676
+ if (journeyEntry.transcript && journeyEntry.transcript.length > 0) {
9677
+ out.transcript = journeyEntry.transcript;
9678
+ }
9679
+ if (journeyEntry.step_config && Object.keys(journeyEntry.step_config).length > 0) {
9680
+ out.step_config = journeyEntry.step_config;
9681
+ }
9682
+ return out;
9683
+ }
9684
+ function writeJourneyYamlFiles(hubFolder, journeys) {
9685
+ const journeysDir = path11.join(hubFolder, "journeys");
9686
+ if (journeys.length === 0) {
9687
+ if (fs9.existsSync(journeysDir)) {
9688
+ cleanJourneyOrphans(journeysDir, /* @__PURE__ */ new Set());
9689
+ try {
9690
+ if (fs9.readdirSync(journeysDir).length === 0) fs9.rmdirSync(journeysDir);
9691
+ } catch {
9692
+ }
9693
+ }
9694
+ return;
9695
+ }
9696
+ if (!fs9.existsSync(journeysDir)) {
9697
+ fs9.mkdirSync(journeysDir, { recursive: true });
9698
+ }
9699
+ const writtenFiles = /* @__PURE__ */ new Set();
9700
+ const usedSlugs = /* @__PURE__ */ new Set();
9701
+ for (const journeyEntry of journeys) {
9702
+ const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
9703
+ const fileName = `${slug}.yaml`;
9704
+ const yamlContent = yaml4.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
9705
+ fs9.writeFileSync(path11.join(journeysDir, fileName), yamlContent, "utf-8");
9706
+ writtenFiles.add(fileName);
9707
+ }
9708
+ cleanJourneyOrphans(journeysDir, writtenFiles);
9709
+ }
9710
+ function cleanJourneyOrphans(journeysDir, writtenFiles) {
9711
+ const entries = fs9.readdirSync(journeysDir, { withFileTypes: true });
9712
+ for (const entry of entries) {
9713
+ if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
9714
+ fs9.unlinkSync(path11.join(journeysDir, entry.name));
9715
+ }
9716
+ }
9717
+ }
9637
9718
  function writeResourceFiles(hubFolder, resources) {
9638
9719
  const resourcesDir = path11.join(hubFolder, "resources");
9639
9720
  const currentSlugs = /* @__PURE__ */ new Set();
@@ -9656,6 +9737,7 @@ var YAML_DUMP_OPTIONS;
9656
9737
  var init_yaml_writer = __esm({
9657
9738
  "src/lib/yaml-writer.ts"() {
9658
9739
  "use strict";
9740
+ init_dist();
9659
9741
  init_utils();
9660
9742
  init_resource_files();
9661
9743
  YAML_DUMP_OPTIONS = {
@@ -9777,6 +9859,10 @@ function parseHubFolder(hubFolder, opts) {
9777
9859
  if (evals.length > 0) {
9778
9860
  payload.evals = evals;
9779
9861
  }
9862
+ const journeys = scanJourneyYamlFiles(hubFolder);
9863
+ if (journeys.length > 0) {
9864
+ payload.journeys = journeys;
9865
+ }
9780
9866
  return payload;
9781
9867
  }
9782
9868
  function scanEvalYamlFiles(hubFolder) {
@@ -9865,6 +9951,76 @@ function parseEvalYaml(filePath, relPath, setName) {
9865
9951
  }
9866
9952
  return evalEntry;
9867
9953
  }
9954
+ function scanJourneyYamlFiles(hubFolder) {
9955
+ const journeysDir = path12.join(hubFolder, "journeys");
9956
+ if (!fs10.existsSync(journeysDir)) return [];
9957
+ const journeys = [];
9958
+ const seen = /* @__PURE__ */ new Set();
9959
+ const entries = fs10.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
9960
+ for (const entry of entries) {
9961
+ if (entry.isDirectory()) {
9962
+ throw expected(
9963
+ `Subfolders are not supported under journeys/: ${path12.relative(hubFolder, path12.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
9964
+ );
9965
+ }
9966
+ if (!entry.isFile() || !entry.name.endsWith(".yaml")) continue;
9967
+ const journeyEntry = parseJourneyYaml(path12.join(journeysDir, entry.name), `journeys/${entry.name}`);
9968
+ if (!journeyEntry) continue;
9969
+ if (seen.has(journeyEntry.name)) {
9970
+ throw expected(
9971
+ `Duplicate journey: "${journeyEntry.name}" (in ${entry.name}). Each journey name must be unique.`
9972
+ );
9973
+ }
9974
+ seen.add(journeyEntry.name);
9975
+ journeys.push(journeyEntry);
9976
+ }
9977
+ return journeys;
9978
+ }
9979
+ function parseJourneyYaml(filePath, relPath) {
9980
+ const content = fs10.readFileSync(filePath, "utf-8");
9981
+ let raw;
9982
+ try {
9983
+ raw = yaml5.load(content);
9984
+ } catch (err) {
9985
+ if (err instanceof yaml5.YAMLException) {
9986
+ throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
9987
+ }
9988
+ throw err;
9989
+ }
9990
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
9991
+ console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
9992
+ return null;
9993
+ }
9994
+ const data = raw;
9995
+ const fileSlug = path12.basename(filePath, ".yaml");
9996
+ const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
9997
+ if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
9998
+ throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
9999
+ }
10000
+ if (data.transcript !== void 0 && !Array.isArray(data.transcript)) {
10001
+ throw expected(`Journey "${journeyName}" in ${relPath}: "transcript" must be an array.`);
10002
+ }
10003
+ if (data.step_config !== void 0 && (typeof data.step_config !== "object" || Array.isArray(data.step_config) || data.step_config === null)) {
10004
+ throw expected(`Journey "${journeyName}" in ${relPath}: "step_config" must be an object keyed by step_id.`);
10005
+ }
10006
+ const journeyEntry = {
10007
+ name: journeyName,
10008
+ agent: data.agent
10009
+ };
10010
+ if (typeof data.id === "string") journeyEntry.id = data.id;
10011
+ if (typeof data.agent_id === "string") journeyEntry.agent_id = data.agent_id;
10012
+ if (data.enabled === false) journeyEntry.enabled = false;
10013
+ if (typeof data.evaluator_instructions === "string") {
10014
+ journeyEntry.evaluator_instructions = data.evaluator_instructions;
10015
+ }
10016
+ if (Array.isArray(data.transcript)) {
10017
+ journeyEntry.transcript = data.transcript;
10018
+ }
10019
+ if (data.step_config && typeof data.step_config === "object" && !Array.isArray(data.step_config)) {
10020
+ journeyEntry.step_config = data.step_config;
10021
+ }
10022
+ return journeyEntry;
10023
+ }
9868
10024
  function scanAgentYamlFiles(hubFolder) {
9869
10025
  const agentsDir = path12.join(hubFolder, "agents");
9870
10026
  if (!fs10.existsSync(agentsDir)) return [];
@@ -10179,6 +10335,12 @@ Sync complete. Preview hub: ${result.preview_hub_id}`);
10179
10335
  if (c.states_created > 0) parts.push(`${c.states_created} state(s) created`);
10180
10336
  if (c.states_updated > 0) parts.push(`${c.states_updated} state(s) updated`);
10181
10337
  if (c.states_deleted > 0) parts.push(`${c.states_deleted} state(s) deleted`);
10338
+ if (c.evals_created > 0) parts.push(`${c.evals_created} eval(s) created`);
10339
+ if (c.evals_updated > 0) parts.push(`${c.evals_updated} eval(s) updated`);
10340
+ if (c.evals_deleted > 0) parts.push(`${c.evals_deleted} eval(s) deleted`);
10341
+ if (c.journeys_created > 0) parts.push(`${c.journeys_created} journey(s) created`);
10342
+ if (c.journeys_updated > 0) parts.push(`${c.journeys_updated} journey(s) updated`);
10343
+ if (c.journeys_deleted > 0) parts.push(`${c.journeys_deleted} journey(s) deleted`);
10182
10344
  if (c.teams_created > 0) parts.push(`${c.teams_created} team(s) created`);
10183
10345
  if (c.teams_updated > 0) parts.push(`${c.teams_updated} team(s) updated`);
10184
10346
  if (c.teams_deleted > 0) parts.push(`${c.teams_deleted} team(s) deleted`);
@@ -12048,6 +12210,80 @@ var init_journey_capture = __esm({
12048
12210
  }
12049
12211
  });
12050
12212
 
12213
+ // src/commands/eval-session-delete.ts
12214
+ var eval_session_delete_exports = {};
12215
+ __export(eval_session_delete_exports, {
12216
+ evalSessionDeleteCommand: () => evalSessionDeleteCommand
12217
+ });
12218
+ async function evalSessionDeleteCommand(args2) {
12219
+ const hubId = resolveActiveHubId(args2);
12220
+ const { config, accessToken } = await requireAuth();
12221
+ requireRepoConfig();
12222
+ const skipConfirm = args2.includes("-y") || args2.includes("--yes");
12223
+ const deleteAll = args2.includes("--all");
12224
+ const sessionId = args2.find((a) => !a.startsWith("-"));
12225
+ if (deleteAll && sessionId) {
12226
+ console.error("Pass either a session id or --all, not both.");
12227
+ process.exit(1);
12228
+ }
12229
+ if (!deleteAll && !sessionId) {
12230
+ console.error("Usage: wayai eval session delete <session_id>");
12231
+ console.error(" wayai eval session delete --all");
12232
+ process.exit(1);
12233
+ }
12234
+ if (sessionId && !UUID_RE.test(sessionId)) {
12235
+ console.error(`Invalid session id: ${sessionId}`);
12236
+ process.exit(1);
12237
+ }
12238
+ const client = new ApiClient({ apiUrl: config.api_url, accessToken });
12239
+ if (deleteAll) {
12240
+ if (!skipConfirm) {
12241
+ const confirmed = await confirm(
12242
+ "This will permanently delete ALL eval sessions on this hub and their conversations. Continue?"
12243
+ );
12244
+ if (!confirmed) {
12245
+ console.log("Aborted.");
12246
+ return;
12247
+ }
12248
+ }
12249
+ const result = await client.deleteAllEvalSessions(hubId);
12250
+ console.log(
12251
+ `Deleted ${result.data.deleted_sessions} session(s) and ${result.data.deleted_conversations} eval conversation(s).`
12252
+ );
12253
+ return;
12254
+ }
12255
+ if (!skipConfirm) {
12256
+ const confirmed = await confirm(
12257
+ "This will permanently delete this eval session and its conversations. Continue?"
12258
+ );
12259
+ if (!confirmed) {
12260
+ console.log("Aborted.");
12261
+ return;
12262
+ }
12263
+ }
12264
+ try {
12265
+ const result = await client.deleteEvalSession(hubId, sessionId);
12266
+ const deletedConversations = result.data?.deleted_conversations ?? 0;
12267
+ console.log(`Deleted session ${sessionId} (${deletedConversations} eval conversation(s)).`);
12268
+ } catch (err) {
12269
+ if (err instanceof ApiError && err.status === 404) {
12270
+ console.error(`Eval session not found: ${sessionId}`);
12271
+ process.exit(1);
12272
+ }
12273
+ throw err;
12274
+ }
12275
+ }
12276
+ var init_eval_session_delete = __esm({
12277
+ "src/commands/eval-session-delete.ts"() {
12278
+ "use strict";
12279
+ init_auth();
12280
+ init_repo_config();
12281
+ init_workspace();
12282
+ init_api_client();
12283
+ init_utils();
12284
+ }
12285
+ });
12286
+
12051
12287
  // src/commands/eval.ts
12052
12288
  var eval_exports = {};
12053
12289
  __export(eval_exports, {
@@ -12077,6 +12313,18 @@ async function evalCommand(args2) {
12077
12313
  process.exit(1);
12078
12314
  return;
12079
12315
  }
12316
+ case "session": {
12317
+ const [sessionSub, ...sessionRest] = rest;
12318
+ if (sessionSub === "delete") {
12319
+ const { evalSessionDeleteCommand: evalSessionDeleteCommand2 } = await Promise.resolve().then(() => (init_eval_session_delete(), eval_session_delete_exports));
12320
+ await evalSessionDeleteCommand2(sessionRest);
12321
+ return;
12322
+ }
12323
+ console.error(`Unknown eval session subcommand: ${sessionSub ?? "(none)"}`);
12324
+ printHelp();
12325
+ process.exit(1);
12326
+ return;
12327
+ }
12080
12328
  case "help":
12081
12329
  case "--help":
12082
12330
  case "-h":
@@ -12100,6 +12348,9 @@ Subcommands:
12100
12348
  exchange as a scenario YAML in evals/.
12101
12349
  journey capture <conversation_id> Capture a real conversation's FULL
12102
12350
  transcript as a platform-managed journey.
12351
+ session delete <session_id> Delete one eval session (and its
12352
+ conversations). Destructive, irreversible.
12353
+ session delete --all Delete every eval session on the hub.
12103
12354
 
12104
12355
  Capture flags (scenario):
12105
12356
  --set <name> Scenario set folder (created if missing). Default: "Default".
@@ -12110,10 +12361,16 @@ Journey capture flags:
12110
12361
  --name <journey_name> Journey name. Default: derived from conversation ID.
12111
12362
  --instructions <text> Default evaluator instructions for the journey's steps.
12112
12363
 
12364
+ Session delete flags:
12365
+ --all Delete ALL sessions on the hub instead of one.
12366
+ -y, --yes Skip the confirmation prompt.
12367
+
12113
12368
  Examples:
12114
12369
  wayai eval capture 11111111-2222-3333-4444-555555555555
12115
12370
  wayai eval capture <conv_id> --set regression-suite --name "Cancel order"
12116
12371
  wayai eval journey capture <conv_id> --name "Refund happy path"
12372
+ wayai eval session delete 11111111-2222-3333-4444-555555555555
12373
+ wayai eval session delete --all -y
12117
12374
 
12118
12375
  Authoring evals (create/update/delete) is file-driven \u2014 edit YAML in
12119
12376
  evals/ and run \`wayai push\`. Capture commands are for production replay only.
@@ -17373,6 +17630,7 @@ Commands:
17373
17630
  run-eval Run all enabled evals and show results
17374
17631
  eval-results View results from a completed eval session
17375
17632
  eval capture Capture a real conversation as a scenario YAML
17633
+ eval session delete Delete one eval session (\`--all\` for every session on the hub)
17376
17634
  sync-skills Sync skills to Anthropic/OpenAI provider connections
17377
17635
  sync-mcp Re-discover an MCP connection's tools (refresh stale schemas)
17378
17636
  list List organizations and hubs