@wayai/cli 0.3.162 → 0.3.164

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
@@ -4624,6 +4624,327 @@ function normalizeAuthType(raw) {
4624
4624
  if (VALID_AUTH_TYPES.has(raw)) return raw;
4625
4625
  return LEGACY_AUTH_TYPE_MAP[raw] ?? raw;
4626
4626
  }
4627
+ function schemaToQuestions(schema) {
4628
+ const classified = classifySchema(schema);
4629
+ if (classified.refusals.length > 0) return { ok: false, refusals: classified.refusals };
4630
+ const questions = {};
4631
+ for (const [field, primitive] of classified.fields) {
4632
+ questions[field] = toQuestion(primitive);
4633
+ }
4634
+ return { ok: true, questions };
4635
+ }
4636
+ function classifySchema(schema) {
4637
+ const fields = /* @__PURE__ */ new Map();
4638
+ if (!isRecord(schema) || schema.type !== "object") {
4639
+ return { fields, refusals: [{ field: null, reason: "schema_not_object" }] };
4640
+ }
4641
+ const properties = schema.properties;
4642
+ if (!isRecord(properties) || Object.keys(properties).length === 0) {
4643
+ return { fields, refusals: [{ field: null, reason: "schema_has_no_properties" }] };
4644
+ }
4645
+ const refusals = [];
4646
+ for (const [field, property] of Object.entries(properties)) {
4647
+ const primitive = classifyField(field, property);
4648
+ if ("reason" in primitive) {
4649
+ refusals.push({ field, reason: primitive.reason });
4650
+ continue;
4651
+ }
4652
+ fields.set(field, primitive);
4653
+ }
4654
+ return refusals.length > 0 ? { fields: /* @__PURE__ */ new Map(), refusals } : { fields, refusals };
4655
+ }
4656
+ function classifyField(field, property) {
4657
+ if (!isRecord(property)) return { reason: "unsupported_type" };
4658
+ const instructions = describeField(field, property);
4659
+ if (property.enum !== void 0) {
4660
+ const declared = property.enum;
4661
+ if (!Array.isArray(declared) || declared.some((option) => typeof option !== "string")) {
4662
+ return { reason: "unsupported_type" };
4663
+ }
4664
+ const options = [...new Set(declared)];
4665
+ if (options.length < 2) return { reason: "too_few_options" };
4666
+ return { kind: "choice", instructions, options };
4667
+ }
4668
+ if (property.type === "boolean") return { kind: "noul", instructions };
4669
+ if (property.type === "integer") {
4670
+ const minimum = property.minimum;
4671
+ const maximum = property.maximum;
4672
+ if (!Number.isInteger(minimum) || !Number.isInteger(maximum)) {
4673
+ return { reason: "integer_not_bounded" };
4674
+ }
4675
+ const levels = maximum - minimum + 1;
4676
+ if (levels < DECISION_SCORE_MIN_LEVELS) return { reason: "too_few_options" };
4677
+ if (levels > DECISION_SCORE_MAX_LEVELS) return { reason: "integer_range_too_large" };
4678
+ return { kind: "score", instructions, minimum, levels };
4679
+ }
4680
+ return { reason: "unsupported_type" };
4681
+ }
4682
+ function toQuestion(primitive) {
4683
+ switch (primitive.kind) {
4684
+ case "choice":
4685
+ return {
4686
+ type: "choice",
4687
+ instructions: primitive.instructions,
4688
+ // A JSON Schema enum carries no per-option text, so each option stands as
4689
+ // its own description. The distinguishing detail an option needs belongs
4690
+ // in the field's `description`, which is the instructions.
4691
+ criteria: Object.fromEntries(primitive.options.map((option) => [option, option]))
4692
+ };
4693
+ case "noul":
4694
+ return { type: "noul", instructions: primitive.instructions };
4695
+ case "score":
4696
+ return {
4697
+ type: "score",
4698
+ instructions: primitive.instructions,
4699
+ criteria: scoreLevels(primitive.minimum, primitive.levels)
4700
+ };
4701
+ }
4702
+ }
4703
+ function describeField(field, property) {
4704
+ const description = isRecord(property) ? property.description : void 0;
4705
+ return typeof description === "string" && description.trim() !== "" ? description : field;
4706
+ }
4707
+ function scoreLevels(minimum, levels) {
4708
+ return Array.from({ length: levels }, (_, index) => String(minimum + index));
4709
+ }
4710
+ function isRecord(value) {
4711
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4712
+ }
4713
+ function isDecisionsModel(modelId) {
4714
+ if (typeof modelId !== "string") return false;
4715
+ const normalized = modelId.trim().toLowerCase();
4716
+ return DECISIONS_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix));
4717
+ }
4718
+ function checkDecisionsModelBinding(agent) {
4719
+ if (!isDecisionsModel(agent.model)) return null;
4720
+ const model = String(agent.model);
4721
+ const role = agent.agent_role;
4722
+ if (!DECISION_CAPABLE_AGENT_ROLES.includes(role)) {
4723
+ return refuse(
4724
+ "role_produces_text",
4725
+ `agent_role "${String(role)}" produces text, which the decisions model "${model}" does not return. A decisions model binds to: ${DECISION_CAPABLE_AGENT_ROLES.join(", ")}.`
4726
+ );
4727
+ }
4728
+ if (agent.response_format !== "json_schema") {
4729
+ return refuse(
4730
+ "response_format_not_json_schema",
4731
+ `response_format must be "json_schema" to bind the decisions model "${model}".`
4732
+ );
4733
+ }
4734
+ if (agent.schema_json === null || agent.schema_json === void 0 || agent.schema_json === "") {
4735
+ return refuse(
4736
+ "schema_missing",
4737
+ `schema_json is required to bind the decisions model "${model}": a decisions model answers questions, and the schema is where they come from.`
4738
+ );
4739
+ }
4740
+ let schema = agent.schema_json;
4741
+ if (typeof schema === "string") {
4742
+ try {
4743
+ schema = JSON.parse(schema);
4744
+ } catch {
4745
+ return refuse(
4746
+ "schema_unreadable",
4747
+ `schema_json is not valid JSON, so the decisions model "${model}" cannot be bound.`
4748
+ );
4749
+ }
4750
+ }
4751
+ const mapped = schemaToQuestions(schema);
4752
+ if (mapped.ok) return null;
4753
+ const detail = mapped.refusals.map((refusal2) => `${refusal2.field ?? "(schema)"}: ${refusal2.reason}`).join("; ");
4754
+ return {
4755
+ reason: "schema_not_closed_set",
4756
+ schema_refusals: mapped.refusals,
4757
+ message: `schema_json is not closed-set, so the decisions model "${model}" cannot answer it \u2014 ${detail}. A decisions model answers enum, boolean and bounded-integer fields only.`
4758
+ };
4759
+ }
4760
+ function refuse(reason, message) {
4761
+ return { reason, schema_refusals: [], message };
4762
+ }
4763
+ function readAgentSettingsModel(agentSettings) {
4764
+ let settings = agentSettings;
4765
+ if (typeof settings === "string") {
4766
+ try {
4767
+ settings = JSON.parse(settings);
4768
+ } catch {
4769
+ return null;
4770
+ }
4771
+ }
4772
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return null;
4773
+ const model = settings.model;
4774
+ return typeof model === "string" ? model : null;
4775
+ }
4776
+ function resolveMonitorTrigger(monitorConfig) {
4777
+ if (!monitorConfig || typeof monitorConfig !== "object") return "idle";
4778
+ const raw = monitorConfig.trigger;
4779
+ return MONITOR_TRIGGERS.includes(raw) ? raw : "idle";
4780
+ }
4781
+ function isFiringTrigger(trigger) {
4782
+ return trigger !== "manual";
4783
+ }
4784
+ function monitorConfigNeedsDelay(monitorConfig) {
4785
+ if (!monitorConfig || typeof monitorConfig !== "object") return false;
4786
+ return resolveMonitorTrigger(monitorConfig) === "idle" && monitorConfig.delay_seconds === void 0;
4787
+ }
4788
+ function monitorInputShapingKeysOnIdle(monitorConfig) {
4789
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
4790
+ if (resolveMonitorTrigger(monitorConfig) !== "idle") return [];
4791
+ const config = monitorConfig;
4792
+ return MONITOR_INPUT_SHAPING_KEYS.filter((key) => config[key] !== void 0);
4793
+ }
4794
+ function monitorRuleKeysOffTrigger(monitorConfig) {
4795
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
4796
+ if (MONITOR_RULE_TRIGGERS.includes(resolveMonitorTrigger(monitorConfig))) return [];
4797
+ const config = monitorConfig;
4798
+ return MONITOR_RULE_KEYS.filter((key) => config[key] !== void 0);
4799
+ }
4800
+ function monitorRuleActionKinds(trigger) {
4801
+ return trigger === "assistant_reply" ? MONITOR_ASSISTANT_REPLY_ACTION_KINDS : MONITOR_USER_MESSAGE_ACTION_KINDS;
4802
+ }
4803
+ function monitorActionKindMessage(kind, trigger) {
4804
+ const allowed = monitorRuleActionKinds(trigger).join(", ");
4805
+ if (trigger === "assistant_reply") {
4806
+ return `an assistant_reply rule cannot select "${kind}". Use one of: ${allowed}.`;
4807
+ }
4808
+ return `a monitor rule cannot select "${kind}": it acts before the reply exists. Use one of: ${allowed}.`;
4809
+ }
4810
+ function monitorRuleReentryTrack(toolName) {
4811
+ return MONITOR_RULE_REENTRY_TRACKS.get(toolName);
4812
+ }
4813
+ function monitorRuleToolNotAllowedMessage(toolName, trigger) {
4814
+ if (trigger === "assistant_reply" && isReplyGateRefusedToolName(toolName)) {
4815
+ return `an assistant_reply rule cannot call "${toolName}": the reply already exists when this monitor runs, so there is no turn left for it to act on. Put it on a user_message monitor instead, which runs before the answering agent.`;
4816
+ }
4817
+ const callable = trigger === "assistant_reply" ? MONITOR_RULE_ALLOWED_NATIVE_TOOLS.filter((name) => !isReplyGateRefusedToolName(name)) : MONITOR_RULE_ALLOWED_NATIVE_TOOLS;
4818
+ return `a ${trigger} rule cannot call "${toolName}". Rules can call ${callable.join(", ")}, and this hub's own external HTTP and MCP tools. Assign anything else to the answering agent instead.`;
4819
+ }
4820
+ function isReplyGateRefusedToolName(toolName) {
4821
+ return monitorRuleReentryTrack(toolName) === "agent" || toolName === INSERT_NOTE_TOOL_NAME;
4822
+ }
4823
+ function isRefusedNativeToolName(toolName, trigger) {
4824
+ if (!NATIVE_TOOL_NAMES.has(toolName)) return false;
4825
+ if (!MONITOR_RULE_ALLOWED_NATIVE_TOOLS.includes(toolName)) return true;
4826
+ return trigger === "assistant_reply" && isReplyGateRefusedToolName(toolName);
4827
+ }
4828
+ function monitorRuleActions(monitorConfig) {
4829
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
4830
+ const config = monitorConfig;
4831
+ const found = [];
4832
+ const visit = (action, path35) => {
4833
+ if (!action || typeof action !== "object" || Array.isArray(action)) return;
4834
+ found.push({ action, path: path35 });
4835
+ };
4836
+ if (Array.isArray(config.rules)) {
4837
+ config.rules.forEach((rule, index) => {
4838
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return;
4839
+ visit(rule.action, ["rules", index, "action"]);
4840
+ });
4841
+ }
4842
+ visit(config.fallback, ["fallback"]);
4843
+ return found;
4844
+ }
4845
+ function collectMonitorRuleIssues(monitorConfig) {
4846
+ const offTrigger = monitorRuleKeysOffTrigger(monitorConfig).map((key) => ({ path: [key], message: MONITOR_RULE_TRIGGER_MESSAGE }));
4847
+ if (offTrigger.length > 0) return offTrigger;
4848
+ const trigger = resolveMonitorTrigger(monitorConfig);
4849
+ const kinds = monitorRuleActionKinds(trigger);
4850
+ const issues = [];
4851
+ for (const { action, path: path35 } of monitorRuleActions(monitorConfig)) {
4852
+ const kind = action.kind;
4853
+ if (typeof kind === "string" && !kinds.includes(kind)) {
4854
+ issues.push({ path: [...path35, "kind"], message: monitorActionKindMessage(kind, trigger) });
4855
+ }
4856
+ const toolName = action.tool_name;
4857
+ if (kind === "call_tool" && typeof toolName === "string" && isRefusedNativeToolName(toolName, trigger)) {
4858
+ issues.push({ path: [...path35, "tool_name"], message: monitorRuleToolNotAllowedMessage(toolName, trigger) });
4859
+ continue;
4860
+ }
4861
+ if (kind === "call_tool" && toolName === INSERT_NOTE_TOOL_NAME) {
4862
+ issues.push(...insertNoteArgumentIssues(action, path35));
4863
+ }
4864
+ if (kind === "call_tool" && toolName === RUN_MONITOR_TOOL_NAME) {
4865
+ const callee = readRunMonitorCallee(action);
4866
+ if (!callee.ok) {
4867
+ issues.push({
4868
+ path: [...path35, "args", "monitor_name"],
4869
+ message: runMonitorCalleeMessage(callee.reason)
4870
+ });
4871
+ }
4872
+ }
4873
+ }
4874
+ return issues;
4875
+ }
4876
+ function readInsertNoteTemplate(action) {
4877
+ const read = readConstStringArg(action, "template");
4878
+ if (!read.ok) {
4879
+ return {
4880
+ ok: false,
4881
+ reason: read.reason === "missing" ? "note_template_missing" : read.reason === "not_const" ? "note_template_not_const" : "note_template_empty"
4882
+ };
4883
+ }
4884
+ if (read.value.length > MONITOR_NOTE_TEMPLATE_MAX) {
4885
+ return { ok: false, reason: "note_template_too_long" };
4886
+ }
4887
+ return { ok: true, template: read.value };
4888
+ }
4889
+ function readConstStringArg(action, argName) {
4890
+ const args2 = action && typeof action === "object" ? action.args : void 0;
4891
+ const arg = args2 && typeof args2 === "object" ? args2[argName] : void 0;
4892
+ if (arg === void 0) return { ok: false, reason: "missing" };
4893
+ const constValue = arg && typeof arg === "object" && !Array.isArray(arg) ? arg.const : void 0;
4894
+ if (typeof constValue !== "string") return { ok: false, reason: "not_const" };
4895
+ if (constValue.trim() === "") return { ok: false, reason: "empty" };
4896
+ return { ok: true, value: constValue };
4897
+ }
4898
+ function readRunMonitorCallee(action) {
4899
+ const read = readConstStringArg(action, "monitor_name");
4900
+ if (read.ok) return { ok: true, name: read.value };
4901
+ return {
4902
+ ok: false,
4903
+ reason: read.reason === "missing" ? "callee_missing" : read.reason === "not_const" ? "callee_not_const" : "callee_empty"
4904
+ };
4905
+ }
4906
+ function runMonitorCalleeMessage(reason) {
4907
+ switch (reason) {
4908
+ case "callee_missing":
4909
+ return "run_monitor needs a monitor_name: the monitor to run.";
4910
+ case "callee_not_const":
4911
+ return "run_monitor's monitor_name must be a const you write \u2014 it chooses which monitor runs, so it cannot come from a variable.";
4912
+ case "callee_empty":
4913
+ return "run_monitor's monitor_name is empty \u2014 name the monitor to run.";
4914
+ }
4915
+ }
4916
+ function insertNoteTemplateMessage(reason) {
4917
+ switch (reason) {
4918
+ case "note_template_missing":
4919
+ return "insert_note needs a template: the note text to put in front of the answering agent.";
4920
+ case "note_template_not_const":
4921
+ return "insert_note's template must be a const string you write \u2014 the answering agent reads it as instructions, so it cannot come from a variable. Use {{variable_name}} inside it to substitute this monitor's variables.";
4922
+ case "note_template_empty":
4923
+ return "insert_note's template is empty \u2014 remove the rule, or give it something to say.";
4924
+ case "note_template_too_long":
4925
+ return `insert_note's template is longer than the ${MONITOR_NOTE_TEMPLATE_MAX}-character limit.`;
4926
+ }
4927
+ }
4928
+ function insertNoteArgumentIssues(action, path35) {
4929
+ const result = readInsertNoteTemplate(action);
4930
+ if (result.ok) return [];
4931
+ return [{
4932
+ path: [...path35, "args", "template"],
4933
+ message: insertNoteTemplateMessage(result.reason)
4934
+ }];
4935
+ }
4936
+ function refineDecisionsModelBinding(body, ctx) {
4937
+ const agent = body;
4938
+ const refusal2 = checkDecisionsModelBinding({
4939
+ model: readAgentSettingsModel(agent.agent_settings),
4940
+ agent_role: agent.agent_role,
4941
+ response_format: agent.response_format,
4942
+ schema_json: agent.schema_json
4943
+ });
4944
+ if (refusal2) {
4945
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agent_settings", "model"], message: refusal2.message });
4946
+ }
4947
+ }
4627
4948
  function visibleLength(s) {
4628
4949
  return s.replace(INVISIBLE_NAME_CHARS, "").length;
4629
4950
  }
@@ -4743,7 +5064,7 @@ function extractToolParameters(toolConfig) {
4743
5064
  if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return void 0;
4744
5065
  return cfg.parameters;
4745
5066
  }
4746
- function isRecord(v) {
5067
+ function isRecord2(v) {
4747
5068
  return typeof v === "object" && v !== null && !Array.isArray(v);
4748
5069
  }
4749
5070
  function isPrimitive(v) {
@@ -4765,7 +5086,7 @@ function validateSchema(schema, path35, errors, opts = {}) {
4765
5086
  });
4766
5087
  return;
4767
5088
  }
