@wayai/cli 0.3.165 → 0.3.167

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
@@ -4797,6 +4797,31 @@ function monitorRuleKeysOffTrigger(monitorConfig) {
4797
4797
  const config = monitorConfig;
4798
4798
  return MONITOR_RULE_KEYS.filter((key) => config[key] !== void 0);
4799
4799
  }
4800
+ function monitorRulesStructuredOutputMessage(trigger) {
4801
+ const [article, consequence] = trigger === "assistant_reply" ? ["an", "every reply is delivered unjudged"] : ["a", "none of their actions runs"];
4802
+ return `${article} ${trigger} monitor with rules needs structured output: its rules read variables only a JSON answer carries, so on text output no rule can match and ${consequence}. Set response_format to json_schema (wayai push), or remove the rules.`;
4803
+ }
4804
+ function monitorRulesStructuredOutputRefusal(agent) {
4805
+ const trigger = refusedRulesTrigger(agent);
4806
+ return trigger === null ? null : monitorRulesStructuredOutputMessage(trigger);
4807
+ }
4808
+ function refusedRulesTrigger(agent) {
4809
+ if (agent.agent_role !== "monitor") return null;
4810
+ const config = decodedMonitorConfig(agent.monitor_config);
4811
+ const trigger = resolveMonitorTrigger(config);
4812
+ if (!MONITOR_RULE_TRIGGERS.includes(trigger)) return null;
4813
+ const rules = config.rules;
4814
+ if (!Array.isArray(rules) || rules.length === 0) return null;
4815
+ return agent.response_format === "json_schema" ? null : trigger;
4816
+ }
4817
+ function decodedMonitorConfig(value) {
4818
+ if (typeof value !== "string") return value;
4819
+ try {
4820
+ return JSON.parse(value);
4821
+ } catch {
4822
+ return null;
4823
+ }
4824
+ }
4800
4825
  function monitorRuleActionKinds(trigger) {
4801
4826
  return trigger === "assistant_reply" ? MONITOR_ASSISTANT_REPLY_ACTION_KINDS : MONITOR_USER_MESSAGE_ACTION_KINDS;
4802
4827
  }
@@ -4945,6 +4970,10 @@ function refineDecisionsModelBinding(body, ctx) {
4945
4970
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agent_settings", "model"], message: refusal2.message });
4946
4971
  }
4947
4972
  }
4973
+ function refineMonitorRulesStructuredOutput(body, ctx) {
4974
+ const refusal2 = monitorRulesStructuredOutputRefusal(body);
4975
+ if (refusal2) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["response_format"], message: refusal2 });
4976
+ }
4948
4977
  function visibleLength(s) {
4949
4978
  return s.replace(INVISIBLE_NAME_CHARS, "").length;
4950
4979
  }
@@ -5791,7 +5820,7 @@ function refineHubAsCodeDecisionsModel(config, ctx) {
5791
5820
  const refusal2 = checkDecisionsModelBinding({
5792
5821
  model: readAgentSettingsModel(a.settings),
5793
5822
  agent_role: a.role,
5794
- response_format: responseFormat ? "json_schema" : "text",
5823
+ response_format: pushedResponseFormatColumn(responseFormat),
5795
5824
  schema_json: responseFormat?.schema_json ?? null
5796
5825
  });
5797
5826
  if (refusal2) {
@@ -5803,6 +5832,25 @@ function refineHubAsCodeDecisionsModel(config, ctx) {
5803
5832
  }
5804
5833
  });
5805
5834
  }
