@wayai/cli 0.3.161 → 0.3.163

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
@@ -9642,7 +11056,7 @@ var init_api_client = __esm({
9642
11056
  * `toDataProxyPath` knows how WayAI mounts that surface. Identity is the
9643
11057
  * caller's ordinary WayAI session — there is one keyring login, and the
9644
11058
  * backend attests the org over its service binding rather than forwarding a
9645
- * credential (docs/reference/rekor-platform-contracts.md §1).
11059
+ * credential.
9646
11060
  *
9647
11061
  * Returns the surface's `{ data, meta }` envelope rather than unwrapping, so
9648
11062
  * cursor-paginated lists can follow `meta.has_more`. A failure throws the same
@@ -9740,11 +11154,11 @@ var init_api_client = __esm({
9740
11154
  // unlabelled body should fall back rather than announce nothing.
9741
11155
  "Content-Type": body instanceof Uint8Array ? contentType || "application/octet-stream" : "application/json",
9742
11156
  // Names the calling surface for the Data Worker's audit rows, which
9743
- // record `cli` vs `api` vs `mcp` (contracts §1.4). A HINT, not an
11157
+ // record `cli` vs `api` vs `mcp`. A HINT, not an
9744
11158
  // authorization input: any client can send it, so the backend must
9745
11159
  // normalize it against what it knows about its own ingress rather than
9746
11160
  // trust it. Nothing is granted or denied on its value; the Data Worker's
9747
- // audit rows are its consumer (contracts §1.4).
11161
+ // audit rows are its consumer.
9748
11162
  "X-WayAI-Client": "cli"
9749
11163
  },
9750
11164
  // Bytes go on the wire verbatim (file content, attachments); everything
@@ -9832,23 +11246,344 @@ function redactSecrets(value) {
9832
11246
  if (BASE64_SECRET_RE.test(value) && BASE64_MARKER_RE.test(value)) {
9833
11247
  return REDACTED;
9834
11248
  }
9835
- return redactTokenShapes(value);
11249
+ return redactTokenShapes(value);
11250
+ }
11251
+ function normalizeAuthType2(raw) {
11252
+ if (VALID_AUTH_TYPES2.has(raw)) return raw;
11253
+ return LEGACY_AUTH_TYPE_MAP2[raw] ?? raw;
11254
+ }
11255
+ function jsonLengthOrNull2(value) {
11256
+ try {
11257
+ const json = JSON.stringify(value);
11258
+ return typeof json === "string" ? json.length : null;
11259
+ } catch {
11260
+ return null;
11261
+ }
11262
+ }
11263
+ function withinJsonLength2(value, max) {
11264
+ const length = jsonLengthOrNull2(value);
11265
+ return length !== null && length <= max;
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 };
9836
11528
  }
9837
- function normalizeAuthType2(raw) {
9838
- if (VALID_AUTH_TYPES2.has(raw)) return raw;
9839
- return LEGACY_AUTH_TYPE_MAP2[raw] ?? raw;
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 };
9840
11537
  }
9841
- function jsonLengthOrNull2(value) {
9842
- try {
9843
- const json = JSON.stringify(value);
9844
- return typeof json === "string" ? json.length : null;
9845
- } catch {
9846
- return null;
9847
- }
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
+ };
9848
11545
  }
9849
- function withinJsonLength2(value, max) {
9850
- const length = jsonLengthOrNull2(value);
9851
- return length !== null && length <= max;
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
+ }
9852
11587
  }
9853
11588
  function visibleLength2(s) {
9854
11589
  return s.replace(INVISIBLE_NAME_CHARS2, "").length;
@@ -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}`
@@ -10618,6 +12353,119 @@ function refineHubAsCodeDelegation2(config, ctx) {
10618
12353
  });
10619
12354
  });
10620
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) {
12448
+ const agents = config?.agents;
12449
+ if (!Array.isArray(agents)) return;
12450
+ agents.forEach((agent, agentIndex) => {
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
+ }
12467
+ });
12468
+ }
10621
12469
  function identifyTokenType(token) {
10622
12470
  if (token.startsWith("way_")) return "way_token";
10623
12471
  if (token.startsWith("eyJ")) return "jwt";
@@ -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
 
@@ -15775,7 +18639,7 @@ var init_registry = __esm({
15775
18639
  // The organization vault this word named is retired: nothing resolves a value stored
15776
18640
  // there any more, so the surviving surface is `bases credentials` below — per-base
15777
18641
  // rows, which is what a `credential:<name>` reference actually reads. The row stays
15778
- // (this table records how every original word was dispositioned, and §3.2's semantic
18642
+ // (this table records how every original word was dispositioned, and the semantic
15779
18643
  // collision list is checked independently of it) with `shipped: false`, which
15780
18644
  // `registry.test.ts` enforces by asserting the invocation is NOT reachable.
15781
18645
  original: "secrets",
@@ -18452,11 +21316,9 @@ function writeIfAbsent(filePath, content) {
18452
21316
  fs13.writeFileSync(filePath, content, "utf-8");
18453
21317
  return path14.basename(filePath);
18454
21318
  }
18455
- var CLAUDE_MD_SHIM;
18456
21319
  var init_workspace_files = __esm({
18457
21320
  "src/lib/workspace-files.ts"() {
18458
21321
  "use strict";
18459
- CLAUDE_MD_SHIM = "@AGENTS.md\n";
18460
21322
  }
18461
21323
  });
18462
21324
 
@@ -18523,12 +21385,11 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18523
21385
  const yamlContent = yaml7.dump(yamlPayload, YAML_DUMP_OPTIONS);
18524
21386
  writeFileIfChanged(hubFolder, path15.join(hubFolder, "hub.yaml"), yamlContent, log);
18525
21387
  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
- }
21388
+ const seeded = writeIfAbsent(
21389
+ path15.join(hubFolder, "AGENTS.md"),
21390
+ perHubAgentsMd(path15.basename(hubFolder))
21391
+ );
21392
+ if (seeded) log.changed.push(seeded);
18532
21393
  }
18533
21394
  const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
18534
21395
  if (fs14.existsSync(oldYamlPath)) {
@@ -26948,8 +29809,7 @@ Sources:
26948
29809
  is NULL). Each row is resolved to a real user and granted both halves of
26949
29810
  access, or dropped when no account matches. A repaired row is a REAL grant
26950
29811
  to a real person and sends no email, so read --dry-run output as the list of
26951
- people about to get access. Idempotent. See
26952
- docs/runbooks/legacy-org-grant-repair.md.
29812
+ people about to get access. Idempotent.
26953
29813
 
26954
29814
  sandbox Run arbitrary code in a fresh E2B sandbox (harness-agents isolation surface).
26955
29815
  \`exec\` provisions a sandbox from a named Sandbox connection, runs one
@@ -26963,7 +29823,7 @@ Sources:
26963
29823
  turn aborts on its next invocation. \`mass-destroy\` reaps every harness
26964
29824
  sandbox/token/slot that is live NOW (scope: --hub < --org < --all, prefer
26965
29825
  the narrowest). Run \`mass-destroy --dry-run\` first to see the blast
26966
- radius. See docs/runbooks/harness-incident-response.md.
29826
+ radius.
26967
29827
 
26968
29828
  Types (do): ${VALID_TYPES.join(" | ")}
26969
29829
  Tables (analytics): ${VALID_ANALYTICS_TABLES.join(" | ")}