@wayai/cli 0.3.105 → 0.3.107

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
@@ -441,11 +441,11 @@ function captureException2(error, context) {
441
441
  Sentry.captureException(error);
442
442
  });
443
443
  }
444
- function addApiBreadcrumb(method, path30) {
444
+ function addApiBreadcrumb(method, path31) {
445
445
  if (!initialized) return;
446
446
  Sentry.addBreadcrumb({
447
447
  category: "http",
448
- message: `${method} ${path30}`,
448
+ message: `${method} ${path31}`,
449
449
  level: "info"
450
450
  });
451
451
  }
@@ -507,9 +507,9 @@ var init_api_client = __esm({
507
507
  ApiError = class extends Error {
508
508
  status;
509
509
  body;
510
- constructor(method, path30, status, body) {
510
+ constructor(method, path31, status, body) {
511
511
  const safeBody = maskSecretsInMessage(body);
512
- super(`API request failed: ${method} ${path30} (${status}): ${safeBody}`);
512
+ super(`API request failed: ${method} ${path31} (${status}): ${safeBody}`);
513
513
  this.name = "ApiError";
514
514
  this.status = status;
515
515
  this.body = safeBody;
@@ -657,8 +657,8 @@ var init_api_client = __esm({
657
657
  ...organizationId && { organization_id: organizationId }
658
658
  });
659
659
  }
660
- async lookup(path30, opts) {
661
- const params = new URLSearchParams({ path: path30 });
660
+ async lookup(path31, opts) {
661
+ const params = new URLSearchParams({ path: path31 });
662
662
  if (opts?.organizationId) params.set("organization_id", opts.organizationId);
663
663
  return this.request("GET", `/api/ci/lookup?${params.toString()}`);
664
664
  }
@@ -1058,9 +1058,9 @@ var init_api_client = __esm({
1058
1058
  * sandbox for this conversation, or the blob was purged).
1059
1059
  */
1060
1060
  async downloadArchiveSandboxFs(hubId, conversationId) {
1061
- const path30 = `/api/archive/${encodeURIComponent(hubId)}/conversations/${encodeURIComponent(conversationId)}/sandbox`;
1062
- addApiBreadcrumb("GET", path30);
1063
- const url = `${this.apiUrl}${path30}`;
1061
+ const path31 = `/api/archive/${encodeURIComponent(hubId)}/conversations/${encodeURIComponent(conversationId)}/sandbox`;
1062
+ addApiBreadcrumb("GET", path31);
1063
+ const url = `${this.apiUrl}${path31}`;
1064
1064
  let response = await this.send(url, "GET");
1065
1065
  if (response.status === 401 && this.onUnauthorized) {
1066
1066
  let refreshed;
@@ -1074,7 +1074,7 @@ var init_api_client = __esm({
1074
1074
  }
1075
1075
  }
1076
1076
  if (!response.ok) {
1077
- throw new ApiError("GET", path30, response.status, await response.text());
1077
+ throw new ApiError("GET", path31, response.status, await response.text());
1078
1078
  }
1079
1079
  return new Uint8Array(await response.arrayBuffer());
1080
1080
  }
@@ -1114,9 +1114,9 @@ var init_api_client = __esm({
1114
1114
  `/api/admin/data-explorer/debug/observability/${encodeURIComponent(hubId)}/conversations/${encodeURIComponent(conversationId)}${qs}`
1115
1115
  );
1116
1116
  }
1117
- async request(method, path30, body) {
1118
- addApiBreadcrumb(method, path30);
1119
- const url = `${this.apiUrl}${path30}`;
1117
+ async request(method, path31, body) {
1118
+ addApiBreadcrumb(method, path31);
1119
+ const url = `${this.apiUrl}${path31}`;
1120
1120
  let refreshedOn401 = false;
1121
1121
  for (let retry = 0; ; retry++) {
1122
1122
  let response = await this.send(url, method, body);
@@ -1140,7 +1140,7 @@ var init_api_client = __esm({
1140
1140
  await delay(RETRYABLE_BACKOFF_MS[retry]);
1141
1141
  continue;
1142
1142
  }
1143
- throw new ApiError(method, path30, response.status, errorBody);
1143
+ throw new ApiError(method, path31, response.status, errorBody);
1144
1144
  }
1145
1145
  }
1146
1146
  /** Issue a single authenticated request with the client's current token. */
@@ -1575,8 +1575,8 @@ var init_parseUtil = __esm({
1575
1575
  init_errors();
1576
1576
  init_en();
1577
1577
  makeIssue = (params) => {
1578
- const { data, path: path30, errorMaps, issueData } = params;
1579
- const fullPath = [...path30, ...issueData.path || []];
1578
+ const { data, path: path31, errorMaps, issueData } = params;
1579
+ const fullPath = [...path31, ...issueData.path || []];
1580
1580
  const fullIssue = {
1581
1581
  ...issueData,
1582
1582
  path: fullPath
@@ -1887,11 +1887,11 @@ var init_types = __esm({
1887
1887
  init_parseUtil();
1888
1888
  init_util();
1889
1889
  ParseInputLazyPath = class {
1890
- constructor(parent, value, path30, key) {
1890
+ constructor(parent, value, path31, key) {
1891
1891
  this._cachedPath = [];
1892
1892
  this.parent = parent;
1893
1893
  this.data = value;
1894
- this._path = path30;
1894
+ this._path = path31;
1895
1895
  this._key = key;
1896
1896
  }
1897
1897
  get path() {
@@ -5375,12 +5375,12 @@ function typeMatches(typeField, allowed) {
5375
5375
  if (Array.isArray(typeField)) return typeField.every((t) => typeof t === "string" && allowed.has(t));
5376
5376
  return false;
5377
5377
  }
5378
- function validateSchema(schema, path30, errors, opts = {}) {
5378
+ function validateSchema(schema, path31, errors, opts = {}) {
5379
5379
  if (typeof schema === "boolean") return;
5380
5380
  const depth = opts.depth ?? 0;
5381
5381
  if (depth > MAX_SCHEMA_DEPTH) {
5382
5382
  errors.push({
5383
- path: path30 || "<root>",
5383
+ path: path31 || "<root>",
5384
5384
  message: `schema nesting exceeds ${MAX_SCHEMA_DEPTH} levels`,
5385
5385
  suggestion: "Flatten the schema; LLM providers reject deeply nested tool input_schemas."
5386
5386
  });
@@ -5388,7 +5388,7 @@ function validateSchema(schema, path30, errors, opts = {}) {
5388
5388
  }
5389
5389
  if (!isRecord(schema)) {
5390
5390
  errors.push({
5391
- path: path30,
5391
+ path: path31,
5392
5392
  message: `expected object, got ${schema === null ? "null" : typeof schema}`
5393
5393
  });
5394
5394
  return;
@@ -5396,14 +5396,14 @@ function validateSchema(schema, path30, errors, opts = {}) {
5396
5396
  if (opts.isRoot) {
5397
5397
  if ("type" in schema && schema.type !== "object") {
5398
5398
  errors.push({
5399
- path: path30 ? `${path30}.type` : "type",
5399
+ path: path31 ? `${path31}.type` : "type",
5400
5400
  message: `root type must be 'object', got ${JSON.stringify(schema.type)}`,
5401
5401
  suggestion: "Tool parameters at the root must be an object: `{ type: 'object', properties: { ... } }`."
5402
5402
  });
5403
5403
  }
5404
5404
  } else if ("type" in schema && !typeMatches(schema.type, ALLOWED_TYPES)) {
5405
5405
  errors.push({
5406
- path: `${path30}.type`,
5406
+ path: `${path31}.type`,
5407
5407
  message: `type must be one of ${[...ALLOWED_TYPES].join("/")} or an array of those, got ${JSON.stringify(schema.type)}`
5408
5408
  });
5409
5409
  }
@@ -5412,25 +5412,25 @@ function validateSchema(schema, path30, errors, opts = {}) {
5412
5412
  if (typeof e === "string") {
5413
5413
  const isPlaceholder = PLACEHOLDER_TOKENS.includes(e);
5414
5414
  errors.push({
5415
- path: `${path30}.enum`,
5415
+ path: `${path31}.enum`,
5416
5416
  message: `enum must be a non-empty array of primitives, got string ${JSON.stringify(e)}`,
5417
5417
  suggestion: isPlaceholder ? "Set `operation: 'update_kanban_status'` on the tool and ensure at least one hub.kanban_status has `allowsAgentUpdate: true` so the platform can render the placeholder into a valid array." : 'Wrap the value in an array: `enum: ["value"]`.'
5418
5418
  });
5419
5419
  } else if (!Array.isArray(e)) {
5420
5420
  errors.push({
5421
- path: `${path30}.enum`,
5421
+ path: `${path31}.enum`,
5422
5422
  message: `enum must be a non-empty array of primitives, got ${typeof e}`
5423
5423
  });
5424
5424
  } else if (e.length === 0) {
5425
5425
  errors.push({
5426
- path: `${path30}.enum`,
5426
+ path: `${path31}.enum`,
5427
5427
  message: "enum must not be empty"
5428
5428
  });
5429
5429
  } else {
5430
5430
  for (let i = 0; i < e.length; i++) {
5431
5431
  if (!isPrimitive(e[i])) {
5432
5432
  errors.push({
5433
- path: `${path30}.enum[${i}]`,
5433
+ path: `${path31}.enum[${i}]`,
5434
5434
  message: `enum entry must be a primitive (string/number/boolean/null), got ${typeof e[i]}`
5435
5435
  });
5436
5436
  }
@@ -5440,7 +5440,7 @@ function validateSchema(schema, path30, errors, opts = {}) {
5440
5440
  for (const key of ["exclusiveMinimum", "exclusiveMaximum"]) {
5441
5441
  if (key in schema && typeof schema[key] === "boolean") {
5442
5442
  errors.push({
5443
- path: `${path30}.${key}`,
5443
+ path: `${path31}.${key}`,
5444
5444
  message: `${key} as boolean is draft-04 syntax; draft 2020-12 requires a numeric value`,
5445
5445
  suggestion: `Replace with the numeric bound, e.g. \`${key}: 0\`.`
5446
5446
  });
@@ -5449,45 +5449,45 @@ function validateSchema(schema, path30, errors, opts = {}) {
5449
5449
  if ("properties" in schema) {
5450
5450
  if (!isRecord(schema.properties)) {
5451
5451
  errors.push({
5452
- path: `${path30}.properties`,
5452
+ path: `${path31}.properties`,
5453
5453
  message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
5454
5454
  });
5455
5455
  } else {
5456
5456
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
5457
- validateSchema(propSchema, `${path30}.properties.${propName}`, errors, { depth: depth + 1 });
5457
+ validateSchema(propSchema, `${path31}.properties.${propName}`, errors, { depth: depth + 1 });
5458
5458
  }
5459
5459
  }
5460
5460
  }
5461
5461
  if (schemaTypeIncludes(schema, "array") && "items" in schema) {
5462
5462
  if (Array.isArray(schema.items)) {
5463
- schema.items.forEach((sub, i) => validateSchema(sub, `${path30}.items[${i}]`, errors, { depth: depth + 1 }));
5463
+ schema.items.forEach((sub, i) => validateSchema(sub, `${path31}.items[${i}]`, errors, { depth: depth + 1 }));
5464
5464
  } else {
5465
- validateSchema(schema.items, `${path30}.items`, errors, { depth: depth + 1 });
5465
+ validateSchema(schema.items, `${path31}.items`, errors, { depth: depth + 1 });
5466
5466
  }
5467
5467
  }
5468
5468
  for (const key of SUBSCHEMA_OBJECT_KEYWORDS) {
5469
5469
  if (key in schema && isRecord(schema[key])) {
5470
- validateSchema(schema[key], `${path30}.${key}`, errors, { depth: depth + 1 });
5470
+ validateSchema(schema[key], `${path31}.${key}`, errors, { depth: depth + 1 });
5471
5471
  }
5472
5472
  }
5473
5473
  for (const key of SUBSCHEMA_LIST_KEYWORDS) {
5474
5474
  const list = schema[key];
5475
5475
  if (Array.isArray(list)) {
5476
- list.forEach((sub, i) => validateSchema(sub, `${path30}.${key}[${i}]`, errors, { depth: depth + 1 }));
5476
+ list.forEach((sub, i) => validateSchema(sub, `${path31}.${key}[${i}]`, errors, { depth: depth + 1 }));
5477
5477
  }
5478
5478
  }
5479
5479
  for (const key of SUBSCHEMA_MAP_KEYWORDS) {
5480
5480
  const map = schema[key];
5481
5481
  if (isRecord(map)) {
5482
5482
  for (const [name, sub] of Object.entries(map)) {
5483
- validateSchema(sub, `${path30}.${key}.${name}`, errors, { depth: depth + 1 });
5483
+ validateSchema(sub, `${path31}.${key}.${name}`, errors, { depth: depth + 1 });
5484
5484
  }
5485
5485
  }
5486
5486
  }
5487
5487
  const reportedPaths = new Set(errors.map((e) => e.path));
5488
5488
  for (const [k, v] of Object.entries(schema)) {
5489
5489
  if (typeof v !== "string") continue;
5490
- const fieldPath = `${path30}.${k}`;
5490
+ const fieldPath = `${path31}.${k}`;
5491
5491
  if (reportedPaths.has(fieldPath)) continue;
5492
5492
  for (const token of PLACEHOLDER_TOKENS) {
5493
5493
  if (v === token) {
@@ -5787,10 +5787,10 @@ function refineHubAsCodeEvals(config, ctx) {
5787
5787
  function refineHubAsCodeEvalAttachments(config, ctx) {
5788
5788
  const cfg = config;
5789
5789
  const referencedHashes = /* @__PURE__ */ new Set();
5790
- const addTurnIssue = (path30, turn, name) => {
5790
+ const addTurnIssue = (path31, turn, name) => {
5791
5791
  const error = evalTurnAttachmentsError(turn);
5792
5792
  if (error) {
5793
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path30, message: `${name}: ${error}` });
5793
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path31, message: `${name}: ${error}` });
5794
5794
  return;
5795
5795
  }
5796
5796
  for (const hash of collectTurnAttachmentHashes(turn)) referencedHashes.add(hash);
@@ -5911,10 +5911,10 @@ function refineHubAsCodeResources(config, ctx) {
5911
5911
  });
5912
5912
  }
5913
5913
  }
5914
- function isSafeTemplatePath(path30) {
5915
- if (path30.length === 0 || path30.length > 300) return false;
5916
- if (path30.startsWith("/") || path30.includes("\\")) return false;
5917
- const segments = path30.split("/");
5914
+ function isSafeTemplatePath(path31) {
5915
+ if (path31.length === 0 || path31.length > 300) return false;
5916
+ if (path31.startsWith("/") || path31.includes("\\")) return false;
5917
+ const segments = path31.split("/");
5918
5918
  return segments.every(
5919
5919
  (seg) => seg.length > 0 && seg !== "." && seg !== ".." && /^[A-Za-z0-9._-]+$/.test(seg)
5920
5920
  );
@@ -5958,7 +5958,7 @@ function findStepBoundaries(transcript) {
5958
5958
  }
5959
5959
  return out;
5960
5960
  }
5961
- var UUID_RE, HEX_RE, WORKOS_ID_RE, OPAQUE_SECRET_RE, BASE64_SECRET_RE, BASE64_MARKER_RE, TOKEN_RE, SENSITIVE_FIELDS, SENSITIVE_KEY_ALTERNATION, SENSITIVE_KEY_PATTERN, JSON_CREDENTIAL_RE, QUERY_CREDENTIAL_RE, REDACTED, PROVIDER_TOKEN_RE, APP_ERROR_BRAND, AppError, LLM_RATE_LIMIT_BRAND, LlmRateLimitError, LLM_PROVIDER_HTTP_BRAND, LlmProviderHttpError, LLM_PROVIDER_CONTENT_BRAND, LlmProviderContentError, LLM_PROVIDER_TIMEOUT_BRAND, LlmProviderTimeoutError, ExternalServiceError, CHANNEL_PROVIDER_HTTP_BRAND, ChannelProviderHttpError, MEDIA_PROVIDER_HTTP_BRAND, MediaProviderHttpError, AUTH_TYPE, ORG_CREDENTIAL_AUTH_TYPES, AUTH_TYPE_DISPLAY, LEGACY_AUTH_TYPE_MAP, VALID_AUTH_TYPES, AGENT_ROLES, SAFE_USER_ID_REGEX, SUPPORTED_LOCALES, uuidSchema, paginationSchema, timestampSchema, idParamSchema, clientOptionalString, clientOptionalBoolean, clientOptionalNumber, messageAttachment, MAX_MESSAGE_BODY_BYTES, MAX_ATTACHMENTS_PER_MESSAGE, primaryRegionSchema, placementSourceSchema, orgIdParam, orgAdminIdParam, createOrganizationBody, updateOrganizationBody, addOrgAdminBody, SUMMARIZATION_THRESHOLD_MIN, SUMMARIZATION_THRESHOLD_MAX, summarizationThresholdField, monitorConfigField, agentIdParam, listAgentsQuery, agentConnectionsQuery, createAgentBody, updateAgentBody, AGENT_PARAMETER_TYPES, AGENT_PARAMETER_NAME_REGEX, agentParameterCreateSchema, MAX_AGENT_PARAMETERS_PER_REQUEST, createAgentParametersBody, SLUG_REGEX, slugSchema, INVISIBLE_NAME_CHARS, MAX_KANBAN_STATUSES, MAX_FOLLOWUPS_PER_STATUS, MAX_LANES, MAX_ADDITIONAL_CONTEXT_SCHEMA_BYTES, RESERVED_TEMPLATE_DUMP_NAME, kanbanStatusSlugSchema, followupSchema, EXCLUSIVE_FLAG_PAIRS, kanbanStatusSchema, kanbanStatusesArraySchema, laneSchema, lanesArraySchema, hubIdParam, hubAdminIdParam, hubUserIdParam, hubIdentityIdParam, hubTeamIdParam, hubTeamUserDeleteParam, hubSchemaQuery, PREVIEW_LABEL_MAX_LENGTH, MAX_ENDED_INDEX_RETENTION_DAYS, previewLabelField, createHubBody, updateHubBody, addHubAdminBody, addHubIdentityAuthorizationBody, contactDecisionBody, approveContactBody, listContactAccessQuery, createHubTeamBody, updateHubTeamBody, addHubTeamUserBody, reassignHubUserBody, replicatePreviewBody, updateChannelTestIdentitiesBody, claimCodeChannelParam, claimCodeDeleteParam, connectionIdParam, connectorIdParam, listConnectionsQuery, deleteConnectionQuery, addConnectionBody, editConnectionBody, PRODUCTION_DIRECT_FIELDS, setProductionCredentialBody, registerWhatsappQuery, registerWhatsappBody, retryProvisioningQuery, resendDomainStatusQuery, PLACEHOLDER_TOKENS, ALLOWED_TYPES, SUBSCHEMA_OBJECT_KEYWORDS, SUBSCHEMA_LIST_KEYWORDS, SUBSCHEMA_MAP_KEYWORDS, MAX_SCHEMA_DEPTH, toolIdParam, listToolsQuery, toolAgentQuery, deleteToolQuery, toolConnectionsQuery, nativeToolIdSchema, addNativeToolBody, addMcpToolBody, createCustomToolBody, updateCustomToolBody, updateToolExecutionConfigBody, initialValueCreateSchema, initialValueUpdateSchema, stateIdParam, listStatesQuery, stateNameSchema, createStateBody, updateStateBody, reorderStatesBody, variantAiModeSchema, experimentStatusSchema, overlayUpdateSchema, experimentIdParam, variantIdParam, listExperimentsQuery, listVariantsQuery, listOverridesQuery, deleteOverrideQuery, createExperimentBody, updateExperimentBody, setExperimentStatusBody, createVariantBody, updateVariantBody, upsertOverrideBody, orgTagIdParam, listOrgTagsQuery, createOrgTagBody, updateOrgTagBody, orgCredentialIdParam, listOrgCredentialsQuery, createOrgCredentialBody, updateOrgCredentialBody, orgResourceIdParam, orgResourceFileIdParam, orgResourceFolderIdParam, hubOrgResourceParam, listOrgResourcesQuery, orgIdQuery, createOrgResourceBody, updateOrgResourceBody, createOrgResourceFolderBody, updateOrgResourceFolderBody, uploadOrgResourceFileBody, updateOrgResourceFileBody, uploadOrgSkillZipBody, linkOrgResourceBody, resourceIdParam, listResourcesQuery, createResourceBody, updateResourceBody, syncSkillsBody, resourceFileIdParam, listResourceFilesQuery, createResourceFileBody, updateResourceFileBody, uploadResourceFileBody, uploadSkillZipBody, resourceFolderIdParam, listResourceFoldersQuery, createResourceFolderBody, updateResourceFolderBody, agentResourceQuery, hubResourceQuery, linkAgentResourceBody, updateAgentResourceBody, unlinkAgentResourceQuery, navItemSchema, hubCountResetBody, navItemCountResetBody, typingBody, analyticsHubIdParam, analyticsVariableIdParam, updateVariablePinBody, updateAnalyticsViewBody, NUMERIC_FILTER_OPS, TEXT_FILTER_OPS, CATEGORICAL_FILTER_OPS, VARIABLE_FILTER_OPS, STRUCTURED_QUERY_OPS, STRUCTURED_QUERY_AGGREGATIONS, filterValueSchema, conversationsBody, analyticsConversationIdParam, messagesQuery, conversationDetailDataQuery, analyticsDataBody, structuredQueryBody, analyticsSqlBody, analyticsSqlSchemaQuery, EVAL_PACING_PRESET_NAMES, PACING_INTERVAL_MIN_MS, PACING_INTERVAL_MAX_MS, EVAL_RUN_DEADLINE_MIN_MS, EVAL_RUN_DEADLINE_MAX_MS, paginationSchema2, evalIdParam, sessionIdParam, scenarioSetIdParam, hubIdQuery, EVAL_INPUT_ROLES, EVAL_INPUT_ROLE_LIST, ROLE_ECHO_MAX, createEvalBody, updateEvalBody, getEvalsQuery, toggleEvalBody, bulkToggleEvalsBody, pacingConfigSchema, sessionConfigSchema, createSessionBody, getSessionsQuery, getSessionResultsQuery, getSessionRunsQuery, evalAnalyticsQuery, getSessionCostQuery, compareSessionsQuery, evalsSqlBody, evalsSqlSchemaQuery, exportResultsQuery, validateEvalBody, hubAgentsQuery, createScenarioSetBody, updateScenarioSetBody, scenarioSetsHubQuery, createScenarioFromConversationBody, createJourneyFromConversationBody, EVAL_ATTACHMENT_HASH_REGEX, evalAttachmentRef, turnMayCarryAttachments, ATTACHMENTS_USER_ONLY_MESSAGE, journeyIdParam, journeyToolCallSchema, journeyTurnSchema, journeyStepOverrideSchema, fixtureField, createJourneyBody, updateJourneyBody, getConversationsQuery, getMessagesQuery, appMessageSenderType, appMessageReceiverType, appMessageBody, updateUserBody, updateProfileBody, pathnameRegex, updatePreferencesBody, registerPushTokenBody, deletePushTokenQuery, activityQuery, deletionModeSchema, deletionRequestBody, archiveHubIdParam, archiveConversationParams, archiveListQuery, archiveMessageObservabilityParams, conversationIdParam, claimBody, releaseBody, transferBody, closeBody, additionalContextPayloadSchema, kanbanUpdateBody, annotateConversationBody, observabilityParams, observabilityListParams, deleteUserConversationsBody, sendMessageBody, listConversationsQuery, getConversationDetailParams, getConversationDetailQuery, listWhatsAppTemplatesQuery, sendWhatsAppTemplateBody, billingOrgIdParam, subscriptionItemParams, spendCapBody, growthPacksBody, portalSessionBody, checkoutSessionBody, updateBillingCurrencyBody, invoicesQuery, downloadBody, uploadQuery, signUrlBody, outboundSchemaQuery, templateIdParam, listTemplatesQuery, deleteTemplateQuery, templateStatusQuery, createTemplateBody, updateTemplateBody, submitTemplateBody, testTemplateBody, contactIdParam, listContactsQuery, createContactBody, updateContactBody, listIdParam, listListsQuery, listListContactsQuery, createListBody, updateListBody, addListContactsBody, removeListContactsBody, scheduleIdParam, listSchedulesQuery, listExecutionsQuery, createScheduleBody, updateScheduleBody, MAX_RESOURCE_FILE_SIZE, MAX_10MB_BASE64_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_CARRIERS, MAX_EVAL_ATTACHMENT_AGGREGATE_ENCODED_BYTES, MAX_RESOURCE_ENCODED_BYTES, MAX_HUB_AS_CODE_RESOURCES, MAX_HUB_AS_CODE_RESOURCE_FILES, MAX_RESOURCE_AGGREGATE_ENCODED_BYTES, MAX_CI_CONFIG_BODY_BYTES, hubAsCodeResourceFileSchema, hubAsCodeResourceSchema, uuidPattern, ciUuidSchema, boundedHubAsCodeResourcesSchema, hubAsCodeConfigSchema, ciPullHubIdParam, ciBranchParam, ciPullQuery, ciHubsQuery, ciLookupQuery, ciBranchesQuery, ciSyncBody, ciPushBody, ciDiffBody, ciPublishBody, ciPublishPreviewBody, ciSyncMcpBody, ciOrgResourcesParam, orgResourcesConfigSchema, ciOrgResourcesPushBody, ciOrgResourcesDiffBody, adminOrgIdParam, adminOrgAdminIdParam, adminUserIdParam, adminPlanIdParam, adminHubIdParam, adminOrganizationsQuery, adminUserSearchQuery, kanbanSlugMigrationQuery, adjustQuotaBody, updatePlanBody, addAdminUserBody, updatePlatformConfigBody, updateFreeOrgLimitBody, adminSetOrgHarnessBody, adminSetHubHarnessBody, adminScopedTargetBody, updatePricingPlanBody, dataExplorerSearchQuery, dataExplorerOrgIdParam, dataExplorerHubIdParam, dataExplorerConvIdParam, dataExplorerDoInstancesQuery, dataExplorerNsIdParam, dataExplorerKvKeysQuery, dataExplorerKvKeyParam, uuidOrHexId, dataExplorerDeleteOrgParam, dataExplorerDeleteHubParam, dataExplorerDeleteConvParam, dataExplorerDeleteUserParam, dataExplorerDeleteQuery, dataExplorerKvDeleteQuery, pitrDoNamespace, pitrBookmarkParam, pitrRestoreBody, healthSamplingTriggerBody, dataExplorerDebugDoType, DEBUG_READ_MAX_LIMIT, DEBUG_READ_DEFAULT_LIMIT, DEBUG_DO_HEX_PREFIX, debugDoEntityId, debugTableName, dataExplorerDebugTablesParam, dataExplorerDebugRowsParam, dataExplorerDebugRowsQuery, dataExplorerDebugAnalyticsTable, debugUuid, dataExplorerDebugAnalyticsRowsParam, dataExplorerDebugAnalyticsRowsQuery, debugHubConvParam, debugMessageIdQuery, sandboxEgressPolicy, SANDBOX_EXEC_MAX_CMD_LENGTH, SANDBOX_EXEC_MAX_ALLOWLIST, SANDBOX_EXEC_MAX_TIMEOUT_MS, adminSandboxExecBody, whatsappCallbackBody, authMeResponse, wsTicketResponse, sessionCheckResponse, logoutResponse, magicCodeSendBody, magicCodeSendResponse, magicCodeVerifyBody, magicCodeVerifyResponse, browserParams, browserFoldersQuery, browserFilesQuery, stateOperationBody, conversationListResourcesQuery, conversationReadResourceQuery, listPendingSchedulesQuery, listHubAggregateSchedulesQuery, reportSourceSchema, intakeReportSourceSchema, reportClassificationSchema, reportStatusSchema, reportLocaleSchema, reportMessageAuthorRoleSchema, sentryRefStatusSchema, createReportBody, editReportBody, contestReportBody, reporterListQuery, listReportsQuery, GITHUB_ISSUE_URL_REGEX, githubIssueUrlSchema, transitionReportBody, groupReportsBody, holdReportBody, clickhouseReadyResponse, bootstrapQuery, requestSnapshotChunkMessage, MAX_NAME_LENGTH, MAX_VALUE_LENGTH, MAX_TAGS, MAX_TAG_LENGTH, MAX_SCOPE_HUBS, MAX_SCOPE_TAGS, vaultSecretScopeSchema, vaultCreateBody, vaultUpdateBody, PERMISSIONS, MAX_NAME_LENGTH2, MAX_HUBS_PER_GRANT, MAX_HUB_TAGS_PER_GRANT, MAX_GRANTS, permissionEnum, grantScopeSchema, grantSchema, createTokenBody, tokenOrgIdParam, tokenGrantScopeSchema, tokenGrantSchema, tokenMetadataSchema, listTokensResponse, createTokenResponse, orgTokenSummarySchema, listOrgTokensResponse, invitePreviewQuery, invitePreviewResponse, discordTier, discordBillingCycle, discordMetadata, discordOAuthStartResponse, discordOAuthFinalizeBody, discordOAuthFinalizeResponse, discordDisconnectResponse, jsonSchemaSchema, hubUserIdentity, stateValueEntry, getHubUserContextResponse, updateHubUserBody, updateHubUserResponse, unlockHubUserNameResponse, stateResetParams, stateResetResponse, hubAlertsParam, hubAlertSeverity, hubAlert, hubAlertsListResponse, noticeSeveritySchema, noticeStatusSchema, NOTICE_SCOPE_REGEX, noticeScopeSchema, noticeLinkSchema, createNoticeBody, updateNoticeBody, listNoticesQuery, noticeIdParamSchema, templateLocaleSchema, TEMPLATE_SLUG_REGEX, templateSlugSchema, templateStatusSchema, templateFileMapSchema, templateTextSchema, templateUsesEntrySchema, templateUsesSchema, templateYamlLocaleEntry, templateYamlWayaiBlock, templateTagsSchema, templateYamlSchema, templateHeroSchema, templatePushLocaleSchema, templatePushBody, templateListQuery, templateSlugParam, templateDetailQuery, ADMIN_SKILL_NAME_REGEX, adminSkillNameParam, WAYAI_WORKSPACE_LAYOUT;
5961
+ var UUID_RE, HEX_RE, WORKOS_ID_RE, OPAQUE_SECRET_RE, BASE64_SECRET_RE, BASE64_MARKER_RE, TOKEN_RE, SENSITIVE_FIELDS, SENSITIVE_KEY_ALTERNATION, SENSITIVE_KEY_PATTERN, JSON_CREDENTIAL_RE, QUERY_CREDENTIAL_RE, REDACTED, PROVIDER_TOKEN_RE, APP_ERROR_BRAND, AppError, LLM_RATE_LIMIT_BRAND, LlmRateLimitError, LLM_PROVIDER_HTTP_BRAND, LlmProviderHttpError, LLM_PROVIDER_CONTENT_BRAND, LlmProviderContentError, LLM_PROVIDER_TIMEOUT_BRAND, LlmProviderTimeoutError, ExternalServiceError, CHANNEL_PROVIDER_HTTP_BRAND, ChannelProviderHttpError, MEDIA_PROVIDER_HTTP_BRAND, MediaProviderHttpError, AUTH_TYPE, ORG_CREDENTIAL_AUTH_TYPES, AUTH_TYPE_DISPLAY, LEGACY_AUTH_TYPE_MAP, VALID_AUTH_TYPES, AGENT_ROLES, SAFE_USER_ID_REGEX, SUPPORTED_LOCALES, uuidSchema, paginationSchema, timestampSchema, idParamSchema, clientOptionalString, clientOptionalBoolean, clientOptionalNumber, messageAttachment, MAX_MESSAGE_BODY_BYTES, MAX_ATTACHMENTS_PER_MESSAGE, primaryRegionSchema, placementSourceSchema, orgIdParam, orgAdminIdParam, createOrganizationBody, updateOrganizationBody, addOrgAdminBody, SUMMARIZATION_THRESHOLD_MIN, SUMMARIZATION_THRESHOLD_MAX, summarizationThresholdField, monitorConfigField, agentIdParam, listAgentsQuery, agentConnectionsQuery, createAgentBody, updateAgentBody, AGENT_PARAMETER_TYPES, AGENT_PARAMETER_NAME_REGEX, agentParameterCreateSchema, MAX_AGENT_PARAMETERS_PER_REQUEST, createAgentParametersBody, SLUG_REGEX, slugSchema, INVISIBLE_NAME_CHARS, MAX_KANBAN_STATUSES, MAX_FOLLOWUPS_PER_STATUS, MAX_LANES, MAX_ADDITIONAL_CONTEXT_SCHEMA_BYTES, RESERVED_TEMPLATE_DUMP_NAME, kanbanStatusSlugSchema, followupSchema, EXCLUSIVE_FLAG_PAIRS, kanbanStatusSchema, kanbanStatusesArraySchema, laneSchema, lanesArraySchema, hubIdParam, hubAdminIdParam, hubUserIdParam, hubIdentityIdParam, hubTeamIdParam, hubTeamUserDeleteParam, hubSchemaQuery, PREVIEW_LABEL_MAX_LENGTH, MAX_ENDED_INDEX_RETENTION_DAYS, previewLabelField, createHubBody, updateHubBody, addHubAdminBody, addHubIdentityAuthorizationBody, contactDecisionBody, approveContactBody, listContactAccessQuery, createHubTeamBody, updateHubTeamBody, addHubTeamUserBody, reassignHubUserBody, replicatePreviewBody, updateChannelTestIdentitiesBody, claimCodeChannelParam, claimCodeDeleteParam, connectionIdParam, connectorIdParam, listConnectionsQuery, deleteConnectionQuery, addConnectionBody, editConnectionBody, PRODUCTION_DIRECT_FIELDS, setProductionCredentialBody, registerWhatsappQuery, registerWhatsappBody, retryProvisioningQuery, resendDomainStatusQuery, PLACEHOLDER_TOKENS, ALLOWED_TYPES, SUBSCHEMA_OBJECT_KEYWORDS, SUBSCHEMA_LIST_KEYWORDS, SUBSCHEMA_MAP_KEYWORDS, MAX_SCHEMA_DEPTH, toolIdParam, listToolsQuery, toolAgentQuery, deleteToolQuery, toolConnectionsQuery, nativeToolIdSchema, addNativeToolBody, addMcpToolBody, createCustomToolBody, updateCustomToolBody, updateToolExecutionConfigBody, initialValueCreateSchema, initialValueUpdateSchema, stateIdParam, listStatesQuery, stateNameSchema, createStateBody, updateStateBody, reorderStatesBody, variantAiModeSchema, experimentStatusSchema, overlayUpdateSchema, experimentIdParam, variantIdParam, listExperimentsQuery, listVariantsQuery, listOverridesQuery, deleteOverrideQuery, createExperimentBody, updateExperimentBody, setExperimentStatusBody, createVariantBody, updateVariantBody, upsertOverrideBody, orgTagIdParam, listOrgTagsQuery, createOrgTagBody, updateOrgTagBody, orgCredentialIdParam, listOrgCredentialsQuery, createOrgCredentialBody, updateOrgCredentialBody, orgResourceIdParam, orgResourceFileIdParam, orgResourceFolderIdParam, hubOrgResourceParam, listOrgResourcesQuery, orgIdQuery, createOrgResourceBody, updateOrgResourceBody, createOrgResourceFolderBody, updateOrgResourceFolderBody, uploadOrgResourceFileBody, updateOrgResourceFileBody, uploadOrgSkillZipBody, linkOrgResourceBody, resourceIdParam, listResourcesQuery, createResourceBody, updateResourceBody, syncSkillsBody, resourceFileIdParam, listResourceFilesQuery, createResourceFileBody, updateResourceFileBody, uploadResourceFileBody, uploadSkillZipBody, resourceFolderIdParam, listResourceFoldersQuery, createResourceFolderBody, updateResourceFolderBody, agentResourceQuery, hubResourceQuery, linkAgentResourceBody, updateAgentResourceBody, unlinkAgentResourceQuery, navItemSchema, hubCountResetBody, navItemCountResetBody, typingBody, analyticsHubIdParam, analyticsVariableIdParam, updateVariablePinBody, updateAnalyticsViewBody, NUMERIC_FILTER_OPS, TEXT_FILTER_OPS, CATEGORICAL_FILTER_OPS, VARIABLE_FILTER_OPS, STRUCTURED_QUERY_OPS, STRUCTURED_QUERY_AGGREGATIONS, filterValueSchema, conversationsBody, analyticsConversationIdParam, messagesQuery, conversationDetailDataQuery, analyticsDataBody, structuredQueryBody, analyticsSqlBody, analyticsSqlSchemaQuery, EVAL_PACING_PRESET_NAMES, PACING_INTERVAL_MIN_MS, PACING_INTERVAL_MAX_MS, EVAL_RUN_DEADLINE_MIN_MS, EVAL_RUN_DEADLINE_MAX_MS, paginationSchema2, evalIdParam, sessionIdParam, scenarioSetIdParam, hubIdQuery, EVAL_INPUT_ROLES, EVAL_INPUT_ROLE_LIST, ROLE_ECHO_MAX, createEvalBody, updateEvalBody, getEvalsQuery, toggleEvalBody, bulkToggleEvalsBody, pacingConfigSchema, sessionConfigSchema, createSessionBody, getSessionsQuery, getSessionResultsQuery, getSessionRunsQuery, evalAnalyticsQuery, getSessionCostQuery, compareSessionsQuery, evalsSqlBody, evalsSqlSchemaQuery, exportResultsQuery, validateEvalBody, hubAgentsQuery, createScenarioSetBody, updateScenarioSetBody, scenarioSetsHubQuery, createScenarioFromConversationBody, createJourneyFromConversationBody, EVAL_ATTACHMENT_HASH_REGEX, evalAttachmentRef, turnMayCarryAttachments, ATTACHMENTS_USER_ONLY_MESSAGE, journeyIdParam, journeyToolCallSchema, journeyTurnSchema, journeyStepOverrideSchema, fixtureField, createJourneyBody, updateJourneyBody, getConversationsQuery, getMessagesQuery, appMessageSenderType, appMessageReceiverType, appMessageBody, updateUserBody, updateProfileBody, pathnameRegex, updatePreferencesBody, registerPushTokenBody, deletePushTokenQuery, activityQuery, deletionModeSchema, deletionRequestBody, archiveHubIdParam, archiveConversationParams, archiveListQuery, archiveMessageObservabilityParams, conversationIdParam, claimBody, releaseBody, transferBody, closeBody, additionalContextPayloadSchema, kanbanUpdateBody, annotateConversationBody, observabilityParams, observabilityListParams, deleteUserConversationsBody, sendMessageBody, listConversationsQuery, getConversationDetailParams, getConversationDetailQuery, listWhatsAppTemplatesQuery, sendWhatsAppTemplateBody, billingOrgIdParam, subscriptionItemParams, spendCapBody, growthPacksBody, portalSessionBody, checkoutSessionBody, updateBillingCurrencyBody, invoicesQuery, downloadBody, uploadQuery, signUrlBody, outboundSchemaQuery, templateIdParam, listTemplatesQuery, deleteTemplateQuery, templateStatusQuery, createTemplateBody, updateTemplateBody, submitTemplateBody, testTemplateBody, contactIdParam, listContactsQuery, createContactBody, updateContactBody, listIdParam, listListsQuery, listListContactsQuery, createListBody, updateListBody, addListContactsBody, removeListContactsBody, scheduleIdParam, listSchedulesQuery, listExecutionsQuery, createScheduleBody, updateScheduleBody, MAX_RESOURCE_FILE_SIZE, MAX_10MB_BASE64_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_CARRIERS, MAX_EVAL_ATTACHMENT_AGGREGATE_ENCODED_BYTES, MAX_RESOURCE_ENCODED_BYTES, MAX_HUB_AS_CODE_RESOURCES, MAX_HUB_AS_CODE_RESOURCE_FILES, MAX_RESOURCE_AGGREGATE_ENCODED_BYTES, MAX_CI_CONFIG_BODY_BYTES, hubAsCodeResourceFileSchema, hubAsCodeResourceSchema, uuidPattern, ciUuidSchema, boundedHubAsCodeResourcesSchema, hubAsCodeConfigSchema, ciPullHubIdParam, ciBranchParam, ciPullQuery, ciHubsQuery, ciLookupQuery, ciBranchesQuery, ciSyncBody, ciPushBody, ciDiffBody, ciPublishBody, ciPublishPreviewBody, ciSyncMcpBody, ciOrgResourcesParam, orgResourcesConfigSchema, ciOrgResourcesPushBody, ciOrgResourcesDiffBody, adminOrgIdParam, adminOrgAdminIdParam, adminUserIdParam, adminPlanIdParam, adminHubIdParam, adminOrganizationsQuery, adminUserSearchQuery, kanbanSlugMigrationQuery, adjustQuotaBody, updatePlanBody, addAdminUserBody, updatePlatformConfigBody, updateFreeOrgLimitBody, adminSetOrgHarnessBody, adminSetHubHarnessBody, adminScopedTargetBody, updatePricingPlanBody, dataExplorerSearchQuery, dataExplorerOrgIdParam, dataExplorerHubIdParam, dataExplorerConvIdParam, dataExplorerDoInstancesQuery, dataExplorerNsIdParam, dataExplorerKvKeysQuery, dataExplorerKvKeyParam, uuidOrHexId, dataExplorerDeleteOrgParam, dataExplorerDeleteHubParam, dataExplorerDeleteConvParam, dataExplorerDeleteUserParam, dataExplorerDeleteQuery, dataExplorerKvDeleteQuery, pitrDoNamespace, pitrBookmarkParam, pitrRestoreBody, healthSamplingTriggerBody, dataExplorerDebugDoType, DEBUG_READ_MAX_LIMIT, DEBUG_READ_DEFAULT_LIMIT, DEBUG_DO_HEX_PREFIX, debugDoEntityId, debugTableName, dataExplorerDebugTablesParam, dataExplorerDebugRowsParam, dataExplorerDebugRowsQuery, dataExplorerDebugAnalyticsTable, debugUuid, dataExplorerDebugAnalyticsRowsParam, dataExplorerDebugAnalyticsRowsQuery, debugHubConvParam, debugMessageIdQuery, sandboxEgressPolicy, SANDBOX_EXEC_MAX_CMD_LENGTH, SANDBOX_EXEC_MAX_ALLOWLIST, SANDBOX_EXEC_MAX_TIMEOUT_MS, adminSandboxExecBody, whatsappCallbackBody, authMeResponse, wsTicketResponse, sessionCheckResponse, logoutResponse, magicCodeSendBody, magicCodeSendResponse, magicCodeVerifyBody, magicCodeVerifyResponse, browserParams, browserFoldersQuery, browserFilesQuery, stateOperationBody, conversationListResourcesQuery, conversationReadResourceQuery, listPendingSchedulesQuery, listHubAggregateSchedulesQuery, reportSourceSchema, intakeReportSourceSchema, reportClassificationSchema, reportStatusSchema, reportLocaleSchema, reportMessageAuthorRoleSchema, reportMessageSchema, sentryRefStatusSchema, createReportBody, editReportBody, reporterReportSummarySchema, reporterListResponseSchema, getReportResponseSchema, reporterTransitionResponseSchema, contestReportBody, reporterListQuery, listReportsQuery, GITHUB_ISSUE_URL_REGEX, githubIssueUrlSchema, transitionReportBody, groupReportsBody, holdReportBody, clickhouseReadyResponse, bootstrapQuery, requestSnapshotChunkMessage, MAX_NAME_LENGTH, MAX_VALUE_LENGTH, MAX_TAGS, MAX_TAG_LENGTH, MAX_SCOPE_HUBS, MAX_SCOPE_TAGS, vaultSecretScopeSchema, vaultCreateBody, vaultUpdateBody, PERMISSIONS, MAX_NAME_LENGTH2, MAX_HUBS_PER_GRANT, MAX_HUB_TAGS_PER_GRANT, MAX_GRANTS, permissionEnum, grantScopeSchema, grantSchema, createTokenBody, tokenOrgIdParam, tokenGrantScopeSchema, tokenGrantSchema, tokenMetadataSchema, listTokensResponse, createTokenResponse, orgTokenSummarySchema, listOrgTokensResponse, invitePreviewQuery, invitePreviewResponse, discordTier, discordBillingCycle, discordMetadata, discordOAuthStartResponse, discordOAuthFinalizeBody, discordOAuthFinalizeResponse, discordDisconnectResponse, jsonSchemaSchema, hubUserIdentity, stateValueEntry, getHubUserContextResponse, updateHubUserBody, updateHubUserResponse, unlockHubUserNameResponse, stateResetParams, stateResetResponse, hubAlertsParam, hubAlertSeverity, hubAlert, hubAlertsListResponse, noticeSeveritySchema, noticeStatusSchema, NOTICE_SCOPE_REGEX, noticeScopeSchema, noticeLinkSchema, createNoticeBody, updateNoticeBody, listNoticesQuery, noticeIdParamSchema, templateLocaleSchema, TEMPLATE_SLUG_REGEX, templateSlugSchema, templateStatusSchema, templateFileMapSchema, templateTextSchema, templateUsesEntrySchema, templateUsesSchema, templateYamlLocaleEntry, templateYamlWayaiBlock, templateTagsSchema, templateYamlSchema, templateHeroSchema, templatePushLocaleSchema, templatePushBody, templateListQuery, templateSlugParam, templateDetailQuery, ADMIN_SKILL_NAME_REGEX, adminSkillNameParam, WAYAI_WORKSPACE_LAYOUT;
5962
5962
  var init_dist = __esm({
5963
5963
  "../../packages/core/dist/index.js"() {
5964
5964
  "use strict";
@@ -8424,6 +8424,20 @@ var init_dist = __esm({
8424
8424
  ]);
8425
8425
  reportLocaleSchema = external_exports.enum(["en", "pt", "es"]);
8426
8426
  reportMessageAuthorRoleSchema = external_exports.enum(["reporter", "triage", "fixer", "system"]);
8427
+ reportMessageSchema = external_exports.object({
8428
+ message_id: external_exports.string(),
8429
+ author_role: reportMessageAuthorRoleSchema,
8430
+ /** The status transition this note accompanied (e.g. 'shipped'); null for a free/system note. */
8431
+ transition: external_exports.string().nullable(),
8432
+ body: external_exports.string(),
8433
+ created_at: timestampSchema,
8434
+ /**
8435
+ * Visibility: `false` = reporter-facing (the default for every narrative entry); `true` =
8436
+ * triage-internal (e.g. a `hold` rationale) — surfaced to platform admins only and NEVER
8437
+ * returned on the reporter read path. The reporter's `GET /api/reports/:id` filters these out.
8438
+ */
8439
+ internal: external_exports.boolean()
8440
+ });
8427
8441
  sentryRefStatusSchema = external_exports.enum(["valid", "not_found", "mismatch", "unverified"]);
8428
8442
  createReportBody = external_exports.object({
8429
8443
  title: external_exports.string().min(5).max(256),
@@ -8455,6 +8469,30 @@ var init_dist = __esm({
8455
8469
  }).strict().refine((b) => Object.keys(b).length > 0, {
8456
8470
  message: "at least one field must be provided"
8457
8471
  });
8472
+ reporterReportSummarySchema = external_exports.object({
8473
+ report_id: external_exports.string(),
8474
+ title: external_exports.string(),
8475
+ description: external_exports.string(),
8476
+ classification: reportClassificationSchema,
8477
+ status: reportStatusSchema,
8478
+ created_at: timestampSchema,
8479
+ triaged_at: timestampSchema.nullable(),
8480
+ resolved_at: timestampSchema.nullable(),
8481
+ /** How many times the reporter has contested (gates the hard cap). */
8482
+ contest_count: external_exports.number(),
8483
+ /** True once triage issued a non-contestable (final) dismissal. */
8484
+ dismissal_final: external_exports.boolean()
8485
+ });
8486
+ reporterListResponseSchema = external_exports.object({
8487
+ data: external_exports.array(reporterReportSummarySchema)
8488
+ });
8489
+ getReportResponseSchema = external_exports.object({
8490
+ report: reporterReportSummarySchema,
8491
+ messages: external_exports.array(reportMessageSchema)
8492
+ });
8493
+ reporterTransitionResponseSchema = external_exports.object({
8494
+ report: reporterReportSummarySchema
8495
+ });
8458
8496
  contestReportBody = external_exports.object({
8459
8497
  reason: external_exports.string().min(1).max(5e3)
8460
8498
  });
@@ -9614,9 +9652,9 @@ function getVersionCachePath(filename = CLI_CACHE_FILE) {
9614
9652
  }
9615
9653
  function readVersionCache(filename = CLI_CACHE_FILE) {
9616
9654
  try {
9617
- const path30 = getVersionCachePath(filename);
9618
- if (!existsSync4(path30)) return null;
9619
- const parsed = JSON.parse(readFileSync5(path30, "utf-8"));
9655
+ const path31 = getVersionCachePath(filename);
9656
+ if (!existsSync4(path31)) return null;
9657
+ const parsed = JSON.parse(readFileSync5(path31, "utf-8"));
9620
9658
  if (typeof parsed.lastCheck !== "number") return null;
9621
9659
  if (parsed.latest !== null && typeof parsed.latest !== "string") return null;
9622
9660
  return parsed;
@@ -9636,10 +9674,10 @@ function isVersionCacheStale(filename = CLI_CACHE_FILE, maxAgeMs = MAX_AGE_24H_M
9636
9674
  return Date.now() - cache.lastCheck > maxAgeMs;
9637
9675
  }
9638
9676
  function writeVersionCache(filename, cache) {
9639
- const path30 = getVersionCachePath(filename);
9640
- const dir = dirname3(path30);
9677
+ const path31 = getVersionCachePath(filename);
9678
+ const dir = dirname3(path31);
9641
9679
  if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
9642
- writeFileSync2(path30, JSON.stringify(cache));
9680
+ writeFileSync2(path31, JSON.stringify(cache));
9643
9681
  }
9644
9682
  function touchVersionCache(filename) {
9645
9683
  writeVersionCache(filename, { lastCheck: Date.now(), latest: readVersionCache(filename)?.latest ?? null });
@@ -9678,14 +9716,14 @@ function parseFrontmatterVersion(content) {
9678
9716
  function findInstalledSkills(projectRoot, paths = SKILL_INSTALL_PATHS) {
9679
9717
  const found = [];
9680
9718
  for (const rel of paths) {
9681
- const path30 = join7(projectRoot, rel);
9682
- if (!existsSync5(path30)) continue;
9719
+ const path31 = join7(projectRoot, rel);
9720
+ if (!existsSync5(path31)) continue;
9683
9721
  let version = null;
9684
9722
  try {
9685
- version = parseFrontmatterVersion(readFileSync6(path30, "utf-8"));
9723
+ version = parseFrontmatterVersion(readFileSync6(path31, "utf-8"));
9686
9724
  } catch {
9687
9725
  }
9688
- found.push({ path: path30, version });
9726
+ found.push({ path: path31, version });
9689
9727
  }
9690
9728
  return found;
9691
9729
  }
@@ -11304,12 +11342,12 @@ function typeMatches2(typeField, allowed) {
11304
11342
  if (Array.isArray(typeField)) return typeField.every((t) => typeof t === "string" && allowed.has(t));
11305
11343
  return false;
11306
11344
  }
11307
- function validateSchema2(schema, path30, errors, opts = {}) {
11345
+ function validateSchema2(schema, path31, errors, opts = {}) {
11308
11346
  if (typeof schema === "boolean") return;
11309
11347
  const depth = opts.depth ?? 0;
11310
11348
  if (depth > MAX_SCHEMA_DEPTH2) {
11311
11349
  errors.push({
11312
- path: path30 || "<root>",
11350
+ path: path31 || "<root>",
11313
11351
  message: `schema nesting exceeds ${MAX_SCHEMA_DEPTH2} levels`,
11314
11352
  suggestion: "Flatten the schema; LLM providers reject deeply nested tool input_schemas."
11315
11353
  });
@@ -11317,7 +11355,7 @@ function validateSchema2(schema, path30, errors, opts = {}) {
11317
11355
  }
11318
11356
  if (!isRecord2(schema)) {
11319
11357
  errors.push({
11320
- path: path30,
11358
+ path: path31,
11321
11359
  message: `expected object, got ${schema === null ? "null" : typeof schema}`
11322
11360
  });
11323
11361
  return;
@@ -11325,14 +11363,14 @@ function validateSchema2(schema, path30, errors, opts = {}) {
11325
11363
  if (opts.isRoot) {
11326
11364
  if ("type" in schema && schema.type !== "object") {
11327
11365
  errors.push({
11328
- path: path30 ? `${path30}.type` : "type",
11366
+ path: path31 ? `${path31}.type` : "type",
11329
11367
  message: `root type must be 'object', got ${JSON.stringify(schema.type)}`,
11330
11368
  suggestion: "Tool parameters at the root must be an object: `{ type: 'object', properties: { ... } }`."
11331
11369
  });
11332
11370
  }
11333
11371
  } else if ("type" in schema && !typeMatches2(schema.type, ALLOWED_TYPES2)) {
11334
11372
  errors.push({
11335
- path: `${path30}.type`,
11373
+ path: `${path31}.type`,
11336
11374
  message: `type must be one of ${[...ALLOWED_TYPES2].join("/")} or an array of those, got ${JSON.stringify(schema.type)}`
11337
11375
  });
11338
11376
  }
@@ -11341,25 +11379,25 @@ function validateSchema2(schema, path30, errors, opts = {}) {
11341
11379
  if (typeof e === "string") {
11342
11380
  const isPlaceholder = PLACEHOLDER_TOKENS2.includes(e);
11343
11381
  errors.push({
11344
- path: `${path30}.enum`,
11382
+ path: `${path31}.enum`,
11345
11383
  message: `enum must be a non-empty array of primitives, got string ${JSON.stringify(e)}`,
11346
11384
  suggestion: isPlaceholder ? "Set `operation: 'update_kanban_status'` on the tool and ensure at least one hub.kanban_status has `allowsAgentUpdate: true` so the platform can render the placeholder into a valid array." : 'Wrap the value in an array: `enum: ["value"]`.'
11347
11385
  });
11348
11386
  } else if (!Array.isArray(e)) {
11349
11387
  errors.push({
11350
- path: `${path30}.enum`,
11388
+ path: `${path31}.enum`,
11351
11389
  message: `enum must be a non-empty array of primitives, got ${typeof e}`
11352
11390
  });
11353
11391
  } else if (e.length === 0) {
11354
11392
  errors.push({
11355
- path: `${path30}.enum`,
11393
+ path: `${path31}.enum`,
11356
11394
  message: "enum must not be empty"
11357
11395
  });
11358
11396
  } else {
11359
11397
  for (let i = 0; i < e.length; i++) {
11360
11398
  if (!isPrimitive2(e[i])) {
11361
11399
  errors.push({
11362
- path: `${path30}.enum[${i}]`,
11400
+ path: `${path31}.enum[${i}]`,
11363
11401
  message: `enum entry must be a primitive (string/number/boolean/null), got ${typeof e[i]}`
11364
11402
  });
11365
11403
  }
@@ -11369,7 +11407,7 @@ function validateSchema2(schema, path30, errors, opts = {}) {
11369
11407
  for (const key of ["exclusiveMinimum", "exclusiveMaximum"]) {
11370
11408
  if (key in schema && typeof schema[key] === "boolean") {
11371
11409
  errors.push({
11372
- path: `${path30}.${key}`,
11410
+ path: `${path31}.${key}`,
11373
11411
  message: `${key} as boolean is draft-04 syntax; draft 2020-12 requires a numeric value`,
11374
11412
  suggestion: `Replace with the numeric bound, e.g. \`${key}: 0\`.`
11375
11413
  });
@@ -11378,45 +11416,45 @@ function validateSchema2(schema, path30, errors, opts = {}) {
11378
11416
  if ("properties" in schema) {
11379
11417
  if (!isRecord2(schema.properties)) {
11380
11418
  errors.push({
11381
- path: `${path30}.properties`,
11419
+ path: `${path31}.properties`,
11382
11420
  message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
11383
11421
  });
11384
11422
  } else {
11385
11423
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
11386
- validateSchema2(propSchema, `${path30}.properties.${propName}`, errors, { depth: depth + 1 });
11424
+ validateSchema2(propSchema, `${path31}.properties.${propName}`, errors, { depth: depth + 1 });
11387
11425
  }
11388
11426
  }
11389
11427
  }
11390
11428
  if (schemaTypeIncludes2(schema, "array") && "items" in schema) {
11391
11429
  if (Array.isArray(schema.items)) {
11392
- schema.items.forEach((sub, i) => validateSchema2(sub, `${path30}.items[${i}]`, errors, { depth: depth + 1 }));
11430
+ schema.items.forEach((sub, i) => validateSchema2(sub, `${path31}.items[${i}]`, errors, { depth: depth + 1 }));
11393
11431
  } else {
11394
- validateSchema2(schema.items, `${path30}.items`, errors, { depth: depth + 1 });
11432
+ validateSchema2(schema.items, `${path31}.items`, errors, { depth: depth + 1 });
11395
11433
  }
11396
11434
  }
11397
11435
  for (const key of SUBSCHEMA_OBJECT_KEYWORDS2) {
11398
11436
  if (key in schema && isRecord2(schema[key])) {
11399
- validateSchema2(schema[key], `${path30}.${key}`, errors, { depth: depth + 1 });
11437
+ validateSchema2(schema[key], `${path31}.${key}`, errors, { depth: depth + 1 });
11400
11438
  }
11401
11439
  }
11402
11440
  for (const key of SUBSCHEMA_LIST_KEYWORDS2) {
11403
11441
  const list = schema[key];
11404
11442
  if (Array.isArray(list)) {
11405
- list.forEach((sub, i) => validateSchema2(sub, `${path30}.${key}[${i}]`, errors, { depth: depth + 1 }));
11443
+ list.forEach((sub, i) => validateSchema2(sub, `${path31}.${key}[${i}]`, errors, { depth: depth + 1 }));
11406
11444
  }
11407
11445
  }
11408
11446
  for (const key of SUBSCHEMA_MAP_KEYWORDS2) {
11409
11447
  const map = schema[key];
11410
11448
  if (isRecord2(map)) {
11411
11449
  for (const [name, sub] of Object.entries(map)) {
11412
- validateSchema2(sub, `${path30}.${key}.${name}`, errors, { depth: depth + 1 });
11450
+ validateSchema2(sub, `${path31}.${key}.${name}`, errors, { depth: depth + 1 });
11413
11451
  }
11414
11452
  }
11415
11453
  }
11416
11454
  const reportedPaths = new Set(errors.map((e) => e.path));
11417
11455
  for (const [k, v] of Object.entries(schema)) {
11418
11456
  if (typeof v !== "string") continue;
11419
- const fieldPath = `${path30}.${k}`;
11457
+ const fieldPath = `${path31}.${k}`;
11420
11458
  if (reportedPaths.has(fieldPath)) continue;
11421
11459
  for (const token of PLACEHOLDER_TOKENS2) {
11422
11460
  if (v === token) {
@@ -11690,10 +11728,10 @@ function refineHubAsCodeEvals2(config, ctx) {
11690
11728
  function refineHubAsCodeEvalAttachments2(config, ctx) {
11691
11729
  const cfg = config;
11692
11730
  const referencedHashes = /* @__PURE__ */ new Set();
11693
- const addTurnIssue = (path30, turn, name) => {
11731
+ const addTurnIssue = (path31, turn, name) => {
11694
11732
  const error = evalTurnAttachmentsError2(turn);
11695
11733
  if (error) {
11696
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path30, message: `${name}: ${error}` });
11734
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path31, message: `${name}: ${error}` });
11697
11735
  return;
11698
11736
  }
11699
11737
  for (const hash of collectTurnAttachmentHashes2(turn)) referencedHashes.add(hash);
@@ -11814,10 +11852,10 @@ function refineHubAsCodeResources2(config, ctx) {
11814
11852
  });
11815
11853
  }
11816
11854
  }
11817
- function isSafeTemplatePath2(path30) {
11818
- if (path30.length === 0 || path30.length > 300) return false;
11819
- if (path30.startsWith("/") || path30.includes("\\")) return false;
11820
- const segments = path30.split("/");
11855
+ function isSafeTemplatePath2(path31) {
11856
+ if (path31.length === 0 || path31.length > 300) return false;
11857
+ if (path31.startsWith("/") || path31.includes("\\")) return false;
11858
+ const segments = path31.split("/");
11821
11859
  return segments.every(
11822
11860
  (seg) => seg.length > 0 && seg !== "." && seg !== ".." && /^[A-Za-z0-9._-]+$/.test(seg)
11823
11861
  );
@@ -11845,14 +11883,14 @@ function isPlainObject(value) {
11845
11883
  const proto = Object.getPrototypeOf(value);
11846
11884
  return proto === Object.prototype || proto === null;
11847
11885
  }
11848
- function isSanitizableSliceFile(path30) {
11849
- if (!/\.ya?ml$/i.test(path30)) return false;
11850
- return path30 !== "resources" && !path30.startsWith("resources/");
11886
+ function isSanitizableSliceFile(path31) {
11887
+ if (!/\.ya?ml$/i.test(path31)) return false;
11888
+ return path31 !== "resources" && !path31.startsWith("resources/");
11851
11889
  }
11852
11890
  function distinctUsesBackends(uses) {
11853
11891
  return [...new Set((uses ?? []).map((u) => u.backend))];
11854
11892
  }
11855
- var uuidSchema2, paginationSchema3, timestampSchema2, idParamSchema2, clientOptionalString2, clientOptionalBoolean2, clientOptionalNumber2, messageAttachment2, MAX_MESSAGE_BODY_BYTES2, MAX_ATTACHMENTS_PER_MESSAGE2, primaryRegionSchema2, placementSourceSchema2, orgIdParam2, orgAdminIdParam2, createOrganizationBody2, updateOrganizationBody2, addOrgAdminBody2, AUTH_TYPE3, ORG_CREDENTIAL_AUTH_TYPES3, AUTH_TYPE_DISPLAY3, LEGACY_AUTH_TYPE_MAP3, VALID_AUTH_TYPES3, AGENT_ROLES2, SAFE_USER_ID_REGEX2, SUPPORTED_LOCALES2, SUMMARIZATION_THRESHOLD_MIN2, SUMMARIZATION_THRESHOLD_MAX2, summarizationThresholdField2, monitorConfigField2, agentIdParam2, listAgentsQuery2, agentConnectionsQuery2, createAgentBody2, updateAgentBody2, AGENT_PARAMETER_TYPES2, AGENT_PARAMETER_NAME_REGEX2, agentParameterCreateSchema2, MAX_AGENT_PARAMETERS_PER_REQUEST2, createAgentParametersBody2, SLUG_REGEX2, slugSchema2, INVISIBLE_NAME_CHARS2, MAX_KANBAN_STATUSES2, MAX_FOLLOWUPS_PER_STATUS2, MAX_LANES2, MAX_ADDITIONAL_CONTEXT_SCHEMA_BYTES2, RESERVED_TEMPLATE_DUMP_NAME2, kanbanStatusSlugSchema2, followupSchema2, EXCLUSIVE_FLAG_PAIRS2, kanbanStatusSchema2, kanbanStatusesArraySchema2, laneSchema2, lanesArraySchema2, hubIdParam2, hubAdminIdParam2, hubUserIdParam2, hubIdentityIdParam2, hubTeamIdParam2, hubTeamUserDeleteParam2, hubSchemaQuery2, PREVIEW_LABEL_MAX_LENGTH2, MAX_ENDED_INDEX_RETENTION_DAYS2, previewLabelField2, createHubBody2, updateHubBody2, addHubAdminBody2, addHubIdentityAuthorizationBody2, contactDecisionBody2, approveContactBody2, listContactAccessQuery2, createHubTeamBody2, updateHubTeamBody2, addHubTeamUserBody2, reassignHubUserBody2, replicatePreviewBody2, updateChannelTestIdentitiesBody2, claimCodeChannelParam2, claimCodeDeleteParam2, connectionIdParam2, connectorIdParam2, listConnectionsQuery2, deleteConnectionQuery2, addConnectionBody2, editConnectionBody2, PRODUCTION_DIRECT_FIELDS2, setProductionCredentialBody2, registerWhatsappQuery2, registerWhatsappBody2, retryProvisioningQuery2, resendDomainStatusQuery2, PLACEHOLDER_TOKENS2, ALLOWED_TYPES2, SUBSCHEMA_OBJECT_KEYWORDS2, SUBSCHEMA_LIST_KEYWORDS2, SUBSCHEMA_MAP_KEYWORDS2, MAX_SCHEMA_DEPTH2, toolIdParam2, listToolsQuery2, toolAgentQuery2, deleteToolQuery2, toolConnectionsQuery2, nativeToolIdSchema2, addNativeToolBody2, addMcpToolBody2, createCustomToolBody2, updateCustomToolBody2, updateToolExecutionConfigBody2, initialValueCreateSchema2, initialValueUpdateSchema2, stateIdParam2, listStatesQuery2, stateNameSchema2, createStateBody2, updateStateBody2, reorderStatesBody2, variantAiModeSchema2, experimentStatusSchema2, overlayUpdateSchema2, experimentIdParam2, variantIdParam2, listExperimentsQuery2, listVariantsQuery2, listOverridesQuery2, deleteOverrideQuery2, createExperimentBody2, updateExperimentBody2, setExperimentStatusBody2, createVariantBody2, updateVariantBody2, upsertOverrideBody2, orgTagIdParam2, listOrgTagsQuery2, createOrgTagBody2, updateOrgTagBody2, orgCredentialIdParam2, listOrgCredentialsQuery2, createOrgCredentialBody2, updateOrgCredentialBody2, orgResourceIdParam2, orgResourceFileIdParam2, orgResourceFolderIdParam2, hubOrgResourceParam2, listOrgResourcesQuery2, orgIdQuery2, createOrgResourceBody2, updateOrgResourceBody2, createOrgResourceFolderBody2, updateOrgResourceFolderBody2, uploadOrgResourceFileBody2, updateOrgResourceFileBody2, uploadOrgSkillZipBody2, linkOrgResourceBody2, resourceIdParam2, listResourcesQuery2, createResourceBody2, updateResourceBody2, syncSkillsBody2, resourceFileIdParam2, listResourceFilesQuery2, createResourceFileBody2, updateResourceFileBody2, uploadResourceFileBody2, uploadSkillZipBody2, resourceFolderIdParam2, listResourceFoldersQuery2, createResourceFolderBody2, updateResourceFolderBody2, agentResourceQuery2, hubResourceQuery2, linkAgentResourceBody2, updateAgentResourceBody2, unlinkAgentResourceQuery2, navItemSchema2, hubCountResetBody2, navItemCountResetBody2, typingBody2, analyticsHubIdParam2, analyticsVariableIdParam2, updateVariablePinBody2, updateAnalyticsViewBody2, NUMERIC_FILTER_OPS2, TEXT_FILTER_OPS2, CATEGORICAL_FILTER_OPS2, VARIABLE_FILTER_OPS2, STRUCTURED_QUERY_OPS2, STRUCTURED_QUERY_AGGREGATIONS2, filterValueSchema2, conversationsBody2, analyticsConversationIdParam2, messagesQuery2, conversationDetailDataQuery2, analyticsDataBody2, structuredQueryBody2, analyticsSqlBody2, analyticsSqlSchemaQuery2, EVAL_PACING_PRESET_NAMES2, PACING_INTERVAL_MIN_MS2, PACING_INTERVAL_MAX_MS2, EVAL_RUN_DEADLINE_MIN_MS2, EVAL_RUN_DEADLINE_MAX_MS2, paginationSchema22, evalIdParam2, sessionIdParam2, scenarioSetIdParam2, hubIdQuery2, EVAL_INPUT_ROLES2, EVAL_INPUT_ROLE_LIST2, ROLE_ECHO_MAX2, createEvalBody2, updateEvalBody2, getEvalsQuery2, toggleEvalBody2, bulkToggleEvalsBody2, pacingConfigSchema2, sessionConfigSchema2, createSessionBody2, getSessionsQuery2, getSessionResultsQuery2, getSessionRunsQuery2, evalAnalyticsQuery2, getSessionCostQuery2, compareSessionsQuery2, evalsSqlBody2, evalsSqlSchemaQuery2, exportResultsQuery2, validateEvalBody2, hubAgentsQuery2, createScenarioSetBody2, updateScenarioSetBody2, scenarioSetsHubQuery2, createScenarioFromConversationBody2, createJourneyFromConversationBody2, EVAL_ATTACHMENT_HASH_REGEX2, evalAttachmentRef2, turnMayCarryAttachments2, ATTACHMENTS_USER_ONLY_MESSAGE2, journeyIdParam2, journeyToolCallSchema2, journeyTurnSchema2, journeyStepOverrideSchema2, fixtureField2, createJourneyBody2, updateJourneyBody2, getConversationsQuery2, getMessagesQuery2, appMessageSenderType2, appMessageReceiverType2, appMessageBody2, updateUserBody2, updateProfileBody2, pathnameRegex2, updatePreferencesBody2, registerPushTokenBody2, deletePushTokenQuery2, activityQuery2, deletionModeSchema2, deletionRequestBody2, archiveHubIdParam2, archiveConversationParams2, archiveListQuery2, archiveMessageObservabilityParams2, conversationIdParam2, claimBody2, releaseBody2, transferBody2, closeBody2, additionalContextPayloadSchema2, kanbanUpdateBody2, annotateConversationBody2, observabilityParams2, observabilityListParams2, deleteUserConversationsBody2, sendMessageBody2, listConversationsQuery2, getConversationDetailParams2, getConversationDetailQuery2, listWhatsAppTemplatesQuery2, sendWhatsAppTemplateBody2, billingOrgIdParam2, subscriptionItemParams2, spendCapBody2, growthPacksBody2, portalSessionBody2, checkoutSessionBody2, updateBillingCurrencyBody2, invoicesQuery2, downloadBody2, uploadQuery2, signUrlBody2, outboundSchemaQuery2, templateIdParam2, listTemplatesQuery2, deleteTemplateQuery2, templateStatusQuery2, createTemplateBody2, updateTemplateBody2, submitTemplateBody2, testTemplateBody2, contactIdParam2, listContactsQuery2, createContactBody2, updateContactBody2, listIdParam2, listListsQuery2, listListContactsQuery2, createListBody2, updateListBody2, addListContactsBody2, removeListContactsBody2, scheduleIdParam2, listSchedulesQuery2, listExecutionsQuery2, createScheduleBody2, updateScheduleBody2, MAX_RESOURCE_FILE_SIZE2, MAX_10MB_BASE64_ENCODED_BYTES2, MAX_EVAL_ATTACHMENT_ENCODED_BYTES2, MAX_EVAL_ATTACHMENT_CARRIERS2, MAX_EVAL_ATTACHMENT_AGGREGATE_ENCODED_BYTES2, MAX_RESOURCE_ENCODED_BYTES2, MAX_HUB_AS_CODE_RESOURCES2, MAX_HUB_AS_CODE_RESOURCE_FILES2, MAX_RESOURCE_AGGREGATE_ENCODED_BYTES2, MAX_CI_CONFIG_BODY_BYTES2, hubAsCodeResourceFileSchema2, hubAsCodeResourceSchema2, uuidPattern2, ciUuidSchema2, boundedHubAsCodeResourcesSchema2, hubAsCodeConfigSchema2, ciPullHubIdParam2, ciBranchParam2, ciPullQuery2, ciHubsQuery2, ciLookupQuery2, ciBranchesQuery2, ciSyncBody2, ciPushBody2, ciDiffBody2, ciPublishBody2, ciPublishPreviewBody2, ciSyncMcpBody2, ciOrgResourcesParam2, orgResourcesConfigSchema2, ciOrgResourcesPushBody2, ciOrgResourcesDiffBody2, adminOrgIdParam2, adminOrgAdminIdParam2, adminUserIdParam2, adminPlanIdParam2, adminHubIdParam2, adminOrganizationsQuery2, adminUserSearchQuery2, kanbanSlugMigrationQuery2, adjustQuotaBody2, updatePlanBody2, addAdminUserBody2, updatePlatformConfigBody2, updateFreeOrgLimitBody2, adminSetOrgHarnessBody2, adminSetHubHarnessBody2, adminScopedTargetBody2, updatePricingPlanBody2, dataExplorerSearchQuery2, dataExplorerOrgIdParam2, dataExplorerHubIdParam2, dataExplorerConvIdParam2, dataExplorerDoInstancesQuery2, dataExplorerNsIdParam2, dataExplorerKvKeysQuery2, dataExplorerKvKeyParam2, uuidOrHexId2, dataExplorerDeleteOrgParam2, dataExplorerDeleteHubParam2, dataExplorerDeleteConvParam2, dataExplorerDeleteUserParam2, dataExplorerDeleteQuery2, dataExplorerKvDeleteQuery2, pitrDoNamespace2, pitrBookmarkParam2, pitrRestoreBody2, healthSamplingTriggerBody2, dataExplorerDebugDoType2, DEBUG_READ_MAX_LIMIT2, DEBUG_READ_DEFAULT_LIMIT2, DEBUG_DO_HEX_PREFIX2, debugDoEntityId2, debugTableName2, dataExplorerDebugTablesParam2, dataExplorerDebugRowsParam2, dataExplorerDebugRowsQuery2, dataExplorerDebugAnalyticsTable2, debugUuid2, dataExplorerDebugAnalyticsRowsParam2, dataExplorerDebugAnalyticsRowsQuery2, debugHubConvParam2, debugMessageIdQuery2, sandboxEgressPolicy2, SANDBOX_EXEC_MAX_CMD_LENGTH2, SANDBOX_EXEC_MAX_ALLOWLIST2, SANDBOX_EXEC_MAX_TIMEOUT_MS2, adminSandboxExecBody2, whatsappCallbackBody2, authMeResponse2, wsTicketResponse2, sessionCheckResponse2, logoutResponse2, magicCodeSendBody2, magicCodeSendResponse2, magicCodeVerifyBody2, magicCodeVerifyResponse2, browserParams2, browserFoldersQuery2, browserFilesQuery2, stateOperationBody2, conversationListResourcesQuery2, conversationReadResourceQuery2, listPendingSchedulesQuery2, listHubAggregateSchedulesQuery2, reportSourceSchema2, intakeReportSourceSchema2, reportClassificationSchema2, reportStatusSchema2, reportLocaleSchema2, reportMessageAuthorRoleSchema2, sentryRefStatusSchema2, createReportBody2, editReportBody2, contestReportBody2, reporterListQuery2, listReportsQuery2, GITHUB_ISSUE_URL_REGEX2, githubIssueUrlSchema2, transitionReportBody2, groupReportsBody2, holdReportBody2, clickhouseReadyResponse2, bootstrapQuery2, requestSnapshotChunkMessage2, MAX_NAME_LENGTH3, MAX_VALUE_LENGTH2, MAX_TAGS2, MAX_TAG_LENGTH2, MAX_SCOPE_HUBS2, MAX_SCOPE_TAGS2, vaultSecretScopeSchema2, vaultCreateBody2, vaultUpdateBody2, PERMISSIONS2, MAX_NAME_LENGTH22, MAX_HUBS_PER_GRANT2, MAX_HUB_TAGS_PER_GRANT2, MAX_GRANTS2, permissionEnum2, grantScopeSchema2, grantSchema2, createTokenBody2, tokenOrgIdParam2, tokenGrantScopeSchema2, tokenGrantSchema2, tokenMetadataSchema2, listTokensResponse2, createTokenResponse2, orgTokenSummarySchema2, listOrgTokensResponse2, invitePreviewQuery2, invitePreviewResponse2, discordTier2, discordBillingCycle2, discordMetadata2, discordOAuthStartResponse2, discordOAuthFinalizeBody2, discordOAuthFinalizeResponse2, discordDisconnectResponse2, jsonSchemaSchema2, hubUserIdentity2, stateValueEntry2, getHubUserContextResponse2, updateHubUserBody2, updateHubUserResponse2, unlockHubUserNameResponse2, stateResetParams2, stateResetResponse2, hubAlertsParam2, hubAlertSeverity2, hubAlert2, hubAlertsListResponse2, noticeSeveritySchema2, noticeStatusSchema2, NOTICE_SCOPE_REGEX2, noticeScopeSchema2, noticeLinkSchema2, createNoticeBody2, updateNoticeBody2, listNoticesQuery2, noticeIdParamSchema2, templateLocaleSchema2, TEMPLATE_SLUG_REGEX2, templateSlugSchema2, templateStatusSchema2, templateFileMapSchema2, TEMPLATE_INSTANCE_ID_KEYS, TEMPLATE_OPAQUE_BLOB_KEYS, TEMPLATE_AUTHOR_DATA_KEYS, templateTextSchema2, templateUsesEntrySchema2, templateUsesSchema2, templateYamlLocaleEntry2, templateYamlWayaiBlock2, templateTagsSchema2, templateYamlSchema2, templateHeroSchema2, templatePushLocaleSchema2, templatePushBody2, templateListQuery2, templateSlugParam2, templateDetailQuery2, ADMIN_SKILL_NAME_REGEX2, adminSkillNameParam2;
11893
+ var uuidSchema2, paginationSchema3, timestampSchema2, idParamSchema2, clientOptionalString2, clientOptionalBoolean2, clientOptionalNumber2, messageAttachment2, MAX_MESSAGE_BODY_BYTES2, MAX_ATTACHMENTS_PER_MESSAGE2, primaryRegionSchema2, placementSourceSchema2, orgIdParam2, orgAdminIdParam2, createOrganizationBody2, updateOrganizationBody2, addOrgAdminBody2, AUTH_TYPE3, ORG_CREDENTIAL_AUTH_TYPES3, AUTH_TYPE_DISPLAY3, LEGACY_AUTH_TYPE_MAP3, VALID_AUTH_TYPES3, AGENT_ROLES2, SAFE_USER_ID_REGEX2, SUPPORTED_LOCALES2, SUMMARIZATION_THRESHOLD_MIN2, SUMMARIZATION_THRESHOLD_MAX2, summarizationThresholdField2, monitorConfigField2, agentIdParam2, listAgentsQuery2, agentConnectionsQuery2, createAgentBody2, updateAgentBody2, AGENT_PARAMETER_TYPES2, AGENT_PARAMETER_NAME_REGEX2, agentParameterCreateSchema2, MAX_AGENT_PARAMETERS_PER_REQUEST2, createAgentParametersBody2, SLUG_REGEX2, slugSchema2, INVISIBLE_NAME_CHARS2, MAX_KANBAN_STATUSES2, MAX_FOLLOWUPS_PER_STATUS2, MAX_LANES2, MAX_ADDITIONAL_CONTEXT_SCHEMA_BYTES2, RESERVED_TEMPLATE_DUMP_NAME2, kanbanStatusSlugSchema2, followupSchema2, EXCLUSIVE_FLAG_PAIRS2, kanbanStatusSchema2, kanbanStatusesArraySchema2, laneSchema2, lanesArraySchema2, hubIdParam2, hubAdminIdParam2, hubUserIdParam2, hubIdentityIdParam2, hubTeamIdParam2, hubTeamUserDeleteParam2, hubSchemaQuery2, PREVIEW_LABEL_MAX_LENGTH2, MAX_ENDED_INDEX_RETENTION_DAYS2, previewLabelField2, createHubBody2, updateHubBody2, addHubAdminBody2, addHubIdentityAuthorizationBody2, contactDecisionBody2, approveContactBody2, listContactAccessQuery2, createHubTeamBody2, updateHubTeamBody2, addHubTeamUserBody2, reassignHubUserBody2, replicatePreviewBody2, updateChannelTestIdentitiesBody2, claimCodeChannelParam2, claimCodeDeleteParam2, connectionIdParam2, connectorIdParam2, listConnectionsQuery2, deleteConnectionQuery2, addConnectionBody2, editConnectionBody2, PRODUCTION_DIRECT_FIELDS2, setProductionCredentialBody2, registerWhatsappQuery2, registerWhatsappBody2, retryProvisioningQuery2, resendDomainStatusQuery2, PLACEHOLDER_TOKENS2, ALLOWED_TYPES2, SUBSCHEMA_OBJECT_KEYWORDS2, SUBSCHEMA_LIST_KEYWORDS2, SUBSCHEMA_MAP_KEYWORDS2, MAX_SCHEMA_DEPTH2, toolIdParam2, listToolsQuery2, toolAgentQuery2, deleteToolQuery2, toolConnectionsQuery2, nativeToolIdSchema2, addNativeToolBody2, addMcpToolBody2, createCustomToolBody2, updateCustomToolBody2, updateToolExecutionConfigBody2, initialValueCreateSchema2, initialValueUpdateSchema2, stateIdParam2, listStatesQuery2, stateNameSchema2, createStateBody2, updateStateBody2, reorderStatesBody2, variantAiModeSchema2, experimentStatusSchema2, overlayUpdateSchema2, experimentIdParam2, variantIdParam2, listExperimentsQuery2, listVariantsQuery2, listOverridesQuery2, deleteOverrideQuery2, createExperimentBody2, updateExperimentBody2, setExperimentStatusBody2, createVariantBody2, updateVariantBody2, upsertOverrideBody2, orgTagIdParam2, listOrgTagsQuery2, createOrgTagBody2, updateOrgTagBody2, orgCredentialIdParam2, listOrgCredentialsQuery2, createOrgCredentialBody2, updateOrgCredentialBody2, orgResourceIdParam2, orgResourceFileIdParam2, orgResourceFolderIdParam2, hubOrgResourceParam2, listOrgResourcesQuery2, orgIdQuery2, createOrgResourceBody2, updateOrgResourceBody2, createOrgResourceFolderBody2, updateOrgResourceFolderBody2, uploadOrgResourceFileBody2, updateOrgResourceFileBody2, uploadOrgSkillZipBody2, linkOrgResourceBody2, resourceIdParam2, listResourcesQuery2, createResourceBody2, updateResourceBody2, syncSkillsBody2, resourceFileIdParam2, listResourceFilesQuery2, createResourceFileBody2, updateResourceFileBody2, uploadResourceFileBody2, uploadSkillZipBody2, resourceFolderIdParam2, listResourceFoldersQuery2, createResourceFolderBody2, updateResourceFolderBody2, agentResourceQuery2, hubResourceQuery2, linkAgentResourceBody2, updateAgentResourceBody2, unlinkAgentResourceQuery2, navItemSchema2, hubCountResetBody2, navItemCountResetBody2, typingBody2, analyticsHubIdParam2, analyticsVariableIdParam2, updateVariablePinBody2, updateAnalyticsViewBody2, NUMERIC_FILTER_OPS2, TEXT_FILTER_OPS2, CATEGORICAL_FILTER_OPS2, VARIABLE_FILTER_OPS2, STRUCTURED_QUERY_OPS2, STRUCTURED_QUERY_AGGREGATIONS2, filterValueSchema2, conversationsBody2, analyticsConversationIdParam2, messagesQuery2, conversationDetailDataQuery2, analyticsDataBody2, structuredQueryBody2, analyticsSqlBody2, analyticsSqlSchemaQuery2, EVAL_PACING_PRESET_NAMES2, PACING_INTERVAL_MIN_MS2, PACING_INTERVAL_MAX_MS2, EVAL_RUN_DEADLINE_MIN_MS2, EVAL_RUN_DEADLINE_MAX_MS2, paginationSchema22, evalIdParam2, sessionIdParam2, scenarioSetIdParam2, hubIdQuery2, EVAL_INPUT_ROLES2, EVAL_INPUT_ROLE_LIST2, ROLE_ECHO_MAX2, createEvalBody2, updateEvalBody2, getEvalsQuery2, toggleEvalBody2, bulkToggleEvalsBody2, pacingConfigSchema2, sessionConfigSchema2, createSessionBody2, getSessionsQuery2, getSessionResultsQuery2, getSessionRunsQuery2, evalAnalyticsQuery2, getSessionCostQuery2, compareSessionsQuery2, evalsSqlBody2, evalsSqlSchemaQuery2, exportResultsQuery2, validateEvalBody2, hubAgentsQuery2, createScenarioSetBody2, updateScenarioSetBody2, scenarioSetsHubQuery2, createScenarioFromConversationBody2, createJourneyFromConversationBody2, EVAL_ATTACHMENT_HASH_REGEX2, evalAttachmentRef2, turnMayCarryAttachments2, ATTACHMENTS_USER_ONLY_MESSAGE2, journeyIdParam2, journeyToolCallSchema2, journeyTurnSchema2, journeyStepOverrideSchema2, fixtureField2, createJourneyBody2, updateJourneyBody2, getConversationsQuery2, getMessagesQuery2, appMessageSenderType2, appMessageReceiverType2, appMessageBody2, updateUserBody2, updateProfileBody2, pathnameRegex2, updatePreferencesBody2, registerPushTokenBody2, deletePushTokenQuery2, activityQuery2, deletionModeSchema2, deletionRequestBody2, archiveHubIdParam2, archiveConversationParams2, archiveListQuery2, archiveMessageObservabilityParams2, conversationIdParam2, claimBody2, releaseBody2, transferBody2, closeBody2, additionalContextPayloadSchema2, kanbanUpdateBody2, annotateConversationBody2, observabilityParams2, observabilityListParams2, deleteUserConversationsBody2, sendMessageBody2, listConversationsQuery2, getConversationDetailParams2, getConversationDetailQuery2, listWhatsAppTemplatesQuery2, sendWhatsAppTemplateBody2, billingOrgIdParam2, subscriptionItemParams2, spendCapBody2, growthPacksBody2, portalSessionBody2, checkoutSessionBody2, updateBillingCurrencyBody2, invoicesQuery2, downloadBody2, uploadQuery2, signUrlBody2, outboundSchemaQuery2, templateIdParam2, listTemplatesQuery2, deleteTemplateQuery2, templateStatusQuery2, createTemplateBody2, updateTemplateBody2, submitTemplateBody2, testTemplateBody2, contactIdParam2, listContactsQuery2, createContactBody2, updateContactBody2, listIdParam2, listListsQuery2, listListContactsQuery2, createListBody2, updateListBody2, addListContactsBody2, removeListContactsBody2, scheduleIdParam2, listSchedulesQuery2, listExecutionsQuery2, createScheduleBody2, updateScheduleBody2, MAX_RESOURCE_FILE_SIZE2, MAX_10MB_BASE64_ENCODED_BYTES2, MAX_EVAL_ATTACHMENT_ENCODED_BYTES2, MAX_EVAL_ATTACHMENT_CARRIERS2, MAX_EVAL_ATTACHMENT_AGGREGATE_ENCODED_BYTES2, MAX_RESOURCE_ENCODED_BYTES2, MAX_HUB_AS_CODE_RESOURCES2, MAX_HUB_AS_CODE_RESOURCE_FILES2, MAX_RESOURCE_AGGREGATE_ENCODED_BYTES2, MAX_CI_CONFIG_BODY_BYTES2, hubAsCodeResourceFileSchema2, hubAsCodeResourceSchema2, uuidPattern2, ciUuidSchema2, boundedHubAsCodeResourcesSchema2, hubAsCodeConfigSchema2, ciPullHubIdParam2, ciBranchParam2, ciPullQuery2, ciHubsQuery2, ciLookupQuery2, ciBranchesQuery2, ciSyncBody2, ciPushBody2, ciDiffBody2, ciPublishBody2, ciPublishPreviewBody2, ciSyncMcpBody2, ciOrgResourcesParam2, orgResourcesConfigSchema2, ciOrgResourcesPushBody2, ciOrgResourcesDiffBody2, adminOrgIdParam2, adminOrgAdminIdParam2, adminUserIdParam2, adminPlanIdParam2, adminHubIdParam2, adminOrganizationsQuery2, adminUserSearchQuery2, kanbanSlugMigrationQuery2, adjustQuotaBody2, updatePlanBody2, addAdminUserBody2, updatePlatformConfigBody2, updateFreeOrgLimitBody2, adminSetOrgHarnessBody2, adminSetHubHarnessBody2, adminScopedTargetBody2, updatePricingPlanBody2, dataExplorerSearchQuery2, dataExplorerOrgIdParam2, dataExplorerHubIdParam2, dataExplorerConvIdParam2, dataExplorerDoInstancesQuery2, dataExplorerNsIdParam2, dataExplorerKvKeysQuery2, dataExplorerKvKeyParam2, uuidOrHexId2, dataExplorerDeleteOrgParam2, dataExplorerDeleteHubParam2, dataExplorerDeleteConvParam2, dataExplorerDeleteUserParam2, dataExplorerDeleteQuery2, dataExplorerKvDeleteQuery2, pitrDoNamespace2, pitrBookmarkParam2, pitrRestoreBody2, healthSamplingTriggerBody2, dataExplorerDebugDoType2, DEBUG_READ_MAX_LIMIT2, DEBUG_READ_DEFAULT_LIMIT2, DEBUG_DO_HEX_PREFIX2, debugDoEntityId2, debugTableName2, dataExplorerDebugTablesParam2, dataExplorerDebugRowsParam2, dataExplorerDebugRowsQuery2, dataExplorerDebugAnalyticsTable2, debugUuid2, dataExplorerDebugAnalyticsRowsParam2, dataExplorerDebugAnalyticsRowsQuery2, debugHubConvParam2, debugMessageIdQuery2, sandboxEgressPolicy2, SANDBOX_EXEC_MAX_CMD_LENGTH2, SANDBOX_EXEC_MAX_ALLOWLIST2, SANDBOX_EXEC_MAX_TIMEOUT_MS2, adminSandboxExecBody2, whatsappCallbackBody2, authMeResponse2, wsTicketResponse2, sessionCheckResponse2, logoutResponse2, magicCodeSendBody2, magicCodeSendResponse2, magicCodeVerifyBody2, magicCodeVerifyResponse2, browserParams2, browserFoldersQuery2, browserFilesQuery2, stateOperationBody2, conversationListResourcesQuery2, conversationReadResourceQuery2, listPendingSchedulesQuery2, listHubAggregateSchedulesQuery2, reportSourceSchema2, intakeReportSourceSchema2, reportClassificationSchema2, reportStatusSchema2, reportLocaleSchema2, reportMessageAuthorRoleSchema2, reportMessageSchema2, sentryRefStatusSchema2, createReportBody2, editReportBody2, reporterReportSummarySchema2, reporterListResponseSchema2, getReportResponseSchema2, reporterTransitionResponseSchema2, contestReportBody2, reporterListQuery2, listReportsQuery2, GITHUB_ISSUE_URL_REGEX2, githubIssueUrlSchema2, transitionReportBody2, groupReportsBody2, holdReportBody2, clickhouseReadyResponse2, bootstrapQuery2, requestSnapshotChunkMessage2, MAX_NAME_LENGTH3, MAX_VALUE_LENGTH2, MAX_TAGS2, MAX_TAG_LENGTH2, MAX_SCOPE_HUBS2, MAX_SCOPE_TAGS2, vaultSecretScopeSchema2, vaultCreateBody2, vaultUpdateBody2, PERMISSIONS2, MAX_NAME_LENGTH22, MAX_HUBS_PER_GRANT2, MAX_HUB_TAGS_PER_GRANT2, MAX_GRANTS2, permissionEnum2, grantScopeSchema2, grantSchema2, createTokenBody2, tokenOrgIdParam2, tokenGrantScopeSchema2, tokenGrantSchema2, tokenMetadataSchema2, listTokensResponse2, createTokenResponse2, orgTokenSummarySchema2, listOrgTokensResponse2, invitePreviewQuery2, invitePreviewResponse2, discordTier2, discordBillingCycle2, discordMetadata2, discordOAuthStartResponse2, discordOAuthFinalizeBody2, discordOAuthFinalizeResponse2, discordDisconnectResponse2, jsonSchemaSchema2, hubUserIdentity2, stateValueEntry2, getHubUserContextResponse2, updateHubUserBody2, updateHubUserResponse2, unlockHubUserNameResponse2, stateResetParams2, stateResetResponse2, hubAlertsParam2, hubAlertSeverity2, hubAlert2, hubAlertsListResponse2, noticeSeveritySchema2, noticeStatusSchema2, NOTICE_SCOPE_REGEX2, noticeScopeSchema2, noticeLinkSchema2, createNoticeBody2, updateNoticeBody2, listNoticesQuery2, noticeIdParamSchema2, templateLocaleSchema2, TEMPLATE_SLUG_REGEX2, templateSlugSchema2, templateStatusSchema2, templateFileMapSchema2, TEMPLATE_INSTANCE_ID_KEYS, TEMPLATE_OPAQUE_BLOB_KEYS, TEMPLATE_AUTHOR_DATA_KEYS, templateTextSchema2, templateUsesEntrySchema2, templateUsesSchema2, templateYamlLocaleEntry2, templateYamlWayaiBlock2, templateTagsSchema2, templateYamlSchema2, templateHeroSchema2, templatePushLocaleSchema2, templatePushBody2, templateListQuery2, templateSlugParam2, templateDetailQuery2, ADMIN_SKILL_NAME_REGEX2, adminSkillNameParam2;
11856
11894
  var init_contracts = __esm({
11857
11895
  "../../packages/core/dist/contracts/index.js"() {
11858
11896
  "use strict";
@@ -14161,6 +14199,20 @@ var init_contracts = __esm({
14161
14199
  ]);
14162
14200
  reportLocaleSchema2 = external_exports.enum(["en", "pt", "es"]);
14163
14201
  reportMessageAuthorRoleSchema2 = external_exports.enum(["reporter", "triage", "fixer", "system"]);
14202
+ reportMessageSchema2 = external_exports.object({
14203
+ message_id: external_exports.string(),
14204
+ author_role: reportMessageAuthorRoleSchema2,
14205
+ /** The status transition this note accompanied (e.g. 'shipped'); null for a free/system note. */
14206
+ transition: external_exports.string().nullable(),
14207
+ body: external_exports.string(),
14208
+ created_at: timestampSchema2,
14209
+ /**
14210
+ * Visibility: `false` = reporter-facing (the default for every narrative entry); `true` =
14211
+ * triage-internal (e.g. a `hold` rationale) — surfaced to platform admins only and NEVER
14212
+ * returned on the reporter read path. The reporter's `GET /api/reports/:id` filters these out.
14213
+ */
14214
+ internal: external_exports.boolean()
14215
+ });
14164
14216
  sentryRefStatusSchema2 = external_exports.enum(["valid", "not_found", "mismatch", "unverified"]);
14165
14217
  createReportBody2 = external_exports.object({
14166
14218
  title: external_exports.string().min(5).max(256),
@@ -14192,6 +14244,30 @@ var init_contracts = __esm({
14192
14244
  }).strict().refine((b) => Object.keys(b).length > 0, {
14193
14245
  message: "at least one field must be provided"
14194
14246
  });
14247
+ reporterReportSummarySchema2 = external_exports.object({
14248
+ report_id: external_exports.string(),
14249
+ title: external_exports.string(),
14250
+ description: external_exports.string(),
14251
+ classification: reportClassificationSchema2,
14252
+ status: reportStatusSchema2,
14253
+ created_at: timestampSchema2,
14254
+ triaged_at: timestampSchema2.nullable(),
14255
+ resolved_at: timestampSchema2.nullable(),
14256
+ /** How many times the reporter has contested (gates the hard cap). */
14257
+ contest_count: external_exports.number(),
14258
+ /** True once triage issued a non-contestable (final) dismissal. */
14259
+ dismissal_final: external_exports.boolean()
14260
+ });
14261
+ reporterListResponseSchema2 = external_exports.object({
14262
+ data: external_exports.array(reporterReportSummarySchema2)
14263
+ });
14264
+ getReportResponseSchema2 = external_exports.object({
14265
+ report: reporterReportSummarySchema2,
14266
+ messages: external_exports.array(reportMessageSchema2)
14267
+ });
14268
+ reporterTransitionResponseSchema2 = external_exports.object({
14269
+ report: reporterReportSummarySchema2
14270
+ });
14195
14271
  contestReportBody2 = external_exports.object({
14196
14272
  reason: external_exports.string().min(1).max(5e3)
14197
14273
  });
@@ -14864,897 +14940,951 @@ var init_resource_files = __esm({
14864
14940
  }
14865
14941
  });
14866
14942
 
14867
- // src/lib/yaml-writer.ts
14943
+ // src/lib/eval-attachments.ts
14868
14944
  import * as fs11 from "fs";
14869
14945
  import * as path13 from "path";
14870
- import * as yaml4 from "js-yaml";
14871
- function buildEvalYamlObject(evalEntry, slug) {
14872
- const out = {};
14873
- if (evalEntry.id) out.id = evalEntry.id;
14874
- if (evalEntry.name !== slug) out.name = evalEntry.name;
14875
- out.agent = evalEntry.agent;
14876
- if (evalEntry.agent_id) out.agent_id = evalEntry.agent_id;
14877
- if (typeof evalEntry.runs === "number" && evalEntry.runs !== 1) {
14878
- out.runs = evalEntry.runs;
14879
- }
14880
- if (evalEntry.enabled === false) {
14881
- out.enabled = false;
14882
- }
14883
- if (evalEntry.history && evalEntry.history.length > 0) {
14884
- out.history = evalEntry.history;
14885
- }
14886
- if (typeof evalEntry.fixture === "string" && evalEntry.fixture.trim()) {
14887
- out.fixture = evalEntry.fixture.trim();
14888
- }
14889
- if (typeof evalEntry.target_base === "string" && evalEntry.target_base.trim()) {
14890
- out.target_base = evalEntry.target_base.trim();
14946
+ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
14947
+ const raw = turn.attachments;
14948
+ if (raw === void 0 || raw === null) return void 0;
14949
+ if (!Array.isArray(raw) || raw.length === 0) {
14950
+ if (Array.isArray(raw)) return void 0;
14951
+ throw expected(`${label}: "attachments" must be a list of file paths.`);
14891
14952
  }
14892
- if (evalEntry.variables && Object.keys(evalEntry.variables).length > 0) {
14893
- out.variables = evalEntry.variables;
14953
+ if (turn.role !== "user") {
14954
+ throw expected(`${label}: attachments are only allowed on user turns (this turn is "${String(turn.role)}").`);
14894
14955
  }
14895
- out.input = evalEntry.input;
14896
- out.expected = evalEntry.expected;
14897
- if (evalEntry.evaluator_instructions) {
14898
- out.evaluator_instructions = evalEntry.evaluator_instructions;
14956
+ const refs = [];
14957
+ for (const entry of raw) {
14958
+ if (typeof entry !== "string" || !entry.trim()) {
14959
+ throw expected(`${label}: each attachment must be a hub-relative file path string.`);
14960
+ }
14961
+ const abs = path13.resolve(hubFolder, entry);
14962
+ const root = path13.resolve(hubFolder);
14963
+ if (abs !== root && !abs.startsWith(root + path13.sep)) {
14964
+ throw expected(`${label}: attachment "${entry}" escapes the hub folder.`);
14965
+ }
14966
+ let stat;
14967
+ try {
14968
+ stat = fs11.statSync(abs);
14969
+ } catch {
14970
+ }
14971
+ if (!stat?.isFile()) {
14972
+ throw expected(`${label}: attachment "${entry}" not found.`);
14973
+ }
14974
+ if (!realpathContained(root, abs)) {
14975
+ throw expected(`${label}: attachment "${entry}" resolves outside the hub folder (symlinks are not allowed).`);
14976
+ }
14977
+ if (stat.size > MAX_RESOURCE_FILE_SIZE3) {
14978
+ throw expected(
14979
+ `${label}: attachment "${entry}" is ${(stat.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
14980
+ );
14981
+ }
14982
+ const data = fs11.readFileSync(abs);
14983
+ const hash = computeHash(data);
14984
+ const fileName = path13.basename(abs);
14985
+ const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
14986
+ refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat.size });
14987
+ if (!bytesByHash.has(hash)) {
14988
+ bytesByHash.set(hash, {
14989
+ hash,
14990
+ content_base64: data.toString("base64"),
14991
+ file_name: fileName,
14992
+ mime_type: mimeType,
14993
+ file_size: stat.size
14994
+ });
14995
+ }
14899
14996
  }
14900
- return out;
14997
+ return refs;
14901
14998
  }
14902
- function writeHubFolder(hubFolder, payload, options = {}) {
14903
- const agentsDir = path13.join(hubFolder, "agents");
14904
- if (!fs11.existsSync(hubFolder)) {
14905
- fs11.mkdirSync(hubFolder, { recursive: true });
14906
- }
14907
- if (!fs11.existsSync(agentsDir)) {
14908
- fs11.mkdirSync(agentsDir, { recursive: true });
14909
- }
14910
- const yamlPayload = buildYamlPayload(payload);
14911
- const agentFiles = extractAgentFiles(payload);
14912
- const yamlContent = yaml4.dump(yamlPayload, YAML_DUMP_OPTIONS);
14913
- fs11.writeFileSync(path13.join(hubFolder, "hub.yaml"), yamlContent, "utf-8");
14914
- if (options.seedAgentContext ?? true) {
14915
- writeIfAbsent(path13.join(hubFolder, "AGENTS.md"), perHubAgentsMd(path13.basename(hubFolder)));
14916
- writeIfAbsent(path13.join(hubFolder, "CLAUDE.md"), CLAUDE_MD_SHIM);
14999
+ function applyTurnAttachments(hubFolder, turn, label, bytesByHash) {
15000
+ const refs = resolveTurnAttachments(hubFolder, turn, label, bytesByHash);
15001
+ if (refs) turn.attachments = refs;
15002
+ else delete turn.attachments;
15003
+ }
15004
+ function assertAttachmentsAreLocalPaths(turn, label) {
15005
+ const attachments = turn?.attachments;
15006
+ if (!Array.isArray(attachments)) return;
15007
+ for (const entry of attachments) {
15008
+ if (entry !== null && typeof entry === "object" && "hash" in entry) {
15009
+ throw new Error(
15010
+ `wire-shape attachment ref reached YAML serialization \u2014 CLI bug (${label}): the payload was not run through materializeHubFolder before writing.`
15011
+ );
15012
+ }
14917
15013
  }
14918
- const oldYamlPath = path13.join(hubFolder, "wayai.yaml");
14919
- if (fs11.existsSync(oldYamlPath)) {
14920
- fs11.unlinkSync(oldYamlPath);
15014
+ }
15015
+ function forEachAttachableTurn(payload, fn) {
15016
+ for (const ev of payload.evals ?? []) {
15017
+ if (ev.input) fn(ev.input);
15018
+ for (const t of ev.history ?? []) fn(t);
14921
15019
  }
14922
- const yamlSlugs = writeAgentYamlFiles(agentsDir, payload.agents || []);
14923
- const mdSlugs = /* @__PURE__ */ new Set();
14924
- for (const { slug, content } of agentFiles) {
14925
- mdSlugs.add(slug);
14926
- fs11.writeFileSync(path13.join(agentsDir, `${slug}.md`), content, "utf-8");
15020
+ for (const jr of payload.journeys ?? []) {
15021
+ for (const t of jr.transcript ?? []) fn(t);
14927
15022
  }
14928
- const existingFiles = fs11.readdirSync(agentsDir);
14929
- for (const file of existingFiles) {
14930
- if (file.endsWith(".yaml")) {
14931
- const slug = file.slice(0, -5);
14932
- if (!yamlSlugs.has(slug)) {
14933
- fs11.unlinkSync(path13.join(agentsDir, file));
15023
+ }
15024
+ function rewriteEvalAttachmentsToLocalPaths(payload) {
15025
+ const urlByHash = new Map((payload.eval_attachment_files ?? []).map((f) => [f.hash, f.download_url]));
15026
+ const safeName = (ref) => {
15027
+ const base = path13.basename(ref.file_name);
15028
+ return base === "" || base === "." || base === ".." ? ref.hash.slice(0, 16) : base;
15029
+ };
15030
+ const hashesByName = /* @__PURE__ */ new Map();
15031
+ const collect = (turn) => {
15032
+ if (!Array.isArray(turn.attachments)) return;
15033
+ for (const ref of turn.attachments) {
15034
+ if (!ref?.hash || !ref.file_name) continue;
15035
+ const name = safeName(ref);
15036
+ let set = hashesByName.get(name);
15037
+ if (!set) {
15038
+ set = /* @__PURE__ */ new Set();
15039
+ hashesByName.set(name, set);
14934
15040
  }
14935
- } else if (file.endsWith(".md")) {
14936
- const slug = file.slice(0, -3);
14937
- if (!mdSlugs.has(slug)) {
14938
- fs11.unlinkSync(path13.join(agentsDir, file));
15041
+ set.add(ref.hash);
15042
+ }
15043
+ };
15044
+ forEachAttachableTurn(payload, collect);
15045
+ const pathByKey = /* @__PURE__ */ new Map();
15046
+ const usedLower = /* @__PURE__ */ new Set();
15047
+ const localPathFor = (ref) => {
15048
+ const name = safeName(ref);
15049
+ const key = `${ref.hash} ${name}`;
15050
+ const existing = pathByKey.get(key);
15051
+ if (existing) return existing;
15052
+ const ext = path13.extname(name);
15053
+ const stem = path13.basename(name, ext);
15054
+ const collides = (hashesByName.get(name)?.size ?? 0) > 1;
15055
+ let rel = collides ? `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, 8)}${ext}` : `${ATTACHMENTS_DIR}/${name}`;
15056
+ if (usedLower.has(rel.toLowerCase())) {
15057
+ let n = 12;
15058
+ while (usedLower.has(rel.toLowerCase()) && n <= ref.hash.length) {
15059
+ rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, n)}${ext}`;
15060
+ n += 4;
15061
+ }
15062
+ for (let extra = 2; usedLower.has(rel.toLowerCase()); extra++) {
15063
+ rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash}-${extra}${ext}`;
14939
15064
  }
14940
15065
  }
14941
- }
14942
- writeResourceFiles(hubFolder, payload.resources || []);
14943
- writeEvalYamlFiles(hubFolder, payload.evals || []);
14944
- writeJourneyYamlFiles(hubFolder, payload.journeys || []);
14945
- }
14946
- function setPreviewLabelInHubYaml(hubFolder, label) {
14947
- const yamlPath = resolveHubYamlPath(hubFolder);
14948
- if (!yamlPath) return;
14949
- const obj = yaml4.load(fs11.readFileSync(yamlPath, "utf-8")) ?? {};
14950
- if (label) obj.preview_label = label;
14951
- else delete obj.preview_label;
14952
- fs11.writeFileSync(yamlPath, yaml4.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
14953
- }
14954
- function buildYamlPayload(payload) {
14955
- const result = {
14956
- version: payload.version,
14957
- hub_id: payload.hub_id,
14958
- hub_environment: payload.hub_environment
15066
+ usedLower.add(rel.toLowerCase());
15067
+ pathByKey.set(key, rel);
15068
+ return rel;
14959
15069
  };
14960
- if (payload.preview_label) {
14961
- result.preview_label = payload.preview_label;
14962
- }
14963
- result.hub = payload.hub;
14964
- if (payload.resources && payload.resources.length > 0) {
14965
- result.resources = payload.resources.map((r) => {
14966
- const { files, ...rest } = r;
14967
- return rest;
15070
+ const downloads = /* @__PURE__ */ new Map();
15071
+ forEachAttachableTurn(payload, (turn) => {
15072
+ if (!Array.isArray(turn.attachments)) return;
15073
+ turn.attachments = turn.attachments.map((ref) => {
15074
+ const rel = localPathFor(ref);
15075
+ const url = urlByHash.get(ref.hash);
15076
+ if (url && !downloads.has(rel)) downloads.set(rel, { relPath: rel, url, hash: ref.hash });
15077
+ return rel;
14968
15078
  });
14969
- }
14970
- if (payload.states && payload.states.length > 0) {
14971
- result.states = payload.states;
14972
- }
14973
- if (payload.teams && payload.teams.length > 0) {
14974
- result.teams = payload.teams;
14975
- }
14976
- if (payload.connections && payload.connections.length > 0) {
14977
- result.connections = payload.connections;
14978
- }
14979
- if (payload.outbound_contacts && payload.outbound_contacts.length > 0) {
14980
- result.outbound_contacts = payload.outbound_contacts;
14981
- }
14982
- if (payload.outbound_lists && payload.outbound_lists.length > 0) {
14983
- result.outbound_lists = payload.outbound_lists;
14984
- }
14985
- if (payload.outbound_schedules && payload.outbound_schedules.length > 0) {
14986
- result.outbound_schedules = payload.outbound_schedules;
14987
- }
14988
- return result;
15079
+ });
15080
+ delete payload.eval_attachment_files;
15081
+ return [...downloads.values()];
14989
15082
  }
14990
- function writeAgentYamlFiles(agentsDir, agents) {
14991
- const slugs = /* @__PURE__ */ new Set();
14992
- for (const agent of agents) {
14993
- const slug = slugify(agent.name);
14994
- slugs.add(slug);
14995
- const entry = { ...agent };
14996
- delete entry.instructions;
14997
- const yamlContent = yaml4.dump(entry, YAML_DUMP_OPTIONS);
14998
- fs11.writeFileSync(path13.join(agentsDir, `${slug}.yaml`), yamlContent, "utf-8");
15083
+ async function downloadEvalAttachments(hubFolder, downloads) {
15084
+ const attachDir = path13.resolve(hubFolder, ATTACHMENTS_DIR);
15085
+ let written = 0;
15086
+ if (!ensureRealSubdirNoSymlink(hubFolder, attachDir, downloads.length > 0)) {
15087
+ console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
15088
+ return written;
14999
15089
  }
15000
- return slugs;
15001
- }
15002
- function extractAgentFiles(payload) {
15003
- const files = [];
15004
- for (const agent of payload.agents || []) {
15005
- if (agent.instructions) {
15006
- files.push({
15007
- slug: slugify(agent.name),
15008
- content: agent.instructions
15009
- });
15090
+ if (!fs11.existsSync(attachDir)) return written;
15091
+ for (const dl of downloads) {
15092
+ const abs = path13.resolve(hubFolder, dl.relPath);
15093
+ if (abs !== attachDir && !abs.startsWith(attachDir + path13.sep)) continue;
15094
+ let existing;
15095
+ try {
15096
+ existing = fs11.lstatSync(abs);
15097
+ } catch {
15010
15098
  }
15011
- }
15012
- return files;
15013
- }
15014
- function writeEvalYamlFiles(hubFolder, evals) {
15015
- const evalsDir = path13.join(hubFolder, "evals");
15016
- if (evals.length === 0) {
15017
- if (fs11.existsSync(evalsDir)) {
15018
- cleanEvalOrphans(evalsDir, /* @__PURE__ */ new Set());
15019
- try {
15020
- if (fs11.readdirSync(evalsDir).length === 0) fs11.rmdirSync(evalsDir);
15021
- } catch {
15099
+ if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs11.readFileSync(abs)) === dl.hash) {
15100
+ continue;
15101
+ }
15102
+ try {
15103
+ const res = await fetch(dl.url);
15104
+ if (!res.ok) {
15105
+ console.warn(` Warning: failed to download ${dl.relPath} (HTTP ${res.status})`);
15106
+ continue;
15022
15107
  }
15108
+ if (writeFileNoFollow(hubFolder, abs, Buffer.from(await res.arrayBuffer()))) written++;
15109
+ } catch (err) {
15110
+ console.warn(` Warning: failed to download ${dl.relPath}: ${err instanceof Error ? err.message : String(err)}`);
15023
15111
  }
15024
- return;
15025
15112
  }
15026
- if (!fs11.existsSync(evalsDir)) {
15027
- fs11.mkdirSync(evalsDir, { recursive: true });
15113
+ const keep = new Set(downloads.map((d) => path13.resolve(hubFolder, d.relPath)));
15114
+ for (const name of fs11.readdirSync(attachDir)) {
15115
+ const abs = path13.join(attachDir, name);
15116
+ const st = fs11.lstatSync(abs);
15117
+ if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) fs11.rmSync(abs);
15028
15118
  }
15029
- const writtenRelPaths = /* @__PURE__ */ new Set();
15030
- for (const evalEntry of evals) {
15031
- const slug = slugify(evalEntry.name);
15032
- const setName = evalEntry.path && evalEntry.path.trim().length > 0 ? evalEntry.path : null;
15033
- const relPath = setName ? `${setName}/${slug}.yaml` : `${slug}.yaml`;
15034
- const targetPath = path13.join(evalsDir, relPath);
15035
- if (!targetPath.startsWith(evalsDir + path13.sep)) {
15036
- console.warn(` Warning: skipping eval "${evalEntry.name}" (scenario set "${setName}" escapes evals/ \u2014 possible bad backend data)`);
15037
- continue;
15038
- }
15039
- if (setName) {
15040
- const setDir = path13.join(evalsDir, setName);
15041
- if (!fs11.existsSync(setDir)) {
15042
- fs11.mkdirSync(setDir, { recursive: true });
15043
- }
15044
- }
15045
- const yamlContent = yaml4.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
15046
- fs11.writeFileSync(targetPath, yamlContent, "utf-8");
15047
- writtenRelPaths.add(relPath);
15119
+ return written;
15120
+ }
15121
+ var ATTACHMENTS_DIR;
15122
+ var init_eval_attachments = __esm({
15123
+ "src/lib/eval-attachments.ts"() {
15124
+ "use strict";
15125
+ init_resource_files();
15126
+ init_fs_safety();
15127
+ init_parser();
15128
+ ATTACHMENTS_DIR = "attachments";
15048
15129
  }
15049
- cleanEvalOrphans(evalsDir, writtenRelPaths);
15130
+ });
15131
+
15132
+ // src/lib/parser.ts
15133
+ import * as fs12 from "fs";
15134
+ import * as path14 from "path";
15135
+ import * as yaml4 from "js-yaml";
15136
+ function expected(message) {
15137
+ const err = new Error(message);
15138
+ err.isExpected = true;
15139
+ return err;
15050
15140
  }
15051
- function cleanEvalOrphans(evalsDir, writtenRelPaths) {
15052
- const entries = fs11.readdirSync(evalsDir, { withFileTypes: true });
15053
- for (const entry of entries) {
15054
- const fullPath = path13.join(evalsDir, entry.name);
15055
- if (entry.isFile()) {
15056
- if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
15057
- fs11.unlinkSync(fullPath);
15058
- }
15059
- continue;
15141
+ function parseHubFolder(hubFolder, opts) {
15142
+ const yamlPath = resolveHubYamlPath(hubFolder);
15143
+ if (!yamlPath) {
15144
+ const err = new Error(`hub.yaml not found in ${hubFolder}`);
15145
+ err.isExpected = true;
15146
+ throw err;
15147
+ }
15148
+ const yamlContent = fs12.readFileSync(yamlPath, "utf-8");
15149
+ let config;
15150
+ try {
15151
+ config = yaml4.load(yamlContent);
15152
+ } catch (err) {
15153
+ if (err instanceof yaml4.YAMLException) {
15154
+ const userError = new Error(`Invalid YAML in ${yamlPath}: ${err.message}`);
15155
+ userError.isExpected = true;
15156
+ throw userError;
15060
15157
  }
15061
- if (entry.isDirectory()) {
15062
- const setName = entry.name;
15063
- const subEntries = fs11.readdirSync(fullPath, { withFileTypes: true });
15064
- for (const sub of subEntries) {
15065
- if (sub.isFile() && sub.name.endsWith(".yaml")) {
15066
- const relPath = `${setName}/${sub.name}`;
15067
- if (!writtenRelPaths.has(relPath)) {
15068
- fs11.unlinkSync(path13.join(fullPath, sub.name));
15069
- }
15070
- }
15158
+ throw err;
15159
+ }
15160
+ if (!config || typeof config !== "object") {
15161
+ const invalidError = new Error(`Invalid YAML in ${yamlPath}`);
15162
+ invalidError.isExpected = true;
15163
+ throw invalidError;
15164
+ }
15165
+ if (!opts?.allowNewHub) {
15166
+ if (!config.hub_id) {
15167
+ const err = new Error(`Missing hub_id in ${yamlPath}`);
15168
+ err.isExpected = true;
15169
+ throw err;
15170
+ }
15171
+ if (!config.hub_environment) {
15172
+ const err = new Error(`Missing hub_environment in ${yamlPath}`);
15173
+ err.isExpected = true;
15174
+ throw err;
15175
+ }
15176
+ }
15177
+ const agentYamlFiles = scanAgentYamlFiles(hubFolder);
15178
+ const rawAgents = agentYamlFiles.length > 0 ? agentYamlFiles : config.agents;
15179
+ const agents = rawAgents?.map((agent) => {
15180
+ const resolved = { ...agent };
15181
+ if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
15182
+ const instrValue = resolved.instructions;
15183
+ const instructionsPath = instrValue.startsWith("agents/") ? path14.join(hubFolder, instrValue) : path14.join(hubFolder, "agents", instrValue);
15184
+ if (fs12.existsSync(instructionsPath)) {
15185
+ resolved.instructions = fs12.readFileSync(instructionsPath, "utf-8");
15186
+ } else {
15187
+ const err = new Error(
15188
+ `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
15189
+ );
15190
+ err.isExpected = true;
15191
+ throw err;
15071
15192
  }
15072
- try {
15073
- if (fs11.readdirSync(fullPath).length === 0) fs11.rmdirSync(fullPath);
15074
- } catch {
15193
+ } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
15194
+ const conventionPath = path14.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
15195
+ if (fs12.existsSync(conventionPath)) {
15196
+ resolved.instructions = fs12.readFileSync(conventionPath, "utf-8");
15075
15197
  }
15076
15198
  }
15199
+ return resolved;
15200
+ });
15201
+ const resources = parseResources(hubFolder, config.resources || []);
15202
+ const payload = {
15203
+ version: config.version || 1,
15204
+ hub_id: config.hub_id || "",
15205
+ hub_environment: config.hub_environment || "preview",
15206
+ hub: config.hub
15207
+ };
15208
+ if (config.preview_label) {
15209
+ payload.preview_label = config.preview_label;
15077
15210
  }
15078
- }
15079
- function buildJourneyYamlObject(journeyEntry, slug) {
15080
- const out = {};
15081
- if (journeyEntry.id) out.id = journeyEntry.id;
15082
- if (journeyEntry.name !== slug) out.name = journeyEntry.name;
15083
- out.agent = journeyEntry.agent;
15084
- if (journeyEntry.agent_id) out.agent_id = journeyEntry.agent_id;
15085
- if (journeyEntry.enabled === false) {
15086
- out.enabled = false;
15211
+ if (agents && agents.length > 0) {
15212
+ payload.agents = agents;
15087
15213
  }
15088
- if (journeyEntry.evaluator_instructions) {
15089
- out.evaluator_instructions = journeyEntry.evaluator_instructions;
15214
+ if (resources.length > 0) {
15215
+ payload.resources = resources;
15090
15216
  }
15091
- if (typeof journeyEntry.fixture === "string" && journeyEntry.fixture.trim()) {
15092
- out.fixture = journeyEntry.fixture.trim();
15217
+ if (config.states && config.states.length > 0) {
15218
+ payload.states = config.states;
15093
15219
  }
15094
- if (typeof journeyEntry.target_base === "string" && journeyEntry.target_base.trim()) {
15095
- out.target_base = journeyEntry.target_base.trim();
15220
+ if (config.teams && config.teams.length > 0) {
15221
+ payload.teams = config.teams;
15096
15222
  }
15097
- if (journeyEntry.variables && Object.keys(journeyEntry.variables).length > 0) {
15098
- out.variables = journeyEntry.variables;
15223
+ if (Array.isArray(config.connections)) {
15224
+ payload.connections = config.connections;
15099
15225
  }
15100
- if (journeyEntry.transcript && journeyEntry.transcript.length > 0) {
15101
- out.transcript = journeyEntry.transcript;
15226
+ if (config.outbound_contacts && config.outbound_contacts.length > 0) {
15227
+ payload.outbound_contacts = config.outbound_contacts;
15102
15228
  }
15103
- if (journeyEntry.step_config && Object.keys(journeyEntry.step_config).length > 0) {
15104
- out.step_config = journeyEntry.step_config;
15229
+ if (config.outbound_lists && config.outbound_lists.length > 0) {
15230
+ payload.outbound_lists = config.outbound_lists;
15105
15231
  }
15106
- return out;
15107
- }
15108
- function writeJourneyYamlFiles(hubFolder, journeys) {
15109
- const journeysDir = path13.join(hubFolder, "journeys");
15110
- if (journeys.length === 0) {
15111
- if (fs11.existsSync(journeysDir)) {
15112
- cleanJourneyOrphans(journeysDir, /* @__PURE__ */ new Set());
15113
- try {
15114
- if (fs11.readdirSync(journeysDir).length === 0) fs11.rmdirSync(journeysDir);
15115
- } catch {
15116
- }
15117
- }
15118
- return;
15232
+ if (config.outbound_schedules && config.outbound_schedules.length > 0) {
15233
+ payload.outbound_schedules = config.outbound_schedules;
15119
15234
  }
15120
- if (!fs11.existsSync(journeysDir)) {
15121
- fs11.mkdirSync(journeysDir, { recursive: true });
15235
+ const bytesByHash = /* @__PURE__ */ new Map();
15236
+ const evals = scanEvalYamlFiles(hubFolder, bytesByHash);
15237
+ if (evals.length > 0) {
15238
+ payload.evals = evals;
15122
15239
  }
15123
- const writtenFiles = /* @__PURE__ */ new Set();
15124
- const usedSlugs = /* @__PURE__ */ new Set();
15125
- for (const journeyEntry of journeys) {
15126
- const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
15127
- const fileName = `${slug}.yaml`;
15128
- const yamlContent = yaml4.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
15129
- fs11.writeFileSync(path13.join(journeysDir, fileName), yamlContent, "utf-8");
15130
- writtenFiles.add(fileName);
15240
+ const journeys = scanJourneyYamlFiles(hubFolder, bytesByHash);
15241
+ if (journeys.length > 0) {
15242
+ payload.journeys = journeys;
15131
15243
  }
15132
- cleanJourneyOrphans(journeysDir, writtenFiles);
15244
+ if (bytesByHash.size > 0) {
15245
+ payload.eval_attachment_files = [...bytesByHash.values()];
15246
+ }
15247
+ return payload;
15133
15248
  }
15134
- function cleanJourneyOrphans(journeysDir, writtenFiles) {
15135
- const entries = fs11.readdirSync(journeysDir, { withFileTypes: true });
15136
- for (const entry of entries) {
15137
- if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
15138
- fs11.unlinkSync(path13.join(journeysDir, entry.name));
15249
+ function scanEvalYamlFiles(hubFolder, bytesByHash) {
15250
+ const evalsDir = path14.join(hubFolder, "evals");
15251
+ if (!fs12.existsSync(evalsDir)) return [];
15252
+ const evals = [];
15253
+ const seen = /* @__PURE__ */ new Set();
15254
+ const topEntries = fs12.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15255
+ for (const entry of topEntries) {
15256
+ if (entry.isFile() && entry.name.endsWith(".yaml")) {
15257
+ collectEval(evals, seen, path14.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
15258
+ } else if (entry.isDirectory()) {
15259
+ const setName = entry.name;
15260
+ const setDir = path14.join(evalsDir, setName);
15261
+ const setEntries = fs12.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15262
+ for (const sub of setEntries) {
15263
+ if (sub.isDirectory()) {
15264
+ throw expected(
15265
+ `Nested scenario sets are not supported: ${path14.relative(hubFolder, path14.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
15266
+ );
15267
+ }
15268
+ if (sub.isFile() && sub.name.endsWith(".yaml")) {
15269
+ collectEval(evals, seen, path14.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
15270
+ }
15271
+ }
15139
15272
  }
15140
15273
  }
15274
+ return evals;
15141
15275
  }
15142
- function writeResourceFiles(hubFolder, resources) {
15143
- const resourcesDir = path13.join(hubFolder, "resources");
15144
- const currentSlugs = /* @__PURE__ */ new Set();
15145
- for (const resource of resources) {
15146
- const resSlug = slugify(resource.name);
15147
- currentSlugs.add(resSlug);
15148
- const resDir = path13.join(resourcesDir, resSlug);
15149
- writeResourceFileTree(resDir, resource.files || [], hubFolder);
15276
+ function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesByHash) {
15277
+ const evalEntry = parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash);
15278
+ if (!evalEntry) return;
15279
+ const key = `${evalEntry.name}|${setName ?? ""}`;
15280
+ if (seen.has(key)) {
15281
+ const where = setName ? `scenario set "${setName}"` : "root";
15282
+ throw expected(
15283
+ `Duplicate eval at ${where}: "${evalEntry.name}" (in ${path14.basename(filePath)}). Each (name, scenario set) pair must be unique.`
15284
+ );
15150
15285
  }
15151
- if (fs11.existsSync(resourcesDir)) {
15152
- const existingDirs = fs11.readdirSync(resourcesDir, { withFileTypes: true });
15153
- for (const entry of existingDirs) {
15154
- if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
15155
- fs11.rmSync(path13.join(resourcesDir, entry.name), { recursive: true, force: true });
15156
- }
15286
+ seen.add(key);
15287
+ evals.push(evalEntry);
15288
+ }
15289
+ function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
15290
+ const content = fs12.readFileSync(filePath, "utf-8");
15291
+ let raw;
15292
+ try {
15293
+ raw = yaml4.load(content);
15294
+ } catch (err) {
15295
+ if (err instanceof yaml4.YAMLException) {
15296
+ throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
15157
15297
  }
15298
+ throw err;
15158
15299
  }
15159
- }
15160
- var YAML_DUMP_OPTIONS;
15161
- var init_yaml_writer = __esm({
15162
- "src/lib/yaml-writer.ts"() {
15163
- "use strict";
15164
- init_dist();
15165
- init_utils();
15166
- init_resource_files();
15167
- init_workspace_files();
15168
- YAML_DUMP_OPTIONS = {
15169
- lineWidth: -1,
15170
- // don't wrap lines
15171
- noRefs: true,
15172
- // don't use YAML anchors
15173
- quotingType: '"',
15174
- forceQuotes: false,
15175
- sortKeys: false
15176
- };
15300
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15301
+ console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
15302
+ return null;
15177
15303
  }
15178
- });
15179
-
15180
- // src/lib/eval-attachments.ts
15181
- import * as fs12 from "fs";
15182
- import * as path14 from "path";
15183
- function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
15184
- const raw = turn.attachments;
15185
- if (raw === void 0 || raw === null) return void 0;
15186
- if (!Array.isArray(raw) || raw.length === 0) {
15187
- if (Array.isArray(raw)) return void 0;
15188
- throw expected(`${label}: "attachments" must be a list of file paths.`);
15304
+ const data = raw;
15305
+ const fileSlug = path14.basename(filePath, ".yaml");
15306
+ const evalName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
15307
+ if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
15308
+ throw expected(`Eval "${evalName}" in ${relPath}: missing required field "agent" (string).`);
15189
15309
  }
15190
- if (turn.role !== "user") {
15191
- throw expected(`${label}: attachments are only allowed on user turns (this turn is "${String(turn.role)}").`);
15310
+ if (!data.input || typeof data.input !== "object" || Array.isArray(data.input)) {
15311
+ throw expected(`Eval "${evalName}" in ${relPath}: missing required field "input" (object).`);
15192
15312
  }
15193
- const refs = [];
15194
- for (const entry of raw) {
15195
- if (typeof entry !== "string" || !entry.trim()) {
15196
- throw expected(`${label}: each attachment must be a hub-relative file path string.`);
15197
- }
15198
- const abs = path14.resolve(hubFolder, entry);
15199
- const root = path14.resolve(hubFolder);
15200
- if (abs !== root && !abs.startsWith(root + path14.sep)) {
15201
- throw expected(`${label}: attachment "${entry}" escapes the hub folder.`);
15202
- }
15203
- let stat;
15204
- try {
15205
- stat = fs12.statSync(abs);
15206
- } catch {
15207
- }
15208
- if (!stat?.isFile()) {
15209
- throw expected(`${label}: attachment "${entry}" not found.`);
15210
- }
15211
- if (!realpathContained(root, abs)) {
15212
- throw expected(`${label}: attachment "${entry}" resolves outside the hub folder (symlinks are not allowed).`);
15213
- }
15214
- if (stat.size > MAX_RESOURCE_FILE_SIZE3) {
15313
+ if (!data.expected || typeof data.expected !== "object" || Array.isArray(data.expected)) {
15314
+ throw expected(`Eval "${evalName}" in ${relPath}: missing required field "expected" (object).`);
15315
+ }
15316
+ const input = { ...data.input };
15317
+ applyTurnAttachments(hubFolder, input, `Eval "${evalName}" input`, bytesByHash);
15318
+ const evalEntry = {
15319
+ name: evalName,
15320
+ agent: data.agent,
15321
+ path: setName,
15322
+ input,
15323
+ expected: data.expected
15324
+ };
15325
+ if (typeof data.id === "string") evalEntry.id = data.id;
15326
+ if (typeof data.agent_id === "string") evalEntry.agent_id = data.agent_id;
15327
+ if (typeof data.runs === "number") evalEntry.runs = data.runs;
15328
+ if (data.enabled === false) evalEntry.enabled = false;
15329
+ if (Array.isArray(data.history)) {
15330
+ evalEntry.history = data.history.map((turn, i) => {
15331
+ const t = { ...turn };
15332
+ applyTurnAttachments(hubFolder, t, `Eval "${evalName}" history[${i}]`, bytesByHash);
15333
+ return t;
15334
+ });
15335
+ }
15336
+ if (typeof data.evaluator_instructions === "string") {
15337
+ evalEntry.evaluator_instructions = data.evaluator_instructions;
15338
+ }
15339
+ const variables = parseVariablesMap(data.variables, `Eval "${evalName}" in ${relPath}`);
15340
+ if (variables) evalEntry.variables = variables;
15341
+ const fixture = parseFixtureField(data.fixture, `Eval "${evalName}" in ${relPath}`, "fixture");
15342
+ if (fixture) evalEntry.fixture = fixture;
15343
+ const targetBase = parseFixtureField(data.target_base, `Eval "${evalName}" in ${relPath}`, "target_base");
15344
+ if (targetBase) evalEntry.target_base = targetBase;
15345
+ return evalEntry;
15346
+ }
15347
+ function parseVariablesMap(raw, label) {
15348
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
15349
+ const vars = raw;
15350
+ if (Object.keys(vars).length === 0) return void 0;
15351
+ if (!Object.values(vars).every((v) => Array.isArray(v))) {
15352
+ throw expected(`${label}: "variables" must be a map of arrays (variables.<name>: [ ... ]).`);
15353
+ }
15354
+ return vars;
15355
+ }
15356
+ function parseFixtureField(raw, label, key) {
15357
+ if (raw === void 0 || raw === null) return void 0;
15358
+ if (typeof raw !== "string") {
15359
+ throw expected(`${label}: "${key}" must be a string (the Rekor fixture name).`);
15360
+ }
15361
+ const trimmed = raw.trim();
15362
+ return trimmed.length > 0 ? trimmed : void 0;
15363
+ }
15364
+ function scanJourneyYamlFiles(hubFolder, bytesByHash) {
15365
+ const journeysDir = path14.join(hubFolder, "journeys");
15366
+ if (!fs12.existsSync(journeysDir)) return [];
15367
+ const journeys = [];
15368
+ const seen = /* @__PURE__ */ new Set();
15369
+ const entries = fs12.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15370
+ for (const entry of entries) {
15371
+ if (entry.isDirectory()) {
15215
15372
  throw expected(
15216
- `${label}: attachment "${entry}" is ${(stat.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
15373
+ `Subfolders are not supported under journeys/: ${path14.relative(hubFolder, path14.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
15217
15374
  );
15218
15375
  }
15219
- const data = fs12.readFileSync(abs);
15220
- const hash = computeHash(data);
15221
- const fileName = path14.basename(abs);
15222
- const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
15223
- refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat.size });
15224
- if (!bytesByHash.has(hash)) {
15225
- bytesByHash.set(hash, {
15226
- hash,
15227
- content_base64: data.toString("base64"),
15228
- file_name: fileName,
15229
- mime_type: mimeType,
15230
- file_size: stat.size
15231
- });
15376
+ if (!entry.isFile() || !entry.name.endsWith(".yaml")) continue;
15377
+ const journeyEntry = parseJourneyYaml(path14.join(journeysDir, entry.name), `journeys/${entry.name}`, hubFolder, bytesByHash);
15378
+ if (!journeyEntry) continue;
15379
+ if (seen.has(journeyEntry.name)) {
15380
+ throw expected(
15381
+ `Duplicate journey: "${journeyEntry.name}" (in ${entry.name}). Each journey name must be unique.`
15382
+ );
15232
15383
  }
15384
+ seen.add(journeyEntry.name);
15385
+ journeys.push(journeyEntry);
15233
15386
  }
15234
- return refs;
15235
- }
15236
- function applyTurnAttachments(hubFolder, turn, label, bytesByHash) {
15237
- const refs = resolveTurnAttachments(hubFolder, turn, label, bytesByHash);
15238
- if (refs) turn.attachments = refs;
15239
- else delete turn.attachments;
15387
+ return journeys;
15240
15388
  }
15241
- function forEachAttachableTurn(payload, fn) {
15242
- for (const ev of payload.evals ?? []) {
15243
- if (ev.input) fn(ev.input);
15244
- for (const t of ev.history ?? []) fn(t);
15389
+ function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
15390
+ const content = fs12.readFileSync(filePath, "utf-8");
15391
+ let raw;
15392
+ try {
15393
+ raw = yaml4.load(content);
15394
+ } catch (err) {
15395
+ if (err instanceof yaml4.YAMLException) {
15396
+ throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
15397
+ }
15398
+ throw err;
15245
15399
  }
15246
- for (const jr of payload.journeys ?? []) {
15247
- for (const t of jr.transcript ?? []) fn(t);
15400
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15401
+ console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
15402
+ return null;
15248
15403
  }
15249
- }
15250
- function rewriteEvalAttachmentsToLocalPaths(payload) {
15251
- const urlByHash = new Map((payload.eval_attachment_files ?? []).map((f) => [f.hash, f.download_url]));
15252
- const safeName = (ref) => {
15253
- const base = path14.basename(ref.file_name);
15254
- return base === "" || base === "." || base === ".." ? ref.hash.slice(0, 16) : base;
15255
- };
15256
- const hashesByName = /* @__PURE__ */ new Map();
15257
- const collect = (turn) => {
15258
- if (!Array.isArray(turn.attachments)) return;
15259
- for (const ref of turn.attachments) {
15260
- if (!ref?.hash || !ref.file_name) continue;
15261
- const name = safeName(ref);
15262
- let set = hashesByName.get(name);
15263
- if (!set) {
15264
- set = /* @__PURE__ */ new Set();
15265
- hashesByName.set(name, set);
15266
- }
15267
- set.add(ref.hash);
15268
- }
15269
- };
15270
- forEachAttachableTurn(payload, collect);
15271
- const pathByKey = /* @__PURE__ */ new Map();
15272
- const usedLower = /* @__PURE__ */ new Set();
15273
- const localPathFor = (ref) => {
15274
- const name = safeName(ref);
15275
- const key = `${ref.hash} ${name}`;
15276
- const existing = pathByKey.get(key);
15277
- if (existing) return existing;
15278
- const ext = path14.extname(name);
15279
- const stem = path14.basename(name, ext);
15280
- const collides = (hashesByName.get(name)?.size ?? 0) > 1;
15281
- let rel = collides ? `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, 8)}${ext}` : `${ATTACHMENTS_DIR}/${name}`;
15282
- if (usedLower.has(rel.toLowerCase())) {
15283
- let n = 12;
15284
- while (usedLower.has(rel.toLowerCase()) && n <= ref.hash.length) {
15285
- rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, n)}${ext}`;
15286
- n += 4;
15287
- }
15288
- for (let extra = 2; usedLower.has(rel.toLowerCase()); extra++) {
15289
- rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash}-${extra}${ext}`;
15290
- }
15291
- }
15292
- usedLower.add(rel.toLowerCase());
15293
- pathByKey.set(key, rel);
15294
- return rel;
15404
+ const data = raw;
15405
+ const fileSlug = path14.basename(filePath, ".yaml");
15406
+ const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
15407
+ if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
15408
+ throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
15409
+ }
15410
+ if (data.transcript !== void 0 && !Array.isArray(data.transcript)) {
15411
+ throw expected(`Journey "${journeyName}" in ${relPath}: "transcript" must be an array.`);
15412
+ }
15413
+ if (data.step_config !== void 0 && (typeof data.step_config !== "object" || Array.isArray(data.step_config) || data.step_config === null)) {
15414
+ throw expected(`Journey "${journeyName}" in ${relPath}: "step_config" must be an object keyed by step_id.`);
15415
+ }
15416
+ const journeyEntry = {
15417
+ name: journeyName,
15418
+ agent: data.agent
15295
15419
  };
15296
- const downloads = /* @__PURE__ */ new Map();
15297
- forEachAttachableTurn(payload, (turn) => {
15298
- if (!Array.isArray(turn.attachments)) return;
15299
- turn.attachments = turn.attachments.map((ref) => {
15300
- const rel = localPathFor(ref);
15301
- const url = urlByHash.get(ref.hash);
15302
- if (url && !downloads.has(rel)) downloads.set(rel, { relPath: rel, url, hash: ref.hash });
15303
- return rel;
15420
+ if (typeof data.id === "string") journeyEntry.id = data.id;
15421
+ if (typeof data.agent_id === "string") journeyEntry.agent_id = data.agent_id;
15422
+ if (data.enabled === false) journeyEntry.enabled = false;
15423
+ if (typeof data.evaluator_instructions === "string") {
15424
+ journeyEntry.evaluator_instructions = data.evaluator_instructions;
15425
+ }
15426
+ if (Array.isArray(data.transcript)) {
15427
+ journeyEntry.transcript = data.transcript.map((turn, i) => {
15428
+ const t = { ...turn };
15429
+ applyTurnAttachments(hubFolder, t, `Journey "${journeyName}" transcript[${i}]`, bytesByHash);
15430
+ return t;
15304
15431
  });
15305
- });
15306
- delete payload.eval_attachment_files;
15307
- return [...downloads.values()];
15308
- }
15309
- async function downloadEvalAttachments(hubFolder, downloads) {
15310
- const attachDir = path14.resolve(hubFolder, ATTACHMENTS_DIR);
15311
- let written = 0;
15312
- if (!ensureRealSubdirNoSymlink(hubFolder, attachDir, downloads.length > 0)) {
15313
- console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
15314
- return written;
15315
15432
  }
15316
- if (!fs12.existsSync(attachDir)) return written;
15317
- for (const dl of downloads) {
15318
- const abs = path14.resolve(hubFolder, dl.relPath);
15319
- if (abs !== attachDir && !abs.startsWith(attachDir + path14.sep)) continue;
15320
- let existing;
15433
+ if (data.step_config && typeof data.step_config === "object" && !Array.isArray(data.step_config)) {
15434
+ journeyEntry.step_config = data.step_config;
15435
+ }
15436
+ const variables = parseVariablesMap(data.variables, `Journey "${journeyName}" in ${relPath}`);
15437
+ if (variables) journeyEntry.variables = variables;
15438
+ const fixture = parseFixtureField(data.fixture, `Journey "${journeyName}" in ${relPath}`, "fixture");
15439
+ if (fixture) journeyEntry.fixture = fixture;
15440
+ const targetBase = parseFixtureField(data.target_base, `Journey "${journeyName}" in ${relPath}`, "target_base");
15441
+ if (targetBase) journeyEntry.target_base = targetBase;
15442
+ return journeyEntry;
15443
+ }
15444
+ function scanAgentYamlFiles(hubFolder) {
15445
+ const agentsDir = path14.join(hubFolder, "agents");
15446
+ if (!fs12.existsSync(agentsDir)) return [];
15447
+ const yamlFiles = fs12.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
15448
+ if (yamlFiles.length === 0) return [];
15449
+ const agents = [];
15450
+ for (const file of yamlFiles) {
15451
+ const filePath = path14.join(agentsDir, file);
15452
+ const content = fs12.readFileSync(filePath, "utf-8");
15453
+ let agent;
15321
15454
  try {
15322
- existing = fs12.lstatSync(abs);
15323
- } catch {
15455
+ agent = yaml4.load(content);
15456
+ } catch (err) {
15457
+ if (err instanceof yaml4.YAMLException) {
15458
+ const userError = new Error(`Invalid YAML in agents/${file}: ${err.message}`);
15459
+ userError.isExpected = true;
15460
+ throw userError;
15461
+ }
15462
+ throw err;
15324
15463
  }
15325
- if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs12.readFileSync(abs)) === dl.hash) {
15464
+ if (!agent || typeof agent !== "object") continue;
15465
+ if (!agent.name) {
15466
+ console.warn(` Warning: skipping ${file} (missing 'name' field)`);
15326
15467
  continue;
15327
15468
  }
15328
- try {
15329
- const res = await fetch(dl.url);
15330
- if (!res.ok) {
15331
- console.warn(` Warning: failed to download ${dl.relPath} (HTTP ${res.status})`);
15332
- continue;
15469
+ agents.push(agent);
15470
+ }
15471
+ return agents;
15472
+ }
15473
+ function parseResources(hubFolder, configResources) {
15474
+ const resourcesDir = path14.join(hubFolder, "resources");
15475
+ const results = [];
15476
+ for (const res of configResources) {
15477
+ const resource = {
15478
+ name: res.name
15479
+ };
15480
+ if (res.id) resource.id = res.id;
15481
+ if (res.type === "knowledge" || res.type === "skill") resource.type = res.type;
15482
+ else if (res.type !== void 0) throw new Error(`Invalid resource type for "${resource.name}": expected knowledge or skill`);
15483
+ if (res.description) resource.description = res.description;
15484
+ if (res.enabled === false) resource.enabled = false;
15485
+ if (res.user_browsable) resource.user_browsable = res.user_browsable;
15486
+ if (res.skill_name) resource.skill_name = res.skill_name;
15487
+ const resSlug = slugify(resource.name);
15488
+ const resDir = path14.join(resourcesDir, resSlug);
15489
+ if (fs12.existsSync(resDir)) {
15490
+ const files = scanResourceFiles(resDir, "");
15491
+ if (files.length > 0) {
15492
+ resource.files = files;
15333
15493
  }
15334
- if (writeFileNoFollow(hubFolder, abs, Buffer.from(await res.arrayBuffer()))) written++;
15335
- } catch (err) {
15336
- console.warn(` Warning: failed to download ${dl.relPath}: ${err instanceof Error ? err.message : String(err)}`);
15337
15494
  }
15495
+ results.push(resource);
15338
15496
  }
15339
- const keep = new Set(downloads.map((d) => path14.resolve(hubFolder, d.relPath)));
15340
- for (const name of fs12.readdirSync(attachDir)) {
15341
- const abs = path14.join(attachDir, name);
15342
- const st = fs12.lstatSync(abs);
15343
- if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) fs12.rmSync(abs);
15344
- }
15345
- return written;
15497
+ return results;
15346
15498
  }
15347
- var ATTACHMENTS_DIR;
15348
- var init_eval_attachments = __esm({
15349
- "src/lib/eval-attachments.ts"() {
15499
+ var init_parser = __esm({
15500
+ "src/lib/parser.ts"() {
15350
15501
  "use strict";
15502
+ init_utils();
15351
15503
  init_resource_files();
15352
- init_fs_safety();
15353
- init_parser();
15354
- ATTACHMENTS_DIR = "attachments";
15504
+ init_eval_attachments();
15355
15505
  }
15356
15506
  });
15357
15507
 
15358
- // src/lib/parser.ts
15359
- import * as fs13 from "fs";
15360
- import * as path15 from "path";
15361
- import * as yaml5 from "js-yaml";
15362
- function expected(message) {
15363
- const err = new Error(message);
15364
- err.isExpected = true;
15365
- return err;
15366
- }
15367
- function parseHubFolder(hubFolder, opts) {
15368
- const yamlPath = resolveHubYamlPath(hubFolder);
15369
- if (!yamlPath) {
15370
- const err = new Error(`hub.yaml not found in ${hubFolder}`);
15371
- err.isExpected = true;
15372
- throw err;
15373
- }
15374
- const yamlContent = fs13.readFileSync(yamlPath, "utf-8");
15375
- let config;
15376
- try {
15377
- config = yaml5.load(yamlContent);
15378
- } catch (err) {
15379
- if (err instanceof yaml5.YAMLException) {
15380
- const userError = new Error(`Invalid YAML in ${yamlPath}: ${err.message}`);
15381
- userError.isExpected = true;
15382
- throw userError;
15508
+ // src/lib/diff-display.ts
15509
+ function printDiff(summary) {
15510
+ const lines = summary.split("\n");
15511
+ for (const line of lines) {
15512
+ if (line.startsWith("**") && line.endsWith("**")) {
15513
+ console.log(`
15514
+ \x1B[1m${line.replace(/\*\*/g, "")}\x1B[0m`);
15515
+ } else if (line.trimStart().startsWith("+ ")) {
15516
+ console.log(`\x1B[32m${line}\x1B[0m`);
15517
+ } else if (line.trimStart().startsWith("- ")) {
15518
+ console.log(`\x1B[31m${line}\x1B[0m`);
15519
+ } else if (line.trimStart().startsWith("~ ")) {
15520
+ console.log(`\x1B[33m${line}\x1B[0m`);
15521
+ } else if (line.includes("(no changes)")) {
15522
+ console.log(`\x1B[2m${line}\x1B[0m`);
15523
+ } else {
15524
+ console.log(line);
15383
15525
  }
15384
- throw err;
15385
- }
15386
- if (!config || typeof config !== "object") {
15387
- const invalidError = new Error(`Invalid YAML in ${yamlPath}`);
15388
- invalidError.isExpected = true;
15389
- throw invalidError;
15390
15526
  }
15391
- if (!opts?.allowNewHub) {
15392
- if (!config.hub_id) {
15393
- const err = new Error(`Missing hub_id in ${yamlPath}`);
15394
- err.isExpected = true;
15395
- throw err;
15396
- }
15397
- if (!config.hub_environment) {
15398
- const err = new Error(`Missing hub_environment in ${yamlPath}`);
15399
- err.isExpected = true;
15400
- throw err;
15401
- }
15527
+ console.log();
15528
+ }
15529
+ var init_diff_display = __esm({
15530
+ "src/lib/diff-display.ts"() {
15531
+ "use strict";
15402
15532
  }
15403
- const agentYamlFiles = scanAgentYamlFiles(hubFolder);
15404
- const rawAgents = agentYamlFiles.length > 0 ? agentYamlFiles : config.agents;
15405
- const agents = rawAgents?.map((agent) => {
15406
- const resolved = { ...agent };
15407
- if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
15408
- const instrValue = resolved.instructions;
15409
- const instructionsPath = instrValue.startsWith("agents/") ? path15.join(hubFolder, instrValue) : path15.join(hubFolder, "agents", instrValue);
15410
- if (fs13.existsSync(instructionsPath)) {
15411
- resolved.instructions = fs13.readFileSync(instructionsPath, "utf-8");
15412
- } else {
15413
- const err = new Error(
15414
- `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
15415
- );
15416
- err.isExpected = true;
15417
- throw err;
15418
- }
15419
- } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
15420
- const conventionPath = path15.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
15421
- if (fs13.existsSync(conventionPath)) {
15422
- resolved.instructions = fs13.readFileSync(conventionPath, "utf-8");
15423
- }
15424
- }
15425
- return resolved;
15426
- });
15427
- const resources = parseResources(hubFolder, config.resources || []);
15428
- const payload = {
15429
- version: config.version || 1,
15430
- hub_id: config.hub_id || "",
15431
- hub_environment: config.hub_environment || "preview",
15432
- hub: config.hub
15433
- };
15434
- if (config.preview_label) {
15435
- payload.preview_label = config.preview_label;
15533
+ });
15534
+
15535
+ // src/lib/yaml-writer.ts
15536
+ import * as fs13 from "fs";
15537
+ import * as path15 from "path";
15538
+ import * as yaml5 from "js-yaml";
15539
+ function buildEvalYamlObject(evalEntry, slug) {
15540
+ const out = {};
15541
+ if (evalEntry.id) out.id = evalEntry.id;
15542
+ if (evalEntry.name !== slug) out.name = evalEntry.name;
15543
+ out.agent = evalEntry.agent;
15544
+ if (evalEntry.agent_id) out.agent_id = evalEntry.agent_id;
15545
+ if (typeof evalEntry.runs === "number" && evalEntry.runs !== 1) {
15546
+ out.runs = evalEntry.runs;
15436
15547
  }
15437
- if (agents && agents.length > 0) {
15438
- payload.agents = agents;
15548
+ if (evalEntry.enabled === false) {
15549
+ out.enabled = false;
15439
15550
  }
15440
- if (resources.length > 0) {
15441
- payload.resources = resources;
15551
+ if (evalEntry.history && evalEntry.history.length > 0) {
15552
+ for (const turn of evalEntry.history) assertAttachmentsAreLocalPaths(turn, `eval "${evalEntry.name}" history`);
15553
+ out.history = evalEntry.history;
15442
15554
  }
15443
- if (config.states && config.states.length > 0) {
15444
- payload.states = config.states;
15555
+ if (typeof evalEntry.fixture === "string" && evalEntry.fixture.trim()) {
15556
+ out.fixture = evalEntry.fixture.trim();
15445
15557
  }
15446
- if (config.teams && config.teams.length > 0) {
15447
- payload.teams = config.teams;
15558
+ if (typeof evalEntry.target_base === "string" && evalEntry.target_base.trim()) {
15559
+ out.target_base = evalEntry.target_base.trim();
15448
15560
  }
15449
- if (Array.isArray(config.connections)) {
15450
- payload.connections = config.connections;
15561
+ if (evalEntry.variables && Object.keys(evalEntry.variables).length > 0) {
15562
+ out.variables = evalEntry.variables;
15451
15563
  }
15452
- if (config.outbound_contacts && config.outbound_contacts.length > 0) {
15453
- payload.outbound_contacts = config.outbound_contacts;
15564
+ assertAttachmentsAreLocalPaths(evalEntry.input, `eval "${evalEntry.name}" input`);
15565
+ out.input = evalEntry.input;
15566
+ out.expected = evalEntry.expected;
15567
+ if (evalEntry.evaluator_instructions) {
15568
+ out.evaluator_instructions = evalEntry.evaluator_instructions;
15454
15569
  }
15455
- if (config.outbound_lists && config.outbound_lists.length > 0) {
15456
- payload.outbound_lists = config.outbound_lists;
15570
+ return out;
15571
+ }
15572
+ function writeHubFolder(hubFolder, payload, options = {}) {
15573
+ const agentsDir = path15.join(hubFolder, "agents");
15574
+ if (!fs13.existsSync(hubFolder)) {
15575
+ fs13.mkdirSync(hubFolder, { recursive: true });
15457
15576
  }
15458
- if (config.outbound_schedules && config.outbound_schedules.length > 0) {
15459
- payload.outbound_schedules = config.outbound_schedules;
15577
+ if (!fs13.existsSync(agentsDir)) {
15578
+ fs13.mkdirSync(agentsDir, { recursive: true });
15460
15579
  }
15461
- const bytesByHash = /* @__PURE__ */ new Map();
15462
- const evals = scanEvalYamlFiles(hubFolder, bytesByHash);
15463
- if (evals.length > 0) {
15464
- payload.evals = evals;
15580
+ const yamlPayload = buildYamlPayload(payload);
15581
+ const agentFiles = extractAgentFiles(payload);
15582
+ const yamlContent = yaml5.dump(yamlPayload, YAML_DUMP_OPTIONS);
15583
+ fs13.writeFileSync(path15.join(hubFolder, "hub.yaml"), yamlContent, "utf-8");
15584
+ if (options.seedAgentContext ?? true) {
15585
+ writeIfAbsent(path15.join(hubFolder, "AGENTS.md"), perHubAgentsMd(path15.basename(hubFolder)));
15586
+ writeIfAbsent(path15.join(hubFolder, "CLAUDE.md"), CLAUDE_MD_SHIM);
15465
15587
  }
15466
- const journeys = scanJourneyYamlFiles(hubFolder, bytesByHash);
15467
- if (journeys.length > 0) {
15468
- payload.journeys = journeys;
15588
+ const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
15589
+ if (fs13.existsSync(oldYamlPath)) {
15590
+ fs13.unlinkSync(oldYamlPath);
15469
15591
  }
15470
- if (bytesByHash.size > 0) {
15471
- payload.eval_attachment_files = [...bytesByHash.values()];
15592
+ const yamlSlugs = writeAgentYamlFiles(agentsDir, payload.agents || []);
15593
+ const mdSlugs = /* @__PURE__ */ new Set();
15594
+ for (const { slug, content } of agentFiles) {
15595
+ mdSlugs.add(slug);
15596
+ fs13.writeFileSync(path15.join(agentsDir, `${slug}.md`), content, "utf-8");
15472
15597
  }
15473
- return payload;
15474
- }
15475
- function scanEvalYamlFiles(hubFolder, bytesByHash) {
15476
- const evalsDir = path15.join(hubFolder, "evals");
15477
- if (!fs13.existsSync(evalsDir)) return [];
15478
- const evals = [];
15479
- const seen = /* @__PURE__ */ new Set();
15480
- const topEntries = fs13.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15481
- for (const entry of topEntries) {
15482
- if (entry.isFile() && entry.name.endsWith(".yaml")) {
15483
- collectEval(evals, seen, path15.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
15484
- } else if (entry.isDirectory()) {
15485
- const setName = entry.name;
15486
- const setDir = path15.join(evalsDir, setName);
15487
- const setEntries = fs13.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15488
- for (const sub of setEntries) {
15489
- if (sub.isDirectory()) {
15490
- throw expected(
15491
- `Nested scenario sets are not supported: ${path15.relative(hubFolder, path15.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
15492
- );
15493
- }
15494
- if (sub.isFile() && sub.name.endsWith(".yaml")) {
15495
- collectEval(evals, seen, path15.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
15496
- }
15598
+ const existingFiles = fs13.readdirSync(agentsDir);
15599
+ for (const file of existingFiles) {
15600
+ if (file.endsWith(".yaml")) {
15601
+ const slug = file.slice(0, -5);
15602
+ if (!yamlSlugs.has(slug)) {
15603
+ fs13.unlinkSync(path15.join(agentsDir, file));
15604
+ }
15605
+ } else if (file.endsWith(".md")) {
15606
+ const slug = file.slice(0, -3);
15607
+ if (!mdSlugs.has(slug)) {
15608
+ fs13.unlinkSync(path15.join(agentsDir, file));
15497
15609
  }
15498
15610
  }
15499
15611
  }
15500
- return evals;
15612
+ writeResourceFiles(hubFolder, payload.resources || []);
15613
+ writeEvalYamlFiles(hubFolder, payload.evals || []);
15614
+ writeJourneyYamlFiles(hubFolder, payload.journeys || []);
15501
15615
  }
15502
- function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesByHash) {
15503
- const evalEntry = parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash);
15504
- if (!evalEntry) return;
15505
- const key = `${evalEntry.name}|${setName ?? ""}`;
15506
- if (seen.has(key)) {
15507
- const where = setName ? `scenario set "${setName}"` : "root";
15508
- throw expected(
15509
- `Duplicate eval at ${where}: "${evalEntry.name}" (in ${path15.basename(filePath)}). Each (name, scenario set) pair must be unique.`
15510
- );
15511
- }
15512
- seen.add(key);
15513
- evals.push(evalEntry);
15616
+ function setPreviewLabelInHubYaml(hubFolder, label) {
15617
+ const yamlPath = resolveHubYamlPath(hubFolder);
15618
+ if (!yamlPath) return;
15619
+ const obj = yaml5.load(fs13.readFileSync(yamlPath, "utf-8")) ?? {};
15620
+ if (label) obj.preview_label = label;
15621
+ else delete obj.preview_label;
15622
+ fs13.writeFileSync(yamlPath, yaml5.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
15514
15623
  }
15515
- function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
15516
- const content = fs13.readFileSync(filePath, "utf-8");
15517
- let raw;
15518
- try {
15519
- raw = yaml5.load(content);
15520
- } catch (err) {
15521
- if (err instanceof yaml5.YAMLException) {
15522
- throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
15523
- }
15524
- throw err;
15624
+ function buildYamlPayload(payload) {
15625
+ const result = {
15626
+ version: payload.version,
15627
+ hub_id: payload.hub_id,
15628
+ hub_environment: payload.hub_environment
15629
+ };
15630
+ if (payload.preview_label) {
15631
+ result.preview_label = payload.preview_label;
15525
15632
  }
15526
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15527
- console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
15528
- return null;
15633
+ result.hub = payload.hub;
15634
+ if (payload.resources && payload.resources.length > 0) {
15635
+ result.resources = payload.resources.map((r) => {
15636
+ const { files, ...rest } = r;
15637
+ return rest;
15638
+ });
15529
15639
  }
15530
- const data = raw;
15531
- const fileSlug = path15.basename(filePath, ".yaml");
15532
- const evalName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
15533
- if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
15534
- throw expected(`Eval "${evalName}" in ${relPath}: missing required field "agent" (string).`);
15640
+ if (payload.states && payload.states.length > 0) {
15641
+ result.states = payload.states;
15535
15642
  }
15536
- if (!data.input || typeof data.input !== "object" || Array.isArray(data.input)) {
15537
- throw expected(`Eval "${evalName}" in ${relPath}: missing required field "input" (object).`);
15643
+ if (payload.teams && payload.teams.length > 0) {
15644
+ result.teams = payload.teams;
15538
15645
  }
15539
- if (!data.expected || typeof data.expected !== "object" || Array.isArray(data.expected)) {
15540
- throw expected(`Eval "${evalName}" in ${relPath}: missing required field "expected" (object).`);
15646
+ if (payload.connections && payload.connections.length > 0) {
15647
+ result.connections = payload.connections;
15541
15648
  }
15542
- const input = { ...data.input };
15543
- applyTurnAttachments(hubFolder, input, `Eval "${evalName}" input`, bytesByHash);
15544
- const evalEntry = {
15545
- name: evalName,
15546
- agent: data.agent,
15547
- path: setName,
15548
- input,
15549
- expected: data.expected
15550
- };
15551
- if (typeof data.id === "string") evalEntry.id = data.id;
15552
- if (typeof data.agent_id === "string") evalEntry.agent_id = data.agent_id;
15553
- if (typeof data.runs === "number") evalEntry.runs = data.runs;
15554
- if (data.enabled === false) evalEntry.enabled = false;
15555
- if (Array.isArray(data.history)) {
15556
- evalEntry.history = data.history.map((turn, i) => {
15557
- const t = { ...turn };
15558
- applyTurnAttachments(hubFolder, t, `Eval "${evalName}" history[${i}]`, bytesByHash);
15559
- return t;
15560
- });
15649
+ if (payload.outbound_contacts && payload.outbound_contacts.length > 0) {
15650
+ result.outbound_contacts = payload.outbound_contacts;
15561
15651
  }
15562
- if (typeof data.evaluator_instructions === "string") {
15563
- evalEntry.evaluator_instructions = data.evaluator_instructions;
15652
+ if (payload.outbound_lists && payload.outbound_lists.length > 0) {
15653
+ result.outbound_lists = payload.outbound_lists;
15564
15654
  }
15565
- const variables = parseVariablesMap(data.variables, `Eval "${evalName}" in ${relPath}`);
15566
- if (variables) evalEntry.variables = variables;
15567
- const fixture = parseFixtureField(data.fixture, `Eval "${evalName}" in ${relPath}`, "fixture");
15568
- if (fixture) evalEntry.fixture = fixture;
15569
- const targetBase = parseFixtureField(data.target_base, `Eval "${evalName}" in ${relPath}`, "target_base");
15570
- if (targetBase) evalEntry.target_base = targetBase;
15571
- return evalEntry;
15655
+ if (payload.outbound_schedules && payload.outbound_schedules.length > 0) {
15656
+ result.outbound_schedules = payload.outbound_schedules;
15657
+ }
15658
+ return result;
15572
15659
  }
15573
- function parseVariablesMap(raw, label) {
15574
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
15575
- const vars = raw;
15576
- if (Object.keys(vars).length === 0) return void 0;
15577
- if (!Object.values(vars).every((v) => Array.isArray(v))) {
15578
- throw expected(`${label}: "variables" must be a map of arrays (variables.<name>: [ ... ]).`);
15660
+ function writeAgentYamlFiles(agentsDir, agents) {
15661
+ const slugs = /* @__PURE__ */ new Set();
15662
+ for (const agent of agents) {
15663
+ const slug = slugify(agent.name);
15664
+ slugs.add(slug);
15665
+ const entry = { ...agent };
15666
+ delete entry.instructions;
15667
+ const yamlContent = yaml5.dump(entry, YAML_DUMP_OPTIONS);
15668
+ fs13.writeFileSync(path15.join(agentsDir, `${slug}.yaml`), yamlContent, "utf-8");
15579
15669
  }
15580
- return vars;
15670
+ return slugs;
15581
15671
  }
15582
- function parseFixtureField(raw, label, key) {
15583
- if (raw === void 0 || raw === null) return void 0;
15584
- if (typeof raw !== "string") {
15585
- throw expected(`${label}: "${key}" must be a string (the Rekor fixture name).`);
15672
+ function extractAgentFiles(payload) {
15673
+ const files = [];
15674
+ for (const agent of payload.agents || []) {
15675
+ if (agent.instructions) {
15676
+ files.push({
15677
+ slug: slugify(agent.name),
15678
+ content: agent.instructions
15679
+ });
15680
+ }
15586
15681
  }
15587
- const trimmed = raw.trim();
15588
- return trimmed.length > 0 ? trimmed : void 0;
15682
+ return files;
15589
15683
  }
15590
- function scanJourneyYamlFiles(hubFolder, bytesByHash) {
15591
- const journeysDir = path15.join(hubFolder, "journeys");
15592
- if (!fs13.existsSync(journeysDir)) return [];
15593
- const journeys = [];
15594
- const seen = /* @__PURE__ */ new Set();
15595
- const entries = fs13.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15684
+ function writeEvalYamlFiles(hubFolder, evals) {
15685
+ const evalsDir = path15.join(hubFolder, "evals");
15686
+ if (evals.length === 0) {
15687
+ if (fs13.existsSync(evalsDir)) {
15688
+ cleanEvalOrphans(evalsDir, /* @__PURE__ */ new Set());
15689
+ try {
15690
+ if (fs13.readdirSync(evalsDir).length === 0) fs13.rmdirSync(evalsDir);
15691
+ } catch {
15692
+ }
15693
+ }
15694
+ return;
15695
+ }
15696
+ if (!fs13.existsSync(evalsDir)) {
15697
+ fs13.mkdirSync(evalsDir, { recursive: true });
15698
+ }
15699
+ const writtenRelPaths = /* @__PURE__ */ new Set();
15700
+ for (const evalEntry of evals) {
15701
+ const slug = slugify(evalEntry.name);
15702
+ const setName = evalEntry.path && evalEntry.path.trim().length > 0 ? evalEntry.path : null;
15703
+ const relPath = setName ? `${setName}/${slug}.yaml` : `${slug}.yaml`;
15704
+ const targetPath = path15.join(evalsDir, relPath);
15705
+ if (!targetPath.startsWith(evalsDir + path15.sep)) {
15706
+ console.warn(` Warning: skipping eval "${evalEntry.name}" (scenario set "${setName}" escapes evals/ \u2014 possible bad backend data)`);
15707
+ continue;
15708
+ }
15709
+ if (setName) {
15710
+ const setDir = path15.join(evalsDir, setName);
15711
+ if (!fs13.existsSync(setDir)) {
15712
+ fs13.mkdirSync(setDir, { recursive: true });
15713
+ }
15714
+ }
15715
+ const yamlContent = yaml5.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
15716
+ fs13.writeFileSync(targetPath, yamlContent, "utf-8");
15717
+ writtenRelPaths.add(relPath);
15718
+ }
15719
+ cleanEvalOrphans(evalsDir, writtenRelPaths);
15720
+ }
15721
+ function cleanEvalOrphans(evalsDir, writtenRelPaths) {
15722
+ const entries = fs13.readdirSync(evalsDir, { withFileTypes: true });
15596
15723
  for (const entry of entries) {
15597
- if (entry.isDirectory()) {
15598
- throw expected(
15599
- `Subfolders are not supported under journeys/: ${path15.relative(hubFolder, path15.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
15600
- );
15724
+ const fullPath = path15.join(evalsDir, entry.name);
15725
+ if (entry.isFile()) {
15726
+ if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
15727
+ fs13.unlinkSync(fullPath);
15728
+ }
15729
+ continue;
15601
15730
  }
15602
- if (!entry.isFile() || !entry.name.endsWith(".yaml")) continue;
15603
- const journeyEntry = parseJourneyYaml(path15.join(journeysDir, entry.name), `journeys/${entry.name}`, hubFolder, bytesByHash);
15604
- if (!journeyEntry) continue;
15605
- if (seen.has(journeyEntry.name)) {
15606
- throw expected(
15607
- `Duplicate journey: "${journeyEntry.name}" (in ${entry.name}). Each journey name must be unique.`
15608
- );
15731
+ if (entry.isDirectory()) {
15732
+ const setName = entry.name;
15733
+ const subEntries = fs13.readdirSync(fullPath, { withFileTypes: true });
15734
+ for (const sub of subEntries) {
15735
+ if (sub.isFile() && sub.name.endsWith(".yaml")) {
15736
+ const relPath = `${setName}/${sub.name}`;
15737
+ if (!writtenRelPaths.has(relPath)) {
15738
+ fs13.unlinkSync(path15.join(fullPath, sub.name));
15739
+ }
15740
+ }
15741
+ }
15742
+ try {
15743
+ if (fs13.readdirSync(fullPath).length === 0) fs13.rmdirSync(fullPath);
15744
+ } catch {
15745
+ }
15609
15746
  }
15610
- seen.add(journeyEntry.name);
15611
- journeys.push(journeyEntry);
15612
15747
  }
15613
- return journeys;
15614
15748
  }
15615
- function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
15616
- const content = fs13.readFileSync(filePath, "utf-8");
15617
- let raw;
15618
- try {
15619
- raw = yaml5.load(content);
15620
- } catch (err) {
15621
- if (err instanceof yaml5.YAMLException) {
15622
- throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
15623
- }
15624
- throw err;
15625
- }
15626
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15627
- console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
15628
- return null;
15749
+ function buildJourneyYamlObject(journeyEntry, slug) {
15750
+ const out = {};
15751
+ if (journeyEntry.id) out.id = journeyEntry.id;
15752
+ if (journeyEntry.name !== slug) out.name = journeyEntry.name;
15753
+ out.agent = journeyEntry.agent;
15754
+ if (journeyEntry.agent_id) out.agent_id = journeyEntry.agent_id;
15755
+ if (journeyEntry.enabled === false) {
15756
+ out.enabled = false;
15629
15757
  }
15630
- const data = raw;
15631
- const fileSlug = path15.basename(filePath, ".yaml");
15632
- const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
15633
- if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
15634
- throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
15758
+ if (journeyEntry.evaluator_instructions) {
15759
+ out.evaluator_instructions = journeyEntry.evaluator_instructions;
15635
15760
  }
15636
- if (data.transcript !== void 0 && !Array.isArray(data.transcript)) {
15637
- throw expected(`Journey "${journeyName}" in ${relPath}: "transcript" must be an array.`);
15761
+ if (typeof journeyEntry.fixture === "string" && journeyEntry.fixture.trim()) {
15762
+ out.fixture = journeyEntry.fixture.trim();
15638
15763
  }
15639
- if (data.step_config !== void 0 && (typeof data.step_config !== "object" || Array.isArray(data.step_config) || data.step_config === null)) {
15640
- throw expected(`Journey "${journeyName}" in ${relPath}: "step_config" must be an object keyed by step_id.`);
15764
+ if (typeof journeyEntry.target_base === "string" && journeyEntry.target_base.trim()) {
15765
+ out.target_base = journeyEntry.target_base.trim();
15641
15766
  }
15642
- const journeyEntry = {
15643
- name: journeyName,
15644
- agent: data.agent
15645
- };
15646
- if (typeof data.id === "string") journeyEntry.id = data.id;
15647
- if (typeof data.agent_id === "string") journeyEntry.agent_id = data.agent_id;
15648
- if (data.enabled === false) journeyEntry.enabled = false;
15649
- if (typeof data.evaluator_instructions === "string") {
15650
- journeyEntry.evaluator_instructions = data.evaluator_instructions;
15767
+ if (journeyEntry.variables && Object.keys(journeyEntry.variables).length > 0) {
15768
+ out.variables = journeyEntry.variables;
15651
15769
  }
15652
- if (Array.isArray(data.transcript)) {
15653
- journeyEntry.transcript = data.transcript.map((turn, i) => {
15654
- const t = { ...turn };
15655
- applyTurnAttachments(hubFolder, t, `Journey "${journeyName}" transcript[${i}]`, bytesByHash);
15656
- return t;
15657
- });
15770
+ if (journeyEntry.transcript && journeyEntry.transcript.length > 0) {
15771
+ for (const turn of journeyEntry.transcript) assertAttachmentsAreLocalPaths(turn, `journey "${journeyEntry.name}" transcript`);
15772
+ out.transcript = journeyEntry.transcript;
15658
15773
  }
15659
- if (data.step_config && typeof data.step_config === "object" && !Array.isArray(data.step_config)) {
15660
- journeyEntry.step_config = data.step_config;
15774
+ if (journeyEntry.step_config && Object.keys(journeyEntry.step_config).length > 0) {
15775
+ out.step_config = journeyEntry.step_config;
15661
15776
  }
15662
- const variables = parseVariablesMap(data.variables, `Journey "${journeyName}" in ${relPath}`);
15663
- if (variables) journeyEntry.variables = variables;
15664
- const fixture = parseFixtureField(data.fixture, `Journey "${journeyName}" in ${relPath}`, "fixture");
15665
- if (fixture) journeyEntry.fixture = fixture;
15666
- const targetBase = parseFixtureField(data.target_base, `Journey "${journeyName}" in ${relPath}`, "target_base");
15667
- if (targetBase) journeyEntry.target_base = targetBase;
15668
- return journeyEntry;
15777
+ return out;
15669
15778
  }
15670
- function scanAgentYamlFiles(hubFolder) {
15671
- const agentsDir = path15.join(hubFolder, "agents");
15672
- if (!fs13.existsSync(agentsDir)) return [];
15673
- const yamlFiles = fs13.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
15674
- if (yamlFiles.length === 0) return [];
15675
- const agents = [];
15676
- for (const file of yamlFiles) {
15677
- const filePath = path15.join(agentsDir, file);
15678
- const content = fs13.readFileSync(filePath, "utf-8");
15679
- let agent;
15680
- try {
15681
- agent = yaml5.load(content);
15682
- } catch (err) {
15683
- if (err instanceof yaml5.YAMLException) {
15684
- const userError = new Error(`Invalid YAML in agents/${file}: ${err.message}`);
15685
- userError.isExpected = true;
15686
- throw userError;
15779
+ function writeJourneyYamlFiles(hubFolder, journeys) {
15780
+ const journeysDir = path15.join(hubFolder, "journeys");
15781
+ if (journeys.length === 0) {
15782
+ if (fs13.existsSync(journeysDir)) {
15783
+ cleanJourneyOrphans(journeysDir, /* @__PURE__ */ new Set());
15784
+ try {
15785
+ if (fs13.readdirSync(journeysDir).length === 0) fs13.rmdirSync(journeysDir);
15786
+ } catch {
15687
15787
  }
15688
- throw err;
15689
15788
  }
15690
- if (!agent || typeof agent !== "object") continue;
15691
- if (!agent.name) {
15692
- console.warn(` Warning: skipping ${file} (missing 'name' field)`);
15693
- continue;
15789
+ return;
15790
+ }
15791
+ if (!fs13.existsSync(journeysDir)) {
15792
+ fs13.mkdirSync(journeysDir, { recursive: true });
15793
+ }
15794
+ const writtenFiles = /* @__PURE__ */ new Set();
15795
+ const usedSlugs = /* @__PURE__ */ new Set();
15796
+ for (const journeyEntry of journeys) {
15797
+ const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
15798
+ const fileName = `${slug}.yaml`;
15799
+ const yamlContent = yaml5.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
15800
+ fs13.writeFileSync(path15.join(journeysDir, fileName), yamlContent, "utf-8");
15801
+ writtenFiles.add(fileName);
15802
+ }
15803
+ cleanJourneyOrphans(journeysDir, writtenFiles);
15804
+ }
15805
+ function cleanJourneyOrphans(journeysDir, writtenFiles) {
15806
+ const entries = fs13.readdirSync(journeysDir, { withFileTypes: true });
15807
+ for (const entry of entries) {
15808
+ if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
15809
+ fs13.unlinkSync(path15.join(journeysDir, entry.name));
15694
15810
  }
15695
- agents.push(agent);
15696
15811
  }
15697
- return agents;
15698
15812
  }
15699
- function parseResources(hubFolder, configResources) {
15813
+ function writeResourceFiles(hubFolder, resources) {
15700
15814
  const resourcesDir = path15.join(hubFolder, "resources");
15701
- const results = [];
15702
- for (const res of configResources) {
15703
- const resource = {
15704
- name: res.name
15705
- };
15706
- if (res.id) resource.id = res.id;
15707
- if (res.type === "knowledge" || res.type === "skill") resource.type = res.type;
15708
- else if (res.type !== void 0) throw new Error(`Invalid resource type for "${resource.name}": expected knowledge or skill`);
15709
- if (res.description) resource.description = res.description;
15710
- if (res.enabled === false) resource.enabled = false;
15711
- if (res.user_browsable) resource.user_browsable = res.user_browsable;
15712
- if (res.skill_name) resource.skill_name = res.skill_name;
15815
+ const currentSlugs = /* @__PURE__ */ new Set();
15816
+ for (const resource of resources) {
15713
15817
  const resSlug = slugify(resource.name);
15818
+ currentSlugs.add(resSlug);
15714
15819
  const resDir = path15.join(resourcesDir, resSlug);
15715
- if (fs13.existsSync(resDir)) {
15716
- const files = scanResourceFiles(resDir, "");
15717
- if (files.length > 0) {
15718
- resource.files = files;
15820
+ writeResourceFileTree(resDir, resource.files || [], hubFolder);
15821
+ }
15822
+ if (fs13.existsSync(resourcesDir)) {
15823
+ const existingDirs = fs13.readdirSync(resourcesDir, { withFileTypes: true });
15824
+ for (const entry of existingDirs) {
15825
+ if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
15826
+ fs13.rmSync(path15.join(resourcesDir, entry.name), { recursive: true, force: true });
15719
15827
  }
15720
15828
  }
15721
- results.push(resource);
15722
15829
  }
15723
- return results;
15724
15830
  }
15725
- var init_parser = __esm({
15726
- "src/lib/parser.ts"() {
15831
+ var YAML_DUMP_OPTIONS;
15832
+ var init_yaml_writer = __esm({
15833
+ "src/lib/yaml-writer.ts"() {
15727
15834
  "use strict";
15835
+ init_dist();
15728
15836
  init_utils();
15729
15837
  init_resource_files();
15730
15838
  init_eval_attachments();
15839
+ init_workspace_files();
15840
+ YAML_DUMP_OPTIONS = {
15841
+ lineWidth: -1,
15842
+ // don't wrap lines
15843
+ noRefs: true,
15844
+ // don't use YAML anchors
15845
+ quotingType: '"',
15846
+ forceQuotes: false,
15847
+ sortKeys: false
15848
+ };
15731
15849
  }
15732
15850
  });
15733
15851
 
15734
- // src/lib/diff-display.ts
15735
- function printDiff(summary) {
15736
- const lines = summary.split("\n");
15737
- for (const line of lines) {
15738
- if (line.startsWith("**") && line.endsWith("**")) {
15739
- console.log(`
15740
- \x1B[1m${line.replace(/\*\*/g, "")}\x1B[0m`);
15741
- } else if (line.trimStart().startsWith("+ ")) {
15742
- console.log(`\x1B[32m${line}\x1B[0m`);
15743
- } else if (line.trimStart().startsWith("- ")) {
15744
- console.log(`\x1B[31m${line}\x1B[0m`);
15745
- } else if (line.trimStart().startsWith("~ ")) {
15746
- console.log(`\x1B[33m${line}\x1B[0m`);
15747
- } else if (line.includes("(no changes)")) {
15748
- console.log(`\x1B[2m${line}\x1B[0m`);
15749
- } else {
15750
- console.log(line);
15751
- }
15852
+ // src/lib/hub-materializer.ts
15853
+ import * as path16 from "path";
15854
+ async function materializeHubFolder(hubFolder, payload, options = {}) {
15855
+ if (materialized.has(payload)) {
15856
+ throw new Error(
15857
+ "materializeHubFolder called twice on the same payload object \u2014 CLI bug. The first run rewrote the payload in place; fetch a fresh payload to write again."
15858
+ );
15752
15859
  }
15753
- console.log();
15860
+ materialized.add(payload);
15861
+ const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(payload);
15862
+ writeHubFolder(hubFolder, payload, options);
15863
+ await downloadBinaryResourceFiles(hubFolder, payload);
15864
+ const attachmentsWritten = await downloadEvalAttachments(hubFolder, attachmentDownloads);
15865
+ return { attachmentsWritten };
15754
15866
  }
15755
- var init_diff_display = __esm({
15756
- "src/lib/diff-display.ts"() {
15867
+ async function downloadBinaryResourceFiles(hubFolder, payload) {
15868
+ const resources = payload.resources || [];
15869
+ let downloadCount = 0;
15870
+ for (const resource of resources) {
15871
+ if (!resource.files) continue;
15872
+ const resDir = path16.join(hubFolder, "resources", slugify(resource.name));
15873
+ downloadCount += await downloadBinaryFiles(resDir, resource.files, hubFolder);
15874
+ }
15875
+ if (downloadCount > 0) {
15876
+ console.log(`Downloaded ${downloadCount} binary resource file(s).`);
15877
+ }
15878
+ }
15879
+ var materialized;
15880
+ var init_hub_materializer = __esm({
15881
+ "src/lib/hub-materializer.ts"() {
15757
15882
  "use strict";
15883
+ init_yaml_writer();
15884
+ init_eval_attachments();
15885
+ init_resource_files();
15886
+ init_utils();
15887
+ materialized = /* @__PURE__ */ new WeakSet();
15758
15888
  }
15759
15889
  });
15760
15890
 
@@ -15762,13 +15892,12 @@ var init_diff_display = __esm({
15762
15892
  var pull_exports = {};
15763
15893
  __export(pull_exports, {
15764
15894
  connectionsEqual: () => connectionsEqual,
15765
- downloadBinaryResourceFiles: () => downloadBinaryResourceFiles,
15766
15895
  pullCommand: () => pullCommand,
15767
15896
  resolveHubTarget: () => resolveHubTarget,
15768
15897
  writeProductionMirror: () => writeProductionMirror
15769
15898
  });
15770
15899
  import * as fs14 from "fs";
15771
- import * as path16 from "path";
15900
+ import * as path17 from "path";
15772
15901
  function parseArgs4(args2) {
15773
15902
  let autoConfirm = false;
15774
15903
  let hubSelector;
@@ -15787,7 +15916,7 @@ function resolveHubTarget(workspaceDir, selector) {
15787
15916
  const allHubs = scanWorkspaceHubs(workspaceDir);
15788
15917
  if (selector) {
15789
15918
  const match = allHubs.find(
15790
- (h) => h.hubId === selector || path16.basename(h.hubFolder) === selector
15919
+ (h) => h.hubId === selector || path17.basename(h.hubFolder) === selector
15791
15920
  );
15792
15921
  if (match) return { hubId: match.hubId, hubFolder: match.hubFolder };
15793
15922
  if (UUID_RE2.test(selector)) return { hubId: selector, hubFolder: null };
@@ -15804,7 +15933,7 @@ function resolveHubTarget(workspaceDir, selector) {
15804
15933
  }
15805
15934
  console.error(`Multiple hubs found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
15806
15935
  for (const h of previewHubs) {
15807
- console.error(` ${path16.basename(h.hubFolder)} (${h.hubId})`);
15936
+ console.error(` ${path17.basename(h.hubFolder)} (${h.hubId})`);
15808
15937
  }
15809
15938
  process.exit(1);
15810
15939
  }
@@ -15831,7 +15960,6 @@ async function pullCommand(args2) {
15831
15960
  return;
15832
15961
  }
15833
15962
  assertHubMatchesBinding(hubId);
15834
- const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(payload);
15835
15963
  let hubFolder = existingFolder;
15836
15964
  let wroteFiles = false;
15837
15965
  if (!hubFolder) {
@@ -15843,31 +15971,20 @@ async function pullCommand(args2) {
15843
15971
  payload.preview_label,
15844
15972
  payload.branch_name
15845
15973
  );
15846
- fs14.mkdirSync(path16.dirname(hubFolder), { recursive: true });
15974
+ fs14.mkdirSync(path17.dirname(hubFolder), { recursive: true });
15847
15975
  console.log("Writing hub configuration...");
15848
- writeHubFolder(hubFolder, payload);
15849
- await downloadBinaryResourceFiles(hubFolder, payload);
15850
- await downloadEvalAttachments(hubFolder, attachmentDownloads);
15976
+ await materializeHubFolder(hubFolder, payload);
15851
15977
  const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
15852
15978
  console.log(`Hub configuration written to ${finalFolder}`);
15853
15979
  wroteFiles = true;
15854
15980
  } else {
15855
- let cancelled = false;
15981
+ let action = "overwrite";
15856
15982
  try {
15857
15983
  const localPayload = parseHubFolder(hubFolder);
15858
15984
  const diffResult = await client.diff(hubId, prepareResourcePayload(localPayload, payload), "pull", organizationId);
15859
15985
  const connectionsChanged = !connectionsEqual(localPayload.connections, payload.connections);
15860
15986
  if (!diffResult.has_changes && !connectionsChanged) {
15861
- writeHubFolder(hubFolder, payload);
15862
- await downloadBinaryResourceFiles(hubFolder, payload);
15863
- await downloadEvalAttachments(hubFolder, attachmentDownloads);
15864
- const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
15865
- wroteFiles = true;
15866
- if (finalFolder === hubFolder) {
15867
- console.log("Metadata synced \u2014 no config changes.");
15868
- } else {
15869
- console.log("Metadata synced; folder renamed.");
15870
- }
15987
+ action = "sync_metadata";
15871
15988
  } else {
15872
15989
  console.log("\nChanges to apply locally:");
15873
15990
  if (connectionsChanged) {
@@ -15878,28 +15995,27 @@ async function pullCommand(args2) {
15878
15995
  const ok = await confirm("Apply these changes?");
15879
15996
  if (!ok) {
15880
15997
  console.log("Cancelled.");
15881
- cancelled = true;
15998
+ return;
15882
15999
  }
15883
16000
  }
15884
- if (!cancelled) {
15885
- writeHubFolder(hubFolder, payload);
15886
- await downloadBinaryResourceFiles(hubFolder, payload);
15887
- await downloadEvalAttachments(hubFolder, attachmentDownloads);
15888
- const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
15889
- console.log(`Hub configuration written to ${finalFolder}`);
15890
- wroteFiles = true;
15891
- }
16001
+ action = "apply";
15892
16002
  }
15893
16003
  } catch {
15894
- console.log("Writing hub configuration...");
15895
- writeHubFolder(hubFolder, payload);
15896
- await downloadBinaryResourceFiles(hubFolder, payload);
15897
- await downloadEvalAttachments(hubFolder, attachmentDownloads);
15898
- const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
16004
+ action = "overwrite";
16005
+ }
16006
+ if (action === "overwrite") console.log("Writing hub configuration...");
16007
+ await materializeHubFolder(hubFolder, payload);
16008
+ const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
16009
+ wroteFiles = true;
16010
+ if (action === "sync_metadata") {
16011
+ if (finalFolder === hubFolder) {
16012
+ console.log("Metadata synced \u2014 no config changes.");
16013
+ } else {
16014
+ console.log("Metadata synced; folder renamed.");
16015
+ }
16016
+ } else {
15899
16017
  console.log(`Hub configuration written to ${finalFolder}`);
15900
- wroteFiles = true;
15901
16018
  }
15902
- if (cancelled) return;
15903
16019
  }
15904
16020
  if (wroteFiles) autoBindIfUnbound(hubId);
15905
16021
  if (payload.production_hub_id) {
@@ -15908,11 +16024,8 @@ async function pullCommand(args2) {
15908
16024
  }
15909
16025
  async function writeProductionMirror(workspaceDir, prodPayload) {
15910
16026
  const folder = resolveHubFolder(workspaceDir, prodPayload.hub_id, prodPayload.hub.name, "production", null, null);
15911
- fs14.mkdirSync(path16.dirname(folder), { recursive: true });
15912
- const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(prodPayload);
15913
- writeHubFolder(folder, prodPayload, { seedAgentContext: false });
15914
- await downloadBinaryResourceFiles(folder, prodPayload);
15915
- await downloadEvalAttachments(folder, attachmentDownloads);
16027
+ fs14.mkdirSync(path17.dirname(folder), { recursive: true });
16028
+ await materializeHubFolder(folder, prodPayload, { seedAgentContext: false });
15916
16029
  const finalFolder = autoRenameHubFolder(folder, prodPayload.hub.name, "production", prodPayload.hub_id, null, null);
15917
16030
  prependMirrorMarker(finalFolder, prodPayload.hub_id);
15918
16031
  return finalFolder;
@@ -15927,7 +16040,7 @@ async function mirrorLinkedProduction(client, workspaceDir, productionHubId, org
15927
16040
  }
15928
16041
  }
15929
16042
  function prependMirrorMarker(hubFolder, productionHubId) {
15930
- const hubYaml = path16.join(hubFolder, "hub.yaml");
16043
+ const hubYaml = path17.join(hubFolder, "hub.yaml");
15931
16044
  try {
15932
16045
  const content = fs14.readFileSync(hubYaml, "utf-8");
15933
16046
  if (content.startsWith(MIRROR_MARKER_PREFIX)) return;
@@ -15937,18 +16050,6 @@ function prependMirrorMarker(hubFolder, productionHubId) {
15937
16050
  } catch {
15938
16051
  }
15939
16052
  }
15940
- async function downloadBinaryResourceFiles(hubFolder, payload) {
15941
- const resources = payload.resources || [];
15942
- let downloadCount = 0;
15943
- for (const resource of resources) {
15944
- if (!resource.files) continue;
15945
- const resDir = path16.join(hubFolder, "resources", slugify(resource.name));
15946
- downloadCount += await downloadBinaryFiles(resDir, resource.files, hubFolder);
15947
- }
15948
- if (downloadCount > 0) {
15949
- console.log(`Downloaded ${downloadCount} binary resource file(s).`);
15950
- }
15951
- }
15952
16053
  function connectionsEqual(a, b) {
15953
16054
  const listA = a || [];
15954
16055
  const listB = b || [];
@@ -15968,12 +16069,11 @@ var init_pull = __esm({
15968
16069
  "use strict";
15969
16070
  init_auth();
15970
16071
  init_api_client();
15971
- init_yaml_writer();
15972
16072
  init_parser();
15973
16073
  init_diff_display();
15974
16074
  init_utils();
15975
16075
  init_resource_files();
15976
- init_eval_attachments();
16076
+ init_hub_materializer();
15977
16077
  init_workspace();
15978
16078
  init_layout();
15979
16079
  init_workspace_files();
@@ -15994,10 +16094,11 @@ __export(push_exports, {
15994
16094
  pushCommand: () => pushCommand,
15995
16095
  reportUnresolvedPushTarget: () => reportUnresolvedPushTarget,
15996
16096
  selectExistingHub: () => selectExistingHub,
15997
- shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel
16097
+ shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel,
16098
+ syncAfterPush: () => syncAfterPush
15998
16099
  });
15999
16100
  import * as fs15 from "fs";
16000
- import * as path17 from "path";
16101
+ import * as path18 from "path";
16001
16102
  import * as yaml6 from "js-yaml";
16002
16103
  function parseArgs5(args2) {
16003
16104
  let autoConfirm = false;
@@ -16090,13 +16191,13 @@ function printErrors(errors) {
16090
16191
  }
16091
16192
  }
16092
16193
  async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, remoteConfig) {
16093
- const agentsDir = path17.join(hubFolder, "agents");
16194
+ const agentsDir = path18.join(hubFolder, "agents");
16094
16195
  let agentsWithIds = [];
16095
16196
  if (fs15.existsSync(agentsDir)) {
16096
16197
  const yamlFiles = fs15.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
16097
16198
  for (const file of yamlFiles) {
16098
16199
  try {
16099
- const content = fs15.readFileSync(path17.join(agentsDir, file), "utf-8");
16200
+ const content = fs15.readFileSync(path18.join(agentsDir, file), "utf-8");
16100
16201
  const agent = yaml6.load(content);
16101
16202
  if (agent?.id && agent.name) {
16102
16203
  agentsWithIds.push({ id: agent.id, name: agent.name });
@@ -16145,12 +16246,12 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
16145
16246
  for (const file of fs15.readdirSync(agentsDir)) {
16146
16247
  if (file.startsWith("__rename_temp_") && (file.endsWith(".md") || file.endsWith(".yaml"))) {
16147
16248
  console.warn(` Warning: removing orphaned temp file agents/${file}`);
16148
- fs15.unlinkSync(path17.join(agentsDir, file));
16249
+ fs15.unlinkSync(path18.join(agentsDir, file));
16149
16250
  }
16150
16251
  }
16151
16252
  const renameFileIfExists = (dir, oldName, newName) => {
16152
- const oldPath = path17.join(dir, oldName);
16153
- const newPath = path17.join(dir, newName);
16253
+ const oldPath = path18.join(dir, oldName);
16254
+ const newPath = path18.join(dir, newName);
16154
16255
  if (!fs15.existsSync(oldPath)) return false;
16155
16256
  fs15.renameSync(oldPath, newPath);
16156
16257
  return true;
@@ -16185,9 +16286,9 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
16185
16286
  }
16186
16287
  } else {
16187
16288
  for (const { oldSlug, newSlug } of renames) {
16188
- const hasOldFile = extensions.some((ext) => fs15.existsSync(path17.join(agentsDir, `${oldSlug}${ext}`)));
16289
+ const hasOldFile = extensions.some((ext) => fs15.existsSync(path18.join(agentsDir, `${oldSlug}${ext}`)));
16189
16290
  if (!hasOldFile) continue;
16190
- const hasNewFile = extensions.some((ext) => fs15.existsSync(path17.join(agentsDir, `${newSlug}${ext}`)));
16291
+ const hasNewFile = extensions.some((ext) => fs15.existsSync(path18.join(agentsDir, `${newSlug}${ext}`)));
16191
16292
  if (hasNewFile) {
16192
16293
  console.warn(` Warning: skipping rename agents/${oldSlug}.* \u2192 agents/${newSlug}.* (target already exists)`);
16193
16294
  continue;
@@ -16214,15 +16315,14 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
16214
16315
  );
16215
16316
  if (updatedYaml !== mainYamlContent) {
16216
16317
  fs15.writeFileSync(mainYamlPath, updatedYaml, "utf-8");
16217
- console.log(` Updated instructions paths in ${path17.basename(mainYamlPath)}`);
16318
+ console.log(` Updated instructions paths in ${path18.basename(mainYamlPath)}`);
16218
16319
  }
16219
16320
  }
16220
16321
  }
16221
16322
  async function syncAfterPush(client, hubId, hubFolder, organizationId) {
16222
16323
  try {
16223
16324
  const remoteConfig = await client.pull(hubId, organizationId);
16224
- writeHubFolder(hubFolder, remoteConfig);
16225
- await downloadBinaryResourceFiles(hubFolder, remoteConfig);
16325
+ await materializeHubFolder(hubFolder, remoteConfig);
16226
16326
  console.log("Local files synced with server.");
16227
16327
  const finalFolder = autoRenameHubFolder(hubFolder, remoteConfig.hub.name, remoteConfig.hub_environment, hubId, remoteConfig.preview_label, remoteConfig.branch_name);
16228
16328
  if (finalFolder !== hubFolder) {
@@ -16283,7 +16383,7 @@ async function pushSingleHub(client, hubId, hubFolder, autoConfirm, organization
16283
16383
  function selectExistingHub(workspaceDir, allHubs, selector, wsLabel) {
16284
16384
  if (selector) {
16285
16385
  const match = allHubs.find(
16286
- (h) => h.hubId === selector || path17.basename(h.hubFolder) === selector
16386
+ (h) => h.hubId === selector || path18.basename(h.hubFolder) === selector
16287
16387
  );
16288
16388
  if (!match) {
16289
16389
  console.error(`No hub matching --hub ${selector} found in ${wsLabel}/.`);
@@ -16351,7 +16451,7 @@ function reportUnresolvedPushTarget(reason, existingHubs, newHubs, selector, wsL
16351
16451
  if (existingHubs.length > 0) {
16352
16452
  console.error(`No pushable hub found in ${wsLabel}/ \u2014 only read-only production mirror folder(s). Edit the linked preview hub, pull one, or create a new hub with \`wayai create\`:`);
16353
16453
  for (const h of existingHubs) {
16354
- console.error(` ${path17.basename(h.hubFolder)} (${h.hubId}, production mirror)`);
16454
+ console.error(` ${path18.basename(h.hubFolder)} (${h.hubId}, production mirror)`);
16355
16455
  }
16356
16456
  } else {
16357
16457
  console.error(`No hub folders found. Create hub files in ${wsLabel}/<hub>/hub.yaml with \`hub: { name: ... }\` and run \`wayai push\` (or \`wayai create\`).`);
@@ -16364,10 +16464,10 @@ function reportUnresolvedPushTarget(reason, existingHubs, newHubs, selector, wsL
16364
16464
  console.error(`Multiple hub folders found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
16365
16465
  }
16366
16466
  for (const h of existingHubs) {
16367
- console.error(` ${path17.basename(h.hubFolder)} (${h.hubId})`);
16467
+ console.error(` ${path18.basename(h.hubFolder)} (${h.hubId})`);
16368
16468
  }
16369
16469
  for (const h of newHubs) {
16370
- console.error(` ${path17.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
16470
+ console.error(` ${path18.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
16371
16471
  }
16372
16472
  process.exit(1);
16373
16473
  }
@@ -16405,8 +16505,8 @@ async function pushCommand(args2) {
16405
16505
  if (label) {
16406
16506
  console.warn("Note: --label is only used when creating a new hub. To change an existing hub's label, use `wayai relabel`.");
16407
16507
  }
16408
- console.log(`Target hub: ${path17.basename(existing.hubFolder)} (${existing.hubId})`);
16409
- assertHubMatchesBinding(existing.hubId, path17.basename(existing.hubFolder));
16508
+ console.log(`Target hub: ${path18.basename(existing.hubFolder)} (${existing.hubId})`);
16509
+ assertHubMatchesBinding(existing.hubId, path18.basename(existing.hubFolder));
16410
16510
  await pushSingleHub(client, existing.hubId, existing.hubFolder, autoConfirm, organizationId);
16411
16511
  return;
16412
16512
  }
@@ -16419,8 +16519,7 @@ var init_push = __esm({
16419
16519
  init_auth();
16420
16520
  init_api_client();
16421
16521
  init_parser();
16422
- init_yaml_writer();
16423
- init_pull();
16522
+ init_hub_materializer();
16424
16523
  init_resource_files();
16425
16524
  init_diff_display();
16426
16525
  init_utils();
@@ -16437,7 +16536,7 @@ var create_exports = {};
16437
16536
  __export(create_exports, {
16438
16537
  createCommand: () => createCommand
16439
16538
  });
16440
- import * as path18 from "path";
16539
+ import * as path19 from "path";
16441
16540
  import * as fs16 from "fs";
16442
16541
  function parseArgs6(args2) {
16443
16542
  let autoConfirm = false;
@@ -16474,7 +16573,7 @@ async function createCommand(args2) {
16474
16573
  const resolution = resolveNewHubForCreate(workspaceDir, existingHubs, newHubs, folderSelector);
16475
16574
  if (!resolution.ok) {
16476
16575
  if (resolution.reason === "exists") {
16477
- const folder = path18.basename(resolution.existing.hubFolder);
16576
+ const folder = path19.basename(resolution.existing.hubFolder);
16478
16577
  console.error(`Hub "${folder}" already exists (${resolution.existing.hubId}). Use \`wayai push --hub ${folder}\` to update it.`);
16479
16578
  process.exit(1);
16480
16579
  }
@@ -16488,7 +16587,7 @@ async function createCommand(args2) {
16488
16587
  }
16489
16588
  console.error(`Multiple new hub folders found in ${wsLabel}/. Pass the folder name (\`wayai create <folder>\`) or run from inside one:`);
16490
16589
  for (const h of newHubs) {
16491
- console.error(` ${path18.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
16590
+ console.error(` ${path19.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
16492
16591
  }
16493
16592
  process.exit(1);
16494
16593
  }
@@ -16600,7 +16699,7 @@ __export(replicate_exports, {
16600
16699
  replicateCommand: () => replicateCommand
16601
16700
  });
16602
16701
  import * as fs17 from "fs";
16603
- import * as path19 from "path";
16702
+ import * as path20 from "path";
16604
16703
  function parseArgs8(args2) {
16605
16704
  let label;
16606
16705
  let hubSelector;
@@ -16646,9 +16745,8 @@ async function replicateCommand(args2) {
16646
16745
  payload.preview_label,
16647
16746
  payload.branch_name
16648
16747
  );
16649
- fs17.mkdirSync(path19.dirname(hubFolder), { recursive: true });
16650
- writeHubFolder(hubFolder, payload);
16651
- await downloadBinaryResourceFiles(hubFolder, payload);
16748
+ fs17.mkdirSync(path20.dirname(hubFolder), { recursive: true });
16749
+ await materializeHubFolder(hubFolder, payload);
16652
16750
  hubFolder = autoRenameHubFolder(
16653
16751
  hubFolder,
16654
16752
  payload.hub.name,
@@ -16658,15 +16756,15 @@ async function replicateCommand(args2) {
16658
16756
  payload.branch_name
16659
16757
  );
16660
16758
  autoBindIfUnbound(previewHubId);
16661
- console.log(`Preview written to ${path19.basename(hubFolder)}`);
16662
- console.log(` Switch to it with: wayai use ${path19.basename(hubFolder)}`);
16759
+ console.log(`Preview written to ${path20.basename(hubFolder)}`);
16760
+ console.log(` Switch to it with: wayai use ${path20.basename(hubFolder)}`);
16663
16761
  }
16664
16762
  var init_replicate = __esm({
16665
16763
  "src/commands/replicate.ts"() {
16666
16764
  "use strict";
16667
16765
  init_auth();
16668
16766
  init_api_client();
16669
- init_yaml_writer();
16767
+ init_hub_materializer();
16670
16768
  init_pull();
16671
16769
  init_workspace();
16672
16770
  init_layout();
@@ -16681,7 +16779,7 @@ var relabel_exports = {};
16681
16779
  __export(relabel_exports, {
16682
16780
  relabelCommand: () => relabelCommand
16683
16781
  });
16684
- import * as path20 from "path";
16782
+ import * as path21 from "path";
16685
16783
  function parseArgs9(args2) {
16686
16784
  let label;
16687
16785
  let clear = false;
@@ -16725,7 +16823,7 @@ async function relabelCommand(args2) {
16725
16823
  } else {
16726
16824
  console.error(`Multiple hubs found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
16727
16825
  for (const h of previewHubs) {
16728
- console.error(` ${path20.basename(h.hubFolder)} (${h.hubId})`);
16826
+ console.error(` ${path21.basename(h.hubFolder)} (${h.hubId})`);
16729
16827
  }
16730
16828
  }
16731
16829
  process.exit(1);
@@ -16734,7 +16832,7 @@ async function relabelCommand(args2) {
16734
16832
  console.log("This is a read-only production mirror \u2014 production hubs have no preview label. Relabel the linked preview hub instead.");
16735
16833
  return;
16736
16834
  }
16737
- assertHubMatchesBinding(target.hubId, path20.basename(target.hubFolder));
16835
+ assertHubMatchesBinding(target.hubId, path21.basename(target.hubFolder));
16738
16836
  console.log(normalizedLabel ? `Setting preview label to "${normalizedLabel}"...` : "Clearing preview label...");
16739
16837
  const { data } = await client.relabelPreview(target.hubId, normalizedLabel);
16740
16838
  const row = data[0];
@@ -16750,7 +16848,7 @@ async function relabelCommand(args2) {
16750
16848
  );
16751
16849
  console.log(serverLabel ? `Preview label set to "${serverLabel}".` : "Preview label cleared.");
16752
16850
  if (finalFolder !== target.hubFolder) {
16753
- console.log(`Hub folder is now ${path20.basename(finalFolder)}`);
16851
+ console.log(`Hub folder is now ${path21.basename(finalFolder)}`);
16754
16852
  }
16755
16853
  }
16756
16854
  var init_relabel = __esm({
@@ -16776,7 +16874,7 @@ __export(publish_exports, {
16776
16874
  renderHubDiff: () => renderHubDiff,
16777
16875
  runPublish: () => runPublish
16778
16876
  });
16779
- import * as path21 from "path";
16877
+ import * as path22 from "path";
16780
16878
  function parseArgs10(args2) {
16781
16879
  let autoConfirm = false;
16782
16880
  let hubSelector;
@@ -16890,7 +16988,7 @@ async function publishCommand(args2) {
16890
16988
  return;
16891
16989
  }
16892
16990
  }
16893
- assertHubMatchesBinding(hubId, hubFolder ? path21.basename(hubFolder) : void 0);
16991
+ assertHubMatchesBinding(hubId, hubFolder ? path22.basename(hubFolder) : void 0);
16894
16992
  await runPublish(client, { hubId, localConfig, organizationId, autoConfirm });
16895
16993
  }
16896
16994
  var STATUS_STYLE;
@@ -16923,7 +17021,7 @@ var use_exports = {};
16923
17021
  __export(use_exports, {
16924
17022
  useCommand: () => useCommand
16925
17023
  });
16926
- import * as path22 from "path";
17024
+ import * as path23 from "path";
16927
17025
  async function useCommand(args2) {
16928
17026
  const target = args2[0];
16929
17027
  if (!target || target.startsWith("-")) {
@@ -16945,7 +17043,7 @@ async function useCommand(args2) {
16945
17043
  const match = findHubByFolderName(workspaceDir, target);
16946
17044
  if (!match) {
16947
17045
  const newHubs = scanNewHubs(workspaceDir);
16948
- const pending = newHubs.find((h) => path22.basename(h.hubFolder) === target);
17046
+ const pending = newHubs.find((h) => path23.basename(h.hubFolder) === target);
16949
17047
  if (pending) {
16950
17048
  console.error(
16951
17049
  `"${target}" is a new hub that hasn't been created on the platform yet, so it has no id to bind to.
@@ -16956,10 +17054,10 @@ Create it first \u2014 the worktree binds automatically afterward:
16956
17054
  }
16957
17055
  console.error(`No hub matching "${target}" found in ${wsLabel}/. Pass a UUID or a folder name from:`);
16958
17056
  for (const h of scanWorkspaceHubs(workspaceDir)) {
16959
- console.error(` ${path22.basename(h.hubFolder)} (${h.hubId})`);
17057
+ console.error(` ${path23.basename(h.hubFolder)} (${h.hubId})`);
16960
17058
  }
16961
17059
  for (const h of newHubs) {
16962
- console.error(` ${path22.basename(h.hubFolder)} (new \u2014 "${h.hubName}", run \`wayai create ${path22.basename(h.hubFolder)}\`)`);
17060
+ console.error(` ${path23.basename(h.hubFolder)} (new \u2014 "${h.hubName}", run \`wayai create ${path23.basename(h.hubFolder)}\`)`);
16963
17061
  }
16964
17062
  process.exit(1);
16965
17063
  }
@@ -16999,10 +17097,10 @@ __export(migrate_exports, {
16999
17097
  });
17000
17098
  import { execFileSync as execFileSync3 } from "child_process";
17001
17099
  import * as fs18 from "fs";
17002
- import * as path23 from "path";
17100
+ import * as path24 from "path";
17003
17101
  function isTracked(gitRoot, p) {
17004
17102
  try {
17005
- execFileSync3("git", ["ls-files", "--error-unmatch", "--", path23.relative(gitRoot, p)], {
17103
+ execFileSync3("git", ["ls-files", "--error-unmatch", "--", path24.relative(gitRoot, p)], {
17006
17104
  cwd: gitRoot,
17007
17105
  stdio: ["pipe", "pipe", "pipe"]
17008
17106
  });
@@ -17012,10 +17110,10 @@ function isTracked(gitRoot, p) {
17012
17110
  }
17013
17111
  }
17014
17112
  function moveDir(gitRoot, from, to) {
17015
- fs18.mkdirSync(path23.dirname(to), { recursive: true });
17113
+ fs18.mkdirSync(path24.dirname(to), { recursive: true });
17016
17114
  if (isTracked(gitRoot, from)) {
17017
17115
  try {
17018
- execFileSync3("git", ["mv", path23.relative(gitRoot, from), path23.relative(gitRoot, to)], {
17116
+ execFileSync3("git", ["mv", path24.relative(gitRoot, from), path24.relative(gitRoot, to)], {
17019
17117
  cwd: gitRoot,
17020
17118
  stdio: ["pipe", "pipe", "pipe"]
17021
17119
  });
@@ -17032,12 +17130,12 @@ async function migrateCommand(_args) {
17032
17130
  console.error("Not inside a git repository.");
17033
17131
  process.exit(1);
17034
17132
  }
17035
- const rel = (p) => path23.relative(gitRoot, p);
17036
- const newWs = path23.join(gitRoot, WAYAI_LAYOUT.wsDir);
17037
- const legacyWs = path23.join(gitRoot, WAYAI_LAYOUT.legacy.wsDir);
17038
- const legacyOrg = path23.join(gitRoot, WAYAI_LAYOUT.legacy.orgAtRoot);
17039
- const newHubs = path23.join(newWs, WAYAI_LAYOUT.hubsSubdir);
17040
- const newOrg = path23.join(newWs, WAYAI_LAYOUT.orgSubdir);
17133
+ const rel = (p) => path24.relative(gitRoot, p);
17134
+ const newWs = path24.join(gitRoot, WAYAI_LAYOUT.wsDir);
17135
+ const legacyWs = path24.join(gitRoot, WAYAI_LAYOUT.legacy.wsDir);
17136
+ const legacyOrg = path24.join(gitRoot, WAYAI_LAYOUT.legacy.orgAtRoot);
17137
+ const newHubs = path24.join(newWs, WAYAI_LAYOUT.hubsSubdir);
17138
+ const newOrg = path24.join(newWs, WAYAI_LAYOUT.orgSubdir);
17041
17139
  const hasLegacyWs = isDirectory(legacyWs);
17042
17140
  const hasLegacyOrg = isDirectory(legacyOrg);
17043
17141
  if (!hasLegacyWs && !hasLegacyOrg) {
@@ -17112,7 +17210,7 @@ __export(send_message_exports, {
17112
17210
  sendMessageCommand: () => sendMessageCommand
17113
17211
  });
17114
17212
  import * as fs19 from "fs";
17115
- import * as path24 from "path";
17213
+ import * as path25 from "path";
17116
17214
  function statAttachment(filePath) {
17117
17215
  let stat;
17118
17216
  try {
@@ -17128,8 +17226,8 @@ function statAttachment(filePath) {
17128
17226
  return { filePath, size: stat.size };
17129
17227
  }
17130
17228
  function readAttachment(filePath, size) {
17131
- const fileName = path24.basename(filePath);
17132
- const ext = path24.extname(fileName).replace(/^\./, "");
17229
+ const fileName = path25.basename(filePath);
17230
+ const ext = path25.extname(fileName).replace(/^\./, "");
17133
17231
  return {
17134
17232
  file_name: fileName,
17135
17233
  file_binary: fs19.readFileSync(filePath).toString("base64"),
@@ -17179,7 +17277,7 @@ async function sendMessageCommand(args2) {
17179
17277
  }
17180
17278
  const stats = filePaths.map(statAttachment);
17181
17279
  const filesTotal = stats.reduce((sum, s) => sum + base64Length(s.size), 0);
17182
- const fileDetail = () => stats.map((s) => `${path24.basename(s.filePath)} ${asMb(s.size)} MB`).join(", ");
17280
+ const fileDetail = () => stats.map((s) => `${path25.basename(s.filePath)} ${asMb(s.size)} MB`).join(", ");
17183
17281
  if (filesTotal > MAX_MESSAGE_BODY_BYTES2) {
17184
17282
  console.error(
17185
17283
  `Error: ${fileDetail()} encode to ~${asMb(filesTotal)} MB, over the ${asMb(MAX_MESSAGE_BODY_BYTES2)} MB per-request limit.`
@@ -18640,7 +18738,7 @@ __export(eval_capture_exports, {
18640
18738
  evalCaptureCommand: () => evalCaptureCommand
18641
18739
  });
18642
18740
  import * as fs20 from "fs";
18643
- import * as path25 from "path";
18741
+ import * as path26 from "path";
18644
18742
  import * as yaml7 from "js-yaml";
18645
18743
  function isValidSetName(name) {
18646
18744
  if (name.length === 0 || name === "." || name === "..") return false;
@@ -18706,15 +18804,15 @@ async function evalCaptureCommand(args2) {
18706
18804
  const setFolderName = targetSetName;
18707
18805
  const scenarioName = parsed.evalName ?? `Capture ${parsed.conversationId.slice(0, 8)}`;
18708
18806
  const slug = slugify(scenarioName);
18709
- const evalsDir = path25.join(hubFolder, "evals");
18710
- const targetDir = path25.join(evalsDir, setFolderName);
18711
- const targetPath = path25.join(targetDir, `${slug}.yaml`);
18712
- if (!targetPath.startsWith(evalsDir + path25.sep)) {
18713
- console.error(`Resolved path "${path25.relative(hubFolder, targetPath)}" escapes evals/. Aborting.`);
18807
+ const evalsDir = path26.join(hubFolder, "evals");
18808
+ const targetDir = path26.join(evalsDir, setFolderName);
18809
+ const targetPath = path26.join(targetDir, `${slug}.yaml`);
18810
+ if (!targetPath.startsWith(evalsDir + path26.sep)) {
18811
+ console.error(`Resolved path "${path26.relative(hubFolder, targetPath)}" escapes evals/. Aborting.`);
18714
18812
  process.exit(1);
18715
18813
  }
18716
18814
  if (fs20.existsSync(targetPath)) {
18717
- console.error(`File already exists: ${path25.relative(hubFolder, targetPath)}. Use --name to choose a different name.`);
18815
+ console.error(`File already exists: ${path26.relative(hubFolder, targetPath)}. Use --name to choose a different name.`);
18718
18816
  process.exit(1);
18719
18817
  }
18720
18818
  console.log("Resolving scenario set...");
@@ -18748,7 +18846,7 @@ async function evalCaptureCommand(args2) {
18748
18846
  const yamlObj = buildEvalYamlObject(evalEntry, slug);
18749
18847
  fs20.mkdirSync(targetDir, { recursive: true });
18750
18848
  fs20.writeFileSync(targetPath, yaml7.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8");
18751
- const relPath = path25.relative(process.cwd(), targetPath);
18849
+ const relPath = path26.relative(process.cwd(), targetPath);
18752
18850
  console.log(`
18753
18851
  Wrote ${relPath}`);
18754
18852
  console.log("Run `wayai pull` to refresh the agent display name, then commit. The scenario is already on the platform.");
@@ -19976,22 +20074,22 @@ var init_set_connection_credential = __esm({
19976
20074
 
19977
20075
  // src/lib/org-workspace.ts
19978
20076
  import * as fs21 from "fs";
19979
- import * as path26 from "path";
20077
+ import * as path27 from "path";
19980
20078
  import * as yaml8 from "js-yaml";
19981
20079
  function getOrgDir(gitRoot) {
19982
20080
  return resolveLayout(gitRoot).orgDir;
19983
20081
  }
19984
20082
  function orgManifestExists(orgDir) {
19985
- return fs21.existsSync(path26.join(orgDir, ORG_MANIFEST_NAME));
20083
+ return fs21.existsSync(path27.join(orgDir, ORG_MANIFEST_NAME));
19986
20084
  }
19987
20085
  function parseOrgResources(orgDir) {
19988
- const manifestPath = path26.join(orgDir, ORG_MANIFEST_NAME);
20086
+ const manifestPath = path27.join(orgDir, ORG_MANIFEST_NAME);
19989
20087
  let manifest = {};
19990
20088
  if (fs21.existsSync(manifestPath)) {
19991
20089
  manifest = yaml8.load(fs21.readFileSync(manifestPath, "utf-8")) ?? {};
19992
20090
  }
19993
20091
  const rawResources = Array.isArray(manifest.resources) ? manifest.resources : [];
19994
- const resourcesDir = path26.join(orgDir, "resources");
20092
+ const resourcesDir = path27.join(orgDir, "resources");
19995
20093
  const resources = rawResources.map((res) => {
19996
20094
  const resource = { name: res.name };
19997
20095
  if (res.id) resource.id = res.id;
@@ -20003,7 +20101,7 @@ function parseOrgResources(orgDir) {
20003
20101
  if (res.environment) resource.environment = res.environment;
20004
20102
  if (Array.isArray(res.tags)) resource.tags = res.tags;
20005
20103
  if (Array.isArray(res.folders)) resource.folders = res.folders;
20006
- const resDir = path26.join(resourcesDir, slugify(resource.name));
20104
+ const resDir = path27.join(resourcesDir, slugify(resource.name));
20007
20105
  if (fs21.existsSync(resDir)) {
20008
20106
  const files = scanResourceFiles(resDir, "");
20009
20107
  if (files.length > 0) resource.files = files;
@@ -20020,21 +20118,21 @@ function writeOrgResources(orgDir, payload) {
20020
20118
  return rest;
20021
20119
  });
20022
20120
  fs21.writeFileSync(
20023
- path26.join(orgDir, ORG_MANIFEST_NAME),
20121
+ path27.join(orgDir, ORG_MANIFEST_NAME),
20024
20122
  yaml8.dump({ version: 1, resources: manifestResources }, YAML_DUMP_OPTIONS),
20025
20123
  "utf-8"
20026
20124
  );
20027
- const resourcesDir = path26.join(orgDir, "resources");
20125
+ const resourcesDir = path27.join(orgDir, "resources");
20028
20126
  const currentSlugs = /* @__PURE__ */ new Set();
20029
20127
  for (const resource of resources) {
20030
20128
  const resSlug = slugify(resource.name);
20031
20129
  currentSlugs.add(resSlug);
20032
- writeResourceFileTree(path26.join(resourcesDir, resSlug), resource.files || [], orgDir);
20130
+ writeResourceFileTree(path27.join(resourcesDir, resSlug), resource.files || [], orgDir);
20033
20131
  }
20034
20132
  if (fs21.existsSync(resourcesDir)) {
20035
20133
  for (const entry of fs21.readdirSync(resourcesDir, { withFileTypes: true })) {
20036
20134
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
20037
- fs21.rmSync(path26.join(resourcesDir, entry.name), { recursive: true, force: true });
20135
+ fs21.rmSync(path27.join(resourcesDir, entry.name), { recursive: true, force: true });
20038
20136
  }
20039
20137
  }
20040
20138
  }
@@ -20043,7 +20141,7 @@ async function downloadOrgBinaryFiles(orgDir, payload) {
20043
20141
  let count = 0;
20044
20142
  for (const resource of payload.resources ?? []) {
20045
20143
  if (!resource.files) continue;
20046
- const resDir = path26.join(orgDir, "resources", slugify(resource.name));
20144
+ const resDir = path27.join(orgDir, "resources", slugify(resource.name));
20047
20145
  count += await downloadBinaryFiles(resDir, resource.files, orgDir);
20048
20146
  }
20049
20147
  if (count > 0) console.log(`Downloaded ${count} binary resource file(s).`);
@@ -20300,13 +20398,13 @@ var init_list = __esm({
20300
20398
 
20301
20399
  // src/lib/template-files.ts
20302
20400
  import * as fs22 from "fs";
20303
- import * as path27 from "path";
20401
+ import * as path28 from "path";
20304
20402
  function readDirToFileMap(rootDir) {
20305
20403
  const out = {};
20306
20404
  function walk(dir, prefix) {
20307
20405
  const entries = fs22.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
20308
20406
  for (const entry of entries) {
20309
- const abs = path27.join(dir, entry.name);
20407
+ const abs = path28.join(dir, entry.name);
20310
20408
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
20311
20409
  if (entry.isDirectory()) walk(abs, rel);
20312
20410
  else if (entry.isFile()) out[rel] = fs22.readFileSync(abs, "utf-8");
@@ -20321,7 +20419,7 @@ function detectCollisions(targetDir, files) {
20321
20419
  if (!isSafeTemplatePath2(rel)) {
20322
20420
  throw new Error(`Refusing to inspect unsafe template path: ${rel}`);
20323
20421
  }
20324
- if (fs22.existsSync(path27.join(targetDir, rel))) collisions.push(rel);
20422
+ if (fs22.existsSync(path28.join(targetDir, rel))) collisions.push(rel);
20325
20423
  }
20326
20424
  return collisions.sort((a, b) => a.localeCompare(b));
20327
20425
  }
@@ -20331,8 +20429,8 @@ function writeFileMap(targetDir, files) {
20331
20429
  if (!isSafeTemplatePath2(rel)) {
20332
20430
  throw new Error(`Refusing to write unsafe template path: ${rel}`);
20333
20431
  }
20334
- const abs = path27.join(targetDir, rel);
20335
- fs22.mkdirSync(path27.dirname(abs), { recursive: true });
20432
+ const abs = path28.join(targetDir, rel);
20433
+ fs22.mkdirSync(path28.dirname(abs), { recursive: true });
20336
20434
  fs22.writeFileSync(abs, body, "utf-8");
20337
20435
  written.push(rel);
20338
20436
  }
@@ -20343,7 +20441,7 @@ function listDirRelFiles(dir) {
20343
20441
  function walk(d, prefix) {
20344
20442
  for (const entry of fs22.readdirSync(d, { withFileTypes: true })) {
20345
20443
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
20346
- if (entry.isDirectory()) walk(path27.join(d, entry.name), rel);
20444
+ if (entry.isDirectory()) walk(path28.join(d, entry.name), rel);
20347
20445
  else if (entry.isFile()) out.push(rel);
20348
20446
  }
20349
20447
  }
@@ -20370,7 +20468,7 @@ var templates_exports = {};
20370
20468
  __export(templates_exports, {
20371
20469
  templateCommand: () => templateCommand
20372
20470
  });
20373
- import * as path28 from "path";
20471
+ import * as path29 from "path";
20374
20472
  async function templateCommand(args2) {
20375
20473
  const [sub, ...rest] = args2;
20376
20474
  switch (sub) {
@@ -20486,8 +20584,8 @@ async function runTemplatePull(rest) {
20486
20584
  console.log(`Template "${slug}" has no WayAI slice${hint}`);
20487
20585
  return;
20488
20586
  }
20489
- const targetDir = path28.join(resolveLayout(gitRoot).hubsDir, slug);
20490
- const relTarget = path28.relative(gitRoot, targetDir);
20587
+ const targetDir = path29.join(resolveLayout(gitRoot).hubsDir, slug);
20588
+ const relTarget = path29.relative(gitRoot, targetDir);
20491
20589
  const collisions = detectCollisions(targetDir, detail.files);
20492
20590
  if (dryRun) {
20493
20591
  const planned = Object.keys(detail.files).sort((a, b) => a.localeCompare(b));
@@ -20678,7 +20776,7 @@ __export(admin_exports, {
20678
20776
  adminCommand: () => adminCommand
20679
20777
  });
20680
20778
  import * as fs23 from "fs";
20681
- import * as path29 from "path";
20779
+ import * as path30 from "path";
20682
20780
  import * as yaml10 from "js-yaml";
20683
20781
  async function adminCommand(args2) {
20684
20782
  const [group, ...afterGroup] = args2;
@@ -21132,10 +21230,10 @@ function loadTemplateManifest(slug) {
21132
21230
  console.error("Not in a git repository. Run from the tutorials/ authoring repo.");
21133
21231
  process.exit(1);
21134
21232
  }
21135
- const templateDir = path29.join(resolveTemplatesDir(gitRoot), slug);
21136
- const manifestPath = path29.join(templateDir, "template.yaml");
21233
+ const templateDir = path30.join(resolveTemplatesDir(gitRoot), slug);
21234
+ const manifestPath = path30.join(templateDir, "template.yaml");
21137
21235
  if (!fs23.existsSync(manifestPath)) {
21138
- console.error(`template.yaml not found at ${path29.relative(gitRoot, manifestPath)}`);
21236
+ console.error(`template.yaml not found at ${path30.relative(gitRoot, manifestPath)}`);
21139
21237
  process.exit(1);
21140
21238
  }
21141
21239
  let manifest;
@@ -21170,19 +21268,19 @@ async function runTemplatePush(positional) {
21170
21268
  const wayaiEntry = manifest.wayai.locales[locale];
21171
21269
  let wayaiFiles;
21172
21270
  if (wayaiEntry) {
21173
- const sliceDir = path29.resolve(templateDir, wayaiEntry.source);
21271
+ const sliceDir = path30.resolve(templateDir, wayaiEntry.source);
21174
21272
  if (!fs23.existsSync(sliceDir)) {
21175
21273
  console.error(`wayai source not found for ${locale}: ${wayaiEntry.source} (resolved ${sliceDir})`);
21176
21274
  process.exit(1);
21177
21275
  }
21178
21276
  if (!isInsideTemplateDir(sliceDir, templateDir)) {
21179
21277
  console.warn(
21180
- `Note: ${locale} source points at a live hub (${wayaiEntry.source}); it can go stale on a hub recreate. Run \`wayai admin template freeze ${slug} --locale ${locale} --from ${path29.basename(sliceDir)}\` to commit a stable frozen slice.`
21278
+ `Note: ${locale} source points at a live hub (${wayaiEntry.source}); it can go stale on a hub recreate. Run \`wayai admin template freeze ${slug} --locale ${locale} --from ${path30.basename(sliceDir)}\` to commit a stable frozen slice.`
21181
21279
  );
21182
21280
  }
21183
21281
  wayaiFiles = readDirToFileMap(sliceDir);
21184
21282
  }
21185
- const readmePath = path29.join(templateDir, `README.${locale}.md`);
21283
+ const readmePath = path30.join(templateDir, `README.${locale}.md`);
21186
21284
  const readmeMarkdown = fs23.existsSync(readmePath) ? fs23.readFileSync(readmePath, "utf-8") : "";
21187
21285
  locales[locale] = {
21188
21286
  readmeMarkdown,
@@ -21194,9 +21292,9 @@ async function runTemplatePush(positional) {
21194
21292
  }
21195
21293
  let hero;
21196
21294
  if (manifest.hero) {
21197
- const heroPath = path29.resolve(templateDir, manifest.hero);
21295
+ const heroPath = path30.resolve(templateDir, manifest.hero);
21198
21296
  if (fs23.existsSync(heroPath)) {
21199
- hero = { filename: path29.basename(heroPath), dataBase64: fs23.readFileSync(heroPath).toString("base64") };
21297
+ hero = { filename: path30.basename(heroPath), dataBase64: fs23.readFileSync(heroPath).toString("base64") };
21200
21298
  } else {
21201
21299
  console.error(`Warning: hero not found at ${manifest.hero}; skipping.`);
21202
21300
  }
@@ -21222,7 +21320,7 @@ async function runTemplatePush(positional) {
21222
21320
  }
21223
21321
  }
21224
21322
  function isInsideTemplateDir(dest, templateDir) {
21225
- return dest === templateDir || dest.startsWith(templateDir + path29.sep);
21323
+ return dest === templateDir || dest.startsWith(templateDir + path30.sep);
21226
21324
  }
21227
21325
  async function runTemplateFreeze(positional, flagArgs) {
21228
21326
  const slug = positional[0];
@@ -21283,7 +21381,7 @@ async function runTemplateFreeze(positional, flagArgs) {
21283
21381
  process.exit(1);
21284
21382
  }
21285
21383
  const entry = manifest.wayai.locales[targetLocale];
21286
- const dest = path29.resolve(templateDir, entry.source);
21384
+ const dest = path30.resolve(templateDir, entry.source);
21287
21385
  if (!isInsideTemplateDir(dest, templateDir) || dest === templateDir) {
21288
21386
  console.error(
21289
21387
  `source for "${targetLocale}" must be a committed path inside ${slug}/ (e.g. "./${targetLocale}/slice"), but resolved to:
@@ -21302,7 +21400,7 @@ Set it to a subdir of the template folder and re-run freeze.`
21302
21400
  const hubs = scanWorkspaceHubs(hubsDir);
21303
21401
  if (hubs.length) {
21304
21402
  console.error("Available hubs:");
21305
- for (const h of hubs) console.error(` ${path29.basename(h.hubFolder)} (${h.hubId})`);
21403
+ for (const h of hubs) console.error(` ${path30.basename(h.hubFolder)} (${h.hubId})`);
21306
21404
  }
21307
21405
  console.error(`
21308
21406
  Pass --from <folder|uuid>${selector ? "." : " or run `wayai use <hub>` first."}`);
@@ -21310,12 +21408,12 @@ Pass --from <folder|uuid>${selector ? "." : " or run `wayai use <hub>` first."}`
21310
21408
  }
21311
21409
  const liveFiles = readDirToFileMap(hubFolder);
21312
21410
  if (Object.keys(liveFiles).length === 0) {
21313
- console.error(`Source hub folder has no files to freeze: ${path29.relative(gitRoot, hubFolder)}`);
21411
+ console.error(`Source hub folder has no files to freeze: ${path30.relative(gitRoot, hubFolder)}`);
21314
21412
  process.exit(1);
21315
21413
  }
21316
21414
  const sanitized = sanitizeWayaiSliceFiles(liveFiles);
21317
- const relDest = path29.relative(gitRoot, dest);
21318
- const relSrc = path29.relative(gitRoot, hubFolder);
21415
+ const relDest = path30.relative(gitRoot, dest);
21416
+ const relSrc = path30.relative(gitRoot, hubFolder);
21319
21417
  if (dryRun) {
21320
21418
  const existing = new Set(listDirRelFiles(dest));
21321
21419
  const nextKeys = new Set(Object.keys(sanitized));
@@ -21375,7 +21473,7 @@ async function runTemplatePull2(positional, flagArgs) {
21375
21473
  console.log(JSON.stringify(detail, null, 2));
21376
21474
  return;
21377
21475
  }
21378
- const target = outDir ? path29.resolve(outDir) : path29.join(process.cwd(), "template-store-pull", slug);
21476
+ const target = outDir ? path30.resolve(outDir) : path30.join(process.cwd(), "template-store-pull", slug);
21379
21477
  const written = writeFileMap(target, detail.files);
21380
21478
  console.log(`Pulled "${slug}" from the store \u2192 ${target} (${written.length} file${written.length === 1 ? "" : "s"}).`);
21381
21479
  const uses = detail.uses ?? [];
@@ -21398,13 +21496,13 @@ async function runSkillInstall(positional) {
21398
21496
  throw err;
21399
21497
  }
21400
21498
  const root = findGitRoot() ?? process.cwd();
21401
- const present = HARNESS_SKILL_DIRS.filter((dir) => fs23.existsSync(path29.join(root, dir)));
21499
+ const present = HARNESS_SKILL_DIRS.filter((dir) => fs23.existsSync(path30.join(root, dir)));
21402
21500
  const targets = present.length > 0 ? present : HARNESS_SKILL_DIRS;
21403
21501
  const fileCount = Object.keys(res.files).length;
21404
21502
  const relDirs = targets.map((harness) => {
21405
- const targetDir = path29.join(root, harness, "skills", name);
21503
+ const targetDir = path30.join(root, harness, "skills", name);
21406
21504
  writeFileMap(targetDir, res.files);
21407
- return `${path29.relative(root, targetDir)}/`;
21505
+ return `${path30.relative(root, targetDir)}/`;
21408
21506
  });
21409
21507
  console.log(`Installed skill "${name}" (${fileCount} file${fileCount === 1 ? "" : "s"}) \u2192 ${relDirs.join(", ")}`);
21410
21508
  console.log("Reload your agent (e.g. restart Claude Code) to pick up the skill.");
@@ -21869,7 +21967,7 @@ async function runReportList(flagArgs) {
21869
21967
  }
21870
21968
  console.log(`Reports: ${result.data.length}/${result.total}`);
21871
21969
  for (const r of result.data) {
21872
- const age = Math.floor((Date.now() - r.created_at) / 1e3 / 60);
21970
+ const age = Math.floor((Date.now() - Date.parse(r.created_at)) / 1e3 / 60);
21873
21971
  const meta = [
21874
21972
  r.source,
21875
21973
  r.status,
@@ -22290,6 +22388,21 @@ var init_admin = __esm({
22290
22388
  }
22291
22389
  });
22292
22390
 
22391
+ // src/lib/terminal-output.ts
22392
+ import { stripVTControlCharacters } from "util";
22393
+ function sanitizeTerminalText(value) {
22394
+ const normalizedNewlines = value.replace(/\r\n?/g, "\n");
22395
+ return stripVTControlCharacters(normalizedNewlines).replace(
22396
+ /[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g,
22397
+ ""
22398
+ );
22399
+ }
22400
+ var init_terminal_output = __esm({
22401
+ "src/lib/terminal-output.ts"() {
22402
+ "use strict";
22403
+ }
22404
+ });
22405
+
22293
22406
  // src/lib/version-status.ts
22294
22407
  function componentStatus(current, latest) {
22295
22408
  return { current, latest, behind: Boolean(current && latest && isNewerVersion(latest, current)) };
@@ -22602,7 +22715,8 @@ async function reportListCommand(args2) {
22602
22715
  return;
22603
22716
  }
22604
22717
  for (const r of result.data) {
22605
- console.log(`${r.report_id} ${r.status.padEnd(10)} ${r.classification}`);
22718
+ const title = sanitizeTerminalText(r.title).replace(/\s+/g, " ").trim();
22719
+ console.log(`${r.report_id} ${r.status.padEnd(10)} ${r.classification} ${title}`);
22606
22720
  }
22607
22721
  } catch (err) {
22608
22722
  reportActionError(err);
@@ -22624,7 +22738,11 @@ async function reportGetCommand(args2) {
22624
22738
  return;
22625
22739
  }
22626
22740
  const { report, messages } = result;
22741
+ const title = sanitizeTerminalText(report.title);
22742
+ const description = sanitizeTerminalText(report.description);
22627
22743
  console.log(`Report ${report.report_id}`);
22744
+ console.log(` title: ${title}`);
22745
+ console.log(` description: ${description.replace(/\n/g, "\n ")}`);
22628
22746
  console.log(` status: ${report.status}`);
22629
22747
  console.log(` classification: ${report.classification}`);
22630
22748
  if (report.contest_count > 0) console.log(` contests: ${report.contest_count}`);
@@ -22632,7 +22750,7 @@ async function reportGetCommand(args2) {
22632
22750
  if (messages.length > 0) {
22633
22751
  console.log("\n Thread:");
22634
22752
  for (const m of messages) {
22635
- console.log(` [${m.author_role}] ${m.body}`);
22753
+ console.log(` [${m.author_role}] ${sanitizeTerminalText(m.body)}`);
22636
22754
  }
22637
22755
  }
22638
22756
  if (report.status === "shipped") {
@@ -22701,6 +22819,7 @@ var init_report = __esm({
22701
22819
  "use strict";
22702
22820
  init_auth();
22703
22821
  init_api_client();
22822
+ init_terminal_output();
22704
22823
  init_report_edit_args();
22705
22824
  init_report_create();
22706
22825
  }
@@ -22748,7 +22867,7 @@ var init_update = __esm({
22748
22867
  init_sentry();
22749
22868
  import { readFileSync as readFileSync20 } from "fs";
22750
22869
  import { fileURLToPath as fileURLToPath2 } from "url";
22751
- import { dirname as dirname9, join as join27 } from "path";
22870
+ import { dirname as dirname9, join as join28 } from "path";
22752
22871
 
22753
22872
  // src/lib/errors.ts
22754
22873
  init_api_client();
@@ -22928,7 +23047,7 @@ Run \`wayai admin skill install\` to update.`);
22928
23047
 
22929
23048
  // src/index.ts
22930
23049
  var __dirname = dirname9(fileURLToPath2(import.meta.url));
22931
- var pkg = JSON.parse(readFileSync20(join27(__dirname, "..", "package.json"), "utf-8"));
23050
+ var pkg = JSON.parse(readFileSync20(join28(__dirname, "..", "package.json"), "utf-8"));
22932
23051
  var [, , command, ...args] = process.argv;
22933
23052
  var isBackgroundRefresh = command === REFRESH_COMMAND;
22934
23053
  if (!isBackgroundRefresh) initSentry(command, pkg.version);