5835
+ function pushedResponseFormatColumn(responseFormat) {
5836
+ return responseFormat ? "json_schema" : "text";
5837
+ }
5838
+ function refineHubAsCodeMonitorRulesOutput(config, ctx) {
5839
+ const agents = config?.agents;
5840
+ if (!Array.isArray(agents)) return;
5841
+ agents.forEach((agent, agentIndex) => {
5842
+ const a = agent;
5843
+ if (!a) return;
5844
+ const refusal2 = monitorRulesStructuredOutputRefusal({
5845
+ agent_role: a.role,
5846
+ response_format: pushedResponseFormatColumn(a.response_format),
5847
+ monitor_config: a.monitor_config
5848
+ });
5849
+ if (refusal2) {
5850
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agents", agentIndex, "response_format"], message: refusal2 });
5851
+ }
5852
+ });
5853
+ }
5806
5854
  var uuidSchema, paginationSchema, timestampSchema, idParamSchema, clientOptionalString, clientOptionalBoolean, clientOptionalNumber, messageAttachment, MAX_MESSAGE_BODY_BYTES, MAX_ATTACHMENTS_PER_MESSAGE, MAX_AUDIO_FILE_BASE64_BYTES, primaryRegionSchema, placementSourceSchema, orgIdParam, orgAdminIdParam, createOrganizationBody, updateOrganizationBody, addOrgAdminBody, AUTH_TYPE, ORG_CREDENTIAL_AUTH_TYPES, AUTH_TYPE_DISPLAY, LEGACY_AUTH_TYPE_MAP, VALID_AUTH_TYPES, AGENT_ROLES, SAFE_USER_ID_REGEX, SUPPORTED_LOCALES, SUMMARIZATION_THRESHOLD_MIN, SUMMARIZATION_THRESHOLD_MAX, PREVIOUS_CONVERSATIONS_MAX, FLAG_CONDITION_OPERATORS, DECISION_SCORE_MIN_LEVELS, DECISION_SCORE_MAX_LEVELS, DECISIONS_MODEL_PREFIXES, DECISION_CAPABLE_AGENT_ROLES, NATIVE_TOOL_SCHEMAS, WAYAI_CONNECTOR, BASE_NATIVE_TOOLS, NATIVE_TOOLS, NATIVE_TOOL_NAMES, previousConversationsCountField, summarizationThresholdField, flagConditionSchema, MONITOR_TRIGGERS, monitorTriggerSchema, MONITOR_FIRING_TRIGGERS, MONITOR_DELAY_SECONDS_MIN, MONITOR_IDLE_DELAY_REQUIRED_MESSAGE, MONITOR_HISTORY_MESSAGES_MAX, monitorHistoryMessagesSchema, monitorIncludeToolResultsSchema, MONITOR_INPUT_SHAPING_KEYS, MONITOR_INPUT_SHAPING_SCHEMAS, MONITOR_INPUT_SHAPING_IDLE_MESSAGE, monitorArgumentSourceSchema, MONITOR_NOTE_TEMPLATE_MAX, monitorActionSchema, monitorRuleSchema, MONITOR_RULE_KEYS, MONITOR_RULE_SCHEMAS, MONITOR_RULE_TRIGGERS, MONITOR_RULE_TRIGGER_MESSAGE, MONITOR_USER_MESSAGE_ACTION_KINDS, MONITOR_ASSISTANT_REPLY_ACTION_KINDS, INSERT_NOTE_TOOL_NAME, RUN_MONITOR_TOOL_NAME, MONITOR_RULE_ALLOWED_NATIVE_TOOLS, MONITOR_RULE_REENTRY_TOOLS, MONITOR_RULE_REENTRY_TRACKS, monitorConfigField, flagConditionsField, 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, followupTypeSchema, EVENT_RELATIVE_FOLLOWUP_TYPES, followupTimeUnitSchema, followupIdSchema, SELECTABLE_FOLLOWUP_TYPES, MAX_LINKED_FOLLOWUP_TARGETS_PER_STATUS, followupSchema, MAX_OUTCOMES_PER_STATUS, kanbanOutcomeSchema, EXCLUSIVE_FLAG_PAIRS, kanbanStatusSchema, kanbanStatusesArraySchema, laneSchema, lanesArraySchema, hubIdParam, hubAdminIdParam, hubUserIdParam, hubIdentityIdParam, hubTeamIdParam, hubTeamUserDeleteParam, hubSchemaQuery, PREVIEW_LABEL_MAX_LENGTH, MAX_ENDED_INDEX_RETENTION_DAYS, MAX_EVAL_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, CONTEXT_BOUNDARIES, contextBoundarySchema, 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, ORG_TAG_NAME_REGEX, ORG_TAG_NAME_MAX_LENGTH, createOrgTagBody, updateOrgTagBody, orgCredentialIdParam, listOrgCredentialsQuery, createOrgCredentialBody, updateOrgCredentialBody, BASE_CREDENTIAL_MANAGED_BY, BASE_CREDENTIAL_NAME_MAX, baseCredentialNameSchema, createBaseCredentialLinkBody, baseCredentialLinkParams, baseCredentialLinkQuery, 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, analyticsSqlTable, analyticsSqlSchemaQuery, EVAL_PACING_PRESET_NAMES, PACING_INTERVAL_MIN_MS, PACING_INTERVAL_MAX_MS, EVAL_MAX_RUNS_PER_SCENARIO, EVAL_RUN_DEADLINE_MIN_MS, EVAL_RUN_DEADLINE_MAX_MS, ISO_UTC_RE, paginationSchema2, evalIdParam, sessionIdParam, scenarioSetIdParam, hubIdQuery, evalDateContextSchema, EVAL_FIXTURE_NAME_MAX_LENGTH, evalFixtureOverrideSchema, runSessionQuery, EVAL_INPUT_ROLES, EVAL_INPUT_ROLE_LIST, ROLE_ECHO_MAX, EVAL_INITIAL_STATE_SCOPES, MAX_INITIAL_STATE_ENTRIES, evalInitialStateEntry, createEvalBody, updateEvalBody, EVAL_LIST_MAX_LIMIT, evalListLimit, getEvalsQuery, toggleEvalBody, bulkToggleEvalsBody, pacingConfigSchema, EVAL_MAX_SELECTED_SCENARIOS, evalScenarioSelectionEntrySchema, evalScenarioSelectionSchema, 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, evalSessionConfigResponseSchema, evalSessionResponseSchema, evalSessionListConfigSchema, evalSessionListItemSchema, seedReleaseStatusSchema, EVAL_SESSION_NOT_QUIESCENT, getConversationsQuery, getMessagesQuery, appMessageSenderType, appMessageReceiverType, apiChannelMessage, apiChannelMessageBody, appMessageBody, appMessageResponse, updateUserBody, updateProfileBody, pathnameRegex, updatePreferencesBody, registerPushTokenBody, deletePushTokenQuery, activityQuery, deletionModeSchema, deletionRequestBody, archiveHubIdParam, archiveConversationParams, archiveListQuery, archiveMessageObservabilityParams, conversationIdParam, claimBody, releaseBody, transferBody, closeBody, additionalContextPayloadSchema, kanbanUpdateBody, annotateConversationBody, observabilityParams, observabilityListParams, deleteUserConversationsBody, deleteUserConversationsResponse, sendMessageBody, listConversationsQuery, flagSourceSchema, getConversationsResponse, getConversationDetailParams, getConversationDetailQuery, getConversationDetailResponse, listWhatsAppTemplatesQuery, sendWhatsAppTemplateBody, copilotSuggestBody, copilotSuggestResponse, CONSULT_THREAD_STATUSES, consultThreadStatus, CONSULT_THREAD_TITLE_MAX, consultThreadTitle, consultThreadSummary, listConsultThreadsQuery, listConsultThreadsResponse, createConsultThreadBody, createConsultThreadResponse, consultThreadIdParam, updateConsultThreadBody, updateConsultThreadResponse, CONSULT_AWAIT_TIMEOUT_MS, billingOrgIdParam, subscriptionItemParams, spendCapBody, growthPacksBody, portalSessionBody, checkoutSessionBody, updateBillingCurrencyBody, invoicesQuery, planKeySchema, billingStatusSchema, billingPlanTypeSchema, dataUsageCountersSchema, REKOR_ENTITLEMENT_CONTRACT_VERSION, entitlementProjectionSchema, rekorEntitlementRefreshMessageSchema, 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, hubAsCodeStateSchema, hubAsCodeResourceFileSchema, hubAsCodeResourceSchema, uuidPattern, ciUuidSchema, boundedHubAsCodeResourcesSchema, hubAsCodeConfigSchema, ciPullHubIdParam, ciBranchParam, ciPullQuery, ciHubsQuery, ciLookupQuery, ciBranchesQuery, ciSyncBody, ciPushBody, ciDiffBody, ciPublishBody, ciPublishPreviewBody, ciSyncMcpBody, ciOrgResourcesParam, orgResourcesConfigSchema, ciOrgResourcesPushBody, ciOrgResourcesDiffBody, CI_APPLY_ENTITY_KINDS, CI_APPLY_OPERATIONS, ciApplyErrorBaseSchema, ciApplyErrorSchema, ciSyncCountSchema, ciSyncChangesSchema, ciSyncWarningSchema, ciSyncResponseSchema, adminOrgIdParam, adminOrgAdminIdParam, adminUserIdParam, PRICING_PLAN_ID_RE, adminPlanIdParam, adminOrganizationsQuery, adminUserSearchQuery, kanbanSlugMigrationQuery, adjustQuotaBody, updatePlanBody, addAdminUserBody, updatePlatformConfigBody, updateFreeOrgLimitBody, adminScopedTargetBody, adminRepairLegacyOrgGrantsBody, adminRepairCompedPlansBody, updatePricingPlanBody, dataExplorerSearchQuery, dataExplorerOrgIdParam, dataExplorerHubIdParam, dataExplorerConvIdParam, dataExplorerDoInstancesQuery, dataExplorerNsIdParam, dataExplorerKvKeysBody, dataExplorerKvKeyBody, uuidOrHexId, dataExplorerDeleteOrgParam, dataExplorerDeleteHubParam, dataExplorerDeleteConvParam, dataExplorerDeleteUserParam, dataExplorerDeleteQuery, dataExplorerKvDeleteBody, 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, META_ENTITY_ID_PATTERN, metaEntityId, whatsappCallbackBody, authMeResponse, wsTicketResponse, sessionCheckResponse, logoutResponse, magicCodeSendBody, magicCodeSendResponse, magicCodeVerifyBody, magicCodeVerifyResponse, passwordLoginBody, 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, jsonSchemaSchema, hubUserIdentity, stateValueEntry, getHubUserContextResponse, updateHubUserBody, updateHubUserResponse, unlockHubUserNameResponse, stateResetParams, stateResetResponse, hubAlertsParam, hubAlertSeverity, hubAlert, hubAlertsListResponse, noticeSeveritySchema, noticeStatusSchema, NOTICE_SCOPE_REGEX, noticeScopeSchema, noticeLinkSchema, createNoticeBody, updateNoticeBody, listNoticesQuery, noticeIdParamSchema, ADMIN_SKILL_NAME_REGEX, adminSkillNameParam, REKOR_ACTOR_TYPE_HEADER, REKOR_ACTOR_ID_HEADER, REKOR_ACTOR_LABEL_HEADER, REKOR_ACTOR_ORG_HEADER, REKOR_ACTOR_ADMIN_HEADER, REKOR_CLIENT_HEADER, REKOR_TOOL_ID_HEADER, REKOR_CLIENTS, rekorClientSchema, MAX_REKOR_TOOL_ID_LENGTH, rekorToolIdSchema, DATA_PROXY_MOUNT, DATA_PROXY_PREFIX, REKOR_V1_PREFIX, DATA_PROXY_ORG_QUERY_PARAM, MAX_PROVISIONING_BODY_BYTES, dataProxyQuery, rekorAttestation, REQUIRED_REKOR_ATTESTATION_HEADERS, BASE_DESTRUCTION_MOUNT, BASE_DESTRUCTION_PREFIX, BASE_DESTRUCTION_CONFIRM_PATH, baseDestructionConfirmBody, BASE_DESTRUCTION_GRACE_MS, BASE_DESTRUCTION_CONFIRM_TTL_MS, BASE_DESTRUCTION_ORG_QUERY_PARAM, baseDestructionQuery, baseDestructionParam, baseDestructionInitiateBody, baseDestructionStatus, baseDestructionRequest, baseDestructionListResponse, baseDestructionInitiateResponse, baseDestructionConfirmResponse, baseDestructionCancelResponse, rekorBaseChangeMessageSchema;
