@wayai/cli 0.3.222 → 0.3.223

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
@@ -4604,6 +4604,105 @@ var init_zod = __esm({
4604
4604
  }
4605
4605
  });
4606
4606
 
4607
+ // ../../packages/core/dist/chunk-FK6HBXM7.js
4608
+ var orgResourceIdParam, orgResourceFileIdParam, orgResourceFolderIdParam, hubOrgResourceParam, listOrgResourcesQuery, orgIdQuery, createOrgResourceBody, updateOrgResourceBody, createOrgResourceFolderBody, updateOrgResourceFolderBody, uploadOrgResourceFileBody, updateOrgResourceFileBody, uploadOrgSkillZipBody, linkOrgResourceBody;
4609
+ var init_chunk_FK6HBXM7 = __esm({
4610
+ "../../packages/core/dist/chunk-FK6HBXM7.js"() {
4611
+ "use strict";
4612
+ init_zod();
4613
+ orgResourceIdParam = external_exports.object({
4614
+ id: external_exports.string().uuid()
4615
+ });
4616
+ orgResourceFileIdParam = external_exports.object({
4617
+ file_id: external_exports.string().uuid()
4618
+ });
4619
+ orgResourceFolderIdParam = external_exports.object({
4620
+ folder_id: external_exports.string().uuid()
4621
+ });
4622
+ hubOrgResourceParam = external_exports.object({
4623
+ id: external_exports.string().uuid(),
4624
+ org_resource_id: external_exports.string().uuid()
4625
+ });
4626
+ listOrgResourcesQuery = external_exports.object({
4627
+ organization_id: external_exports.string().uuid(),
4628
+ hub_id: external_exports.string().uuid().optional()
4629
+ });
4630
+ orgIdQuery = external_exports.object({
4631
+ organization_id: external_exports.string().uuid()
4632
+ });
4633
+ createOrgResourceBody = external_exports.object({
4634
+ organization_id: external_exports.string().uuid(),
4635
+ resource_name: external_exports.string().min(1).max(255),
4636
+ resource_description: external_exports.string().max(1e3).nullish(),
4637
+ resource_type: external_exports.enum(["knowledge", "skill"]).default("knowledge"),
4638
+ // Skill-only fields. Required for `resource_type = 'skill'` (validated in
4639
+ // the route layer since cross-field refinement complicates the inferred
4640
+ // input type for callers). Ignored for `knowledge`.
4641
+ skill_name: external_exports.string().min(1).max(255).nullish(),
4642
+ skill_version: external_exports.string().max(50).nullish(),
4643
+ folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4644
+ file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4645
+ enabled: external_exports.boolean().optional(),
4646
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4647
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4648
+ });
4649
+ updateOrgResourceBody = external_exports.object({
4650
+ resource_name: external_exports.string().min(1).max(255).optional(),
4651
+ resource_description: external_exports.string().max(1e3).nullish(),
4652
+ skill_name: external_exports.string().min(1).max(255).nullish(),
4653
+ skill_version: external_exports.string().max(50).nullish(),
4654
+ folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4655
+ file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4656
+ enabled: external_exports.boolean().optional(),
4657
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4658
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4659
+ });
4660
+ createOrgResourceFolderBody = external_exports.object({
4661
+ org_resource_id: external_exports.string().uuid(),
4662
+ folder_name: external_exports.string().min(1).max(255),
4663
+ folder_description: external_exports.string().max(1e3).nullish(),
4664
+ parent_folder_id: external_exports.string().uuid().nullish(),
4665
+ order_index: external_exports.number().int().nonnegative().optional(),
4666
+ enabled: external_exports.boolean().optional(),
4667
+ metadata: external_exports.record(external_exports.unknown()).optional()
4668
+ });
4669
+ updateOrgResourceFolderBody = external_exports.object({
4670
+ folder_name: external_exports.string().min(1).max(255).optional(),
4671
+ folder_description: external_exports.string().max(1e3).nullish(),
4672
+ parent_folder_id: external_exports.string().uuid().nullish(),
4673
+ order_index: external_exports.number().int().nonnegative().optional(),
4674
+ enabled: external_exports.boolean().optional(),
4675
+ metadata: external_exports.record(external_exports.unknown()).optional()
4676
+ });
4677
+ uploadOrgResourceFileBody = external_exports.object({
4678
+ org_resource_id: external_exports.string().uuid(),
4679
+ folder_id: external_exports.string().uuid().nullish(),
4680
+ file_name: external_exports.string().min(1).max(255),
4681
+ file_data: external_exports.string(),
4682
+ // base64
4683
+ mime_type: external_exports.string().optional(),
4684
+ file_size: external_exports.number().int().nonnegative().optional(),
4685
+ resource_file_tags: external_exports.array(external_exports.string()).optional(),
4686
+ metadata: external_exports.record(external_exports.unknown()).optional()
4687
+ });
4688
+ updateOrgResourceFileBody = external_exports.object({
4689
+ resource_file_title: external_exports.string().min(1).max(255).optional(),
4690
+ folder_id: external_exports.string().uuid().nullish(),
4691
+ order_index: external_exports.number().int().nonnegative().optional(),
4692
+ enabled: external_exports.boolean().optional(),
4693
+ resource_file_tags: external_exports.array(external_exports.string()).optional(),
4694
+ metadata: external_exports.record(external_exports.unknown()).optional()
4695
+ });
4696
+ uploadOrgSkillZipBody = external_exports.object({
4697
+ zip_data: external_exports.string().min(1),
4698
+ replace_existing: external_exports.boolean().optional()
4699
+ });
4700
+ linkOrgResourceBody = external_exports.object({
4701
+ org_resource_id: external_exports.string().uuid()
4702
+ });
4703
+ }
4704
+ });
4705
+
4607
4706
  // ../../packages/core/dist/chunk-VXEAR36B.js
4608
4707
  var orgTagIdParam, listOrgTagsQuery, ORG_TAG_NAME_REGEX, ORG_TAG_NAME_MAX_LENGTH, createOrgTagBody, updateOrgTagBody;