4768
- if (!isRecord(schema)) {
5089
+ if (!isRecord2(schema)) {
4769
5090
  errors.push({
4770
5091
  path: path35,
4771
5092
  message: `expected object, got ${schema === null ? "null" : typeof schema}`
@@ -4827,7 +5148,7 @@ function validateSchema(schema, path35, errors, opts = {}) {
4827
5148
  }
4828
5149
  }
4829
5150
  if ("properties" in schema) {
4830
- if (!isRecord(schema.properties)) {
5151
+ if (!isRecord2(schema.properties)) {
4831
5152
  errors.push({
4832
5153
  path: `${path35}.properties`,
4833
5154
  message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
@@ -4846,7 +5167,7 @@ function validateSchema(schema, path35, errors, opts = {}) {
4846
5167
  }
4847
5168
  }
4848
5169
  for (const key of SUBSCHEMA_OBJECT_KEYWORDS) {
4849
- if (key in schema && isRecord(schema[key])) {
5170
+ if (key in schema && isRecord2(schema[key])) {
4850
5171
  validateSchema(schema[key], `${path35}.${key}`, errors, { depth: depth + 1 });
4851
5172
  }
4852
5173
  }
@@ -4858,7 +5179,7 @@ function validateSchema(schema, path35, errors, opts = {}) {
4858
5179
  }
4859
5180
  for (const key of SUBSCHEMA_MAP_KEYWORDS) {
4860
5181
  const map = schema[key];
4861
- if (isRecord(map)) {
5182
+ if (isRecord2(map)) {
4862
5183
  for (const [name, sub] of Object.entries(map)) {
4863
5184
  validateSchema(sub, `${path35}.${key}.${name}`, errors, { depth: depth + 1 });
4864
5185
  }
@@ -4888,7 +5209,7 @@ function schemaTypeIncludes(schema, wanted) {
4888
5209
  }
4889
5210
  function validateToolInputSchema(schema) {
4890
5211
  const errors = [];
4891
- if (!isRecord(schema)) {
5212
+ if (!isRecord2(schema)) {
4892
5213
  errors.push({
4893
5214
  path: "",
4894
5215
  message: `expected object at root, got ${schema === null ? "null" : typeof schema}`
@@ -5369,7 +5690,120 @@ function refineHubAsCodeDelegation(config, ctx) {
5369
5690
  });
5370
5691
  });
5371
5692
  }
5372
- 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, previousConversationsCountField, 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, 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;
5693
+ function refineHubAsCodeFlagConditions(config, ctx) {
5694
+ const agents = config?.agents;
5695
+ if (!Array.isArray(agents)) return;
5696
+ const checkList = (list, basePath) => {
5697
+ if (!Array.isArray(list)) return;
5698
+ list.forEach((row, rowIndex) => {
5699
+ const operator = row?.operator;
5700
+ if (!FLAG_CONDITION_OPERATORS.includes(operator)) {
5701
+ ctx.addIssue({
5702
+ code: external_exports.ZodIssueCode.custom,
5703
+ path: [...basePath, rowIndex, "operator"],
5704
+ message: `operator must be one of: ${FLAG_CONDITION_OPERATORS.join(", ")}`
5705
+ });
5706
+ }
5707
+ });
5708
+ };
5709
+ agents.forEach((agent, agentIndex) => {
5710
+ const a = agent;
5711
+ checkList(a?.flag_conditions, ["agents", agentIndex, "flag_conditions"]);
5712
+ const monitorConfig = a?.monitor_config;
5713
+ if (monitorConfig && typeof monitorConfig === "object" && !Array.isArray(monitorConfig)) {
5714
+ const base = ["agents", agentIndex, "monitor_config"];
5715
+ checkList(
5716
+ monitorConfig.flag_conditions,
5717
+ [...base, "flag_conditions"]
5718
+ );
5719
+ const rules = monitorConfig.rules;
5720
+ if (Array.isArray(rules)) {
5721
+ rules.forEach((rule, ruleIndex) => {
5722
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return;
5723
+ checkList(rule.when, [...base, "rules", ruleIndex, "when"]);
5724
+ });
5725
+ }
5726
+ }
5727
+ });
5728
+ }
5729
+ function refineHubAsCodeMonitorConfig(config, ctx) {
5730
+ const agents = config?.agents;
5731
+ if (!Array.isArray(agents)) return;
5732
+ agents.forEach((agent, agentIndex) => {
5733
+ const monitorConfig = agent?.monitor_config;
5734
+ if (!monitorConfig || typeof monitorConfig !== "object" || Array.isArray(monitorConfig)) return;
5735
+ const basePath = ["agents", agentIndex, "monitor_config"];
5736
+ const checkKeyValues = (keys, schemas) => {
5737
+ for (const key of keys) {
5738
+ const value = monitorConfig[key];
5739
+ if (value === void 0) continue;
5740
+ const parsed = schemas[key].safeParse(value);
5741
+ if (!parsed.success) {
5742
+ ctx.addIssue({
5743
+ code: external_exports.ZodIssueCode.custom,
5744
+ path: [...basePath, key],
5745
+ message: parsed.error?.issues[0]?.message ?? `invalid ${key}`
5746
+ });
5747
+ }
5748
+ }
5749
+ };
5750
+ const trigger = monitorConfig.trigger;
5751
+ if (trigger !== void 0 && !MONITOR_TRIGGERS.includes(trigger)) {
5752
+ ctx.addIssue({
5753
+ code: external_exports.ZodIssueCode.custom,
5754
+ path: [...basePath, "trigger"],
5755
+ message: `trigger must be one of: ${MONITOR_TRIGGERS.join(", ")}`
5756
+ });
5757
+ return;
5758
+ }
5759
+ if (monitorConfigNeedsDelay(monitorConfig)) {
5760
+ ctx.addIssue({
5761
+ code: external_exports.ZodIssueCode.custom,
5762
+ path: [...basePath, "delay_seconds"],
5763
+ message: MONITOR_IDLE_DELAY_REQUIRED_MESSAGE
5764
+ });
5765
+ }
5766
+ for (const key of monitorInputShapingKeysOnIdle(monitorConfig)) {
5767
+ ctx.addIssue({
5768
+ code: external_exports.ZodIssueCode.custom,
5769
+ path: [...basePath, key],
5770
+ message: MONITOR_INPUT_SHAPING_IDLE_MESSAGE
5771
+ });
5772
+ }
5773
+ checkKeyValues(MONITOR_INPUT_SHAPING_KEYS, MONITOR_INPUT_SHAPING_SCHEMAS);
5774
+ checkKeyValues(MONITOR_RULE_KEYS, MONITOR_RULE_SCHEMAS);
5775
+ for (const issue of collectMonitorRuleIssues(monitorConfig)) {
5776
+ ctx.addIssue({
5777
+ code: external_exports.ZodIssueCode.custom,
5778
+ path: [...basePath, ...issue.path],
5779
+ message: issue.message
5780
+ });
5781
+ }
5782
+ });
5783
+ }
5784
+ function refineHubAsCodeDecisionsModel(config, ctx) {
5785
+ const agents = config?.agents;
5786
+ if (!Array.isArray(agents)) return;
5787
+ agents.forEach((agent, agentIndex) => {
5788
+ const a = agent;
5789
+ if (!a) return;
5790
+ const responseFormat = a.response_format;
5791
+ const refusal2 = checkDecisionsModelBinding({
5792
+ model: readAgentSettingsModel(a.settings),
5793
+ agent_role: a.role,
5794
+ response_format: responseFormat ? "json_schema" : "text",
5795
+ schema_json: responseFormat?.schema_json ?? null
5796
+ });
5797
+ if (refusal2) {
5798
+ ctx.addIssue({
5799
+ code: external_exports.ZodIssueCode.custom,
5800
+ path: ["agents", agentIndex, "settings", "model"],
5801
+ message: refusal2.message
5802
+ });
5803
+ }
5804
+ });
5805
+ }
5806
+ 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;
5373
5807
  var init_contracts = __esm({
5374
5808
  "../../packages/core/dist/contracts/index.js"() {
5375
5809
  "use strict";
@@ -5546,18 +5980,993 @@ var init_contracts = __esm({
5546
5980
  SUMMARIZATION_THRESHOLD_MIN = 1e3;
5547
5981
  SUMMARIZATION_THRESHOLD_MAX = 1e6;
5548
5982
  PREVIOUS_CONVERSATIONS_MAX = 20;
5983
+ FLAG_CONDITION_OPERATORS = ["=", "!=", ">=", "<=", ">", "<"];
5984
+ DECISION_SCORE_MIN_LEVELS = 2;
5985
+ DECISION_SCORE_MAX_LEVELS = 10;
5986
+ DECISIONS_MODEL_PREFIXES = ["typesafe/jev-", "jev-"];
5987
+ DECISION_CAPABLE_AGENT_ROLES = [
5988
+ "monitor",
5989
+ "conversation_evaluator",
5990
+ "message_evaluator"
5991
+ ];
5992
+ NATIVE_TOOL_SCHEMAS = {
5993
+ "close_conversation": {
5994
+ "tool_config": {
5995
+ "name": "close_conversation",
5996
+ "description": "Closes the current conversation and marks it as resolved",
5997
+ // Non-strict, mirroring the `update_kanban_status` sibling below: under strict
5998
+ // mode there are no optional fields, so `outcome` outside `required` makes the
5999
+ // provider reject the definition and `outcome` inside `required` forces the
6000
+ // model to emit it on every call. Requiredness is enforced SERVER-side instead
6001
+ // (ops `outcomeRequired`) — and it must stay out of `required` here, or
6002
+ // validateAndSanitizeToolParams would refuse the "retry without an outcome"
6003
+ // recovery path a re-close depends on.
6004
+ "strict": false,
6005
+ "parameters": {
6006
+ "type": "object",
6007
+ "properties": {
6008
+ "outcome": {
6009
+ "type": "string",
6010
+ "enum": "[KANBAN_OUTCOME_VALUES]",
6011
+ "description": "Required only when the hub's terminal (conversation-ending) status declares outcomes: the closing outcome. Available outcomes: [KANBAN_OUTCOME_NAMES]"
6012
+ }
6013
+ },
6014
+ "required": [],
6015
+ "additionalProperties": false
6016
+ }
6017
+ },
6018
+ "tool_instructions": "Use when the user's issue has been fully resolved."
6019
+ },
6020
+ "transfer_to_team": {
6021
+ "tool_config": {
6022
+ "name": "transfer_to_team",
6023
+ "description": "Hand off conversation to a human support team",
6024
+ "strict": true,
6025
+ "parameters": {
6026
+ "type": "object",
6027
+ "properties": {
6028
+ "team_name": {
6029
+ "type": "string",
6030
+ "description": "Name of the team to transfer to"
6031
+ }
6032
+ },
6033
+ "required": [
6034
+ "team_name"
6035
+ ],
6036
+ "additionalProperties": false
6037
+ }
6038
+ },
6039
+ "tool_instructions": "Use when the user needs assistance that requires human intervention. The team will receive the full conversation history."
6040
+ },
6041
+ "delegate_to_hub": {
6042
+ "tool_config": {
6043
+ "name": "delegate_to_hub",
6044
+ "description": "Delegate a request to another hub. The target hub handles it as a task conversation and returns the result asynchronously.",
6045
+ "strict": true,
6046
+ "parameters": {
6047
+ "type": "object",
6048
+ "properties": {
6049
+ "request": {
6050
+ "type": "string",
6051
+ "description": "The request to delegate to the target hub, phrased as a self-contained task."
6052
+ }
6053
+ },
6054
+ "required": [
6055
+ "request"
6056
+ ],
6057
+ "additionalProperties": false
6058
+ }
6059
+ },
6060
+ "tool_instructions": "Use to hand a self-contained request to a configured partner hub. The target hub is set by an admin on the tool; you do not choose it. The result comes back asynchronously \u2014 do not wait for it in this turn."
6061
+ },
6062
+ "start_consult_thread": {
6063
+ "tool_config": {
6064
+ "name": "start_consult_thread",
6065
+ "description": "Ask the consultant configured on this tool a question, in a consult thread the support team can see. The answer may come back in this turn or later.",
6066
+ "strict": true,
6067
+ "parameters": {
6068
+ "type": "object",
6069
+ "properties": {
6070
+ "question": {
6071
+ "type": "string",
6072
+ "description": "What you need from the consultant, phrased so it can be answered without further back-and-forth."
6073
+ },
6074
+ "title": {
6075
+ "type": "string",
6076
+ "description": "A short label for the consult thread (a few words), so the support team can tell parallel consults apart."
6077
+ }
6078
+ },
6079
+ "required": [
6080
+ "question",
6081
+ "title"
6082
+ ],
6083
+ "additionalProperties": false
6084
+ }
6085
+ },
6086
+ "tool_instructions": "Use when answering needs knowledge you do not have and a consultant is configured for it. The consultant is set by an admin; you do not choose it. If the result says the answer will follow, END YOUR TURN \u2014 tell the customer you are checking and will come back to them. You will be brought back automatically when the answer arrives; do not poll or call this tool again for the same question."
6087
+ },
6088
+ "update_kanban_status": {
6089
+ "tool_config": {
6090
+ "name": "update_kanban_status",
6091
+ "description": "Updates the kanban status of the current conversation to track its progress through workflow stages",
6092
+ "strict": false,
6093
+ "parameters": {
6094
+ "type": "object",
6095
+ "properties": {
6096
+ "new_kanban_status": {
6097
+ "type": "string",
6098
+ "enum": "[KANBAN_STATUS_VALUES]",
6099
+ "description": "The new kanban status for the conversation. Available statuses: [KANBAN_STATUS_NAMES]"
6100
+ },
6101
+ "outcome": {
6102
+ "type": "string",
6103
+ "enum": "[KANBAN_OUTCOME_VALUES]",
6104
+ "description": "Required only when moving to the terminal (conversation-ending) status: the closing outcome. Available outcomes: [KANBAN_OUTCOME_NAMES]"
6105
+ },
6106
+ "scheduled_event_date": {
6107
+ "type": "string",
6108
+ "description": 'Scheduled event date in RFC3339 format with timezone (e.g., "2024-01-01T00:00:00-03:00")'
6109
+ },
6110
+ "event_description": {
6111
+ "type": "string",
6112
+ "description": "Description of the scheduled event"
6113
+ },
6114
+ "event_sid": {
6115
+ "type": "string",
6116
+ "description": "External event ID for integration purposes"
6117
+ }
6118
+ },
6119
+ "required": [
6120
+ "new_kanban_status"
6121
+ ],
6122
+ "additionalProperties": false
6123
+ }
6124
+ },
6125
+ "tool_instructions": "Use to update the kanban status of a conversation to reflect its current stage in the workflow."
6126
+ },
6127
+ "schedule_followup": {
6128
+ "tool_config": {
6129
+ "name": "schedule_followup",
6130
+ "description": "Schedules a custom manual followup for the current conversation at an exact future time. The system automatically determines the receiver based on conversation status and hub AI mode.",
6131
+ "strict": true,
6132
+ "parameters": {
6133
+ "type": "object",
6134
+ "properties": {
6135
+ "scheduled_time": {
6136
+ "type": "string",
6137
+ "description": 'Exact time to execute the followup in ISO 8601 format with timezone (e.g., "2024-01-01T14:30:00-03:00"). Must be in the future.'
6138
+ },
6139
+ "message": {
6140
+ "type": "string",
6141
+ "description": 'The followup message to send to the customer. Can include AI instructions in brackets like: "Message text [AI Context: instructions for AI]"'
6142
+ }
6143
+ },
6144
+ "required": [
6145
+ "scheduled_time",
6146
+ "message"
6147
+ ],
6148
+ "additionalProperties": false
6149
+ }
6150
+ },
6151
+ "tool_instructions": "Use to schedule a custom followup message at an exact future time. The caller is responsible for choosing appropriate timing (business hours, etc.). This is separate from automatic kanban-based followups."
6152
+ },
6153
+ "transfer_to_agent": {
6154
+ "tool_config": {
6155
+ "name": "transfer_to_agent",
6156
+ "description": "Hand off conversation to another AI agent",
6157
+ "strict": true,
6158
+ "parameters": {
6159
+ "type": "object",
6160
+ "properties": {
6161
+ "agent_name": {
6162
+ "type": "string",
6163
+ "description": "Name of the AI agent to transfer to"
6164
+ }
6165
+ },
6166
+ "required": [
6167
+ "agent_name"
6168
+ ],
6169
+ "additionalProperties": false
6170
+ }
6171
+ },
6172
+ "tool_instructions": "Use when you need to hand the conversation to another agent on the same track \u2014 a specialist for a specific domain, or back to the entry pilot/copilot acting as a router for a request outside your domain."
6173
+ },
6174
+ "consult_agent": {
6175
+ "tool_config": {
6176
+ "name": "consult_agent",
6177
+ "description": "Consult an advisor agent for specialized knowledge without transferring the conversation",
6178
+ "strict": true,
6179
+ "parameters": {
6180
+ "type": "object",
6181
+ "properties": {
6182
+ "agent_name": {
6183
+ "type": "string",
6184
+ "description": "Name of the advisor agent to consult for advice"
6185
+ },
6186
+ "consult_question": {
6187
+ "type": "string",
6188
+ "description": "The specific question to ask the advisor agent"
6189
+ }
6190
+ },
6191
+ "required": [
6192
+ "agent_name",
6193
+ "consult_question"
6194
+ ],
6195
+ "additionalProperties": false
6196
+ }
6197
+ },
6198
+ "tool_instructions": "Use when you need expert advice from an advisor agent but want to maintain conversation ownership. The consulted advisor will provide advice that you can use to help the user."
6199
+ },
6200
+ "read_file": {
6201
+ "tool_config": {
6202
+ "name": "read_file",
6203
+ "description": "Retrieve a file by path (preferred) or by ID. The file is injected into the conversation context for AI processing.",
6204
+ "strict": false,
6205
+ "parameters": {
6206
+ "type": "object",
6207
+ "properties": {
6208
+ "path": {
6209
+ "type": "string",
6210
+ "description": 'Path to the file. Knowledge and skill files live under "resources/", exactly as listed in your resource structure or in a file listing \u2014 e.g. "resources/product-docs/references/menu.md", where the segment after "resources/" names the resource. Files attached to this conversation live under "conversation/" and are announced in the transcript by that path \u2014 e.g. "conversation/report.pdf". A shorter form also works for those two and is matched across everything you can read, preferring an exact path over one that merely ends with what you wrote ("references/menu.md", or just "menu.md"); dropping a middle segment does not resolve, and a bare name carried by both a resource and this conversation is reported as ambiguous so you can retry with the full path. Files from an EARLIER conversation with the same user live under "conversations/<conversation_id>/" \u2014 e.g. "conversations/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed/receipt.pdf", exactly as the transcript of that conversation announces them (get_conversation). That form takes no shortening: write the mount, the conversation id and the file name in full. Prefer this over file_id: paths are stable, while IDs are regenerated whenever a hub is published.'
6211
+ },
6212
+ "file_id": {
6213
+ "type": "string",
6214
+ "description": "The file ID to retrieve, from a conversation message or a file listing. Still supported, but prefer path for resource files."
6215
+ },
6216
+ "resource_id": {
6217
+ "type": "string",
6218
+ "description": "Optional. Restricts a path to one resource \u2014 use it when the same file name exists in more than one resource and the tool reports the reference as ambiguous. Must be a resource linked to this agent."
6219
+ }
6220
+ },
6221
+ "required": [],
6222
+ "additionalProperties": false
6223
+ }
6224
+ },
6225
+ "tool_instructions": "Use this tool to read a file for AI analysis. Provide either path (preferred \u2014 the exact paths appear in your resource structure and in list_files output) or file_id. The file will be injected into the conversation context."
6226
+ },
6227
+ "download_file": {
6228
+ "tool_config": {
6229
+ "name": "download_file",
6230
+ "description": "Mount a resource file into the code execution sandbox so you can edit it. Path is provider-specific: Anthropic mounts at /tmp/<filename>, OpenAI mounts at /mnt/data/<filename> \u2014 use the `sandbox_path` returned in the result. After mounting, use the provider's code-execution tool (Anthropic code_execution view/str_replace/create, or OpenAI code_interpreter Python) to read or modify the file. When finished, call upload_file to commit changes back to the resource library. Bytes never enter conversation context.",
6231
+ "strict": true,
6232
+ "parameters": {
6233
+ "type": "object",
6234
+ "properties": {
6235
+ "file_id": {
6236
+ "type": "string",
6237
+ "description": "The resource file_id to mount (from a file listing or read_file \u2014 a listing row carries file_id alongside its path). Must be a text file; binary files are rejected."
6238
+ }
6239
+ },
6240
+ "required": [
6241
+ "file_id"
6242
+ ],
6243
+ "additionalProperties": false
6244
+ }
6245
+ },
6246
+ "tool_instructions": "Use to begin editing a resource file. Returns { sandbox_path, filename, sandbox_file_id }. After this, use your provider's code-execution tool to view and modify the file inside the sandbox, then call upload_file with the sandbox_file_id of the modified file to persist changes."
6247
+ },
6248
+ "upload_file": {
6249
+ "tool_config": {
6250
+ "name": "upload_file",
6251
+ "description": "Persist a sandbox file to the resource library. Two modes: (1) UPDATE \u2014 pass file_id of an existing resource file to overwrite it (after download_file + code-execution edits). (2) CREATE \u2014 pass resource_id + filename instead of file_id to add a new file to that resource (after creating the file in the sandbox with code-execution). Requires write_enabled on the agent_resource binding for the target resource. sandbox_file_id is always required.",
6252
+ "strict": false,
6253
+ "parameters": {
6254
+ "type": "object",
6255
+ "properties": {
6256
+ "file_id": {
6257
+ "type": "string",
6258
+ "description": "UPDATE mode only: the existing resource file_id to overwrite. Omit when creating a new file."
6259
+ },
6260
+ "resource_id": {
6261
+ "type": "string",
6262
+ "description": "CREATE mode only: the resource the new file belongs to. Required when file_id is omitted; ignored when file_id is provided."
6263
+ },
6264
+ "filename": {
6265
+ "type": "string",
6266
+ "description": "CREATE mode only: the filename for the new file (e.g. 'pricing_v2.md'). Required when file_id is omitted. MIME type is inferred from the extension; only text files are supported."
6267
+ },
6268
+ "folder_id": {
6269
+ "type": "string",
6270
+ "description": "CREATE mode only: optional folder to place the file in. Omit for resource root."
6271
+ },
6272
+ "sandbox_file_id": {
6273
+ "type": "string",
6274
+ "description": "The provider-specific file_id of the file inside the sandbox. Anthropic: take from bash_code_execution_tool_result content[].file_id. OpenAI: take from the container_file_citation annotations on code_interpreter_call output. Either one identifies the file as it currently exists in the sandbox."
6275
+ }
6276
+ },
6277
+ "required": [
6278
+ "sandbox_file_id"
6279
+ ],
6280
+ "additionalProperties": false
6281
+ }
6282
+ },
6283
+ "tool_instructions": "Use to commit either an edit (file_id + sandbox_file_id) or a new file (resource_id + filename + sandbox_file_id). Returns { saved, file_id, file_hash, bytes } on success \u2014 the file_id you can reference later. Errors clearly when write access is denied or when neither identifier set is provided."
6284
+ },
6285
+ "send_files": {
6286
+ "tool_config": {
6287
+ "name": "send_files",
6288
+ "description": "Send files to the end user through their conversation channel (App, WhatsApp, email, etc.)",
6289
+ "strict": false,
6290
+ "parameters": {
6291
+ "type": "object",
6292
+ "properties": {
6293
+ "file_ids": {
6294
+ "type": "array",
6295
+ "items": {
6296
+ "type": "string"
6297
+ },
6298
+ "description": 'Files to send to the user, each named by path (preferred) or by ID. Paths are the addresses announced in the transcript and in your resource structure \u2014 "conversation/report.pdf" for a file attached to this conversation, "conversations/<conversation_id>/receipt.pdf" for one attached to an earlier conversation with the same user, "resources/product-docs/price-list.pdf" for a knowledge or skill file. IDs remain accepted, but they are regenerated whenever a hub is published, so a path is the stable address.'
6299
+ },
6300
+ "folder_ids": {
6301
+ "type": "array",
6302
+ "items": {
6303
+ "type": "string"
6304
+ },
6305
+ "description": "Array of resource folder IDs to send all files from. Alternative to file_ids."
6306
+ },
6307
+ "message_text": {
6308
+ "type": "string",
6309
+ "description": "Message text to accompany the files. For WhatsApp, this becomes the caption on the last file. For email, this becomes the email body."
6310
+ }
6311
+ },
6312
+ "required": [],
6313
+ "additionalProperties": false
6314
+ }
6315
+ },
6316
+ "tool_instructions": "Use this tool to send files to users. WhatsApp sends files individually with optional caption on last file. Email sends all files as attachments in a single email."
6317
+ },
6318
+ "list_files": {
6319
+ "tool_config": {
6320
+ "name": "list_files",
6321
+ "description": "Browse the files you can reach, by path. Call it with no path to see the mounts, then pass a mount or a folder path to list what is inside it. Every row carries the exact path read_file and send_files accept.",
6322
+ "strict": false,
6323
+ "parameters": {
6324
+ "type": "object",
6325
+ "properties": {
6326
+ "path": {
6327
+ "type": "string",
6328
+ "description": 'What to list. Omit it (or pass "") for the mounts you can browse \u2014 the available mounts are listed here at runtime. "conversation" lists the files attached to this conversation. "resources" lists every resource mount. "resources/<resource-slug>" lists the top level of a knowledge resource or skill, and "resources/<resource-slug>/<folder>/<subfolder>" lists that folder. Folder rows come back with their own path, so you drill down by passing back the path of the row you want.'
6329
+ },
6330
+ "query": {
6331
+ "type": "string",
6332
+ "description": "Search text. Matched against a file's title and its file name. Under a resource path this searches the WHOLE subtree below it, not just the level named, and every result is returned as a full path."
6333
+ },
6334
+ "tags": {
6335
+ "type": "array",
6336
+ "items": {
6337
+ "type": "string"
6338
+ },
6339
+ "description": "Return files carrying ANY of these tags. Applies to resource files only \u2014 conversation attachments carry no tags. Searches the whole subtree, like query."
6340
+ },
6341
+ "metadata_filter": {
6342
+ "type": "object",
6343
+ "description": 'Filter resource files by their metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (substring for text, membership for a list), $and/$or/$not (logical). Nested paths like "location.city" are supported, and a bare value means equality. Examples: {"status": "active"}, {"priority": {"$gte": 5}}, {"location.city": "Miami"}, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]}, {"$not": {"status": "archived"}}. A comparison between different types simply does not match. Schema from resource.file_metadata_schema. Searches the whole subtree, like query.'
6344
+ },
6345
+ "limit": {
6346
+ "type": "integer",
6347
+ "description": "Maximum number of rows to return (default: 50, max: 100). The response reports the total the page was drawn from."
6348
+ },
6349
+ "offset": {
6350
+ "type": "integer",
6351
+ "description": "Number of rows to skip for pagination (default: 0)"
6352
+ }
6353
+ },
6354
+ "required": [],
6355
+ "additionalProperties": false
6356
+ }
6357
+ },
6358
+ "tool_instructions": 'Use to find out which files exist before reading one. Call it with no path first: that returns the mounts \u2014 "conversation" for what the user attached here, and one "resources/<slug>" per knowledge resource or skill linked to you. Then pass a mount or folder path back to list its contents. Every row carries a `path`; pass that path to read_file to open the file, or to send_files to deliver it. Pass query, tags or metadata_filter to search a whole resource at once instead of walking it folder by folder. Files from EARLIER conversations are not listed here \u2014 the transcript of each past conversation announces its own files by path (get_conversation).'
6359
+ },
6360
+ "list_resource_folders": {
6361
+ "tool_config": {
6362
+ "name": "list_resource_folders",
6363
+ "description": "(deprecated \u2014 use list_files) List all folders in resources that this agent has access to. Returns all folders with parent_folder_id for hierarchy reconstruction.",
6364
+ "strict": false,
6365
+ "parameters": {
6366
+ "type": "object",
6367
+ "properties": {
6368
+ "resource_id": {
6369
+ "type": "string",
6370
+ "description": "Resource ID to query folders from. Must be one of the knowledge resources linked to this agent (the available ids are listed here at runtime). Required for access control and performance."
6371
+ },
6372
+ "search_query": {
6373
+ "type": "string",
6374
+ "description": "Search folders by name"
6375
+ },
6376
+ "metadata_filter": {
6377
+ "type": "object",
6378
+ "description": 'Filter by metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (contains value), $and/$or/$not (logical). Supports nested paths like "location.city". Returns clear errors for type mismatches. Examples: {"status": "active"} for simple equality, {"priority": {"$gte": 5}} for comparison, {"location.city": "Miami"} for nested path, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]} for complex logic, {"$not": {"status": "archived"}} for negation. Schema from resource.folder_metadata_schema.'
6379
+ }
6380
+ },
6381
+ "required": [
6382
+ "resource_id"
6383
+ ],
6384
+ "additionalProperties": false
6385
+ }
6386
+ },
6387
+ "tool_instructions": "Deprecated: prefer list_files, which walks the same folders by path and needs no resource_id. Still works \u2014 lists all folders in a resource, filtered by search_query or metadata, returning folder info with parent_folder_id, file counts, and subfolder counts."
6388
+ },
6389
+ "list_resource_files": {
6390
+ "tool_config": {
6391
+ "name": "list_resource_files",
6392
+ "description": "(deprecated \u2014 use list_files) List files in resources that this agent has access to. Each file is returned with its path \u2014 the address read_file accepts \u2014 plus metadata.",
6393
+ "strict": false,
6394
+ "parameters": {
6395
+ "type": "object",
6396
+ "properties": {
6397
+ "resource_id": {
6398
+ "type": "string",
6399
+ "description": "Resource ID to query files from. Must be one of the knowledge resources linked to this agent (the available ids are listed here at runtime). Required for access control and performance."
6400
+ },
6401
+ "folder_id": {
6402
+ "type": "string",
6403
+ "description": "Filter by folder ID. Omit to return ALL files. Use 00000000-0000-0000-0000-000000000000 for root-level files only."
6404
+ },
6405
+ "search_query": {
6406
+ "type": "string",
6407
+ "description": "Search files by title or file name"
6408
+ },
6409
+ "tags": {
6410
+ "type": "array",
6411
+ "items": {
6412
+ "type": "string"
6413
+ },
6414
+ "description": "Filter by tags (matches files with any of the specified tags)"
6415
+ },
6416
+ "metadata_filter": {
6417
+ "type": "object",
6418
+ "description": 'Filter by metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (contains value), $and/$or/$not (logical). Supports nested paths like "location.city". Returns clear errors for type mismatches. Examples: {"status": "active"} for simple equality, {"priority": {"$gte": 5}} for comparison, {"location.city": "Miami"} for nested path, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]} for complex logic, {"$not": {"status": "archived"}} for negation. Schema from resource.file_metadata_schema.'
6419
+ },
6420
+ "limit": {
6421
+ "type": "integer",
6422
+ "description": "Maximum number of files to return (default: 50, max: 100)"
6423
+ },
6424
+ "offset": {
6425
+ "type": "integer",
6426
+ "description": "Number of files to skip for pagination (default: 0)"
6427
+ }
6428
+ },
6429
+ "required": [
6430
+ "resource_id"
6431
+ ],
6432
+ "additionalProperties": false
6433
+ }
6434
+ },
6435
+ "tool_instructions": "Deprecated: prefer list_files, which lists the same files by path, needs no resource_id, and also sees this conversation's attachments. Still works \u2014 each row carries a `path`; pass that path to read_file to retrieve the file's content for AI analysis. `file_id` is also returned and still accepted."
6436
+ },
6437
+ "get_tool_schema": {
6438
+ "tool_config": {
6439
+ "name": "get_tool_schema",
6440
+ "description": "MANDATORY FIRST STEP: Retrieves the complete parameter schema for any tool by name. You MUST call this before using ANY tool to get its exact required parameters. This ensures execute_tool will succeed.",
6441
+ "strict": true,
6442
+ "parameters": {
6443
+ "type": "object",
6444
+ "properties": {
6445
+ "tool_name": {
6446
+ "type": "string",
6447
+ "description": "The name of the tool to retrieve the schema for"
6448
+ }
6449
+ },
6450
+ "required": [
6451
+ "tool_name"
6452
+ ],
6453
+ "additionalProperties": false
6454
+ }
6455
+ },
6456
+ "tool_instructions": "Mandatory first step before execute_tool. Returns `function_schema` for the target tool \u2014 pass `function_schema.parameters` (same shape, same keys) as the `parameters` envelope in your execute_tool call."
6457
+ },
6458
+ "execute_tool": {
6459
+ "tool_config": {
6460
+ "name": "execute_tool",
6461
+ "description": "Executes any tool by name. You MUST call get_tool_schema first for the target tool to learn its parameter shape, then pass that shape verbatim under `parameters`.",
6462
+ "strict": false,
6463
+ "parameters": {
6464
+ "type": "object",
6465
+ "required": [
6466
+ "tool_name",
6467
+ "parameters"
6468
+ ],
6469
+ "properties": {
6470
+ "tool_name": {
6471
+ "type": "string",
6472
+ "description": "The name of the tool to execute."
6473
+ },
6474
+ "parameters": {
6475
+ "type": "object",
6476
+ "description": "The arguments object for the target tool. Must match `function_schema.parameters` returned by get_tool_schema \u2014 same keys, same shape, same required fields. Inner contents are validated by the target tool, not by execute_tool."
6477
+ }
6478
+ },
6479
+ "additionalProperties": false
6480
+ }
6481
+ },
6482
+ "tool_instructions": "MANDATORY WORKFLOW: 1) Call get_tool_schema(tool_name) to receive the target's `function_schema`. 2) Build a `parameters` object that matches `function_schema.parameters` \u2014 same property keys, same types, all required fields. 3) Call execute_tool({ tool_name, parameters }). The `parameters` envelope here is identical in shape to `function_schema.parameters`."
6483
+ },
6484
+ "expand_summary": {
6485
+ "tool_config": {
6486
+ "name": "expand_summary",
6487
+ "description": "Retrieve the original messages of a section in the conversation summary. The summary block at the top of the conversation lists sections with stable `id`s \u2014 pass one to get the verbatim messages it covers.",
6488
+ "strict": true,
6489
+ "parameters": {
6490
+ "type": "object",
6491
+ "properties": {
6492
+ "section_id": {
6493
+ "type": "string",
6494
+ "description": "The `id` of the section from the <conversation_summary> block at the top of the conversation."
6495
+ }
6496
+ },
6497
+ "required": [
6498
+ "section_id"
6499
+ ],
6500
+ "additionalProperties": false
6501
+ }
6502
+ },
6503
+ "tool_instructions": "Use when a summary section is too compressed for the question at hand \u2014 e.g. you need the user's exact wording, the precise text of a previous decision, or a tool result that the summary only references. If you pass an unknown `section_id`, the response will include `available_section_ids` so you can retry with a valid one."
6504
+ },
6505
+ "read_skill": {
6506
+ "tool_config": {
6507
+ "name": "read_skill",
6508
+ "description": "Read a skill's SKILL.md content. Pass a skill_id from the skills linked to this agent (available ids are listed in the skill_id parameter).",
6509
+ "strict": true,
6510
+ "parameters": {
6511
+ "type": "object",
6512
+ "properties": {
6513
+ "skill_id": {
6514
+ "type": "string",
6515
+ "description": "The skill's resource_id. Must be one of the skills linked to this agent (the available ids are listed here at runtime)."
6516
+ }
6517
+ },
6518
+ "required": [
6519
+ "skill_id"
6520
+ ],
6521
+ "additionalProperties": false
6522
+ }
6523
+ },
6524
+ "tool_instructions": "Use this tool to read a skill's instructions. Returns the full SKILL.md content as a string. Use read_skill_file to access other files referenced in the skill."
6525
+ },
6526
+ "read_skill_file": {
6527
+ "tool_config": {
6528
+ "name": "read_skill_file",
6529
+ "description": "Read a file from a skill by relative path. No need to call read_skill first if you already know the file path.",
6530
+ "strict": true,
6531
+ "parameters": {
6532
+ "type": "object",
6533
+ "properties": {
6534
+ "skill_id": {
6535
+ "type": "string",
6536
+ "description": "The skill's resource_id. Must be one of the skills linked to this agent (the available ids are listed here at runtime)."
6537
+ },
6538
+ "file_path": {
6539
+ "type": "string",
6540
+ "description": 'Relative path from skill root (e.g., "references/menu.md")'
6541
+ }
6542
+ },
6543
+ "required": [
6544
+ "skill_id",
6545
+ "file_path"
6546
+ ],
6547
+ "additionalProperties": false
6548
+ }
6549
+ },
6550
+ "tool_instructions": 'Use this tool to read files referenced in SKILL.md. Provide the relative path as shown in the skill content (e.g., "references/menu.md"). Returns content for text files or signed URL for binary files.'
6551
+ },
6552
+ "update_state": {
6553
+ "tool_config": {
6554
+ "name": "update_state",
6555
+ "description": "Updates a configured state by merging updates with its current value. Scope (conversation vs user) is inferred from the state's definition.",
6556
+ "strict": false,
6557
+ "parameters": {
6558
+ "type": "object",
6559
+ "properties": {
6560
+ "state_slug": {
6561
+ "type": "string",
6562
+ "description": "Stable slug of the state to update. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
6563
+ },
6564
+ "updates": {
6565
+ "type": "object",
6566
+ "description": "Object with key-value pairs to merge into the current state"
6567
+ }
6568
+ },
6569
+ "required": [
6570
+ "state_slug",
6571
+ "updates"
6572
+ ],
6573
+ "additionalProperties": false
6574
+ }
6575
+ },
6576
+ "tool_instructions": "Use to track and update contextual information during conversations. Examples: updating cart items, recording form answers, saving user preferences. The updates are merged with existing state."
6577
+ },
6578
+ "get_state": {
6579
+ "tool_config": {
6580
+ "name": "get_state",
6581
+ "description": "Retrieves the current value of a configured state. Scope is inferred from the state's definition.",
6582
+ "strict": false,
6583
+ "parameters": {
6584
+ "type": "object",
6585
+ "properties": {
6586
+ "state_slug": {
6587
+ "type": "string",
6588
+ "description": "Stable slug of the state to retrieve. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
6589
+ }
6590
+ },
6591
+ "required": [
6592
+ "state_slug"
6593
+ ],
6594
+ "additionalProperties": false
6595
+ }
6596
+ },
6597
+ "tool_instructions": "Use to retrieve the current state value. This is useful when you need to check the current state before making decisions or when the state is not already available in the prompt context."
6598
+ },
6599
+ "reset_state": {
6600
+ "tool_config": {
6601
+ "name": "reset_state",
6602
+ "description": "Resets a configured state to its initial value as defined in the state schema. Scope is inferred from the state's definition.",
6603
+ "strict": false,
6604
+ "parameters": {
6605
+ "type": "object",
6606
+ "properties": {
6607
+ "state_slug": {
6608
+ "type": "string",
6609
+ "description": "Stable slug of the state to reset. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
6610
+ }
6611
+ },
6612
+ "required": [
6613
+ "state_slug"
6614
+ ],
6615
+ "additionalProperties": false
6616
+ }
6617
+ },
6618
+ "tool_instructions": "Use to reset state to its default initial value. This is useful when you need to clear accumulated state and start fresh, such as clearing a shopping cart or resetting form progress."
6619
+ },
6620
+ "get_conversations_summary": {
6621
+ "tool_config": {
6622
+ "name": "get_conversations_summary",
6623
+ "description": "Retrieves a timeline of past conversations with the current user, showing summaries and conversation IDs.",
6624
+ "strict": false,
6625
+ "parameters": {
6626
+ "type": "object",
6627
+ "properties": {
6628
+ "max_conversations": {
6629
+ "type": "integer",
6630
+ "description": "Maximum number of past conversations to return (default: 10)"
6631
+ },
6632
+ "max_characters": {
6633
+ "type": "integer",
6634
+ "description": "Maximum characters in the output (default: 20000, max: 40000). Truncates oldest entries first."
6635
+ }
6636
+ },
6637
+ "required": [],
6638
+ "additionalProperties": false
6639
+ }
6640
+ },
6641
+ "tool_instructions": "Use this tool at the start of a conversation to retrieve the history of past conversations with this user. Returns summaries with conversation IDs that can be used with get_conversation for full transcripts."
6642
+ },
6643
+ "get_conversation": {
6644
+ "tool_config": {
6645
+ "name": "get_conversation",
6646
+ "description": "Retrieves the full transcript of a specific past conversation, plus the IDs of the conversations closed immediately before and after it. Use get_conversations_summary first to find conversation IDs.",
6647
+ "strict": false,
6648
+ "parameters": {
6649
+ "type": "object",
6650
+ "properties": {
6651
+ "conversation_id": {
6652
+ "type": "string",
6653
+ "description": "The ID of the conversation to retrieve (from get_conversations_summary results)"
6654
+ },
6655
+ "max_characters": {
6656
+ "type": "integer",
6657
+ "description": "Maximum characters in the output (default: 30000, max: 60000). Truncates oldest messages first."
6658
+ }
6659
+ },
6660
+ "required": [
6661
+ "conversation_id"
6662
+ ],
6663
+ "additionalProperties": false
6664
+ }
6665
+ },
6666
+ "tool_instructions": "Use this tool to read the full transcript of a specific past conversation. First use get_conversations_summary to find conversation IDs, then use this tool to drill into the details. The response may include previous_conversation_id and next_conversation_id \u2014 the conversations closed nearest in time either side of this one, which you can pass straight back to this tool to keep moving through the user's history. They are neighbours by closing time, not a fixed chain, and either may be absent."
6667
+ },
6668
+ // ---------------------------------------------------------------------------
6669
+ // Tools added to the TS catalog after the Supabase migration — schemas
6670
+ // reconstructed from the handler signatures in
6671
+ // apps/backend/src/tools/native/wayai/*.ts.
6672
+ // ---------------------------------------------------------------------------
6673
+ "set_state_path": {
6674
+ "tool_config": {
6675
+ "name": "set_state_path",
6676
+ "description": "Set a specific path within a state object (dot/array path) without overwriting the rest of the state. Scope is inferred from the state's definition.",
6677
+ // strict:false matches the other state ops so legacy composed-tool YAML
6678
+ // mapping `state_scope` (now ignored at runtime) doesn't trip OpenAI
6679
+ // strict-mode validators on in-flight calls.
6680
+ "strict": false,
6681
+ "parameters": {
6682
+ "type": "object",
6683
+ "properties": {
6684
+ "state_slug": {
6685
+ "type": "string",
6686
+ "description": "Stable slug of the state variable to update. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
6687
+ },
6688
+ "path": {
6689
+ "type": "array",
6690
+ "items": { "type": "string" },
6691
+ "description": "Path within the state object, as an ordered list of keys."
6692
+ },
6693
+ "value": {
6694
+ "description": "The value to write at the given path. Any JSON value is allowed."
6695
+ }
6696
+ },
6697
+ "required": ["state_slug", "path", "value"],
6698
+ "additionalProperties": false
6699
+ }
6700
+ },
6701
+ "tool_instructions": "Use this tool when you need to update a single field inside a structured state object without replacing the entire state. Provide the full path from the root of the state object."
6702
+ },
6703
+ // NEVER OFFERED TO A MODEL, unlike every other entry in this file — and it is
6704
+ // `model_callable: false` on the catalog entry that delivers that, NOT the role
6705
+ // restriction beside it. An earlier version of this comment credited
6706
+ // `assignable_roles: ['monitor']`, which was wrong: a monitor runs a DISPATCHING
6707
+ // turn on `idle` (only the synchronous side-call sets `omit_tools`), and
6708
+ // `PATCH /api/setup/agents/:id` can re-role an agent that keeps the rows it
6709
+ // holds — so the role gate left the tool offerable on two paths.
6710
+ //
6711
+ // The schema exists because the catalog requires one per tool (the parity check
6712
+ // at the bottom of `native-tools.ts`) and because it is the one place the two
6713
+ // arguments are described for whoever writes the rule.
6714
+ //
6715
+ // `tool_instructions: null` FOLLOWS FROM THAT: instructions are injected into an
6716
+ // agent's system message when the tool is attached, and this tool is attached to
6717
+ // an agent whose model is never told it exists.
6718
+ // NEVER OFFERED TO A MODEL, like `insert_note` below and for the same reason:
6719
+ // `model_callable: false` on the catalog entry. A monitor's RULE selects it and
6720
+ // the monitor runtime carries it out — it never reaches `handleTool`, because
6721
+ // running a callee means re-entering the side-call with the caller's cascade
6722
+ // state, which no `ToolContext` carries.
6723
+ "run_monitor": {
6724
+ "tool_config": {
6725
+ "name": "run_monitor",
6726
+ "description": "Run another monitor on this hub. The named monitor judges the same conversation and acts on its own rules; nothing it finds is returned here.",
6727
+ "strict": false,
6728
+ "parameters": {
6729
+ "type": "object",
6730
+ "properties": {
6731
+ "monitor_name": {
6732
+ "type": "string",
6733
+ "description": "The name of the monitor to run. It must be a monitor on this hub whose trigger is manual."
6734
+ }
6735
+ },
6736
+ "required": ["monitor_name"],
6737
+ "additionalProperties": false
6738
+ }
6739
+ },
6740
+ "tool_instructions": null
6741
+ },
6742
+ "insert_note": {
6743
+ "tool_config": {
6744
+ "name": "insert_note",
6745
+ "description": "Insert an admin-authored note into the answering agent's context for this turn only. The note is written by the administrator, not by a model; the monitor's variables are substituted into it.",
6746
+ "strict": false,
6747
+ "parameters": {
6748
+ "type": "object",
6749
+ "properties": {
6750
+ "template": {
6751
+ "type": "string",
6752
+ "description": "The note text. Write {{variable_name}} to substitute one of this monitor's own variables. Unresolved names render as nothing and are recorded on the audit row."
6753
+ },
6754
+ "keep_in_history": {
6755
+ "type": "boolean",
6756
+ "description": "Default false: the note briefs this turn only and is never stored. Set true to also record it as an internal message the support team can see and the customer never receives."
6757
+ }
6758
+ },
6759
+ "required": ["template"],
6760
+ "additionalProperties": false
6761
+ }
6762
+ },
6763
+ "tool_instructions": null
6764
+ }
6765
+ };
6766
+ WAYAI_CONNECTOR = "b17d9f3a-4e1b-46c9-b648-a2f0c3611aa4";
6767
+ BASE_NATIVE_TOOLS = [
6768
+ // --- Wayai core tools ---
6769
+ { tool_native_id: "nt-001", tool_name: "transfer_to_agent", tool_display_name: "Transfer to Agent", tool_description: "Transfer the conversation to another AI agent", connector_id: WAYAI_CONNECTOR, operation: "transfer_to_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null },
6770
+ { tool_native_id: "nt-002", tool_name: "transfer_to_team", tool_display_name: "Transfer to Team", tool_description: "Escalate conversation to a human support team", connector_id: WAYAI_CONNECTOR, operation: "transfer_to_team", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null },
6771
+ { tool_native_id: "nt-003", tool_name: "close_conversation", tool_display_name: "Close Conversation", tool_description: "Close and end the current conversation", connector_id: WAYAI_CONNECTOR, operation: "close_conversation", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6772
+ { tool_native_id: "nt-004", tool_name: "update_kanban_status", tool_display_name: "Update Kanban Status", tool_description: "Update the kanban status of the conversation", connector_id: WAYAI_CONNECTOR, operation: "update_kanban_status", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6773
+ { tool_native_id: "nt-005", tool_name: "schedule_followup", tool_display_name: "Schedule Follow-up", tool_description: "Schedule a follow-up message for the conversation", connector_id: WAYAI_CONNECTOR, operation: "schedule_followup", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: true, execution_config: null },
6774
+ { tool_native_id: "nt-006", tool_name: "send_files", tool_display_name: "Send Files", tool_description: "Send files to the user", connector_id: WAYAI_CONNECTOR, operation: "send_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6775
+ { tool_native_id: "nt-007", tool_name: "update_state", tool_display_name: "Update State", tool_description: "Update conversation or user state variables", connector_id: WAYAI_CONNECTOR, operation: "update_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6776
+ { tool_native_id: "nt-008", tool_name: "get_state", tool_display_name: "Get State", tool_description: "Retrieve conversation or user state values", connector_id: WAYAI_CONNECTOR, operation: "get_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6777
+ { tool_native_id: "nt-009", tool_name: "reset_state", tool_display_name: "Reset State", tool_description: "Reset conversation or user state to initial values", connector_id: WAYAI_CONNECTOR, operation: "reset_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6778
+ { tool_native_id: "nt-010", tool_name: "set_state_path", tool_display_name: "Set State Path", tool_description: "Set a specific path within state object", connector_id: WAYAI_CONNECTOR, operation: "set_state_path", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6779
+ { tool_native_id: "nt-011", tool_name: "consult_agent", tool_display_name: "Consult Agent", tool_description: "Consult an advisor agent for advice without transferring", connector_id: WAYAI_CONNECTOR, operation: "consult_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6780
+ { tool_native_id: "nt-013", tool_name: "read_skill", tool_display_name: "Read Skill", tool_description: "Read the full specification of a skill", connector_id: WAYAI_CONNECTOR, operation: "read_skill", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6781
+ { tool_native_id: "nt-014", tool_name: "read_skill_file", tool_display_name: "Read Skill File", tool_description: "Read a specific file within a skill", connector_id: WAYAI_CONNECTOR, operation: "read_skill_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6782
+ { tool_native_id: "nt-015", tool_name: "read_file", tool_display_name: "Read File", tool_description: "Read the contents of a resource file", connector_id: WAYAI_CONNECTOR, operation: "read_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6783
+ // Superseded by `list_files` (nt-027), which browses the same resource files
6784
+ // by PATH plus this conversation's attachments.
6785
+ //
6786
+ // `ui_assignable: false` — DEPRECATED, not withdrawn. Hiding the Add grid entry
6787
+ // stops a NEW agent from being wired to a tool that addresses files by an id a
6788
+ // publish re-mints and that cannot see a conversation attachment at all (the id
6789
+ // itself is discoverable — `RESOURCE_TOOL_INJECTIONS` still writes the linked
6790
+ // ids into their `resource_id` description every turn), while every hub that
6791
+ // already assigns one keeps
6792
+ // working: the handler still dispatches, CI name resolution still resolves the
6793
+ // name, and `wayai pull` / `push` still round-trips the YAML. There is no
6794
+ // auto-provisioning anywhere in the platform, so an existing agent is never
6795
+ // migrated for free — an author swaps the tool deliberately.
6796
+ { tool_native_id: "nt-016", tool_name: "list_resource_files", tool_display_name: "List Resource Files", tool_description: "(deprecated \u2014 use list_files) List files in a resource folder", connector_id: WAYAI_CONNECTOR, operation: "list_resource_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, ui_assignable: false },
6797
+ { tool_native_id: "nt-017", tool_name: "list_resource_folders", tool_display_name: "List Resource Folders", tool_description: "(deprecated \u2014 use list_files) List all resource folders", connector_id: WAYAI_CONNECTOR, operation: "list_resource_folders", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, ui_assignable: false },
6798
+ { tool_native_id: "nt-018", tool_name: "get_conversations_summary", tool_display_name: "Get Conversations Summary", tool_description: "Get a summary of recent conversations", connector_id: WAYAI_CONNECTOR, operation: "get_conversations_summary", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6799
+ { tool_native_id: "nt-019", tool_name: "get_conversation", tool_display_name: "Get Conversation", tool_description: "Retrieve full conversation history", connector_id: WAYAI_CONNECTOR, operation: "get_conversation", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6800
+ { tool_native_id: "nt-020", tool_name: "get_tool_schema", tool_display_name: "Get Tool Schema", tool_description: "Retrieve the JSON schema for a tool", connector_id: WAYAI_CONNECTOR, operation: "get_tool_schema", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6801
+ { tool_native_id: "nt-021", tool_name: "execute_tool", tool_display_name: "Execute Tool", tool_description: "Execute any available tool by name", connector_id: WAYAI_CONNECTOR, operation: "execute_tool", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6802
+ { tool_native_id: "nt-022", tool_name: "expand_summary", tool_display_name: "Expand Summary", tool_description: "Retrieve the original messages of a section in the conversation summary", connector_id: WAYAI_CONNECTOR, operation: "expand_summary", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6803
+ { tool_native_id: "nt-023", tool_name: "download_file", tool_display_name: "Download File for Edit", tool_description: "Mount a resource file into the code execution sandbox for editing", connector_id: WAYAI_CONNECTOR, operation: "download_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6804
+ { tool_native_id: "nt-024", tool_name: "upload_file", tool_display_name: "Upload Edited File", tool_description: "Persist a sandbox-edited file back to the resource library", connector_id: WAYAI_CONNECTOR, operation: "upload_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6805
+ // Hub-as-agent delegation (consultant-agents H1.5). UN-GATED in PR 7 (H1.5b): the
6806
+ // spawn + cross-DO completion callback shipped, so the tool is live across the
6807
+ // catalog listing, CI push, the config snapshot, and the handler. (PR 6 shipped it
6808
+ // `assignable: false` so no visible-but-inert tool could reach production between
6809
+ // the two merges.)
6810
+ //
6811
+ // `ui_assignable: false` — CONFIG-AS-CODE ONLY for v1. Attaching this tool requires
6812
+ // two admin decisions the Add dialog cannot yet collect: the target hub and the
6813
+ // `context_boundary` (a data-sensitivity choice). Without a `hub` branch in the
6814
+ // delegation dialog, clicking Add would submit no delegation params and the write
6815
+ // path would reject it — a live button that only ever errors. Assign it via
6816
+ // `wayai push` until the picker + boundary control ship.
6817
+ { tool_native_id: "nt-025", tool_name: "delegate_to_hub", tool_display_name: "Delegate to Hub", tool_description: "Delegate a request to another hub as a task, and receive the result asynchronously", connector_id: WAYAI_CONNECTOR, operation: "delegate_to_hub", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, ui_assignable: false },
6818
+ // Agent-initiated consults (consultant-agents H1.7b). A delegation tool like the
6819
+ // three above: the TARGET is admin configuration (`delegated_agent_id` for a
6820
+ // same-hub consultant, `delegated_hub_id` for a partner hub) and the model supplies
6821
+ // only the question — so scoping "which consultants may this agent reach" reuses
6822
+ // the existing assignment model with no new config surface.
6823
+ //
6824
+ // `ui_assignable: false` — CONFIG-AS-CODE ONLY for v1, same reasoning as
6825
+ // `delegate_to_hub`: attaching it requires a target the Add dialog cannot yet
6826
+ // collect (and, for a consultant target, the `allow_consultant_chain` opt-in). A
6827
+ // live button that only ever errors is worse than no button. Assign via `wayai push`
6828
+ // until the picker ships.
6829
+ { tool_native_id: "nt-026", tool_name: "start_consult_thread", tool_display_name: "Start Consult Thread", tool_description: "Ask a configured consultant (or partner hub) a question in a visible consult thread", connector_id: WAYAI_CONNECTOR, operation: "start_consult_thread", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, ui_assignable: false },
6830
+ // One drill-down listing for every file namespace, replacing `list_resource_files`
6831
+ // (nt-016) and `list_resource_folders` (nt-017). Fully visible: it needs no
6832
+ // configuration the Add dialog cannot collect — the mounts an agent may browse
6833
+ // come from its existing resource links and its own conversation, and the only
6834
+ // model-supplied input is a path.
6835
+ { tool_native_id: "nt-027", tool_name: "list_files", tool_display_name: "List Files", tool_description: "Browse the files this agent can reach, by path", connector_id: WAYAI_CONNECTOR, operation: "list_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
6836
+ // TWO gates, and BOTH are load-bearing — the first shipped alone and was not
6837
+ // enough. `assignable_roles: ['monitor']` says only a monitor may HOLD it,
6838
+ // because the tool has no meaning off a monitor: a rule SELECTS it and the note
6839
+ // is injected into the ANSWERING agent's prompt for that one turn.
6840
+ // `model_callable: false` says no model is ever TOLD it exists — which the role
6841
+ // restriction does NOT imply, because an `idle` monitor runs a dispatching turn
6842
+ // and a re-roled agent keeps the tools it holds. Offered and called, it reaches
6843
+ // the native dispatcher's `default:` branch: a Sentry event and a provider
6844
+ // re-call. See `NativeTool.model_callable`.
6845
+ { tool_native_id: "nt-028", tool_name: "insert_note", tool_display_name: "Insert Note", tool_description: "Brief the answering agent with an admin-authored note before it replies", connector_id: WAYAI_CONNECTOR, operation: "insert_note", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, assignable_roles: ["monitor"], model_callable: false },
6846
+ // TWO gates, same pair and same reasons as `insert_note` above — this is the
6847
+ // second tool to need both, which is why they are catalog fields rather than a
6848
+ // check at one site. `assignable_roles: ['monitor']`: only a monitor may hold it,
6849
+ // because only a monitor's RULE can select it. `model_callable: false`: no model
6850
+ // is ever told it exists, which the role restriction does NOT imply — an `idle`
6851
+ // monitor runs a dispatching turn, and a re-roled agent keeps the rows it holds.
6852
+ //
6853
+ // The CALLEE is named, not identified by id (owner decision 9): a hub-relative
6854
+ // name survives every copy path untouched, where a per-hub UUID in a JSON column
6855
+ // would need remapping on both branching mechanisms and normalizing on both CI
6856
+ // directions. `action.tool_name`, one key away in the same object, is the
6857
+ // precedent.
6858
+ { tool_native_id: "nt-029", tool_name: "run_monitor", tool_display_name: "Run Monitor", tool_description: "Run another monitor on this hub and let it act on its own rules", connector_id: WAYAI_CONNECTOR, operation: "run_monitor", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, assignable_roles: ["monitor"], model_callable: false }
6859
+ ];
6860
+ NATIVE_TOOLS = BASE_NATIVE_TOOLS.map((base) => {
6861
+ const schema = NATIVE_TOOL_SCHEMAS[base.tool_name];
6862
+ if (!schema) {
6863
+ throw new Error(
6864
+ `Native tool "${base.tool_name}" (${base.tool_native_id}) is missing an entry in NATIVE_TOOL_SCHEMAS. Add it to workers/shared/src/catalog/native-tool-schemas.ts.`
6865
+ );
6866
+ }
6867
+ return {
6868
+ ...base,
6869
+ tool_config: schema.tool_config,
6870
+ tool_instructions: schema.tool_instructions
6871
+ };
6872
+ });
6873
+ NATIVE_TOOL_NAMES = new Set(NATIVE_TOOLS.map((t) => t.tool_name));
5549
6874
  previousConversationsCountField = external_exports.number().int().min(0).max(PREVIOUS_CONVERSATIONS_MAX).nullable().optional();
5550
6875
  summarizationThresholdField = external_exports.number().int().min(SUMMARIZATION_THRESHOLD_MIN).max(SUMMARIZATION_THRESHOLD_MAX).nullable().optional();
6876
+ flagConditionSchema = external_exports.object({
6877
+ variable: external_exports.string(),
6878
+ operator: external_exports.enum(FLAG_CONDITION_OPERATORS),
6879
+ // `boolean` is admitted for parity with the CI write path, whose refine gates the
6880
+ // operator alone, so `value: false` on a boolean evaluation variable is storable
6881
+ // config today and the evaluator honours it through `String()`. Narrowing here would
6882
+ // reject on PATCH exactly what `wayai push` persists, so any REST caller that reads
6883
+ // an agent and writes it back — the typed clients, the CLI's `ApiClient`, MCP — would
6884
+ // fail on a hub configured through the other surface.
6885
+ value: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])
6886
+ });
6887
+ MONITOR_TRIGGERS = ["idle", "user_message", "assistant_reply", "manual"];
6888
+ monitorTriggerSchema = external_exports.enum(MONITOR_TRIGGERS);
6889
+ MONITOR_FIRING_TRIGGERS = MONITOR_TRIGGERS.filter(isFiringTrigger);
6890
+ MONITOR_DELAY_SECONDS_MIN = 10;
6891
+ MONITOR_IDLE_DELAY_REQUIRED_MESSAGE = "delay_seconds is required for an idle monitor (an absent trigger is idle)";
6892
+ MONITOR_HISTORY_MESSAGES_MAX = 100;
6893
+ monitorHistoryMessagesSchema = external_exports.number().int().min(1).max(MONITOR_HISTORY_MESSAGES_MAX);
6894
+ monitorIncludeToolResultsSchema = external_exports.boolean();
6895
+ MONITOR_INPUT_SHAPING_KEYS = ["history_messages", "include_tool_results"];
6896
+ MONITOR_INPUT_SHAPING_SCHEMAS = {
6897
+ history_messages: monitorHistoryMessagesSchema.optional(),
6898
+ include_tool_results: monitorIncludeToolResultsSchema.optional()
6899
+ };
6900
+ MONITOR_INPUT_SHAPING_IDLE_MESSAGE = "only a user_message, assistant_reply or manual monitor reads this \u2014 an idle monitor runs as a full turn and takes the ordinary history window";
6901
+ monitorArgumentSourceSchema = external_exports.union([
6902
+ external_exports.object({ from_variable: external_exports.string().min(1) }).strict(),
6903
+ external_exports.object({ const: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]) }).strict()
6904
+ ]);
6905
+ MONITOR_NOTE_TEMPLATE_MAX = 2e3;
6906
+ monitorActionSchema = external_exports.discriminatedUnion("kind", [
6907
+ external_exports.object({ kind: external_exports.literal("none") }).strict(),
6908
+ external_exports.object({
6909
+ kind: external_exports.literal("call_tool"),
6910
+ tool_name: external_exports.string().min(1),
6911
+ args: external_exports.record(monitorArgumentSourceSchema).optional()
6912
+ }).strict(),
6913
+ external_exports.object({ kind: external_exports.literal("hold") }).strict(),
6914
+ external_exports.object({ kind: external_exports.literal("rewrite"), note: external_exports.string().min(1).max(MONITOR_NOTE_TEMPLATE_MAX) }).strict()
6915
+ ]);
6916
+ monitorRuleSchema = external_exports.object({
6917
+ when: external_exports.array(flagConditionSchema).min(1),
6918
+ action: monitorActionSchema
6919
+ }).strict();
6920
+ MONITOR_RULE_KEYS = ["rules", "fallback"];
6921
+ MONITOR_RULE_SCHEMAS = {
6922
+ rules: external_exports.array(monitorRuleSchema).optional(),
6923
+ fallback: monitorActionSchema.optional()
6924
+ };
6925
+ MONITOR_RULE_TRIGGERS = ["user_message", "assistant_reply", "manual"];
6926
+ MONITOR_RULE_TRIGGER_MESSAGE = "only a user_message, assistant_reply or manual monitor acts on rules \u2014 set one of those triggers, or remove this key. A manual monitor acts on its rules when another monitor runs it.";
6927
+ MONITOR_USER_MESSAGE_ACTION_KINDS = ["none", "call_tool"];
6928
+ MONITOR_ASSISTANT_REPLY_ACTION_KINDS = ["none", "call_tool", "hold", "rewrite"];
6929
+ INSERT_NOTE_TOOL_NAME = "insert_note";
6930
+ RUN_MONITOR_TOOL_NAME = "run_monitor";
6931
+ MONITOR_RULE_ALLOWED_NATIVE_TOOLS = [
6932
+ "update_state",
6933
+ "schedule_followup",
6934
+ "transfer_to_agent",
6935
+ "transfer_to_team",
6936
+ INSERT_NOTE_TOOL_NAME,
6937
+ RUN_MONITOR_TOOL_NAME
6938
+ ];
6939
+ MONITOR_RULE_REENTRY_TOOLS = {
6940
+ transfer_to_agent: "agent",
6941
+ transfer_to_team: "team"
6942
+ };
6943
+ MONITOR_RULE_REENTRY_TRACKS = new Map(Object.entries(MONITOR_RULE_REENTRY_TOOLS));
5551
6944
  monitorConfigField = external_exports.object({
5552
- delay_seconds: external_exports.number().int().min(10),
5553
- flag_conditions: external_exports.array(
5554
- external_exports.object({
5555
- variable: external_exports.string(),
5556
- operator: external_exports.enum(["=", "!="]),
5557
- value: external_exports.union([external_exports.string(), external_exports.number()])
5558
- })
5559
- ).optional()
5560
- }).passthrough().nullable().optional();
6945
+ delay_seconds: external_exports.number().int().min(MONITOR_DELAY_SECONDS_MIN).optional(),
6946
+ trigger: monitorTriggerSchema.optional(),
6947
+ ...MONITOR_INPUT_SHAPING_SCHEMAS,
6948
+ ...MONITOR_RULE_SCHEMAS,
6949
+ flag_conditions: external_exports.array(flagConditionSchema).optional()
6950
+ }).passthrough().superRefine((config, ctx) => {
6951
+ if (monitorConfigNeedsDelay(config)) {
6952
+ ctx.addIssue({
6953
+ code: external_exports.ZodIssueCode.custom,
6954
+ path: ["delay_seconds"],
6955
+ message: MONITOR_IDLE_DELAY_REQUIRED_MESSAGE
6956
+ });
6957
+ }
6958
+ for (const key of monitorInputShapingKeysOnIdle(config)) {
6959
+ ctx.addIssue({
6960
+ code: external_exports.ZodIssueCode.custom,
6961
+ path: [key],
6962
+ message: MONITOR_INPUT_SHAPING_IDLE_MESSAGE
6963
+ });
6964
+ }
6965
+ for (const issue of collectMonitorRuleIssues(config)) {
6966
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: issue.path, message: issue.message });
6967
+ }
6968
+ }).nullable().optional();
6969
+ flagConditionsField = external_exports.array(flagConditionSchema).nullable().optional();
5561
6970
  agentIdParam = external_exports.object({
5562
6971
  id: uuidSchema
5563
6972
  });
@@ -5574,13 +6983,15 @@ var init_contracts = __esm({
5574
6983
  additional_context_template: external_exports.string().max(2e4).optional(),
5575
6984
  summarization_threshold_tokens: summarizationThresholdField,
5576
6985
  previous_conversations_count: previousConversationsCountField,
5577
- monitor_config: monitorConfigField
5578
- }).passthrough();
6986
+ monitor_config: monitorConfigField,
6987
+ flag_conditions: flagConditionsField
6988
+ }).passthrough().superRefine(refineDecisionsModelBinding);
5579
6989
  updateAgentBody = external_exports.object({
5580
6990
  additional_context_template: external_exports.string().max(2e4).optional(),
5581
6991
  summarization_threshold_tokens: summarizationThresholdField,
5582
6992
  previous_conversations_count: previousConversationsCountField,
5583
- monitor_config: monitorConfigField
6993
+ monitor_config: monitorConfigField,
6994
+ flag_conditions: flagConditionsField
5584
6995
  }).passthrough();
5585
6996
  AGENT_PARAMETER_TYPES = ["string", "number", "boolean", "integer", "enum"];
5586
6997
  AGENT_PARAMETER_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/;
@@ -7572,11 +8983,11 @@ var init_contracts = __esm({
7572
8983
  ops_tenths_this_month: external_exports.number().nonnegative(),
7573
8984
  records_count: external_exports.number().nonnegative(),
7574
8985
  month_key: external_exports.string(),
7575
- // The closed window's meter reading (§2.7's prior-window snapshot). OPTIONAL for the same
8986
+ // The closed window's meter reading (the prior-window snapshot). OPTIONAL for the same
7576
8987
  // reason this object is not `.strict()`: the producer deploys on its own cadence, and a
7577
8988
  // required field would fail the parse for every counter on any producer build that predates
7578
8989
  // it — turning an additive wire change into an outage of the gauge above. Absence and a
7579
- // non-matching key are the SAME outcome for a reader (§2.7 property 1: do not bill), so
8990
+ // non-matching key are the SAME outcome for a reader (do not bill), so
7580
8991
  // nothing is lost by tolerating it.
7581
8992
  prior_window_key: external_exports.string().optional(),
7582
8993
  prior_window_ops_tenths: external_exports.number().nonnegative().optional()
@@ -7588,41 +8999,41 @@ var init_contracts = __esm({
7588
8999
  * Monotonic per org, WayAI-assigned. An ordering guard for concurrent WayAI
7589
9000
  * writers (a plan change racing a token mint) — NOT a cross-repo CAS. Gaps are
7590
9001
  * expected: a version is allocated per push attempt, and an idempotency skip
7591
- * (§2.4) burns none while a failed KV put burns one.
9002
+ * burns none while a failed KV put burns one.
7592
9003
  */
7593
9004
  projection_version: external_exports.number().int().nonnegative(),
7594
- /** Diagnostic only — never gates. Freshness is `window_resets_at`'s job (§2.2). */
9005
+ /** Diagnostic only — never gates. Freshness is `window_resets_at`'s job. */
7595
9006
  written_at: external_exports.string().datetime(),
7596
9007
  /** The WayAI org UUID. The `account_id` spelling is Rekor's documented carve-out. */
7597
9008
  account_id: external_exports.string().uuid(),
7598
- /** The EFFECTIVE plan, not the subscribed one (§2.2.1). Never gates. */
9009
+ /** The EFFECTIVE plan, not the subscribed one. Never gates. */
7599
9010
  plan_key: planKeySchema,
7600
- /** Informational only (§2.2.1). `cancelled` spelling matches `BillingRecord`. */
9011
+ /** Informational only. `cancelled` spelling matches `BillingRecord`. */
7601
9012
  billing_status: billingStatusSchema,
7602
9013
  /** Tenths, matching Rekor's meter granularity (write 10, point-read 1, list/SQL 10). */
7603
9014
  ops_tenths_included: external_exports.number().int().nonnegative(),
7604
9015
  max_records: external_exports.number().int().nonnegative(),
7605
- /** Its own field, no longer derived from `max_records` (§2.2). */
9016
+ /** Its own field, no longer derived from `max_records`. */
7606
9017
  import_rows_included: external_exports.number().int().nonnegative(),
7607
- /** REQUIRED — absence is malformed, never coerced to `false` (§2.2.2). */
9018
+ /** REQUIRED — absence is malformed, never coerced to `false`. */
7608
9019
  production_bases: external_exports.boolean(),
7609
- /** Opaque to Rekor; compared for equality only, to reset `usage:` counters (§2.3). */
9020
+ /** Opaque to Rekor; compared for equality only, to reset `usage:` counters. */
7610
9021
  window_key: external_exports.string().min(1),
7611
9022
  /**
7612
- * The window's TRUE lower bound, for the reconcile's `operations_log` sum (§2.2).
9023
+ * The window's TRUE lower bound, for the reconcile's `operations_log` sum.
7613
9024
  *
7614
9025
  * OPTIONAL, and that is not a rollout convenience — it is what "never gates" means
7615
9026
  * on the writer's side. The tier sources can be absent (a paid row carrying
7616
9027
  * `current_period_end` with no `current_period_start`) and inverted pairs are a
7617
- * §2.2 diagnostic, so requiring it here would let an inert field throw the strict
7618
- * parse and leave the org with NO projection — permanently stale under §2.3, with
7619
- * §2.5's fail-closed gates refusing base creation and 503-ing every import. The
9028
+ * diagnostic, so requiring it here would let an inert field throw the strict
9029
+ * parse and leave the org with NO projection — permanently stale, with the
9030
+ * fail-closed gates refusing base creation and 503-ing every import. The
7620
9031
  * writer emits it whenever it has a sound value and omits it otherwise; Rekor's
7621
9032
  * observed-flip fallback covers the gap, and rollout step 3 keeps that fallback
7622
9033
  * for exactly this reason.
7623
9034
  */
7624
9035
  window_start: external_exports.string().datetime().optional(),
7625
- /** The sole staleness authority (§2.3). Never triggers a counter reset. */
9036
+ /** The sole staleness authority. Never triggers a counter reset. */
7626
9037
  window_resets_at: external_exports.string().datetime()
7627
9038
  }).strict();
7628
9039
  rekorEntitlementRefreshMessageSchema = external_exports.object({
@@ -7862,6 +9273,9 @@ var init_contracts = __esm({
7862
9273
  refineHubAsCodeEvalAttachments(config, ctx);
7863
9274
  refineHubAsCodeResources(config, ctx);
7864
9275
  refineHubAsCodeDelegation(config, ctx);
9276
+ refineHubAsCodeFlagConditions(config, ctx);
9277
+ refineHubAsCodeMonitorConfig(config, ctx);
9278
+ refineHubAsCodeDecisionsModel(config, ctx);
7865
9279
  });
7866
9280
  ciPullHubIdParam = external_exports.object({
7867
9281
  hub_id: ciUuidSchema
@@ -8089,7 +9503,7 @@ var init_contracts = __esm({
8089
9503
  *
8090
9504
  * Past-or-now only. A future start freezes the org's window permanently:
8091
9505
  * `maybeRolloverBillingWindow` derives a NEGATIVE elapsed and so never archives the
8092
- * metrics nor restamps the column, while the §2.6 trigger-5 sweep selects only windows
9506
+ * metrics nor restamps the column, while the rollover sweep selects only windows
8093
9507
  * that have already passed — so nothing rolls the org over, its consumed operations
8094
9508
  * never reset, and past quota every AI turn is refused until a human re-patches.
8095
9509
  */
@@ -8184,7 +9598,7 @@ var init_contracts = __esm({
8184
9598
  seats_per_unit: external_exports.number().int().min(1, "seats_per_unit must be a positive integer").optional(),
8185
9599
  seats_scaling: external_exports.enum(["fixed", "per_quantity"]).optional(),
8186
9600
  is_active: external_exports.boolean().optional(),
8187
- // Rekor entitlement allowances (rekor-platform-contracts §2.2). Editable here for the
9601
+ // Rekor entitlement allowances. Editable here for the
8188
9602
  // same reason `messages_per_unit` is — and this route is the only ROUTINE way to change an
8189
9603
  // allowance on a live plan: the seed establishes a plan row and never rewrites one, so a
8190
9604
  // seed-version bump no longer moves these values (see `seed.ts`).
@@ -8321,7 +9735,7 @@ var init_contracts = __esm({
8321
9735
  cmd: external_exports.string().min(1, "cmd is required").max(SANDBOX_EXEC_MAX_CMD_LENGTH, `cmd must be <= ${SANDBOX_EXEC_MAX_CMD_LENGTH} chars`),
8322
9736
  /**
8323
9737
  * Egress posture. Omitted ⇒ the driver fails closed to deny-all outbound (the
8324
- * §4 safe default). The route re-validates the (policy, allowlist) pair via
9738
+ * safe default). The route re-validates the (policy, allowlist) pair via
8325
9739
  * `validateEgressConfig`.
8326
9740
  */
8327
9741
  egress_policy: sandboxEgressPolicy.optional(),
@@ -8912,7 +10326,7 @@ var init_contracts = __esm({
8912
10326
  base_id: external_exports.string()
8913
10327
  });
8914
10328
  rekorBaseChangeMessageSchema = external_exports.object({
8915
- /** The WayAI org UUID — one id namespace (plan §2). */
10329
+ /** The WayAI org UUID — one id namespace. */
8916
10330
  org_id: external_exports.string().uuid(),
8917
10331
  /**
8918
10332
  * Not a UUID: a preview base id is `{prod_base_id}--{slug}`. Immutable per base — a
@@ -9850,6 +11264,327 @@ function withinJsonLength2(value, max) {
9850
11264
  const length = jsonLengthOrNull2(value);
9851
11265
  return length !== null && length <= max;
9852
11266
  }
11267
+ function schemaToQuestions2(schema) {
11268
+ const classified = classifySchema2(schema);
11269
+ if (classified.refusals.length > 0) return { ok: false, refusals: classified.refusals };
11270
+ const questions = {};
11271
+ for (const [field, primitive] of classified.fields) {
11272
+ questions[field] = toQuestion2(primitive);
11273
+ }
11274
+ return { ok: true, questions };
11275
+ }
11276
+ function classifySchema2(schema) {
11277
+ const fields = /* @__PURE__ */ new Map();
11278
+ if (!isRecord3(schema) || schema.type !== "object") {
11279
+ return { fields, refusals: [{ field: null, reason: "schema_not_object" }] };
11280
+ }
11281
+ const properties = schema.properties;
11282
+ if (!isRecord3(properties) || Object.keys(properties).length === 0) {
11283
+ return { fields, refusals: [{ field: null, reason: "schema_has_no_properties" }] };
11284
+ }
11285
+ const refusals = [];
11286
+ for (const [field, property] of Object.entries(properties)) {
11287
+ const primitive = classifyField2(field, property);
11288
+ if ("reason" in primitive) {
11289
+ refusals.push({ field, reason: primitive.reason });
11290
+ continue;
11291
+ }
11292
+ fields.set(field, primitive);
11293
+ }
11294
+ return refusals.length > 0 ? { fields: /* @__PURE__ */ new Map(), refusals } : { fields, refusals };
11295
+ }
11296
+ function classifyField2(field, property) {
11297
+ if (!isRecord3(property)) return { reason: "unsupported_type" };
11298
+ const instructions = describeField2(field, property);
11299
+ if (property.enum !== void 0) {
11300
+ const declared = property.enum;
11301
+ if (!Array.isArray(declared) || declared.some((option) => typeof option !== "string")) {
11302
+ return { reason: "unsupported_type" };
11303
+ }
11304
+ const options = [...new Set(declared)];
11305
+ if (options.length < 2) return { reason: "too_few_options" };
11306
+ return { kind: "choice", instructions, options };
11307
+ }
11308
+ if (property.type === "boolean") return { kind: "noul", instructions };
11309
+ if (property.type === "integer") {
11310
+ const minimum = property.minimum;
11311
+ const maximum = property.maximum;
11312
+ if (!Number.isInteger(minimum) || !Number.isInteger(maximum)) {
11313
+ return { reason: "integer_not_bounded" };
11314
+ }
11315
+ const levels = maximum - minimum + 1;
11316
+ if (levels < DECISION_SCORE_MIN_LEVELS2) return { reason: "too_few_options" };
11317
+ if (levels > DECISION_SCORE_MAX_LEVELS2) return { reason: "integer_range_too_large" };
11318
+ return { kind: "score", instructions, minimum, levels };
11319
+ }
11320
+ return { reason: "unsupported_type" };
11321
+ }
11322
+ function toQuestion2(primitive) {
11323
+ switch (primitive.kind) {
11324
+ case "choice":
11325
+ return {
11326
+ type: "choice",
11327
+ instructions: primitive.instructions,
11328
+ // A JSON Schema enum carries no per-option text, so each option stands as
11329
+ // its own description. The distinguishing detail an option needs belongs
11330
+ // in the field's `description`, which is the instructions.
11331
+ criteria: Object.fromEntries(primitive.options.map((option) => [option, option]))
11332
+ };
11333
+ case "noul":
11334
+ return { type: "noul", instructions: primitive.instructions };
11335
+ case "score":
11336
+ return {
11337
+ type: "score",
11338
+ instructions: primitive.instructions,
11339
+ criteria: scoreLevels2(primitive.minimum, primitive.levels)
11340
+ };
11341
+ }
11342
+ }
11343
+ function describeField2(field, property) {
11344
+ const description = isRecord3(property) ? property.description : void 0;
11345
+ return typeof description === "string" && description.trim() !== "" ? description : field;
11346
+ }
11347
+ function scoreLevels2(minimum, levels) {
11348
+ return Array.from({ length: levels }, (_, index) => String(minimum + index));
11349
+ }
11350
+ function isRecord3(value) {
11351
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11352
+ }
11353
+ function isDecisionsModel2(modelId) {
11354
+ if (typeof modelId !== "string") return false;
11355
+ const normalized = modelId.trim().toLowerCase();
11356
+ return DECISIONS_MODEL_PREFIXES2.some((prefix) => normalized.startsWith(prefix));
11357
+ }
11358
+ function checkDecisionsModelBinding2(agent) {
11359
+ if (!isDecisionsModel2(agent.model)) return null;
11360
+ const model = String(agent.model);
11361
+ const role = agent.agent_role;
11362
+ if (!DECISION_CAPABLE_AGENT_ROLES2.includes(role)) {
11363
+ return refuse2(
11364
+ "role_produces_text",
11365
+ `agent_role "${String(role)}" produces text, which the decisions model "${model}" does not return. A decisions model binds to: ${DECISION_CAPABLE_AGENT_ROLES2.join(", ")}.`
11366
+ );
11367
+ }
11368
+ if (agent.response_format !== "json_schema") {
11369
+ return refuse2(
11370
+ "response_format_not_json_schema",
11371
+ `response_format must be "json_schema" to bind the decisions model "${model}".`
11372
+ );
11373
+ }
11374
+ if (agent.schema_json === null || agent.schema_json === void 0 || agent.schema_json === "") {
11375
+ return refuse2(
11376
+ "schema_missing",
11377
+ `schema_json is required to bind the decisions model "${model}": a decisions model answers questions, and the schema is where they come from.`
11378
+ );
11379
+ }
11380
+ let schema = agent.schema_json;
11381
+ if (typeof schema === "string") {
11382
+ try {
11383
+ schema = JSON.parse(schema);
11384
+ } catch {
11385
+ return refuse2(
11386
+ "schema_unreadable",
11387
+ `schema_json is not valid JSON, so the decisions model "${model}" cannot be bound.`
11388
+ );
11389
+ }
11390
+ }
11391
+ const mapped = schemaToQuestions2(schema);
11392
+ if (mapped.ok) return null;
11393
+ const detail = mapped.refusals.map((refusal2) => `${refusal2.field ?? "(schema)"}: ${refusal2.reason}`).join("; ");
11394
+ return {
11395
+ reason: "schema_not_closed_set",
11396
+ schema_refusals: mapped.refusals,
11397
+ message: `schema_json is not closed-set, so the decisions model "${model}" cannot answer it \u2014 ${detail}. A decisions model answers enum, boolean and bounded-integer fields only.`
11398
+ };
11399
+ }
11400
+ function refuse2(reason, message) {
11401
+ return { reason, schema_refusals: [], message };
11402
+ }
11403
+ function readAgentSettingsModel2(agentSettings) {
11404
+ let settings = agentSettings;
11405
+ if (typeof settings === "string") {
11406
+ try {
11407
+ settings = JSON.parse(settings);
11408
+ } catch {
11409
+ return null;
11410
+ }
11411
+ }
11412
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return null;
11413
+ const model = settings.model;
11414
+ return typeof model === "string" ? model : null;
11415
+ }
11416
+ function resolveMonitorTrigger2(monitorConfig) {
11417
+ if (!monitorConfig || typeof monitorConfig !== "object") return "idle";
11418
+ const raw = monitorConfig.trigger;
11419
+ return MONITOR_TRIGGERS2.includes(raw) ? raw : "idle";
11420
+ }
11421
+ function isFiringTrigger2(trigger) {
11422
+ return trigger !== "manual";
11423
+ }
11424
+ function monitorConfigNeedsDelay2(monitorConfig) {
11425
+ if (!monitorConfig || typeof monitorConfig !== "object") return false;
11426
+ return resolveMonitorTrigger2(monitorConfig) === "idle" && monitorConfig.delay_seconds === void 0;
11427
+ }
11428
+ function monitorInputShapingKeysOnIdle2(monitorConfig) {
11429
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
11430
+ if (resolveMonitorTrigger2(monitorConfig) !== "idle") return [];
11431
+ const config = monitorConfig;
11432
+ return MONITOR_INPUT_SHAPING_KEYS2.filter((key) => config[key] !== void 0);
11433
+ }
11434
+ function monitorRuleKeysOffTrigger2(monitorConfig) {
11435
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
11436
+ if (MONITOR_RULE_TRIGGERS2.includes(resolveMonitorTrigger2(monitorConfig))) return [];
11437
+ const config = monitorConfig;
11438
+ return MONITOR_RULE_KEYS2.filter((key) => config[key] !== void 0);
11439
+ }
11440
+ function monitorRuleActionKinds2(trigger) {
11441
+ return trigger === "assistant_reply" ? MONITOR_ASSISTANT_REPLY_ACTION_KINDS2 : MONITOR_USER_MESSAGE_ACTION_KINDS2;
11442
+ }
11443
+ function monitorActionKindMessage2(kind, trigger) {
11444
+ const allowed = monitorRuleActionKinds2(trigger).join(", ");
11445
+ if (trigger === "assistant_reply") {
11446
+ return `an assistant_reply rule cannot select "${kind}". Use one of: ${allowed}.`;
11447
+ }
11448
+ return `a monitor rule cannot select "${kind}": it acts before the reply exists. Use one of: ${allowed}.`;
11449
+ }
11450
+ function monitorRuleReentryTrack2(toolName) {
11451
+ return MONITOR_RULE_REENTRY_TRACKS2.get(toolName);
11452
+ }
11453
+ function monitorRuleToolNotAllowedMessage2(toolName, trigger) {
11454
+ if (trigger === "assistant_reply" && isReplyGateRefusedToolName2(toolName)) {
11455
+ return `an assistant_reply rule cannot call "${toolName}": the reply already exists when this monitor runs, so there is no turn left for it to act on. Put it on a user_message monitor instead, which runs before the answering agent.`;
11456
+ }
11457
+ const callable = trigger === "assistant_reply" ? MONITOR_RULE_ALLOWED_NATIVE_TOOLS2.filter((name) => !isReplyGateRefusedToolName2(name)) : MONITOR_RULE_ALLOWED_NATIVE_TOOLS2;
11458
+ return `a ${trigger} rule cannot call "${toolName}". Rules can call ${callable.join(", ")}, and this hub's own external HTTP and MCP tools. Assign anything else to the answering agent instead.`;
11459
+ }
11460
+ function isReplyGateRefusedToolName2(toolName) {
11461
+ return monitorRuleReentryTrack2(toolName) === "agent" || toolName === INSERT_NOTE_TOOL_NAME2;
11462
+ }
11463
+ function isRefusedNativeToolName2(toolName, trigger) {
11464
+ if (!NATIVE_TOOL_NAMES2.has(toolName)) return false;
11465
+ if (!MONITOR_RULE_ALLOWED_NATIVE_TOOLS2.includes(toolName)) return true;
11466
+ return trigger === "assistant_reply" && isReplyGateRefusedToolName2(toolName);
11467
+ }
11468
+ function monitorRuleActions2(monitorConfig) {
11469
+ if (!monitorConfig || typeof monitorConfig !== "object") return [];
11470
+ const config = monitorConfig;
11471
+ const found = [];
11472
+ const visit = (action, path35) => {
11473
+ if (!action || typeof action !== "object" || Array.isArray(action)) return;
11474
+ found.push({ action, path: path35 });
11475
+ };
11476
+ if (Array.isArray(config.rules)) {
11477
+ config.rules.forEach((rule, index) => {
11478
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return;
11479
+ visit(rule.action, ["rules", index, "action"]);
11480
+ });
11481
+ }
11482
+ visit(config.fallback, ["fallback"]);
11483
+ return found;
11484
+ }
11485
+ function collectMonitorRuleIssues2(monitorConfig) {
11486
+ const offTrigger = monitorRuleKeysOffTrigger2(monitorConfig).map((key) => ({ path: [key], message: MONITOR_RULE_TRIGGER_MESSAGE2 }));
11487
+ if (offTrigger.length > 0) return offTrigger;
11488
+ const trigger = resolveMonitorTrigger2(monitorConfig);
11489
+ const kinds = monitorRuleActionKinds2(trigger);
11490
+ const issues = [];
11491
+ for (const { action, path: path35 } of monitorRuleActions2(monitorConfig)) {
11492
+ const kind = action.kind;
11493
+ if (typeof kind === "string" && !kinds.includes(kind)) {
11494
+ issues.push({ path: [...path35, "kind"], message: monitorActionKindMessage2(kind, trigger) });
11495
+ }
11496
+ const toolName = action.tool_name;
11497
+ if (kind === "call_tool" && typeof toolName === "string" && isRefusedNativeToolName2(toolName, trigger)) {
11498
+ issues.push({ path: [...path35, "tool_name"], message: monitorRuleToolNotAllowedMessage2(toolName, trigger) });
11499
+ continue;
11500
+ }
11501
+ if (kind === "call_tool" && toolName === INSERT_NOTE_TOOL_NAME2) {
11502
+ issues.push(...insertNoteArgumentIssues2(action, path35));
11503
+ }
11504
+ if (kind === "call_tool" && toolName === RUN_MONITOR_TOOL_NAME2) {
11505
+ const callee = readRunMonitorCallee2(action);
11506
+ if (!callee.ok) {
11507
+ issues.push({
11508
+ path: [...path35, "args", "monitor_name"],
11509
+ message: runMonitorCalleeMessage2(callee.reason)
11510
+ });
11511
+ }
11512
+ }
11513
+ }
11514
+ return issues;
11515
+ }
11516
+ function readInsertNoteTemplate2(action) {
11517
+ const read = readConstStringArg2(action, "template");
11518
+ if (!read.ok) {
11519
+ return {
11520
+ ok: false,
11521
+ reason: read.reason === "missing" ? "note_template_missing" : read.reason === "not_const" ? "note_template_not_const" : "note_template_empty"
11522
+ };
11523
+ }
11524
+ if (read.value.length > MONITOR_NOTE_TEMPLATE_MAX2) {
11525
+ return { ok: false, reason: "note_template_too_long" };
11526
+ }
11527
+ return { ok: true, template: read.value };
11528
+ }
11529
+ function readConstStringArg2(action, argName) {
11530
+ const args2 = action && typeof action === "object" ? action.args : void 0;
11531
+ const arg = args2 && typeof args2 === "object" ? args2[argName] : void 0;
11532
+ if (arg === void 0) return { ok: false, reason: "missing" };
11533
+ const constValue = arg && typeof arg === "object" && !Array.isArray(arg) ? arg.const : void 0;
11534
+ if (typeof constValue !== "string") return { ok: false, reason: "not_const" };
11535
+ if (constValue.trim() === "") return { ok: false, reason: "empty" };
11536
+ return { ok: true, value: constValue };
11537
+ }
11538
+ function readRunMonitorCallee2(action) {
11539
+ const read = readConstStringArg2(action, "monitor_name");
11540
+ if (read.ok) return { ok: true, name: read.value };
11541
+ return {
11542
+ ok: false,
11543
+ reason: read.reason === "missing" ? "callee_missing" : read.reason === "not_const" ? "callee_not_const" : "callee_empty"
11544
+ };
11545
+ }
11546
+ function runMonitorCalleeMessage2(reason) {
11547
+ switch (reason) {
11548
+ case "callee_missing":
11549
+ return "run_monitor needs a monitor_name: the monitor to run.";
11550
+ case "callee_not_const":
11551
+ return "run_monitor's monitor_name must be a const you write \u2014 it chooses which monitor runs, so it cannot come from a variable.";
11552
+ case "callee_empty":
11553
+ return "run_monitor's monitor_name is empty \u2014 name the monitor to run.";
11554
+ }
11555
+ }
11556
+ function insertNoteTemplateMessage2(reason) {
11557
+ switch (reason) {
11558
+ case "note_template_missing":
11559
+ return "insert_note needs a template: the note text to put in front of the answering agent.";
11560
+ case "note_template_not_const":
11561
+ return "insert_note's template must be a const string you write \u2014 the answering agent reads it as instructions, so it cannot come from a variable. Use {{variable_name}} inside it to substitute this monitor's variables.";
11562
+ case "note_template_empty":
11563
+ return "insert_note's template is empty \u2014 remove the rule, or give it something to say.";
11564
+ case "note_template_too_long":
11565
+ return `insert_note's template is longer than the ${MONITOR_NOTE_TEMPLATE_MAX2}-character limit.`;
11566
+ }
11567
+ }
11568
+ function insertNoteArgumentIssues2(action, path35) {
11569
+ const result = readInsertNoteTemplate2(action);
11570
+ if (result.ok) return [];
11571
+ return [{
11572
+ path: [...path35, "args", "template"],
11573
+ message: insertNoteTemplateMessage2(result.reason)
11574
+ }];
11575
+ }
11576
+ function refineDecisionsModelBinding2(body, ctx) {
11577
+ const agent = body;
11578
+ const refusal2 = checkDecisionsModelBinding2({
11579
+ model: readAgentSettingsModel2(agent.agent_settings),
11580
+ agent_role: agent.agent_role,
11581
+ response_format: agent.response_format,
11582
+ schema_json: agent.schema_json
11583
+ });
11584
+ if (refusal2) {
11585
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["agent_settings", "model"], message: refusal2.message });
11586
+ }
11587
+ }
9853
11588
  function visibleLength2(s) {
9854
11589
  return s.replace(INVISIBLE_NAME_CHARS2, "").length;
9855
11590
  }
@@ -9969,7 +11704,7 @@ function extractToolParameters2(toolConfig) {
9969
11704
  if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return void 0;
9970
11705
  return cfg.parameters;
9971
11706
  }
9972
- function isRecord2(v) {
11707
+ function isRecord22(v) {
9973
11708
  return typeof v === "object" && v !== null && !Array.isArray(v);
9974
11709
  }
9975
11710
  function isPrimitive2(v) {
@@ -9991,7 +11726,7 @@ function validateSchema2(schema, path35, errors, opts = {}) {
9991
11726
  });
9992
11727
  return;
9993
11728
  }
9994
- if (!isRecord2(schema)) {
11729
+ if (!isRecord22(schema)) {
9995
11730
  errors.push({
9996
11731
  path: path35,
9997
11732
  message: `expected object, got ${schema === null ? "null" : typeof schema}`
@@ -10053,7 +11788,7 @@ function validateSchema2(schema, path35, errors, opts = {}) {
10053
11788
  }
10054
11789
  }
10055
11790
  if ("properties" in schema) {
10056
- if (!isRecord2(schema.properties)) {
11791
+ if (!isRecord22(schema.properties)) {
10057
11792
  errors.push({
10058
11793
  path: `${path35}.properties`,
10059
11794
  message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
@@ -10072,7 +11807,7 @@ function validateSchema2(schema, path35, errors, opts = {}) {
10072
11807
  }
10073
11808
  }
10074
11809
  for (const key of SUBSCHEMA_OBJECT_KEYWORDS2) {
10075
- if (key in schema && isRecord2(schema[key])) {
11810
+ if (key in schema && isRecord22(schema[key])) {
10076
11811
  validateSchema2(schema[key], `${path35}.${key}`, errors, { depth: depth + 1 });
10077
11812
  }
10078
11813
  }
@@ -10084,7 +11819,7 @@ function validateSchema2(schema, path35, errors, opts = {}) {
10084
11819
  }
10085
11820
  for (const key of SUBSCHEMA_MAP_KEYWORDS2) {
10086
11821
  const map = schema[key];
10087
- if (isRecord2(map)) {
11822
+ if (isRecord22(map)) {
10088
11823
  for (const [name, sub] of Object.entries(map)) {
10089
11824
  validateSchema2(sub, `${path35}.${key}.${name}`, errors, { depth: depth + 1 });
10090
11825
  }
@@ -10114,7 +11849,7 @@ function schemaTypeIncludes2(schema, wanted) {
10114
11849
  }
10115
11850
  function validateToolInputSchema2(schema) {
10116
11851
  const errors = [];
10117
- if (!isRecord2(schema)) {
11852
+ if (!isRecord22(schema)) {
10118
11853
  errors.push({
10119
11854
  path: "",
10120
11855
  message: `expected object at root, got ${schema === null ? "null" : typeof schema}`
@@ -10574,48 +12309,161 @@ function refineHubAsCodeResources2(config, ctx) {
10574
12309
  });
10575
12310
  }
10576
12311
  }
10577
- function refineHubAsCodeDelegation2(config, ctx) {
12312
+ function refineHubAsCodeDelegation2(config, ctx) {
12313
+ const agents = config?.agents;
12314
+ if (!Array.isArray(agents)) return;
12315
+ agents.forEach((agent, agentIndex) => {
12316
+ const delegation = agent?.tools?.delegation;
12317
+ if (!Array.isArray(delegation)) return;
12318
+ delegation.forEach((entry, delIndex) => {
12319
+ const del = entry;
12320
+ if (del.allow_consultant_chain !== void 0) {
12321
+ const chainPath = ["agents", agentIndex, "tools", "delegation", delIndex, "allow_consultant_chain"];
12322
+ if (typeof del.allow_consultant_chain !== "boolean") {
12323
+ ctx.addIssue({
12324
+ code: external_exports.ZodIssueCode.custom,
12325
+ path: chainPath,
12326
+ message: "allow_consultant_chain must be a boolean"
12327
+ });
12328
+ } else if (del.tool !== "start_consult_thread") {
12329
+ ctx.addIssue({
12330
+ code: external_exports.ZodIssueCode.custom,
12331
+ path: chainPath,
12332
+ message: `allow_consultant_chain applies only to \`tool: start_consult_thread\` (got "${String(del.tool)}")`
12333
+ });
12334
+ }
12335
+ }
12336
+ if (del.context_boundary === void 0) return;
12337
+ const path35 = ["agents", agentIndex, "tools", "delegation", delIndex, "context_boundary"];
12338
+ if (del.type !== "hub") {
12339
+ ctx.addIssue({
12340
+ code: external_exports.ZodIssueCode.custom,
12341
+ path: path35,
12342
+ message: `context_boundary applies only to \`type: hub\` delegations (got type "${String(del.type)}")`
12343
+ });
12344
+ return;
12345
+ }
12346
+ if (!CONTEXT_BOUNDARIES2.includes(del.context_boundary)) {
12347
+ ctx.addIssue({
12348
+ code: external_exports.ZodIssueCode.custom,
12349
+ path: path35,
12350
+ message: `context_boundary must be one of: ${CONTEXT_BOUNDARIES2.join(", ")}`
12351
+ });
12352
+ }
12353
+ });
12354
+ });
12355
+ }
12356
+ function refineHubAsCodeFlagConditions2(config, ctx) {
12357
+ const agents = config?.agents;
12358
+ if (!Array.isArray(agents)) return;
12359
+ const checkList = (list, basePath) => {
12360
+ if (!Array.isArray(list)) return;
12361
+ list.forEach((row, rowIndex) => {
12362
+ const operator = row?.operator;
12363
+ if (!FLAG_CONDITION_OPERATORS2.includes(operator)) {
12364
+ ctx.addIssue({
12365
+ code: external_exports.ZodIssueCode.custom,
12366
+ path: [...basePath, rowIndex, "operator"],
12367
+ message: `operator must be one of: ${FLAG_CONDITION_OPERATORS2.join(", ")}`
12368
+ });
12369
+ }
12370
+ });
12371
+ };
12372
+ agents.forEach((agent, agentIndex) => {
12373
+ const a = agent;
12374
+ checkList(a?.flag_conditions, ["agents", agentIndex, "flag_conditions"]);
12375
+ const monitorConfig = a?.monitor_config;
12376
+ if (monitorConfig && typeof monitorConfig === "object" && !Array.isArray(monitorConfig)) {
12377
+ const base = ["agents", agentIndex, "monitor_config"];
12378
+ checkList(
12379
+ monitorConfig.flag_conditions,
12380
+ [...base, "flag_conditions"]
12381
+ );
12382
+ const rules = monitorConfig.rules;
12383
+ if (Array.isArray(rules)) {
12384
+ rules.forEach((rule, ruleIndex) => {
12385
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return;
12386
+ checkList(rule.when, [...base, "rules", ruleIndex, "when"]);
12387
+ });
12388
+ }
12389
+ }
12390
+ });
12391
+ }
12392
+ function refineHubAsCodeMonitorConfig2(config, ctx) {
12393
+ const agents = config?.agents;
12394
+ if (!Array.isArray(agents)) return;
12395
+ agents.forEach((agent, agentIndex) => {
12396
+ const monitorConfig = agent?.monitor_config;
12397
+ if (!monitorConfig || typeof monitorConfig !== "object" || Array.isArray(monitorConfig)) return;
12398
+ const basePath = ["agents", agentIndex, "monitor_config"];
12399
+ const checkKeyValues = (keys, schemas) => {
12400
+ for (const key of keys) {
12401
+ const value = monitorConfig[key];
12402
+ if (value === void 0) continue;
12403
+ const parsed = schemas[key].safeParse(value);
12404
+ if (!parsed.success) {
12405
+ ctx.addIssue({
12406
+ code: external_exports.ZodIssueCode.custom,
12407
+ path: [...basePath, key],
12408
+ message: parsed.error?.issues[0]?.message ?? `invalid ${key}`
12409
+ });
12410
+ }
12411
+ }
12412
+ };
12413
+ const trigger = monitorConfig.trigger;
12414
+ if (trigger !== void 0 && !MONITOR_TRIGGERS2.includes(trigger)) {
12415
+ ctx.addIssue({
12416
+ code: external_exports.ZodIssueCode.custom,
12417
+ path: [...basePath, "trigger"],
12418
+ message: `trigger must be one of: ${MONITOR_TRIGGERS2.join(", ")}`
12419
+ });
12420
+ return;
12421
+ }
12422
+ if (monitorConfigNeedsDelay2(monitorConfig)) {
12423
+ ctx.addIssue({
12424
+ code: external_exports.ZodIssueCode.custom,
12425
+ path: [...basePath, "delay_seconds"],
12426
+ message: MONITOR_IDLE_DELAY_REQUIRED_MESSAGE2
12427
+ });
12428
+ }
12429
+ for (const key of monitorInputShapingKeysOnIdle2(monitorConfig)) {
12430
+ ctx.addIssue({
12431
+ code: external_exports.ZodIssueCode.custom,
12432
+ path: [...basePath, key],
12433
+ message: MONITOR_INPUT_SHAPING_IDLE_MESSAGE2
12434
+ });
12435
+ }
12436
+ checkKeyValues(MONITOR_INPUT_SHAPING_KEYS2, MONITOR_INPUT_SHAPING_SCHEMAS2);
12437
+ checkKeyValues(MONITOR_RULE_KEYS2, MONITOR_RULE_SCHEMAS2);
12438
+ for (const issue of collectMonitorRuleIssues2(monitorConfig)) {
12439
+ ctx.addIssue({
12440
+ code: external_exports.ZodIssueCode.custom,
12441
+ path: [...basePath, ...issue.path],
12442
+ message: issue.message
12443
+ });
12444
+ }
12445
+ });
12446
+ }
12447
+ function refineHubAsCodeDecisionsModel2(config, ctx) {
10578
12448
  const agents = config?.agents;
10579
12449
  if (!Array.isArray(agents)) return;
10580
12450
  agents.forEach((agent, agentIndex) => {
10581
- const delegation = agent?.tools?.delegation;
10582
- if (!Array.isArray(delegation)) return;
10583
- delegation.forEach((entry, delIndex) => {
10584
- const del = entry;
10585
- if (del.allow_consultant_chain !== void 0) {
10586
- const chainPath = ["agents", agentIndex, "tools", "delegation", delIndex, "allow_consultant_chain"];
10587
- if (typeof del.allow_consultant_chain !== "boolean") {
10588
- ctx.addIssue({
10589
- code: external_exports.ZodIssueCode.custom,
10590
- path: chainPath,
10591
- message: "allow_consultant_chain must be a boolean"
10592
- });
10593
- } else if (del.tool !== "start_consult_thread") {
10594
- ctx.addIssue({
10595
- code: external_exports.ZodIssueCode.custom,
10596
- path: chainPath,
10597
- message: `allow_consultant_chain applies only to \`tool: start_consult_thread\` (got "${String(del.tool)}")`
10598
- });
10599
- }
10600
- }
10601
- if (del.context_boundary === void 0) return;
10602
- const path35 = ["agents", agentIndex, "tools", "delegation", delIndex, "context_boundary"];
10603
- if (del.type !== "hub") {
10604
- ctx.addIssue({
10605
- code: external_exports.ZodIssueCode.custom,
10606
- path: path35,
10607
- message: `context_boundary applies only to \`type: hub\` delegations (got type "${String(del.type)}")`
10608
- });
10609
- return;
10610
- }
10611
- if (!CONTEXT_BOUNDARIES2.includes(del.context_boundary)) {
10612
- ctx.addIssue({
10613
- code: external_exports.ZodIssueCode.custom,
10614
- path: path35,
10615
- message: `context_boundary must be one of: ${CONTEXT_BOUNDARIES2.join(", ")}`
10616
- });
10617
- }
10618
- });
12451
+ const a = agent;
12452
+ if (!a) return;
12453
+ const responseFormat = a.response_format;
12454
+ const refusal2 = checkDecisionsModelBinding2({
12455
+ model: readAgentSettingsModel2(a.settings),
12456
+ agent_role: a.role,
12457
+ response_format: responseFormat ? "json_schema" : "text",
12458
+ schema_json: responseFormat?.schema_json ?? null
12459
+ });
12460
+ if (refusal2) {
12461
+ ctx.addIssue({
12462
+ code: external_exports.ZodIssueCode.custom,
12463
+ path: ["agents", agentIndex, "settings", "model"],
12464
+ message: refusal2.message
12465
+ });
12466
+ }
10619
12467
  });
10620
12468
  }
10621
12469
  function identifyTokenType(token) {
@@ -10657,7 +12505,7 @@ function findStepBoundaries(transcript) {
10657
12505
  }
10658
12506
  return out;
10659
12507
  }
10660
- 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, REKOR_ACTOR_TYPE_HEADER2, REKOR_ACTOR_ID_HEADER2, REKOR_ACTOR_LABEL_HEADER2, REKOR_ACTOR_ORG_HEADER2, REKOR_ACTOR_ADMIN_HEADER2, REKOR_CLIENT_HEADER2, REKOR_TOOL_ID_HEADER2, REKOR_CLIENTS2, rekorClientSchema2, MAX_REKOR_TOOL_ID_LENGTH2, rekorToolIdSchema2, DATA_PROXY_MOUNT2, DATA_PROXY_PREFIX2, DATA_PROXY_ORG_QUERY_PARAM2, MAX_PROVISIONING_BODY_BYTES2, dataProxyQuery2, rekorAttestation2, REQUIRED_REKOR_ATTESTATION_HEADERS2, BASE_DESTRUCTION_MOUNT2, BASE_DESTRUCTION_PREFIX2, BASE_DESTRUCTION_CONFIRM_PATH2, baseDestructionConfirmBody2, BASE_DESTRUCTION_GRACE_MS2, BASE_DESTRUCTION_CONFIRM_TTL_MS2, BASE_DESTRUCTION_ORG_QUERY_PARAM2, baseDestructionQuery2, baseDestructionParam2, baseDestructionInitiateBody2, baseDestructionStatus2, baseDestructionRequest2, baseDestructionListResponse2, baseDestructionInitiateResponse2, baseDestructionConfirmResponse2, baseDestructionCancelResponse2, ENDPOINT_CONFIGS, AUTH_TYPE2, ORG_CREDENTIAL_AUTH_TYPES2, AUTH_TYPE_DISPLAY2, LEGACY_AUTH_TYPE_MAP2, VALID_AUTH_TYPES2, AGENT_ROLES2, SAFE_USER_ID_REGEX2, SUPPORTED_LOCALES2, uuidSchema2, paginationSchema3, timestampSchema2, idParamSchema2, clientOptionalString2, clientOptionalBoolean2, clientOptionalNumber2, messageAttachment2, MAX_MESSAGE_BODY_BYTES2, MAX_ATTACHMENTS_PER_MESSAGE2, MAX_AUDIO_FILE_BASE64_BYTES2, primaryRegionSchema2, placementSourceSchema2, orgIdParam2, orgAdminIdParam2, createOrganizationBody2, updateOrganizationBody2, addOrgAdminBody2, SUMMARIZATION_THRESHOLD_MIN2, SUMMARIZATION_THRESHOLD_MAX2, PREVIOUS_CONVERSATIONS_MAX2, previousConversationsCountField2, 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, followupTypeSchema2, EVENT_RELATIVE_FOLLOWUP_TYPES2, followupTimeUnitSchema2, followupIdSchema2, SELECTABLE_FOLLOWUP_TYPES2, MAX_LINKED_FOLLOWUP_TARGETS_PER_STATUS2, followupSchema2, MAX_OUTCOMES_PER_STATUS2, kanbanOutcomeSchema2, EXCLUSIVE_FLAG_PAIRS2, kanbanStatusSchema2, kanbanStatusesArraySchema2, laneSchema2, lanesArraySchema2, hubIdParam2, hubAdminIdParam2, hubUserIdParam2, hubIdentityIdParam2, hubTeamIdParam2, hubTeamUserDeleteParam2, hubSchemaQuery2, PREVIEW_LABEL_MAX_LENGTH2, MAX_ENDED_INDEX_RETENTION_DAYS2, MAX_EVAL_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, CONTEXT_BOUNDARIES2, contextBoundarySchema2, 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, ORG_TAG_NAME_REGEX2, ORG_TAG_NAME_MAX_LENGTH2, createOrgTagBody2, updateOrgTagBody2, orgCredentialIdParam2, listOrgCredentialsQuery2, createOrgCredentialBody2, updateOrgCredentialBody2, BASE_CREDENTIAL_NAME_MAX2, baseCredentialNameSchema2, createBaseCredentialLinkBody2, baseCredentialLinkParams2, baseCredentialLinkQuery2, 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, analyticsSqlTable2, analyticsSqlSchemaQuery2, EVAL_PACING_PRESET_NAMES2, PACING_INTERVAL_MIN_MS2, PACING_INTERVAL_MAX_MS2, EVAL_MAX_RUNS_PER_SCENARIO2, EVAL_RUN_DEADLINE_MIN_MS2, EVAL_RUN_DEADLINE_MAX_MS2, ISO_UTC_RE2, paginationSchema22, evalIdParam2, sessionIdParam2, scenarioSetIdParam2, hubIdQuery2, evalDateContextSchema2, EVAL_FIXTURE_NAME_MAX_LENGTH2, evalFixtureOverrideSchema2, runSessionQuery2, EVAL_INPUT_ROLES2, EVAL_INPUT_ROLE_LIST2, ROLE_ECHO_MAX2, EVAL_INITIAL_STATE_SCOPES2, MAX_INITIAL_STATE_ENTRIES2, evalInitialStateEntry2, createEvalBody2, updateEvalBody2, EVAL_LIST_MAX_LIMIT2, evalListLimit2, getEvalsQuery2, toggleEvalBody2, bulkToggleEvalsBody2, pacingConfigSchema2, EVAL_MAX_SELECTED_SCENARIOS2, evalScenarioSelectionEntrySchema2, evalScenarioSelectionSchema2, 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, evalSessionConfigResponseSchema2, evalSessionResponseSchema2, evalSessionListConfigSchema2, evalSessionListItemSchema2, seedReleaseStatusSchema2, getConversationsQuery2, getMessagesQuery2, appMessageSenderType2, appMessageReceiverType2, apiChannelMessage2, apiChannelMessageBody2, appMessageBody2, appMessageResponse2, updateUserBody2, updateProfileBody2, pathnameRegex2, updatePreferencesBody2, registerPushTokenBody2, deletePushTokenQuery2, activityQuery2, deletionModeSchema2, deletionRequestBody2, archiveHubIdParam2, archiveConversationParams2, archiveListQuery2, archiveMessageObservabilityParams2, conversationIdParam2, claimBody2, releaseBody2, transferBody2, closeBody2, additionalContextPayloadSchema2, kanbanUpdateBody2, annotateConversationBody2, observabilityParams2, observabilityListParams2, deleteUserConversationsBody2, deleteUserConversationsResponse2, sendMessageBody2, listConversationsQuery2, flagSourceSchema2, getConversationsResponse2, getConversationDetailParams2, getConversationDetailQuery2, getConversationDetailResponse2, listWhatsAppTemplatesQuery2, sendWhatsAppTemplateBody2, copilotSuggestBody2, copilotSuggestResponse2, CONSULT_THREAD_STATUSES2, consultThreadStatus2, CONSULT_THREAD_TITLE_MAX2, consultThreadTitle2, consultThreadSummary2, listConsultThreadsQuery2, listConsultThreadsResponse2, createConsultThreadBody2, createConsultThreadResponse2, consultThreadIdParam2, updateConsultThreadBody2, updateConsultThreadResponse2, CONSULT_AWAIT_TIMEOUT_MS2, billingOrgIdParam2, subscriptionItemParams2, spendCapBody2, growthPacksBody2, portalSessionBody2, checkoutSessionBody2, updateBillingCurrencyBody2, invoicesQuery2, planKeySchema2, billingStatusSchema2, billingPlanTypeSchema2, dataUsageCountersSchema2, REKOR_ENTITLEMENT_CONTRACT_VERSION2, entitlementProjectionSchema2, rekorEntitlementRefreshMessageSchema2, 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, hubAsCodeStateSchema2, hubAsCodeResourceFileSchema2, hubAsCodeResourceSchema2, uuidPattern2, ciUuidSchema2, boundedHubAsCodeResourcesSchema2, hubAsCodeConfigSchema2, ciPullHubIdParam2, ciBranchParam2, ciPullQuery2, ciHubsQuery2, ciLookupQuery2, ciBranchesQuery2, ciSyncBody2, ciPushBody2, ciDiffBody2, ciPublishBody2, ciPublishPreviewBody2, ciSyncMcpBody2, ciOrgResourcesParam2, orgResourcesConfigSchema2, ciOrgResourcesPushBody2, ciOrgResourcesDiffBody2, CI_APPLY_ENTITY_KINDS2, CI_APPLY_OPERATIONS2, ciApplyErrorBaseSchema2, ciApplyErrorSchema2, ciSyncCountSchema2, ciSyncChangesSchema2, ciSyncWarningSchema2, ciSyncResponseSchema2, adminOrgIdParam2, adminOrgAdminIdParam2, adminUserIdParam2, PRICING_PLAN_ID_RE2, adminPlanIdParam2, adminOrganizationsQuery2, adminUserSearchQuery2, kanbanSlugMigrationQuery2, adjustQuotaBody2, updatePlanBody2, addAdminUserBody2, updatePlatformConfigBody2, updateFreeOrgLimitBody2, adminScopedTargetBody2, adminRepairLegacyOrgGrantsBody2, adminRepairCompedPlansBody2, updatePricingPlanBody2, dataExplorerSearchQuery2, dataExplorerOrgIdParam2, dataExplorerHubIdParam2, dataExplorerConvIdParam2, dataExplorerDoInstancesQuery2, dataExplorerNsIdParam2, dataExplorerKvKeysBody2, dataExplorerKvKeyBody2, uuidOrHexId2, dataExplorerDeleteOrgParam2, dataExplorerDeleteHubParam2, dataExplorerDeleteConvParam2, dataExplorerDeleteUserParam2, dataExplorerDeleteQuery2, dataExplorerKvDeleteBody2, 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, META_ENTITY_ID_PATTERN2, metaEntityId2, whatsappCallbackBody2, authMeResponse2, wsTicketResponse2, sessionCheckResponse2, logoutResponse2, magicCodeSendBody2, magicCodeSendResponse2, magicCodeVerifyBody2, magicCodeVerifyResponse2, passwordLoginBody2, 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, jsonSchemaSchema2, hubUserIdentity2, stateValueEntry2, getHubUserContextResponse2, updateHubUserBody2, updateHubUserResponse2, unlockHubUserNameResponse2, stateResetParams2, stateResetResponse2, hubAlertsParam2, hubAlertSeverity2, hubAlert2, hubAlertsListResponse2, noticeSeveritySchema2, noticeStatusSchema2, NOTICE_SCOPE_REGEX2, noticeScopeSchema2, noticeLinkSchema2, createNoticeBody2, updateNoticeBody2, listNoticesQuery2, noticeIdParamSchema2, ADMIN_SKILL_NAME_REGEX2, adminSkillNameParam2, rekorBaseChangeMessageSchema2, WAYAI_WORKSPACE_LAYOUT;
12508
+ 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, REKOR_ACTOR_TYPE_HEADER2, REKOR_ACTOR_ID_HEADER2, REKOR_ACTOR_LABEL_HEADER2, REKOR_ACTOR_ORG_HEADER2, REKOR_ACTOR_ADMIN_HEADER2, REKOR_CLIENT_HEADER2, REKOR_TOOL_ID_HEADER2, REKOR_CLIENTS2, rekorClientSchema2, MAX_REKOR_TOOL_ID_LENGTH2, rekorToolIdSchema2, DATA_PROXY_MOUNT2, DATA_PROXY_PREFIX2, DATA_PROXY_ORG_QUERY_PARAM2, MAX_PROVISIONING_BODY_BYTES2, dataProxyQuery2, rekorAttestation2, REQUIRED_REKOR_ATTESTATION_HEADERS2, BASE_DESTRUCTION_MOUNT2, BASE_DESTRUCTION_PREFIX2, BASE_DESTRUCTION_CONFIRM_PATH2, baseDestructionConfirmBody2, BASE_DESTRUCTION_GRACE_MS2, BASE_DESTRUCTION_CONFIRM_TTL_MS2, BASE_DESTRUCTION_ORG_QUERY_PARAM2, baseDestructionQuery2, baseDestructionParam2, baseDestructionInitiateBody2, baseDestructionStatus2, baseDestructionRequest2, baseDestructionListResponse2, baseDestructionInitiateResponse2, baseDestructionConfirmResponse2, baseDestructionCancelResponse2, ENDPOINT_CONFIGS, AUTH_TYPE2, ORG_CREDENTIAL_AUTH_TYPES2, AUTH_TYPE_DISPLAY2, LEGACY_AUTH_TYPE_MAP2, VALID_AUTH_TYPES2, AGENT_ROLES2, SAFE_USER_ID_REGEX2, SUPPORTED_LOCALES2, uuidSchema2, paginationSchema3, timestampSchema2, idParamSchema2, clientOptionalString2, clientOptionalBoolean2, clientOptionalNumber2, messageAttachment2, MAX_MESSAGE_BODY_BYTES2, MAX_ATTACHMENTS_PER_MESSAGE2, MAX_AUDIO_FILE_BASE64_BYTES2, primaryRegionSchema2, placementSourceSchema2, orgIdParam2, orgAdminIdParam2, createOrganizationBody2, updateOrganizationBody2, addOrgAdminBody2, SUMMARIZATION_THRESHOLD_MIN2, SUMMARIZATION_THRESHOLD_MAX2, PREVIOUS_CONVERSATIONS_MAX2, FLAG_CONDITION_OPERATORS2, DECISION_SCORE_MIN_LEVELS2, DECISION_SCORE_MAX_LEVELS2, DECISIONS_MODEL_PREFIXES2, DECISION_CAPABLE_AGENT_ROLES2, NATIVE_TOOL_SCHEMAS2, WAYAI_CONNECTOR2, BASE_NATIVE_TOOLS2, NATIVE_TOOLS2, NATIVE_TOOL_NAMES2, previousConversationsCountField2, summarizationThresholdField2, flagConditionSchema2, MONITOR_TRIGGERS2, monitorTriggerSchema2, MONITOR_FIRING_TRIGGERS2, MONITOR_DELAY_SECONDS_MIN2, MONITOR_IDLE_DELAY_REQUIRED_MESSAGE2, MONITOR_HISTORY_MESSAGES_MAX2, monitorHistoryMessagesSchema2, monitorIncludeToolResultsSchema2, MONITOR_INPUT_SHAPING_KEYS2, MONITOR_INPUT_SHAPING_SCHEMAS2, MONITOR_INPUT_SHAPING_IDLE_MESSAGE2, monitorArgumentSourceSchema2, MONITOR_NOTE_TEMPLATE_MAX2, monitorActionSchema2, monitorRuleSchema2, MONITOR_RULE_KEYS2, MONITOR_RULE_SCHEMAS2, MONITOR_RULE_TRIGGERS2, MONITOR_RULE_TRIGGER_MESSAGE2, MONITOR_USER_MESSAGE_ACTION_KINDS2, MONITOR_ASSISTANT_REPLY_ACTION_KINDS2, INSERT_NOTE_TOOL_NAME2, RUN_MONITOR_TOOL_NAME2, MONITOR_RULE_ALLOWED_NATIVE_TOOLS2, MONITOR_RULE_REENTRY_TOOLS2, MONITOR_RULE_REENTRY_TRACKS2, monitorConfigField2, flagConditionsField2, 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, followupTypeSchema2, EVENT_RELATIVE_FOLLOWUP_TYPES2, followupTimeUnitSchema2, followupIdSchema2, SELECTABLE_FOLLOWUP_TYPES2, MAX_LINKED_FOLLOWUP_TARGETS_PER_STATUS2, followupSchema2, MAX_OUTCOMES_PER_STATUS2, kanbanOutcomeSchema2, EXCLUSIVE_FLAG_PAIRS2, kanbanStatusSchema2, kanbanStatusesArraySchema2, laneSchema2, lanesArraySchema2, hubIdParam2, hubAdminIdParam2, hubUserIdParam2, hubIdentityIdParam2, hubTeamIdParam2, hubTeamUserDeleteParam2, hubSchemaQuery2, PREVIEW_LABEL_MAX_LENGTH2, MAX_ENDED_INDEX_RETENTION_DAYS2, MAX_EVAL_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, CONTEXT_BOUNDARIES2, contextBoundarySchema2, 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, ORG_TAG_NAME_REGEX2, ORG_TAG_NAME_MAX_LENGTH2, createOrgTagBody2, updateOrgTagBody2, orgCredentialIdParam2, listOrgCredentialsQuery2, createOrgCredentialBody2, updateOrgCredentialBody2, BASE_CREDENTIAL_NAME_MAX2, baseCredentialNameSchema2, createBaseCredentialLinkBody2, baseCredentialLinkParams2, baseCredentialLinkQuery2, 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, analyticsSqlTable2, analyticsSqlSchemaQuery2, EVAL_PACING_PRESET_NAMES2, PACING_INTERVAL_MIN_MS2, PACING_INTERVAL_MAX_MS2, EVAL_MAX_RUNS_PER_SCENARIO2, EVAL_RUN_DEADLINE_MIN_MS2, EVAL_RUN_DEADLINE_MAX_MS2, ISO_UTC_RE2, paginationSchema22, evalIdParam2, sessionIdParam2, scenarioSetIdParam2, hubIdQuery2, evalDateContextSchema2, EVAL_FIXTURE_NAME_MAX_LENGTH2, evalFixtureOverrideSchema2, runSessionQuery2, EVAL_INPUT_ROLES2, EVAL_INPUT_ROLE_LIST2, ROLE_ECHO_MAX2, EVAL_INITIAL_STATE_SCOPES2, MAX_INITIAL_STATE_ENTRIES2, evalInitialStateEntry2, createEvalBody2, updateEvalBody2, EVAL_LIST_MAX_LIMIT2, evalListLimit2, getEvalsQuery2, toggleEvalBody2, bulkToggleEvalsBody2, pacingConfigSchema2, EVAL_MAX_SELECTED_SCENARIOS2, evalScenarioSelectionEntrySchema2, evalScenarioSelectionSchema2, 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, evalSessionConfigResponseSchema2, evalSessionResponseSchema2, evalSessionListConfigSchema2, evalSessionListItemSchema2, seedReleaseStatusSchema2, getConversationsQuery2, getMessagesQuery2, appMessageSenderType2, appMessageReceiverType2, apiChannelMessage2, apiChannelMessageBody2, appMessageBody2, appMessageResponse2, updateUserBody2, updateProfileBody2, pathnameRegex2, updatePreferencesBody2, registerPushTokenBody2, deletePushTokenQuery2, activityQuery2, deletionModeSchema2, deletionRequestBody2, archiveHubIdParam2, archiveConversationParams2, archiveListQuery2, archiveMessageObservabilityParams2, conversationIdParam2, claimBody2, releaseBody2, transferBody2, closeBody2, additionalContextPayloadSchema2, kanbanUpdateBody2, annotateConversationBody2, observabilityParams2, observabilityListParams2, deleteUserConversationsBody2, deleteUserConversationsResponse2, sendMessageBody2, listConversationsQuery2, flagSourceSchema2, getConversationsResponse2, getConversationDetailParams2, getConversationDetailQuery2, getConversationDetailResponse2, listWhatsAppTemplatesQuery2, sendWhatsAppTemplateBody2, copilotSuggestBody2, copilotSuggestResponse2, CONSULT_THREAD_STATUSES2, consultThreadStatus2, CONSULT_THREAD_TITLE_MAX2, consultThreadTitle2, consultThreadSummary2, listConsultThreadsQuery2, listConsultThreadsResponse2, createConsultThreadBody2, createConsultThreadResponse2, consultThreadIdParam2, updateConsultThreadBody2, updateConsultThreadResponse2, CONSULT_AWAIT_TIMEOUT_MS2, billingOrgIdParam2, subscriptionItemParams2, spendCapBody2, growthPacksBody2, portalSessionBody2, checkoutSessionBody2, updateBillingCurrencyBody2, invoicesQuery2, planKeySchema2, billingStatusSchema2, billingPlanTypeSchema2, dataUsageCountersSchema2, REKOR_ENTITLEMENT_CONTRACT_VERSION2, entitlementProjectionSchema2, rekorEntitlementRefreshMessageSchema2, 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, hubAsCodeStateSchema2, hubAsCodeResourceFileSchema2, hubAsCodeResourceSchema2, uuidPattern2, ciUuidSchema2, boundedHubAsCodeResourcesSchema2, hubAsCodeConfigSchema2, ciPullHubIdParam2, ciBranchParam2, ciPullQuery2, ciHubsQuery2, ciLookupQuery2, ciBranchesQuery2, ciSyncBody2, ciPushBody2, ciDiffBody2, ciPublishBody2, ciPublishPreviewBody2, ciSyncMcpBody2, ciOrgResourcesParam2, orgResourcesConfigSchema2, ciOrgResourcesPushBody2, ciOrgResourcesDiffBody2, CI_APPLY_ENTITY_KINDS2, CI_APPLY_OPERATIONS2, ciApplyErrorBaseSchema2, ciApplyErrorSchema2, ciSyncCountSchema2, ciSyncChangesSchema2, ciSyncWarningSchema2, ciSyncResponseSchema2, adminOrgIdParam2, adminOrgAdminIdParam2, adminUserIdParam2, PRICING_PLAN_ID_RE2, adminPlanIdParam2, adminOrganizationsQuery2, adminUserSearchQuery2, kanbanSlugMigrationQuery2, adjustQuotaBody2, updatePlanBody2, addAdminUserBody2, updatePlatformConfigBody2, updateFreeOrgLimitBody2, adminScopedTargetBody2, adminRepairLegacyOrgGrantsBody2, adminRepairCompedPlansBody2, updatePricingPlanBody2, dataExplorerSearchQuery2, dataExplorerOrgIdParam2, dataExplorerHubIdParam2, dataExplorerConvIdParam2, dataExplorerDoInstancesQuery2, dataExplorerNsIdParam2, dataExplorerKvKeysBody2, dataExplorerKvKeyBody2, uuidOrHexId2, dataExplorerDeleteOrgParam2, dataExplorerDeleteHubParam2, dataExplorerDeleteConvParam2, dataExplorerDeleteUserParam2, dataExplorerDeleteQuery2, dataExplorerKvDeleteBody2, 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, META_ENTITY_ID_PATTERN2, metaEntityId2, whatsappCallbackBody2, authMeResponse2, wsTicketResponse2, sessionCheckResponse2, logoutResponse2, magicCodeSendBody2, magicCodeSendResponse2, magicCodeVerifyBody2, magicCodeVerifyResponse2, passwordLoginBody2, 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, jsonSchemaSchema2, hubUserIdentity2, stateValueEntry2, getHubUserContextResponse2, updateHubUserBody2, updateHubUserResponse2, unlockHubUserNameResponse2, stateResetParams2, stateResetResponse2, hubAlertsParam2, hubAlertSeverity2, hubAlert2, hubAlertsListResponse2, noticeSeveritySchema2, noticeStatusSchema2, NOTICE_SCOPE_REGEX2, noticeScopeSchema2, noticeLinkSchema2, createNoticeBody2, updateNoticeBody2, listNoticesQuery2, noticeIdParamSchema2, ADMIN_SKILL_NAME_REGEX2, adminSkillNameParam2, rekorBaseChangeMessageSchema2, WAYAI_WORKSPACE_LAYOUT, decisionAnswerSchema, decisionsResponseSchema;
10661
12509
  var init_dist = __esm({
10662
12510
  "../../packages/core/dist/index.js"() {
10663
12511
  "use strict";
@@ -10713,6 +12561,7 @@ var init_dist = __esm({
10713
12561
  init_zod();
10714
12562
  init_zod();
10715
12563
  init_zod();
12564
+ init_zod();
10716
12565
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10717
12566
  HEX_RE = /^[0-9a-f]{16,}$/;
10718
12567
  WORKOS_ID_RE = /^(?:user|org)_[0-9A-HJKMNP-TV-Z]{26}$/;
@@ -10862,13 +12711,21 @@ var init_dist = __esm({
10862
12711
  };
10863
12712
  MEDIA_PROVIDER_HTTP_BRAND = /* @__PURE__ */ Symbol.for("wayai.MediaProviderHttpError");
10864
12713
  MediaProviderHttpError = class extends Error {
10865
- constructor(provider, status) {
12714
+ constructor(provider, status, options = {}) {
10866
12715
  super(`${provider} API error (${status})`);
10867
12716
  this.provider = provider;
10868
12717
  this.status = status;
10869
12718
  this.name = "MediaProviderHttpError";
12719
+ this.credentialRejected = options.credentialRejected ?? false;
10870
12720
  }
10871
12721
  [MEDIA_PROVIDER_HTTP_BRAND] = true;
12722
+ /**
12723
+ * The provider said the credential itself was rejected, under a status that does not say so
12724
+ * (Google answers an invalid or expired key with 400 `API_KEY_INVALID`, not 401). Read off
12725
+ * the body at the throw site, which carries only this flag on. Alerting keys on it so the
12726
+ * admin is told to re-enter the key, not that the key is fine.
12727
+ */
12728
+ credentialRejected;
10872
12729
  };
10873
12730
  REKOR_ACTOR_TYPE_HEADER2 = "X-Rekor-Actor-Type";
10874
12731
  REKOR_ACTOR_ID_HEADER2 = "X-Rekor-Actor-Id";
@@ -11032,7 +12889,7 @@ var init_dist = __esm({
11032
12889
  "/api/users/me/deletion": { tier: "STRICT", userLimit: 3, userWindow: 3600, failClosed: true },
11033
12890
  "/api/users/me/deletion/preflight": { tier: "STANDARD", userLimit: 30, failClosed: true },
11034
12891
  "/api/users/deletion/undo": { tier: "STRICT", failClosed: true },
11035
- // Production-base destruction (docs/plans/base-deletion-lifecycle.md D4).
12892
+ // Production-base destruction.
11036
12893
  //
11037
12894
  // Keyed off the CONTRACT constants, not literals: `BASE_DESTRUCTION_CONFIRM_PATH`'s own
11038
12895
  // docstring promises that the mount, the `AUTH_BYPASS_PATHS` entry, the rate-limit key and
@@ -11280,18 +13137,993 @@ var init_dist = __esm({
11280
13137
  SUMMARIZATION_THRESHOLD_MIN2 = 1e3;
11281
13138
  SUMMARIZATION_THRESHOLD_MAX2 = 1e6;
11282
13139
  PREVIOUS_CONVERSATIONS_MAX2 = 20;
13140
+ FLAG_CONDITION_OPERATORS2 = ["=", "!=", ">=", "<=", ">", "<"];
13141
+ DECISION_SCORE_MIN_LEVELS2 = 2;
13142
+ DECISION_SCORE_MAX_LEVELS2 = 10;
13143
+ DECISIONS_MODEL_PREFIXES2 = ["typesafe/jev-", "jev-"];
13144
+ DECISION_CAPABLE_AGENT_ROLES2 = [
13145
+ "monitor",
13146
+ "conversation_evaluator",
13147
+ "message_evaluator"
13148
+ ];
13149
+ NATIVE_TOOL_SCHEMAS2 = {
13150
+ "close_conversation": {
13151
+ "tool_config": {
13152
+ "name": "close_conversation",
13153
+ "description": "Closes the current conversation and marks it as resolved",
13154
+ // Non-strict, mirroring the `update_kanban_status` sibling below: under strict
13155
+ // mode there are no optional fields, so `outcome` outside `required` makes the
13156
+ // provider reject the definition and `outcome` inside `required` forces the
13157
+ // model to emit it on every call. Requiredness is enforced SERVER-side instead
13158
+ // (ops `outcomeRequired`) — and it must stay out of `required` here, or
13159
+ // validateAndSanitizeToolParams would refuse the "retry without an outcome"
13160
+ // recovery path a re-close depends on.
13161
+ "strict": false,
13162
+ "parameters": {
13163
+ "type": "object",
13164
+ "properties": {
13165
+ "outcome": {
13166
+ "type": "string",
13167
+ "enum": "[KANBAN_OUTCOME_VALUES]",
13168
+ "description": "Required only when the hub's terminal (conversation-ending) status declares outcomes: the closing outcome. Available outcomes: [KANBAN_OUTCOME_NAMES]"
13169
+ }
13170
+ },
13171
+ "required": [],
13172
+ "additionalProperties": false
13173
+ }
13174
+ },
13175
+ "tool_instructions": "Use when the user's issue has been fully resolved."
13176
+ },
13177
+ "transfer_to_team": {
13178
+ "tool_config": {
13179
+ "name": "transfer_to_team",
13180
+ "description": "Hand off conversation to a human support team",
13181
+ "strict": true,
13182
+ "parameters": {
13183
+ "type": "object",
13184
+ "properties": {
13185
+ "team_name": {
13186
+ "type": "string",
13187
+ "description": "Name of the team to transfer to"
13188
+ }
13189
+ },
13190
+ "required": [
13191
+ "team_name"
13192
+ ],
13193
+ "additionalProperties": false
13194
+ }
13195
+ },
13196
+ "tool_instructions": "Use when the user needs assistance that requires human intervention. The team will receive the full conversation history."
13197
+ },
13198
+ "delegate_to_hub": {
13199
+ "tool_config": {
13200
+ "name": "delegate_to_hub",
13201
+ "description": "Delegate a request to another hub. The target hub handles it as a task conversation and returns the result asynchronously.",
13202
+ "strict": true,
13203
+ "parameters": {
13204
+ "type": "object",
13205
+ "properties": {
13206
+ "request": {
13207
+ "type": "string",
13208
+ "description": "The request to delegate to the target hub, phrased as a self-contained task."
13209
+ }
13210
+ },
13211
+ "required": [
13212
+ "request"
13213
+ ],
13214
+ "additionalProperties": false
13215
+ }
13216
+ },
13217
+ "tool_instructions": "Use to hand a self-contained request to a configured partner hub. The target hub is set by an admin on the tool; you do not choose it. The result comes back asynchronously \u2014 do not wait for it in this turn."
13218
+ },
13219
+ "start_consult_thread": {
13220
+ "tool_config": {
13221
+ "name": "start_consult_thread",
13222
+ "description": "Ask the consultant configured on this tool a question, in a consult thread the support team can see. The answer may come back in this turn or later.",
13223
+ "strict": true,
13224
+ "parameters": {
13225
+ "type": "object",
13226
+ "properties": {
13227
+ "question": {
13228
+ "type": "string",
13229
+ "description": "What you need from the consultant, phrased so it can be answered without further back-and-forth."
13230
+ },
13231
+ "title": {
13232
+ "type": "string",
13233
+ "description": "A short label for the consult thread (a few words), so the support team can tell parallel consults apart."
13234
+ }
13235
+ },
13236
+ "required": [
13237
+ "question",
13238
+ "title"
13239
+ ],
13240
+ "additionalProperties": false
13241
+ }
13242
+ },
13243
+ "tool_instructions": "Use when answering needs knowledge you do not have and a consultant is configured for it. The consultant is set by an admin; you do not choose it. If the result says the answer will follow, END YOUR TURN \u2014 tell the customer you are checking and will come back to them. You will be brought back automatically when the answer arrives; do not poll or call this tool again for the same question."
13244
+ },
13245
+ "update_kanban_status": {
13246
+ "tool_config": {
13247
+ "name": "update_kanban_status",
13248
+ "description": "Updates the kanban status of the current conversation to track its progress through workflow stages",
13249
+ "strict": false,
13250
+ "parameters": {
13251
+ "type": "object",
13252
+ "properties": {
13253
+ "new_kanban_status": {
13254
+ "type": "string",
13255
+ "enum": "[KANBAN_STATUS_VALUES]",
13256
+ "description": "The new kanban status for the conversation. Available statuses: [KANBAN_STATUS_NAMES]"
13257
+ },
13258
+ "outcome": {
13259
+ "type": "string",
13260
+ "enum": "[KANBAN_OUTCOME_VALUES]",
13261
+ "description": "Required only when moving to the terminal (conversation-ending) status: the closing outcome. Available outcomes: [KANBAN_OUTCOME_NAMES]"
13262
+ },
13263
+ "scheduled_event_date": {
13264
+ "type": "string",
13265
+ "description": 'Scheduled event date in RFC3339 format with timezone (e.g., "2024-01-01T00:00:00-03:00")'
13266
+ },
13267
+ "event_description": {
13268
+ "type": "string",
13269
+ "description": "Description of the scheduled event"
13270
+ },
13271
+ "event_sid": {
13272
+ "type": "string",
13273
+ "description": "External event ID for integration purposes"
13274
+ }
13275
+ },
13276
+ "required": [
13277
+ "new_kanban_status"
13278
+ ],
13279
+ "additionalProperties": false
13280
+ }
13281
+ },
13282
+ "tool_instructions": "Use to update the kanban status of a conversation to reflect its current stage in the workflow."
13283
+ },
13284
+ "schedule_followup": {
13285
+ "tool_config": {
13286
+ "name": "schedule_followup",
13287
+ "description": "Schedules a custom manual followup for the current conversation at an exact future time. The system automatically determines the receiver based on conversation status and hub AI mode.",
13288
+ "strict": true,
13289
+ "parameters": {
13290
+ "type": "object",
13291
+ "properties": {
13292
+ "scheduled_time": {
13293
+ "type": "string",
13294
+ "description": 'Exact time to execute the followup in ISO 8601 format with timezone (e.g., "2024-01-01T14:30:00-03:00"). Must be in the future.'
13295
+ },
13296
+ "message": {
13297
+ "type": "string",
13298
+ "description": 'The followup message to send to the customer. Can include AI instructions in brackets like: "Message text [AI Context: instructions for AI]"'
13299
+ }
13300
+ },
13301
+ "required": [
13302
+ "scheduled_time",
13303
+ "message"
13304
+ ],
13305
+ "additionalProperties": false
13306
+ }
13307
+ },
13308
+ "tool_instructions": "Use to schedule a custom followup message at an exact future time. The caller is responsible for choosing appropriate timing (business hours, etc.). This is separate from automatic kanban-based followups."
13309
+ },
13310
+ "transfer_to_agent": {
13311
+ "tool_config": {
13312
+ "name": "transfer_to_agent",
13313
+ "description": "Hand off conversation to another AI agent",
13314
+ "strict": true,
13315
+ "parameters": {
13316
+ "type": "object",
13317
+ "properties": {
13318
+ "agent_name": {
13319
+ "type": "string",
13320
+ "description": "Name of the AI agent to transfer to"
13321
+ }
13322
+ },
13323
+ "required": [
13324
+ "agent_name"
13325
+ ],
13326
+ "additionalProperties": false
13327
+ }
13328
+ },
13329
+ "tool_instructions": "Use when you need to hand the conversation to another agent on the same track \u2014 a specialist for a specific domain, or back to the entry pilot/copilot acting as a router for a request outside your domain."
13330
+ },
13331
+ "consult_agent": {
13332
+ "tool_config": {
13333
+ "name": "consult_agent",
13334
+ "description": "Consult an advisor agent for specialized knowledge without transferring the conversation",
13335
+ "strict": true,
13336
+ "parameters": {
13337
+ "type": "object",
13338
+ "properties": {
13339
+ "agent_name": {
13340
+ "type": "string",
13341
+ "description": "Name of the advisor agent to consult for advice"
13342
+ },
13343
+ "consult_question": {
13344
+ "type": "string",
13345
+ "description": "The specific question to ask the advisor agent"
13346
+ }
13347
+ },
13348
+ "required": [
13349
+ "agent_name",
13350
+ "consult_question"
13351
+ ],
13352
+ "additionalProperties": false
13353
+ }
13354
+ },
13355
+ "tool_instructions": "Use when you need expert advice from an advisor agent but want to maintain conversation ownership. The consulted advisor will provide advice that you can use to help the user."
13356
+ },
13357
+ "read_file": {
13358
+ "tool_config": {
13359
+ "name": "read_file",
13360
+ "description": "Retrieve a file by path (preferred) or by ID. The file is injected into the conversation context for AI processing.",
13361
+ "strict": false,
13362
+ "parameters": {
13363
+ "type": "object",
13364
+ "properties": {
13365
+ "path": {
13366
+ "type": "string",
13367
+ "description": 'Path to the file. Knowledge and skill files live under "resources/", exactly as listed in your resource structure or in a file listing \u2014 e.g. "resources/product-docs/references/menu.md", where the segment after "resources/" names the resource. Files attached to this conversation live under "conversation/" and are announced in the transcript by that path \u2014 e.g. "conversation/report.pdf". A shorter form also works for those two and is matched across everything you can read, preferring an exact path over one that merely ends with what you wrote ("references/menu.md", or just "menu.md"); dropping a middle segment does not resolve, and a bare name carried by both a resource and this conversation is reported as ambiguous so you can retry with the full path. Files from an EARLIER conversation with the same user live under "conversations/<conversation_id>/" \u2014 e.g. "conversations/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed/receipt.pdf", exactly as the transcript of that conversation announces them (get_conversation). That form takes no shortening: write the mount, the conversation id and the file name in full. Prefer this over file_id: paths are stable, while IDs are regenerated whenever a hub is published.'
13368
+ },
13369
+ "file_id": {
13370
+ "type": "string",
13371
+ "description": "The file ID to retrieve, from a conversation message or a file listing. Still supported, but prefer path for resource files."
13372
+ },
13373
+ "resource_id": {
13374
+ "type": "string",
13375
+ "description": "Optional. Restricts a path to one resource \u2014 use it when the same file name exists in more than one resource and the tool reports the reference as ambiguous. Must be a resource linked to this agent."
13376
+ }
13377
+ },
13378
+ "required": [],
13379
+ "additionalProperties": false
13380
+ }
13381
+ },
13382
+ "tool_instructions": "Use this tool to read a file for AI analysis. Provide either path (preferred \u2014 the exact paths appear in your resource structure and in list_files output) or file_id. The file will be injected into the conversation context."
13383
+ },
13384
+ "download_file": {
13385
+ "tool_config": {
13386
+ "name": "download_file",
13387
+ "description": "Mount a resource file into the code execution sandbox so you can edit it. Path is provider-specific: Anthropic mounts at /tmp/<filename>, OpenAI mounts at /mnt/data/<filename> \u2014 use the `sandbox_path` returned in the result. After mounting, use the provider's code-execution tool (Anthropic code_execution view/str_replace/create, or OpenAI code_interpreter Python) to read or modify the file. When finished, call upload_file to commit changes back to the resource library. Bytes never enter conversation context.",
13388
+ "strict": true,
13389
+ "parameters": {
13390
+ "type": "object",
13391
+ "properties": {
13392
+ "file_id": {
13393
+ "type": "string",
13394
+ "description": "The resource file_id to mount (from a file listing or read_file \u2014 a listing row carries file_id alongside its path). Must be a text file; binary files are rejected."
13395
+ }
13396
+ },
13397
+ "required": [
13398
+ "file_id"
13399
+ ],
13400
+ "additionalProperties": false
13401
+ }
13402
+ },
13403
+ "tool_instructions": "Use to begin editing a resource file. Returns { sandbox_path, filename, sandbox_file_id }. After this, use your provider's code-execution tool to view and modify the file inside the sandbox, then call upload_file with the sandbox_file_id of the modified file to persist changes."
13404
+ },
13405
+ "upload_file": {
13406
+ "tool_config": {
13407
+ "name": "upload_file",
13408
+ "description": "Persist a sandbox file to the resource library. Two modes: (1) UPDATE \u2014 pass file_id of an existing resource file to overwrite it (after download_file + code-execution edits). (2) CREATE \u2014 pass resource_id + filename instead of file_id to add a new file to that resource (after creating the file in the sandbox with code-execution). Requires write_enabled on the agent_resource binding for the target resource. sandbox_file_id is always required.",
13409
+ "strict": false,
13410
+ "parameters": {
13411
+ "type": "object",
13412
+ "properties": {
13413
+ "file_id": {
13414
+ "type": "string",
13415
+ "description": "UPDATE mode only: the existing resource file_id to overwrite. Omit when creating a new file."
13416
+ },
13417
+ "resource_id": {
13418
+ "type": "string",
13419
+ "description": "CREATE mode only: the resource the new file belongs to. Required when file_id is omitted; ignored when file_id is provided."
13420
+ },
13421
+ "filename": {
13422
+ "type": "string",
13423
+ "description": "CREATE mode only: the filename for the new file (e.g. 'pricing_v2.md'). Required when file_id is omitted. MIME type is inferred from the extension; only text files are supported."
13424
+ },
13425
+ "folder_id": {
13426
+ "type": "string",
13427
+ "description": "CREATE mode only: optional folder to place the file in. Omit for resource root."
13428
+ },
13429
+ "sandbox_file_id": {
13430
+ "type": "string",
13431
+ "description": "The provider-specific file_id of the file inside the sandbox. Anthropic: take from bash_code_execution_tool_result content[].file_id. OpenAI: take from the container_file_citation annotations on code_interpreter_call output. Either one identifies the file as it currently exists in the sandbox."
13432
+ }
13433
+ },
13434
+ "required": [
13435
+ "sandbox_file_id"
13436
+ ],
13437
+ "additionalProperties": false
13438
+ }
13439
+ },
13440
+ "tool_instructions": "Use to commit either an edit (file_id + sandbox_file_id) or a new file (resource_id + filename + sandbox_file_id). Returns { saved, file_id, file_hash, bytes } on success \u2014 the file_id you can reference later. Errors clearly when write access is denied or when neither identifier set is provided."
13441
+ },
13442
+ "send_files": {
13443
+ "tool_config": {
13444
+ "name": "send_files",
13445
+ "description": "Send files to the end user through their conversation channel (App, WhatsApp, email, etc.)",
13446
+ "strict": false,
13447
+ "parameters": {
13448
+ "type": "object",
13449
+ "properties": {
13450
+ "file_ids": {
13451
+ "type": "array",
13452
+ "items": {
13453
+ "type": "string"
13454
+ },
13455
+ "description": 'Files to send to the user, each named by path (preferred) or by ID. Paths are the addresses announced in the transcript and in your resource structure \u2014 "conversation/report.pdf" for a file attached to this conversation, "conversations/<conversation_id>/receipt.pdf" for one attached to an earlier conversation with the same user, "resources/product-docs/price-list.pdf" for a knowledge or skill file. IDs remain accepted, but they are regenerated whenever a hub is published, so a path is the stable address.'
13456
+ },
13457
+ "folder_ids": {
13458
+ "type": "array",
13459
+ "items": {
13460
+ "type": "string"
13461
+ },
13462
+ "description": "Array of resource folder IDs to send all files from. Alternative to file_ids."
13463
+ },
13464
+ "message_text": {
13465
+ "type": "string",
13466
+ "description": "Message text to accompany the files. For WhatsApp, this becomes the caption on the last file. For email, this becomes the email body."
13467
+ }
13468
+ },
13469
+ "required": [],
13470
+ "additionalProperties": false
13471
+ }
13472
+ },
13473
+ "tool_instructions": "Use this tool to send files to users. WhatsApp sends files individually with optional caption on last file. Email sends all files as attachments in a single email."
13474
+ },
13475
+ "list_files": {
13476
+ "tool_config": {
13477
+ "name": "list_files",
13478
+ "description": "Browse the files you can reach, by path. Call it with no path to see the mounts, then pass a mount or a folder path to list what is inside it. Every row carries the exact path read_file and send_files accept.",
13479
+ "strict": false,
13480
+ "parameters": {
13481
+ "type": "object",
13482
+ "properties": {
13483
+ "path": {
13484
+ "type": "string",
13485
+ "description": 'What to list. Omit it (or pass "") for the mounts you can browse \u2014 the available mounts are listed here at runtime. "conversation" lists the files attached to this conversation. "resources" lists every resource mount. "resources/<resource-slug>" lists the top level of a knowledge resource or skill, and "resources/<resource-slug>/<folder>/<subfolder>" lists that folder. Folder rows come back with their own path, so you drill down by passing back the path of the row you want.'
13486
+ },
13487
+ "query": {
13488
+ "type": "string",
13489
+ "description": "Search text. Matched against a file's title and its file name. Under a resource path this searches the WHOLE subtree below it, not just the level named, and every result is returned as a full path."
13490
+ },
13491
+ "tags": {
13492
+ "type": "array",
13493
+ "items": {
13494
+ "type": "string"
13495
+ },
13496
+ "description": "Return files carrying ANY of these tags. Applies to resource files only \u2014 conversation attachments carry no tags. Searches the whole subtree, like query."
13497
+ },
13498
+ "metadata_filter": {
13499
+ "type": "object",
13500
+ "description": 'Filter resource files by their metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (substring for text, membership for a list), $and/$or/$not (logical). Nested paths like "location.city" are supported, and a bare value means equality. Examples: {"status": "active"}, {"priority": {"$gte": 5}}, {"location.city": "Miami"}, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]}, {"$not": {"status": "archived"}}. A comparison between different types simply does not match. Schema from resource.file_metadata_schema. Searches the whole subtree, like query.'
13501
+ },
13502
+ "limit": {
13503
+ "type": "integer",
13504
+ "description": "Maximum number of rows to return (default: 50, max: 100). The response reports the total the page was drawn from."
13505
+ },
13506
+ "offset": {
13507
+ "type": "integer",
13508
+ "description": "Number of rows to skip for pagination (default: 0)"
13509
+ }
13510
+ },
13511
+ "required": [],
13512
+ "additionalProperties": false
13513
+ }
13514
+ },
13515
+ "tool_instructions": 'Use to find out which files exist before reading one. Call it with no path first: that returns the mounts \u2014 "conversation" for what the user attached here, and one "resources/<slug>" per knowledge resource or skill linked to you. Then pass a mount or folder path back to list its contents. Every row carries a `path`; pass that path to read_file to open the file, or to send_files to deliver it. Pass query, tags or metadata_filter to search a whole resource at once instead of walking it folder by folder. Files from EARLIER conversations are not listed here \u2014 the transcript of each past conversation announces its own files by path (get_conversation).'
13516
+ },
13517
+ "list_resource_folders": {
13518
+ "tool_config": {
13519
+ "name": "list_resource_folders",
13520
+ "description": "(deprecated \u2014 use list_files) List all folders in resources that this agent has access to. Returns all folders with parent_folder_id for hierarchy reconstruction.",
13521
+ "strict": false,
13522
+ "parameters": {
13523
+ "type": "object",
13524
+ "properties": {
13525
+ "resource_id": {
13526
+ "type": "string",
13527
+ "description": "Resource ID to query folders from. Must be one of the knowledge resources linked to this agent (the available ids are listed here at runtime). Required for access control and performance."
13528
+ },
13529
+ "search_query": {
13530
+ "type": "string",
13531
+ "description": "Search folders by name"
13532
+ },
13533
+ "metadata_filter": {
13534
+ "type": "object",
13535
+ "description": 'Filter by metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (contains value), $and/$or/$not (logical). Supports nested paths like "location.city". Returns clear errors for type mismatches. Examples: {"status": "active"} for simple equality, {"priority": {"$gte": 5}} for comparison, {"location.city": "Miami"} for nested path, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]} for complex logic, {"$not": {"status": "archived"}} for negation. Schema from resource.folder_metadata_schema.'
13536
+ }
13537
+ },
13538
+ "required": [
13539
+ "resource_id"
13540
+ ],
13541
+ "additionalProperties": false
13542
+ }
13543
+ },
13544
+ "tool_instructions": "Deprecated: prefer list_files, which walks the same folders by path and needs no resource_id. Still works \u2014 lists all folders in a resource, filtered by search_query or metadata, returning folder info with parent_folder_id, file counts, and subfolder counts."
13545
+ },
13546
+ "list_resource_files": {
13547
+ "tool_config": {
13548
+ "name": "list_resource_files",
13549
+ "description": "(deprecated \u2014 use list_files) List files in resources that this agent has access to. Each file is returned with its path \u2014 the address read_file accepts \u2014 plus metadata.",
13550
+ "strict": false,
13551
+ "parameters": {
13552
+ "type": "object",
13553
+ "properties": {
13554
+ "resource_id": {
13555
+ "type": "string",
13556
+ "description": "Resource ID to query files from. Must be one of the knowledge resources linked to this agent (the available ids are listed here at runtime). Required for access control and performance."
13557
+ },
13558
+ "folder_id": {
13559
+ "type": "string",
13560
+ "description": "Filter by folder ID. Omit to return ALL files. Use 00000000-0000-0000-0000-000000000000 for root-level files only."
13561
+ },
13562
+ "search_query": {
13563
+ "type": "string",
13564
+ "description": "Search files by title or file name"
13565
+ },
13566
+ "tags": {
13567
+ "type": "array",
13568
+ "items": {
13569
+ "type": "string"
13570
+ },
13571
+ "description": "Filter by tags (matches files with any of the specified tags)"
13572
+ },
13573
+ "metadata_filter": {
13574
+ "type": "object",
13575
+ "description": 'Filter by metadata using MongoDB-style operators. Supported: $eq (equal), $ne (not equal), $gt/$gte/$lt/$lte (comparisons), $in/$nin (in/not in array), $contains (contains value), $and/$or/$not (logical). Supports nested paths like "location.city". Returns clear errors for type mismatches. Examples: {"status": "active"} for simple equality, {"priority": {"$gte": 5}} for comparison, {"location.city": "Miami"} for nested path, {"$and": [{"status": "active"}, {"priority": {"$gt": 3}}]} for complex logic, {"$not": {"status": "archived"}} for negation. Schema from resource.file_metadata_schema.'
13576
+ },
13577
+ "limit": {
13578
+ "type": "integer",
13579
+ "description": "Maximum number of files to return (default: 50, max: 100)"
13580
+ },
13581
+ "offset": {
13582
+ "type": "integer",
13583
+ "description": "Number of files to skip for pagination (default: 0)"
13584
+ }
13585
+ },
13586
+ "required": [
13587
+ "resource_id"
13588
+ ],
13589
+ "additionalProperties": false
13590
+ }
13591
+ },
13592
+ "tool_instructions": "Deprecated: prefer list_files, which lists the same files by path, needs no resource_id, and also sees this conversation's attachments. Still works \u2014 each row carries a `path`; pass that path to read_file to retrieve the file's content for AI analysis. `file_id` is also returned and still accepted."
13593
+ },
13594
+ "get_tool_schema": {
13595
+ "tool_config": {
13596
+ "name": "get_tool_schema",
13597
+ "description": "MANDATORY FIRST STEP: Retrieves the complete parameter schema for any tool by name. You MUST call this before using ANY tool to get its exact required parameters. This ensures execute_tool will succeed.",
13598
+ "strict": true,
13599
+ "parameters": {
13600
+ "type": "object",
13601
+ "properties": {
13602
+ "tool_name": {
13603
+ "type": "string",
13604
+ "description": "The name of the tool to retrieve the schema for"
13605
+ }
13606
+ },
13607
+ "required": [
13608
+ "tool_name"
13609
+ ],
13610
+ "additionalProperties": false
13611
+ }
13612
+ },
13613
+ "tool_instructions": "Mandatory first step before execute_tool. Returns `function_schema` for the target tool \u2014 pass `function_schema.parameters` (same shape, same keys) as the `parameters` envelope in your execute_tool call."
13614
+ },
13615
+ "execute_tool": {
13616
+ "tool_config": {
13617
+ "name": "execute_tool",
13618
+ "description": "Executes any tool by name. You MUST call get_tool_schema first for the target tool to learn its parameter shape, then pass that shape verbatim under `parameters`.",
13619
+ "strict": false,
13620
+ "parameters": {
13621
+ "type": "object",
13622
+ "required": [
13623
+ "tool_name",
13624
+ "parameters"
13625
+ ],
13626
+ "properties": {
13627
+ "tool_name": {
13628
+ "type": "string",
13629
+ "description": "The name of the tool to execute."
13630
+ },
13631
+ "parameters": {
13632
+ "type": "object",
13633
+ "description": "The arguments object for the target tool. Must match `function_schema.parameters` returned by get_tool_schema \u2014 same keys, same shape, same required fields. Inner contents are validated by the target tool, not by execute_tool."
13634
+ }
13635
+ },
13636
+ "additionalProperties": false
13637
+ }
13638
+ },
13639
+ "tool_instructions": "MANDATORY WORKFLOW: 1) Call get_tool_schema(tool_name) to receive the target's `function_schema`. 2) Build a `parameters` object that matches `function_schema.parameters` \u2014 same property keys, same types, all required fields. 3) Call execute_tool({ tool_name, parameters }). The `parameters` envelope here is identical in shape to `function_schema.parameters`."
13640
+ },
13641
+ "expand_summary": {
13642
+ "tool_config": {
13643
+ "name": "expand_summary",
13644
+ "description": "Retrieve the original messages of a section in the conversation summary. The summary block at the top of the conversation lists sections with stable `id`s \u2014 pass one to get the verbatim messages it covers.",
13645
+ "strict": true,
13646
+ "parameters": {
13647
+ "type": "object",
13648
+ "properties": {
13649
+ "section_id": {
13650
+ "type": "string",
13651
+ "description": "The `id` of the section from the <conversation_summary> block at the top of the conversation."
13652
+ }
13653
+ },
13654
+ "required": [
13655
+ "section_id"
13656
+ ],
13657
+ "additionalProperties": false
13658
+ }
13659
+ },
13660
+ "tool_instructions": "Use when a summary section is too compressed for the question at hand \u2014 e.g. you need the user's exact wording, the precise text of a previous decision, or a tool result that the summary only references. If you pass an unknown `section_id`, the response will include `available_section_ids` so you can retry with a valid one."
13661
+ },
13662
+ "read_skill": {
13663
+ "tool_config": {
13664
+ "name": "read_skill",
13665
+ "description": "Read a skill's SKILL.md content. Pass a skill_id from the skills linked to this agent (available ids are listed in the skill_id parameter).",
13666
+ "strict": true,
13667
+ "parameters": {
13668
+ "type": "object",
13669
+ "properties": {
13670
+ "skill_id": {
13671
+ "type": "string",
13672
+ "description": "The skill's resource_id. Must be one of the skills linked to this agent (the available ids are listed here at runtime)."
13673
+ }
13674
+ },
13675
+ "required": [
13676
+ "skill_id"
13677
+ ],
13678
+ "additionalProperties": false
13679
+ }
13680
+ },
13681
+ "tool_instructions": "Use this tool to read a skill's instructions. Returns the full SKILL.md content as a string. Use read_skill_file to access other files referenced in the skill."
13682
+ },
13683
+ "read_skill_file": {
13684
+ "tool_config": {
13685
+ "name": "read_skill_file",
13686
+ "description": "Read a file from a skill by relative path. No need to call read_skill first if you already know the file path.",
13687
+ "strict": true,
13688
+ "parameters": {
13689
+ "type": "object",
13690
+ "properties": {
13691
+ "skill_id": {
13692
+ "type": "string",
13693
+ "description": "The skill's resource_id. Must be one of the skills linked to this agent (the available ids are listed here at runtime)."
13694
+ },
13695
+ "file_path": {
13696
+ "type": "string",
13697
+ "description": 'Relative path from skill root (e.g., "references/menu.md")'
13698
+ }
13699
+ },
13700
+ "required": [
13701
+ "skill_id",
13702
+ "file_path"
13703
+ ],
13704
+ "additionalProperties": false
13705
+ }
13706
+ },
13707
+ "tool_instructions": 'Use this tool to read files referenced in SKILL.md. Provide the relative path as shown in the skill content (e.g., "references/menu.md"). Returns content for text files or signed URL for binary files.'
13708
+ },
13709
+ "update_state": {
13710
+ "tool_config": {
13711
+ "name": "update_state",
13712
+ "description": "Updates a configured state by merging updates with its current value. Scope (conversation vs user) is inferred from the state's definition.",
13713
+ "strict": false,
13714
+ "parameters": {
13715
+ "type": "object",
13716
+ "properties": {
13717
+ "state_slug": {
13718
+ "type": "string",
13719
+ "description": "Stable slug of the state to update. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
13720
+ },
13721
+ "updates": {
13722
+ "type": "object",
13723
+ "description": "Object with key-value pairs to merge into the current state"
13724
+ }
13725
+ },
13726
+ "required": [
13727
+ "state_slug",
13728
+ "updates"
13729
+ ],
13730
+ "additionalProperties": false
13731
+ }
13732
+ },
13733
+ "tool_instructions": "Use to track and update contextual information during conversations. Examples: updating cart items, recording form answers, saving user preferences. The updates are merged with existing state."
13734
+ },
13735
+ "get_state": {
13736
+ "tool_config": {
13737
+ "name": "get_state",
13738
+ "description": "Retrieves the current value of a configured state. Scope is inferred from the state's definition.",
13739
+ "strict": false,
13740
+ "parameters": {
13741
+ "type": "object",
13742
+ "properties": {
13743
+ "state_slug": {
13744
+ "type": "string",
13745
+ "description": "Stable slug of the state to retrieve. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
13746
+ }
13747
+ },
13748
+ "required": [
13749
+ "state_slug"
13750
+ ],
13751
+ "additionalProperties": false
13752
+ }
13753
+ },
13754
+ "tool_instructions": "Use to retrieve the current state value. This is useful when you need to check the current state before making decisions or when the state is not already available in the prompt context."
13755
+ },
13756
+ "reset_state": {
13757
+ "tool_config": {
13758
+ "name": "reset_state",
13759
+ "description": "Resets a configured state to its initial value as defined in the state schema. Scope is inferred from the state's definition.",
13760
+ "strict": false,
13761
+ "parameters": {
13762
+ "type": "object",
13763
+ "properties": {
13764
+ "state_slug": {
13765
+ "type": "string",
13766
+ "description": "Stable slug of the state to reset. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
13767
+ }
13768
+ },
13769
+ "required": [
13770
+ "state_slug"
13771
+ ],
13772
+ "additionalProperties": false
13773
+ }
13774
+ },
13775
+ "tool_instructions": "Use to reset state to its default initial value. This is useful when you need to clear accumulated state and start fresh, such as clearing a shopping cart or resetting form progress."
13776
+ },
13777
+ "get_conversations_summary": {
13778
+ "tool_config": {
13779
+ "name": "get_conversations_summary",
13780
+ "description": "Retrieves a timeline of past conversations with the current user, showing summaries and conversation IDs.",
13781
+ "strict": false,
13782
+ "parameters": {
13783
+ "type": "object",
13784
+ "properties": {
13785
+ "max_conversations": {
13786
+ "type": "integer",
13787
+ "description": "Maximum number of past conversations to return (default: 10)"
13788
+ },
13789
+ "max_characters": {
13790
+ "type": "integer",
13791
+ "description": "Maximum characters in the output (default: 20000, max: 40000). Truncates oldest entries first."
13792
+ }
13793
+ },
13794
+ "required": [],
13795
+ "additionalProperties": false
13796
+ }
13797
+ },
13798
+ "tool_instructions": "Use this tool at the start of a conversation to retrieve the history of past conversations with this user. Returns summaries with conversation IDs that can be used with get_conversation for full transcripts."
13799
+ },
13800
+ "get_conversation": {
13801
+ "tool_config": {
13802
+ "name": "get_conversation",
13803
+ "description": "Retrieves the full transcript of a specific past conversation, plus the IDs of the conversations closed immediately before and after it. Use get_conversations_summary first to find conversation IDs.",
13804
+ "strict": false,
13805
+ "parameters": {
13806
+ "type": "object",
13807
+ "properties": {
13808
+ "conversation_id": {
13809
+ "type": "string",
13810
+ "description": "The ID of the conversation to retrieve (from get_conversations_summary results)"
13811
+ },
13812
+ "max_characters": {
13813
+ "type": "integer",
13814
+ "description": "Maximum characters in the output (default: 30000, max: 60000). Truncates oldest messages first."
13815
+ }
13816
+ },
13817
+ "required": [
13818
+ "conversation_id"
13819
+ ],
13820
+ "additionalProperties": false
13821
+ }
13822
+ },
13823
+ "tool_instructions": "Use this tool to read the full transcript of a specific past conversation. First use get_conversations_summary to find conversation IDs, then use this tool to drill into the details. The response may include previous_conversation_id and next_conversation_id \u2014 the conversations closed nearest in time either side of this one, which you can pass straight back to this tool to keep moving through the user's history. They are neighbours by closing time, not a fixed chain, and either may be absent."
13824
+ },
13825
+ // ---------------------------------------------------------------------------
13826
+ // Tools added to the TS catalog after the Supabase migration — schemas
13827
+ // reconstructed from the handler signatures in
13828
+ // apps/backend/src/tools/native/wayai/*.ts.
13829
+ // ---------------------------------------------------------------------------
13830
+ "set_state_path": {
13831
+ "tool_config": {
13832
+ "name": "set_state_path",
13833
+ "description": "Set a specific path within a state object (dot/array path) without overwriting the rest of the state. Scope is inferred from the state's definition.",
13834
+ // strict:false matches the other state ops so legacy composed-tool YAML
13835
+ // mapping `state_scope` (now ignored at runtime) doesn't trip OpenAI
13836
+ // strict-mode validators on in-flight calls.
13837
+ "strict": false,
13838
+ "parameters": {
13839
+ "type": "object",
13840
+ "properties": {
13841
+ "state_slug": {
13842
+ "type": "string",
13843
+ "description": "Stable slug of the state variable to update. Pass the slug (the left half), not the display name. Available states (slug + display name): [STATE_SLUG_VALUES]"
13844
+ },
13845
+ "path": {
13846
+ "type": "array",
13847
+ "items": { "type": "string" },
13848
+ "description": "Path within the state object, as an ordered list of keys."
13849
+ },
13850
+ "value": {
13851
+ "description": "The value to write at the given path. Any JSON value is allowed."
13852
+ }
13853
+ },
13854
+ "required": ["state_slug", "path", "value"],
13855
+ "additionalProperties": false
13856
+ }
13857
+ },
13858
+ "tool_instructions": "Use this tool when you need to update a single field inside a structured state object without replacing the entire state. Provide the full path from the root of the state object."
13859
+ },
13860
+ // NEVER OFFERED TO A MODEL, unlike every other entry in this file — and it is
13861
+ // `model_callable: false` on the catalog entry that delivers that, NOT the role
13862
+ // restriction beside it. An earlier version of this comment credited
13863
+ // `assignable_roles: ['monitor']`, which was wrong: a monitor runs a DISPATCHING
13864
+ // turn on `idle` (only the synchronous side-call sets `omit_tools`), and
13865
+ // `PATCH /api/setup/agents/:id` can re-role an agent that keeps the rows it
13866
+ // holds — so the role gate left the tool offerable on two paths.
13867
+ //
13868
+ // The schema exists because the catalog requires one per tool (the parity check
13869
+ // at the bottom of `native-tools.ts`) and because it is the one place the two
13870
+ // arguments are described for whoever writes the rule.
13871
+ //
13872
+ // `tool_instructions: null` FOLLOWS FROM THAT: instructions are injected into an
13873
+ // agent's system message when the tool is attached, and this tool is attached to
13874
+ // an agent whose model is never told it exists.
13875
+ // NEVER OFFERED TO A MODEL, like `insert_note` below and for the same reason:
13876
+ // `model_callable: false` on the catalog entry. A monitor's RULE selects it and
13877
+ // the monitor runtime carries it out — it never reaches `handleTool`, because
13878
+ // running a callee means re-entering the side-call with the caller's cascade
13879
+ // state, which no `ToolContext` carries.
13880
+ "run_monitor": {
13881
+ "tool_config": {
13882
+ "name": "run_monitor",
13883
+ "description": "Run another monitor on this hub. The named monitor judges the same conversation and acts on its own rules; nothing it finds is returned here.",
13884
+ "strict": false,
13885
+ "parameters": {
13886
+ "type": "object",
13887
+ "properties": {
13888
+ "monitor_name": {
13889
+ "type": "string",
13890
+ "description": "The name of the monitor to run. It must be a monitor on this hub whose trigger is manual."
13891
+ }
13892
+ },
13893
+ "required": ["monitor_name"],
13894
+ "additionalProperties": false
13895
+ }
13896
+ },
13897
+ "tool_instructions": null
13898
+ },
13899
+ "insert_note": {
13900
+ "tool_config": {
13901
+ "name": "insert_note",
13902
+ "description": "Insert an admin-authored note into the answering agent's context for this turn only. The note is written by the administrator, not by a model; the monitor's variables are substituted into it.",
13903
+ "strict": false,
13904
+ "parameters": {
13905
+ "type": "object",
13906
+ "properties": {
13907
+ "template": {
13908
+ "type": "string",
13909
+ "description": "The note text. Write {{variable_name}} to substitute one of this monitor's own variables. Unresolved names render as nothing and are recorded on the audit row."
13910
+ },
13911
+ "keep_in_history": {
13912
+ "type": "boolean",
13913
+ "description": "Default false: the note briefs this turn only and is never stored. Set true to also record it as an internal message the support team can see and the customer never receives."
13914
+ }
13915
+ },
13916
+ "required": ["template"],
13917
+ "additionalProperties": false
13918
+ }
13919
+ },
13920
+ "tool_instructions": null
13921
+ }
13922
+ };
13923
+ WAYAI_CONNECTOR2 = "b17d9f3a-4e1b-46c9-b648-a2f0c3611aa4";
13924
+ BASE_NATIVE_TOOLS2 = [
13925
+ // --- Wayai core tools ---
13926
+ { tool_native_id: "nt-001", tool_name: "transfer_to_agent", tool_display_name: "Transfer to Agent", tool_description: "Transfer the conversation to another AI agent", connector_id: WAYAI_CONNECTOR2, operation: "transfer_to_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null },
13927
+ { tool_native_id: "nt-002", tool_name: "transfer_to_team", tool_display_name: "Transfer to Team", tool_description: "Escalate conversation to a human support team", connector_id: WAYAI_CONNECTOR2, operation: "transfer_to_team", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null },
13928
+ { tool_native_id: "nt-003", tool_name: "close_conversation", tool_display_name: "Close Conversation", tool_description: "Close and end the current conversation", connector_id: WAYAI_CONNECTOR2, operation: "close_conversation", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13929
+ { tool_native_id: "nt-004", tool_name: "update_kanban_status", tool_display_name: "Update Kanban Status", tool_description: "Update the kanban status of the conversation", connector_id: WAYAI_CONNECTOR2, operation: "update_kanban_status", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13930
+ { tool_native_id: "nt-005", tool_name: "schedule_followup", tool_display_name: "Schedule Follow-up", tool_description: "Schedule a follow-up message for the conversation", connector_id: WAYAI_CONNECTOR2, operation: "schedule_followup", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: true, execution_config: null },
13931
+ { tool_native_id: "nt-006", tool_name: "send_files", tool_display_name: "Send Files", tool_description: "Send files to the user", connector_id: WAYAI_CONNECTOR2, operation: "send_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13932
+ { tool_native_id: "nt-007", tool_name: "update_state", tool_display_name: "Update State", tool_description: "Update conversation or user state variables", connector_id: WAYAI_CONNECTOR2, operation: "update_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13933
+ { tool_native_id: "nt-008", tool_name: "get_state", tool_display_name: "Get State", tool_description: "Retrieve conversation or user state values", connector_id: WAYAI_CONNECTOR2, operation: "get_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13934
+ { tool_native_id: "nt-009", tool_name: "reset_state", tool_display_name: "Reset State", tool_description: "Reset conversation or user state to initial values", connector_id: WAYAI_CONNECTOR2, operation: "reset_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13935
+ { tool_native_id: "nt-010", tool_name: "set_state_path", tool_display_name: "Set State Path", tool_description: "Set a specific path within state object", connector_id: WAYAI_CONNECTOR2, operation: "set_state_path", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13936
+ { tool_native_id: "nt-011", tool_name: "consult_agent", tool_display_name: "Consult Agent", tool_description: "Consult an advisor agent for advice without transferring", connector_id: WAYAI_CONNECTOR2, operation: "consult_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13937
+ { tool_native_id: "nt-013", tool_name: "read_skill", tool_display_name: "Read Skill", tool_description: "Read the full specification of a skill", connector_id: WAYAI_CONNECTOR2, operation: "read_skill", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13938
+ { tool_native_id: "nt-014", tool_name: "read_skill_file", tool_display_name: "Read Skill File", tool_description: "Read a specific file within a skill", connector_id: WAYAI_CONNECTOR2, operation: "read_skill_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13939
+ { tool_native_id: "nt-015", tool_name: "read_file", tool_display_name: "Read File", tool_description: "Read the contents of a resource file", connector_id: WAYAI_CONNECTOR2, operation: "read_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13940
+ // Superseded by `list_files` (nt-027), which browses the same resource files
13941
+ // by PATH plus this conversation's attachments.
13942
+ //
13943
+ // `ui_assignable: false` — DEPRECATED, not withdrawn. Hiding the Add grid entry
13944
+ // stops a NEW agent from being wired to a tool that addresses files by an id a
13945
+ // publish re-mints and that cannot see a conversation attachment at all (the id
13946
+ // itself is discoverable — `RESOURCE_TOOL_INJECTIONS` still writes the linked
13947
+ // ids into their `resource_id` description every turn), while every hub that
13948
+ // already assigns one keeps
13949
+ // working: the handler still dispatches, CI name resolution still resolves the
13950
+ // name, and `wayai pull` / `push` still round-trips the YAML. There is no
13951
+ // auto-provisioning anywhere in the platform, so an existing agent is never
13952
+ // migrated for free — an author swaps the tool deliberately.
13953
+ { tool_native_id: "nt-016", tool_name: "list_resource_files", tool_display_name: "List Resource Files", tool_description: "(deprecated \u2014 use list_files) List files in a resource folder", connector_id: WAYAI_CONNECTOR2, operation: "list_resource_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, ui_assignable: false },
13954
+ { tool_native_id: "nt-017", tool_name: "list_resource_folders", tool_display_name: "List Resource Folders", tool_description: "(deprecated \u2014 use list_files) List all resource folders", connector_id: WAYAI_CONNECTOR2, operation: "list_resource_folders", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, ui_assignable: false },
13955
+ { tool_native_id: "nt-018", tool_name: "get_conversations_summary", tool_display_name: "Get Conversations Summary", tool_description: "Get a summary of recent conversations", connector_id: WAYAI_CONNECTOR2, operation: "get_conversations_summary", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13956
+ { tool_native_id: "nt-019", tool_name: "get_conversation", tool_display_name: "Get Conversation", tool_description: "Retrieve full conversation history", connector_id: WAYAI_CONNECTOR2, operation: "get_conversation", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13957
+ { tool_native_id: "nt-020", tool_name: "get_tool_schema", tool_display_name: "Get Tool Schema", tool_description: "Retrieve the JSON schema for a tool", connector_id: WAYAI_CONNECTOR2, operation: "get_tool_schema", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13958
+ { tool_native_id: "nt-021", tool_name: "execute_tool", tool_display_name: "Execute Tool", tool_description: "Execute any available tool by name", connector_id: WAYAI_CONNECTOR2, operation: "execute_tool", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13959
+ { tool_native_id: "nt-022", tool_name: "expand_summary", tool_display_name: "Expand Summary", tool_description: "Retrieve the original messages of a section in the conversation summary", connector_id: WAYAI_CONNECTOR2, operation: "expand_summary", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13960
+ { tool_native_id: "nt-023", tool_name: "download_file", tool_display_name: "Download File for Edit", tool_description: "Mount a resource file into the code execution sandbox for editing", connector_id: WAYAI_CONNECTOR2, operation: "download_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13961
+ { tool_native_id: "nt-024", tool_name: "upload_file", tool_display_name: "Upload Edited File", tool_description: "Persist a sandbox-edited file back to the resource library", connector_id: WAYAI_CONNECTOR2, operation: "upload_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13962
+ // Hub-as-agent delegation (consultant-agents H1.5). UN-GATED in PR 7 (H1.5b): the
13963
+ // spawn + cross-DO completion callback shipped, so the tool is live across the
13964
+ // catalog listing, CI push, the config snapshot, and the handler. (PR 6 shipped it
13965
+ // `assignable: false` so no visible-but-inert tool could reach production between
13966
+ // the two merges.)
13967
+ //
13968
+ // `ui_assignable: false` — CONFIG-AS-CODE ONLY for v1. Attaching this tool requires
13969
+ // two admin decisions the Add dialog cannot yet collect: the target hub and the
13970
+ // `context_boundary` (a data-sensitivity choice). Without a `hub` branch in the
13971
+ // delegation dialog, clicking Add would submit no delegation params and the write
13972
+ // path would reject it — a live button that only ever errors. Assign it via
13973
+ // `wayai push` until the picker + boundary control ship.
13974
+ { tool_native_id: "nt-025", tool_name: "delegate_to_hub", tool_display_name: "Delegate to Hub", tool_description: "Delegate a request to another hub as a task, and receive the result asynchronously", connector_id: WAYAI_CONNECTOR2, operation: "delegate_to_hub", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, ui_assignable: false },
13975
+ // Agent-initiated consults (consultant-agents H1.7b). A delegation tool like the
13976
+ // three above: the TARGET is admin configuration (`delegated_agent_id` for a
13977
+ // same-hub consultant, `delegated_hub_id` for a partner hub) and the model supplies
13978
+ // only the question — so scoping "which consultants may this agent reach" reuses
13979
+ // the existing assignment model with no new config surface.
13980
+ //
13981
+ // `ui_assignable: false` — CONFIG-AS-CODE ONLY for v1, same reasoning as
13982
+ // `delegate_to_hub`: attaching it requires a target the Add dialog cannot yet
13983
+ // collect (and, for a consultant target, the `allow_consultant_chain` opt-in). A
13984
+ // live button that only ever errors is worse than no button. Assign via `wayai push`
13985
+ // until the picker ships.
13986
+ { tool_native_id: "nt-026", tool_name: "start_consult_thread", tool_display_name: "Start Consult Thread", tool_description: "Ask a configured consultant (or partner hub) a question in a visible consult thread", connector_id: WAYAI_CONNECTOR2, operation: "start_consult_thread", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, ui_assignable: false },
13987
+ // One drill-down listing for every file namespace, replacing `list_resource_files`
13988
+ // (nt-016) and `list_resource_folders` (nt-017). Fully visible: it needs no
13989
+ // configuration the Add dialog cannot collect — the mounts an agent may browse
13990
+ // come from its existing resource links and its own conversation, and the only
13991
+ // model-supplied input is a path.
13992
+ { tool_native_id: "nt-027", tool_name: "list_files", tool_display_name: "List Files", tool_description: "Browse the files this agent can reach, by path", connector_id: WAYAI_CONNECTOR2, operation: "list_files", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null },
13993
+ // TWO gates, and BOTH are load-bearing — the first shipped alone and was not
13994
+ // enough. `assignable_roles: ['monitor']` says only a monitor may HOLD it,
13995
+ // because the tool has no meaning off a monitor: a rule SELECTS it and the note
13996
+ // is injected into the ANSWERING agent's prompt for that one turn.
13997
+ // `model_callable: false` says no model is ever TOLD it exists — which the role
13998
+ // restriction does NOT imply, because an `idle` monitor runs a dispatching turn
13999
+ // and a re-roled agent keeps the tools it holds. Offered and called, it reaches
14000
+ // the native dispatcher's `default:` branch: a Sentry event and a provider
14001
+ // re-call. See `NativeTool.model_callable`.
14002
+ { tool_native_id: "nt-028", tool_name: "insert_note", tool_display_name: "Insert Note", tool_description: "Brief the answering agent with an admin-authored note before it replies", connector_id: WAYAI_CONNECTOR2, operation: "insert_note", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, assignable_roles: ["monitor"], model_callable: false },
14003
+ // TWO gates, same pair and same reasons as `insert_note` above — this is the
14004
+ // second tool to need both, which is why they are catalog fields rather than a
14005
+ // check at one site. `assignable_roles: ['monitor']`: only a monitor may hold it,
14006
+ // because only a monitor's RULE can select it. `model_callable: false`: no model
14007
+ // is ever told it exists, which the role restriction does NOT imply — an `idle`
14008
+ // monitor runs a dispatching turn, and a re-roled agent keeps the rows it holds.
14009
+ //
14010
+ // The CALLEE is named, not identified by id (owner decision 9): a hub-relative
14011
+ // name survives every copy path untouched, where a per-hub UUID in a JSON column
14012
+ // would need remapping on both branching mechanisms and normalizing on both CI
14013
+ // directions. `action.tool_name`, one key away in the same object, is the
14014
+ // precedent.
14015
+ { tool_native_id: "nt-029", tool_name: "run_monitor", tool_display_name: "Run Monitor", tool_description: "Run another monitor on this hub and let it act on its own rules", connector_id: WAYAI_CONNECTOR2, operation: "run_monitor", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, assignable_roles: ["monitor"], model_callable: false }
14016
+ ];
14017
+ NATIVE_TOOLS2 = BASE_NATIVE_TOOLS2.map((base) => {
14018
+ const schema = NATIVE_TOOL_SCHEMAS2[base.tool_name];
14019
+ if (!schema) {
14020
+ throw new Error(
14021
+ `Native tool "${base.tool_name}" (${base.tool_native_id}) is missing an entry in NATIVE_TOOL_SCHEMAS. Add it to workers/shared/src/catalog/native-tool-schemas.ts.`
14022
+ );
14023
+ }
14024
+ return {
14025
+ ...base,
14026
+ tool_config: schema.tool_config,
14027
+ tool_instructions: schema.tool_instructions
14028
+ };
14029
+ });
14030
+ NATIVE_TOOL_NAMES2 = new Set(NATIVE_TOOLS2.map((t) => t.tool_name));
11283
14031
  previousConversationsCountField2 = external_exports.number().int().min(0).max(PREVIOUS_CONVERSATIONS_MAX2).nullable().optional();
11284
14032
  summarizationThresholdField2 = external_exports.number().int().min(SUMMARIZATION_THRESHOLD_MIN2).max(SUMMARIZATION_THRESHOLD_MAX2).nullable().optional();
14033
+ flagConditionSchema2 = external_exports.object({
14034
+ variable: external_exports.string(),
14035
+ operator: external_exports.enum(FLAG_CONDITION_OPERATORS2),
14036
+ // `boolean` is admitted for parity with the CI write path, whose refine gates the
14037
+ // operator alone, so `value: false` on a boolean evaluation variable is storable
14038
+ // config today and the evaluator honours it through `String()`. Narrowing here would
14039
+ // reject on PATCH exactly what `wayai push` persists, so any REST caller that reads
14040
+ // an agent and writes it back — the typed clients, the CLI's `ApiClient`, MCP — would
14041
+ // fail on a hub configured through the other surface.
14042
+ value: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])
14043
+ });
14044
+ MONITOR_TRIGGERS2 = ["idle", "user_message", "assistant_reply", "manual"];
14045
+ monitorTriggerSchema2 = external_exports.enum(MONITOR_TRIGGERS2);
14046
+ MONITOR_FIRING_TRIGGERS2 = MONITOR_TRIGGERS2.filter(isFiringTrigger2);
14047
+ MONITOR_DELAY_SECONDS_MIN2 = 10;
14048
+ MONITOR_IDLE_DELAY_REQUIRED_MESSAGE2 = "delay_seconds is required for an idle monitor (an absent trigger is idle)";
14049
+ MONITOR_HISTORY_MESSAGES_MAX2 = 100;
14050
+ monitorHistoryMessagesSchema2 = external_exports.number().int().min(1).max(MONITOR_HISTORY_MESSAGES_MAX2);
14051
+ monitorIncludeToolResultsSchema2 = external_exports.boolean();
14052
+ MONITOR_INPUT_SHAPING_KEYS2 = ["history_messages", "include_tool_results"];
14053
+ MONITOR_INPUT_SHAPING_SCHEMAS2 = {
14054
+ history_messages: monitorHistoryMessagesSchema2.optional(),
14055
+ include_tool_results: monitorIncludeToolResultsSchema2.optional()
14056
+ };
14057
+ MONITOR_INPUT_SHAPING_IDLE_MESSAGE2 = "only a user_message, assistant_reply or manual monitor reads this \u2014 an idle monitor runs as a full turn and takes the ordinary history window";
14058
+ monitorArgumentSourceSchema2 = external_exports.union([
14059
+ external_exports.object({ from_variable: external_exports.string().min(1) }).strict(),
14060
+ external_exports.object({ const: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]) }).strict()
14061
+ ]);
14062
+ MONITOR_NOTE_TEMPLATE_MAX2 = 2e3;
14063
+ monitorActionSchema2 = external_exports.discriminatedUnion("kind", [
14064
+ external_exports.object({ kind: external_exports.literal("none") }).strict(),
14065
+ external_exports.object({
14066
+ kind: external_exports.literal("call_tool"),
14067
+ tool_name: external_exports.string().min(1),
14068
+ args: external_exports.record(monitorArgumentSourceSchema2).optional()
14069
+ }).strict(),
14070
+ external_exports.object({ kind: external_exports.literal("hold") }).strict(),
14071
+ external_exports.object({ kind: external_exports.literal("rewrite"), note: external_exports.string().min(1).max(MONITOR_NOTE_TEMPLATE_MAX2) }).strict()
14072
+ ]);
14073
+ monitorRuleSchema2 = external_exports.object({
14074
+ when: external_exports.array(flagConditionSchema2).min(1),
14075
+ action: monitorActionSchema2
14076
+ }).strict();
14077
+ MONITOR_RULE_KEYS2 = ["rules", "fallback"];
14078
+ MONITOR_RULE_SCHEMAS2 = {
14079
+ rules: external_exports.array(monitorRuleSchema2).optional(),
14080
+ fallback: monitorActionSchema2.optional()
14081
+ };
14082
+ MONITOR_RULE_TRIGGERS2 = ["user_message", "assistant_reply", "manual"];
14083
+ MONITOR_RULE_TRIGGER_MESSAGE2 = "only a user_message, assistant_reply or manual monitor acts on rules \u2014 set one of those triggers, or remove this key. A manual monitor acts on its rules when another monitor runs it.";
14084
+ MONITOR_USER_MESSAGE_ACTION_KINDS2 = ["none", "call_tool"];
14085
+ MONITOR_ASSISTANT_REPLY_ACTION_KINDS2 = ["none", "call_tool", "hold", "rewrite"];
14086
+ INSERT_NOTE_TOOL_NAME2 = "insert_note";
14087
+ RUN_MONITOR_TOOL_NAME2 = "run_monitor";
14088
+ MONITOR_RULE_ALLOWED_NATIVE_TOOLS2 = [
14089
+ "update_state",
14090
+ "schedule_followup",
14091
+ "transfer_to_agent",
14092
+ "transfer_to_team",
14093
+ INSERT_NOTE_TOOL_NAME2,
14094
+ RUN_MONITOR_TOOL_NAME2
14095
+ ];
14096
+ MONITOR_RULE_REENTRY_TOOLS2 = {
14097
+ transfer_to_agent: "agent",
14098
+ transfer_to_team: "team"
14099
+ };
14100
+ MONITOR_RULE_REENTRY_TRACKS2 = new Map(Object.entries(MONITOR_RULE_REENTRY_TOOLS2));
11285
14101
  monitorConfigField2 = external_exports.object({
11286
- delay_seconds: external_exports.number().int().min(10),
11287
- flag_conditions: external_exports.array(
11288
- external_exports.object({
11289
- variable: external_exports.string(),
11290
- operator: external_exports.enum(["=", "!="]),
11291
- value: external_exports.union([external_exports.string(), external_exports.number()])
11292
- })
11293
- ).optional()
11294
- }).passthrough().nullable().optional();
14102
+ delay_seconds: external_exports.number().int().min(MONITOR_DELAY_SECONDS_MIN2).optional(),
14103
+ trigger: monitorTriggerSchema2.optional(),
14104
+ ...MONITOR_INPUT_SHAPING_SCHEMAS2,
14105
+ ...MONITOR_RULE_SCHEMAS2,
14106
+ flag_conditions: external_exports.array(flagConditionSchema2).optional()
14107
+ }).passthrough().superRefine((config, ctx) => {
14108
+ if (monitorConfigNeedsDelay2(config)) {
14109
+ ctx.addIssue({
14110
+ code: external_exports.ZodIssueCode.custom,
14111
+ path: ["delay_seconds"],
14112
+ message: MONITOR_IDLE_DELAY_REQUIRED_MESSAGE2
14113
+ });
14114
+ }
14115
+ for (const key of monitorInputShapingKeysOnIdle2(config)) {
14116
+ ctx.addIssue({
14117
+ code: external_exports.ZodIssueCode.custom,
14118
+ path: [key],
14119
+ message: MONITOR_INPUT_SHAPING_IDLE_MESSAGE2
14120
+ });
14121
+ }
14122
+ for (const issue of collectMonitorRuleIssues2(config)) {
14123
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: issue.path, message: issue.message });
14124
+ }
14125
+ }).nullable().optional();
14126
+ flagConditionsField2 = external_exports.array(flagConditionSchema2).nullable().optional();
11295
14127
  agentIdParam2 = external_exports.object({
11296
14128
  id: uuidSchema2
11297
14129
  });
@@ -11308,13 +14140,15 @@ var init_dist = __esm({
11308
14140
  additional_context_template: external_exports.string().max(2e4).optional(),
11309
14141
  summarization_threshold_tokens: summarizationThresholdField2,
11310
14142
  previous_conversations_count: previousConversationsCountField2,
11311
- monitor_config: monitorConfigField2
11312
- }).passthrough();
14143
+ monitor_config: monitorConfigField2,
14144
+ flag_conditions: flagConditionsField2
14145
+ }).passthrough().superRefine(refineDecisionsModelBinding2);
11313
14146
  updateAgentBody2 = external_exports.object({
11314
14147
  additional_context_template: external_exports.string().max(2e4).optional(),
11315
14148
  summarization_threshold_tokens: summarizationThresholdField2,
11316
14149
  previous_conversations_count: previousConversationsCountField2,
11317
- monitor_config: monitorConfigField2
14150
+ monitor_config: monitorConfigField2,
14151
+ flag_conditions: flagConditionsField2
11318
14152
  }).passthrough();
11319
14153
  AGENT_PARAMETER_TYPES2 = ["string", "number", "boolean", "integer", "enum"];
11320
14154
  AGENT_PARAMETER_NAME_REGEX2 = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/;
@@ -13301,11 +16135,11 @@ var init_dist = __esm({
13301
16135
  ops_tenths_this_month: external_exports.number().nonnegative(),
13302
16136
  records_count: external_exports.number().nonnegative(),
13303
16137
  month_key: external_exports.string(),
13304
- // The closed window's meter reading (§2.7's prior-window snapshot). OPTIONAL for the same
16138
+ // The closed window's meter reading (the prior-window snapshot). OPTIONAL for the same
13305
16139
  // reason this object is not `.strict()`: the producer deploys on its own cadence, and a
13306
16140
  // required field would fail the parse for every counter on any producer build that predates
13307
16141
  // it — turning an additive wire change into an outage of the gauge above. Absence and a
13308
- // non-matching key are the SAME outcome for a reader (§2.7 property 1: do not bill), so
16142
+ // non-matching key are the SAME outcome for a reader (do not bill), so
13309
16143
  // nothing is lost by tolerating it.
13310
16144
  prior_window_key: external_exports.string().optional(),
13311
16145
  prior_window_ops_tenths: external_exports.number().nonnegative().optional()
@@ -13317,41 +16151,41 @@ var init_dist = __esm({
13317
16151
  * Monotonic per org, WayAI-assigned. An ordering guard for concurrent WayAI
13318
16152
  * writers (a plan change racing a token mint) — NOT a cross-repo CAS. Gaps are
13319
16153
  * expected: a version is allocated per push attempt, and an idempotency skip
13320
- * (§2.4) burns none while a failed KV put burns one.
16154
+ * burns none while a failed KV put burns one.
13321
16155
  */
13322
16156
  projection_version: external_exports.number().int().nonnegative(),
13323
- /** Diagnostic only — never gates. Freshness is `window_resets_at`'s job (§2.2). */
16157
+ /** Diagnostic only — never gates. Freshness is `window_resets_at`'s job. */
13324
16158
  written_at: external_exports.string().datetime(),
13325
16159
  /** The WayAI org UUID. The `account_id` spelling is Rekor's documented carve-out. */
13326
16160
  account_id: external_exports.string().uuid(),
13327
- /** The EFFECTIVE plan, not the subscribed one (§2.2.1). Never gates. */
16161
+ /** The EFFECTIVE plan, not the subscribed one. Never gates. */
13328
16162
  plan_key: planKeySchema2,
13329
- /** Informational only (§2.2.1). `cancelled` spelling matches `BillingRecord`. */
16163
+ /** Informational only. `cancelled` spelling matches `BillingRecord`. */
13330
16164
  billing_status: billingStatusSchema2,
13331
16165
  /** Tenths, matching Rekor's meter granularity (write 10, point-read 1, list/SQL 10). */
13332
16166
  ops_tenths_included: external_exports.number().int().nonnegative(),
13333
16167
  max_records: external_exports.number().int().nonnegative(),
13334
- /** Its own field, no longer derived from `max_records` (§2.2). */
16168
+ /** Its own field, no longer derived from `max_records`. */
13335
16169
  import_rows_included: external_exports.number().int().nonnegative(),
13336
- /** REQUIRED — absence is malformed, never coerced to `false` (§2.2.2). */
16170
+ /** REQUIRED — absence is malformed, never coerced to `false`. */
13337
16171
  production_bases: external_exports.boolean(),
13338
- /** Opaque to Rekor; compared for equality only, to reset `usage:` counters (§2.3). */
16172
+ /** Opaque to Rekor; compared for equality only, to reset `usage:` counters. */
13339
16173
  window_key: external_exports.string().min(1),
13340
16174
  /**
13341
- * The window's TRUE lower bound, for the reconcile's `operations_log` sum (§2.2).
16175
+ * The window's TRUE lower bound, for the reconcile's `operations_log` sum.
13342
16176
  *
13343
16177
  * OPTIONAL, and that is not a rollout convenience — it is what "never gates" means
13344
16178
  * on the writer's side. The tier sources can be absent (a paid row carrying
13345
16179
  * `current_period_end` with no `current_period_start`) and inverted pairs are a
13346
- * §2.2 diagnostic, so requiring it here would let an inert field throw the strict
13347
- * parse and leave the org with NO projection — permanently stale under §2.3, with
13348
- * §2.5's fail-closed gates refusing base creation and 503-ing every import. The
16180
+ * diagnostic, so requiring it here would let an inert field throw the strict
16181
+ * parse and leave the org with NO projection — permanently stale, with the
16182
+ * fail-closed gates refusing base creation and 503-ing every import. The
13349
16183
  * writer emits it whenever it has a sound value and omits it otherwise; Rekor's
13350
16184
  * observed-flip fallback covers the gap, and rollout step 3 keeps that fallback
13351
16185
  * for exactly this reason.
13352
16186
  */
13353
16187
  window_start: external_exports.string().datetime().optional(),
13354
- /** The sole staleness authority (§2.3). Never triggers a counter reset. */
16188
+ /** The sole staleness authority. Never triggers a counter reset. */
13355
16189
  window_resets_at: external_exports.string().datetime()
13356
16190
  }).strict();
13357
16191
  rekorEntitlementRefreshMessageSchema2 = external_exports.object({
@@ -13591,6 +16425,9 @@ var init_dist = __esm({
13591
16425
  refineHubAsCodeEvalAttachments2(config, ctx);
13592
16426
  refineHubAsCodeResources2(config, ctx);
13593
16427
  refineHubAsCodeDelegation2(config, ctx);
16428
+ refineHubAsCodeFlagConditions2(config, ctx);
16429
+ refineHubAsCodeMonitorConfig2(config, ctx);
16430
+ refineHubAsCodeDecisionsModel2(config, ctx);
13594
16431
  });
13595
16432
  ciPullHubIdParam2 = external_exports.object({
13596
16433
  hub_id: ciUuidSchema2
@@ -13818,7 +16655,7 @@ var init_dist = __esm({
13818
16655
  *
13819
16656
  * Past-or-now only. A future start freezes the org's window permanently:
13820
16657
  * `maybeRolloverBillingWindow` derives a NEGATIVE elapsed and so never archives the
13821
- * metrics nor restamps the column, while the §2.6 trigger-5 sweep selects only windows
16658
+ * metrics nor restamps the column, while the rollover sweep selects only windows
13822
16659
  * that have already passed — so nothing rolls the org over, its consumed operations
13823
16660
  * never reset, and past quota every AI turn is refused until a human re-patches.
13824
16661
  */
@@ -13913,7 +16750,7 @@ var init_dist = __esm({
13913
16750
  seats_per_unit: external_exports.number().int().min(1, "seats_per_unit must be a positive integer").optional(),
13914
16751
  seats_scaling: external_exports.enum(["fixed", "per_quantity"]).optional(),
13915
16752
  is_active: external_exports.boolean().optional(),
13916
- // Rekor entitlement allowances (rekor-platform-contracts §2.2). Editable here for the
16753
+ // Rekor entitlement allowances. Editable here for the
13917
16754
  // same reason `messages_per_unit` is — and this route is the only ROUTINE way to change an
13918
16755
  // allowance on a live plan: the seed establishes a plan row and never rewrites one, so a
13919
16756
  // seed-version bump no longer moves these values (see `seed.ts`).
@@ -14050,7 +16887,7 @@ var init_dist = __esm({
14050
16887
  cmd: external_exports.string().min(1, "cmd is required").max(SANDBOX_EXEC_MAX_CMD_LENGTH2, `cmd must be <= ${SANDBOX_EXEC_MAX_CMD_LENGTH2} chars`),
14051
16888
  /**
14052
16889
  * Egress posture. Omitted ⇒ the driver fails closed to deny-all outbound (the
14053
- * §4 safe default). The route re-validates the (policy, allowlist) pair via
16890
+ * safe default). The route re-validates the (policy, allowlist) pair via
14054
16891
  * `validateEgressConfig`.
14055
16892
  */
14056
16893
  egress_policy: sandboxEgressPolicy2.optional(),
@@ -14549,7 +17386,7 @@ var init_dist = __esm({
14549
17386
  name: external_exports.string().regex(ADMIN_SKILL_NAME_REGEX2, "invalid skill name")
14550
17387
  });
14551
17388
  rekorBaseChangeMessageSchema2 = external_exports.object({
14552
- /** The WayAI org UUID — one id namespace (plan §2). */
17389
+ /** The WayAI org UUID — one id namespace. */
14553
17390
  org_id: external_exports.string().uuid(),
14554
17391
  /**
14555
17392
  * Not a UUID: a preview base id is `{prod_base_id}--{slug}`. Immutable per base — a
@@ -14576,6 +17413,33 @@ var init_dist = __esm({
14576
17413
  /** Subdir of `wsDir` holding base folders. */
14577
17414
  basesSubdir: "bases"
14578
17415
  };
17416
+ decisionAnswerSchema = external_exports.discriminatedUnion("type", [
17417
+ external_exports.object({
17418
+ type: external_exports.literal("choice"),
17419
+ choice: external_exports.string(),
17420
+ confidence: external_exports.number().optional()
17421
+ }),
17422
+ external_exports.object({
17423
+ type: external_exports.literal("score"),
17424
+ score: external_exports.number(),
17425
+ confidence: external_exports.number().optional()
17426
+ }),
17427
+ external_exports.object({
17428
+ type: external_exports.literal("noul"),
17429
+ noul: external_exports.number()
17430
+ })
17431
+ ]);
17432
+ decisionsResponseSchema = external_exports.object({
17433
+ id: external_exports.string().optional(),
17434
+ model: external_exports.string().optional(),
17435
+ provider: external_exports.string().optional(),
17436
+ answers: external_exports.record(decisionAnswerSchema),
17437
+ usage: external_exports.object({
17438
+ input_tokens: external_exports.number().optional(),
17439
+ output_tokens: external_exports.number().optional(),
17440
+ cost: external_exports.number().optional()
17441
+ }).optional()
17442
+ });
14579
17443
  }
14580
17444
  });
14581
17445
 
@@ -17585,6 +20449,16 @@ function writeFileNoFollow(root, abs, data) {
17585
20449
  fs9.writeFileSync(abs, data);
17586
20450
  return true;
17587
20451
  }
20452
+ function createFileNoFollow(root, abs, data) {
20453
+ if (!ensureRealSubdirNoSymlink(root, path10.dirname(abs), true)) return "refused";
20454
+ try {
20455
+ fs9.writeFileSync(abs, data, { flag: "wx" });
20456
+ } catch (err) {
20457
+ if (err.code === "EEXIST") return "exists";
20458
+ throw err;
20459
+ }
20460
+ return "created";
20461
+ }
17588
20462
  var init_fs_safety = __esm({
17589
20463
  "src/lib/fs-safety.ts"() {
17590
20464
  "use strict";
@@ -18431,7 +21305,6 @@ var init_diff_display = __esm({
18431
21305
  });
18432
21306
 
18433
21307
  // src/lib/workspace-files.ts
18434
- import * as fs13 from "fs";
18435
21308
  import * as path14 from "path";
18436
21309
  function perHubAgentsMd(hubFolderName) {
18437
21310
  return [
@@ -18447,21 +21320,18 @@ function perHubAgentsMd(hubFolderName) {
18447
21320
  ""
18448
21321
  ].join("\n");
18449
21322
  }
18450
- function writeIfAbsent(filePath, content) {
18451
- if (fs13.existsSync(filePath)) return null;
18452
- fs13.writeFileSync(filePath, content, "utf-8");
18453
- return path14.basename(filePath);
21323
+ function writeIfAbsent(root, filePath, content) {
21324
+ return createFileNoFollow(root, filePath, Buffer.from(content, "utf-8")) === "created" ? path14.basename(filePath) : null;
18454
21325
  }
18455
- var CLAUDE_MD_SHIM;
18456
21326
  var init_workspace_files = __esm({
18457
21327
  "src/lib/workspace-files.ts"() {
18458
21328
  "use strict";
18459
- CLAUDE_MD_SHIM = "@AGENTS.md\n";
21329
+ init_fs_safety();
18460
21330
  }
18461
21331
  });
18462
21332
 
18463
21333
  // src/lib/yaml-writer.ts
18464
- import * as fs14 from "fs";
21334
+ import * as fs13 from "fs";
18465
21335
  import * as path15 from "path";
18466
21336
  import * as yaml7 from "js-yaml";
18467
21337
  function writeFileIfChanged(hubFolder, absPath, content, log) {
@@ -18512,27 +21382,27 @@ function buildEvalYamlObject(evalEntry, slug) {
18512
21382
  function writeHubFolder(hubFolder, payload, options = {}) {
18513
21383
  const log = { changed: [], removed: [] };
18514
21384
  const agentsDir = path15.join(hubFolder, "agents");
18515
- if (!fs14.existsSync(hubFolder)) {
18516
- fs14.mkdirSync(hubFolder, { recursive: true });
21385
+ if (!fs13.existsSync(hubFolder)) {
21386
+ fs13.mkdirSync(hubFolder, { recursive: true });
18517
21387
  }
18518
- if (!fs14.existsSync(agentsDir)) {
18519
- fs14.mkdirSync(agentsDir, { recursive: true });
21388
+ if (!fs13.existsSync(agentsDir)) {
21389
+ fs13.mkdirSync(agentsDir, { recursive: true });
18520
21390
  }
18521
21391
  const yamlPayload = buildYamlPayload(payload);
18522
21392
  const agentFiles = extractAgentFiles(payload);
18523
21393
  const yamlContent = yaml7.dump(yamlPayload, YAML_DUMP_OPTIONS);
18524
21394
  writeFileIfChanged(hubFolder, path15.join(hubFolder, "hub.yaml"), yamlContent, log);
18525
21395
  if (options.seedAgentContext ?? true) {
18526
- for (const seeded of [
18527
- writeIfAbsent(path15.join(hubFolder, "AGENTS.md"), perHubAgentsMd(path15.basename(hubFolder))),
18528
- writeIfAbsent(path15.join(hubFolder, "CLAUDE.md"), CLAUDE_MD_SHIM)
18529
- ]) {
18530
- if (seeded) log.changed.push(seeded);
18531
- }
21396
+ const seeded = writeIfAbsent(
21397
+ hubFolder,
21398
+ path15.join(hubFolder, "AGENTS.md"),
21399
+ perHubAgentsMd(path15.basename(hubFolder))
21400
+ );
21401
+ if (seeded) log.changed.push(seeded);
18532
21402
  }
18533
21403
  const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
18534
- if (fs14.existsSync(oldYamlPath)) {
18535
- fs14.unlinkSync(oldYamlPath);
21404
+ if (fs13.existsSync(oldYamlPath)) {
21405
+ fs13.unlinkSync(oldYamlPath);
18536
21406
  log.removed.push(path15.relative(hubFolder, oldYamlPath));
18537
21407
  }
18538
21408
  const yamlSlugs = writeAgentYamlFiles(hubFolder, agentsDir, payload.agents || [], log);
@@ -18541,20 +21411,20 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18541
21411
  mdSlugs.add(slug);
18542
21412
  writeFileIfChanged(hubFolder, path15.join(agentsDir, `${slug}.md`), content, log);
18543
21413
  }
18544
- const existingFiles = fs14.readdirSync(agentsDir);
21414
+ const existingFiles = fs13.readdirSync(agentsDir);
18545
21415
  for (const file of existingFiles) {
18546
21416
  if (file.endsWith(".yaml")) {
18547
21417
  const slug = file.slice(0, -5);
18548
21418
  if (!yamlSlugs.has(slug)) {
18549
21419
  const orphan = path15.join(agentsDir, file);
18550
- fs14.unlinkSync(orphan);
21420
+ fs13.unlinkSync(orphan);
18551
21421
  log.removed.push(path15.relative(hubFolder, orphan));
18552
21422
  }
18553
21423
  } else if (file.endsWith(".md")) {
18554
21424
  const slug = file.slice(0, -3);
18555
21425
  if (!mdSlugs.has(slug)) {
18556
21426
  const orphan = path15.join(agentsDir, file);
18557
- fs14.unlinkSync(orphan);
21427
+ fs13.unlinkSync(orphan);
18558
21428
  log.removed.push(path15.relative(hubFolder, orphan));
18559
21429
  }
18560
21430
  }
@@ -18567,10 +21437,10 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18567
21437
  function setPreviewLabelInHubYaml(hubFolder, label) {
18568
21438
  const yamlPath = resolveHubYamlPath(hubFolder);
18569
21439
  if (!yamlPath) return;
18570
- const obj = yaml7.load(fs14.readFileSync(yamlPath, "utf-8")) ?? {};
21440
+ const obj = yaml7.load(fs13.readFileSync(yamlPath, "utf-8")) ?? {};
18571
21441
  if (label) obj.preview_label = label;
18572
21442
  else delete obj.preview_label;
18573
- fs14.writeFileSync(yamlPath, yaml7.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
21443
+ fs13.writeFileSync(yamlPath, yaml7.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
18574
21444
  }
18575
21445
  function buildYamlPayload(payload) {
18576
21446
  const result = {
@@ -18635,17 +21505,17 @@ function extractAgentFiles(payload) {
18635
21505
  function writeEvalYamlFiles(hubFolder, evals, log) {
18636
21506
  const evalsDir = path15.join(hubFolder, "evals");
18637
21507
  if (evals.length === 0) {
18638
- if (fs14.existsSync(evalsDir)) {
21508
+ if (fs13.existsSync(evalsDir)) {
18639
21509
  cleanEvalOrphans(hubFolder, evalsDir, /* @__PURE__ */ new Set(), log);
18640
21510
  try {
18641
- if (fs14.readdirSync(evalsDir).length === 0) fs14.rmdirSync(evalsDir);
21511
+ if (fs13.readdirSync(evalsDir).length === 0) fs13.rmdirSync(evalsDir);
18642
21512
  } catch {
18643
21513
  }
18644
21514
  }
18645
21515
  return;
18646
21516
  }
18647
- if (!fs14.existsSync(evalsDir)) {
18648
- fs14.mkdirSync(evalsDir, { recursive: true });
21517
+ if (!fs13.existsSync(evalsDir)) {
21518
+ fs13.mkdirSync(evalsDir, { recursive: true });
18649
21519
  }
18650
21520
  const writtenRelPaths = /* @__PURE__ */ new Set();
18651
21521
  for (const evalEntry of evals) {
@@ -18659,8 +21529,8 @@ function writeEvalYamlFiles(hubFolder, evals, log) {
18659
21529
  }
18660
21530
  if (setName) {
18661
21531
  const setDir = path15.join(evalsDir, setName);
18662
- if (!fs14.existsSync(setDir)) {
18663
- fs14.mkdirSync(setDir, { recursive: true });
21532
+ if (!fs13.existsSync(setDir)) {
21533
+ fs13.mkdirSync(setDir, { recursive: true });
18664
21534
  }
18665
21535
  }
18666
21536
  const yamlContent = yaml7.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
@@ -18670,31 +21540,31 @@ function writeEvalYamlFiles(hubFolder, evals, log) {
18670
21540
  cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log);
18671
21541
  }
18672
21542
  function cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log) {
18673
- const entries = fs14.readdirSync(evalsDir, { withFileTypes: true });
21543
+ const entries = fs13.readdirSync(evalsDir, { withFileTypes: true });
18674
21544
  for (const entry of entries) {
18675
21545
  const fullPath = path15.join(evalsDir, entry.name);
18676
21546
  if (entry.isFile()) {
18677
21547
  if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
18678
- fs14.unlinkSync(fullPath);
21548
+ fs13.unlinkSync(fullPath);
18679
21549
  log.removed.push(path15.relative(hubFolder, fullPath));
18680
21550
  }
18681
21551
  continue;
18682
21552
  }
18683
21553
  if (entry.isDirectory()) {
18684
21554
  const setName = entry.name;
18685
- const subEntries = fs14.readdirSync(fullPath, { withFileTypes: true });
21555
+ const subEntries = fs13.readdirSync(fullPath, { withFileTypes: true });
18686
21556
  for (const sub of subEntries) {
18687
21557
  if (sub.isFile() && sub.name.endsWith(".yaml")) {
18688
21558
  const relPath = `${setName}/${sub.name}`;
18689
21559
  if (!writtenRelPaths.has(relPath)) {
18690
21560
  const orphan = path15.join(fullPath, sub.name);
18691
- fs14.unlinkSync(orphan);
21561
+ fs13.unlinkSync(orphan);
18692
21562
  log.removed.push(path15.relative(hubFolder, orphan));
18693
21563
  }
18694
21564
  }
18695
21565
  }
18696
21566
  try {
18697
- if (fs14.readdirSync(fullPath).length === 0) fs14.rmdirSync(fullPath);
21567
+ if (fs13.readdirSync(fullPath).length === 0) fs13.rmdirSync(fullPath);
18698
21568
  } catch {
18699
21569
  }
18700
21570
  }
@@ -18733,17 +21603,17 @@ function buildJourneyYamlObject(journeyEntry, slug) {
18733
21603
  function writeJourneyYamlFiles(hubFolder, journeys, log) {
18734
21604
  const journeysDir = path15.join(hubFolder, "journeys");
18735
21605
  if (journeys.length === 0) {
18736
- if (fs14.existsSync(journeysDir)) {
21606
+ if (fs13.existsSync(journeysDir)) {
18737
21607
  cleanJourneyOrphans(hubFolder, journeysDir, /* @__PURE__ */ new Set(), log);
18738
21608
  try {
18739
- if (fs14.readdirSync(journeysDir).length === 0) fs14.rmdirSync(journeysDir);
21609
+ if (fs13.readdirSync(journeysDir).length === 0) fs13.rmdirSync(journeysDir);
18740
21610
  } catch {
18741
21611
  }
18742
21612
  }
18743
21613
  return;
18744
21614
  }
18745
- if (!fs14.existsSync(journeysDir)) {
18746
- fs14.mkdirSync(journeysDir, { recursive: true });
21615
+ if (!fs13.existsSync(journeysDir)) {
21616
+ fs13.mkdirSync(journeysDir, { recursive: true });
18747
21617
  }
18748
21618
  const writtenFiles = /* @__PURE__ */ new Set();
18749
21619
  const usedSlugs = /* @__PURE__ */ new Set();
@@ -18757,11 +21627,11 @@ function writeJourneyYamlFiles(hubFolder, journeys, log) {
18757
21627
  cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log);
18758
21628
  }
18759
21629
  function cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log) {
18760
- const entries = fs14.readdirSync(journeysDir, { withFileTypes: true });
21630
+ const entries = fs13.readdirSync(journeysDir, { withFileTypes: true });
18761
21631
  for (const entry of entries) {
18762
21632
  if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
18763
21633
  const orphan = path15.join(journeysDir, entry.name);
18764
- fs14.unlinkSync(orphan);
21634
+ fs13.unlinkSync(orphan);
18765
21635
  log.removed.push(path15.relative(hubFolder, orphan));
18766
21636
  }
18767
21637
  }
@@ -18775,12 +21645,12 @@ function writeResourceFiles(hubFolder, resources, log) {
18775
21645
  const resDir = path15.join(resourcesDir, resSlug);
18776
21646
  writeResourceFileTree(resDir, resource.files || [], hubFolder, log);
18777
21647
  }
18778
- if (fs14.existsSync(resourcesDir)) {
18779
- const existingDirs = fs14.readdirSync(resourcesDir, { withFileTypes: true });
21648
+ if (fs13.existsSync(resourcesDir)) {
21649
+ const existingDirs = fs13.readdirSync(resourcesDir, { withFileTypes: true });
18780
21650
  for (const entry of existingDirs) {
18781
21651
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
18782
21652
  const orphanDir = path15.join(resourcesDir, entry.name);
18783
- fs14.rmSync(orphanDir, { recursive: true, force: true });
21653
+ fs13.rmSync(orphanDir, { recursive: true, force: true });
18784
21654
  log.removed.push(`${path15.relative(hubFolder, orphanDir)}/`);
18785
21655
  }
18786
21656
  }
@@ -18869,7 +21739,7 @@ var init_terminal_output = __esm({
18869
21739
  });
18870
21740
 
18871
21741
  // src/lib/base-workspace.ts
18872
- import * as fs15 from "fs";
21742
+ import * as fs14 from "fs";
18873
21743
  import * as path17 from "path";
18874
21744
  import * as yaml8 from "js-yaml";
18875
21745
  function readBaseMeta(folder) {
@@ -18887,7 +21757,7 @@ function readBaseMeta(folder) {
18887
21757
  function listBaseFolders(basesDir) {
18888
21758
  let entries;
18889
21759
  try {
18890
- entries = fs15.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
21760
+ entries = fs14.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
18891
21761
  } catch {
18892
21762
  return [];
18893
21763
  }
@@ -18974,7 +21844,7 @@ function resolveBaseSelectorToId(gitRoot, selector) {
18974
21844
  }
18975
21845
  function hasBaseMetaFile(folder) {
18976
21846
  try {
18977
- return fs15.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
21847
+ return fs14.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
18978
21848
  } catch {
18979
21849
  return false;
18980
21850
  }
@@ -19440,7 +22310,7 @@ var init_types2 = __esm({
19440
22310
  });
19441
22311
 
19442
22312
  // src/data/config-as-code/config-writer.ts
19443
- import * as fs16 from "fs";
22313
+ import * as fs15 from "fs";
19444
22314
  import * as path19 from "path";
19445
22315
  import * as yaml9 from "js-yaml";
19446
22316
  function dump5(value) {
@@ -19475,14 +22345,14 @@ function pruneOrphans(folder, dir, keep, log) {
19475
22345
  if (!ensureRealSubdirNoSymlink(folder, dir, false)) return;
19476
22346
  let entries;
19477
22347
  try {
19478
- entries = fs16.readdirSync(dir);
22348
+ entries = fs15.readdirSync(dir);
19479
22349
  } catch {
19480
22350
  return;
19481
22351
  }
19482
22352
  for (const file of entries) {
19483
22353
  if (!file.endsWith(".yaml") || keep.has(file)) continue;
19484
22354
  const abs = path19.join(dir, file);
19485
- fs16.rmSync(abs);
22355
+ fs15.rmSync(abs);
19486
22356
  log.removed.push(path19.relative(folder, abs));
19487
22357
  }
19488
22358
  }
@@ -19496,7 +22366,7 @@ function metaFileObject(meta) {
19496
22366
  function writeBaseFolder(folder, meta, config) {
19497
22367
  const delta = { changed: [], removed: [] };
19498
22368
  const parent = path19.dirname(folder);
19499
- fs16.mkdirSync(parent, { recursive: true });
22369
+ fs15.mkdirSync(parent, { recursive: true });
19500
22370
  if (!ensureRealSubdirNoSymlink(parent, folder, true)) {
19501
22371
  throw expected(
19502
22372
  `Refusing to write ${folder}: the path crosses a symlink. Remove it and pull again.`
@@ -19523,7 +22393,7 @@ function writeBaseFolder(folder, meta, config) {
19523
22393
  for (const deprecated of DEPRECATED_ENTITY_DIRS) {
19524
22394
  const dir = path19.join(folder, deprecated);
19525
22395
  if (ensureRealSubdirNoSymlink(folder, dir, false)) {
19526
- fs16.rmSync(dir, { recursive: true, force: true });
22396
+ fs15.rmSync(dir, { recursive: true, force: true });
19527
22397
  }
19528
22398
  }
19529
22399
  return delta;
@@ -19675,7 +22545,7 @@ var init_api = __esm({
19675
22545
  });
19676
22546
 
19677
22547
  // src/data/config-as-code/config-parser.ts
19678
- import * as fs17 from "fs";
22548
+ import * as fs16 from "fs";
19679
22549
  import * as path20 from "path";
19680
22550
  import * as yaml10 from "js-yaml";
19681
22551
  function readEntityDir(folder, dir) {
@@ -19684,7 +22554,7 @@ function readEntityDir(folder, dir) {
19684
22554
  }
19685
22555
  if (!isDirectory(dir)) return [];
19686
22556
  const out = [];
19687
- for (const file of fs17.readdirSync(dir).sort()) {
22557
+ for (const file of fs16.readdirSync(dir).sort()) {
19688
22558
  if (!file.endsWith(".yaml")) continue;
19689
22559
  const abs = path20.join(dir, file);
19690
22560
  const bytes = readFileNoFollow(folder, abs);
@@ -20071,7 +22941,7 @@ __export(push_exports, {
20071
22941
  shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel,
20072
22942
  syncAfterPush: () => syncAfterPush
20073
22943
  });
20074
- import * as fs18 from "fs";
22944
+ import * as fs17 from "fs";
20075
22945
  import * as path22 from "path";
20076
22946
  import * as yaml11 from "js-yaml";
20077
22947
  function parseArgs5(args2) {
@@ -20182,11 +23052,11 @@ function printLocalFileChanges(delta) {
20182
23052
  async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, remoteConfig) {
20183
23053
  const agentsDir = path22.join(hubFolder, "agents");
20184
23054
  let agentsWithIds = [];
20185
- if (fs18.existsSync(agentsDir)) {
20186
- const yamlFiles = fs18.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
23055
+ if (fs17.existsSync(agentsDir)) {
23056
+ const yamlFiles = fs17.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
20187
23057
  for (const file of yamlFiles) {
20188
23058
  try {
20189
- const content = fs18.readFileSync(path22.join(agentsDir, file), "utf-8");
23059
+ const content = fs17.readFileSync(path22.join(agentsDir, file), "utf-8");
20190
23060
  const agent = yaml11.load(content);
20191
23061
  if (agent?.id && agent.name) {
20192
23062
  agentsWithIds.push({ id: agent.id, name: agent.name });
@@ -20199,7 +23069,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
20199
23069
  if (agentsWithIds.length === 0) {
20200
23070
  const yamlPath = resolveHubYamlPath(hubFolder);
20201
23071
  if (!yamlPath) return;
20202
- const yamlContent = fs18.readFileSync(yamlPath, "utf-8");
23072
+ const yamlContent = fs17.readFileSync(yamlPath, "utf-8");
20203
23073
  const config = yaml11.load(yamlContent);
20204
23074
  agentsWithIds = (config.agents || []).filter((a) => !!a.id && !!a.name);
20205
23075
  }
@@ -20231,18 +23101,18 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
20231
23101
  }
20232
23102
  }
20233
23103
  if (renames.length === 0) return;
20234
- if (!fs18.existsSync(agentsDir)) return;
20235
- for (const file of fs18.readdirSync(agentsDir)) {
23104
+ if (!fs17.existsSync(agentsDir)) return;
23105
+ for (const file of fs17.readdirSync(agentsDir)) {
20236
23106
  if (file.startsWith("__rename_temp_") && (file.endsWith(".md") || file.endsWith(".yaml"))) {
20237
23107
  console.warn(` Warning: removing orphaned temp file agents/${file}`);
20238
- fs18.unlinkSync(path22.join(agentsDir, file));
23108
+ fs17.unlinkSync(path22.join(agentsDir, file));
20239
23109
  }
20240
23110
  }
20241
23111
  const renameFileIfExists = (dir, oldName, newName) => {
20242
23112
  const oldPath = path22.join(dir, oldName);
20243
23113
  const newPath = path22.join(dir, newName);
20244
- if (!fs18.existsSync(oldPath)) return false;
20245
- fs18.renameSync(oldPath, newPath);
23114
+ if (!fs17.existsSync(oldPath)) return false;
23115
+ fs17.renameSync(oldPath, newPath);
20246
23116
  return true;
20247
23117
  };
20248
23118
  const oldSlugs = new Set(renames.map((r) => r.oldSlug));
@@ -20275,9 +23145,9 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
20275
23145
  }
20276
23146
  } else {
20277
23147
  for (const { oldSlug, newSlug } of renames) {
20278
- const hasOldFile = extensions.some((ext) => fs18.existsSync(path22.join(agentsDir, `${oldSlug}${ext}`)));
23148
+ const hasOldFile = extensions.some((ext) => fs17.existsSync(path22.join(agentsDir, `${oldSlug}${ext}`)));
20279
23149
  if (!hasOldFile) continue;
20280
- const hasNewFile = extensions.some((ext) => fs18.existsSync(path22.join(agentsDir, `${newSlug}${ext}`)));
23150
+ const hasNewFile = extensions.some((ext) => fs17.existsSync(path22.join(agentsDir, `${newSlug}${ext}`)));
20281
23151
  if (hasNewFile) {
20282
23152
  console.warn(` Warning: skipping rename agents/${oldSlug}.* \u2192 agents/${newSlug}.* (target already exists)`);
20283
23153
  continue;
@@ -20292,7 +23162,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
20292
23162
  if (completedRenames.length === 0) return;
20293
23163
  const mainYamlPath = resolveHubYamlPath(hubFolder);
20294
23164
  if (mainYamlPath) {
20295
- const mainYamlContent = fs18.readFileSync(mainYamlPath, "utf-8");
23165
+ const mainYamlContent = fs17.readFileSync(mainYamlPath, "utf-8");
20296
23166
  const substitutionMap = /* @__PURE__ */ new Map();
20297
23167
  for (const { oldSlug, newSlug } of completedRenames) {
20298
23168
  substitutionMap.set(`agents/${oldSlug}.md`, `agents/${newSlug}.md`);
@@ -20303,7 +23173,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
20303
23173
  (_match, prefix, pathMatch) => `${prefix}${substitutionMap.get(pathMatch) ?? pathMatch}`
20304
23174
  );
20305
23175
  if (updatedYaml !== mainYamlContent) {
20306
- fs18.writeFileSync(mainYamlPath, updatedYaml, "utf-8");
23176
+ fs17.writeFileSync(mainYamlPath, updatedYaml, "utf-8");
20307
23177
  console.log(` Updated instructions paths in ${path22.basename(mainYamlPath)}`);
20308
23178
  }
20309
23179
  }
@@ -20415,7 +23285,7 @@ New hub: "${newHub.hubName}" (${hubType})`);
20415
23285
  process.exit(1);
20416
23286
  }
20417
23287
  console.log(`Hub created: ${createdHub.hub_name} (${hubId})`);
20418
- const content = fs18.readFileSync(hubYamlPath, "utf-8");
23288
+ const content = fs17.readFileSync(hubYamlPath, "utf-8");
20419
23289
  const hasVersion = content.match(/^version:\s/m);
20420
23290
  let updated;
20421
23291
  if (hasVersion) {
@@ -20431,7 +23301,7 @@ hub_id: "${hubId}"
20431
23301
  hub_environment: preview
20432
23302
  ${content}`;
20433
23303
  }
20434
- fs18.writeFileSync(hubYamlPath, updated, "utf-8");
23304
+ fs17.writeFileSync(hubYamlPath, updated, "utf-8");
20435
23305
  seedScopeIfEmpty("hubs", hubId);
20436
23306
  await pushSingleHub(client, hubId, newHub.hubFolder, opts.autoConfirm, opts.organizationId, { skipAgentRename: true });
20437
23307
  }
@@ -20477,7 +23347,7 @@ async function pushCommand(args2) {
20477
23347
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
20478
23348
  const workspaceDir = resolveWorkspaceDir();
20479
23349
  const wsLabel = hubsDirLabel(gitRoot);
20480
- if (!fs18.existsSync(workspaceDir)) {
23350
+ if (!fs17.existsSync(workspaceDir)) {
20481
23351
  console.error(`No ${wsLabel}/ directory found. Run \`wayai pull\` first or create hub files in ${wsLabel}/<hub>/hub.yaml.`);
20482
23352
  process.exit(1);
20483
23353
  }
@@ -20533,7 +23403,7 @@ __export(pull_exports, {
20533
23403
  resolveHubTarget: () => resolveHubTarget,
20534
23404
  writeProductionMirror: () => writeProductionMirror2
20535
23405
  });
20536
- import * as fs19 from "fs";
23406
+ import * as fs18 from "fs";
20537
23407
  import * as path23 from "path";
20538
23408
  function parseArgs6(args2) {
20539
23409
  return { autoConfirm: args2.includes("--yes") || args2.includes("-y") };
@@ -20600,7 +23470,7 @@ async function pullCommand(args2) {
20600
23470
  payload.preview_label,
20601
23471
  payload.branch_name
20602
23472
  );
20603
- fs19.mkdirSync(path23.dirname(hubFolder), { recursive: true });
23473
+ fs18.mkdirSync(path23.dirname(hubFolder), { recursive: true });
20604
23474
  console.log("Writing hub configuration...");
20605
23475
  await materializeHubFolder(hubFolder, payload);
20606
23476
  const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
@@ -20654,7 +23524,7 @@ async function pullCommand(args2) {
20654
23524
  }
20655
23525
  async function writeProductionMirror2(workspaceDir, prodPayload) {
20656
23526
  const folder = resolveHubFolder(workspaceDir, prodPayload.hub_id, prodPayload.hub.name, "production", null, null);
20657
- fs19.mkdirSync(path23.dirname(folder), { recursive: true });
23527
+ fs18.mkdirSync(path23.dirname(folder), { recursive: true });
20658
23528
  await materializeHubFolder(folder, prodPayload, { seedAgentContext: false });
20659
23529
  const finalFolder = autoRenameHubFolder(folder, prodPayload.hub.name, "production", prodPayload.hub_id, null, null);
20660
23530
  prependMirrorMarker(finalFolder, prodPayload.hub_id);
@@ -20672,11 +23542,11 @@ async function mirrorLinkedProduction2(client, workspaceDir, productionHubId, or
20672
23542
  function prependMirrorMarker(hubFolder, productionHubId) {
20673
23543
  const hubYaml = path23.join(hubFolder, "hub.yaml");
20674
23544
  try {
20675
- const content = fs19.readFileSync(hubYaml, "utf-8");
23545
+ const content = fs18.readFileSync(hubYaml, "utf-8");
20676
23546
  if (content.startsWith(MIRROR_MARKER_PREFIX2)) return;
20677
23547
  const marker = `${MIRROR_MARKER_PREFIX2} ${productionHubId}. Edits are ignored; push is blocked. Edit the linked preview hub instead.
20678
23548
  `;
20679
- fs19.writeFileSync(hubYaml, marker + content, "utf-8");
23549
+ fs18.writeFileSync(hubYaml, marker + content, "utf-8");
20680
23550
  } catch {
20681
23551
  }
20682
23552
  }
@@ -20720,7 +23590,7 @@ __export(create_exports, {
20720
23590
  createCommand: () => createCommand
20721
23591
  });
20722
23592
  import * as path24 from "path";
20723
- import * as fs20 from "fs";
23593
+ import * as fs19 from "fs";
20724
23594
  function parseArgs7(args2) {
20725
23595
  let autoConfirm = false;
20726
23596
  let folderSelector;
@@ -20747,7 +23617,7 @@ async function createCommand(args2) {
20747
23617
  const gitRoot = findGitRoot();
20748
23618
  const wsLabel = hubsDirLabel(gitRoot);
20749
23619
  if (gitRoot) warnLayoutOnce(gitRoot);
20750
- if (!fs20.existsSync(workspaceDir)) {
23620
+ if (!fs19.existsSync(workspaceDir)) {
20751
23621
  console.error(`No ${wsLabel}/ directory found. Create hub files in ${wsLabel}/<hub>/hub.yaml with \`hub: { name: ... }\` first.`);
20752
23622
  process.exit(1);
20753
23623
  }
@@ -20881,7 +23751,7 @@ var replicate_exports = {};
20881
23751
  __export(replicate_exports, {
20882
23752
  replicateCommand: () => replicateCommand
20883
23753
  });
20884
- import * as fs21 from "fs";
23754
+ import * as fs20 from "fs";
20885
23755
  import * as path25 from "path";
20886
23756
  function parseArgs9(args2) {
20887
23757
  let label;
@@ -20928,8 +23798,8 @@ async function replicateCommand(args2) {
20928
23798
  payload.preview_label,
20929
23799
  payload.branch_name
20930
23800
  );
20931
- const folderPreExisted = fs21.existsSync(hubFolder);
20932
- fs21.mkdirSync(path25.dirname(hubFolder), { recursive: true });
23801
+ const folderPreExisted = fs20.existsSync(hubFolder);
23802
+ fs20.mkdirSync(path25.dirname(hubFolder), { recursive: true });
20933
23803
  const delta = await materializeHubFolder(hubFolder, payload);
20934
23804
  hubFolder = autoRenameHubFolder(
20935
23805
  hubFolder,
@@ -21356,7 +24226,7 @@ __export(migrate_exports, {
21356
24226
  migrateCommand: () => migrateCommand
21357
24227
  });
21358
24228
  import { execFileSync as execFileSync3 } from "child_process";
21359
- import * as fs22 from "fs";
24229
+ import * as fs21 from "fs";
21360
24230
  import * as path29 from "path";
21361
24231
  function isTracked(gitRoot, p) {
21362
24232
  try {
@@ -21370,7 +24240,7 @@ function isTracked(gitRoot, p) {
21370
24240
  }
21371
24241
  }
21372
24242
  function moveDir(gitRoot, from, to) {
21373
- fs22.mkdirSync(path29.dirname(to), { recursive: true });
24243
+ fs21.mkdirSync(path29.dirname(to), { recursive: true });
21374
24244
  if (isTracked(gitRoot, from)) {
21375
24245
  try {
21376
24246
  execFileSync3("git", ["mv", path29.relative(gitRoot, from), path29.relative(gitRoot, to)], {
@@ -21381,7 +24251,7 @@ function moveDir(gitRoot, from, to) {
21381
24251
  } catch {
21382
24252
  }
21383
24253
  }
21384
- fs22.renameSync(from, to);
24254
+ fs21.renameSync(from, to);
21385
24255
  return "fs";
21386
24256
  }
21387
24257
  async function migrateCommand(_args) {
@@ -21544,12 +24414,12 @@ var send_message_exports = {};
21544
24414
  __export(send_message_exports, {
21545
24415
  sendMessageCommand: () => sendMessageCommand
21546
24416
  });
21547
- import * as fs23 from "fs";
24417
+ import * as fs22 from "fs";
21548
24418
  import * as path30 from "path";
21549
24419
  function statAttachment(filePath) {
21550
24420
  let stat2;
21551
24421
  try {
21552
- stat2 = fs23.statSync(filePath);
24422
+ stat2 = fs22.statSync(filePath);
21553
24423
  } catch {
21554
24424
  console.error(`Error: file not found: ${filePath}`);
21555
24425
  process.exit(1);
@@ -21565,7 +24435,7 @@ function readAttachment(filePath, size) {
21565
24435
  const ext = path30.extname(fileName).replace(/^\./, "");
21566
24436
  return {
21567
24437
  file_name: fileName,
21568
- file_binary: fs23.readFileSync(filePath).toString("base64"),
24438
+ file_binary: fs22.readFileSync(filePath).toString("base64"),
21569
24439
  file_size: size,
21570
24440
  ...ext && { file_extension: ext }
21571
24441
  };
@@ -23764,7 +26634,7 @@ var eval_capture_exports = {};
23764
26634
  __export(eval_capture_exports, {
23765
26635
  evalCaptureCommand: () => evalCaptureCommand
23766
26636
  });
23767
- import * as fs24 from "fs";
26637
+ import * as fs23 from "fs";
23768
26638
  import * as path31 from "path";
23769
26639
  import * as yaml12 from "js-yaml";
23770
26640
  function isValidSetName(name) {
@@ -23838,7 +26708,7 @@ async function evalCaptureCommand(args2) {
23838
26708
  console.error(`Resolved path "${path31.relative(hubFolder, targetPath)}" escapes evals/. Aborting.`);
23839
26709
  process.exit(1);
23840
26710
  }
23841
- if (fs24.existsSync(targetPath)) {
26711
+ if (fs23.existsSync(targetPath)) {
23842
26712
  console.error(`File already exists: ${path31.relative(hubFolder, targetPath)}. Use --name to choose a different name.`);
23843
26713
  process.exit(1);
23844
26714
  }
@@ -23871,8 +26741,8 @@ async function evalCaptureCommand(args2) {
23871
26741
  ...captured.evaluator_instructions ? { evaluator_instructions: captured.evaluator_instructions } : {}
23872
26742
  };
23873
26743
  const yamlObj = buildEvalYamlObject(evalEntry, slug);
23874
- fs24.mkdirSync(targetDir, { recursive: true });
23875
- fs24.writeFileSync(targetPath, yaml12.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8");
26744
+ fs23.mkdirSync(targetDir, { recursive: true });
26745
+ fs23.writeFileSync(targetPath, yaml12.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8");
23876
26746
  const relPath = path31.relative(process.cwd(), targetPath);
23877
26747
  console.log(`
23878
26748
  Wrote ${relPath}`);
@@ -25244,20 +28114,20 @@ var init_set_connection_credential = __esm({
25244
28114
  });
25245
28115
 
25246
28116
  // src/lib/org-workspace.ts
25247
- import * as fs25 from "fs";
28117
+ import * as fs24 from "fs";
25248
28118
  import * as path32 from "path";
25249
28119
  import * as yaml13 from "js-yaml";
25250
28120
  function getOrgDir(gitRoot) {
25251
28121
  return resolveLayout(gitRoot).orgDir;
25252
28122
  }
25253
28123
  function orgManifestExists(orgDir) {
25254
- return fs25.existsSync(path32.join(orgDir, ORG_MANIFEST_NAME));
28124
+ return fs24.existsSync(path32.join(orgDir, ORG_MANIFEST_NAME));
25255
28125
  }
25256
28126
  function parseOrgResources(orgDir) {
25257
28127
  const manifestPath = path32.join(orgDir, ORG_MANIFEST_NAME);
25258
28128
  let manifest = {};
25259
- if (fs25.existsSync(manifestPath)) {
25260
- manifest = yaml13.load(fs25.readFileSync(manifestPath, "utf-8")) ?? {};
28129
+ if (fs24.existsSync(manifestPath)) {
28130
+ manifest = yaml13.load(fs24.readFileSync(manifestPath, "utf-8")) ?? {};
25261
28131
  }
25262
28132
  const rawResources = Array.isArray(manifest.resources) ? manifest.resources : [];
25263
28133
  const resourcesDir = path32.join(orgDir, "resources");
@@ -25273,7 +28143,7 @@ function parseOrgResources(orgDir) {
25273
28143
  if (Array.isArray(res.tags)) resource.tags = res.tags;
25274
28144
  if (Array.isArray(res.folders)) resource.folders = res.folders;
25275
28145
  const resDir = path32.join(resourcesDir, slugify(resource.name));
25276
- if (fs25.existsSync(resDir)) {
28146
+ if (fs24.existsSync(resDir)) {
25277
28147
  const files = scanResourceFiles(resDir, "");
25278
28148
  if (files.length > 0) resource.files = files;
25279
28149
  }
@@ -25282,13 +28152,13 @@ function parseOrgResources(orgDir) {
25282
28152
  return { version: 1, resources };
25283
28153
  }
25284
28154
  function writeOrgResources(orgDir, payload) {
25285
- fs25.mkdirSync(orgDir, { recursive: true });
28155
+ fs24.mkdirSync(orgDir, { recursive: true });
25286
28156
  const resources = payload.resources ?? [];
25287
28157
  const manifestResources = resources.map((r) => {
25288
28158
  const { files: _files, ...rest } = r;
25289
28159
  return rest;
25290
28160
  });
25291
- fs25.writeFileSync(
28161
+ fs24.writeFileSync(
25292
28162
  path32.join(orgDir, ORG_MANIFEST_NAME),
25293
28163
  yaml13.dump({ version: 1, resources: manifestResources }, YAML_DUMP_OPTIONS),
25294
28164
  "utf-8"
@@ -25300,10 +28170,10 @@ function writeOrgResources(orgDir, payload) {
25300
28170
  currentSlugs.add(resSlug);
25301
28171
  writeResourceFileTree(path32.join(resourcesDir, resSlug), resource.files || [], orgDir);
25302
28172
  }
25303
- if (fs25.existsSync(resourcesDir)) {
25304
- for (const entry of fs25.readdirSync(resourcesDir, { withFileTypes: true })) {
28173
+ if (fs24.existsSync(resourcesDir)) {
28174
+ for (const entry of fs24.readdirSync(resourcesDir, { withFileTypes: true })) {
25305
28175
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
25306
- fs25.rmSync(path32.join(resourcesDir, entry.name), { recursive: true, force: true });
28176
+ fs24.rmSync(path32.join(resourcesDir, entry.name), { recursive: true, force: true });
25307
28177
  }
25308
28178
  }
25309
28179
  }
@@ -25608,7 +28478,7 @@ var init_report_edit_args = __esm({
25608
28478
  });
25609
28479
 
25610
28480
  // src/lib/file-map.ts
25611
- import * as fs26 from "fs";
28481
+ import * as fs25 from "fs";
25612
28482
  import * as path33 from "path";
25613
28483
  function isSafeRelPath(rel) {
25614
28484
  if (rel.length === 0 || rel.length > 300) return false;
@@ -25625,8 +28495,8 @@ function writeFileMap(targetDir, files) {
25625
28495
  throw new Error(`Refusing to write unsafe path: ${rel}`);
25626
28496
  }
25627
28497
  const abs = path33.join(targetDir, rel);
25628
- fs26.mkdirSync(path33.dirname(abs), { recursive: true });
25629
- fs26.writeFileSync(abs, body, "utf-8");
28498
+ fs25.mkdirSync(path33.dirname(abs), { recursive: true });
28499
+ fs25.writeFileSync(abs, body, "utf-8");
25630
28500
  written.push(rel);
25631
28501
  }
25632
28502
  return written;
@@ -25642,7 +28512,7 @@ var admin_exports = {};
25642
28512
  __export(admin_exports, {
25643
28513
  adminCommand: () => adminCommand
25644
28514
  });
25645
- import * as fs27 from "fs";
28515
+ import * as fs26 from "fs";
25646
28516
  import * as path34 from "path";
25647
28517
  async function adminCommand(args2) {
25648
28518
  const [group, ...afterGroup] = args2;
@@ -25958,7 +28828,7 @@ async function runArchiveRead(positional, flagArgs) {
25958
28828
  exitOnApiError(err);
25959
28829
  throw err;
25960
28830
  }
25961
- fs27.writeFileSync(outPath, zip);
28831
+ fs26.writeFileSync(outPath, zip);
25962
28832
  console.log(`Wrote ${zip.byteLength} bytes to ${outPath}`);
25963
28833
  return;
25964
28834
  }
@@ -26100,7 +28970,7 @@ async function runSkillInstall(positional) {
26100
28970
  throw err;
26101
28971
  }
26102
28972
  const root = findGitRoot() ?? process.cwd();
26103
- const present = HARNESS_SKILL_DIRS.filter((dir) => fs27.existsSync(path34.join(root, dir)));
28973
+ const present = HARNESS_SKILL_DIRS.filter((dir) => fs26.existsSync(path34.join(root, dir)));
26104
28974
  const targets = present.length > 0 ? present : HARNESS_SKILL_DIRS;
26105
28975
  const fileCount = Object.keys(res.files).length;
26106
28976
  const relDirs = targets.map((harness) => {
@@ -28099,7 +30969,7 @@ var init_import = __esm({
28099
30969
 
28100
30970
  // src/data/commands/providers.ts
28101
30971
  import { Command as Command5 } from "commander";
28102
- import { writeFileSync as writeFileSync15 } from "fs";
30972
+ import { writeFileSync as writeFileSync14 } from "fs";
28103
30973
  function providerSegment(provider) {
28104
30974
  if (!VALID_PROVIDERS.includes(provider)) {
28105
30975
  throw expected(`Unknown provider ${JSON.stringify(provider)}. Expected one of: ${VALID_PROVIDERS_HELP}.`);
@@ -28132,7 +31002,7 @@ function buildBasesProvidersCommand() {
28132
31002
  );
28133
31003
  if (opts.to) {
28134
31004
  try {
28135
- writeFileSync15(opts.to, JSON.stringify(data, null, 2));
31005
+ writeFileSync14(opts.to, JSON.stringify(data, null, 2));
28136
31006
  } catch (e) {
28137
31007
  throw expected(`--to ${opts.to}: ${e instanceof Error ? e.message : "could not be written"}`);
28138
31008
  }
@@ -29217,7 +32087,7 @@ var init_file_types = __esm({
29217
32087
 
29218
32088
  // src/data/commands/files.ts
29219
32089
  import { Command as Command12 } from "commander";
29220
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
32090
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
29221
32091
  import { basename as basename18 } from "path";
29222
32092
  function renderFileDiff(fileType, filePath, from, to, d) {
29223
32093
  console.log(sanitizeTerminalText(`${fileType}/${filePath}: v${from} \u2192 v${to}`));
@@ -29297,7 +32167,7 @@ function buildFilesCommand() {
29297
32167
  const { bytes } = await client.download(
29298
32168
  `/v1/${base}/files/${pathSegment(fileType, "file_type")}/${encoded}${versionQs ? `?${versionQs}` : ""}`
29299
32169
  );
29300
- writeFileSync16(out, bytes);
32170
+ writeFileSync15(out, bytes);
29301
32171
  console.log(`Downloaded to ${out}`);
29302
32172
  });
29303
32173
  files.command("history <file_type> <path>").description("List the content versions of a file (newest first)").option("--limit <n>", "Max versions to return").option("--offset <n>", "Pagination offset").action(async function(fileType, filePath, opts) {