5807
5855
  var init_contracts = __esm({
5808
5856
  "../../packages/core/dist/contracts/index.js"() {
@@ -6985,7 +7033,10 @@ var init_contracts = __esm({
6985
7033
  previous_conversations_count: previousConversationsCountField,
6986
7034
  monitor_config: monitorConfigField,
6987
7035
  flag_conditions: flagConditionsField
6988
- }).passthrough().superRefine(refineDecisionsModelBinding);
7036
+ }).passthrough().superRefine((body, ctx) => {
7037
+ refineDecisionsModelBinding(body, ctx);
7038
+ refineMonitorRulesStructuredOutput(body, ctx);
7039
+ });
6989
7040
  updateAgentBody = external_exports.object({
6990
7041
  additional_context_template: external_exports.string().max(2e4).optional(),
6991
7042
  summarization_threshold_tokens: summarizationThresholdField,
@@ -8498,8 +8549,12 @@ var init_contracts = __esm({
8498
8549
  name: clientOptionalString
8499
8550
  }).optional(),
8500
8551
  message: apiChannelMessage,
8501
- // Idempotency key: maps to channel_message_sid; a retried POST echoes the original
8502
- // message_id and skips billing / turn dispatch (dedupe in ConversationDO.storeMessage).
8552
+ // Idempotency key: maps to channel_message_sid, deduped within one conversation
8553
+ // (ConversationDO.storeMessage). A retried POST resolves to the same conversation (a chat
8554
+ // hub's user, the external_ref, the conversation_id, or on a task hub with neither, this key
8555
+ // scoped by the sender), so it echoes the original message_id and skips billing / turn
8556
+ // dispatch. A failed store answers a retryable 503 only when this key is set; without it a
8557
+ // retry could store the message twice, so it answers 400.
8503
8558
  client_message_id: external_exports.string().min(1).max(256).optional()
8504
8559
  }).refine((b) => !(b.external_ref && b.conversation_id), {
8505
8560
  message: "provide external_ref or conversation_id, not both",
@@ -9276,6 +9331,7 @@ var init_contracts = __esm({
9276
9331
  refineHubAsCodeFlagConditions(config, ctx);
9277
9332
  refineHubAsCodeMonitorConfig(config, ctx);
9278
9333
  refineHubAsCodeDecisionsModel(config, ctx);
9334
+ refineHubAsCodeMonitorRulesOutput(config, ctx);
9279
9335
  });
9280
9336
  ciPullHubIdParam = external_exports.object({
9281
9337
  hub_id: ciUuidSchema
@@ -11437,6 +11493,31 @@ function monitorRuleKeysOffTrigger2(monitorConfig) {
11437
11493
  const config = monitorConfig;
11438
11494
  return MONITOR_RULE_KEYS2.filter((key) => config[key] !== void 0);
11439
11495
  }
11496
+ function monitorRulesStructuredOutputMessage2(trigger) {
11497
+ const [article, consequence] = trigger === "assistant_reply" ? ["an", "every reply is delivered unjudged"] : ["a", "none of their actions runs"];
11498
+ return `${article} ${trigger} monitor with rules needs structured output: its rules read variables only a JSON answer carries, so on text output no rule can match and ${consequence}. Set response_format to json_schema (wayai push), or remove the rules.`;
11499
+ }
11500
+ function monitorRulesStructuredOutputRefusal2(agent) {
11501
+ const trigger = refusedRulesTrigger2(agent);
11502
+ return trigger === null ? null : monitorRulesStructuredOutputMessage2(trigger);
11503
+ }
11504
+ function refusedRulesTrigger2(agent) {
11505
+ if (agent.agent_role !== "monitor") return null;
11506
+ const config = decodedMonitorConfig2(agent.monitor_config);
11507
+ const trigger = resolveMonitorTrigger2(config);
11508
+ if (!MONITOR_RULE_TRIGGERS2.includes(trigger)) return null;
11509
+ const rules = config.rules;
11510
+ if (!Array.isArray(rules) || rules.length === 0) return null;
11511
+ return agent.response_format === "json_schema" ? null : trigger;
11512
+ }
11513
+ function decodedMonitorConfig2(value) {
11514
+ if (typeof value !== "string") return value;
11515
+ try {
11516
+ return JSON.parse(value);
11517
+ } catch {
11518
+ return null;
11519
+ }
11520
+ }
11440
11521
  function monitorRuleActionKinds2(trigger) {
11441
11522
  return trigger === "assistant_reply" ? MONITOR_ASSISTANT_REPLY_ACTION_KINDS2 : MONITOR_USER_MESSAGE_ACTION_KINDS2;
11442
11523
  }
@@ -11585,6 +11666,10 @@ function refineDecisionsModelBinding2(body, ctx) {
11585
11666
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agent_settings", "model"], message: refusal2.message });
11586
11667
  }
11587
11668
  }
11669
+ function refineMonitorRulesStructuredOutput2(body, ctx) {
11670
+ const refusal2 = monitorRulesStructuredOutputRefusal2(body);
11671
+ if (refusal2) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["response_format"], message: refusal2 });
11672
+ }
11588
11673
  function visibleLength2(s) {
11589
11674
  return s.replace(INVISIBLE_NAME_CHARS2, "").length;
11590
11675
  }
@@ -12454,7 +12539,7 @@ function refineHubAsCodeDecisionsModel2(config, ctx) {
12454
12539
  const refusal2 = checkDecisionsModelBinding2({
12455
12540
  model: readAgentSettingsModel2(a.settings),
12456
12541
  agent_role: a.role,
12457
- response_format: responseFormat ? "json_schema" : "text",
12542
+ response_format: pushedResponseFormatColumn2(responseFormat),
12458
12543
  schema_json: responseFormat?.schema_json ?? null
12459
12544
  });
12460
12545
  if (refusal2) {
@@ -12466,6 +12551,25 @@ function refineHubAsCodeDecisionsModel2(config, ctx) {
12466
12551
  }
12467
12552
  });
12468
12553
  }
12554
+ function pushedResponseFormatColumn2(responseFormat) {
12555
+ return responseFormat ? "json_schema" : "text";
12556
+ }
12557
+ function refineHubAsCodeMonitorRulesOutput2(config, ctx) {
12558
+ const agents = config?.agents;
12559
+ if (!Array.isArray(agents)) return;
12560
+ agents.forEach((agent, agentIndex) => {
12561
+ const a = agent;
12562
+ if (!a) return;
12563
+ const refusal2 = monitorRulesStructuredOutputRefusal2({
12564
+ agent_role: a.role,
12565
+ response_format: pushedResponseFormatColumn2(a.response_format),
12566
+ monitor_config: a.monitor_config
12567
+ });
12568
+ if (refusal2) {
12569
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agents", agentIndex, "response_format"], message: refusal2 });
12570
+ }
12571
+ });
12572
+ }
12469
12573
  function identifyTokenType(token) {
12470
12574
  if (token.startsWith("way_")) return "way_token";
12471
12575
  if (token.startsWith("eyJ")) return "jwt";
@@ -14142,7 +14246,10 @@ var init_dist = __esm({
14142
14246
  previous_conversations_count: previousConversationsCountField2,
14143
14247
  monitor_config: monitorConfigField2,
14144
14248
  flag_conditions: flagConditionsField2
14145
- }).passthrough().superRefine(refineDecisionsModelBinding2);
14249
+ }).passthrough().superRefine((body, ctx) => {
14250
+ refineDecisionsModelBinding2(body, ctx);
14251
+ refineMonitorRulesStructuredOutput2(body, ctx);
14252
+ });
14146
14253
  updateAgentBody2 = external_exports.object({
14147
14254
  additional_context_template: external_exports.string().max(2e4).optional(),
14148
14255
  summarization_threshold_tokens: summarizationThresholdField2,
@@ -15650,8 +15757,12 @@ var init_dist = __esm({
15650
15757
  name: clientOptionalString2
15651
15758
  }).optional(),
15652
15759
  message: apiChannelMessage2,
15653
- // Idempotency key: maps to channel_message_sid; a retried POST echoes the original
15654
- // message_id and skips billing / turn dispatch (dedupe in ConversationDO.storeMessage).
15760
+ // Idempotency key: maps to channel_message_sid, deduped within one conversation
15761
+ // (ConversationDO.storeMessage). A retried POST resolves to the same conversation (a chat
15762
+ // hub's user, the external_ref, the conversation_id, or on a task hub with neither, this key
15763
+ // scoped by the sender), so it echoes the original message_id and skips billing / turn
15764
+ // dispatch. A failed store answers a retryable 503 only when this key is set; without it a
15765
+ // retry could store the message twice, so it answers 400.
15655
15766
  client_message_id: external_exports.string().min(1).max(256).optional()
15656
15767
  }).refine((b) => !(b.external_ref && b.conversation_id), {
15657
15768
  message: "provide external_ref or conversation_id, not both",
@@ -16428,6 +16539,7 @@ var init_dist = __esm({
16428
16539
  refineHubAsCodeFlagConditions2(config, ctx);
16429
16540
  refineHubAsCodeMonitorConfig2(config, ctx);
16430
16541
  refineHubAsCodeDecisionsModel2(config, ctx);
16542
+ refineHubAsCodeMonitorRulesOutput2(config, ctx);
16431
16543
  });
16432
16544
  ciPullHubIdParam2 = external_exports.object({
16433
16545
  hub_id: ciUuidSchema2
@@ -17528,11 +17640,33 @@ function ensureRealSubdirNoSymlink(root, target, create) {
17528
17640
  }
17529
17641
  return true;
17530
17642
  }
17643
+ function isUnder(parent, child) {
17644
+ const rel = path.relative(parent, child);
17645
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
17646
+ }
17647
+ function isSymlink(p) {
17648
+ try {
17649
+ return fs.lstatSync(p).isSymbolicLink();
17650
+ } catch {
17651
+ return false;
17652
+ }
17653
+ }
17654
+ function workspaceRefusal(message) {
17655
+ const err = expected(message);
17656
+ err.isWorkspaceRefusal = true;
17657
+ return err;
17658
+ }
17659
+ function isWorkspaceRefusal(err) {
17660
+ return err?.isWorkspaceRefusal === true;
17661
+ }
17662
+ function symlinkRefusal(rel, kind) {
17663
+ return workspaceRefusal(
17664
+ `${rel} is a symlink, is reached through one, or is not a ${kind}. The CLI never reads or writes workspace config through a symlink \u2014 make it a real ${kind}.`
17665
+ );
17666
+ }
17531
17667
  function requireRealSubdirNoSymlink(root, target, create) {
17532
17668
  if (ensureRealSubdirNoSymlink(root, target, create)) return;
17533
- throw expected(
17534
- `${path.relative(path.dirname(root), target)} is reached through a symlink (or a file is in the way). The CLI never reads or writes workspace config through a symlink \u2014 make it a real directory.`
17535
- );
17669
+ throw symlinkRefusal(path.relative(path.dirname(root), target), "directory");
17536
17670
  }
17537
17671
  function requireRealHubFolder(hubFolder, create) {
17538
17672
  if (!create) {
@@ -17553,11 +17687,7 @@ function requireRealHubFolder(hubFolder, create) {
17553
17687
  } catch {
17554
17688
  continue;
17555
17689
  }
17556
- if (!st.isFile()) {
17557
- throw expected(
17558
- `${path.join(path.basename(hubFolder), name)} is a symlink (or not a file). The CLI never reads or writes workspace config through a symlink \u2014 make it a real file.`
17559
- );
17560
- }
17690
+ if (!st.isFile()) throw symlinkRefusal(path.join(path.basename(hubFolder), name), "file");
17561
17691
  }
17562
17692
  }
17563
17693
  function readRealFileOrThrow(root, abs) {
@@ -17568,11 +17698,7 @@ function readRealFileOrThrow(root, abs) {
17568
17698
  return null;
17569
17699
  }
17570
17700
  const bytes = st.isFile() ? readFileNoFollow(root, abs) : null;
17571
- if (!bytes) {
17572
- throw expected(
17573
- `${path.relative(path.dirname(root), abs)} is a symlink (or reached through one). The CLI never reads or writes workspace config through a symlink \u2014 make it a real file.`
17574
- );
17575
- }
17701
+ if (!bytes) throw symlinkRefusal(path.relative(path.dirname(root), abs), "file");
17576
17702
  return bytes.toString("utf-8");
17577
17703
  }
17578
17704
  function readFileNoFollow(root, abs) {
@@ -17767,10 +17893,9 @@ function resolveWorkspaceDir() {
17767
17893
  return resolveLayout(gitRoot).hubsDir;
17768
17894
  }
17769
17895
  function readHubYaml(yamlPath) {
17770
- if (!fs3.existsSync(yamlPath)) return null;
17896
+ if (!fs3.existsSync(yamlPath) || isSymlink(yamlPath)) return null;
17771
17897
  try {
17772
- const content = fs3.readFileSync(yamlPath, "utf-8");
17773
- const config = yaml.load(content);
17898
+ const config = yaml.load(fs3.readFileSync(yamlPath, "utf-8"));
17774
17899
  if (!config?.hub_id || !config.hub_environment) return null;
17775
17900
  return { hubId: config.hub_id, hubEnvironment: config.hub_environment };
17776
17901
  } catch {
@@ -17796,10 +17921,9 @@ function filterPreviewHubs(hubs) {
17796
17921
  return hubs.filter(isPreviewHub);
17797
17922
  }
17798
17923
  function readNewHubYaml(yamlPath) {
17799
- if (!fs3.existsSync(yamlPath)) return null;
17924
+ if (!fs3.existsSync(yamlPath) || isSymlink(yamlPath)) return null;
17800
17925
  try {
17801
- const content = fs3.readFileSync(yamlPath, "utf-8");
17802
- const config = yaml.load(content);
17926
+ const config = yaml.load(fs3.readFileSync(yamlPath, "utf-8"));
17803
17927
  if (config?.hub_id) return null;
17804
17928
  if (!config?.hub?.name) return null;
17805
17929
  return { hubName: config.hub.name, hubType: config.hub.hub_type };
@@ -17882,6 +18006,7 @@ function getChangedHubs(workspaceDir) {
17882
18006
  }
17883
18007
  const hubs = [];
17884
18008
  for (const hubFolder of hubFolders) {
18009
+ if (!isRealDirectory(hubFolder)) continue;
17885
18010
  const meta = readHubYaml(path3.join(hubFolder, "hub.yaml")) || readHubYaml(path3.join(hubFolder, "wayai.yaml"));
17886
18011
  if (meta) {
17887
18012
  hubs.push({ hubFolder, hubId: meta.hubId, hubEnvironment: meta.hubEnvironment });
@@ -17941,30 +18066,28 @@ function autoRenameHubFolder(currentFolder, hubName, hubEnvironment, hubId, prev
17941
18066
  function findEnclosingHubFolder(workspaceDir) {
17942
18067
  let current = process.cwd();
17943
18068
  const stop = path3.resolve(workspaceDir);
17944
- while (true) {
18069
+ while (isUnder(stop, current)) {
17945
18070
  const meta = readHubYaml(path3.join(current, "hub.yaml")) || readHubYaml(path3.join(current, "wayai.yaml"));
17946
18071
  if (meta) {
17947
18072
  return { hubFolder: current, hubId: meta.hubId, hubEnvironment: meta.hubEnvironment };
17948
18073
  }
17949
18074
  if (current === stop) return null;
17950
- const parent = path3.dirname(current);
17951
- if (parent === current) return null;
17952
- current = parent;
18075
+ current = path3.dirname(current);
17953
18076
  }
18077
+ return null;
17954
18078
  }
17955
18079
  function findEnclosingNewHubFolder(workspaceDir) {
17956
18080
  let current = process.cwd();
17957
18081
  const stop = path3.resolve(workspaceDir);
17958
- while (true) {
18082
+ while (isUnder(stop, current)) {
17959
18083
  const meta = readNewHubYaml(path3.join(current, "hub.yaml")) || readNewHubYaml(path3.join(current, "wayai.yaml"));
17960
18084
  if (meta) {
17961
18085
  return { hubFolder: current, hubName: meta.hubName, hubType: meta.hubType };
17962
18086
  }
17963
18087
  if (current === stop) return null;
17964
- const parent = path3.dirname(current);
17965
- if (parent === current) return null;
17966
- current = parent;
18088
+ current = path3.dirname(current);
17967
18089
  }
18090
+ return null;
17968
18091
  }
17969
18092
  function resolvePushTarget(workspaceDir, existingHubs, newHubs, selector) {
17970
18093
  if (selector) {
@@ -18066,6 +18189,7 @@ var init_workspace = __esm({
18066
18189
  "use strict";
18067
18190
  init_utils();
18068
18191
  init_layout();
18192
+ init_fs_safety();
18069
18193
  }
18070
18194
  });
18071
18195
 
@@ -18080,6 +18204,11 @@ function rootConfigPath(gitRoot) {
18080
18204
  return path4.join(gitRoot, ROOT_CONFIG_FILE);
18081
18205
  }
18082
18206
  function loadYamlMapping(file) {
18207
+ for (const p of [file, path4.dirname(file)]) {
18208
+ if (isSymlink(p)) {
18209
+ return { kind: "malformed", reason: `${path4.basename(p)} is a symlink \u2014 make it a real ${p === file ? "file" : "directory"}` };
18210
+ }
18211
+ }
18083
18212
  let raw;
18084
18213
  try {
18085
18214
  raw = fs4.readFileSync(file, "utf-8");
@@ -18107,6 +18236,7 @@ var init_workspace_manifest = __esm({
18107
18236
  "src/lib/workspace-manifest.ts"() {
18108
18237
  "use strict";
18109
18238
  init_layout();
18239
+ init_fs_safety();
18110
18240
  WORKSPACE_MANIFEST_FILE = "wayai.yaml";
18111
18241
  WORKSPACE_MANIFEST_LABEL = `${WAYAI_LAYOUT.wsDir}/${WORKSPACE_MANIFEST_FILE}`;
18112
18242
  ROOT_CONFIG_FILE = ".wayai.yaml";
@@ -18903,7 +19033,7 @@ var init_registry = __esm({
18903
19033
 
18904
19034
  // src/lib/version-cache.ts
18905
19035
  import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
18906
- import { dirname as dirname5, join as join7 } from "path";
19036
+ import { dirname as dirname6, join as join7 } from "path";
18907
19037
  import { homedir as homedir2 } from "os";
18908
19038
  function getVersionCachePath(filename = CLI_CACHE_FILE) {
18909
19039
  return join7(homedir2(), ".wayai", filename);
@@ -18933,7 +19063,7 @@ function isVersionCacheStale(filename = CLI_CACHE_FILE, maxAgeMs = MAX_AGE_24H_M
18933
19063
  }
18934
19064
  function writeVersionCache(filename, cache) {
18935
19065
  const path35 = getVersionCachePath(filename);
18936
- const dir = dirname5(path35);
19066
+ const dir = dirname6(path35);
18937
19067
  if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
18938
19068
  writeFileSync2(path35, JSON.stringify(cache));
18939
19069
  }
@@ -20583,7 +20713,9 @@ function scanResourceFiles(dir, prefix = "") {
20583
20713
  const entries = fs9.readdirSync(dir, { withFileTypes: true });
20584
20714
  for (const entry of entries) {
20585
20715
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
20586
- if (entry.isDirectory()) {
20716
+ if (entry.isSymbolicLink()) {
20717
+ throw symlinkRefusal(relPath, "file or folder");
20718
+ } else if (entry.isDirectory()) {
20587
20719
  files.push(...scanResourceFiles(path11.join(dir, entry.name), relPath));
20588
20720
  } else if (entry.isFile()) {
20589
20721
  const fullPath = path11.join(dir, entry.name);
@@ -20963,6 +21095,18 @@ var init_eval_attachments = __esm({
20963
21095
  import * as fs11 from "fs";
20964
21096
  import * as path13 from "path";
20965
21097
  import * as yaml6 from "js-yaml";
21098
+ function refuseConsumedLink(hubFolder, dir, entry) {
21099
+ if (!entry.isSymbolicLink()) return;
21100
+ const abs = path13.join(dir, entry.name);
21101
+ let toDir = false;
21102
+ try {
21103
+ toDir = fs11.statSync(abs).isDirectory();
21104
+ } catch {
21105
+ }
21106
+ if (toDir || entry.name.endsWith(".yaml")) {
21107
+ throw symlinkRefusal(path13.relative(hubFolder, abs), "file or folder");
21108
+ }
21109
+ }
20966
21110
  function parseHubFolder(hubFolder, opts) {
20967
21111
  requireRealHubFolder(hubFolder, false);
20968
21112
  const yamlPath = resolveHubYamlPath(hubFolder);
@@ -20997,8 +21141,14 @@ function parseHubFolder(hubFolder, opts) {
20997
21141
  if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
20998
21142
  const instrValue = resolved.instructions;
20999
21143
  const instructionsPath = instrValue.startsWith("agents/") ? path13.join(hubFolder, instrValue) : path13.join(hubFolder, "agents", instrValue);
21000
- if (fs11.existsSync(instructionsPath)) {
21001
- resolved.instructions = fs11.readFileSync(instructionsPath, "utf-8");
21144
+ if (!isUnder(path13.join(hubFolder, "agents"), instructionsPath)) {
21145
+ throw workspaceRefusal(
21146
+ `Agent instructions path "${instrValue}" (agent "${agent.name}") must stay inside agents/.`
21147
+ );
21148
+ }
21149
+ const instructions = readRealFileOrThrow(hubFolder, instructionsPath);
21150
+ if (instructions !== null) {
21151
+ resolved.instructions = instructions;
21002
21152
  } else {
21003
21153
  throw expected(
21004
21154
  `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
@@ -21006,9 +21156,8 @@ function parseHubFolder(hubFolder, opts) {
21006
21156
  }
21007
21157
  } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
21008
21158
  const conventionPath = path13.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
21009
- if (fs11.existsSync(conventionPath)) {
21010
- resolved.instructions = fs11.readFileSync(conventionPath, "utf-8");
21011
- }
21159
+ const instructions = readRealFileOrThrow(hubFolder, conventionPath);
21160
+ if (instructions !== null) resolved.instructions = instructions;
21012
21161
  }
21013
21162
  return resolved;
21014
21163
  });
@@ -21067,6 +21216,7 @@ function scanEvalYamlFiles(hubFolder, bytesByHash) {
21067
21216
  const seen = /* @__PURE__ */ new Set();
21068
21217
  const topEntries = fs11.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21069
21218
  for (const entry of topEntries) {
21219
+ refuseConsumedLink(hubFolder, evalsDir, entry);
21070
21220
  if (entry.isFile() && entry.name.endsWith(".yaml")) {
21071
21221
  collectEval(evals, seen, path13.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
21072
21222
  } else if (entry.isDirectory()) {
@@ -21074,6 +21224,7 @@ function scanEvalYamlFiles(hubFolder, bytesByHash) {
21074
21224
  const setDir = path13.join(evalsDir, setName);
21075
21225
  const setEntries = fs11.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21076
21226
  for (const sub of setEntries) {
21227
+ refuseConsumedLink(hubFolder, setDir, sub);
21077
21228
  if (sub.isDirectory()) {
21078
21229
  throw expected(
21079
21230
  `Nested scenario sets are not supported: ${path13.relative(hubFolder, path13.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
@@ -21209,6 +21360,7 @@ function scanJourneyYamlFiles(hubFolder, bytesByHash) {
21209
21360
  const seen = /* @__PURE__ */ new Set();
21210
21361
  const entries = fs11.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21211
21362
  for (const entry of entries) {
21363
+ refuseConsumedLink(hubFolder, journeysDir, entry);
21212
21364
  if (entry.isDirectory()) {
21213
21365
  throw expected(
21214
21366
  `Subfolders are not supported under journeys/: ${path13.relative(hubFolder, path13.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
@@ -21290,7 +21442,7 @@ function scanAgentYamlFiles(hubFolder) {
21290
21442
  const agents = [];
21291
21443
  for (const file of yamlFiles) {
21292
21444
  const filePath = path13.join(agentsDir, file);
21293
- const content = fs11.readFileSync(filePath, "utf-8");
21445
+ const content = readRealFileOrThrow(hubFolder, filePath) ?? "";
21294
21446
  let agent;
21295
21447
  try {
21296
21448
  agent = yaml6.load(content);
@@ -21325,6 +21477,7 @@ function parseResources(hubFolder, configResources) {
21325
21477
  if (res.skill_name) resource.skill_name = res.skill_name;
21326
21478
  const resSlug = slugify(resource.name);
21327
21479
  const resDir = path13.join(resourcesDir, resSlug);
21480
+ requireRealSubdirNoSymlink(hubFolder, resDir, false);
21328
21481
  if (fs11.existsSync(resDir)) {
21329
21482
  const files = scanResourceFiles(resDir, "");
21330
21483
  if (files.length > 0) {
@@ -21842,10 +21995,6 @@ function isProductionFolder(meta) {
21842
21995
  function filterEditableBases(bases) {
21843
21996
  return bases.filter((b) => !isProductionFolder(b.meta));
21844
21997
  }
21845
- function isUnder(parent, child) {
21846
- const rel = path17.relative(parent, child);
21847
- return rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel);
21848
- }
21849
21998
  function findEnclosingBaseFolder(basesDir, cwd) {
21850
21999
  let dir = path17.resolve(cwd);
21851
22000
  const stop = path17.resolve(basesDir);
@@ -21923,6 +22072,7 @@ var init_base_workspace = __esm({
21923
22072
  init_expected();
21924
22073
  init_base_id();
21925
22074
  init_fs_safety();
22075
+ init_fs_safety();
21926
22076
  BASE_META_FILE = "base.yaml";
21927
22077
  }
21928
22078
  });
@@ -23122,8 +23272,8 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23122
23272
  if (fs16.existsSync(agentsDir)) {
23123
23273
  const yamlFiles = fs16.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
23124
23274
  for (const file of yamlFiles) {
23275
+ const content = readRealFileOrThrow(hubFolder, path22.join(agentsDir, file)) ?? "";
23125
23276
  try {
23126
- const content = fs16.readFileSync(path22.join(agentsDir, file), "utf-8");
23127
23277
  const agent = yaml11.load(content);
23128
23278
  if (agent?.id && agent.name) {
23129
23279
  agentsWithIds.push({ id: agent.id, name: agent.name });
@@ -23568,7 +23718,8 @@ async function pullCommand(args2) {
23568
23718
  }
23569
23719
  action = "apply";
23570
23720
  }
23571
- } catch {
23721
+ } catch (err) {
23722
+ if (isWorkspaceRefusal(err)) throw err;
23572
23723
  action = "overwrite";
23573
23724
  }
23574
23725
  if (action === "overwrite") console.log("Writing hub configuration...");
@@ -23643,6 +23794,7 @@ var init_pull = __esm({
23643
23794
  init_utils();
23644
23795
  init_resource_files();
23645
23796
  init_hub_materializer();
23797
+ init_fs_safety();
23646
23798
  init_push();
23647
23799
  init_workspace();
23648
23800
  init_layout();
@@ -28230,6 +28382,7 @@ function parseOrgResources(orgDir) {
28230
28382
  if (Array.isArray(res.tags)) resource.tags = res.tags;
28231
28383
  if (Array.isArray(res.folders)) resource.folders = res.folders;
28232
28384
  const resDir = path32.join(resourcesDir, slugify(resource.name));
28385
+ requireRealSubdirNoSymlink(orgDir, resDir, false);
28233
28386
  if (fs23.existsSync(resDir)) {
28234
28387
  const files = scanResourceFiles(resDir, "");
28235
28388
  if (files.length > 0) resource.files = files;
@@ -30617,7 +30770,7 @@ var init_actions = __esm({
30617
30770
 
30618
30771
  // src/data/commands/attachments.ts
30619
30772
  import { Command as Command2 } from "commander";
30620
- import { readFileSync as readFileSync18 } from "fs";
30773
+ import { readFileSync as readFileSync17 } from "fs";
30621
30774
  function findAttachmentByFilename(attachments, filename) {
30622
30775
  return attachments.find((a) => a.key.endsWith(`/${filename}`)) ?? null;
30623
30776
  }
@@ -30658,7 +30811,7 @@ function buildAttachmentsCommand() {
30658
30811
  printOutput(data, outputFormat(this));
30659
30812
  return;
30660
30813
  }
30661
- const body = readFileSync18(opts.file);
30814
+ const body = readFileSync17(opts.file);
30662
30815
  await client.upload(uploadPathFrom(data?.upload_url), body, opts.contentType);
30663
30816
  printOutput({ ...data, uploaded: true }, outputFormat(this));
30664
30817
  });
@@ -31138,8 +31291,8 @@ var init_providers = __esm({
31138
31291
 
31139
31292
  // src/data/commands/report.ts
31140
31293
  import { Command as Command6 } from "commander";
31141
- import { readFileSync as readFileSync19 } from "fs";
31142
- import { dirname as dirname12, join as join30 } from "path";
31294
+ import { readFileSync as readFileSync18 } from "fs";
31295
+ import { dirname as dirname13, join as join30 } from "path";
31143
31296
  import { fileURLToPath as fileURLToPath2 } from "url";
31144
31297
  function resolveCliVersion() {
31145
31298
  for (const candidate of [
@@ -31147,7 +31300,7 @@ function resolveCliVersion() {
31147
31300
  join30(here, "..", "..", "..", "package.json")
31148
31301
  ]) {
31149
31302
  try {
31150
- const version = JSON.parse(readFileSync19(candidate, "utf-8")).version;
31303
+ const version = JSON.parse(readFileSync18(candidate, "utf-8")).version;
31151
31304
  if (typeof version === "string" && version) return version;
31152
31305
  } catch {
31153
31306
  }
@@ -31343,13 +31496,13 @@ var init_report2 = __esm({
31343
31496
  init_terminal_output();
31344
31497
  init_workspace();
31345
31498
  init_skill_version();
31346
- here = dirname12(fileURLToPath2(import.meta.url));
31499
+ here = dirname13(fileURLToPath2(import.meta.url));
31347
31500
  }
31348
31501
  });
31349
31502
 
31350
31503
  // src/data/commands/credentials.ts
31351
31504
  import { Command as Command7 } from "commander";
31352
- import { readFileSync as readFileSync20 } from "fs";
31505
+ import { readFileSync as readFileSync19 } from "fs";
31353
31506
  function withValueSourceOptions(cmd, what) {
31354
31507
  return cmd.option(
31355
31508
  "--file <path>",
@@ -31362,7 +31515,7 @@ async function resolveValue(opts, label) {
31362
31515
  throw expected("--file cannot be combined with --value-stdin or --value-prompt \u2014 pass one.");
31363
31516
  }
31364
31517
  try {
31365
- return readFileSync20(opts.file).toString("base64");
31518
+ return readFileSync19(opts.file).toString("base64");
31366
31519
  } catch (e) {
31367
31520
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
31368
31521
  }
@@ -31511,7 +31664,7 @@ var init_credentials = __esm({
31511
31664
 
31512
31665
  // src/data/commands/sql.ts
31513
31666
  import { Command as Command8 } from "commander";
31514
- import { readFileSync as readFileSync21 } from "fs";
31667
+ import { readFileSync as readFileSync20 } from "fs";
31515
31668
  function buildBasesSqlCommand() {
31516
31669
  return withBaseOption(new Command8("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
31517
31670
  "--param <kv...>",
@@ -31521,7 +31674,7 @@ function buildBasesSqlCommand() {
31521
31674
  let query;
31522
31675
  if (opts.file) {
31523
31676
  try {
31524
- query = readFileSync21(opts.file, "utf-8").trim();
31677
+ query = readFileSync20(opts.file, "utf-8").trim();
31525
31678
  } catch (e) {
31526
31679
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
31527
31680
  }
@@ -32176,8 +32329,8 @@ var init_file_types = __esm({
32176
32329
 
32177
32330
  // src/data/commands/files.ts
32178
32331
  import { Command as Command12 } from "commander";
32179
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync10 } from "fs";
32180
- import { basename as basename19 } from "path";
32332
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
32333
+ import { basename as basename20 } from "path";
32181
32334
  function renderFileDiff(fileType, filePath, from, to, d) {
32182
32335
  console.log(sanitizeTerminalText(`${fileType}/${filePath}: v${from} \u2192 v${to}`));
32183
32336
  const md = d.metadata_delta;
@@ -32209,7 +32362,7 @@ function renderFileDiff(fileType, filePath, from, to, d) {
32209
32362
  }
32210
32363
  function downloadTarget(remotePath, to) {
32211
32364
  if (to) return to;
32212
- const derived = basename19(remotePath);
32365
+ const derived = basename20(remotePath);
32213
32366
  if (derived === "" || derived === "." || derived === "..") {
32214
32367
  throw expected(
32215
32368
  `Cannot derive a local filename from "${remotePath}" \u2014 pass --to <local> to name it.`
@@ -32225,7 +32378,7 @@ function buildFilesCommand() {
32225
32378
  "Upload a local file to a path (e.g. wayai files put reports q3/summary.pdf --file ./summary.pdf)"
32226
32379
  ).requiredOption("--file <local>", "Local file to upload").option("--content-type <type>", "MIME type", "application/octet-stream").action(async function(fileType, filePath, opts) {
32227
32380
  const base = pathSegment(requireBase(this), "--base");
32228
- const body = readFileSync22(opts.file);
32381
+ const body = readFileSync21(opts.file);
32229
32382
  const client = await createDataClient();
32230
32383
  printOutput(
32231
32384
  await client.upload(
@@ -33274,9 +33427,9 @@ init_errors2();
33274
33427
  init_mask_secrets();
33275
33428
  init_utils();
33276
33429
  init_registry();
33277
- import { readFileSync as readFileSync23 } from "fs";
33430
+ import { readFileSync as readFileSync22 } from "fs";
33278
33431
  import { fileURLToPath as fileURLToPath3 } from "url";
33279
- import { dirname as dirname13, join as join31 } from "path";
33432
+ import { dirname as dirname14, join as join31 } from "path";
33280
33433
 
33281
33434
  // src/lib/version-refresh.ts
33282
33435
  init_version_cache();
@@ -33431,8 +33584,8 @@ Run \`wayai admin skill install\` to update.`);
33431
33584
  }
33432
33585
 
33433
33586
  // src/index.ts
33434
- var __dirname = dirname13(fileURLToPath3(import.meta.url));
33435
- var pkg = JSON.parse(readFileSync23(join31(__dirname, "..", "package.json"), "utf-8"));
33587
+ var __dirname = dirname14(fileURLToPath3(import.meta.url));
33588
+ var pkg = JSON.parse(readFileSync22(join31(__dirname, "..", "package.json"), "utf-8"));
33436
33589
  var [, , command, ...args] = process.argv;
33437
33590
  var isBackgroundRefresh = command === REFRESH_COMMAND;
33438
33591
  if (!isBackgroundRefresh) initSentry(command, pkg.version);