4609
4708
  var init_chunk_VXEAR36B = __esm({
@@ -5184,105 +5283,6 @@ var init_chunk_SXDX4GHS = __esm({
5184
5283
  }
5185
5284
  });
5186
5285
 
5187
- // ../../packages/core/dist/chunk-FK6HBXM7.js
5188
- var orgResourceIdParam, orgResourceFileIdParam, orgResourceFolderIdParam, hubOrgResourceParam, listOrgResourcesQuery, orgIdQuery, createOrgResourceBody, updateOrgResourceBody, createOrgResourceFolderBody, updateOrgResourceFolderBody, uploadOrgResourceFileBody, updateOrgResourceFileBody, uploadOrgSkillZipBody, linkOrgResourceBody;
5189
- var init_chunk_FK6HBXM7 = __esm({
5190
- "../../packages/core/dist/chunk-FK6HBXM7.js"() {
5191
- "use strict";
5192
- init_zod();
5193
- orgResourceIdParam = external_exports.object({
5194
- id: external_exports.string().uuid()
5195
- });
5196
- orgResourceFileIdParam = external_exports.object({
5197
- file_id: external_exports.string().uuid()
5198
- });
5199
- orgResourceFolderIdParam = external_exports.object({
5200
- folder_id: external_exports.string().uuid()
5201
- });
5202
- hubOrgResourceParam = external_exports.object({
5203
- id: external_exports.string().uuid(),
5204
- org_resource_id: external_exports.string().uuid()
5205
- });
5206
- listOrgResourcesQuery = external_exports.object({
5207
- organization_id: external_exports.string().uuid(),
5208
- hub_id: external_exports.string().uuid().optional()
5209
- });
5210
- orgIdQuery = external_exports.object({
5211
- organization_id: external_exports.string().uuid()
5212
- });
5213
- createOrgResourceBody = external_exports.object({
5214
- organization_id: external_exports.string().uuid(),
5215
- resource_name: external_exports.string().min(1).max(255),
5216
- resource_description: external_exports.string().max(1e3).nullish(),
5217
- resource_type: external_exports.enum(["knowledge", "skill"]).default("knowledge"),
5218
- // Skill-only fields. Required for `resource_type = 'skill'` (validated in
5219
- // the route layer since cross-field refinement complicates the inferred
5220
- // input type for callers). Ignored for `knowledge`.
5221
- skill_name: external_exports.string().min(1).max(255).nullish(),
5222
- skill_version: external_exports.string().max(50).nullish(),
5223
- folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
5224
- file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
5225
- enabled: external_exports.boolean().optional(),
5226
- tags: external_exports.array(external_exports.string().uuid()).optional(),
5227
- environment: external_exports.enum(["preview", "production", "all"]).optional()
5228
- });
5229
- updateOrgResourceBody = external_exports.object({
5230
- resource_name: external_exports.string().min(1).max(255).optional(),
5231
- resource_description: external_exports.string().max(1e3).nullish(),
5232
- skill_name: external_exports.string().min(1).max(255).nullish(),
5233
- skill_version: external_exports.string().max(50).nullish(),
5234
- folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
5235
- file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
5236
- enabled: external_exports.boolean().optional(),
5237
- tags: external_exports.array(external_exports.string().uuid()).optional(),
5238
- environment: external_exports.enum(["preview", "production", "all"]).optional()
5239
- });
5240
- createOrgResourceFolderBody = external_exports.object({
5241
- org_resource_id: external_exports.string().uuid(),
5242
- folder_name: external_exports.string().min(1).max(255),
5243
- folder_description: external_exports.string().max(1e3).nullish(),
5244
- parent_folder_id: external_exports.string().uuid().nullish(),
5245
- order_index: external_exports.number().int().nonnegative().optional(),
5246
- enabled: external_exports.boolean().optional(),
5247
- metadata: external_exports.record(external_exports.unknown()).optional()
5248
- });
5249
- updateOrgResourceFolderBody = external_exports.object({
5250
- folder_name: external_exports.string().min(1).max(255).optional(),
5251
- folder_description: external_exports.string().max(1e3).nullish(),
5252
- parent_folder_id: external_exports.string().uuid().nullish(),
5253
- order_index: external_exports.number().int().nonnegative().optional(),
5254
- enabled: external_exports.boolean().optional(),
5255
- metadata: external_exports.record(external_exports.unknown()).optional()
5256
- });
5257
- uploadOrgResourceFileBody = external_exports.object({
5258
- org_resource_id: external_exports.string().uuid(),
5259
- folder_id: external_exports.string().uuid().nullish(),
5260
- file_name: external_exports.string().min(1).max(255),
5261
- file_data: external_exports.string(),
5262
- // base64
5263
- mime_type: external_exports.string().optional(),
5264
- file_size: external_exports.number().int().nonnegative().optional(),
5265
- resource_file_tags: external_exports.array(external_exports.string()).optional(),
5266
- metadata: external_exports.record(external_exports.unknown()).optional()
5267
- });
5268
- updateOrgResourceFileBody = external_exports.object({
5269
- resource_file_title: external_exports.string().min(1).max(255).optional(),
5270
- folder_id: external_exports.string().uuid().nullish(),
5271
- order_index: external_exports.number().int().nonnegative().optional(),
5272
- enabled: external_exports.boolean().optional(),
5273
- resource_file_tags: external_exports.array(external_exports.string()).optional(),
5274
- metadata: external_exports.record(external_exports.unknown()).optional()
5275
- });
5276
- uploadOrgSkillZipBody = external_exports.object({
5277
- zip_data: external_exports.string().min(1),
5278
- replace_existing: external_exports.boolean().optional()
5279
- });
5280
- linkOrgResourceBody = external_exports.object({
5281
- org_resource_id: external_exports.string().uuid()
5282
- });
5283
- }
5284
- });
5285
-
5286
5286
  // ../../packages/core/dist/chunk-GPM7PE44.js
5287
5287
  var bootstrapQuery;
5288
5288
  var init_chunk_GPM7PE44 = __esm({
@@ -6193,13 +6193,19 @@ var init_chunk_BEOHBREG = __esm({
6193
6193
  });
6194
6194
 
6195
6195
  // ../../packages/core/dist/chunk-4MRBBR5L.js
6196
- var TOOLSET_SLUG_PATTERN, REKOR_STRUCTURAL_ID_PATTERN, baseEnvironmentSchema, TOOLSET_FILE_OPERATIONS, toolsetFileToolSchema, toolsetSurfaceSchema, toolsetConnectionBindingSchema, rekorToolsetToolsSchema, rekorBoundAttestation, rekorResolvedToolsetSchema, resolvedFileToolSchema, rekorToolsetFileSchema, rekorToolsetFileListSchema, rekorToolsetFileGetSchema, rekorToolsetFilePutSchema, TOOLSET_CUSTOMER_FACING_REFUSAL_MARK, TOOLSET_CUSTOMER_FACING_REFUSAL;
6196
+ function toolsetFileToolName(entry, op) {
6197
+ const names = entry.names;
6198
+ const stated = names?.[op];
6199
+ return stated !== void 0 && stated !== null ? String(stated) : `${op}_${entry.name ?? entry.file_type}`;
6200
+ }
6201
+ var TOOLSET_SLUG_SETTING, TOOLSET_SLUG_PATTERN, REKOR_STRUCTURAL_ID_PATTERN, baseEnvironmentSchema, TOOLSET_FILE_OPERATIONS, toolsetFileToolSchema, toolsetSurfaceSchema, toolsetConnectionBindingSchema, rekorToolsetToolsSchema, rekorBoundAttestation, rekorResolvedToolsetSchema, resolvedFileToolSchema, rekorToolsetFileSchema, rekorToolsetFileListSchema, rekorToolsetFileGetSchema, rekorToolsetFilePutSchema, TOOLSET_CUSTOMER_FACING_REFUSAL_MARK, TOOLSET_CUSTOMER_FACING_REFUSAL;
6197
6202
  var init_chunk_4MRBBR5L = __esm({
6198
6203
  "../../packages/core/dist/chunk-4MRBBR5L.js"() {
6199
6204
  "use strict";
6200
6205
  init_chunk_BEOHBREG();
6201
6206
  init_chunk_DHPUPI4Y();
6202
6207
  init_zod();
6208
+ TOOLSET_SLUG_SETTING = "toolset";
6203
6209
  TOOLSET_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
6204
6210
  REKOR_STRUCTURAL_ID_PATTERN = TOOLSET_SLUG_PATTERN;
6205
6211
  baseEnvironmentSchema = external_exports.enum(["preview", "production"]);
@@ -6783,7 +6789,7 @@ var init_chunk_E4DIIOOE = __esm({
6783
6789
  }
6784
6790
  });
6785
6791
 
6786
- // ../../packages/core/dist/chunk-FMXFEZN4.js
6792
+ // ../../packages/core/dist/chunk-4MXPUC6I.js
6787
6793
  function spokenLineField(label, description2) {
6788
6794
  return { type: "text", label, maxLength: SPOKEN_LINE_MAX_CHARS, default: "", description: description2 };
6789
6795
  }
@@ -6793,8 +6799,8 @@ function byLowercaseName(a, b) {
6793
6799
  return x < y ? -1 : x > y ? 1 : 0;
6794
6800
  }
6795
6801
  var CALLER_LANGUAGE, TTS_VOICE_REPLY_ENABLED_FIELD, COPILOT_TRIGGER_FIELD, SANDBOX_AGENT_FIELDS, AUDIO_LANGUAGE_OPTIONS, whatsapp, instagram, resend, telegram, API_CHANNEL_DELIVERY_EVENTS, apiChannel, OPENAI_REASONING_MODELS, openai, anthropic, googleAiStudio, openRouter, xai, groqStt, openaiStt, elevenLabsStt, openaiTts, groqTts, elevenLabsTts, GEMINI_TTS_VOICES, googleTts, wayai, externalResources, restApiTool, mcpServer, REKOR_TOOLSET_SERVICE_NAME, rekorToolset, CLAUDE_HARNESS_MODELS, CLAUDE_HARNESS_MODEL_OPTIONS, HARNESS_MCP_SERVERS_FIELD, HARNESS_EGRESS_FIELDS, claudeManagedAgents, SPOKEN_LINE_MAX_CHARS, gptLive, CONNECTORS;
6796
- var init_chunk_FMXFEZN4 = __esm({
6797
- "../../packages/core/dist/chunk-FMXFEZN4.js"() {
6802
+ var init_chunk_4MXPUC6I = __esm({
6803
+ "../../packages/core/dist/chunk-4MXPUC6I.js"() {
6798
6804
  "use strict";
6799
6805
  CALLER_LANGUAGE = "caller";
6800
6806
  TTS_VOICE_REPLY_ENABLED_FIELD = {
@@ -6822,7 +6828,17 @@ var init_chunk_FMXFEZN4 = __esm({
6822
6828
  type: "toggle",
6823
6829
  label: "Code Execution Sandbox",
6824
6830
  default: false,
6825
- description: "Let this agent run code in a WayAI sandbox with the sandbox tools, bash, copy_to_sandbox and copy_from_sandbox (add them under Tools): one disposable Linux container per conversation, started on first use and ended after 10 minutes without use or 60 minutes in all."
6831
+ description: "Let this agent run code in a WayAI sandbox with the sandbox tools, bash, copy_to_sandbox and copy_from_sandbox (add them under Tools): a disposable Linux container, shared with the conversation's other agents or private to this agent (Sandbox Scope), started on first use and ended after 10 minutes without use, 60 minutes in all, or when the conversation closes."
6832
+ },
6833
+ sandbox_scope: {
6834
+ type: "select",
6835
+ label: "Sandbox Scope",
6836
+ options: [
6837
+ { value: "conversation", label: "Conversation (shared with the conversation's other agents)" },
6838
+ { value: "agent", label: "Agent (private to this agent)" }
6839
+ ],
6840
+ default: "conversation",
6841
+ description: "Whose sandbox this agent uses. Conversation: shared with the conversation's other agents; every agent sharing it sees its files, and agents sharing it need the same Sandbox Network setting, or a command from one ends the sandbox and starts an empty one. Agent: private to this agent; no other agent sees its files. Set Agent for an agent that handles files others must not see, such as a copilot with internal documents. Each agent sandbox is its own container, counts toward the organization's limit on running sandboxes, and ends with the conversation."
6826
6842
  },
6827
6843
  sandbox_network: {
6828
6844
  type: "select",
@@ -7748,13 +7764,13 @@ var init_chunk_FMXFEZN4 = __esm({
7748
7764
  }
7749
7765
  });
7750
7766
 
7751
- // ../../packages/core/dist/chunk-VOC3AGY4.js
7767
+ // ../../packages/core/dist/chunk-NTJMGSSO.js
7752
7768
  var evalCallInstant, EVAL_CALL_MIN_SECONDS, EVAL_CALL_MAX_SECONDS, EVAL_CALL_SPOKEN_LINE_MAX_CHARS, hubScoped2, createEvalCallBody, createEvalCallResponse, evalCallConversationQuery, evalCallRecordQuery, evalCallSpeaker, evalCallUtterance, evalCallTurn, evalCallRecording, evalCallRecordResponse, evalCallFinishResponse;
7753
- var init_chunk_VOC3AGY4 = __esm({
7754
- "../../packages/core/dist/chunk-VOC3AGY4.js"() {
7769
+ var init_chunk_NTJMGSSO = __esm({
7770
+ "../../packages/core/dist/chunk-NTJMGSSO.js"() {
7755
7771
  "use strict";
7756
7772
  init_chunk_E4DIIOOE();
7757
- init_chunk_FMXFEZN4();
7773
+ init_chunk_4MXPUC6I();
7758
7774
  init_chunk_HZ6CADSY();
7759
7775
  init_chunk_MIWZOSPI();
7760
7776
  init_zod();
@@ -8121,15 +8137,15 @@ var init_chunk_MH4O3YEX = __esm({
8121
8137
  }
8122
8138
  });
8123
8139
 
8124
- // ../../packages/core/dist/chunk-HX2LNKHX.js
8140
+ // ../../packages/core/dist/chunk-CWUV6NL3.js
8125
8141
  function catalogToolSideEffects(toolName) {
8126
8142
  const sideEffects = CATALOG_SIDE_EFFECTS.get(toolName);
8127
8143
  if (sideEffects === void 0) return "unknown";
8128
8144
  return sideEffects ? "side_effects" : "none";
8129
8145
  }
8130
8146
  var NATIVE_TOOL_SCHEMAS, WAYAI_CONNECTOR, SANDBOX_AGENT_ROLES, BASE_NATIVE_TOOLS, NATIVE_TOOLS, CATALOG_SIDE_EFFECTS;
8131
- var init_chunk_HX2LNKHX = __esm({
8132
- "../../packages/core/dist/chunk-HX2LNKHX.js"() {
8147
+ var init_chunk_CWUV6NL3 = __esm({
8148
+ "../../packages/core/dist/chunk-CWUV6NL3.js"() {
8133
8149
  "use strict";
8134
8150
  init_chunk_DHPUPI4Y();
8135
8151
  NATIVE_TOOL_SCHEMAS = {
@@ -9110,9 +9126,10 @@ var init_chunk_HX2LNKHX = __esm({
9110
9126
  // code it runs can send data out of the conversation.
9111
9127
  { tool_native_id: "nt-030", tool_name: "bash", tool_display_name: "Run Code (Bash)", tool_description: "Run a Bash command in the conversation's code sandbox", connector_id: WAYAI_CONNECTOR, operation: "bash", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: true, replay: "reconcile", assignable_roles: SANDBOX_AGENT_ROLES },
9112
9128
  // Copies a conversation file, or a toolset document (`data/`, read through the toolset's `get`),
9113
- // INTO the sandbox: the sandbox is conversation state, so nothing leaves the conversation. It is
9114
- // shared by the conversation's agents that use it, so a document copied in is there for each of
9115
- // them, as anything else an agent leaves in it.
9129
+ // INTO the sandbox: the sandbox is conversation state, so nothing leaves the conversation. The
9130
+ // conversation's sandbox is shared by its agents whose `sandbox_scope` is `conversation`, so a
9131
+ // document copied in is there for each of them, as anything else an agent leaves in it; an agent
9132
+ // set to `agent` copies into its own, which no other agent reaches (decision 147).
9116
9133
  { tool_native_id: "nt-031", tool_name: "copy_to_sandbox", tool_display_name: "Copy File to Sandbox", tool_description: "Copy one of the conversation's files into its code sandbox", connector_id: WAYAI_CONNECTOR, operation: "copy_to_sandbox", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun", assignable_roles: SANDBOX_AGENT_ROLES },
9117
9134
  // Copies a sandbox file OUT into the conversation, as an internal note for the team: nothing
9118
9135
  // reaches the customer until `send_files` sends it. With `path`, it writes a toolset document
@@ -9160,11 +9177,104 @@ var init_chunk_IBL7FANV = __esm({
9160
9177
  });
9161
9178
 
9162
9179
  // ../../packages/core/dist/chunk-PFDZEKDE.js
9163
- var PREVIOUS_CONVERSATIONS_MAX;
9180
+ function parseExpr(expr) {
9181
+ const fnMatch = expr.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*?)\)\s*(?:\.(.*))?$/);
9182
+ if (!fnMatch) return null;
9183
+ const [, fn, rawArgs, rawPath] = fnMatch;
9184
+ const args2 = splitArgs(rawArgs);
9185
+ const path42 = rawPath ? rawPath.split(".").flatMap((seg) => {
9186
+ const indexed = [];
9187
+ seg.replace(/([^\[\]]+)|\[(\d+)\]/g, (_, prop, idx) => {
9188
+ if (prop) indexed.push(prop);
9189
+ else if (idx !== void 0) indexed.push(idx);
9190
+ return "";
9191
+ });
9192
+ return indexed;
9193
+ }) : [];
9194
+ return { fn, args: args2, path: path42 };
9195
+ }
9196
+ function splitArgs(rawArgs) {
9197
+ const trimmed = rawArgs.trim();
9198
+ return trimmed ? trimmed.split(",").map((s) => stripQuotes(s.trim())) : [];
9199
+ }
9200
+ function stripQuotes(s) {
9201
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
9202
+ return s.slice(1, -1);
9203
+ }
9204
+ return s;
9205
+ }
9206
+ function parseCatalogArgs(args2) {
9207
+ let kind = null;
9208
+ let tree = false;
9209
+ for (const token of args2) {
9210
+ if (token === "tree") tree = true;
9211
+ else if ((token === "kb" || token === "skill") && kind === null) kind = token;
9212
+ }
9213
+ return { kind, tree };
9214
+ }
9215
+ function widenKinds(kinds) {
9216
+ if (kinds.has("all") || kinds.size > 1) return "all";
9217
+ return [...kinds][0] ?? null;
9218
+ }
9219
+ function* parseCalls(template) {
9220
+ for (const match of template.matchAll(PLACEHOLDER_RE)) {
9221
+ const parsed = parseExpr(match[1].trim());
9222
+ if (parsed) yield parsed;
9223
+ }
9224
+ }
9225
+ function scanTemplateResources(template) {
9226
+ if (!template || !template.includes("{{")) return EMPTY_SCAN;
9227
+ const scan = { ...EMPTY_SCAN };
9228
+ const names = /* @__PURE__ */ new Set();
9229
+ const paths = /* @__PURE__ */ new Set();
9230
+ const treeKinds = /* @__PURE__ */ new Set();
9231
+ const legacyKinds = /* @__PURE__ */ new Set();
9232
+ for (const { fn, args: args2 } of parseCalls(template)) {
9233
+ switch (fn) {
9234
+ case CATALOG_FN:
9235
+ case LEGACY_CATALOG_FN: {
9236
+ scan.needs_catalog = true;
9237
+ scan.has_catalog_call = true;
9238
+ const { kind, tree } = parseCatalogArgs(args2);
9239
+ if (fn === LEGACY_CATALOG_FN) legacyKinds.add(kind ?? "all");
9240
+ if (tree) treeKinds.add(kind ?? "all");
9241
+ break;
9242
+ }
9243
+ case "agent_skills":
9244
+ scan.needs_catalog = true;
9245
+ break;
9246
+ case "resource_content":
9247
+ if (args2[0]) names.add(args2[0]);
9248
+ break;
9249
+ case "file_content":
9250
+ if (args2[0]) paths.add(args2[0]);
9251
+ break;
9252
+ default:
9253
+ break;
9254
+ }
9255
+ }
9256
+ scan.tree_kinds = widenKinds(treeKinds);
9257
+ scan.legacy_kinds = widenKinds(legacyKinds);
9258
+ scan.resource_content_names = [...names];
9259
+ scan.file_content_paths = [...paths];
9260
+ return scan;
9261
+ }
9262
+ var PREVIOUS_CONVERSATIONS_MAX, PLACEHOLDER_RE, CATALOG_FN, LEGACY_CATALOG_FN, EMPTY_SCAN;
9164
9263
  var init_chunk_PFDZEKDE = __esm({
9165
9264
  "../../packages/core/dist/chunk-PFDZEKDE.js"() {
9166
9265
  "use strict";
9167
9266
  PREVIOUS_CONVERSATIONS_MAX = 20;
9267
+ PLACEHOLDER_RE = /\{\{\s*([^{}]+?)\s*\}\}/g;
9268
+ CATALOG_FN = "files";
9269
+ LEGACY_CATALOG_FN = "resources";
9270
+ EMPTY_SCAN = {
9271
+ needs_catalog: false,
9272
+ has_catalog_call: false,
9273
+ legacy_kinds: null,
9274
+ tree_kinds: null,
9275
+ resource_content_names: [],
9276
+ file_content_paths: []
9277
+ };
9168
9278
  }
9169
9279
  });
9170
9280
 
@@ -9355,7 +9465,7 @@ var init_chunk_T7KQKDET = __esm({
9355
9465
  }
9356
9466
  });
9357
9467
 
9358
- // ../../packages/core/dist/chunk-BUXNYLB7.js
9468
+ // ../../packages/core/dist/chunk-7PJOBY4O.js
9359
9469
  function resolveMonitorTrigger(monitorConfig) {
9360
9470
  if (!monitorConfig || typeof monitorConfig !== "object") return "idle";
9361
9471
  const raw = monitorConfig.trigger;
@@ -9661,10 +9771,10 @@ function refineMonitorRulesStructuredOutput(body, ctx) {
9661
9771
  if (refusal2) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["response_format"], message: refusal2 });
9662
9772
  }
9663
9773
  var NATIVE_TOOL_NAMES, previousConversationsCountField, summarizationThresholdField, flagConditionSchema, MONITOR_TRIGGERS, monitorTriggerSchema, AUTOMATION_GATE_TRIGGERS, NON_FIRING_TRIGGERS, 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, MONITOR_STEER_NOTE_MAX, MONITOR_GATE_AGENT_NAME_MAX, monitorActionSchema, monitorRuleSchema, MONITOR_RULE_KEYS, MONITOR_RULE_SCHEMAS, MONITOR_RULE_TRIGGERS, MONITOR_RULE_TRIGGER_MESSAGE, CALL_UTTERANCE_SPEAKERS, callUtteranceSpeakerSchema, MONITOR_CALL_KEYS, MONITOR_CALL_SCHEMAS, MONITOR_CALL_KEY_MESSAGE, CALL_STEERING_MIN_CONFIDENCE, CALL_UTTERANCE_RULE_NOT_CONFIDENT_MESSAGE, CALL_UTTERANCE_FALLBACK_MESSAGE, CALL_UTTERANCE_FLAG_CONDITIONS_MESSAGE, CALL_STEER_NOTE_BLANK_MESSAGE, MONITOR_USER_MESSAGE_ACTION_KINDS, MONITOR_ASSISTANT_REPLY_ACTION_KINDS, MONITOR_CALL_UTTERANCE_ACTION_KINDS, MONITOR_AUTOMATION_FIRE_ACTION_KINDS, MONITOR_AUTOMATION_ITEM_ACTION_KINDS, INSERT_NOTE_TOOL_NAME, RUN_MONITOR_TOOL_NAME, MONITOR_RULE_ALLOWED_NATIVE_TOOLS, MONITOR_RULE_REENTRY_TOOLS, MONITOR_RULE_REENTRY_TRACKS, AUTOMATION_FIRE_FLAG_CONDITIONS_MESSAGE, AUTOMATION_FIRE_INPUT_SHAPING_MESSAGE, AUTOMATION_ITEM_FLAG_CONDITIONS_MESSAGE, AUTOMATION_ITEM_INPUT_SHAPING_MESSAGE, AUTOMATION_GATE_KEY_MESSAGES, monitorConfigField, flagConditionsField, agentIdParam, listAgentsQuery, agentConnectionsQuery, createAgentBody, updateAgentBody, AGENT_PARAMETER_TYPES, AGENT_PARAMETER_NAME_REGEX, agentParameterCreateSchema, MAX_AGENT_PARAMETERS_PER_REQUEST, createAgentParametersBody;
9664
- var init_chunk_BUXNYLB7 = __esm({
9665
- "../../packages/core/dist/chunk-BUXNYLB7.js"() {
9774
+ var init_chunk_7PJOBY4O = __esm({
9775
+ "../../packages/core/dist/chunk-7PJOBY4O.js"() {
9666
9776
  "use strict";
9667
- init_chunk_HX2LNKHX();
9777
+ init_chunk_CWUV6NL3();
9668
9778
  init_chunk_IBL7FANV();
9669
9779
  init_chunk_PFDZEKDE();
9670
9780
  init_chunk_HZ6CADSY();
@@ -9887,12 +9997,12 @@ var init_chunk_BUXNYLB7 = __esm({
9887
9997
  }
9888
9998
  });
9889
9999
 
9890
- // ../../packages/core/dist/chunk-7YJWGPAR.js
10000
+ // ../../packages/core/dist/chunk-CYBQP65X.js
9891
10001
  var conversationIdParam, conversationHubQuery, claimBody, releaseBody, transferBody, closeBody, additionalContextPayloadSchema, kanbanUpdateBody, annotateConversationBody, observabilityParams, observabilityListParams, deleteUserConversationsBody, deleteUserConversationsResponse, sendMessageBody, listConversationsQuery, flagSourceSchema, getConversationsResponse, getConversationDetailParams, getConversationDetailQuery, getConversationDetailResponse, listWhatsAppTemplatesQuery, sendWhatsAppTemplateBody;
9892
- var init_chunk_7YJWGPAR = __esm({
9893
- "../../packages/core/dist/chunk-7YJWGPAR.js"() {
10002
+ var init_chunk_CYBQP65X = __esm({
10003
+ "../../packages/core/dist/chunk-CYBQP65X.js"() {
9894
10004
  "use strict";
9895
- init_chunk_BUXNYLB7();
10005
+ init_chunk_7PJOBY4O();
9896
10006
  init_chunk_HZ6CADSY();
9897
10007
  init_chunk_DHPUPI4Y();
9898
10008
  init_zod();
@@ -10348,7 +10458,7 @@ var init_chunk_BPCNB5RT = __esm({
10348
10458
  }
10349
10459
  });
10350
10460
 
10351
- // ../../packages/core/dist/chunk-AR73HGPH.js
10461
+ // ../../packages/core/dist/chunk-NRJINQVD.js
10352
10462
  function idempotencyHeaderRefusal(value) {
10353
10463
  if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/.test(value)) {
10354
10464
  return "idempotency_header must be a header name: letters, digits and dashes, at most 64";
@@ -10359,10 +10469,10 @@ function idempotencyHeaderRefusal(value) {
10359
10469
  return null;
10360
10470
  }
10361
10471
  var CATALOG_REPLAY, REPLAY_OVERRIDES, RESERVED_HEADERS;
10362
- var init_chunk_AR73HGPH = __esm({
10363
- "../../packages/core/dist/chunk-AR73HGPH.js"() {
10472
+ var init_chunk_NRJINQVD = __esm({
10473
+ "../../packages/core/dist/chunk-NRJINQVD.js"() {
10364
10474
  "use strict";
10365
- init_chunk_HX2LNKHX();
10475
+ init_chunk_CWUV6NL3();
10366
10476
  CATALOG_REPLAY = new Map(
10367
10477
  NATIVE_TOOLS.map((tool) => [tool.tool_name, tool.replay])
10368
10478
  );
@@ -10595,7 +10705,7 @@ var init_chunk_WEXVYJ5H = __esm({
10595
10705
  }
10596
10706
  });
10597
10707
 
10598
- // ../../packages/core/dist/chunk-MK6D4I2Q.js
10708
+ // ../../packages/core/dist/chunk-QXHWCZ74.js
10599
10709
  function refineToolConfig(value, ctx) {
10600
10710
  const raw = value.tool_config;
10601
10711
  if (raw == null) return;
@@ -10631,10 +10741,10 @@ function refineToolConfig(value, ctx) {
10631
10741
  }
10632
10742
  }
10633
10743
  var toolIdParam, listToolsQuery, toolAgentQuery, deleteToolQuery, toolConnectionsQuery, nativeToolIdSchema, CONTEXT_BOUNDARIES, contextBoundarySchema, addNativeToolBody, addMcpToolBody, toolReplaySettingsFields, toolReplaySettingsSchema, createCustomToolBody, updateCustomToolBody, updateToolExecutionConfigBody;
10634
- var init_chunk_MK6D4I2Q = __esm({
10635
- "../../packages/core/dist/chunk-MK6D4I2Q.js"() {
10744
+ var init_chunk_QXHWCZ74 = __esm({
10745
+ "../../packages/core/dist/chunk-QXHWCZ74.js"() {
10636
10746
  "use strict";
10637
- init_chunk_AR73HGPH();
10747
+ init_chunk_NRJINQVD();
10638
10748
  init_chunk_WEXVYJ5H();
10639
10749
  init_chunk_HZ6CADSY();
10640
10750
  init_zod();
@@ -12006,7 +12116,7 @@ var init_chunk_6NI5RAM4 = __esm({
12006
12116
  }
12007
12117
  });
12008
12118
 
12009
- // ../../packages/core/dist/chunk-EAT2SP4E.js
12119
+ // ../../packages/core/dist/chunk-QJTHNMWD.js
12010
12120
  function refineHubAsCodeCustomTools(config, ctx) {
12011
12121
  const agents = config.agents;
12012
12122
  if (!Array.isArray(agents)) return;
@@ -12542,12 +12652,12 @@ function refineHubAsCodeMonitorRulesOutput(config, ctx) {
12542
12652
  });
12543
12653
  }
12544
12654
  var 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, MAX_CALL_OPENING_CARRIERS, MAX_CALL_OPENING_AGGREGATE_ENCODED_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;
12545
- var init_chunk_EAT2SP4E = __esm({
12546
- "../../packages/core/dist/chunk-EAT2SP4E.js"() {
12655
+ var init_chunk_QJTHNMWD = __esm({
12656
+ "../../packages/core/dist/chunk-QJTHNMWD.js"() {
12547
12657
  "use strict";
12548
- init_chunk_MK6D4I2Q();
12658
+ init_chunk_QXHWCZ74();
12549
12659
  init_chunk_623SRYWZ();
12550
- init_chunk_BUXNYLB7();
12660
+ init_chunk_7PJOBY4O();
12551
12661
  init_chunk_LBDH7LCG();
12552
12662
  init_chunk_4LXDRTO4();
12553
12663
  init_chunk_T4Q5COKJ();
@@ -13556,13 +13666,13 @@ var init_chunk_LQY2OCQL = __esm({
13556
13666
  }
13557
13667
  });
13558
13668
 
13559
- // ../../packages/core/dist/chunk-6N2QIHHQ.js
13669
+ // ../../packages/core/dist/chunk-UWFYCJCK.js
13560
13670
  var AUTOMATION_TRIGGER_TYPES, AUTOMATION_EVENT_TYPES, AUTOMATION_TARGET_TYPES, AUTOMATION_ACTION_TYPES, AUTOMATION_CHANNEL_TYPES, AUTOMATION_PURPOSES, AUTOMATION_NAME_MAX, AUTOMATION_DESCRIPTION_MAX, AUTOMATION_INSTRUCTIONS_MAX, AUTOMATION_TEXT_MAX, AUTOMATION_CRON_MAX, AUTOMATION_WEBHOOK_AUTHS, AUTOMATION_WEBHOOK_HEADERS, AUTOMATION_WEBHOOK_BODY_MAX_BYTES, AUTOMATION_WEBHOOK_HEADER_NAME_MAX, WEBHOOK_RESERVED_HEADERS, automationTriggerSchema, automationTargetSchema, automationActionSchema, automationGateSchema, automationName, automationDescription, automationIdParam, automationHubQuery, automationWebhookParams, AUTOMATION_RUNS_PAGE_MAX, listAutomationRunsQuery, createAutomationBody, updateAutomationBody, AUTOMATION_ALERT_CATEGORIES, AUTOMATION_BLOCKED_ALERT_CATEGORY, automationEventSubscriberParam;
13561
- var init_chunk_6N2QIHHQ = __esm({
13562
- "../../packages/core/dist/chunk-6N2QIHHQ.js"() {
13671
+ var init_chunk_UWFYCJCK = __esm({
13672
+ "../../packages/core/dist/chunk-UWFYCJCK.js"() {
13563
13673
  "use strict";
13564
13674
  init_chunk_LQY2OCQL();
13565
- init_chunk_BUXNYLB7();
13675
+ init_chunk_7PJOBY4O();
13566
13676
  init_chunk_3HSB6J4J();
13567
13677
  init_chunk_F3JHI4Y4();
13568
13678
  init_chunk_DHPUPI4Y();
@@ -14807,6 +14917,7 @@ var init_chunk_TS4EYU3W = __esm({
14807
14917
  var init_contracts = __esm({
14808
14918
  "../../packages/core/dist/contracts/index.js"() {
14809
14919
  "use strict";
14920
+ init_chunk_FK6HBXM7();
14810
14921
  init_chunk_VXEAR36B();
14811
14922
  init_chunk_TIZ57ZBA();
14812
14923
  init_chunk_Q7KIQHAZ();
@@ -14814,7 +14925,6 @@ var init_contracts = __esm({
14814
14925
  init_chunk_IPXBFWUU();
14815
14926
  init_chunk_GK27ENTT();
14816
14927
  init_chunk_SXDX4GHS();
14817
- init_chunk_FK6HBXM7();
14818
14928
  init_chunk_GPM7PE44();
14819
14929
  init_chunk_DGKMMFIR();
14820
14930
  init_chunk_ZC6YGHNX();
@@ -14829,7 +14939,7 @@ var init_contracts = __esm({
14829
14939
  init_chunk_34LE6P7E();
14830
14940
  init_chunk_FPSDNS2E();
14831
14941
  init_chunk_ZXK6N2HN();
14832
- init_chunk_VOC3AGY4();
14942
+ init_chunk_NTJMGSSO();
14833
14943
  init_chunk_C4HEZ7ZI();
14834
14944
  init_chunk_IR2NOSI3();
14835
14945
  init_chunk_EA23LSDS();
@@ -14837,12 +14947,12 @@ var init_contracts = __esm({
14837
14947
  init_chunk_LWMLR2WI();
14838
14948
  init_chunk_ACGAI25T();
14839
14949
  init_chunk_MH4O3YEX();
14840
- init_chunk_7YJWGPAR();
14950
+ init_chunk_CYBQP65X();
14841
14951
  init_chunk_KSTYTQ7I();
14842
14952
  init_chunk_LKBSHANA();
14843
14953
  init_chunk_BPCNB5RT();
14844
- init_chunk_EAT2SP4E();
14845
- init_chunk_MK6D4I2Q();
14954
+ init_chunk_QJTHNMWD();
14955
+ init_chunk_QXHWCZ74();
14846
14956
  init_chunk_623SRYWZ();
14847
14957
  init_chunk_JDJ4EUOL();
14848
14958
  init_chunk_3KYTITOR();
@@ -14852,9 +14962,9 @@ var init_contracts = __esm({
14852
14962
  init_chunk_YULHXJ5M();
14853
14963
  init_chunk_V4FRPRH5();
14854
14964
  init_chunk_ZHJEQBXJ();
14855
- init_chunk_6N2QIHHQ();
14965
+ init_chunk_UWFYCJCK();
14856
14966
  init_chunk_LQY2OCQL();
14857
- init_chunk_BUXNYLB7();
14967
+ init_chunk_7PJOBY4O();
14858
14968
  init_chunk_3HSB6J4J();
14859
14969
  init_chunk_IG6QBZMA();
14860
14970
  init_chunk_ZJJ5ODOD();
@@ -14863,9 +14973,9 @@ var init_contracts = __esm({
14863
14973
  init_chunk_4LXDRTO4();
14864
14974
  init_chunk_E4DIIOOE();
14865
14975
  init_chunk_7CYHPF35();
14866
- init_chunk_FMXFEZN4();
14867
- init_chunk_AR73HGPH();
14868
- init_chunk_HX2LNKHX();
14976
+ init_chunk_4MXPUC6I();
14977
+ init_chunk_NRJINQVD();
14978
+ init_chunk_CWUV6NL3();
14869
14979
  init_chunk_NVKU5PPQ();
14870
14980
  init_chunk_2FCLSQSQ();
14871
14981
  init_chunk_FECLIZTY();
@@ -15286,6 +15396,61 @@ var init_api_client = __esm({
15286
15396
  }
15287
15397
  return new Uint8Array(await response.arrayBuffer());
15288
15398
  }
15399
+ // ---- A preview hub's knowledge cut-over (`wayai knowledge cut-over`) ----
15400
+ //
15401
+ // The setup routes the web console writes through, so each write takes the same checks: the
15402
+ // as-code push never sees a hub's copies of organization resources or their agent links.
15403
+ async listAgents(hubId) {
15404
+ const params = new URLSearchParams({ hub_id: hubId });
15405
+ return this.request("GET", `/api/setup/agents?${params}`);
15406
+ }
15407
+ /** Patch an agent of a preview hub: the text it reads (`updateAgentBody` takes more). */
15408
+ async updateAgent(hubId, agentId, patch) {
15409
+ const params = new URLSearchParams({ hub_id: hubId });
15410
+ await this.request("PATCH", `/api/setup/agents/${encodeURIComponent(agentId)}?${params}`, patch);
15411
+ }
15412
+ async listAgentResources(hubId, agentId) {
15413
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15414
+ return this.request("GET", `/api/setup/resources/agent-resources?${params}`);
15415
+ }
15416
+ async updateAgentResource(body) {
15417
+ return this.request("PATCH", "/api/setup/resources/agent-resources", body);
15418
+ }
15419
+ /** An agent's tools, and the native and connection tools it could add (`available_action: 'Add'`). */
15420
+ async listAgentTools(hubId, agentId) {
15421
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15422
+ return this.request("GET", `/api/setup/tools?${params}`);
15423
+ }
15424
+ async addNativeTool(hubId, agentId, toolNativeId) {
15425
+ return this.request("POST", "/api/setup/tools/native", {
15426
+ hub_id: hubId,
15427
+ agent_id: agentId,
15428
+ tool_native_id: toolNativeId
15429
+ });
15430
+ }
15431
+ async addMcpTool(hubId, agentId, mcpToolId) {
15432
+ return this.request("POST", "/api/setup/tools/mcp", { hub_id: hubId, agent_id: agentId, mcp_tool_id: mcpToolId });
15433
+ }
15434
+ async enableTool(hubId, agentId, toolId) {
15435
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15436
+ await this.request("POST", `/api/setup/tools/${encodeURIComponent(toolId)}/enable?${params}`);
15437
+ }
15438
+ /** Add a token-holding connection (a Base Toolset's) to a preview hub; the answer lists the hub's connections. */
15439
+ async addConnection(body) {
15440
+ return this.request("POST", "/api/setup/connections", body);
15441
+ }
15442
+ async listAutomations(hubId) {
15443
+ const params = new URLSearchParams({ hub_id: hubId });
15444
+ return this.request("GET", `/api/automations?${params}`);
15445
+ }
15446
+ async updateAutomation(hubId, automationId, body) {
15447
+ const params = new URLSearchParams({ hub_id: hubId });
15448
+ await this.request("PATCH", `/api/automations/${encodeURIComponent(automationId)}?${params}`, body);
15449
+ }
15450
+ /** Record one knowledge resource's move into the org's knowledge base (#5142 step 4). */
15451
+ async recordKnowledgeMove(body) {
15452
+ return this.request("POST", "/api/setup/knowledge/moves", body);
15453
+ }
15289
15454
  async me() {
15290
15455
  return this.request("GET", "/api/auth/me");
15291
15456
  }
@@ -33543,6 +33708,12 @@ var init_resource_paths = __esm({
33543
33708
  });
33544
33709
 
33545
33710
  // src/knowledge/source.ts
33711
+ function orgSourceKey(orgResourceId) {
33712
+ return `organization resource ${orgResourceId}`;
33713
+ }
33714
+ function hubSourceKey(productionHubId, resourceId) {
33715
+ return `hub ${productionHubId} resource ${resourceId}`;
33716
+ }
33546
33717
  function movesFromOrg(resource) {
33547
33718
  return resource.resource_type === "knowledge";
33548
33719
  }
@@ -33592,7 +33763,7 @@ function orgSources(resources, rows) {
33592
33763
  skill_name: r.skill_name,
33593
33764
  resource_type: r.resource_type,
33594
33765
  created_at: r.created_at,
33595
- sourceKey: `organization resource ${r.org_resource_id}`,
33766
+ sourceKey: orgSourceKey(r.org_resource_id),
33596
33767
  description: r.resource_description ?? "",
33597
33768
  folderSchema: r.folder_metadata_schema,
33598
33769
  fileSchema: r.file_metadata_schema,
@@ -33641,7 +33812,7 @@ function hubSources(hubId, resources, rows) {
33641
33812
  skill_name: r.skill_name,
33642
33813
  resource_type: r.resource_type,
33643
33814
  created_at: r.created_at,
33644
- sourceKey: `hub ${hubId} resource ${r.resource_id}`,
33815
+ sourceKey: hubSourceKey(hubId, r.resource_id),
33645
33816
  description: r.resource_description ?? "",
33646
33817
  folderSchema: r.folder_metadata_schema,
33647
33818
  fileSchema: r.file_metadata_schema,
@@ -33712,7 +33883,10 @@ function orgFileTypeId(resource) {
33712
33883
  return resource.slug;
33713
33884
  }
33714
33885
  function hubFileTypeId(hubSlug, resource) {
33715
- return `${hubSlug}--${resource.slug}`;
33886
+ return `${hubSlug}${HUB_PART_SEPARATOR}${resource.slug}`;
33887
+ }
33888
+ function hubPartOf(fileTypeId) {
33889
+ return fileTypeId.split(HUB_PART_SEPARATOR)[0];
33716
33890
  }
33717
33891
  function toolAlias(id) {
33718
33892
  const hash = createHash5("sha256").update(id).digest("hex").slice(0, 6);
@@ -34251,7 +34425,7 @@ function describeSkip(skip) {
34251
34425
  return `${skip.fileType}: folder "${skip.folder}" has a description but no path, so it gets no ${INDEX_FILE}.`;
34252
34426
  }
34253
34427
  }
34254
- var MAX_TOOL_BASE_LENGTH, INDEX_FILE, PROVENANCE_PREFIX, MAX_NAME_LENGTH3, MAX_PATH_LENGTH, TITLE_SCHEMA, TAGS_SCHEMA, MAX_NOTE_VALUES, DEFINITION_KEYS, DRAFT, MAX_PATTERNED_VALUE_LENGTH, SUBSCHEMA_KEYS, SUBSCHEMA_MAP_KEYS;
34428
+ var MAX_TOOL_BASE_LENGTH, INDEX_FILE, PROVENANCE_PREFIX, MAX_NAME_LENGTH3, MAX_PATH_LENGTH, TITLE_SCHEMA, TAGS_SCHEMA, MAX_NOTE_VALUES, HUB_PART_SEPARATOR, DEFINITION_KEYS, DRAFT, MAX_PATTERNED_VALUE_LENGTH, SUBSCHEMA_KEYS, SUBSCHEMA_MAP_KEYS;
34255
34429
  var init_plan = __esm({
34256
34430
  "src/knowledge/plan.ts"() {
34257
34431
  "use strict";
@@ -34266,6 +34440,7 @@ var init_plan = __esm({
34266
34440
  TITLE_SCHEMA = { type: "string" };
34267
34441
  TAGS_SCHEMA = { type: "array", items: { type: "string" } };
34268
34442
  MAX_NOTE_VALUES = 3;
34443
+ HUB_PART_SEPARATOR = "--";
34269
34444
  DEFINITION_KEYS = ["$defs", "definitions"];
34270
34445
  DRAFT = "2020-12";
34271
34446
  MAX_PATTERNED_VALUE_LENGTH = 1024;
@@ -34373,17 +34548,17 @@ function sameFileType(remote, spec) {
34373
34548
  function isObject(value) {
34374
34549
  return typeof value === "object" && value !== null && !Array.isArray(value);
34375
34550
  }
34376
- function promotionProblems(result, ids) {
34551
+ function promotionProblems(result, ids, section = "file_types") {
34377
34552
  const answer = isObject(result) ? result : {};
34378
34553
  const problems2 = [];
34379
- for (const [section, bucket2] of Object.entries(isObject(answer.diff) ? answer.diff : {})) {
34554
+ for (const [kind, bucket2] of Object.entries(isObject(answer.diff) ? answer.diff : {})) {
34380
34555
  if (!isObject(bucket2)) continue;
34381
34556
  for (const change of ["added", "modified", "removed"]) {
34382
34557
  for (const entry of Array.isArray(bucket2[change]) ? bucket2[change] : []) {
34383
34558
  const row = isObject(entry) ? isObject(entry.after) ? entry.after : entry : {};
34384
34559
  const id = typeof row.id === "string" ? row.id : JSON.stringify(row).slice(0, 80);
34385
- if (section === "file_types" && change !== "removed" && ids.has(id)) continue;
34386
- problems2.push(`it would also apply ${section} ${change}: "${id}"`);
34560
+ if (kind === section && change !== "removed" && ids.has(id)) continue;
34561
+ problems2.push(`it would also apply ${kind} ${change}: "${id}"`);
34387
34562
  }
34388
34563
  }
34389
34564
  }
@@ -34652,6 +34827,253 @@ var init_headers = __esm({
34652
34827
  }
34653
34828
  });
34654
34829
 
34830
+ // src/data/commands/tokens.ts
34831
+ import { Command } from "commander";
34832
+ function rawTokenFrom(data) {
34833
+ const token = typeof data === "object" && data !== null && "token" in data ? data.token : void 0;
34834
+ if (typeof token !== "string" || !RAW_TOKEN_PATTERN.test(token)) {
34835
+ throw new Error("Token creation response did not contain a valid rec_ token");
34836
+ }
34837
+ return token;
34838
+ }
34839
+ function addTokenLifecycleOptions(cmd) {
34840
+ return cmd.option("--expires-at <date>", "Expiration date (ISO 8601, e.g. 2026-06-01T00:00:00Z)").option(
34841
+ "--ttl <duration>",
34842
+ "Relative expiry, e.g. 10m, 2h, 7d (use for disposable probe/test tokens)"
34843
+ ).option(
34844
+ "--description <text>",
34845
+ 'What holds this token (e.g. "behind credential X"); shown in list and revoke warnings'
34846
+ ).addHelpText("after", TOKEN_OUTPUT_HELP);
34847
+ }
34848
+ function tokenCreationOutput(cmd, opts) {
34849
+ return opts.tokenOnly ? "token" : outputFormat(cmd);
34850
+ }
34851
+ function printCreatedToken(data, output) {
34852
+ if (output !== "token") {
34853
+ printOutput(data, output);
34854
+ return;
34855
+ }
34856
+ console.log(rawTokenFrom(data));
34857
+ }
34858
+ function resolveExpiresAt(opts) {
34859
+ if (opts.ttl && opts.expiresAt) {
34860
+ throw expected("--ttl and --expires-at are mutually exclusive \u2014 pass one.");
34861
+ }
34862
+ if (opts.ttl) return new Date(Date.now() + parseDuration(opts.ttl, "--ttl")).toISOString();
34863
+ return opts.expiresAt;
34864
+ }
34865
+ function applyTokenLifecycleOpts(body, opts) {
34866
+ const expiresAt = resolveExpiresAt(opts);
34867
+ if (expiresAt) body.expires_at = expiresAt;
34868
+ if (opts.description) body.description = opts.description;
34869
+ }
34870
+ function listAllTokens(client) {
34871
+ return client.collectPages(
34872
+ (cursor) => `/v1/tokens?limit=${TOKEN_LIST_PAGE_LIMIT}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`
34873
+ );
34874
+ }
34875
+ function pruneRow(t, reason) {
34876
+ return {
34877
+ token_id: t.token_id,
34878
+ name: t.name,
34879
+ reason,
34880
+ last_used: t.last_used_at ?? "never"
34881
+ };
34882
+ }
34883
+ function buildBasesTokensCommand() {
34884
+ const tokens2 = new Command("tokens").description("Manage API tokens");
34885
+ addTokenLifecycleOptions(
34886
+ tokens2.command("create").description("Create a scoped API token").requiredOption("--name <name>", "Token name").requiredOption("--grants <json>", "Grant definitions as JSON array").option(
34887
+ "--token-only",
34888
+ "Print only the raw rec_ token for secure command substitution (shown once)"
34889
+ )
34890
+ ).action(async function(opts) {
34891
+ const grants = parseData(opts.grants, "--grants");
34892
+ const body = { name: opts.name, grants };
34893
+ applyTokenLifecycleOpts(body, opts);
34894
+ const client = await createDataClient();
34895
+ const data = await client.request("POST", "/v1/tokens", body);
34896
+ printCreatedToken(data, tokenCreationOutput(this, opts));
34897
+ });
34898
+ addTokenLifecycleOptions(
34899
+ // The only token verb that addresses a base, so it alone declares `--base`.
34900
+ // Putting it on the `tokens` group would advertise a flag that does nothing
34901
+ // on `list`, `revoke` and `prune` — the defect `withBaseOption` exists to
34902
+ // avoid.
34903
+ withBaseOption(tokens2.command("create-for-toolset <slug>")).description(
34904
+ "Create a toolset-bound (consumer) token whose authorization IS the toolset tool surface \u2014 it can call exactly the tools that toolset exposes (those record types + operations, relationships, batch, and sql only if enabled) and nothing else. Binds to the configured --base."
34905
+ ).option("--name <name>", 'Token name (default: "<slug> consumer")').option(
34906
+ "--token-only",
34907
+ "Print only the raw rec_ token for secure command substitution (shown once)"
34908
+ )
34909
+ ).action(async function(slug, opts) {
34910
+ const base = requireBase(this);
34911
+ const body = {
34912
+ name: opts.name ?? `${slug} consumer`,
34913
+ toolset_binding: { base_id: base, toolset_id: slug }
34914
+ };
34915
+ applyTokenLifecycleOpts(body, opts);
34916
+ const client = await createDataClient();
34917
+ const data = await client.request("POST", "/v1/tokens", body);
34918
+ const output = tokenCreationOutput(this, opts);
34919
+ printCreatedToken(data, output);
34920
+ if (output === "table") {
34921
+ console.log(`
34922
+ MCP URL: ${toolsetMcpUrl(slug)}`);
34923
+ }
34924
+ });
34925
+ tokens2.command("list").description("List API tokens (status, last_used_at, expires_at, description)").action(async function() {
34926
+ const client = await createDataClient();
34927
+ printOutput(await listAllTokens(client), outputFormat(this));
34928
+ });
34929
+ tokens2.command("revoke <token_id>").description(
34930
+ "Revoke an API token (permanent). A recently-used or toolset-bound token requires --force."
34931
+ ).option("--force", "Revoke even if the token appears to be in live use").action(async function(tokenId, opts) {
34932
+ const client = await createDataClient();
34933
+ const id = pathSegment(tokenId, "token id");
34934
+ const path42 = (force) => `/v1/tokens/${id}${force ? "?force=true" : ""}`;
34935
+ const format = outputFormat(this);
34936
+ const revoked = (forced) => printOutput({ token_id: tokenId, revoked: true, forced }, format);
34937
+ if (opts.force) {
34938
+ await client.request("DELETE", path42(true));
34939
+ revoked(true);
34940
+ return;
34941
+ }
34942
+ try {
34943
+ await client.request("DELETE", path42(false));
34944
+ revoked(false);
34945
+ } catch (err) {
34946
+ if (!(err instanceof ApiError) || err.status !== 409 || dataErrorDetails(err)?.requires_force !== true) {
34947
+ throw err;
34948
+ }
34949
+ console.error(err.message);
34950
+ if (!isInteractive()) {
34951
+ console.error("\nRe-run with --force to revoke anyway.");
34952
+ return process.exit(1);
34953
+ }
34954
+ if (!await confirm("Revoke anyway?")) {
34955
+ console.error("Aborted.");
34956
+ return process.exit(1);
34957
+ }
34958
+ await client.request("DELETE", path42(true));
34959
+ revoked(true);
34960
+ }
34961
+ });
34962
+ tokens2.command("prune").description(
34963
+ "Revoke stale tokens, selected by staleness only (expired, or unused past --unused-since) \u2014 never by name. Previews by default; revoking requires --yes or an interactive confirmation."
34964
+ ).option(
34965
+ "--unused-since <duration>",
34966
+ "Also prune tokens with no activity for this long (e.g. 30d). Skips toolset-bound and legacy usage-untracked tokens."
34967
+ ).option("--include-bound", "Allow pruning stale toolset-bound (consumer) tokens").option("-y, --yes", "Revoke without prompting (required when non-interactive)").action(async function(opts) {
34968
+ const staleMs = opts.unusedSince ? parseDuration(opts.unusedSince, "--unused-since") : void 0;
34969
+ const client = await createDataClient();
34970
+ const allTokens = await listAllTokens(client);
34971
+ const now = Date.now();
34972
+ const targets = [];
34973
+ const skipped = [];
34974
+ for (const t of allTokens) {
34975
+ const expired = t.status ? t.status === "expired" : !!t.expires_at && Date.parse(t.expires_at) <= now;
34976
+ if (expired) {
34977
+ targets.push(pruneRow(t, "expired"));
34978
+ continue;
34979
+ }
34980
+ if (staleMs === void 0) continue;
34981
+ const lastActivity = Date.parse(t.last_used_at ?? t.created_at);
34982
+ if (!(now - lastActivity > staleMs)) continue;
34983
+ if (t.usage_tracked !== true) {
34984
+ skipped.push(
34985
+ pruneRow(
34986
+ t,
34987
+ "usage untracked (legacy token) \u2014 revoke individually if truly unused"
34988
+ )
34989
+ );
34990
+ } else if (t.toolset_binding && !opts.includeBound) {
34991
+ skipped.push(
34992
+ pruneRow(
34993
+ t,
34994
+ `toolset-bound ('${t.toolset_binding.toolset_id}') \u2014 pass --include-bound to prune`
34995
+ )
34996
+ );
34997
+ } else {
34998
+ targets.push(pruneRow(t, `unused for over ${opts.unusedSince}`));
34999
+ }
35000
+ }
35001
+ const format = outputFormat(this);
35002
+ const json = format === "json";
35003
+ const note = (line) => json ? console.error(line) : console.log(line);
35004
+ const printPreview = () => {
35005
+ if (targets.length === 0) {
35006
+ note("Nothing to prune.");
35007
+ } else {
35008
+ note(`Would revoke ${targets.length} token(s):`);
35009
+ if (!json) printOutput(targets, format);
35010
+ }
35011
+ if (skipped.length === 0) return;
35012
+ note(`
35013
+ Skipped ${skipped.length} stale token(s):`);
35014
+ if (!json) printOutput(skipped, format);
35015
+ };
35016
+ printPreview();
35017
+ if (targets.length === 0) {
35018
+ if (json) printOutput({ revoked: 0, failed: 0, targets, skipped }, format);
35019
+ return;
35020
+ }
35021
+ if (!opts.yes) {
35022
+ if (!isInteractive()) {
35023
+ console.error("\nPreview only \u2014 re-run with --yes to revoke these tokens.");
35024
+ if (json) printOutput({ preview: true, revoked: 0, failed: 0, targets, skipped }, format);
35025
+ return process.exit(1);
35026
+ }
35027
+ if (!await confirm(`
35028
+ Revoke ${targets.length} token(s)?`)) {
35029
+ console.error("Aborted.");
35030
+ return process.exit(1);
35031
+ }
35032
+ }
35033
+ let revoked = 0;
35034
+ let failed = 0;
35035
+ for (const t of targets) {
35036
+ try {
35037
+ await client.request(
35038
+ "DELETE",
35039
+ `/v1/tokens/${pathSegment(t.token_id, "token id")}?force=true`
35040
+ );
35041
+ revoked++;
35042
+ } catch (err) {
35043
+ failed++;
35044
+ console.error(
35045
+ `Failed to revoke ${t.token_id} ('${sanitizeTerminalText(t.name)}'): ${err instanceof Error ? err.message : String(err)}`
35046
+ );
35047
+ }
35048
+ }
35049
+ if (json) printOutput({ revoked, failed, targets, skipped }, format);
35050
+ else console.log(`Revoked ${revoked} token(s)${failed > 0 ? `, ${failed} failed` : ""}.`);
35051
+ if (failed > 0) return process.exit(1);
35052
+ });
35053
+ return tokens2;
35054
+ }
35055
+ var RAW_TOKEN_PATTERN, TOKEN_OUTPUT_HELP, TOKEN_LIST_PAGE_LIMIT;
35056
+ var init_tokens = __esm({
35057
+ "src/data/commands/tokens.ts"() {
35058
+ "use strict";
35059
+ init_client();
35060
+ init_output();
35061
+ init_helpers();
35062
+ init_api_client();
35063
+ init_terminal_output();
35064
+ init_utils();
35065
+ init_expected();
35066
+ RAW_TOKEN_PATTERN = /^rec_[A-Za-z0-9_-]{43}$/;
35067
+ TOKEN_OUTPUT_HELP = `
35068
+ Output:
35069
+ --json prints the unwrapped response object; the secret is at top-level .token, never .data.token.
35070
+ --token-only prints only a validated rec_ token and overrides table/JSON formatting.
35071
+ The raw token is shown once. Capture it securely and do not log it.
35072
+ `;
35073
+ TOKEN_LIST_PAGE_LIMIT = 250;
35074
+ }
35075
+ });
35076
+
34655
35077
  // src/knowledge/remote.ts
34656
35078
  function pacedRequestsPerMinute(path42, fallback) {
34657
35079
  const budget = getEndpointConfig(path42)?.userLimit;
@@ -34702,6 +35124,14 @@ function knowledgeBase(client) {
34702
35124
  const request = (method, path42, body) => pacer.run(() => client.request(method, path42, body));
34703
35125
  const baseSeg = (id) => pathSegment(id, "--base");
34704
35126
  const typeSeg = (id) => pathSegment(id, "file type");
35127
+ const toolsetSeg = (id) => pathSegment(id, "toolset slug");
35128
+ const promoteOnly = (production, preview, section, ids, dryRun) => request("POST", `/v1/${baseSeg(production)}/promote`, {
35129
+ source_base_id: preview,
35130
+ dry_run: dryRun,
35131
+ ...Object.fromEntries(ENTITY_KINDS.map((kind) => [kind, []])),
35132
+ file_types: [],
35133
+ [section]: ids
35134
+ });
34705
35135
  return {
34706
35136
  getBase: (id) => unlessMissing(() => pacer.run(() => getBaseRecord(client, id))),
34707
35137
  listPreviews: (id) => pacer.run(
@@ -34713,14 +35143,7 @@ function knowledgeBase(client) {
34713
35143
  const { id, ...body } = spec;
34714
35144
  await request("PUT", `/v1/${baseSeg(base)}/file-types/${typeSeg(id)}`, body);
34715
35145
  },
34716
- async promoteFileTypes(production, preview, ids, dryRun) {
34717
- return request("POST", `/v1/${baseSeg(production)}/promote`, {
34718
- source_base_id: preview,
34719
- dry_run: dryRun,
34720
- ...Object.fromEntries(ENTITY_KINDS.map((kind) => [kind, []])),
34721
- file_types: ids
34722
- });
34723
- },
35146
+ promoteFileTypes: (production, preview, ids, dryRun) => promoteOnly(production, preview, "file_types", ids, dryRun),
34724
35147
  async listFiles(base, fileType) {
34725
35148
  const rows = [];
34726
35149
  for (let page = 0; page < MAX_LIST_PAGES; page++) {
@@ -34731,6 +35154,13 @@ function knowledgeBase(client) {
34731
35154
  }
34732
35155
  throw expected(`The listing of file type "${fileType}" on base "${base}" did not end after ${MAX_LIST_PAGES} pages.`);
34733
35156
  },
35157
+ getToolset: (base, slug) => unlessMissing(() => request("GET", `/v1/${baseSeg(base)}/toolsets/${toolsetSeg(slug)}`)),
35158
+ async putToolset(base, spec) {
35159
+ const { id, ...body } = spec;
35160
+ await request("PUT", `/v1/${baseSeg(base)}/toolsets/${toolsetSeg(id)}`, { ...body, actions: [] });
35161
+ },
35162
+ promoteToolsets: (production, preview, slugs, dryRun) => promoteOnly(production, preview, "toolsets", slugs, dryRun),
35163
+ mintToolsetToken: async (base, slug, name) => rawTokenFrom(await request("POST", "/v1/tokens", { name, toolset_binding: { base_id: base, toolset_id: slug } })),
34734
35164
  // `requestRaw`: the page is read whole, `{ data: … }`, as WayAI's copy of its answer declares it
34735
35165
  // (`@wayai/core/contracts` `rekorFileSnapshotAnswerSchema`).
34736
35166
  snapshotPage: (preview, cursor) => pacer.run(() => client.requestRaw("POST", `/v1/${baseSeg(preview)}/files/snapshot`, cursor === void 0 ? {} : { cursor })),
@@ -34746,7 +35176,70 @@ function knowledgeBase(client) {
34746
35176
  }
34747
35177
  };
34748
35178
  }
34749
- var LIST_PAGE, MAX_LIST_PAGES, MIGRATION_LABEL, READER_BUDGETS;
35179
+ function connectionsOf(rows) {
35180
+ return (rows ?? []).flatMap(
35181
+ (c) => c.connection_id ? [{ connection_id: c.connection_id, connection_display_name: c.connection_display_name, connector_id: c.connector_id, settings: c.settings }] : []
35182
+ );
35183
+ }
35184
+ function hubSetup(api, orgId, pacers = {
35185
+ setup: pacerFor(CUT_OVER_BUDGETS.setup, "setup"),
35186
+ automations: pacerFor(CUT_OVER_BUDGETS.automations, "automations")
35187
+ }) {
35188
+ const run = (send) => pacers.setup.run(send);
35189
+ const auto = (send) => pacers.automations.run(send);
35190
+ return {
35191
+ listHubs: async () => hubsOf(await run(() => api.getUserSchema()), orgId),
35192
+ hubDiff: async (hubId) => (await run(() => api.getHubDiff(hubId))).data,
35193
+ // The rows are the hub's `resource` rows whole, the move's mark (`moved_file_type`) among them.
35194
+ listResources: async (hubId) => (await run(() => api.listHubResources(hubId))).data ?? [],
35195
+ listAgents: async (hubId) => (await run(() => api.listAgents(hubId))).data ?? [],
35196
+ listAgentLinks: async (hubId, agentId) => (await run(() => api.listAgentResources(hubId, agentId))).resources ?? [],
35197
+ listAgentTools: async (hubId, agentId) => {
35198
+ const tools = await run(() => api.listAgentTools(hubId, agentId));
35199
+ return { native: tools.tools ?? [], connection: (tools.mcp_tools ?? []).map((t) => ({ ...t, tool_id: t.tool_id ?? null })) };
35200
+ },
35201
+ listConnections: async (hubId) => connectionsOf((await run(() => api.listConnections(hubId))).connections),
35202
+ listAutomations: async (hubId) => (await auto(() => api.listAutomations(hubId))).automations ?? [],
35203
+ // The answer lists the hub's connections, the new one among them.
35204
+ addToolsetConnection: async (hubId, input) => connectionsOf(
35205
+ (await run(
35206
+ () => api.addConnection({
35207
+ hub_id: hubId,
35208
+ connector_id: input.connectorId,
35209
+ connection_display_name: input.name,
35210
+ settings: { [TOOLSET_SLUG_SETTING]: input.toolset },
35211
+ access_token: input.token
35212
+ })
35213
+ )).connections
35214
+ ),
35215
+ async syncConnection(hubId, connectionId) {
35216
+ const answer = await run(() => api.syncMcp(hubId, connectionId));
35217
+ if (!answer.success) {
35218
+ throw expected(`Syncing connection ${connectionId}'s tools failed: ${sanitizeTerminalText(answer.message ?? "no reason given")}`);
35219
+ }
35220
+ },
35221
+ async addNativeTool(hubId, agentId, toolNativeId) {
35222
+ await run(() => api.addNativeTool(hubId, agentId, toolNativeId));
35223
+ },
35224
+ async addConnectionTool(hubId, agentId, mcpToolId) {
35225
+ await run(() => api.addMcpTool(hubId, agentId, mcpToolId));
35226
+ },
35227
+ async enableTool(hubId, agentId, toolId) {
35228
+ await run(() => api.enableTool(hubId, agentId, toolId));
35229
+ },
35230
+ async updateAgentText(hubId, agentId, patch) {
35231
+ await run(() => api.updateAgent(hubId, agentId, patch));
35232
+ },
35233
+ async updateAutomationAction(hubId, automationId, action) {
35234
+ await auto(() => api.updateAutomation(hubId, automationId, { action }));
35235
+ },
35236
+ async disableLink(hubId, agentId, resourceId) {
35237
+ await run(() => api.updateAgentResource({ hub_id: hubId, agent_id: agentId, resource_id: resourceId, enabled: false }));
35238
+ },
35239
+ recordMove: async (body) => (await run(() => api.recordKnowledgeMove(body))).data.recorded
35240
+ };
35241
+ }
35242
+ var LIST_PAGE, MAX_LIST_PAGES, MIGRATION_LABEL, READER_BUDGETS, CUT_OVER_BUDGETS;
34750
35243
  var init_remote = __esm({
34751
35244
  "src/knowledge/remote.ts"() {
34752
35245
  "use strict";
@@ -34759,10 +35252,14 @@ var init_remote = __esm({
34759
35252
  init_types2();
34760
35253
  init_headers();
34761
35254
  init_helpers();
35255
+ init_terminal_output();
35256
+ init_tokens();
35257
+ init_rekor_toolset();
34762
35258
  LIST_PAGE = 1e3;
34763
35259
  MAX_LIST_PAGES = 1e3;
34764
35260
  MIGRATION_LABEL = "wayai knowledge migrate";
34765
35261
  READER_BUDGETS = { setup: "/api/setup/organization-resources", sign: "/api/files/sign" };
35262
+ CUT_OVER_BUDGETS = { setup: "/api/setup/agents", automations: "/api/automations" };
34766
35263
  }
34767
35264
  });
34768
35265
 
@@ -34797,6 +35294,520 @@ var init_refresh_preview = __esm({
34797
35294
  }
34798
35295
  });
34799
35296
 
35297
+ // ../../packages/core/dist/catalog/index.js
35298
+ function getConnectors(filter) {
35299
+ if (!filter?.connector_type && !filter?.service_name) return CONNECTORS;
35300
+ return CONNECTORS.filter(
35301
+ (c) => (!filter.connector_type || c.connector_type === filter.connector_type) && (!filter.service_name || c.service_name === filter.service_name)
35302
+ );
35303
+ }
35304
+ function getConnectorById(id) {
35305
+ return connectorById.get(id) ?? null;
35306
+ }
35307
+ function isRekorToolsetConnector(connector) {
35308
+ return connector?.service_name === REKOR_TOOLSET_SERVICE_NAME;
35309
+ }
35310
+ function isRekorToolsetConnectorId(connectorId) {
35311
+ return typeof connectorId === "string" && isRekorToolsetConnector(connectorById.get(connectorId));
35312
+ }
35313
+ var COUNTER_KEYS, STT_PRICING, CONTAINER_PRICING, connectorById, nativeToolById;
35314
+ var init_catalog = __esm({
35315
+ "../../packages/core/dist/catalog/index.js"() {
35316
+ "use strict";
35317
+ init_chunk_4MXPUC6I();
35318
+ init_chunk_NRJINQVD();
35319
+ init_chunk_CWUV6NL3();
35320
+ init_chunk_MIWZOSPI();
35321
+ init_chunk_DHPUPI4Y();
35322
+ COUNTER_KEYS = Object.keys({
35323
+ input_tokens: true,
35324
+ output_tokens: true,
35325
+ cache_read_input_tokens: true,
35326
+ cache_creation_input_tokens: true,
35327
+ cache_creation_1h_input_tokens: true
35328
+ });
35329
+ STT_PRICING = {
35330
+ "Openai/whisper-1": { per_second: 6e-3 / 60 },
35331
+ "Openai/gpt-4o-mini-transcribe": { per_second: 3e-3 / 60 },
35332
+ "Groq/whisper-large-v3-turbo": { per_second: 0.04 / 3600 },
35333
+ "ElevenLabs/scribe_v2": { per_second: 0.22 / 3600 }
35334
+ };
35335
+ CONTAINER_PRICING = {
35336
+ anthropic_per_request_usd: 0.05 * 10 / 3600,
35337
+ // $0.05/hr × ~10s/request ≈ $0.000139
35338
+ openai_per_session_usd: 0.03
35339
+ // 1GB tier, 20-min session
35340
+ };
35341
+ connectorById = new Map(CONNECTORS.map((c) => [c.connector_id, c]));
35342
+ nativeToolById = new Map(NATIVE_TOOLS.map((t) => [t.tool_native_id, t]));
35343
+ }
35344
+ });
35345
+
35346
+ // src/knowledge/cut-over.ts
35347
+ import { createHash as createHash6 } from "crypto";
35348
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
35349
+ function toolsetSlug(productionHubId, fileTypes) {
35350
+ const hash = createHash6("sha256").update([productionHubId, ...fileTypes].join("\n")).digest("hex").slice(0, 12);
35351
+ return `${CUT_OVER_TOOLSET_PREFIX}${hash}`;
35352
+ }
35353
+ function toolsetEntry(fileType) {
35354
+ const base = toolBase(fileType);
35355
+ return { file_type: fileType, operations: [...FILE_OPERATIONS], ...base === fileType ? {} : { name: base } };
35356
+ }
35357
+ function toolsetToolName(op, fileType) {
35358
+ return toolsetFileToolName(toolsetEntry(fileType), op);
35359
+ }
35360
+ function rewriteCitations(text, fileTypeBySlug) {
35361
+ return text.replace(CITATION_RE, (whole, slash, slug) => {
35362
+ const fileType = fileTypeBySlug.get(slug.toLowerCase());
35363
+ return fileType ? `${slash}${DATA_NAMESPACE}/${fileType}` : whole;
35364
+ });
35365
+ }
35366
+ function knowledgePlaceholders(template, losing) {
35367
+ if (losing.length === 0 || !template.includes("{{")) return [];
35368
+ const scan = scanTemplateResources(template);
35369
+ const kinds = scanTemplateResources(template.replace(/\{\{(\s*)files(\s*\()/g, "{{$1resources$2")).legacy_kinds;
35370
+ const uses = [];
35371
+ if (kinds === "all" || kinds === "kb") uses.push("a {{files()}} catalog that lists knowledge");
35372
+ const names = new Set(losing.map((r) => r.name));
35373
+ for (const name of scan.resource_content_names) if (names.has(name)) uses.push(`{{resource_content(${name})}}`);
35374
+ const slugs = new Set(losing.map((r) => r.slug));
35375
+ for (const path42 of scan.file_content_paths) {
35376
+ const [namespace, slug] = cleanRelativePath(path42).split("/");
35377
+ if (namespace?.toLowerCase() === RESOURCE_NAMESPACE && slug && slugs.has(slug.toLowerCase())) {
35378
+ uses.push(`{{file_content(${path42})}}`);
35379
+ }
35380
+ }
35381
+ return uses;
35382
+ }
35383
+ function sameToolset(remote, spec) {
35384
+ if (!remote) return false;
35385
+ const files = (entries) => [...entries ?? []].map((e) => ({ file_type: e.file_type, operations: [...e.operations].sort(), name: e.name ?? null, path_prefix: e.path_prefix || null })).sort((a, b) => a.file_type.localeCompare(b.file_type));
35386
+ return remote.name === spec.name && (remote.description ?? "") === spec.description && (remote.actions ?? []).length === 0 && (remote.relationships ?? []).length === 0 && !remote.batch && !remote.sql_query && isDeepStrictEqual4(files(remote.files), files(spec.files));
35387
+ }
35388
+ function toolsetConnectorId() {
35389
+ const connector = getConnectors().find((c) => isRekorToolsetConnector(c));
35390
+ if (!connector) throw new Error("The Base Toolset connector is missing from the catalog");
35391
+ return connector.connector_id;
35392
+ }
35393
+ function cutOverToolsetOf(connection) {
35394
+ const toolset = connection.settings?.[TOOLSET_SLUG_SETTING];
35395
+ return isRekorToolsetConnectorId(connection.connector_id) && typeof toolset === "string" && CUT_OVER_TOOLSET_RE.test(toolset) ? toolset : null;
35396
+ }
35397
+ function wantedConnectionTools(fileTypes) {
35398
+ return fileTypes.flatMap((ft) => FILE_OPERATIONS.map((op) => toolsetToolName(op, ft)));
35399
+ }
35400
+ function toolWork(tools, wanted, connectionId) {
35401
+ const native = [];
35402
+ const unavailable = [];
35403
+ for (const name of MOUNT_TOOLS) {
35404
+ const row = tools.native.find((t) => t.tool_name === name);
35405
+ if (!row) unavailable.push(name);
35406
+ else if (row.available_action !== "Disable") native.push(row);
35407
+ }
35408
+ const connection = [];
35409
+ const uncatalogued = [];
35410
+ for (const name of wanted) {
35411
+ const row = connectionId ? tools.connection.find((t) => t.connection_id === connectionId && t.tool_name === name) : void 0;
35412
+ if (!row) uncatalogued.push(name);
35413
+ else if (row.available_action !== "Disable") connection.push(row);
35414
+ }
35415
+ return { native, connection, uncatalogued, unavailable };
35416
+ }
35417
+ function describeToolWork(work) {
35418
+ return [
35419
+ ...work.native.map((t) => `${t.available_action === "Enable" ? "enable" : "add"} ${t.tool_name}`),
35420
+ ...work.connection.map((t) => `${t.available_action === "Enable" ? "enable" : "add"} ${t.tool_name}`),
35421
+ ...work.uncatalogued.map((name) => `add ${name}`)
35422
+ ];
35423
+ }
35424
+ async function cutOverHub(hub, kb, opts, io) {
35425
+ const { production, preview } = await resolveBases(kb, opts.base, opts.orgId);
35426
+ const stops = [];
35427
+ const hubs = await hub.listHubs();
35428
+ const previewHub = hubs.find((h) => h.hub_id === opts.hubId);
35429
+ if (!previewHub) throw expected(`Hub ${safe2(opts.hubId)} not found in this organization, or you cannot read it.`);
35430
+ if (previewHub.hub_environment !== "preview") {
35431
+ throw expected(`Hub ${safe2(opts.hubId)} is a production hub. A hub is cut over on its preview; publishing carries it to production.`);
35432
+ }
35433
+ const productionHub2 = previewHub.production_hub_id ? hubs.find((h) => h.hub_id === previewHub.production_hub_id) : void 0;
35434
+ if (!productionHub2) {
35435
+ throw expected(`Hub ${safe2(opts.hubId)} has not been published, and its knowledge moved from its production hub. Publish it, then migrate its knowledge first.`);
35436
+ }
35437
+ const productionHubId = productionHub2.hub_id;
35438
+ const fileTypes = await kb.listFileTypes(production);
35439
+ const fileTypeBySource = /* @__PURE__ */ new Map();
35440
+ for (const ft of fileTypes) {
35441
+ const source = provenanceOf(ft.metadata_schema);
35442
+ if (source) fileTypeBySource.set(source, ft.id);
35443
+ }
35444
+ const resources = await hub.listResources(opts.hubId);
35445
+ const ownKnowledge = (rows) => rows.filter((r) => r.resource_type === "knowledge" && !r.org_resource_id);
35446
+ const productionOwn = ownKnowledge(await hub.listResources(productionHubId));
35447
+ const previewOwn = ownKnowledge(resources);
35448
+ const diff = await hub.hubDiff(opts.hubId);
35449
+ const marked = new Set(previewOwn.filter((r) => r.moved_file_type).map((r) => r.resource_name));
35450
+ const knowledgeNames = new Set([...previewOwn, ...productionOwn].map((r) => r.resource_name).filter((name) => !marked.has(name)));
35451
+ const changed = [
35452
+ ...diff.resources.filter((e) => knowledgeNames.has(e.name)).map((e) => `resource "${e.name}" (${e.status})`),
35453
+ ...(diff.resource_folders ?? []).filter((e) => e.type && knowledgeNames.has(e.type)).map((e) => `a folder of "${e.type}"`),
35454
+ ...(diff.resource_files ?? []).filter((e) => e.type && knowledgeNames.has(e.type)).map((e) => `a file of "${e.type}"`)
35455
+ ];
35456
+ if (changed.length > 0) {
35457
+ stops.push(
35458
+ `The preview's knowledge resources differ from production's, which moved (${[...new Set(changed)].join(", ")}): sync or discard those changes first, so its agents are cut over to what they read.`
35459
+ );
35460
+ }
35461
+ const fileTypeOf = /* @__PURE__ */ new Map();
35462
+ for (const resource of resources) {
35463
+ if (resource.resource_type !== "knowledge") continue;
35464
+ if (resource.org_resource_id) {
35465
+ const ft2 = fileTypeBySource.get(orgSourceKey(resource.org_resource_id));
35466
+ if (ft2) fileTypeOf.set(resource.resource_id, ft2);
35467
+ continue;
35468
+ }
35469
+ if (resource.moved_file_type) {
35470
+ fileTypeOf.set(resource.resource_id, resource.moved_file_type);
35471
+ continue;
35472
+ }
35473
+ const twins = productionOwn.filter((r) => r.resource_name === resource.resource_name);
35474
+ const previewTwins = previewOwn.filter((r) => r.resource_name === resource.resource_name);
35475
+ if (twins.length > 1 || previewTwins.length > 1) {
35476
+ stops.push(`Two knowledge resources are named "${resource.resource_name}": rename one (in the preview, then sync), so each pairs with its production copy.`);
35477
+ continue;
35478
+ }
35479
+ const ft = twins[0] ? fileTypeBySource.get(hubSourceKey(productionHubId, twins[0].resource_id)) : void 0;
35480
+ if (ft) fileTypeOf.set(resource.resource_id, ft);
35481
+ }
35482
+ const productionHubs = hubs.filter((h) => h.hub_environment === "production");
35483
+ for (const resource of previewOwn) {
35484
+ const ft = fileTypeOf.get(resource.resource_id);
35485
+ if (!ft || resource.moved_file_type) continue;
35486
+ const hubPart = hubPartOf(ft);
35487
+ const holders = productionHubs.filter((h) => slugifySkillName(h.hub_name) === hubPart);
35488
+ if (holders.length > 1) {
35489
+ stops.push(
35490
+ `File type "${ft}" names the hub slug "${hubPart}", which ${holders.length} production hubs share (${holders.map((h) => `"${h.hub_name}"`).join(", ")}): its move can't be recorded until all but one are renamed.`
35491
+ );
35492
+ }
35493
+ }
35494
+ const slugById = buildResourceSlugMap(resources);
35495
+ const fileTypeBySlug = /* @__PURE__ */ new Map();
35496
+ for (const [resourceId, ft] of fileTypeOf) {
35497
+ const slug = slugById.get(resourceId);
35498
+ if (slug) fileTypeBySlug.set(slug, ft);
35499
+ }
35500
+ const byId = new Map(resources.map((r) => [r.resource_id, r]));
35501
+ const connections = await hub.listConnections(opts.hubId);
35502
+ const previewToolsets = /* @__PURE__ */ new Map();
35503
+ const connectionBySlug = /* @__PURE__ */ new Map();
35504
+ for (const connection of connections) {
35505
+ const slug = cutOverToolsetOf(connection);
35506
+ if (!slug) continue;
35507
+ connectionBySlug.set(slug, connection);
35508
+ if (!previewToolsets.has(slug)) previewToolsets.set(slug, await kb.getToolset(preview, slug));
35509
+ }
35510
+ const toolsetTypes = (slug) => (previewToolsets.get(slug)?.files ?? []).map((e) => e.file_type);
35511
+ const slugByConnection = new Map([...connectionBySlug].map(([slug, c]) => [c.connection_id, slug]));
35512
+ const agents = await hub.listAgents(opts.hubId);
35513
+ const plans = [];
35514
+ for (const agent of agents) {
35515
+ const links2 = (await hub.listAgentLinks(opts.hubId, agent.agent_id)).filter((l) => l.resource_type === "knowledge");
35516
+ const enabled = links2.filter((l) => l.enabled);
35517
+ const tools = await hub.listAgentTools(opts.hubId, agent.agent_id);
35518
+ const set = /* @__PURE__ */ new Set();
35519
+ for (const link of enabled) {
35520
+ const ft = fileTypeOf.get(link.resource_id);
35521
+ const resource = byId.get(link.resource_id);
35522
+ if (!ft) {
35523
+ stops.push(
35524
+ `Agent "${agent.agent_name}" reads knowledge resource "${resource?.resource_name ?? link.resource_id}", which has not moved: move it first (\`wayai knowledge migrate --org ${opts.orgId} --base ${opts.base}${resource?.org_resource_id ? "" : ` --hub ${productionHubId}`}\`).`
35525
+ );
35526
+ continue;
35527
+ }
35528
+ set.add(ft);
35529
+ }
35530
+ for (const tool of tools.connection) {
35531
+ const slug = tool.connection_id ? slugByConnection.get(tool.connection_id) : void 0;
35532
+ if (slug && tool.available_action === "Disable") for (const ft of toolsetTypes(slug)) set.add(ft);
35533
+ }
35534
+ const runner = connections.find((c) => c.connection_id === agent.connection_id);
35535
+ if (enabled.length > 0 && runner && getConnectorById(runner.connector_id)?.is_harness === true) {
35536
+ stops.push(
35537
+ `Agent "${agent.agent_name}" runs on a hosted harness, which takes no Base Toolset's tools yet, so it would lose its knowledge: connect it to a toolset through an MCP Server connection, or detach its knowledge links, then run again.`
35538
+ );
35539
+ }
35540
+ const losing = enabled.flatMap((link) => {
35541
+ const resource = byId.get(link.resource_id);
35542
+ const slug = slugById.get(link.resource_id);
35543
+ return resource && slug ? [{ slug, name: resourceDisplayName(resource) }] : [];
35544
+ });
35545
+ for (const [field, template] of [
35546
+ ["instructions", agent.instructions],
35547
+ ["additional context", agent.additional_context_template]
35548
+ ]) {
35549
+ for (const use of knowledgePlaceholders(template ?? "", losing)) {
35550
+ stops.push(
35551
+ `Agent "${agent.agent_name}" uses ${use} in its ${field}, which reads nothing once its knowledge moves (K19): move that text into its instructions or a skill, then run again.`
35552
+ );
35553
+ }
35554
+ }
35555
+ if (set.size > 0 || enabled.length > 0) plans.push({ agent, fileTypes: [...set].sort(), linksToDisable: enabled, tools });
35556
+ }
35557
+ const groups = /* @__PURE__ */ new Map();
35558
+ for (const plan of plans) {
35559
+ if (plan.fileTypes.length === 0) continue;
35560
+ const slug = toolsetSlug(productionHubId, plan.fileTypes);
35561
+ const group = groups.get(slug);
35562
+ if (group) {
35563
+ group.agents.push(plan.agent);
35564
+ plan.group = group;
35565
+ continue;
35566
+ }
35567
+ plan.group = {
35568
+ slug,
35569
+ fileTypes: plan.fileTypes,
35570
+ agents: [plan.agent],
35571
+ connection: connectionBySlug.get(slug),
35572
+ created: !connectionBySlug.has(slug),
35573
+ spec: {
35574
+ id: slug,
35575
+ name: `Knowledge of ${previewHub.hub_name}`.slice(0, 200),
35576
+ description: `Read-only knowledge for agents of hub ${productionHubId}, written by wayai knowledge cut-over: list and get on ${plan.fileTypes.join(", ")}.`,
35577
+ files: plan.fileTypes.map(toolsetEntry)
35578
+ }
35579
+ };
35580
+ groups.set(slug, plan.group);
35581
+ }
35582
+ const plannedTypes = [...new Set([...groups.values()].flatMap((g) => g.fileTypes))].sort();
35583
+ const missing = [];
35584
+ for (const ft of plannedTypes) {
35585
+ const [inProduction, inPreview] = await Promise.all([kb.listFiles(production, ft), filesOrNone(kb, preview, ft)]);
35586
+ const there = new Set(inPreview.map((f) => f.path));
35587
+ for (const file of inProduction) if (!there.has(file.path)) missing.push(`${ft}/${file.path}`);
35588
+ }
35589
+ if (missing.length > 0) {
35590
+ stops.push(
35591
+ `The preview base "${preview}" lacks ${missing.length} file(s) production holds (${missing.slice(0, 5).join(", ")}${missing.length > 5 ? ", \u2026" : ""}), so the preview's agents would read none of them: copy them in with \`${refreshPreviewCommand(opts.orgId, opts.base)}\`, then run again.`
35592
+ );
35593
+ }
35594
+ const toolsetsToWrite = [];
35595
+ const toolsetsToPromote = [];
35596
+ for (const group of groups.values()) {
35597
+ const [inPreview, inProduction] = await Promise.all([
35598
+ previewToolsets.has(group.slug) ? previewToolsets.get(group.slug) : kb.getToolset(preview, group.slug),
35599
+ kb.getToolset(production, group.slug)
35600
+ ]);
35601
+ if (!sameToolset(inPreview, group.spec)) toolsetsToWrite.push(group.spec);
35602
+ if (!sameToolset(inProduction, group.spec)) toolsetsToPromote.push(group.spec);
35603
+ }
35604
+ const connectionsToCreate = [...groups.values()].filter((g) => g.created);
35605
+ const toolLines = [];
35606
+ let toolWrites = 0;
35607
+ for (const plan of plans) {
35608
+ const group = plan.group;
35609
+ if (!group) continue;
35610
+ const work = toolWork(plan.tools, wantedConnectionTools(group.fileTypes), group.connection?.connection_id);
35611
+ for (const name of work.unavailable) stops.push(`Agent "${plan.agent.agent_name}" can't be given ${name}: the hub offers it no such tool.`);
35612
+ const lines = describeToolWork(work);
35613
+ toolWrites += lines.length;
35614
+ if (lines.length > 0) toolLines.push(` ${safe2(plan.agent.agent_name)}: ${lines.map(safe2).join(", ")} (${group.slug})`);
35615
+ }
35616
+ const agentRewrites = [];
35617
+ for (const agent of agents) {
35618
+ const patch = {};
35619
+ const instructions = rewriteCitations(agent.instructions ?? "", fileTypeBySlug);
35620
+ if (instructions !== (agent.instructions ?? "")) patch.instructions = instructions;
35621
+ const context = rewriteCitations(agent.additional_context_template ?? "", fileTypeBySlug);
35622
+ if (context !== (agent.additional_context_template ?? "")) patch.additional_context_template = context;
35623
+ if (Object.keys(patch).length > 0) agentRewrites.push({ agent, patch });
35624
+ }
35625
+ const automationRewrites = [];
35626
+ for (const automation of await hub.listAutomations(opts.hubId)) {
35627
+ const text = automation.action.type === "run_agent" ? automation.action.instructions : null;
35628
+ if (!text) continue;
35629
+ const rewritten = rewriteCitations(text, fileTypeBySlug);
35630
+ if (rewritten !== text) automationRewrites.push({ automation, action: { ...automation.action, instructions: rewritten } });
35631
+ }
35632
+ const moves = [];
35633
+ for (const resource of previewOwn) {
35634
+ const ft = fileTypeOf.get(resource.resource_id);
35635
+ if (!ft || resource.moved_file_type) continue;
35636
+ moves.push({
35637
+ body: { scope: "hub", hub_id: opts.hubId, resource_id: resource.resource_id, file_type: ft, base_id: production },
35638
+ label: `hub resource "${resource.resource_name}" \u2192 ${ft}`
35639
+ });
35640
+ }
35641
+ const orgRecorded = /* @__PURE__ */ new Set();
35642
+ for (const plan of plans) {
35643
+ for (const link of plan.linksToDisable) {
35644
+ const resource = byId.get(link.resource_id);
35645
+ const ft = fileTypeOf.get(link.resource_id);
35646
+ if (!resource?.org_resource_id || !ft || orgRecorded.has(resource.org_resource_id)) continue;
35647
+ orgRecorded.add(resource.org_resource_id);
35648
+ moves.push({
35649
+ body: { scope: "organization", organization_id: opts.orgId, org_resource_id: resource.org_resource_id, file_type: ft, base_id: production },
35650
+ label: `organization resource "${resource.resource_name}" \u2192 ${ft}`
35651
+ });
35652
+ }
35653
+ }
35654
+ const links = plans.flatMap((plan) => plan.linksToDisable.map((link) => ({ plan, link })));
35655
+ const result = {
35656
+ toolsets: [...groups.values()].map((g) => ({ slug: g.slug, fileTypes: g.fileTypes, agents: g.agents.map((a) => a.agent_name) })),
35657
+ toolsetsWritten: toolsetsToWrite.map((s) => s.id),
35658
+ toolsetsPromoted: toolsetsToPromote.map((s) => s.id),
35659
+ connectionsCreated: connectionsToCreate.map((g) => g.slug),
35660
+ toolsAssigned: toolLines,
35661
+ agentsRewritten: agentRewrites.map((r) => r.agent.agent_name),
35662
+ automationsRewritten: automationRewrites.map((r) => r.automation.name),
35663
+ movesRecorded: moves.map((m) => m.label),
35664
+ linksDisabled: links.map(({ plan, link }) => `${plan.agent.agent_name}: ${byId.get(link.resource_id)?.resource_name ?? link.resource_id}`),
35665
+ written: false
35666
+ };
35667
+ io.log(`Cut-over of hub "${safe2(previewHub.hub_name)}" (${opts.hubId}) to base "${safe2(production)}" (toolsets written in "${safe2(preview)}")`);
35668
+ io.log("\nToolsets (read-only: list and get):");
35669
+ if (groups.size === 0) io.log(" (none: no agent reads moved knowledge)");
35670
+ for (const group of groups.values()) {
35671
+ const mark = toolsetsToPromote.includes(group.spec) || toolsetsToWrite.includes(group.spec) ? "+" : "=";
35672
+ io.log(` ${mark} ${group.slug}: ${group.fileTypes.map(safe2).join(", ")} \u2014 ${group.agents.map((a) => safe2(a.agent_name)).join(", ")}`);
35673
+ }
35674
+ io.log("\nConnections:");
35675
+ for (const group of groups.values()) {
35676
+ io.log(` ${group.connection ? "=" : "+"} Base Toolset "${group.slug}"${group.connection ? "" : ", with a new token bound to it on the preview base"}`);
35677
+ }
35678
+ io.log("\nAgent tools:");
35679
+ if (toolLines.length === 0) io.log(" (nothing to assign)");
35680
+ for (const line of toolLines) io.log(line);
35681
+ io.log("\nPaths rewritten to data/ (K13):");
35682
+ if (agentRewrites.length + automationRewrites.length === 0) io.log(" (none)");
35683
+ for (const { agent, patch } of agentRewrites) io.log(` agent "${safe2(agent.agent_name)}": ${Object.keys(patch).join(", ")}`);
35684
+ for (const { automation } of automationRewrites) io.log(` automation "${safe2(automation.name)}": instructions`);
35685
+ io.log("\nMoves recorded:");
35686
+ if (moves.length === 0) io.log(" (none)");
35687
+ for (const move of moves) io.log(` + ${safe2(move.label)}`);
35688
+ io.log("\nKnowledge links disabled:");
35689
+ if (links.length === 0) io.log(" (none)");
35690
+ for (const line of result.linksDisabled) io.log(` - ${safe2(line)}`);
35691
+ if (stops.length > 0) {
35692
+ io.log("\nStopped:");
35693
+ for (const stop of stops) io.log(` ${safe2(stop)}`);
35694
+ throw expected(`The cut-over stops on ${stops.length} reason(s) above; nothing was written.`);
35695
+ }
35696
+ const writes = toolsetsToWrite.length + toolsetsToPromote.length + connectionsToCreate.length + toolWrites + agentRewrites.length + automationRewrites.length + moves.length + links.length;
35697
+ const printProduction = () => printProductionSteps(io, opts, production, productionHubId, [...groups.values()]);
35698
+ if (opts.dryRun) {
35699
+ io.log(`
35700
+ Dry run: nothing written (${writes === 0 ? "nothing to write" : `${writes} write(s) planned`}).`);
35701
+ printProduction();
35702
+ return result;
35703
+ }
35704
+ if (writes === 0) {
35705
+ io.log("\nNothing to write: this hub is cut over.");
35706
+ printProduction();
35707
+ return result;
35708
+ }
35709
+ if (!opts.yes && !await io.confirm(`Make these ${writes} change(s) to the preview hub and its knowledge base?`)) {
35710
+ io.log("Cancelled.");
35711
+ return result;
35712
+ }
35713
+ for (const move of moves) await hub.recordMove(move.body);
35714
+ for (const spec of toolsetsToWrite) await kb.putToolset(preview, spec);
35715
+ if (toolsetsToPromote.length > 0) {
35716
+ const slugs = toolsetsToPromote.map((s) => s.id);
35717
+ const problems2 = promotionProblems(await kb.promoteToolsets(production, preview, slugs, true), new Set(slugs), "toolsets");
35718
+ if (problems2.length > 0) {
35719
+ throw expected(
35720
+ `The promotion into "${safe2(production)}" would not apply as planned (${problems2.map(safe2).join("; ")}). The toolsets are written in "${safe2(preview)}"; nothing reached production, and the hub's agents are unchanged.`
35721
+ );
35722
+ }
35723
+ await kb.promoteToolsets(production, preview, slugs, false);
35724
+ }
35725
+ if (connectionsToCreate.length > 0) {
35726
+ const connectorId = toolsetConnectorId();
35727
+ for (const group of connectionsToCreate) {
35728
+ const token = await kb.mintToolsetToken(preview, group.slug, `${group.slug} (hub ${opts.hubId})`);
35729
+ const after = await hub.addToolsetConnection(opts.hubId, { connectorId, name: group.slug, toolset: group.slug, token });
35730
+ group.connection = after.find((c) => cutOverToolsetOf(c) === group.slug);
35731
+ if (!group.connection) throw new Error(`The Base Toolset connection "${group.slug}" was not found after it was created.`);
35732
+ }
35733
+ }
35734
+ const synced2 = /* @__PURE__ */ new Set();
35735
+ for (const plan of plans) {
35736
+ const group = plan.group;
35737
+ if (!group?.connection) continue;
35738
+ const connectionId = group.connection.connection_id;
35739
+ const wanted = wantedConnectionTools(group.fileTypes);
35740
+ const stale = group.created || synced2.has(connectionId);
35741
+ let tools = stale ? await hub.listAgentTools(opts.hubId, plan.agent.agent_id) : plan.tools;
35742
+ let work = toolWork(tools, wanted, connectionId);
35743
+ if (work.uncatalogued.length > 0 && !synced2.has(connectionId)) {
35744
+ await hub.syncConnection(opts.hubId, connectionId);
35745
+ synced2.add(connectionId);
35746
+ tools = await hub.listAgentTools(opts.hubId, plan.agent.agent_id);
35747
+ work = toolWork(tools, wanted, connectionId);
35748
+ }
35749
+ if (work.uncatalogued.length > 0) {
35750
+ throw new Error(
35751
+ `Connection "${group.slug}" catalogs no ${safe2(work.uncatalogued.join(", "))} after a sync, so agent "${safe2(plan.agent.agent_name)}" can't be given them.`
35752
+ );
35753
+ }
35754
+ for (const tool of [...work.native, ...work.connection]) {
35755
+ if (tool.available_action === "Enable" && tool.tool_id) await hub.enableTool(opts.hubId, plan.agent.agent_id, tool.tool_id);
35756
+ else if (tool.tool_native_id) await hub.addNativeTool(opts.hubId, plan.agent.agent_id, tool.tool_native_id);
35757
+ else if (tool.mcp_tool_id) await hub.addConnectionTool(opts.hubId, plan.agent.agent_id, tool.mcp_tool_id);
35758
+ }
35759
+ }
35760
+ for (const { agent, patch } of agentRewrites) await hub.updateAgentText(opts.hubId, agent.agent_id, patch);
35761
+ for (const { automation, action } of automationRewrites) await hub.updateAutomationAction(opts.hubId, automation.automation_id, action);
35762
+ for (const { plan, link } of links) await hub.disableLink(opts.hubId, plan.agent.agent_id, link.resource_id);
35763
+ io.log(`
35764
+ Done: the preview hub's agents read their knowledge from "${safe2(preview)}" through ${groups.size} toolset(s).`);
35765
+ printProduction();
35766
+ return { ...result, written: true };
35767
+ }
35768
+ async function filesOrNone(kb, base, fileType) {
35769
+ try {
35770
+ return await kb.listFiles(base, fileType);
35771
+ } catch (err) {
35772
+ if (err instanceof ApiError && err.status === 404) return [];
35773
+ throw err;
35774
+ }
35775
+ }
35776
+ function printProductionSteps(io, opts, production, productionHubId, groups) {
35777
+ if (groups.length === 0) return;
35778
+ io.log("\nProduction (not run by this command), once the preview passes its evals:");
35779
+ io.log(` 1. Publish the preview: wayai publish --hub ${opts.hubId}`);
35780
+ io.log(" 2. For each toolset, mint a token bound to it on the production base, and save it on production's connection:");
35781
+ for (const group of groups) {
35782
+ io.log(
35783
+ ` wayai bases tokens create-for-toolset ${group.slug} --base ${safe2(production)} --token-only | wayai set-connection-credential --hub ${productionHubId} --connection "${group.slug}" --field access_token --stdin`
35784
+ );
35785
+ }
35786
+ }
35787
+ var CUT_OVER_TOOLSET_PREFIX, CUT_OVER_TOOLSET_RE, FILE_OPERATIONS, MOUNT_TOOLS, DATA_NAMESPACE, safe2, CITATION_RE;
35788
+ var init_cut_over = __esm({
35789
+ "src/knowledge/cut-over.ts"() {
35790
+ "use strict";
35791
+ init_dist();
35792
+ init_catalog();
35793
+ init_rekor_toolset();
35794
+ init_resource_paths();
35795
+ init_expected();
35796
+ init_terminal_output();
35797
+ init_api_client();
35798
+ init_migrate2();
35799
+ init_plan();
35800
+ init_source();
35801
+ CUT_OVER_TOOLSET_PREFIX = "knowledge-";
35802
+ CUT_OVER_TOOLSET_RE = /^knowledge-[0-9a-f]{12}$/;
35803
+ FILE_OPERATIONS = ["list", "get"];
35804
+ MOUNT_TOOLS = ["list_files", "read_file"];
35805
+ DATA_NAMESPACE = "data";
35806
+ safe2 = (text) => sanitizeTerminalText(text);
35807
+ CITATION_RE = /(?<![\w./-])(\/?)resources\/([a-z0-9]+(?:-[a-z0-9]+)*)(?![a-z0-9_]|-[a-z0-9]|\.\w)/gi;
35808
+ }
35809
+ });
35810
+
34800
35811
  // src/commands/knowledge.ts
34801
35812
  var knowledge_exports = {};
34802
35813
  __export(knowledge_exports, {
@@ -34818,7 +35829,7 @@ function readArgs(args2) {
34818
35829
  function parseKnowledgeArgs(args2) {
34819
35830
  const { values, positionals } = readArgs(args2);
34820
35831
  const [verb, ...extra] = positionals;
34821
- if (verb !== "migrate" && verb !== "refresh-preview") {
35832
+ if (verb !== "migrate" && verb !== "refresh-preview" && verb !== "cut-over") {
34822
35833
  throw usageError3(verb === void 0 ? "No subcommand given." : `Unknown subcommand ${JSON.stringify(verb)}.`);
34823
35834
  }
34824
35835
  if (extra.length > 0) throw usageError3(`Unexpected argument ${JSON.stringify(extra[0])}.`);
@@ -34826,13 +35837,17 @@ function parseKnowledgeArgs(args2) {
34826
35837
  if (!UUID_RE3.test(values.org)) throw usageError3(`Invalid --org ${JSON.stringify(values.org)}: expected an organization id (a UUID).`);
34827
35838
  if (!values.base) throw usageError3("--base <knowledge base> is required.");
34828
35839
  if (verb === "refresh-preview") {
34829
- if (values.hub !== void 0) throw usageError3("--hub is migrate's: a refresh copies the whole knowledge base.");
34830
- if (values["dry-run"] !== void 0) throw usageError3("--dry-run is migrate's: a refresh has none.");
35840
+ if (values.hub !== void 0) throw usageError3("--hub is migrate's and cut-over's: a refresh copies the whole knowledge base.");
35841
+ if (values["dry-run"] !== void 0) throw usageError3("--dry-run is migrate's and cut-over's: a refresh has none.");
34831
35842
  return { verb, orgId: values.org, base: values.base, yes: values.yes === true };
34832
35843
  }
34833
35844
  if (values.hub !== void 0 && !UUID_RE3.test(values.hub)) {
34834
35845
  throw usageError3(`Invalid --hub ${JSON.stringify(values.hub)}: expected a hub id (a UUID).`);
34835
35846
  }
35847
+ if (verb === "cut-over") {
35848
+ if (values.hub === void 0) throw usageError3("--hub <preview hub_id> is required: a cut-over is one preview hub's.");
35849
+ return { verb, orgId: values.org, base: values.base, hubId: values.hub, dryRun: values["dry-run"] === true, yes: values.yes === true };
35850
+ }
34836
35851
  return {
34837
35852
  verb,
34838
35853
  orgId: values.org,
@@ -34860,10 +35875,12 @@ async function knowledgeCommand(args2) {
34860
35875
  const { config, accessToken } = await requireAuth();
34861
35876
  const api = new ApiClient({ apiUrl: config.api_url, accessToken, onUnauthorized: () => getAccessToken(config) });
34862
35877
  const data = await createDataClient(options.orgId, { refreshLogin: true });
34863
- await migrateKnowledge(knowledgeReader(api, options.orgId), knowledgeBase(data), options, {
34864
- log: (line) => console.log(line),
34865
- confirm
34866
- });
35878
+ const io = { log: (line) => console.log(line), confirm };
35879
+ if (options.verb === "cut-over") {
35880
+ await cutOverHub(hubSetup(api, options.orgId), knowledgeBase(data), options, io);
35881
+ return;
35882
+ }
35883
+ await migrateKnowledge(knowledgeReader(api, options.orgId), knowledgeBase(data), options, io);
34867
35884
  }
34868
35885
  var USAGE4, OPTIONS;
34869
35886
  var init_knowledge = __esm({
@@ -34878,12 +35895,14 @@ var init_knowledge = __esm({
34878
35895
  init_migrate2();
34879
35896
  init_remote();
34880
35897
  init_refresh_preview();
35898
+ init_cut_over();
34881
35899
  USAGE4 = `
34882
35900
  wayai knowledge \u2014 the organization's knowledge base
34883
35901
 
34884
35902
  Usage:
34885
35903
  wayai knowledge migrate --org <org_id> --base <knowledge base> [--hub <hub_id>] [--dry-run] [-y]
34886
35904
  wayai knowledge refresh-preview --org <org_id> --base <knowledge base> [-y]
35905
+ wayai knowledge cut-over --org <org_id> --base <knowledge base> --hub <preview hub_id> [--dry-run] [-y]
34887
35906
 
34888
35907
  migrate: move knowledge resources into the organization's knowledge base.
34889
35908
 
@@ -34905,6 +35924,21 @@ refresh-preview: make the knowledge base's preview hold its production base's fi
34905
35924
 
34906
35925
  --base The production knowledge base, or its preview.
34907
35926
  -y, --yes Skip the question before copying.
35927
+
35928
+ cut-over: switch a preview hub's agents from their knowledge resources to the knowledge base, once
35929
+ its knowledge has moved. Each distinct set of knowledge its agents read gets one read-only
35930
+ toolset on the knowledge base, reached through a Base Toolset connection; each agent is given
35931
+ list_files, read_file and its toolset's list_ and get_ tools, its resources/<slug>/ paths become
35932
+ data/<file type>/, its knowledge links are disabled, and each move is recorded. It changes the
35933
+ preview only, and prints the production steps. The preview base must hold production's files
35934
+ first (refresh-preview): the preview's agents read them there. It stops before writing on a resource placeholder
35935
+ ({{files()}}, {{resource_content()}}, {{file_content()}}) that reads knowledge an agent is about
35936
+ to lose: move that text into its instructions or a skill first.
35937
+
35938
+ --hub The preview hub.
35939
+ --base The production knowledge base, or its preview.
35940
+ --dry-run List every change, and every reason it would stop, without writing.
35941
+ -y, --yes Skip the question before writing.
34908
35942
  `;
34909
35943
  OPTIONS = {
34910
35944
  org: { type: "string" },
@@ -34960,17 +35994,17 @@ async function adminCommand(args2) {
34960
35994
  process.exit(1);
34961
35995
  }
34962
35996
  if (group === "archive") {
34963
- const { positional: positional2, flagArgs: flagArgs2 } = splitArgs(afterGroup);
35997
+ const { positional: positional2, flagArgs: flagArgs2 } = splitArgs2(afterGroup);
34964
35998
  await runArchiveRead(positional2, flagArgs2);
34965
35999
  return;
34966
36000
  }
34967
36001
  if (group === "observability") {
34968
- const { positional: positional2, flagArgs: flagArgs2 } = splitArgs(afterGroup);
36002
+ const { positional: positional2, flagArgs: flagArgs2 } = splitArgs2(afterGroup);
34969
36003
  await runObservabilityRead(positional2, flagArgs2);
34970
36004
  return;
34971
36005
  }
34972
36006
  const [sub, ...rest] = afterGroup;
34973
- const { positional, flagArgs } = splitArgs(rest);
36007
+ const { positional, flagArgs } = splitArgs2(rest);
34974
36008
  if (group === "do") {
34975
36009
  if (!sub) {
34976
36010
  printHelp2();
@@ -35258,7 +36292,7 @@ async function adminCommand(args2) {
35258
36292
  printHelp2();
35259
36293
  process.exit(1);
35260
36294
  }
35261
- function splitArgs(args2) {
36295
+ function splitArgs2(args2) {
35262
36296
  const positional = [];
35263
36297
  const flagArgs = [];
35264
36298
  for (const a of args2) {
@@ -37383,9 +38417,9 @@ var init_update = __esm({
37383
38417
  });
37384
38418
 
37385
38419
  // src/data/commands/actions.ts
37386
- import { Command } from "commander";
38420
+ import { Command as Command2 } from "commander";
37387
38421
  function buildActionsCommand() {
37388
- const actions = new Command("actions").description(
38422
+ const actions = new Command2("actions").description(
37389
38423
  "Manage Actions \u2014 reusable, named definitions: one record_type + one operation, or a composite of atomic write steps (--config with `steps`)"
37390
38424
  );
37391
38425
  actions.command("upsert <id>").description(
@@ -37454,7 +38488,7 @@ var init_actions = __esm({
37454
38488
  });
37455
38489
 
37456
38490
  // src/data/commands/attachments.ts
37457
- import { Command as Command2 } from "commander";
38491
+ import { Command as Command3 } from "commander";
37458
38492
  import { readFileSync as readFileSync19 } from "fs";
37459
38493
  function findAttachmentByFilename(attachments, filename) {
37460
38494
  return attachments.find((a) => a.key.endsWith(`/${filename}`)) ?? null;
@@ -37481,7 +38515,7 @@ function uploadPathFrom(uploadUrl) {
37481
38515
  return uploadUrl;
37482
38516
  }
37483
38517
  function buildAttachmentsCommand() {
37484
- const attachments = new Command2("attachments").description("Manage record attachments");
38518
+ const attachments = new Command3("attachments").description("Manage record attachments");
37485
38519
  attachments.command("upload <record_type> <id>").description(
37486
38520
  "Get a presigned upload URL for a record attachment. With --file, uploads the file content in one step."
37487
38521
  ).requiredOption("--filename <name>", "File name or path (e.g. docs/guide.md)").option("--content-type <type>", "MIME type", "application/octet-stream").option("--file <path>", "Local file to upload (skips presigned URL output, uploads directly)").action(async function(recordType2, id, opts) {
@@ -37561,9 +38595,9 @@ var init_attachments = __esm({
37561
38595
  });
37562
38596
 
37563
38597
  // src/data/commands/batch.ts
37564
- import { Command as Command3 } from "commander";
38598
+ import { Command as Command4 } from "commander";
37565
38599
  function buildBasesBatchCommand() {
37566
- return withBaseOption(new Command3("batch")).description("Execute atomic batch operations (up to 1,000 operations)").requiredOption("--operations <json>", "Operations array (inline JSON or @filename)").action(async function(opts) {
38600
+ return withBaseOption(new Command4("batch")).description("Execute atomic batch operations (up to 1,000 operations)").requiredOption("--operations <json>", "Operations array (inline JSON or @filename)").action(async function(opts) {
37567
38601
  const base = pathSegment(requireBase(this), "--base");
37568
38602
  const operations = parseData(opts.operations, "--operations");
37569
38603
  const client = await createDataClient();
@@ -37592,7 +38626,7 @@ import { createReadStream } from "fs";
37592
38626
  import { access, constants, stat } from "fs/promises";
37593
38627
  import { createInterface as createInterface3 } from "readline";
37594
38628
  import { randomUUID } from "crypto";
37595
- import { Command as Command4 } from "commander";
38629
+ import { Command as Command5 } from "commander";
37596
38630
  function isRetryable(err) {
37597
38631
  if (isNetworkError(err)) return true;
37598
38632
  if (!(err instanceof ApiError)) return false;
@@ -37668,7 +38702,7 @@ async function beginSession(state, client, base, recordType2, requestKey) {
37668
38702
  return res.session;
37669
38703
  }
37670
38704
  function buildBasesImportCommand() {
37671
- const importCmd = withBaseOption(new Command4("import")).description(
38705
+ const importCmd = withBaseOption(new Command5("import")).description(
37672
38706
  "Bulk historical import: load an NDJSON backfill, list sessions, or undo one"
37673
38707
  );
37674
38708
  importCmd.command("run <record_type>").description(
@@ -37895,7 +38929,7 @@ var init_import = __esm({
37895
38929
  });
37896
38930
 
37897
38931
  // src/data/commands/providers.ts
37898
- import { Command as Command5 } from "commander";
38932
+ import { Command as Command6 } from "commander";
37899
38933
  import { writeFileSync as writeFileSync10 } from "fs";
37900
38934
  function providerSegment(provider) {
37901
38935
  if (!VALID_PROVIDERS.includes(provider)) {
@@ -37904,7 +38938,7 @@ function providerSegment(provider) {
37904
38938
  return pathSegment(provider, "provider");
37905
38939
  }
37906
38940
  function buildBasesProvidersCommand() {
37907
- const providers = withBaseOption(new Command5("providers")).description(
38941
+ const providers = withBaseOption(new Command6("providers")).description(
37908
38942
  "Import/export tool definitions between LLM providers and record types"
37909
38943
  );
37910
38944
  providers.command("import <provider>").description(`Import tool definitions as record types. Providers: ${VALID_PROVIDERS_HELP}`).requiredOption("--tools <json>", "Tool definitions (inline JSON or @filename)").action(async function(provider, opts) {
@@ -37975,7 +39009,7 @@ var init_providers = __esm({
37975
39009
  });
37976
39010
 
37977
39011
  // src/data/commands/report.ts
37978
- import { Command as Command6 } from "commander";
39012
+ import { Command as Command7 } from "commander";
37979
39013
  import { readFileSync as readFileSync20 } from "fs";
37980
39014
  import { dirname as dirname15, join as join36 } from "path";
37981
39015
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -38019,7 +39053,7 @@ function reportActionError2(err) {
38019
39053
  throw err;
38020
39054
  }
38021
39055
  function buildBasesReportCommand() {
38022
- const report2 = new Command6("report").description(
39056
+ const report2 = new Command7("report").description(
38023
39057
  "File a report to the Data triage queue and verify the outcome"
38024
39058
  );
38025
39059
  report2.command("create").description("Submit a report to the Data triage queue (deduplicated)").requiredOption("--title <title>", "Short summary").requiredOption("--description <text>", "What happened").option("--source <source>", "Origin: cli_report (default) | review | security_audit").option("--classification <c>", "Nature: bug (default) | flaky_test | security | enhancement").option(
@@ -38186,7 +39220,7 @@ var init_report3 = __esm({
38186
39220
  });
38187
39221
 
38188
39222
  // src/data/commands/credentials.ts
38189
- import { Command as Command7 } from "commander";
39223
+ import { Command as Command8 } from "commander";
38190
39224
  import { readFileSync as readFileSync21 } from "fs";
38191
39225
  function withValueSourceOptions(cmd, what) {
38192
39226
  return cmd.option(
@@ -38221,7 +39255,7 @@ function credentialPath(base, id) {
38221
39255
  }
38222
39256
  function buildBasesCredentialsCommand() {
38223
39257
  const credentials = withBaseOption(
38224
- new Command7("credentials").description("Manage a base's integration credentials")
39258
+ new Command8("credentials").description("Manage a base's integration credentials")
38225
39259
  );
38226
39260
  withValueSourceOptions(
38227
39261
  credentials.command("create").description("Store a credential on this base (value read from stdin, a masked prompt, or --file)").requiredOption("--name <name>", "Credential name \u2014 what a credential:<name> reference resolves").option("--content-type <mime>", "MIME type of the value (e.g. application/x-pkcs12)").option(
@@ -38348,10 +39382,10 @@ var init_credentials = __esm({
38348
39382
  });
38349
39383
 
38350
39384
  // src/data/commands/sql.ts
38351
- import { Command as Command8 } from "commander";
39385
+ import { Command as Command9 } from "commander";
38352
39386
  import { readFileSync as readFileSync22 } from "fs";
38353
39387
  function buildBasesSqlCommand() {
38354
- return withBaseOption(new Command8("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
39388
+ return withBaseOption(new Command9("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
38355
39389
  "--param <kv...>",
38356
39390
  "Named parameters as key=value pairs (e.g. --param status=issued)"
38357
39391
  ).action(async function(queryArg, opts) {
@@ -38393,250 +39427,6 @@ var init_sql = __esm({
38393
39427
  }
38394
39428
  });
38395
39429
 
38396
- // src/data/commands/tokens.ts
38397
- import { Command as Command9 } from "commander";
38398
- function addTokenLifecycleOptions(cmd) {
38399
- return cmd.option("--expires-at <date>", "Expiration date (ISO 8601, e.g. 2026-06-01T00:00:00Z)").option(
38400
- "--ttl <duration>",
38401
- "Relative expiry, e.g. 10m, 2h, 7d (use for disposable probe/test tokens)"
38402
- ).option(
38403
- "--description <text>",
38404
- 'What holds this token (e.g. "behind credential X"); shown in list and revoke warnings'
38405
- ).addHelpText("after", TOKEN_OUTPUT_HELP);
38406
- }
38407
- function tokenCreationOutput(cmd, opts) {
38408
- return opts.tokenOnly ? "token" : outputFormat(cmd);
38409
- }
38410
- function printCreatedToken(data, output) {
38411
- if (output !== "token") {
38412
- printOutput(data, output);
38413
- return;
38414
- }
38415
- const token = typeof data === "object" && data !== null && "token" in data ? data.token : void 0;
38416
- if (typeof token !== "string" || !RAW_TOKEN_PATTERN.test(token)) {
38417
- throw new Error("Token creation response did not contain a valid rec_ token");
38418
- }
38419
- console.log(token);
38420
- }
38421
- function resolveExpiresAt(opts) {
38422
- if (opts.ttl && opts.expiresAt) {
38423
- throw expected("--ttl and --expires-at are mutually exclusive \u2014 pass one.");
38424
- }
38425
- if (opts.ttl) return new Date(Date.now() + parseDuration(opts.ttl, "--ttl")).toISOString();
38426
- return opts.expiresAt;
38427
- }
38428
- function applyTokenLifecycleOpts(body, opts) {
38429
- const expiresAt = resolveExpiresAt(opts);
38430
- if (expiresAt) body.expires_at = expiresAt;
38431
- if (opts.description) body.description = opts.description;
38432
- }
38433
- function listAllTokens(client) {
38434
- return client.collectPages(
38435
- (cursor) => `/v1/tokens?limit=${TOKEN_LIST_PAGE_LIMIT}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`
38436
- );
38437
- }
38438
- function pruneRow(t, reason) {
38439
- return {
38440
- token_id: t.token_id,
38441
- name: t.name,
38442
- reason,
38443
- last_used: t.last_used_at ?? "never"
38444
- };
38445
- }
38446
- function buildBasesTokensCommand() {
38447
- const tokens2 = new Command9("tokens").description("Manage API tokens");
38448
- addTokenLifecycleOptions(
38449
- tokens2.command("create").description("Create a scoped API token").requiredOption("--name <name>", "Token name").requiredOption("--grants <json>", "Grant definitions as JSON array").option(
38450
- "--token-only",
38451
- "Print only the raw rec_ token for secure command substitution (shown once)"
38452
- )
38453
- ).action(async function(opts) {
38454
- const grants = parseData(opts.grants, "--grants");
38455
- const body = { name: opts.name, grants };
38456
- applyTokenLifecycleOpts(body, opts);
38457
- const client = await createDataClient();
38458
- const data = await client.request("POST", "/v1/tokens", body);
38459
- printCreatedToken(data, tokenCreationOutput(this, opts));
38460
- });
38461
- addTokenLifecycleOptions(
38462
- // The only token verb that addresses a base, so it alone declares `--base`.
38463
- // Putting it on the `tokens` group would advertise a flag that does nothing
38464
- // on `list`, `revoke` and `prune` — the defect `withBaseOption` exists to
38465
- // avoid.
38466
- withBaseOption(tokens2.command("create-for-toolset <slug>")).description(
38467
- "Create a toolset-bound (consumer) token whose authorization IS the toolset tool surface \u2014 it can call exactly the tools that toolset exposes (those record types + operations, relationships, batch, and sql only if enabled) and nothing else. Binds to the configured --base."
38468
- ).option("--name <name>", 'Token name (default: "<slug> consumer")').option(
38469
- "--token-only",
38470
- "Print only the raw rec_ token for secure command substitution (shown once)"
38471
- )
38472
- ).action(async function(slug, opts) {
38473
- const base = requireBase(this);
38474
- const body = {
38475
- name: opts.name ?? `${slug} consumer`,
38476
- toolset_binding: { base_id: base, toolset_id: slug }
38477
- };
38478
- applyTokenLifecycleOpts(body, opts);
38479
- const client = await createDataClient();
38480
- const data = await client.request("POST", "/v1/tokens", body);
38481
- const output = tokenCreationOutput(this, opts);
38482
- printCreatedToken(data, output);
38483
- if (output === "table") {
38484
- console.log(`
38485
- MCP URL: ${toolsetMcpUrl(slug)}`);
38486
- }
38487
- });
38488
- tokens2.command("list").description("List API tokens (status, last_used_at, expires_at, description)").action(async function() {
38489
- const client = await createDataClient();
38490
- printOutput(await listAllTokens(client), outputFormat(this));
38491
- });
38492
- tokens2.command("revoke <token_id>").description(
38493
- "Revoke an API token (permanent). A recently-used or toolset-bound token requires --force."
38494
- ).option("--force", "Revoke even if the token appears to be in live use").action(async function(tokenId, opts) {
38495
- const client = await createDataClient();
38496
- const id = pathSegment(tokenId, "token id");
38497
- const path42 = (force) => `/v1/tokens/${id}${force ? "?force=true" : ""}`;
38498
- const format = outputFormat(this);
38499
- const revoked = (forced) => printOutput({ token_id: tokenId, revoked: true, forced }, format);
38500
- if (opts.force) {
38501
- await client.request("DELETE", path42(true));
38502
- revoked(true);
38503
- return;
38504
- }
38505
- try {
38506
- await client.request("DELETE", path42(false));
38507
- revoked(false);
38508
- } catch (err) {
38509
- if (!(err instanceof ApiError) || err.status !== 409 || dataErrorDetails(err)?.requires_force !== true) {
38510
- throw err;
38511
- }
38512
- console.error(err.message);
38513
- if (!isInteractive()) {
38514
- console.error("\nRe-run with --force to revoke anyway.");
38515
- return process.exit(1);
38516
- }
38517
- if (!await confirm("Revoke anyway?")) {
38518
- console.error("Aborted.");
38519
- return process.exit(1);
38520
- }
38521
- await client.request("DELETE", path42(true));
38522
- revoked(true);
38523
- }
38524
- });
38525
- tokens2.command("prune").description(
38526
- "Revoke stale tokens, selected by staleness only (expired, or unused past --unused-since) \u2014 never by name. Previews by default; revoking requires --yes or an interactive confirmation."
38527
- ).option(
38528
- "--unused-since <duration>",
38529
- "Also prune tokens with no activity for this long (e.g. 30d). Skips toolset-bound and legacy usage-untracked tokens."
38530
- ).option("--include-bound", "Allow pruning stale toolset-bound (consumer) tokens").option("-y, --yes", "Revoke without prompting (required when non-interactive)").action(async function(opts) {
38531
- const staleMs = opts.unusedSince ? parseDuration(opts.unusedSince, "--unused-since") : void 0;
38532
- const client = await createDataClient();
38533
- const allTokens = await listAllTokens(client);
38534
- const now = Date.now();
38535
- const targets = [];
38536
- const skipped = [];
38537
- for (const t of allTokens) {
38538
- const expired = t.status ? t.status === "expired" : !!t.expires_at && Date.parse(t.expires_at) <= now;
38539
- if (expired) {
38540
- targets.push(pruneRow(t, "expired"));
38541
- continue;
38542
- }
38543
- if (staleMs === void 0) continue;
38544
- const lastActivity = Date.parse(t.last_used_at ?? t.created_at);
38545
- if (!(now - lastActivity > staleMs)) continue;
38546
- if (t.usage_tracked !== true) {
38547
- skipped.push(
38548
- pruneRow(
38549
- t,
38550
- "usage untracked (legacy token) \u2014 revoke individually if truly unused"
38551
- )
38552
- );
38553
- } else if (t.toolset_binding && !opts.includeBound) {
38554
- skipped.push(
38555
- pruneRow(
38556
- t,
38557
- `toolset-bound ('${t.toolset_binding.toolset_id}') \u2014 pass --include-bound to prune`
38558
- )
38559
- );
38560
- } else {
38561
- targets.push(pruneRow(t, `unused for over ${opts.unusedSince}`));
38562
- }
38563
- }
38564
- const format = outputFormat(this);
38565
- const json = format === "json";
38566
- const note = (line) => json ? console.error(line) : console.log(line);
38567
- const printPreview = () => {
38568
- if (targets.length === 0) {
38569
- note("Nothing to prune.");
38570
- } else {
38571
- note(`Would revoke ${targets.length} token(s):`);
38572
- if (!json) printOutput(targets, format);
38573
- }
38574
- if (skipped.length === 0) return;
38575
- note(`
38576
- Skipped ${skipped.length} stale token(s):`);
38577
- if (!json) printOutput(skipped, format);
38578
- };
38579
- printPreview();
38580
- if (targets.length === 0) {
38581
- if (json) printOutput({ revoked: 0, failed: 0, targets, skipped }, format);
38582
- return;
38583
- }
38584
- if (!opts.yes) {
38585
- if (!isInteractive()) {
38586
- console.error("\nPreview only \u2014 re-run with --yes to revoke these tokens.");
38587
- if (json) printOutput({ preview: true, revoked: 0, failed: 0, targets, skipped }, format);
38588
- return process.exit(1);
38589
- }
38590
- if (!await confirm(`
38591
- Revoke ${targets.length} token(s)?`)) {
38592
- console.error("Aborted.");
38593
- return process.exit(1);
38594
- }
38595
- }
38596
- let revoked = 0;
38597
- let failed = 0;
38598
- for (const t of targets) {
38599
- try {
38600
- await client.request(
38601
- "DELETE",
38602
- `/v1/tokens/${pathSegment(t.token_id, "token id")}?force=true`
38603
- );
38604
- revoked++;
38605
- } catch (err) {
38606
- failed++;
38607
- console.error(
38608
- `Failed to revoke ${t.token_id} ('${sanitizeTerminalText(t.name)}'): ${err instanceof Error ? err.message : String(err)}`
38609
- );
38610
- }
38611
- }
38612
- if (json) printOutput({ revoked, failed, targets, skipped }, format);
38613
- else console.log(`Revoked ${revoked} token(s)${failed > 0 ? `, ${failed} failed` : ""}.`);
38614
- if (failed > 0) return process.exit(1);
38615
- });
38616
- return tokens2;
38617
- }
38618
- var RAW_TOKEN_PATTERN, TOKEN_OUTPUT_HELP, TOKEN_LIST_PAGE_LIMIT;
38619
- var init_tokens = __esm({
38620
- "src/data/commands/tokens.ts"() {
38621
- "use strict";
38622
- init_client();
38623
- init_output();
38624
- init_helpers();
38625
- init_api_client();
38626
- init_terminal_output();
38627
- init_utils();
38628
- init_expected();
38629
- RAW_TOKEN_PATTERN = /^rec_[A-Za-z0-9_-]{43}$/;
38630
- TOKEN_OUTPUT_HELP = `
38631
- Output:
38632
- --json prints the unwrapped response object; the secret is at top-level .token, never .data.token.
38633
- --token-only prints only a validated rec_ token and overrides table/JSON formatting.
38634
- The raw token is shown once. Capture it securely and do not log it.
38635
- `;
38636
- TOKEN_LIST_PAGE_LIMIT = 250;
38637
- }
38638
- });
38639
-
38640
39430
  // src/data/base-tags.ts
38641
39431
  async function resolveBaseTags(rawTags, client) {
38642
39432
  const refs = splitList(rawTags);
@@ -39550,7 +40340,7 @@ function isConflict(err) {
39550
40340
  return err instanceof ApiError && (err.status === 409 || err.status === 412);
39551
40341
  }
39552
40342
  function conflictLine(c, texts) {
39553
- return `${safe2(c.rel)}: ${texts[c.reason]}${c.other === void 0 ? "" : ` "${safe2(c.other)}"`}`;
40343
+ return `${safe3(c.rel)}: ${texts[c.reason]}${c.other === void 0 ? "" : ` "${safe3(c.other)}"`}`;
39554
40344
  }
39555
40345
  function remedy(conflicts, overwriteCommand) {
39556
40346
  const isClash = (c) => c.reason === "case_twin" || c.reason === "file_folder";
@@ -39571,7 +40361,7 @@ async function pullFiles(opts) {
39571
40361
  const plan = planPull({ synced: existing?.files ?? {}, local, remote, overwrite: opts.overwrite });
39572
40362
  if (plan.replacedLocal.length > 0) {
39573
40363
  console.log("These local changes would be replaced with the base's copies:");
39574
- for (const rel of plan.replacedLocal) console.log(` ${safe2(rel)}`);
40364
+ for (const rel of plan.replacedLocal) console.log(` ${safe3(rel)}`);
39575
40365
  if (!await agree(`Replace ${plan.replacedLocal.length} locally changed file(s)?`, opts.yes)) {
39576
40366
  console.log("Cancelled.");
39577
40367
  return;
@@ -39592,7 +40382,7 @@ async function pullFiles(opts) {
39592
40382
  }
39593
40383
  removeLocal(dir, rel);
39594
40384
  delete record.files[rel];
39595
- changes.push(`${MARK.delete} ${safe2(rel)}`);
40385
+ changes.push(`${MARK.delete} ${safe3(rel)}`);
39596
40386
  }
39597
40387
  for (const w of plan.writes) {
39598
40388
  const clash = diskClash(dir, w.rel);
@@ -39625,7 +40415,7 @@ async function pullFiles(opts) {
39625
40415
  const hash = computeHash(bytes);
39626
40416
  writeLocal(dir, w.rel, bytes);
39627
40417
  record.files[w.rel] = { version, content_sha256: hash };
39628
- changes.push(`${local.has(w.rel) ? MARK.update : MARK.create} ${safe2(w.rel)}`);
40418
+ changes.push(`${local.has(w.rel) ? MARK.update : MARK.create} ${safe3(w.rel)}`);
39629
40419
  }
39630
40420
  } finally {
39631
40421
  writeRecord(dir, record);
@@ -39633,13 +40423,13 @@ async function pullFiles(opts) {
39633
40423
  const count = Object.keys(record.files).length;
39634
40424
  console.log(sanitizeTerminalText(`Pulled ${describeSync(target)} \u2192 ${dir} (${count} file${count === 1 ? "" : "s"}).`));
39635
40425
  printSection("Changes:", changes);
39636
- printSection("Kept \u2014 your local changes:", plan.kept.map((k) => `${safe2(k.rel)}: ${PULL_KEPT[k.reason]}`));
40426
+ printSection("Kept \u2014 your local changes:", plan.kept.map((k) => `${safe3(k.rel)}: ${PULL_KEPT[k.reason]}`));
39637
40427
  printSection(
39638
40428
  `Conflicts \u2014 your copy is kept; to settle them, ${remedy(conflicts, pullCommand2(target, opts.dir, true))}:`,
39639
40429
  conflicts.map((c) => conflictLine(c, PULL_CONFLICT))
39640
40430
  );
39641
- printSection("Changed or deleted on the base while pulling \u2014 left as is; pull again to take it:", changedWhilePulling.map(safe2));
39642
- printSection(`Not synced \u2014 names a synced folder leaves out (${OUTSIDE_SYNC.join(", ")}):`, excluded.map(safe2));
40431
+ printSection("Changed or deleted on the base while pulling \u2014 left as is; pull again to take it:", changedWhilePulling.map(safe3));
40432
+ printSection(`Not synced \u2014 names a synced folder leaves out (${OUTSIDE_SYNC.join(", ")}):`, excluded.map(safe3));
39643
40433
  }
39644
40434
  function isTextType(contentType) {
39645
40435
  const ct = contentType.split(";")[0].trim().toLowerCase();
@@ -39657,7 +40447,7 @@ function changedLines(before, after) {
39657
40447
  async function describeSteps(client, t, dir, steps) {
39658
40448
  const lines = [];
39659
40449
  for (const step of steps) {
39660
- const rel = safe2(step.rel);
40450
+ const rel = safe3(step.rel);
39661
40451
  if (step.op === "create") {
39662
40452
  lines.push(`${MARK.create} ${rel} (new, ${step.local.size} bytes)`);
39663
40453
  } else if (step.op === "delete") {
@@ -39684,10 +40474,10 @@ async function describeSteps(client, t, dir, steps) {
39684
40474
  return lines;
39685
40475
  }
39686
40476
  function printPushNotes(plan) {
39687
- printSection("Changed on the base since your last pull \u2014 left as is; pull to take it:", plan.changedOnBase.map(safe2));
39688
- printSection("Deleted on the base since your last pull \u2014 left as is; pull to remove your copy:", plan.deletedOnBase.map(safe2));
39689
- printSection("On the base, not in your files \u2014 left as is; pull to fetch it:", plan.onlyOnBase.map(safe2));
39690
- printSection("Deleted in your files \u2014 kept on the base; push with --prune to delete it:", plan.keptOnBase.map(safe2));
40477
+ printSection("Changed on the base since your last pull \u2014 left as is; pull to take it:", plan.changedOnBase.map(safe3));
40478
+ printSection("Deleted on the base since your last pull \u2014 left as is; pull to remove your copy:", plan.deletedOnBase.map(safe3));
40479
+ printSection("On the base, not in your files \u2014 left as is; pull to fetch it:", plan.onlyOnBase.map(safe3));
40480
+ printSection("Deleted in your files \u2014 kept on the base; push with --prune to delete it:", plan.keptOnBase.map(safe3));
39691
40481
  }
39692
40482
  async function pushFiles(opts) {
39693
40483
  const label = opts.label === void 0 ? void 0 : labelValue(opts.label);
@@ -39750,7 +40540,7 @@ Push ${n} change${n === 1 ? "" : "s"} to ${describeSync(target)}?`, opts.yes)) {
39750
40540
  } else {
39751
40541
  const bytes = readLocal(dir, step.rel);
39752
40542
  if (computeHash(bytes) !== step.local.content_sha256) {
39753
- throw expected(`${safe2(step.rel)} changed while the push ran. Nothing more was pushed; run the push again.`);
40543
+ throw expected(`${safe3(step.rel)} changed while the push ran. Nothing more was pushed; run the push again.`);
39754
40544
  }
39755
40545
  const contentType = step.op === "update" ? step.remote.content_type : guessMimeType(step.rel) ?? "application/octet-stream";
39756
40546
  const row = await client.upload(
@@ -39765,7 +40555,7 @@ Push ${n} change${n === 1 ? "" : "s"} to ${describeSync(target)}?`, opts.yes)) {
39765
40555
  };
39766
40556
  }
39767
40557
  pushed++;
39768
- console.log(` ${MARK[step.op]} ${safe2(step.rel)}`);
40558
+ console.log(` ${MARK[step.op]} ${safe3(step.rel)}`);
39769
40559
  } catch (err) {
39770
40560
  if (step.op === "delete" && err instanceof ApiError && err.status === 404) {
39771
40561
  delete record.files[step.rel];
@@ -39792,7 +40582,7 @@ function finishWithConflicts(t, dir, conflicts) {
39792
40582
  `${count} conflict${count === 1 ? "" : "s"} not pushed \u2014 the base's copies of those files were left as they are. To settle them, ${remedy(conflicts, pullCommand2(t, dir, true))}, then push again.`
39793
40583
  );
39794
40584
  }
39795
- var LIST_PAGE2, MAX_LIST_PAGES2, MAX_DIFF_BYTES, MARK, safe2, PULL_KEPT, PULL_CONFLICT, PUSH_CONFLICT;
40585
+ var LIST_PAGE2, MAX_LIST_PAGES2, MAX_DIFF_BYTES, MARK, safe3, PULL_KEPT, PULL_CONFLICT, PUSH_CONFLICT;
39796
40586
  var init_sync3 = __esm({
39797
40587
  "src/data/files-sync/sync.ts"() {
39798
40588
  "use strict";
@@ -39814,7 +40604,7 @@ var init_sync3 = __esm({
39814
40604
  MAX_LIST_PAGES2 = 1e3;
39815
40605
  MAX_DIFF_BYTES = 256 * 1024;
39816
40606
  MARK = { create: "+", update: "~", delete: "-" };
39817
- safe2 = (rel) => sanitizeTerminalText(rel);
40607
+ safe3 = (rel) => sanitizeTerminalText(rel);
39818
40608
  PULL_KEPT = {
39819
40609
  edited: "edited in your files, not pushed yet",
39820
40610
  deleted: "deleted in your files, not pushed yet (push --prune deletes it from the base)"
@@ -41436,6 +42226,7 @@ Commands:
41436
42226
  contacts Link a contact to its customer record in a base, or unlink it (browser sign-in only) \u2014 \`wayai contacts --help\`
41437
42227
  knowledge migrate Move an organization's knowledge resources, then a hub's (--hub), into its knowledge base as file types and files \u2014 \`wayai knowledge --help\`
41438
42228
  knowledge refresh-preview Make the knowledge base's preview hold its production base's files \u2014 \`wayai knowledge --help\`
42229
+ knowledge cut-over Switch a preview hub's agents from their knowledge resources to the knowledge base \u2014 \`wayai knowledge --help\`
41439
42230
  report create Report a platform bug (submits to the triage queue)
41440
42231
  report edit Amend your own pending report (title/description/error/steps/context)
41441
42232
  update Update CLI to the latest version