@wayai/cli 0.3.222 → 0.3.224

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
@@ -488,6 +488,67 @@ var init_sentry = __esm({
488
488
  }
489
489
  });
490
490
 
491
+ // ../../packages/core/dist/chunk-DHPUPI4Y.js
492
+ function normalizeAuthType(raw) {
493
+ if (VALID_AUTH_TYPES.has(raw)) return raw;
494
+ return LEGACY_AUTH_TYPE_MAP[raw] ?? raw;
495
+ }
496
+ var AUTH_TYPE, ORG_CREDENTIAL_AUTH_TYPES, AUTH_TYPE_DISPLAY, LEGACY_AUTH_TYPE_MAP, VALID_AUTH_TYPES, AGENT_ROLES, PILOT_TRACK_ROLES, COPILOT_TRACK_ROLES, CUSTOMER_FACING_AGENT_ROLES, SAFE_USER_ID_REGEX, SUPPORTED_LOCALES, SKILL_INSTALL_COMMAND;
497
+ var init_chunk_DHPUPI4Y = __esm({
498
+ "../../packages/core/dist/chunk-DHPUPI4Y.js"() {
499
+ "use strict";
500
+ AUTH_TYPE = {
501
+ OAUTH: "oauth",
502
+ API_KEY: "api_key",
503
+ BASIC_AUTH: "basic_auth",
504
+ BEARER: "bearer",
505
+ NONE: "none"
506
+ };
507
+ ORG_CREDENTIAL_AUTH_TYPES = [
508
+ AUTH_TYPE.API_KEY,
509
+ AUTH_TYPE.BEARER,
510
+ AUTH_TYPE.BASIC_AUTH
511
+ ];
512
+ AUTH_TYPE_DISPLAY = {
513
+ [AUTH_TYPE.OAUTH]: "OAuth",
514
+ [AUTH_TYPE.API_KEY]: "API Key",
515
+ [AUTH_TYPE.BASIC_AUTH]: "Basic Auth",
516
+ [AUTH_TYPE.BEARER]: "Bearer Token",
517
+ [AUTH_TYPE.NONE]: "No Authentication"
518
+ };
519
+ LEGACY_AUTH_TYPE_MAP = {
520
+ "OAuth": AUTH_TYPE.OAUTH,
521
+ "API Key": AUTH_TYPE.API_KEY,
522
+ "Basic Auth": AUTH_TYPE.BASIC_AUTH,
523
+ "Bearer Token": AUTH_TYPE.BEARER,
524
+ "No authentication": AUTH_TYPE.NONE,
525
+ // Also handle the old DISPLAY_TO_CODE bug where 'basic' was used instead of 'basic_auth'
526
+ "basic": AUTH_TYPE.BASIC_AUTH
527
+ };
528
+ VALID_AUTH_TYPES = new Set(Object.values(AUTH_TYPE));
529
+ AGENT_ROLES = [
530
+ "pilot",
531
+ "copilot",
532
+ "pilot_specialist",
533
+ "copilot_specialist",
534
+ "pilot_advisor",
535
+ "copilot_advisor",
536
+ "monitor",
537
+ "conversation_evaluator",
538
+ "message_evaluator",
539
+ "summarizer",
540
+ "consultant",
541
+ "pilot_voice"
542
+ ];
543
+ PILOT_TRACK_ROLES = ["pilot", "pilot_specialist"];
544
+ COPILOT_TRACK_ROLES = ["copilot", "copilot_specialist"];
545
+ CUSTOMER_FACING_AGENT_ROLES = [...PILOT_TRACK_ROLES, "pilot_advisor"];
546
+ SAFE_USER_ID_REGEX = /^[A-Za-z0-9_-]{1,64}$/;
547
+ SUPPORTED_LOCALES = ["en", "pt", "es"];
548
+ SKILL_INSTALL_COMMAND = "mkdir -p .claude && npx skills add wayai-pro/wayai-skill -y";
549
+ }
550
+ });
551
+
491
552
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
492
553
  var util, objectUtil, ZodParsedType, getParsedType;
493
554
  var init_util = __esm({
@@ -4604,6 +4665,146 @@ var init_zod = __esm({
4604
4665
  }
4605
4666
  });
4606
4667
 
4668
+ // ../../packages/core/dist/chunk-SXDX4GHS.js
4669
+ var orgCredentialIdParam, listOrgCredentialsQuery, createOrgCredentialBody, updateOrgCredentialBody;
4670
+ var init_chunk_SXDX4GHS = __esm({
4671
+ "../../packages/core/dist/chunk-SXDX4GHS.js"() {
4672
+ "use strict";
4673
+ init_chunk_DHPUPI4Y();
4674
+ init_zod();
4675
+ orgCredentialIdParam = external_exports.object({
4676
+ id: external_exports.string().uuid()
4677
+ });
4678
+ listOrgCredentialsQuery = external_exports.object({
4679
+ organization_id: external_exports.string().uuid(),
4680
+ hub_id: external_exports.string().uuid().optional()
4681
+ });
4682
+ createOrgCredentialBody = external_exports.object({
4683
+ organization_id: external_exports.string().uuid(),
4684
+ credential_display_name: external_exports.string().min(1).max(255),
4685
+ credential_description: external_exports.string().max(1e3).nullish(),
4686
+ authentication_type: external_exports.enum(["api_key", "bearer", "basic_auth"]).or(
4687
+ external_exports.enum(["API Key", "Bearer Token", "Basic Auth"]).transform((v) => normalizeAuthType(v))
4688
+ ),
4689
+ api_key: external_exports.string().optional(),
4690
+ access_token: external_exports.string().optional(),
4691
+ username: external_exports.string().optional(),
4692
+ password: external_exports.string().optional(),
4693
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4694
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4695
+ });
4696
+ updateOrgCredentialBody = external_exports.object({
4697
+ credential_display_name: external_exports.string().min(1).max(255).optional(),
4698
+ credential_description: external_exports.string().max(1e3).nullish(),
4699
+ api_key: external_exports.string().optional(),
4700
+ access_token: external_exports.string().optional(),
4701
+ username: external_exports.string().optional(),
4702
+ password: external_exports.string().optional(),
4703
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4704
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4705
+ });
4706
+ }
4707
+ });
4708
+
4709
+ // ../../packages/core/dist/chunk-FK6HBXM7.js
4710
+ var orgResourceIdParam, orgResourceFileIdParam, orgResourceFolderIdParam, hubOrgResourceParam, listOrgResourcesQuery, orgIdQuery, createOrgResourceBody, updateOrgResourceBody, createOrgResourceFolderBody, updateOrgResourceFolderBody, uploadOrgResourceFileBody, updateOrgResourceFileBody, uploadOrgSkillZipBody, linkOrgResourceBody;
4711
+ var init_chunk_FK6HBXM7 = __esm({
4712
+ "../../packages/core/dist/chunk-FK6HBXM7.js"() {
4713
+ "use strict";
4714
+ init_zod();
4715
+ orgResourceIdParam = external_exports.object({
4716
+ id: external_exports.string().uuid()
4717
+ });
4718
+ orgResourceFileIdParam = external_exports.object({
4719
+ file_id: external_exports.string().uuid()
4720
+ });
4721
+ orgResourceFolderIdParam = external_exports.object({
4722
+ folder_id: external_exports.string().uuid()
4723
+ });
4724
+ hubOrgResourceParam = external_exports.object({
4725
+ id: external_exports.string().uuid(),
4726
+ org_resource_id: external_exports.string().uuid()
4727
+ });
4728
+ listOrgResourcesQuery = external_exports.object({
4729
+ organization_id: external_exports.string().uuid(),
4730
+ hub_id: external_exports.string().uuid().optional()
4731
+ });
4732
+ orgIdQuery = external_exports.object({
4733
+ organization_id: external_exports.string().uuid()
4734
+ });
4735
+ createOrgResourceBody = external_exports.object({
4736
+ organization_id: external_exports.string().uuid(),
4737
+ resource_name: external_exports.string().min(1).max(255),
4738
+ resource_description: external_exports.string().max(1e3).nullish(),
4739
+ resource_type: external_exports.enum(["knowledge", "skill"]).default("knowledge"),
4740
+ // Skill-only fields. Required for `resource_type = 'skill'` (validated in
4741
+ // the route layer since cross-field refinement complicates the inferred
4742
+ // input type for callers). Ignored for `knowledge`.
4743
+ skill_name: external_exports.string().min(1).max(255).nullish(),
4744
+ skill_version: external_exports.string().max(50).nullish(),
4745
+ folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4746
+ file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4747
+ enabled: external_exports.boolean().optional(),
4748
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4749
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4750
+ });
4751
+ updateOrgResourceBody = external_exports.object({
4752
+ resource_name: external_exports.string().min(1).max(255).optional(),
4753
+ resource_description: external_exports.string().max(1e3).nullish(),
4754
+ skill_name: external_exports.string().min(1).max(255).nullish(),
4755
+ skill_version: external_exports.string().max(50).nullish(),
4756
+ folder_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4757
+ file_metadata_schema: external_exports.record(external_exports.unknown()).optional(),
4758
+ enabled: external_exports.boolean().optional(),
4759
+ tags: external_exports.array(external_exports.string().uuid()).optional(),
4760
+ environment: external_exports.enum(["preview", "production", "all"]).optional()
4761
+ });
4762
+ createOrgResourceFolderBody = external_exports.object({
4763
+ org_resource_id: external_exports.string().uuid(),
4764
+ folder_name: external_exports.string().min(1).max(255),
4765
+ folder_description: external_exports.string().max(1e3).nullish(),
4766
+ parent_folder_id: external_exports.string().uuid().nullish(),
4767
+ order_index: external_exports.number().int().nonnegative().optional(),
4768
+ enabled: external_exports.boolean().optional(),
4769
+ metadata: external_exports.record(external_exports.unknown()).optional()
4770
+ });
4771
+ updateOrgResourceFolderBody = external_exports.object({
4772
+ folder_name: external_exports.string().min(1).max(255).optional(),
4773
+ folder_description: external_exports.string().max(1e3).nullish(),
4774
+ parent_folder_id: external_exports.string().uuid().nullish(),
4775
+ order_index: external_exports.number().int().nonnegative().optional(),
4776
+ enabled: external_exports.boolean().optional(),
4777
+ metadata: external_exports.record(external_exports.unknown()).optional()
4778
+ });
4779
+ uploadOrgResourceFileBody = external_exports.object({
4780
+ org_resource_id: external_exports.string().uuid(),
4781
+ folder_id: external_exports.string().uuid().nullish(),
4782
+ file_name: external_exports.string().min(1).max(255),
4783
+ file_data: external_exports.string(),
4784
+ // base64
4785
+ mime_type: external_exports.string().optional(),
4786
+ file_size: external_exports.number().int().nonnegative().optional(),
4787
+ resource_file_tags: external_exports.array(external_exports.string()).optional(),
4788
+ metadata: external_exports.record(external_exports.unknown()).optional()
4789
+ });
4790
+ updateOrgResourceFileBody = external_exports.object({
4791
+ resource_file_title: external_exports.string().min(1).max(255).optional(),
4792
+ folder_id: external_exports.string().uuid().nullish(),
4793
+ order_index: external_exports.number().int().nonnegative().optional(),
4794
+ enabled: external_exports.boolean().optional(),
4795
+ resource_file_tags: external_exports.array(external_exports.string()).optional(),
4796
+ metadata: external_exports.record(external_exports.unknown()).optional()
4797
+ });
4798
+ uploadOrgSkillZipBody = external_exports.object({
4799
+ zip_data: external_exports.string().min(1),
4800
+ replace_existing: external_exports.boolean().optional()
4801
+ });
4802
+ linkOrgResourceBody = external_exports.object({
4803
+ org_resource_id: external_exports.string().uuid()
4804
+ });
4805
+ }
4806
+ });
4807
+
4607
4808
  // ../../packages/core/dist/chunk-VXEAR36B.js
4608
4809
  var orgTagIdParam, listOrgTagsQuery, ORG_TAG_NAME_REGEX, ORG_TAG_NAME_MAX_LENGTH, createOrgTagBody, updateOrgTagBody;
4609
4810
  var init_chunk_VXEAR36B = __esm({
@@ -4912,10 +5113,10 @@ var init_chunk_Q7KIQHAZ = __esm({
4912
5113
  }
4913
5114
  });
4914
5115
 
4915
- // ../../packages/core/dist/chunk-2U4BTOR7.js
5116
+ // ../../packages/core/dist/chunk-QUGQIL47.js
4916
5117
  var connectionIdParam, connectorIdParam, listConnectionsQuery, deleteConnectionQuery, addConnectionBody, editConnectionBody, PRODUCTION_DIRECT_FIELDS, setProductionCredentialBody, registerWhatsappQuery, registerWhatsappBody, retryProvisioningQuery, resendDomainStatusQuery;
4917
- var init_chunk_2U4BTOR7 = __esm({
4918
- "../../packages/core/dist/chunk-2U4BTOR7.js"() {
5118
+ var init_chunk_QUGQIL47 = __esm({
5119
+ "../../packages/core/dist/chunk-QUGQIL47.js"() {
4919
5120
  "use strict";
4920
5121
  init_chunk_HZ6CADSY();
4921
5122
  init_zod();
@@ -4959,8 +5160,10 @@ var init_chunk_2U4BTOR7 = __esm({
4959
5160
  webhook_signing_secret: external_exports.string().optional()
4960
5161
  }).strict().superRefine((data, ctx) => {
4961
5162
  const hasOrgLink = !!data.organization_credential_id || !!data.organization_credential_token_id;
4962
- const hasDirect = PRODUCTION_DIRECT_FIELDS.some((f) => data[f] != null);
4963
- if (hasOrgLink && hasDirect) {
5163
+ const sets = (f) => data[f] != null && data[f] !== `__USE_EXISTING_${f.toUpperCase()}__`;
5164
+ const hasDirect = PRODUCTION_DIRECT_FIELDS.some(sets);
5165
+ const hasDirectBesideLink = PRODUCTION_DIRECT_FIELDS.some((f) => f !== "webhook_secret_token" && sets(f));
5166
+ if (hasOrgLink && hasDirectBesideLink) {
4964
5167
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "Provide either an organization credential link or direct credential fields, not both" });
4965
5168
  }
4966
5169
  if (!hasOrgLink && !hasDirect) {
@@ -5082,207 +5285,6 @@ var init_chunk_GK27ENTT = __esm({
5082
5285
  }
5083
5286
  });
5084
5287
 
5085
- // ../../packages/core/dist/chunk-DHPUPI4Y.js
5086
- function normalizeAuthType(raw) {
5087
- if (VALID_AUTH_TYPES.has(raw)) return raw;
5088
- return LEGACY_AUTH_TYPE_MAP[raw] ?? raw;
5089
- }
5090
- var AUTH_TYPE, ORG_CREDENTIAL_AUTH_TYPES, AUTH_TYPE_DISPLAY, LEGACY_AUTH_TYPE_MAP, VALID_AUTH_TYPES, AGENT_ROLES, PILOT_TRACK_ROLES, COPILOT_TRACK_ROLES, CUSTOMER_FACING_AGENT_ROLES, SAFE_USER_ID_REGEX, SUPPORTED_LOCALES, SKILL_INSTALL_COMMAND;
5091
- var init_chunk_DHPUPI4Y = __esm({
5092
- "../../packages/core/dist/chunk-DHPUPI4Y.js"() {
5093
- "use strict";
5094
- AUTH_TYPE = {
5095
- OAUTH: "oauth",
5096
- API_KEY: "api_key",
5097
- BASIC_AUTH: "basic_auth",
5098
- BEARER: "bearer",
5099
- NONE: "none"
5100
- };
5101
- ORG_CREDENTIAL_AUTH_TYPES = [
5102
- AUTH_TYPE.API_KEY,
5103
- AUTH_TYPE.BEARER,
5104
- AUTH_TYPE.BASIC_AUTH
5105
- ];
5106
- AUTH_TYPE_DISPLAY = {
5107
- [AUTH_TYPE.OAUTH]: "OAuth",
5108
- [AUTH_TYPE.API_KEY]: "API Key",
5109
- [AUTH_TYPE.BASIC_AUTH]: "Basic Auth",
5110
- [AUTH_TYPE.BEARER]: "Bearer Token",
5111
- [AUTH_TYPE.NONE]: "No Authentication"
5112
- };
5113
- LEGACY_AUTH_TYPE_MAP = {
5114
- "OAuth": AUTH_TYPE.OAUTH,
5115
- "API Key": AUTH_TYPE.API_KEY,
5116
- "Basic Auth": AUTH_TYPE.BASIC_AUTH,
5117
- "Bearer Token": AUTH_TYPE.BEARER,
5118
- "No authentication": AUTH_TYPE.NONE,
5119
- // Also handle the old DISPLAY_TO_CODE bug where 'basic' was used instead of 'basic_auth'
5120
- "basic": AUTH_TYPE.BASIC_AUTH
5121
- };
5122
- VALID_AUTH_TYPES = new Set(Object.values(AUTH_TYPE));
5123
- AGENT_ROLES = [
5124
- "pilot",
5125
- "copilot",
5126
- "pilot_specialist",
5127
- "copilot_specialist",
5128
- "pilot_advisor",
5129
- "copilot_advisor",
5130
- "monitor",
5131
- "conversation_evaluator",
5132
- "message_evaluator",
5133
- "summarizer",
5134
- "consultant",
5135
- "pilot_voice"
5136
- ];
5137
- PILOT_TRACK_ROLES = ["pilot", "pilot_specialist"];
5138
- COPILOT_TRACK_ROLES = ["copilot", "copilot_specialist"];
5139
- CUSTOMER_FACING_AGENT_ROLES = [...PILOT_TRACK_ROLES, "pilot_advisor"];
5140
- SAFE_USER_ID_REGEX = /^[A-Za-z0-9_-]{1,64}$/;
5141
- SUPPORTED_LOCALES = ["en", "pt", "es"];
5142
- SKILL_INSTALL_COMMAND = "mkdir -p .claude && npx skills add wayai-pro/wayai-skill -y";
5143
- }
5144
- });
5145
-
5146
- // ../../packages/core/dist/chunk-SXDX4GHS.js
5147
- var orgCredentialIdParam, listOrgCredentialsQuery, createOrgCredentialBody, updateOrgCredentialBody;
5148
- var init_chunk_SXDX4GHS = __esm({
5149
- "../../packages/core/dist/chunk-SXDX4GHS.js"() {
5150
- "use strict";
5151
- init_chunk_DHPUPI4Y();
5152
- init_zod();
5153
- orgCredentialIdParam = external_exports.object({
5154
- id: external_exports.string().uuid()
5155
- });
5156
- listOrgCredentialsQuery = external_exports.object({
5157
- organization_id: external_exports.string().uuid(),
5158
- hub_id: external_exports.string().uuid().optional()
5159
- });
5160
- createOrgCredentialBody = external_exports.object({
5161
- organization_id: external_exports.string().uuid(),
5162
- credential_display_name: external_exports.string().min(1).max(255),
5163
- credential_description: external_exports.string().max(1e3).nullish(),
5164
- authentication_type: external_exports.enum(["api_key", "bearer", "basic_auth"]).or(
5165
- external_exports.enum(["API Key", "Bearer Token", "Basic Auth"]).transform((v) => normalizeAuthType(v))
5166
- ),
5167
- api_key: external_exports.string().optional(),
5168
- access_token: external_exports.string().optional(),
5169
- username: external_exports.string().optional(),
5170
- password: external_exports.string().optional(),
5171
- tags: external_exports.array(external_exports.string().uuid()).optional(),
5172
- environment: external_exports.enum(["preview", "production", "all"]).optional()
5173
- });
5174
- updateOrgCredentialBody = external_exports.object({
5175
- credential_display_name: external_exports.string().min(1).max(255).optional(),
5176
- credential_description: external_exports.string().max(1e3).nullish(),
5177
- api_key: external_exports.string().optional(),
5178
- access_token: external_exports.string().optional(),
5179
- username: external_exports.string().optional(),
5180
- password: external_exports.string().optional(),
5181
- tags: external_exports.array(external_exports.string().uuid()).optional(),
5182
- environment: external_exports.enum(["preview", "production", "all"]).optional()
5183
- });
5184
- }
5185
- });
5186
-
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
5288
  // ../../packages/core/dist/chunk-GPM7PE44.js
5287
5289
  var bootstrapQuery;
5288
5290
  var init_chunk_GPM7PE44 = __esm({
@@ -5517,6 +5519,34 @@ var init_chunk_ZVXO3XL6 = __esm({
5517
5519
  }
5518
5520
  });
5519
5521
 
5522
+ // ../../packages/core/dist/chunk-ZXK6N2HN.js
5523
+ var PEOPLE_MODERATION_NOTE_MAX_LENGTH, ADMIN_PEOPLE_REPORTS_PAGE_MAX, ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT, PEOPLE_SUSPENDED_REASON, peopleModerationActionSchema, moderationNoteSchema, adminPeopleReportListQuery, adminPeopleReportIdParam, adminPeopleMessageRemovalBody, adminPeopleSuspendBody, adminPeopleNoteBody;
5524
+ var init_chunk_ZXK6N2HN = __esm({
5525
+ "../../packages/core/dist/chunk-ZXK6N2HN.js"() {
5526
+ "use strict";
5527
+ init_chunk_ZVXO3XL6();
5528
+ init_chunk_HZ6CADSY();
5529
+ init_zod();
5530
+ PEOPLE_MODERATION_NOTE_MAX_LENGTH = 500;
5531
+ ADMIN_PEOPLE_REPORTS_PAGE_MAX = 100;
5532
+ ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT = 50;
5533
+ PEOPLE_SUSPENDED_REASON = "suspended";
5534
+ peopleModerationActionSchema = external_exports.enum(["remove_message", "suspend", "unsuspend", "dismiss"]);
5535
+ moderationNoteSchema = optionalTrimmedText(PEOPLE_MODERATION_NOTE_MAX_LENGTH);
5536
+ adminPeopleReportListQuery = external_exports.object({
5537
+ pending: external_exports.enum(["true", "false"]).optional().transform((value) => value === "true"),
5538
+ /** Lower bound on `created_at`. */
5539
+ since: timestampSchema.optional(),
5540
+ limit: external_exports.coerce.number().int().min(1).max(ADMIN_PEOPLE_REPORTS_PAGE_MAX).default(ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT),
5541
+ offset: external_exports.coerce.number().int().min(0).default(0)
5542
+ });
5543
+ adminPeopleReportIdParam = external_exports.object({ reportId: uuidSchema });
5544
+ adminPeopleMessageRemovalBody = external_exports.object({ report_id: uuidSchema, message_id: uuidSchema.optional(), note: moderationNoteSchema }).strict();
5545
+ adminPeopleSuspendBody = external_exports.object({ report_id: uuidSchema.optional(), note: moderationNoteSchema }).strict();
5546
+ adminPeopleNoteBody = external_exports.object({ note: moderationNoteSchema }).strict();
5547
+ }
5548
+ });
5549
+
5520
5550
  // ../../packages/core/dist/chunk-Y7CFCYX4.js
5521
5551
  var PEOPLE_REPORT_DETAILS_MAX_LENGTH, peopleReportReasonSchema, reportDetailsSchema, reportFields, peopleReportBody;
5522
5552
  var init_chunk_Y7CFCYX4 = __esm({
@@ -5572,10 +5602,10 @@ var init_chunk_5DGHIY7L = __esm({
5572
5602
  }
5573
5603
  });
5574
5604
 
5575
- // ../../packages/core/dist/chunk-F3JHI4Y4.js
5576
- var DATA_PROXY_MOUNT, DATA_PROXY_PREFIX, BASE_DESTRUCTION_MOUNT, BASE_DESTRUCTION_PREFIX, BASE_DESTRUCTION_CONFIRM_PATH, COMMUNITY_EMAIL_UNSUBSCRIBE_PATH, CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH, AUTOMATION_WEBHOOK_MOUNT;
5577
- var init_chunk_F3JHI4Y4 = __esm({
5578
- "../../packages/core/dist/chunk-F3JHI4Y4.js"() {
5605
+ // ../../packages/core/dist/chunk-5XEJSR22.js
5606
+ var DATA_PROXY_MOUNT, DATA_PROXY_PREFIX, BASE_DESTRUCTION_MOUNT, BASE_DESTRUCTION_PREFIX, BASE_DESTRUCTION_CONFIRM_PATH, COMMUNITY_EMAIL_UNSUBSCRIBE_PATH, CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH, AUTOMATION_WEBHOOK_MOUNT, MANAGED_AGENTS_WEBHOOK_MOUNT;
5607
+ var init_chunk_5XEJSR22 = __esm({
5608
+ "../../packages/core/dist/chunk-5XEJSR22.js"() {
5579
5609
  "use strict";
5580
5610
  DATA_PROXY_MOUNT = "/data";
5581
5611
  DATA_PROXY_PREFIX = `/api${DATA_PROXY_MOUNT}`;
@@ -5585,10 +5615,11 @@ var init_chunk_F3JHI4Y4 = __esm({
5585
5615
  COMMUNITY_EMAIL_UNSUBSCRIBE_PATH = "/api/communities/email-unsubscribe";
5586
5616
  CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH = "/api/contact-book/email-unsubscribe";
5587
5617
  AUTOMATION_WEBHOOK_MOUNT = "/webhooks/automations";
5618
+ MANAGED_AGENTS_WEBHOOK_MOUNT = "/webhooks/managed-agents";
5588
5619
  }
5589
5620
  });
5590
5621
 
5591
- // ../../packages/core/dist/chunk-IEWJ5MZH.js
5622
+ // ../../packages/core/dist/chunk-ISFDR7SP.js
5592
5623
  function matchPath(pattern, path42) {
5593
5624
  const normalizedPath = path42.replace(/\/$/, "");
5594
5625
  if (pattern.endsWith("/*")) {
@@ -5616,10 +5647,10 @@ function getEndpointConfig(path42) {
5616
5647
  return { tier: "STANDARD" };
5617
5648
  }
5618
5649
  var ENDPOINT_CONFIGS, EXCLUDED_PATHS;
5619
- var init_chunk_IEWJ5MZH = __esm({
5620
- "../../packages/core/dist/chunk-IEWJ5MZH.js"() {
5650
+ var init_chunk_ISFDR7SP = __esm({
5651
+ "../../packages/core/dist/chunk-ISFDR7SP.js"() {
5621
5652
  "use strict";
5622
- init_chunk_F3JHI4Y4();
5653
+ init_chunk_5XEJSR22();
5623
5654
  ENDPOINT_CONFIGS = {
5624
5655
  // OAuth callbacks - strict, fail-closed (prevent brute force)
5625
5656
  "/oauth/callback/*": { tier: "STRICT", failClosed: true },
@@ -5774,6 +5805,8 @@ var init_chunk_IEWJ5MZH = __esm({
5774
5805
  // platform's egress addresses carry many tenants' deliveries. The per-automation budget is
5775
5806
  // `AUTOMATION_WEBHOOK_PER_AUTOMATION_LIMIT`, counted after the delivery is verified.
5776
5807
  [`${AUTOMATION_WEBHOOK_MOUNT}/*`]: { tier: "HIGH" },
5808
+ // Managed Agents session events (#5083 Phase 4): HIGH, one vendor address carries many tenants'.
5809
+ [`${MANAGED_AGENTS_WEBHOOK_MOUNT}/*`]: { tier: "HIGH" },
5777
5810
  // Reports (CLI → platform_report triage queue). One per-user budget covers intake
5778
5811
  // (POST), the content edit (PATCH /:id), the reporter read-back (GET, GET /:id), and the
5779
5812
  // verification loop (POST /:id/accept, /:id/contest). The limiter is path-based, so a
@@ -6082,7 +6115,7 @@ var init_chunk_IEWJ5MZH = __esm({
6082
6115
  }
6083
6116
  });
6084
6117
 
6085
- // ../../packages/core/dist/chunk-CGOLULK2.js
6118
+ // ../../packages/core/dist/chunk-FNNHGL7K.js
6086
6119
  function fileSnapshotChanged(run) {
6087
6120
  return run.written + run.removed > 0;
6088
6121
  }
@@ -6123,11 +6156,11 @@ async function sendWithRetry(opts, cursor) {
6123
6156
  }
6124
6157
  }
6125
6158
  var rekorFileSnapshotAnswerSchema, FILE_SNAPSHOT_MAX_PAGES, FILE_SNAPSHOT_CONFLICT_ATTEMPTS, FILE_SNAPSHOT_PAGE_SPACING_MS, FILE_SNAPSHOT_CONFLICT_STATUS;
6126
- var init_chunk_CGOLULK2 = __esm({
6127
- "../../packages/core/dist/chunk-CGOLULK2.js"() {
6159
+ var init_chunk_FNNHGL7K = __esm({
6160
+ "../../packages/core/dist/chunk-FNNHGL7K.js"() {
6128
6161
  "use strict";
6129
- init_chunk_IEWJ5MZH();
6130
- init_chunk_F3JHI4Y4();
6162
+ init_chunk_ISFDR7SP();
6163
+ init_chunk_5XEJSR22();
6131
6164
  init_zod();
6132
6165
  rekorFileSnapshotAnswerSchema = external_exports.object({
6133
6166
  data: external_exports.object({
@@ -6193,13 +6226,19 @@ var init_chunk_BEOHBREG = __esm({
6193
6226
  });
6194
6227
 
6195
6228
  // ../../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;
6229
+ function toolsetFileToolName(entry, op) {
6230
+ const names = entry.names;
6231
+ const stated = names?.[op];
6232
+ return stated !== void 0 && stated !== null ? String(stated) : `${op}_${entry.name ?? entry.file_type}`;
6233
+ }
6234
+ 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
6235
  var init_chunk_4MRBBR5L = __esm({
6198
6236
  "../../packages/core/dist/chunk-4MRBBR5L.js"() {
6199
6237
  "use strict";
6200
6238
  init_chunk_BEOHBREG();
6201
6239
  init_chunk_DHPUPI4Y();
6202
6240
  init_zod();
6241
+ TOOLSET_SLUG_SETTING = "toolset";
6203
6242
  TOOLSET_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
6204
6243
  REKOR_STRUCTURAL_ID_PATTERN = TOOLSET_SLUG_PATTERN;
6205
6244
  baseEnvironmentSchema = external_exports.enum(["preview", "production"]);
@@ -6525,6 +6564,20 @@ var init_chunk_YUZFHVB2 = __esm({
6525
6564
  }
6526
6565
  });
6527
6566
 
6567
+ // ../../packages/core/dist/chunk-I3O2RACG.js
6568
+ var managedAgentsWebhookParams, MANAGED_AGENTS_WEBHOOK_BODY_MAX_BYTES;
6569
+ var init_chunk_I3O2RACG = __esm({
6570
+ "../../packages/core/dist/chunk-I3O2RACG.js"() {
6571
+ "use strict";
6572
+ init_zod();
6573
+ managedAgentsWebhookParams = external_exports.object({
6574
+ hub_id: external_exports.string().uuid(),
6575
+ connection_id: external_exports.string().uuid()
6576
+ });
6577
+ MANAGED_AGENTS_WEBHOOK_BODY_MAX_BYTES = 16 * 1024;
6578
+ }
6579
+ });
6580
+
6528
6581
  // ../../packages/core/dist/chunk-34LE6P7E.js
6529
6582
  var templateIdParam, listTemplatesQuery, deleteTemplateQuery, templateStatusQuery, createTemplateBody, updateTemplateBody, submitTemplateBody, testTemplateBody;
6530
6583
  var init_chunk_34LE6P7E = __esm({
@@ -6600,34 +6653,6 @@ var init_chunk_FPSDNS2E = __esm({
6600
6653
  }
6601
6654
  });
6602
6655
 
6603
- // ../../packages/core/dist/chunk-ZXK6N2HN.js
6604
- var PEOPLE_MODERATION_NOTE_MAX_LENGTH, ADMIN_PEOPLE_REPORTS_PAGE_MAX, ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT, PEOPLE_SUSPENDED_REASON, peopleModerationActionSchema, moderationNoteSchema, adminPeopleReportListQuery, adminPeopleReportIdParam, adminPeopleMessageRemovalBody, adminPeopleSuspendBody, adminPeopleNoteBody;
6605
- var init_chunk_ZXK6N2HN = __esm({
6606
- "../../packages/core/dist/chunk-ZXK6N2HN.js"() {
6607
- "use strict";
6608
- init_chunk_ZVXO3XL6();
6609
- init_chunk_HZ6CADSY();
6610
- init_zod();
6611
- PEOPLE_MODERATION_NOTE_MAX_LENGTH = 500;
6612
- ADMIN_PEOPLE_REPORTS_PAGE_MAX = 100;
6613
- ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT = 50;
6614
- PEOPLE_SUSPENDED_REASON = "suspended";
6615
- peopleModerationActionSchema = external_exports.enum(["remove_message", "suspend", "unsuspend", "dismiss"]);
6616
- moderationNoteSchema = optionalTrimmedText(PEOPLE_MODERATION_NOTE_MAX_LENGTH);
6617
- adminPeopleReportListQuery = external_exports.object({
6618
- pending: external_exports.enum(["true", "false"]).optional().transform((value) => value === "true"),
6619
- /** Lower bound on `created_at`. */
6620
- since: timestampSchema.optional(),
6621
- limit: external_exports.coerce.number().int().min(1).max(ADMIN_PEOPLE_REPORTS_PAGE_MAX).default(ADMIN_PEOPLE_REPORTS_PAGE_DEFAULT),
6622
- offset: external_exports.coerce.number().int().min(0).default(0)
6623
- });
6624
- adminPeopleReportIdParam = external_exports.object({ reportId: uuidSchema });
6625
- adminPeopleMessageRemovalBody = external_exports.object({ report_id: uuidSchema, message_id: uuidSchema.optional(), note: moderationNoteSchema }).strict();
6626
- adminPeopleSuspendBody = external_exports.object({ report_id: uuidSchema.optional(), note: moderationNoteSchema }).strict();
6627
- adminPeopleNoteBody = external_exports.object({ note: moderationNoteSchema }).strict();
6628
- }
6629
- });
6630
-
6631
6656
  // ../../packages/core/dist/chunk-MIWZOSPI.js
6632
6657
  function toIso(epochMs) {
6633
6658
  return new Date(epochMs).toISOString();
@@ -6783,7 +6808,7 @@ var init_chunk_E4DIIOOE = __esm({
6783
6808
  }
6784
6809
  });
6785
6810
 
6786
- // ../../packages/core/dist/chunk-FMXFEZN4.js
6811
+ // ../../packages/core/dist/chunk-GMWMQ4YA.js
6787
6812
  function spokenLineField(label, description2) {
6788
6813
  return { type: "text", label, maxLength: SPOKEN_LINE_MAX_CHARS, default: "", description: description2 };
6789
6814
  }
@@ -6793,8 +6818,8 @@ function byLowercaseName(a, b) {
6793
6818
  return x < y ? -1 : x > y ? 1 : 0;
6794
6819
  }
6795
6820
  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"() {
6821
+ var init_chunk_GMWMQ4YA = __esm({
6822
+ "../../packages/core/dist/chunk-GMWMQ4YA.js"() {
6798
6823
  "use strict";
6799
6824
  CALLER_LANGUAGE = "caller";
6800
6825
  TTS_VOICE_REPLY_ENABLED_FIELD = {
@@ -6822,7 +6847,17 @@ var init_chunk_FMXFEZN4 = __esm({
6822
6847
  type: "toggle",
6823
6848
  label: "Code Execution Sandbox",
6824
6849
  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."
6850
+ 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."
6851
+ },
6852
+ sandbox_scope: {
6853
+ type: "select",
6854
+ label: "Sandbox Scope",
6855
+ options: [
6856
+ { value: "conversation", label: "Conversation (shared with the conversation's other agents)" },
6857
+ { value: "agent", label: "Agent (private to this agent)" }
6858
+ ],
6859
+ default: "conversation",
6860
+ 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 a new one, which keeps its /workspace files but none of its running processes. 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
6861
  },
6827
6862
  sandbox_network: {
6828
6863
  type: "select",
@@ -7662,7 +7697,8 @@ var init_chunk_FMXFEZN4 = __esm({
7662
7697
  api_key: {
7663
7698
  connection_display_name: { type: "text", label: "Connection Name", placeholder: "Enter a name for this connection", description: "This name will be used to identify the connection in the system", required: true },
7664
7699
  api_key: { type: "password", label: "API Key", placeholder: "Enter your Anthropic API key", description: "Anthropic API key for the Managed Agents API. Used only from the WayAI Worker \u2014 no sandbox ever sees it.", required: true, maskInEdit: true, useExistingPlaceholder: "__USE_EXISTING_API_KEY__" },
7665
- access_token: { type: "password", label: "GitHub Token (Optional)", placeholder: "Enter a GitHub personal access token", description: "Optional: personal access token for the repo_url checkout. Anthropic's git proxy injects it after requests leave the sandbox \u2014 it never enters the sandbox.", maskInEdit: true, useExistingPlaceholder: "__USE_EXISTING_ACCESS_TOKEN__" }
7700
+ access_token: { type: "password", label: "GitHub Token (Optional)", placeholder: "Enter a GitHub personal access token", description: "Optional: personal access token for the repo_url checkout. Anthropic's git proxy injects it after requests leave the sandbox \u2014 it never enters the sandbox.", maskInEdit: true, useExistingPlaceholder: "__USE_EXISTING_ACCESS_TOKEN__" },
7701
+ webhook_secret_token: { type: "password", label: "Webhook Signing Secret", placeholder: "whsec_\u2026", description: "Signing secret of this connection's webhook endpoint in the Claude Console. Without it, a finished turn is picked up only by a check every 5 minutes.", maskInEdit: true, useExistingPlaceholder: "__USE_EXISTING_WEBHOOK_SECRET_TOKEN__" }
7666
7702
  }
7667
7703
  },
7668
7704
  token_refresh_config: { api_key: { strategy: "none" } },
@@ -7748,13 +7784,13 @@ var init_chunk_FMXFEZN4 = __esm({
7748
7784
  }
7749
7785
  });
7750
7786
 
7751
- // ../../packages/core/dist/chunk-VOC3AGY4.js
7787
+ // ../../packages/core/dist/chunk-PEXTNF6S.js
7752
7788
  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"() {
7789
+ var init_chunk_PEXTNF6S = __esm({
7790
+ "../../packages/core/dist/chunk-PEXTNF6S.js"() {
7755
7791
  "use strict";
7756
7792
  init_chunk_E4DIIOOE();
7757
- init_chunk_FMXFEZN4();
7793
+ init_chunk_GMWMQ4YA();
7758
7794
  init_chunk_HZ6CADSY();
7759
7795
  init_chunk_MIWZOSPI();
7760
7796
  init_zod();
@@ -8121,15 +8157,15 @@ var init_chunk_MH4O3YEX = __esm({
8121
8157
  }
8122
8158
  });
8123
8159
 
8124
- // ../../packages/core/dist/chunk-HX2LNKHX.js
8160
+ // ../../packages/core/dist/chunk-UZ7SPBM3.js
8125
8161
  function catalogToolSideEffects(toolName) {
8126
8162
  const sideEffects = CATALOG_SIDE_EFFECTS.get(toolName);
8127
8163
  if (sideEffects === void 0) return "unknown";
8128
8164
  return sideEffects ? "side_effects" : "none";
8129
8165
  }
8130
8166
  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"() {
8167
+ var init_chunk_UZ7SPBM3 = __esm({
8168
+ "../../packages/core/dist/chunk-UZ7SPBM3.js"() {
8133
8169
  "use strict";
8134
8170
  init_chunk_DHPUPI4Y();
8135
8171
  NATIVE_TOOL_SCHEMAS = {
@@ -8450,7 +8486,7 @@ var init_chunk_HX2LNKHX = __esm({
8450
8486
  "bash": {
8451
8487
  "tool_config": {
8452
8488
  "name": "bash",
8453
- "description": "Run a Bash command in this conversation's sandbox: a disposable Linux container (Debian, with Python 3, curl, jq, zip and other common tools) whose working directory is /workspace. The sandbox starts on the first command and keeps its files between commands in this conversation, but it ends after 10 minutes without use or 60 minutes in all; the next command then starts an empty one, and its result says so. What the sandbox can reach over the network is set for this agent by a hub admin; with no network, nothing leaves it. A command runs for at most timeout_seconds and never past the turn's own time limit. Long output keeps only its first and last parts. The result gives the exit code, stdout and stderr.",
8489
+ "description": "Run a Bash command in this conversation's sandbox: a disposable Linux container (Debian, with Python 3, curl, jq, zip and other common tools) whose working directory is /workspace. The sandbox starts on the first command and keeps its files between commands in this conversation, but it ends after 10 minutes without use or 60 minutes in all; the next command then starts a new one with the /workspace files from the last save, made when a sandbox stopped and at the end of each turn that ran a command, and its result says what came back. Only files under /workspace are kept, never a running process: a long job should write its progress to files under /workspace, so the work can go on from them. HOME is /workspace, so a pip install --user, or a virtual environment under /workspace, is kept too; a plain pip install is not. Installs count toward the saved workspace's size, and when it is too large to keep, what is under /workspace/.local is left out first. What the sandbox can reach over the network is set for this agent by a hub admin; with no network, nothing leaves it. A command runs for at most timeout_seconds and never past the turn's own time limit. Long output keeps only its first and last parts. The result gives the exit code, stdout and stderr.",
8454
8490
  "strict": false,
8455
8491
  "parameters": {
8456
8492
  "type": "object",
@@ -8477,7 +8513,7 @@ var init_chunk_HX2LNKHX = __esm({
8477
8513
  "copy_to_sandbox": {
8478
8514
  "tool_config": {
8479
8515
  "name": "copy_to_sandbox",
8480
- "description": `Copy one of this conversation's files, or one of your documents, into the code sandbox, so commands run with bash can read or edit it there. path names the file as read_file does: this conversation's attachments under "conversation/" (e.g. "conversation/report.xlsx"), an earlier conversation's under "conversations/<conversation_id>/<name>", or a document under "data/<file-type>/" (e.g. "data/contracts/acme/msa.docx"), whose result gives the version you copied: pass it to copy_from_sandbox to copy the document back. Hub resource files cannot be copied. The copy lands at sandbox_path, replacing any file there; a file larger than 25 MiB is refused, and a document larger than 1 MiB. If no sandbox is running, the copy starts an empty one.`,
8516
+ "description": `Copy one of this conversation's files, or one of your documents, into the code sandbox, so commands run with bash can read or edit it there. path names the file as read_file does: this conversation's attachments under "conversation/" (e.g. "conversation/report.xlsx"), an earlier conversation's under "conversations/<conversation_id>/<name>", or a document under "data/<file-type>/" (e.g. "data/contracts/acme/msa.docx"), whose result gives the version you copied: pass it to copy_from_sandbox to copy the document back. Hub resource files cannot be copied. The copy lands at sandbox_path, replacing any file there; a file larger than 25 MiB is refused, and a document larger than 1 MiB. If no sandbox is running, the copy starts one, with the /workspace files from the last save.`,
8481
8517
  "strict": false,
8482
8518
  "parameters": {
8483
8519
  "type": "object",
@@ -9110,9 +9146,10 @@ var init_chunk_HX2LNKHX = __esm({
9110
9146
  // code it runs can send data out of the conversation.
9111
9147
  { 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
9148
  // 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.
9149
+ // INTO the sandbox: the sandbox is conversation state, so nothing leaves the conversation. The
9150
+ // conversation's sandbox is shared by its agents whose `sandbox_scope` is `conversation`, so a
9151
+ // document copied in is there for each of them, as anything else an agent leaves in it; an agent
9152
+ // set to `agent` copies into its own, which no other agent reaches (decision 147).
9116
9153
  { 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
9154
  // Copies a sandbox file OUT into the conversation, as an internal note for the team: nothing
9118
9155
  // reaches the customer until `send_files` sends it. With `path`, it writes a toolset document
@@ -9160,11 +9197,104 @@ var init_chunk_IBL7FANV = __esm({
9160
9197
  });
9161
9198
 
9162
9199
  // ../../packages/core/dist/chunk-PFDZEKDE.js
9163
- var PREVIOUS_CONVERSATIONS_MAX;
9200
+ function parseExpr(expr) {
9201
+ const fnMatch = expr.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*?)\)\s*(?:\.(.*))?$/);
9202
+ if (!fnMatch) return null;
9203
+ const [, fn, rawArgs, rawPath] = fnMatch;
9204
+ const args2 = splitArgs(rawArgs);
9205
+ const path42 = rawPath ? rawPath.split(".").flatMap((seg) => {
9206
+ const indexed = [];
9207
+ seg.replace(/([^\[\]]+)|\[(\d+)\]/g, (_, prop, idx) => {
9208
+ if (prop) indexed.push(prop);
9209
+ else if (idx !== void 0) indexed.push(idx);
9210
+ return "";
9211
+ });
9212
+ return indexed;
9213
+ }) : [];
9214
+ return { fn, args: args2, path: path42 };
9215
+ }
9216
+ function splitArgs(rawArgs) {
9217
+ const trimmed = rawArgs.trim();
9218
+ return trimmed ? trimmed.split(",").map((s) => stripQuotes(s.trim())) : [];
9219
+ }
9220
+ function stripQuotes(s) {
9221
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
9222
+ return s.slice(1, -1);
9223
+ }
9224
+ return s;
9225
+ }
9226
+ function parseCatalogArgs(args2) {
9227
+ let kind = null;
9228
+ let tree = false;
9229
+ for (const token of args2) {
9230
+ if (token === "tree") tree = true;
9231
+ else if ((token === "kb" || token === "skill") && kind === null) kind = token;
9232
+ }
9233
+ return { kind, tree };
9234
+ }
9235
+ function widenKinds(kinds) {
9236
+ if (kinds.has("all") || kinds.size > 1) return "all";
9237
+ return [...kinds][0] ?? null;
9238
+ }
9239
+ function* parseCalls(template) {
9240
+ for (const match of template.matchAll(PLACEHOLDER_RE)) {
9241
+ const parsed = parseExpr(match[1].trim());
9242
+ if (parsed) yield parsed;
9243
+ }
9244
+ }
9245
+ function scanTemplateResources(template) {
9246
+ if (!template || !template.includes("{{")) return EMPTY_SCAN;
9247
+ const scan = { ...EMPTY_SCAN };
9248
+ const names = /* @__PURE__ */ new Set();
9249
+ const paths = /* @__PURE__ */ new Set();
9250
+ const treeKinds = /* @__PURE__ */ new Set();
9251
+ const legacyKinds = /* @__PURE__ */ new Set();
9252
+ for (const { fn, args: args2 } of parseCalls(template)) {
9253
+ switch (fn) {
9254
+ case CATALOG_FN:
9255
+ case LEGACY_CATALOG_FN: {
9256
+ scan.needs_catalog = true;
9257
+ scan.has_catalog_call = true;
9258
+ const { kind, tree } = parseCatalogArgs(args2);
9259
+ if (fn === LEGACY_CATALOG_FN) legacyKinds.add(kind ?? "all");
9260
+ if (tree) treeKinds.add(kind ?? "all");
9261
+ break;
9262
+ }
9263
+ case "agent_skills":
9264
+ scan.needs_catalog = true;
9265
+ break;
9266
+ case "resource_content":
9267
+ if (args2[0]) names.add(args2[0]);
9268
+ break;
9269
+ case "file_content":
9270
+ if (args2[0]) paths.add(args2[0]);
9271
+ break;
9272
+ default:
9273
+ break;
9274
+ }
9275
+ }
9276
+ scan.tree_kinds = widenKinds(treeKinds);
9277
+ scan.legacy_kinds = widenKinds(legacyKinds);
9278
+ scan.resource_content_names = [...names];
9279
+ scan.file_content_paths = [...paths];
9280
+ return scan;
9281
+ }
9282
+ var PREVIOUS_CONVERSATIONS_MAX, PLACEHOLDER_RE, CATALOG_FN, LEGACY_CATALOG_FN, EMPTY_SCAN;
9164
9283
  var init_chunk_PFDZEKDE = __esm({
9165
9284
  "../../packages/core/dist/chunk-PFDZEKDE.js"() {
9166
9285
  "use strict";
9167
9286
  PREVIOUS_CONVERSATIONS_MAX = 20;
9287
+ PLACEHOLDER_RE = /\{\{\s*([^{}]+?)\s*\}\}/g;
9288
+ CATALOG_FN = "files";
9289
+ LEGACY_CATALOG_FN = "resources";
9290
+ EMPTY_SCAN = {
9291
+ needs_catalog: false,
9292
+ has_catalog_call: false,
9293
+ legacy_kinds: null,
9294
+ tree_kinds: null,
9295
+ resource_content_names: [],
9296
+ file_content_paths: []
9297
+ };
9168
9298
  }
9169
9299
  });
9170
9300
 
@@ -9355,7 +9485,7 @@ var init_chunk_T7KQKDET = __esm({
9355
9485
  }
9356
9486
  });
9357
9487
 
9358
- // ../../packages/core/dist/chunk-BUXNYLB7.js
9488
+ // ../../packages/core/dist/chunk-XOGFONTQ.js
9359
9489
  function resolveMonitorTrigger(monitorConfig) {
9360
9490
  if (!monitorConfig || typeof monitorConfig !== "object") return "idle";
9361
9491
  const raw = monitorConfig.trigger;
@@ -9661,10 +9791,10 @@ function refineMonitorRulesStructuredOutput(body, ctx) {
9661
9791
  if (refusal2) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["response_format"], message: refusal2 });
9662
9792
  }
9663
9793
  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"() {
9794
+ var init_chunk_XOGFONTQ = __esm({
9795
+ "../../packages/core/dist/chunk-XOGFONTQ.js"() {
9666
9796
  "use strict";
9667
- init_chunk_HX2LNKHX();
9797
+ init_chunk_UZ7SPBM3();
9668
9798
  init_chunk_IBL7FANV();
9669
9799
  init_chunk_PFDZEKDE();
9670
9800
  init_chunk_HZ6CADSY();
@@ -9887,12 +10017,12 @@ var init_chunk_BUXNYLB7 = __esm({
9887
10017
  }
9888
10018
  });
9889
10019
 
9890
- // ../../packages/core/dist/chunk-7YJWGPAR.js
10020
+ // ../../packages/core/dist/chunk-TUP5Y6ZB.js
9891
10021
  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"() {
10022
+ var init_chunk_TUP5Y6ZB = __esm({
10023
+ "../../packages/core/dist/chunk-TUP5Y6ZB.js"() {
9894
10024
  "use strict";
9895
- init_chunk_BUXNYLB7();
10025
+ init_chunk_XOGFONTQ();
9896
10026
  init_chunk_HZ6CADSY();
9897
10027
  init_chunk_DHPUPI4Y();
9898
10028
  init_zod();
@@ -10348,7 +10478,7 @@ var init_chunk_BPCNB5RT = __esm({
10348
10478
  }
10349
10479
  });
10350
10480
 
10351
- // ../../packages/core/dist/chunk-AR73HGPH.js
10481
+ // ../../packages/core/dist/chunk-LQSENJ26.js
10352
10482
  function idempotencyHeaderRefusal(value) {
10353
10483
  if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/.test(value)) {
10354
10484
  return "idempotency_header must be a header name: letters, digits and dashes, at most 64";
@@ -10359,10 +10489,10 @@ function idempotencyHeaderRefusal(value) {
10359
10489
  return null;
10360
10490
  }
10361
10491
  var CATALOG_REPLAY, REPLAY_OVERRIDES, RESERVED_HEADERS;
10362
- var init_chunk_AR73HGPH = __esm({
10363
- "../../packages/core/dist/chunk-AR73HGPH.js"() {
10492
+ var init_chunk_LQSENJ26 = __esm({
10493
+ "../../packages/core/dist/chunk-LQSENJ26.js"() {
10364
10494
  "use strict";
10365
- init_chunk_HX2LNKHX();
10495
+ init_chunk_UZ7SPBM3();
10366
10496
  CATALOG_REPLAY = new Map(
10367
10497
  NATIVE_TOOLS.map((tool) => [tool.tool_name, tool.replay])
10368
10498
  );
@@ -10595,7 +10725,7 @@ var init_chunk_WEXVYJ5H = __esm({
10595
10725
  }
10596
10726
  });
10597
10727
 
10598
- // ../../packages/core/dist/chunk-MK6D4I2Q.js
10728
+ // ../../packages/core/dist/chunk-KRGVB7U6.js
10599
10729
  function refineToolConfig(value, ctx) {
10600
10730
  const raw = value.tool_config;
10601
10731
  if (raw == null) return;
@@ -10631,10 +10761,10 @@ function refineToolConfig(value, ctx) {
10631
10761
  }
10632
10762
  }
10633
10763
  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"() {
10764
+ var init_chunk_KRGVB7U6 = __esm({
10765
+ "../../packages/core/dist/chunk-KRGVB7U6.js"() {
10636
10766
  "use strict";
10637
- init_chunk_AR73HGPH();
10767
+ init_chunk_LQSENJ26();
10638
10768
  init_chunk_WEXVYJ5H();
10639
10769
  init_chunk_HZ6CADSY();
10640
10770
  init_zod();
@@ -12006,7 +12136,7 @@ var init_chunk_6NI5RAM4 = __esm({
12006
12136
  }
12007
12137
  });
12008
12138
 
12009
- // ../../packages/core/dist/chunk-EAT2SP4E.js
12139
+ // ../../packages/core/dist/chunk-RT35VFUY.js
12010
12140
  function refineHubAsCodeCustomTools(config, ctx) {
12011
12141
  const agents = config.agents;
12012
12142
  if (!Array.isArray(agents)) return;
@@ -12542,12 +12672,12 @@ function refineHubAsCodeMonitorRulesOutput(config, ctx) {
12542
12672
  });
12543
12673
  }
12544
12674
  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"() {
12675
+ var init_chunk_RT35VFUY = __esm({
12676
+ "../../packages/core/dist/chunk-RT35VFUY.js"() {
12547
12677
  "use strict";
12548
- init_chunk_MK6D4I2Q();
12678
+ init_chunk_KRGVB7U6();
12549
12679
  init_chunk_623SRYWZ();
12550
- init_chunk_BUXNYLB7();
12680
+ init_chunk_XOGFONTQ();
12551
12681
  init_chunk_LBDH7LCG();
12552
12682
  init_chunk_4LXDRTO4();
12553
12683
  init_chunk_T4Q5COKJ();
@@ -13556,15 +13686,15 @@ var init_chunk_LQY2OCQL = __esm({
13556
13686
  }
13557
13687
  });
13558
13688
 
13559
- // ../../packages/core/dist/chunk-6N2QIHHQ.js
13689
+ // ../../packages/core/dist/chunk-2VTDOH67.js
13560
13690
  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"() {
13691
+ var init_chunk_2VTDOH67 = __esm({
13692
+ "../../packages/core/dist/chunk-2VTDOH67.js"() {
13563
13693
  "use strict";
13564
13694
  init_chunk_LQY2OCQL();
13565
- init_chunk_BUXNYLB7();
13695
+ init_chunk_XOGFONTQ();
13566
13696
  init_chunk_3HSB6J4J();
13567
- init_chunk_F3JHI4Y4();
13697
+ init_chunk_5XEJSR22();
13568
13698
  init_chunk_DHPUPI4Y();
13569
13699
  init_chunk_T7KQKDET();
13570
13700
  init_zod();
@@ -14807,29 +14937,30 @@ var init_chunk_TS4EYU3W = __esm({
14807
14937
  var init_contracts = __esm({
14808
14938
  "../../packages/core/dist/contracts/index.js"() {
14809
14939
  "use strict";
14940
+ init_chunk_SXDX4GHS();
14941
+ init_chunk_FK6HBXM7();
14810
14942
  init_chunk_VXEAR36B();
14811
14943
  init_chunk_TIZ57ZBA();
14812
14944
  init_chunk_Q7KIQHAZ();
14813
- init_chunk_2U4BTOR7();
14945
+ init_chunk_QUGQIL47();
14814
14946
  init_chunk_IPXBFWUU();
14815
14947
  init_chunk_GK27ENTT();
14816
- init_chunk_SXDX4GHS();
14817
- init_chunk_FK6HBXM7();
14818
14948
  init_chunk_GPM7PE44();
14819
14949
  init_chunk_DGKMMFIR();
14820
14950
  init_chunk_ZC6YGHNX();
14821
14951
  init_chunk_ACKF2MVU();
14952
+ init_chunk_ZXK6N2HN();
14822
14953
  init_chunk_Y7CFCYX4();
14823
14954
  init_chunk_5DGHIY7L();
14824
- init_chunk_CGOLULK2();
14955
+ init_chunk_FNNHGL7K();
14825
14956
  init_chunk_4MRBBR5L();
14826
14957
  init_chunk_BEOHBREG();
14827
14958
  init_chunk_IPDOHAZ5();
14828
14959
  init_chunk_YUZFHVB2();
14960
+ init_chunk_I3O2RACG();
14829
14961
  init_chunk_34LE6P7E();
14830
14962
  init_chunk_FPSDNS2E();
14831
- init_chunk_ZXK6N2HN();
14832
- init_chunk_VOC3AGY4();
14963
+ init_chunk_PEXTNF6S();
14833
14964
  init_chunk_C4HEZ7ZI();
14834
14965
  init_chunk_IR2NOSI3();
14835
14966
  init_chunk_EA23LSDS();
@@ -14837,12 +14968,12 @@ var init_contracts = __esm({
14837
14968
  init_chunk_LWMLR2WI();
14838
14969
  init_chunk_ACGAI25T();
14839
14970
  init_chunk_MH4O3YEX();
14840
- init_chunk_7YJWGPAR();
14971
+ init_chunk_TUP5Y6ZB();
14841
14972
  init_chunk_KSTYTQ7I();
14842
14973
  init_chunk_LKBSHANA();
14843
14974
  init_chunk_BPCNB5RT();
14844
- init_chunk_EAT2SP4E();
14845
- init_chunk_MK6D4I2Q();
14975
+ init_chunk_RT35VFUY();
14976
+ init_chunk_KRGVB7U6();
14846
14977
  init_chunk_623SRYWZ();
14847
14978
  init_chunk_JDJ4EUOL();
14848
14979
  init_chunk_3KYTITOR();
@@ -14852,9 +14983,9 @@ var init_contracts = __esm({
14852
14983
  init_chunk_YULHXJ5M();
14853
14984
  init_chunk_V4FRPRH5();
14854
14985
  init_chunk_ZHJEQBXJ();
14855
- init_chunk_6N2QIHHQ();
14986
+ init_chunk_2VTDOH67();
14856
14987
  init_chunk_LQY2OCQL();
14857
- init_chunk_BUXNYLB7();
14988
+ init_chunk_XOGFONTQ();
14858
14989
  init_chunk_3HSB6J4J();
14859
14990
  init_chunk_IG6QBZMA();
14860
14991
  init_chunk_ZJJ5ODOD();
@@ -14863,9 +14994,9 @@ var init_contracts = __esm({
14863
14994
  init_chunk_4LXDRTO4();
14864
14995
  init_chunk_E4DIIOOE();
14865
14996
  init_chunk_7CYHPF35();
14866
- init_chunk_FMXFEZN4();
14867
- init_chunk_AR73HGPH();
14868
- init_chunk_HX2LNKHX();
14997
+ init_chunk_GMWMQ4YA();
14998
+ init_chunk_LQSENJ26();
14999
+ init_chunk_UZ7SPBM3();
14869
15000
  init_chunk_NVKU5PPQ();
14870
15001
  init_chunk_2FCLSQSQ();
14871
15002
  init_chunk_FECLIZTY();
@@ -14878,11 +15009,11 @@ var init_contracts = __esm({
14878
15009
  init_chunk_OMPT4ZHR();
14879
15010
  init_chunk_2PZIF56U();
14880
15011
  init_chunk_UEJOG2Q7();
14881
- init_chunk_IEWJ5MZH();
15012
+ init_chunk_ISFDR7SP();
14882
15013
  init_chunk_SOJMWM63();
14883
15014
  init_chunk_WEXVYJ5H();
14884
15015
  init_chunk_LZDVDC2S();
14885
- init_chunk_F3JHI4Y4();
15016
+ init_chunk_5XEJSR22();
14886
15017
  init_chunk_IBL7FANV();
14887
15018
  init_chunk_PFDZEKDE();
14888
15019
  init_chunk_VORPHGDT();
@@ -15286,6 +15417,61 @@ var init_api_client = __esm({
15286
15417
  }
15287
15418
  return new Uint8Array(await response.arrayBuffer());
15288
15419
  }
15420
+ // ---- A preview hub's knowledge cut-over (`wayai knowledge cut-over`) ----
15421
+ //
15422
+ // The setup routes the web console writes through, so each write takes the same checks: the
15423
+ // as-code push never sees a hub's copies of organization resources or their agent links.
15424
+ async listAgents(hubId) {
15425
+ const params = new URLSearchParams({ hub_id: hubId });
15426
+ return this.request("GET", `/api/setup/agents?${params}`);
15427
+ }
15428
+ /** Patch an agent of a preview hub: the text it reads (`updateAgentBody` takes more). */
15429
+ async updateAgent(hubId, agentId, patch) {
15430
+ const params = new URLSearchParams({ hub_id: hubId });
15431
+ await this.request("PATCH", `/api/setup/agents/${encodeURIComponent(agentId)}?${params}`, patch);
15432
+ }
15433
+ async listAgentResources(hubId, agentId) {
15434
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15435
+ return this.request("GET", `/api/setup/resources/agent-resources?${params}`);
15436
+ }
15437
+ async updateAgentResource(body) {
15438
+ return this.request("PATCH", "/api/setup/resources/agent-resources", body);
15439
+ }
15440
+ /** An agent's tools, and the native and connection tools it could add (`available_action: 'Add'`). */
15441
+ async listAgentTools(hubId, agentId) {
15442
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15443
+ return this.request("GET", `/api/setup/tools?${params}`);
15444
+ }
15445
+ async addNativeTool(hubId, agentId, toolNativeId) {
15446
+ return this.request("POST", "/api/setup/tools/native", {
15447
+ hub_id: hubId,
15448
+ agent_id: agentId,
15449
+ tool_native_id: toolNativeId
15450
+ });
15451
+ }
15452
+ async addMcpTool(hubId, agentId, mcpToolId) {
15453
+ return this.request("POST", "/api/setup/tools/mcp", { hub_id: hubId, agent_id: agentId, mcp_tool_id: mcpToolId });
15454
+ }
15455
+ async enableTool(hubId, agentId, toolId) {
15456
+ const params = new URLSearchParams({ hub_id: hubId, agent_id: agentId });
15457
+ await this.request("POST", `/api/setup/tools/${encodeURIComponent(toolId)}/enable?${params}`);
15458
+ }
15459
+ /** Add a token-holding connection (a Base Toolset's) to a preview hub; the answer lists the hub's connections. */
15460
+ async addConnection(body) {
15461
+ return this.request("POST", "/api/setup/connections", body);
15462
+ }
15463
+ async listAutomations(hubId) {
15464
+ const params = new URLSearchParams({ hub_id: hubId });
15465
+ return this.request("GET", `/api/automations?${params}`);
15466
+ }
15467
+ async updateAutomation(hubId, automationId, body) {
15468
+ const params = new URLSearchParams({ hub_id: hubId });
15469
+ await this.request("PATCH", `/api/automations/${encodeURIComponent(automationId)}?${params}`, body);
15470
+ }
15471
+ /** Record one knowledge resource's move into the org's knowledge base (#5142 step 4). */
15472
+ async recordKnowledgeMove(body) {
15473
+ return this.request("POST", "/api/setup/knowledge/moves", body);
15474
+ }
15289
15475
  async me() {
15290
15476
  return this.request("GET", "/api/auth/me");
15291
15477
  }
@@ -16151,11 +16337,11 @@ var UUID_RE2, WAYAI_WORKSPACE_LAYOUT;
16151
16337
  var init_dist = __esm({
16152
16338
  "../../packages/core/dist/index.js"() {
16153
16339
  "use strict";
16154
- init_chunk_IEWJ5MZH();
16340
+ init_chunk_ISFDR7SP();
16155
16341
  init_chunk_SOJMWM63();
16156
16342
  init_chunk_WEXVYJ5H();
16157
16343
  init_chunk_LZDVDC2S();
16158
- init_chunk_F3JHI4Y4();
16344
+ init_chunk_5XEJSR22();
16159
16345
  init_chunk_IBL7FANV();
16160
16346
  init_chunk_PFDZEKDE();
16161
16347
  init_chunk_P3AWZG3N();
@@ -21694,6 +21880,7 @@ var init_types2 = __esm({
21694
21880
  ENTITY_KINDS = [
21695
21881
  "record_types",
21696
21882
  "relationship_types",
21883
+ "file_types",
21697
21884
  "inbound_webhooks",
21698
21885
  "triggers",
21699
21886
  "toolsets",
@@ -21703,6 +21890,7 @@ var init_types2 = __esm({
21703
21890
  ENTITY_DIRS = {
21704
21891
  record_types: "record-types",
21705
21892
  relationship_types: "relationship-types",
21893
+ file_types: "file-types",
21706
21894
  inbound_webhooks: "inbound-webhooks",
21707
21895
  triggers: "triggers",
21708
21896
  toolsets: "toolsets",
@@ -21839,6 +22027,7 @@ var init_config_writer = __esm({
21839
22027
  "target_record_types",
21840
22028
  "cascade"
21841
22029
  ],
22030
+ file_types: ["id", "name", "description", "icon", "color", "max_size", "allowed_content_types", "metadata_schema"],
21842
22031
  inbound_webhooks: [
21843
22032
  "id",
21844
22033
  "name",
@@ -21992,9 +22181,12 @@ function parseBaseFolder(folder) {
21992
22181
  );
21993
22182
  }
21994
22183
  const read = (kind) => readEntityDir(folder, path22.join(folder, ENTITY_DIRS[kind]));
22184
+ const fileTypesDir = path22.join(folder, ENTITY_DIRS.file_types);
22185
+ const fileTypes = readEntityDir(folder, fileTypesDir);
21995
22186
  const config = {
21996
22187
  record_types: read("record_types"),
21997
22188
  relationship_types: read("relationship_types"),
22189
+ ...isDirectory(fileTypesDir) ? { file_types: fileTypes } : {},
21998
22190
  inbound_webhooks: read("inbound_webhooks"),
21999
22191
  triggers: read("triggers"),
22000
22192
  toolsets: read("toolsets"),
@@ -22024,10 +22216,15 @@ function isEmptyBucket(b) {
22024
22216
  function isDiffEmpty(diff) {
22025
22217
  return ENTITY_KINDS.every((kind) => isEmptyBucket(bucket(diff, kind)));
22026
22218
  }
22219
+ function isPrunable(id) {
22220
+ return isPathSafeId(id);
22221
+ }
22027
22222
  function collectRemovals(diff) {
22028
22223
  const out = [];
22029
22224
  for (const kind of ENTITY_KINDS) {
22030
- for (const entity of bucket(diff, kind)?.removed ?? []) out.push({ kind, id: entity.id });
22225
+ for (const entity of bucket(diff, kind)?.removed ?? []) {
22226
+ if (isPrunable(entity.id)) out.push({ kind, id: entity.id });
22227
+ }
22031
22228
  }
22032
22229
  return out;
22033
22230
  }
@@ -22040,7 +22237,7 @@ function countChanges(diff) {
22040
22237
  if (!b) continue;
22041
22238
  added += b.added.length;
22042
22239
  modified += b.modified.length;
22043
- removed += b.removed.length;
22240
+ removed += b.removed.filter((e) => isPrunable(e.id)).length;
22044
22241
  }
22045
22242
  return { added, modified, removed };
22046
22243
  }
@@ -22052,7 +22249,11 @@ function renderDiff(diff) {
22052
22249
  lines.push(`${BOLD}${LABELS[kind]}s${RESET}`);
22053
22250
  for (const e of b.added) lines.push(`${GREEN} + ${sanitizeTerminalText(e.id)}${RESET}`);
22054
22251
  for (const m of b.modified) lines.push(`${YELLOW} ~ ${sanitizeTerminalText(m.after.id)}${RESET}`);
22055
- for (const e of b.removed) lines.push(`${RED} - ${sanitizeTerminalText(e.id)}${RESET}`);
22252
+ for (const e of b.removed) {
22253
+ lines.push(
22254
+ isPrunable(e.id) ? `${RED} - ${sanitizeTerminalText(e.id)}${RESET}` : `${YELLOW} = ${sanitizeTerminalText(e.id)} (kept: pull cannot write its id to a file)${RESET}`
22255
+ );
22256
+ }
22056
22257
  }
22057
22258
  const { added, modified, removed } = countChanges(diff);
22058
22259
  lines.push("");
@@ -22061,7 +22262,7 @@ function renderDiff(diff) {
22061
22262
  );
22062
22263
  if (removed > 0) {
22063
22264
  lines.push(
22064
- `${RED} ${removed} entit${removed === 1 ? "y" : "ies"} exist on the base but not in your files \u2014 re-run with --prune to delete (record types cascade to their records).${RESET}`
22265
+ `${RED} ${removed} entit${removed === 1 ? "y" : "ies"} exist on the base but not in your files \u2014 re-run with --prune to delete (record types cascade to their records, file types to their files).${RESET}`
22065
22266
  );
22066
22267
  }
22067
22268
  return lines.join("\n");
@@ -22071,10 +22272,12 @@ var init_diff_format = __esm({
22071
22272
  "src/data/config-as-code/diff-format.ts"() {
22072
22273
  "use strict";
22073
22274
  init_terminal_output();
22275
+ init_base_id();
22074
22276
  init_types2();
22075
22277
  LABELS = {
22076
22278
  record_types: "record type",
22077
22279
  relationship_types: "relationship type",
22280
+ file_types: "file type",
22078
22281
  inbound_webhooks: "inbound webhook",
22079
22282
  triggers: "trigger",
22080
22283
  toolsets: "toolset",
@@ -22296,7 +22499,7 @@ ${RED}${removals.length} ${word} not in your files were left on the base. Re-run
22296
22499
  );
22297
22500
  } else if (autoConfirm || await confirm(
22298
22501
  `
22299
- Delete ${removals.length} server ${word} not in your files? Record types cascade to their records.`
22502
+ Delete ${removals.length} server ${word} not in your files? Record types cascade to their records, file types to their files.`
22300
22503
  )) {
22301
22504
  for (const removal of removals) {
22302
22505
  await deleteEntity(client, baseId2, removal.kind, removal.id);
@@ -33543,6 +33746,12 @@ var init_resource_paths = __esm({
33543
33746
  });
33544
33747
 
33545
33748
  // src/knowledge/source.ts
33749
+ function orgSourceKey(orgResourceId) {
33750
+ return `organization resource ${orgResourceId}`;
33751
+ }
33752
+ function hubSourceKey(productionHubId, resourceId) {
33753
+ return `hub ${productionHubId} resource ${resourceId}`;
33754
+ }
33546
33755
  function movesFromOrg(resource) {
33547
33756
  return resource.resource_type === "knowledge";
33548
33757
  }
@@ -33592,7 +33801,7 @@ function orgSources(resources, rows) {
33592
33801
  skill_name: r.skill_name,
33593
33802
  resource_type: r.resource_type,
33594
33803
  created_at: r.created_at,
33595
- sourceKey: `organization resource ${r.org_resource_id}`,
33804
+ sourceKey: orgSourceKey(r.org_resource_id),
33596
33805
  description: r.resource_description ?? "",
33597
33806
  folderSchema: r.folder_metadata_schema,
33598
33807
  fileSchema: r.file_metadata_schema,
@@ -33641,7 +33850,7 @@ function hubSources(hubId, resources, rows) {
33641
33850
  skill_name: r.skill_name,
33642
33851
  resource_type: r.resource_type,
33643
33852
  created_at: r.created_at,
33644
- sourceKey: `hub ${hubId} resource ${r.resource_id}`,
33853
+ sourceKey: hubSourceKey(hubId, r.resource_id),
33645
33854
  description: r.resource_description ?? "",
33646
33855
  folderSchema: r.folder_metadata_schema,
33647
33856
  fileSchema: r.file_metadata_schema,
@@ -33712,7 +33921,10 @@ function orgFileTypeId(resource) {
33712
33921
  return resource.slug;
33713
33922
  }
33714
33923
  function hubFileTypeId(hubSlug, resource) {
33715
- return `${hubSlug}--${resource.slug}`;
33924
+ return `${hubSlug}${HUB_PART_SEPARATOR}${resource.slug}`;
33925
+ }
33926
+ function hubPartOf(fileTypeId) {
33927
+ return fileTypeId.split(HUB_PART_SEPARATOR)[0];
33716
33928
  }
33717
33929
  function toolAlias(id) {
33718
33930
  const hash = createHash5("sha256").update(id).digest("hex").slice(0, 6);
@@ -34251,7 +34463,7 @@ function describeSkip(skip) {
34251
34463
  return `${skip.fileType}: folder "${skip.folder}" has a description but no path, so it gets no ${INDEX_FILE}.`;
34252
34464
  }
34253
34465
  }
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;
34466
+ 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
34467
  var init_plan = __esm({
34256
34468
  "src/knowledge/plan.ts"() {
34257
34469
  "use strict";
@@ -34266,6 +34478,7 @@ var init_plan = __esm({
34266
34478
  TITLE_SCHEMA = { type: "string" };
34267
34479
  TAGS_SCHEMA = { type: "array", items: { type: "string" } };
34268
34480
  MAX_NOTE_VALUES = 3;
34481
+ HUB_PART_SEPARATOR = "--";
34269
34482
  DEFINITION_KEYS = ["$defs", "definitions"];
34270
34483
  DRAFT = "2020-12";
34271
34484
  MAX_PATTERNED_VALUE_LENGTH = 1024;
@@ -34373,17 +34586,17 @@ function sameFileType(remote, spec) {
34373
34586
  function isObject(value) {
34374
34587
  return typeof value === "object" && value !== null && !Array.isArray(value);
34375
34588
  }
34376
- function promotionProblems(result, ids) {
34589
+ function promotionProblems(result, ids, section = "file_types") {
34377
34590
  const answer = isObject(result) ? result : {};
34378
34591
  const problems2 = [];
34379
- for (const [section, bucket2] of Object.entries(isObject(answer.diff) ? answer.diff : {})) {
34592
+ for (const [kind, bucket2] of Object.entries(isObject(answer.diff) ? answer.diff : {})) {
34380
34593
  if (!isObject(bucket2)) continue;
34381
34594
  for (const change of ["added", "modified", "removed"]) {
34382
34595
  for (const entry of Array.isArray(bucket2[change]) ? bucket2[change] : []) {
34383
34596
  const row = isObject(entry) ? isObject(entry.after) ? entry.after : entry : {};
34384
34597
  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}"`);
34598
+ if (kind === section && change !== "removed" && ids.has(id)) continue;
34599
+ problems2.push(`it would also apply ${kind} ${change}: "${id}"`);
34387
34600
  }
34388
34601
  }
34389
34602
  }
@@ -34641,6 +34854,7 @@ function metadataValue(value) {
34641
34854
  function fileWriteHeaders(opts) {
34642
34855
  const headers = {};
34643
34856
  if (opts.ifMatch !== void 0) headers["If-Match"] = ifMatchValue(opts.ifMatch);
34857
+ if (opts.createOnly) headers["If-None-Match"] = "*";
34644
34858
  if (opts.label !== void 0) headers["X-Data-Label"] = labelValue(opts.label);
34645
34859
  if (opts.metadata !== void 0) headers["X-Data-Metadata"] = metadataValue(opts.metadata);
34646
34860
  return headers;
@@ -34652,6 +34866,253 @@ var init_headers = __esm({
34652
34866
  }
34653
34867
  });
34654
34868
 
34869
+ // src/data/commands/tokens.ts
34870
+ import { Command } from "commander";
34871
+ function rawTokenFrom(data) {
34872
+ const token = typeof data === "object" && data !== null && "token" in data ? data.token : void 0;
34873
+ if (typeof token !== "string" || !RAW_TOKEN_PATTERN.test(token)) {
34874
+ throw new Error("Token creation response did not contain a valid rec_ token");
34875
+ }
34876
+ return token;
34877
+ }
34878
+ function addTokenLifecycleOptions(cmd) {
34879
+ return cmd.option("--expires-at <date>", "Expiration date (ISO 8601, e.g. 2026-06-01T00:00:00Z)").option(
34880
+ "--ttl <duration>",
34881
+ "Relative expiry, e.g. 10m, 2h, 7d (use for disposable probe/test tokens)"
34882
+ ).option(
34883
+ "--description <text>",
34884
+ 'What holds this token (e.g. "behind credential X"); shown in list and revoke warnings'
34885
+ ).addHelpText("after", TOKEN_OUTPUT_HELP);
34886
+ }
34887
+ function tokenCreationOutput(cmd, opts) {
34888
+ return opts.tokenOnly ? "token" : outputFormat(cmd);
34889
+ }
34890
+ function printCreatedToken(data, output) {
34891
+ if (output !== "token") {
34892
+ printOutput(data, output);
34893
+ return;
34894
+ }
34895
+ console.log(rawTokenFrom(data));
34896
+ }
34897
+ function resolveExpiresAt(opts) {
34898
+ if (opts.ttl && opts.expiresAt) {
34899
+ throw expected("--ttl and --expires-at are mutually exclusive \u2014 pass one.");
34900
+ }
34901
+ if (opts.ttl) return new Date(Date.now() + parseDuration(opts.ttl, "--ttl")).toISOString();
34902
+ return opts.expiresAt;
34903
+ }
34904
+ function applyTokenLifecycleOpts(body, opts) {
34905
+ const expiresAt = resolveExpiresAt(opts);
34906
+ if (expiresAt) body.expires_at = expiresAt;
34907
+ if (opts.description) body.description = opts.description;
34908
+ }
34909
+ function listAllTokens(client) {
34910
+ return client.collectPages(
34911
+ (cursor) => `/v1/tokens?limit=${TOKEN_LIST_PAGE_LIMIT}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`
34912
+ );
34913
+ }
34914
+ function pruneRow(t, reason) {
34915
+ return {
34916
+ token_id: t.token_id,
34917
+ name: t.name,
34918
+ reason,
34919
+ last_used: t.last_used_at ?? "never"
34920
+ };
34921
+ }
34922
+ function buildBasesTokensCommand() {
34923
+ const tokens2 = new Command("tokens").description("Manage API tokens");
34924
+ addTokenLifecycleOptions(
34925
+ tokens2.command("create").description("Create a scoped API token").requiredOption("--name <name>", "Token name").requiredOption("--grants <json>", "Grant definitions as JSON array").option(
34926
+ "--token-only",
34927
+ "Print only the raw rec_ token for secure command substitution (shown once)"
34928
+ )
34929
+ ).action(async function(opts) {
34930
+ const grants = parseData(opts.grants, "--grants");
34931
+ const body = { name: opts.name, grants };
34932
+ applyTokenLifecycleOpts(body, opts);
34933
+ const client = await createDataClient();
34934
+ const data = await client.request("POST", "/v1/tokens", body);
34935
+ printCreatedToken(data, tokenCreationOutput(this, opts));
34936
+ });
34937
+ addTokenLifecycleOptions(
34938
+ // The only token verb that addresses a base, so it alone declares `--base`.
34939
+ // Putting it on the `tokens` group would advertise a flag that does nothing
34940
+ // on `list`, `revoke` and `prune` — the defect `withBaseOption` exists to
34941
+ // avoid.
34942
+ withBaseOption(tokens2.command("create-for-toolset <slug>")).description(
34943
+ "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."
34944
+ ).option("--name <name>", 'Token name (default: "<slug> consumer")').option(
34945
+ "--token-only",
34946
+ "Print only the raw rec_ token for secure command substitution (shown once)"
34947
+ )
34948
+ ).action(async function(slug, opts) {
34949
+ const base = requireBase(this);
34950
+ const body = {
34951
+ name: opts.name ?? `${slug} consumer`,
34952
+ toolset_binding: { base_id: base, toolset_id: slug }
34953
+ };
34954
+ applyTokenLifecycleOpts(body, opts);
34955
+ const client = await createDataClient();
34956
+ const data = await client.request("POST", "/v1/tokens", body);
34957
+ const output = tokenCreationOutput(this, opts);
34958
+ printCreatedToken(data, output);
34959
+ if (output === "table") {
34960
+ console.log(`
34961
+ MCP URL: ${toolsetMcpUrl(slug)}`);
34962
+ }
34963
+ });
34964
+ tokens2.command("list").description("List API tokens (status, last_used_at, expires_at, description)").action(async function() {
34965
+ const client = await createDataClient();
34966
+ printOutput(await listAllTokens(client), outputFormat(this));
34967
+ });
34968
+ tokens2.command("revoke <token_id>").description(
34969
+ "Revoke an API token (permanent). A recently-used or toolset-bound token requires --force."
34970
+ ).option("--force", "Revoke even if the token appears to be in live use").action(async function(tokenId, opts) {
34971
+ const client = await createDataClient();
34972
+ const id = pathSegment(tokenId, "token id");
34973
+ const path42 = (force) => `/v1/tokens/${id}${force ? "?force=true" : ""}`;
34974
+ const format = outputFormat(this);
34975
+ const revoked = (forced) => printOutput({ token_id: tokenId, revoked: true, forced }, format);
34976
+ if (opts.force) {
34977
+ await client.request("DELETE", path42(true));
34978
+ revoked(true);
34979
+ return;
34980
+ }
34981
+ try {
34982
+ await client.request("DELETE", path42(false));
34983
+ revoked(false);
34984
+ } catch (err) {
34985
+ if (!(err instanceof ApiError) || err.status !== 409 || dataErrorDetails(err)?.requires_force !== true) {
34986
+ throw err;
34987
+ }
34988
+ console.error(err.message);
34989
+ if (!isInteractive()) {
34990
+ console.error("\nRe-run with --force to revoke anyway.");
34991
+ return process.exit(1);
34992
+ }
34993
+ if (!await confirm("Revoke anyway?")) {
34994
+ console.error("Aborted.");
34995
+ return process.exit(1);
34996
+ }
34997
+ await client.request("DELETE", path42(true));
34998
+ revoked(true);
34999
+ }
35000
+ });
35001
+ tokens2.command("prune").description(
35002
+ "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."
35003
+ ).option(
35004
+ "--unused-since <duration>",
35005
+ "Also prune tokens with no activity for this long (e.g. 30d). Skips toolset-bound and legacy usage-untracked tokens."
35006
+ ).option("--include-bound", "Allow pruning stale toolset-bound (consumer) tokens").option("-y, --yes", "Revoke without prompting (required when non-interactive)").action(async function(opts) {
35007
+ const staleMs = opts.unusedSince ? parseDuration(opts.unusedSince, "--unused-since") : void 0;
35008
+ const client = await createDataClient();
35009
+ const allTokens = await listAllTokens(client);
35010
+ const now = Date.now();
35011
+ const targets = [];
35012
+ const skipped = [];
35013
+ for (const t of allTokens) {
35014
+ const expired = t.status ? t.status === "expired" : !!t.expires_at && Date.parse(t.expires_at) <= now;
35015
+ if (expired) {
35016
+ targets.push(pruneRow(t, "expired"));
35017
+ continue;
35018
+ }
35019
+ if (staleMs === void 0) continue;
35020
+ const lastActivity = Date.parse(t.last_used_at ?? t.created_at);
35021
+ if (!(now - lastActivity > staleMs)) continue;
35022
+ if (t.usage_tracked !== true) {
35023
+ skipped.push(
35024
+ pruneRow(
35025
+ t,
35026
+ "usage untracked (legacy token) \u2014 revoke individually if truly unused"
35027
+ )
35028
+ );
35029
+ } else if (t.toolset_binding && !opts.includeBound) {
35030
+ skipped.push(
35031
+ pruneRow(
35032
+ t,
35033
+ `toolset-bound ('${t.toolset_binding.toolset_id}') \u2014 pass --include-bound to prune`
35034
+ )
35035
+ );
35036
+ } else {
35037
+ targets.push(pruneRow(t, `unused for over ${opts.unusedSince}`));
35038
+ }
35039
+ }
35040
+ const format = outputFormat(this);
35041
+ const json = format === "json";
35042
+ const note = (line) => json ? console.error(line) : console.log(line);
35043
+ const printPreview = () => {
35044
+ if (targets.length === 0) {
35045
+ note("Nothing to prune.");
35046
+ } else {
35047
+ note(`Would revoke ${targets.length} token(s):`);
35048
+ if (!json) printOutput(targets, format);
35049
+ }
35050
+ if (skipped.length === 0) return;
35051
+ note(`
35052
+ Skipped ${skipped.length} stale token(s):`);
35053
+ if (!json) printOutput(skipped, format);
35054
+ };
35055
+ printPreview();
35056
+ if (targets.length === 0) {
35057
+ if (json) printOutput({ revoked: 0, failed: 0, targets, skipped }, format);
35058
+ return;
35059
+ }
35060
+ if (!opts.yes) {
35061
+ if (!isInteractive()) {
35062
+ console.error("\nPreview only \u2014 re-run with --yes to revoke these tokens.");
35063
+ if (json) printOutput({ preview: true, revoked: 0, failed: 0, targets, skipped }, format);
35064
+ return process.exit(1);
35065
+ }
35066
+ if (!await confirm(`
35067
+ Revoke ${targets.length} token(s)?`)) {
35068
+ console.error("Aborted.");
35069
+ return process.exit(1);
35070
+ }
35071
+ }
35072
+ let revoked = 0;
35073
+ let failed = 0;
35074
+ for (const t of targets) {
35075
+ try {
35076
+ await client.request(
35077
+ "DELETE",
35078
+ `/v1/tokens/${pathSegment(t.token_id, "token id")}?force=true`
35079
+ );
35080
+ revoked++;
35081
+ } catch (err) {
35082
+ failed++;
35083
+ console.error(
35084
+ `Failed to revoke ${t.token_id} ('${sanitizeTerminalText(t.name)}'): ${err instanceof Error ? err.message : String(err)}`
35085
+ );
35086
+ }
35087
+ }
35088
+ if (json) printOutput({ revoked, failed, targets, skipped }, format);
35089
+ else console.log(`Revoked ${revoked} token(s)${failed > 0 ? `, ${failed} failed` : ""}.`);
35090
+ if (failed > 0) return process.exit(1);
35091
+ });
35092
+ return tokens2;
35093
+ }
35094
+ var RAW_TOKEN_PATTERN, TOKEN_OUTPUT_HELP, TOKEN_LIST_PAGE_LIMIT;
35095
+ var init_tokens = __esm({
35096
+ "src/data/commands/tokens.ts"() {
35097
+ "use strict";
35098
+ init_client();
35099
+ init_output();
35100
+ init_helpers();
35101
+ init_api_client();
35102
+ init_terminal_output();
35103
+ init_utils();
35104
+ init_expected();
35105
+ RAW_TOKEN_PATTERN = /^rec_[A-Za-z0-9_-]{43}$/;
35106
+ TOKEN_OUTPUT_HELP = `
35107
+ Output:
35108
+ --json prints the unwrapped response object; the secret is at top-level .token, never .data.token.
35109
+ --token-only prints only a validated rec_ token and overrides table/JSON formatting.
35110
+ The raw token is shown once. Capture it securely and do not log it.
35111
+ `;
35112
+ TOKEN_LIST_PAGE_LIMIT = 250;
35113
+ }
35114
+ });
35115
+
34655
35116
  // src/knowledge/remote.ts
34656
35117
  function pacedRequestsPerMinute(path42, fallback) {
34657
35118
  const budget = getEndpointConfig(path42)?.userLimit;
@@ -34702,6 +35163,14 @@ function knowledgeBase(client) {
34702
35163
  const request = (method, path42, body) => pacer.run(() => client.request(method, path42, body));
34703
35164
  const baseSeg = (id) => pathSegment(id, "--base");
34704
35165
  const typeSeg = (id) => pathSegment(id, "file type");
35166
+ const toolsetSeg = (id) => pathSegment(id, "toolset slug");
35167
+ const promoteOnly = (production, preview, section, ids, dryRun) => request("POST", `/v1/${baseSeg(production)}/promote`, {
35168
+ source_base_id: preview,
35169
+ dry_run: dryRun,
35170
+ ...Object.fromEntries(ENTITY_KINDS.map((kind) => [kind, []])),
35171
+ file_types: [],
35172
+ [section]: ids
35173
+ });
34705
35174
  return {
34706
35175
  getBase: (id) => unlessMissing(() => pacer.run(() => getBaseRecord(client, id))),
34707
35176
  listPreviews: (id) => pacer.run(
@@ -34713,14 +35182,7 @@ function knowledgeBase(client) {
34713
35182
  const { id, ...body } = spec;
34714
35183
  await request("PUT", `/v1/${baseSeg(base)}/file-types/${typeSeg(id)}`, body);
34715
35184
  },
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
- },
35185
+ promoteFileTypes: (production, preview, ids, dryRun) => promoteOnly(production, preview, "file_types", ids, dryRun),
34724
35186
  async listFiles(base, fileType) {
34725
35187
  const rows = [];
34726
35188
  for (let page = 0; page < MAX_LIST_PAGES; page++) {
@@ -34731,6 +35193,13 @@ function knowledgeBase(client) {
34731
35193
  }
34732
35194
  throw expected(`The listing of file type "${fileType}" on base "${base}" did not end after ${MAX_LIST_PAGES} pages.`);
34733
35195
  },
35196
+ getToolset: (base, slug) => unlessMissing(() => request("GET", `/v1/${baseSeg(base)}/toolsets/${toolsetSeg(slug)}`)),
35197
+ async putToolset(base, spec) {
35198
+ const { id, ...body } = spec;
35199
+ await request("PUT", `/v1/${baseSeg(base)}/toolsets/${toolsetSeg(id)}`, { ...body, actions: [] });
35200
+ },
35201
+ promoteToolsets: (production, preview, slugs, dryRun) => promoteOnly(production, preview, "toolsets", slugs, dryRun),
35202
+ mintToolsetToken: async (base, slug, name) => rawTokenFrom(await request("POST", "/v1/tokens", { name, toolset_binding: { base_id: base, toolset_id: slug } })),
34734
35203
  // `requestRaw`: the page is read whole, `{ data: … }`, as WayAI's copy of its answer declares it
34735
35204
  // (`@wayai/core/contracts` `rekorFileSnapshotAnswerSchema`).
34736
35205
  snapshotPage: (preview, cursor) => pacer.run(() => client.requestRaw("POST", `/v1/${baseSeg(preview)}/files/snapshot`, cursor === void 0 ? {} : { cursor })),
@@ -34746,7 +35215,70 @@ function knowledgeBase(client) {
34746
35215
  }
34747
35216
  };
34748
35217
  }
34749
- var LIST_PAGE, MAX_LIST_PAGES, MIGRATION_LABEL, READER_BUDGETS;
35218
+ function connectionsOf(rows) {
35219
+ return (rows ?? []).flatMap(
35220
+ (c) => c.connection_id ? [{ connection_id: c.connection_id, connection_display_name: c.connection_display_name, connector_id: c.connector_id, settings: c.settings }] : []
35221
+ );
35222
+ }
35223
+ function hubSetup(api, orgId, pacers = {
35224
+ setup: pacerFor(CUT_OVER_BUDGETS.setup, "setup"),
35225
+ automations: pacerFor(CUT_OVER_BUDGETS.automations, "automations")
35226
+ }) {
35227
+ const run = (send) => pacers.setup.run(send);
35228
+ const auto = (send) => pacers.automations.run(send);
35229
+ return {
35230
+ listHubs: async () => hubsOf(await run(() => api.getUserSchema()), orgId),
35231
+ hubDiff: async (hubId) => (await run(() => api.getHubDiff(hubId))).data,
35232
+ // The rows are the hub's `resource` rows whole, the move's mark (`moved_file_type`) among them.
35233
+ listResources: async (hubId) => (await run(() => api.listHubResources(hubId))).data ?? [],
35234
+ listAgents: async (hubId) => (await run(() => api.listAgents(hubId))).data ?? [],
35235
+ listAgentLinks: async (hubId, agentId) => (await run(() => api.listAgentResources(hubId, agentId))).resources ?? [],
35236
+ listAgentTools: async (hubId, agentId) => {
35237
+ const tools = await run(() => api.listAgentTools(hubId, agentId));
35238
+ return { native: tools.tools ?? [], connection: (tools.mcp_tools ?? []).map((t) => ({ ...t, tool_id: t.tool_id ?? null })) };
35239
+ },
35240
+ listConnections: async (hubId) => connectionsOf((await run(() => api.listConnections(hubId))).connections),
35241
+ listAutomations: async (hubId) => (await auto(() => api.listAutomations(hubId))).automations ?? [],
35242
+ // The answer lists the hub's connections, the new one among them.
35243
+ addToolsetConnection: async (hubId, input) => connectionsOf(
35244
+ (await run(
35245
+ () => api.addConnection({
35246
+ hub_id: hubId,
35247
+ connector_id: input.connectorId,
35248
+ connection_display_name: input.name,
35249
+ settings: { [TOOLSET_SLUG_SETTING]: input.toolset },
35250
+ access_token: input.token
35251
+ })
35252
+ )).connections
35253
+ ),
35254
+ async syncConnection(hubId, connectionId) {
35255
+ const answer = await run(() => api.syncMcp(hubId, connectionId));
35256
+ if (!answer.success) {
35257
+ throw expected(`Syncing connection ${connectionId}'s tools failed: ${sanitizeTerminalText(answer.message ?? "no reason given")}`);
35258
+ }
35259
+ },
35260
+ async addNativeTool(hubId, agentId, toolNativeId) {
35261
+ await run(() => api.addNativeTool(hubId, agentId, toolNativeId));
35262
+ },
35263
+ async addConnectionTool(hubId, agentId, mcpToolId) {
35264
+ await run(() => api.addMcpTool(hubId, agentId, mcpToolId));
35265
+ },
35266
+ async enableTool(hubId, agentId, toolId) {
35267
+ await run(() => api.enableTool(hubId, agentId, toolId));
35268
+ },
35269
+ async updateAgentText(hubId, agentId, patch) {
35270
+ await run(() => api.updateAgent(hubId, agentId, patch));
35271
+ },
35272
+ async updateAutomationAction(hubId, automationId, action) {
35273
+ await auto(() => api.updateAutomation(hubId, automationId, { action }));
35274
+ },
35275
+ async disableLink(hubId, agentId, resourceId) {
35276
+ await run(() => api.updateAgentResource({ hub_id: hubId, agent_id: agentId, resource_id: resourceId, enabled: false }));
35277
+ },
35278
+ recordMove: async (body) => (await run(() => api.recordKnowledgeMove(body))).data.recorded
35279
+ };
35280
+ }
35281
+ var LIST_PAGE, MAX_LIST_PAGES, MIGRATION_LABEL, READER_BUDGETS, CUT_OVER_BUDGETS;
34750
35282
  var init_remote = __esm({
34751
35283
  "src/knowledge/remote.ts"() {
34752
35284
  "use strict";
@@ -34759,10 +35291,14 @@ var init_remote = __esm({
34759
35291
  init_types2();
34760
35292
  init_headers();
34761
35293
  init_helpers();
35294
+ init_terminal_output();
35295
+ init_tokens();
35296
+ init_rekor_toolset();
34762
35297
  LIST_PAGE = 1e3;
34763
35298
  MAX_LIST_PAGES = 1e3;
34764
35299
  MIGRATION_LABEL = "wayai knowledge migrate";
34765
35300
  READER_BUDGETS = { setup: "/api/setup/organization-resources", sign: "/api/files/sign" };
35301
+ CUT_OVER_BUDGETS = { setup: "/api/setup/agents", automations: "/api/automations" };
34766
35302
  }
34767
35303
  });
34768
35304
 
@@ -34797,6 +35333,520 @@ var init_refresh_preview = __esm({
34797
35333
  }
34798
35334
  });
34799
35335
 
35336
+ // ../../packages/core/dist/catalog/index.js
35337
+ function getConnectors(filter) {
35338
+ if (!filter?.connector_type && !filter?.service_name) return CONNECTORS;
35339
+ return CONNECTORS.filter(
35340
+ (c) => (!filter.connector_type || c.connector_type === filter.connector_type) && (!filter.service_name || c.service_name === filter.service_name)
35341
+ );
35342
+ }
35343
+ function getConnectorById(id) {
35344
+ return connectorById.get(id) ?? null;
35345
+ }
35346
+ function isRekorToolsetConnector(connector) {
35347
+ return connector?.service_name === REKOR_TOOLSET_SERVICE_NAME;
35348
+ }
35349
+ function isRekorToolsetConnectorId(connectorId) {
35350
+ return typeof connectorId === "string" && isRekorToolsetConnector(connectorById.get(connectorId));
35351
+ }
35352
+ var COUNTER_KEYS, STT_PRICING, CONTAINER_PRICING, connectorById, nativeToolById;
35353
+ var init_catalog = __esm({
35354
+ "../../packages/core/dist/catalog/index.js"() {
35355
+ "use strict";
35356
+ init_chunk_GMWMQ4YA();
35357
+ init_chunk_LQSENJ26();
35358
+ init_chunk_UZ7SPBM3();
35359
+ init_chunk_MIWZOSPI();
35360
+ init_chunk_DHPUPI4Y();
35361
+ COUNTER_KEYS = Object.keys({
35362
+ input_tokens: true,
35363
+ output_tokens: true,
35364
+ cache_read_input_tokens: true,
35365
+ cache_creation_input_tokens: true,
35366
+ cache_creation_1h_input_tokens: true
35367
+ });
35368
+ STT_PRICING = {
35369
+ "Openai/whisper-1": { per_second: 6e-3 / 60 },
35370
+ "Openai/gpt-4o-mini-transcribe": { per_second: 3e-3 / 60 },
35371
+ "Groq/whisper-large-v3-turbo": { per_second: 0.04 / 3600 },
35372
+ "ElevenLabs/scribe_v2": { per_second: 0.22 / 3600 }
35373
+ };
35374
+ CONTAINER_PRICING = {
35375
+ anthropic_per_request_usd: 0.05 * 10 / 3600,
35376
+ // $0.05/hr × ~10s/request ≈ $0.000139
35377
+ openai_per_session_usd: 0.03
35378
+ // 1GB tier, 20-min session
35379
+ };
35380
+ connectorById = new Map(CONNECTORS.map((c) => [c.connector_id, c]));
35381
+ nativeToolById = new Map(NATIVE_TOOLS.map((t) => [t.tool_native_id, t]));
35382
+ }
35383
+ });
35384
+
35385
+ // src/knowledge/cut-over.ts
35386
+ import { createHash as createHash6 } from "crypto";
35387
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
35388
+ function toolsetSlug(productionHubId, fileTypes) {
35389
+ const hash = createHash6("sha256").update([productionHubId, ...fileTypes].join("\n")).digest("hex").slice(0, 12);
35390
+ return `${CUT_OVER_TOOLSET_PREFIX}${hash}`;
35391
+ }
35392
+ function toolsetEntry(fileType) {
35393
+ const base = toolBase(fileType);
35394
+ return { file_type: fileType, operations: [...FILE_OPERATIONS], ...base === fileType ? {} : { name: base } };
35395
+ }
35396
+ function toolsetToolName(op, fileType) {
35397
+ return toolsetFileToolName(toolsetEntry(fileType), op);
35398
+ }
35399
+ function rewriteCitations(text, fileTypeBySlug) {
35400
+ return text.replace(CITATION_RE, (whole, slash, slug) => {
35401
+ const fileType = fileTypeBySlug.get(slug.toLowerCase());
35402
+ return fileType ? `${slash}${DATA_NAMESPACE}/${fileType}` : whole;
35403
+ });
35404
+ }
35405
+ function knowledgePlaceholders(template, losing) {
35406
+ if (losing.length === 0 || !template.includes("{{")) return [];
35407
+ const scan = scanTemplateResources(template);
35408
+ const kinds = scanTemplateResources(template.replace(/\{\{(\s*)files(\s*\()/g, "{{$1resources$2")).legacy_kinds;
35409
+ const uses = [];
35410
+ if (kinds === "all" || kinds === "kb") uses.push("a {{files()}} catalog that lists knowledge");
35411
+ const names = new Set(losing.map((r) => r.name));
35412
+ for (const name of scan.resource_content_names) if (names.has(name)) uses.push(`{{resource_content(${name})}}`);
35413
+ const slugs = new Set(losing.map((r) => r.slug));
35414
+ for (const path42 of scan.file_content_paths) {
35415
+ const [namespace, slug] = cleanRelativePath(path42).split("/");
35416
+ if (namespace?.toLowerCase() === RESOURCE_NAMESPACE && slug && slugs.has(slug.toLowerCase())) {
35417
+ uses.push(`{{file_content(${path42})}}`);
35418
+ }
35419
+ }
35420
+ return uses;
35421
+ }
35422
+ function sameToolset(remote, spec) {
35423
+ if (!remote) return false;
35424
+ 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));
35425
+ 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));
35426
+ }
35427
+ function toolsetConnectorId() {
35428
+ const connector = getConnectors().find((c) => isRekorToolsetConnector(c));
35429
+ if (!connector) throw new Error("The Base Toolset connector is missing from the catalog");
35430
+ return connector.connector_id;
35431
+ }
35432
+ function cutOverToolsetOf(connection) {
35433
+ const toolset = connection.settings?.[TOOLSET_SLUG_SETTING];
35434
+ return isRekorToolsetConnectorId(connection.connector_id) && typeof toolset === "string" && CUT_OVER_TOOLSET_RE.test(toolset) ? toolset : null;
35435
+ }
35436
+ function wantedConnectionTools(fileTypes) {
35437
+ return fileTypes.flatMap((ft) => FILE_OPERATIONS.map((op) => toolsetToolName(op, ft)));
35438
+ }
35439
+ function toolWork(tools, wanted, connectionId) {
35440
+ const native = [];
35441
+ const unavailable = [];
35442
+ for (const name of MOUNT_TOOLS) {
35443
+ const row = tools.native.find((t) => t.tool_name === name);
35444
+ if (!row) unavailable.push(name);
35445
+ else if (row.available_action !== "Disable") native.push(row);
35446
+ }
35447
+ const connection = [];
35448
+ const uncatalogued = [];
35449
+ for (const name of wanted) {
35450
+ const row = connectionId ? tools.connection.find((t) => t.connection_id === connectionId && t.tool_name === name) : void 0;
35451
+ if (!row) uncatalogued.push(name);
35452
+ else if (row.available_action !== "Disable") connection.push(row);
35453
+ }
35454
+ return { native, connection, uncatalogued, unavailable };
35455
+ }
35456
+ function describeToolWork(work) {
35457
+ return [
35458
+ ...work.native.map((t) => `${t.available_action === "Enable" ? "enable" : "add"} ${t.tool_name}`),
35459
+ ...work.connection.map((t) => `${t.available_action === "Enable" ? "enable" : "add"} ${t.tool_name}`),
35460
+ ...work.uncatalogued.map((name) => `add ${name}`)
35461
+ ];
35462
+ }
35463
+ async function cutOverHub(hub, kb, opts, io) {
35464
+ const { production, preview } = await resolveBases(kb, opts.base, opts.orgId);
35465
+ const stops = [];
35466
+ const hubs = await hub.listHubs();
35467
+ const previewHub = hubs.find((h) => h.hub_id === opts.hubId);
35468
+ if (!previewHub) throw expected(`Hub ${safe2(opts.hubId)} not found in this organization, or you cannot read it.`);
35469
+ if (previewHub.hub_environment !== "preview") {
35470
+ throw expected(`Hub ${safe2(opts.hubId)} is a production hub. A hub is cut over on its preview; publishing carries it to production.`);
35471
+ }
35472
+ const productionHub2 = previewHub.production_hub_id ? hubs.find((h) => h.hub_id === previewHub.production_hub_id) : void 0;
35473
+ if (!productionHub2) {
35474
+ 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.`);
35475
+ }
35476
+ const productionHubId = productionHub2.hub_id;
35477
+ const fileTypes = await kb.listFileTypes(production);
35478
+ const fileTypeBySource = /* @__PURE__ */ new Map();
35479
+ for (const ft of fileTypes) {
35480
+ const source = provenanceOf(ft.metadata_schema);
35481
+ if (source) fileTypeBySource.set(source, ft.id);
35482
+ }
35483
+ const resources = await hub.listResources(opts.hubId);
35484
+ const ownKnowledge = (rows) => rows.filter((r) => r.resource_type === "knowledge" && !r.org_resource_id);
35485
+ const productionOwn = ownKnowledge(await hub.listResources(productionHubId));
35486
+ const previewOwn = ownKnowledge(resources);
35487
+ const diff = await hub.hubDiff(opts.hubId);
35488
+ const marked = new Set(previewOwn.filter((r) => r.moved_file_type).map((r) => r.resource_name));
35489
+ const knowledgeNames = new Set([...previewOwn, ...productionOwn].map((r) => r.resource_name).filter((name) => !marked.has(name)));
35490
+ const changed = [
35491
+ ...diff.resources.filter((e) => knowledgeNames.has(e.name)).map((e) => `resource "${e.name}" (${e.status})`),
35492
+ ...(diff.resource_folders ?? []).filter((e) => e.type && knowledgeNames.has(e.type)).map((e) => `a folder of "${e.type}"`),
35493
+ ...(diff.resource_files ?? []).filter((e) => e.type && knowledgeNames.has(e.type)).map((e) => `a file of "${e.type}"`)
35494
+ ];
35495
+ if (changed.length > 0) {
35496
+ stops.push(
35497
+ `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.`
35498
+ );
35499
+ }
35500
+ const fileTypeOf = /* @__PURE__ */ new Map();
35501
+ for (const resource of resources) {
35502
+ if (resource.resource_type !== "knowledge") continue;
35503
+ if (resource.org_resource_id) {
35504
+ const ft2 = fileTypeBySource.get(orgSourceKey(resource.org_resource_id));
35505
+ if (ft2) fileTypeOf.set(resource.resource_id, ft2);
35506
+ continue;
35507
+ }
35508
+ if (resource.moved_file_type) {
35509
+ fileTypeOf.set(resource.resource_id, resource.moved_file_type);
35510
+ continue;
35511
+ }
35512
+ const twins = productionOwn.filter((r) => r.resource_name === resource.resource_name);
35513
+ const previewTwins = previewOwn.filter((r) => r.resource_name === resource.resource_name);
35514
+ if (twins.length > 1 || previewTwins.length > 1) {
35515
+ stops.push(`Two knowledge resources are named "${resource.resource_name}": rename one (in the preview, then sync), so each pairs with its production copy.`);
35516
+ continue;
35517
+ }
35518
+ const ft = twins[0] ? fileTypeBySource.get(hubSourceKey(productionHubId, twins[0].resource_id)) : void 0;
35519
+ if (ft) fileTypeOf.set(resource.resource_id, ft);
35520
+ }
35521
+ const productionHubs = hubs.filter((h) => h.hub_environment === "production");
35522
+ for (const resource of previewOwn) {
35523
+ const ft = fileTypeOf.get(resource.resource_id);
35524
+ if (!ft || resource.moved_file_type) continue;
35525
+ const hubPart = hubPartOf(ft);
35526
+ const holders = productionHubs.filter((h) => slugifySkillName(h.hub_name) === hubPart);
35527
+ if (holders.length > 1) {
35528
+ stops.push(
35529
+ `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.`
35530
+ );
35531
+ }
35532
+ }
35533
+ const slugById = buildResourceSlugMap(resources);
35534
+ const fileTypeBySlug = /* @__PURE__ */ new Map();
35535
+ for (const [resourceId, ft] of fileTypeOf) {
35536
+ const slug = slugById.get(resourceId);
35537
+ if (slug) fileTypeBySlug.set(slug, ft);
35538
+ }
35539
+ const byId = new Map(resources.map((r) => [r.resource_id, r]));
35540
+ const connections = await hub.listConnections(opts.hubId);
35541
+ const previewToolsets = /* @__PURE__ */ new Map();
35542
+ const connectionBySlug = /* @__PURE__ */ new Map();
35543
+ for (const connection of connections) {
35544
+ const slug = cutOverToolsetOf(connection);
35545
+ if (!slug) continue;
35546
+ connectionBySlug.set(slug, connection);
35547
+ if (!previewToolsets.has(slug)) previewToolsets.set(slug, await kb.getToolset(preview, slug));
35548
+ }
35549
+ const toolsetTypes = (slug) => (previewToolsets.get(slug)?.files ?? []).map((e) => e.file_type);
35550
+ const slugByConnection = new Map([...connectionBySlug].map(([slug, c]) => [c.connection_id, slug]));
35551
+ const agents = await hub.listAgents(opts.hubId);
35552
+ const plans = [];
35553
+ for (const agent of agents) {
35554
+ const links2 = (await hub.listAgentLinks(opts.hubId, agent.agent_id)).filter((l) => l.resource_type === "knowledge");
35555
+ const enabled = links2.filter((l) => l.enabled);
35556
+ const tools = await hub.listAgentTools(opts.hubId, agent.agent_id);
35557
+ const set = /* @__PURE__ */ new Set();
35558
+ for (const link of enabled) {
35559
+ const ft = fileTypeOf.get(link.resource_id);
35560
+ const resource = byId.get(link.resource_id);
35561
+ if (!ft) {
35562
+ stops.push(
35563
+ `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}`}\`).`
35564
+ );
35565
+ continue;
35566
+ }
35567
+ set.add(ft);
35568
+ }
35569
+ for (const tool of tools.connection) {
35570
+ const slug = tool.connection_id ? slugByConnection.get(tool.connection_id) : void 0;
35571
+ if (slug && tool.available_action === "Disable") for (const ft of toolsetTypes(slug)) set.add(ft);
35572
+ }
35573
+ const runner = connections.find((c) => c.connection_id === agent.connection_id);
35574
+ if (enabled.length > 0 && runner && getConnectorById(runner.connector_id)?.is_harness === true) {
35575
+ stops.push(
35576
+ `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.`
35577
+ );
35578
+ }
35579
+ const losing = enabled.flatMap((link) => {
35580
+ const resource = byId.get(link.resource_id);
35581
+ const slug = slugById.get(link.resource_id);
35582
+ return resource && slug ? [{ slug, name: resourceDisplayName(resource) }] : [];
35583
+ });
35584
+ for (const [field, template] of [
35585
+ ["instructions", agent.instructions],
35586
+ ["additional context", agent.additional_context_template]
35587
+ ]) {
35588
+ for (const use of knowledgePlaceholders(template ?? "", losing)) {
35589
+ stops.push(
35590
+ `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.`
35591
+ );
35592
+ }
35593
+ }
35594
+ if (set.size > 0 || enabled.length > 0) plans.push({ agent, fileTypes: [...set].sort(), linksToDisable: enabled, tools });
35595
+ }
35596
+ const groups = /* @__PURE__ */ new Map();
35597
+ for (const plan of plans) {
35598
+ if (plan.fileTypes.length === 0) continue;
35599
+ const slug = toolsetSlug(productionHubId, plan.fileTypes);
35600
+ const group = groups.get(slug);
35601
+ if (group) {
35602
+ group.agents.push(plan.agent);
35603
+ plan.group = group;
35604
+ continue;
35605
+ }
35606
+ plan.group = {
35607
+ slug,
35608
+ fileTypes: plan.fileTypes,
35609
+ agents: [plan.agent],
35610
+ connection: connectionBySlug.get(slug),
35611
+ created: !connectionBySlug.has(slug),
35612
+ spec: {
35613
+ id: slug,
35614
+ name: `Knowledge of ${previewHub.hub_name}`.slice(0, 200),
35615
+ description: `Read-only knowledge for agents of hub ${productionHubId}, written by wayai knowledge cut-over: list and get on ${plan.fileTypes.join(", ")}.`,
35616
+ files: plan.fileTypes.map(toolsetEntry)
35617
+ }
35618
+ };
35619
+ groups.set(slug, plan.group);
35620
+ }
35621
+ const plannedTypes = [...new Set([...groups.values()].flatMap((g) => g.fileTypes))].sort();
35622
+ const missing = [];
35623
+ for (const ft of plannedTypes) {
35624
+ const [inProduction, inPreview] = await Promise.all([kb.listFiles(production, ft), filesOrNone(kb, preview, ft)]);
35625
+ const there = new Set(inPreview.map((f) => f.path));
35626
+ for (const file of inProduction) if (!there.has(file.path)) missing.push(`${ft}/${file.path}`);
35627
+ }
35628
+ if (missing.length > 0) {
35629
+ stops.push(
35630
+ `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.`
35631
+ );
35632
+ }
35633
+ const toolsetsToWrite = [];
35634
+ const toolsetsToPromote = [];
35635
+ for (const group of groups.values()) {
35636
+ const [inPreview, inProduction] = await Promise.all([
35637
+ previewToolsets.has(group.slug) ? previewToolsets.get(group.slug) : kb.getToolset(preview, group.slug),
35638
+ kb.getToolset(production, group.slug)
35639
+ ]);
35640
+ if (!sameToolset(inPreview, group.spec)) toolsetsToWrite.push(group.spec);
35641
+ if (!sameToolset(inProduction, group.spec)) toolsetsToPromote.push(group.spec);
35642
+ }
35643
+ const connectionsToCreate = [...groups.values()].filter((g) => g.created);
35644
+ const toolLines = [];
35645
+ let toolWrites = 0;
35646
+ for (const plan of plans) {
35647
+ const group = plan.group;
35648
+ if (!group) continue;
35649
+ const work = toolWork(plan.tools, wantedConnectionTools(group.fileTypes), group.connection?.connection_id);
35650
+ 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.`);
35651
+ const lines = describeToolWork(work);
35652
+ toolWrites += lines.length;
35653
+ if (lines.length > 0) toolLines.push(` ${safe2(plan.agent.agent_name)}: ${lines.map(safe2).join(", ")} (${group.slug})`);
35654
+ }
35655
+ const agentRewrites = [];
35656
+ for (const agent of agents) {
35657
+ const patch = {};
35658
+ const instructions = rewriteCitations(agent.instructions ?? "", fileTypeBySlug);
35659
+ if (instructions !== (agent.instructions ?? "")) patch.instructions = instructions;
35660
+ const context = rewriteCitations(agent.additional_context_template ?? "", fileTypeBySlug);
35661
+ if (context !== (agent.additional_context_template ?? "")) patch.additional_context_template = context;
35662
+ if (Object.keys(patch).length > 0) agentRewrites.push({ agent, patch });
35663
+ }
35664
+ const automationRewrites = [];
35665
+ for (const automation of await hub.listAutomations(opts.hubId)) {
35666
+ const text = automation.action.type === "run_agent" ? automation.action.instructions : null;
35667
+ if (!text) continue;
35668
+ const rewritten = rewriteCitations(text, fileTypeBySlug);
35669
+ if (rewritten !== text) automationRewrites.push({ automation, action: { ...automation.action, instructions: rewritten } });
35670
+ }
35671
+ const moves = [];
35672
+ for (const resource of previewOwn) {
35673
+ const ft = fileTypeOf.get(resource.resource_id);
35674
+ if (!ft || resource.moved_file_type) continue;
35675
+ moves.push({
35676
+ body: { scope: "hub", hub_id: opts.hubId, resource_id: resource.resource_id, file_type: ft, base_id: production },
35677
+ label: `hub resource "${resource.resource_name}" \u2192 ${ft}`
35678
+ });
35679
+ }
35680
+ const orgRecorded = /* @__PURE__ */ new Set();
35681
+ for (const plan of plans) {
35682
+ for (const link of plan.linksToDisable) {
35683
+ const resource = byId.get(link.resource_id);
35684
+ const ft = fileTypeOf.get(link.resource_id);
35685
+ if (!resource?.org_resource_id || !ft || orgRecorded.has(resource.org_resource_id)) continue;
35686
+ orgRecorded.add(resource.org_resource_id);
35687
+ moves.push({
35688
+ body: { scope: "organization", organization_id: opts.orgId, org_resource_id: resource.org_resource_id, file_type: ft, base_id: production },
35689
+ label: `organization resource "${resource.resource_name}" \u2192 ${ft}`
35690
+ });
35691
+ }
35692
+ }
35693
+ const links = plans.flatMap((plan) => plan.linksToDisable.map((link) => ({ plan, link })));
35694
+ const result = {
35695
+ toolsets: [...groups.values()].map((g) => ({ slug: g.slug, fileTypes: g.fileTypes, agents: g.agents.map((a) => a.agent_name) })),
35696
+ toolsetsWritten: toolsetsToWrite.map((s) => s.id),
35697
+ toolsetsPromoted: toolsetsToPromote.map((s) => s.id),
35698
+ connectionsCreated: connectionsToCreate.map((g) => g.slug),
35699
+ toolsAssigned: toolLines,
35700
+ agentsRewritten: agentRewrites.map((r) => r.agent.agent_name),
35701
+ automationsRewritten: automationRewrites.map((r) => r.automation.name),
35702
+ movesRecorded: moves.map((m) => m.label),
35703
+ linksDisabled: links.map(({ plan, link }) => `${plan.agent.agent_name}: ${byId.get(link.resource_id)?.resource_name ?? link.resource_id}`),
35704
+ written: false
35705
+ };
35706
+ io.log(`Cut-over of hub "${safe2(previewHub.hub_name)}" (${opts.hubId}) to base "${safe2(production)}" (toolsets written in "${safe2(preview)}")`);
35707
+ io.log("\nToolsets (read-only: list and get):");
35708
+ if (groups.size === 0) io.log(" (none: no agent reads moved knowledge)");
35709
+ for (const group of groups.values()) {
35710
+ const mark = toolsetsToPromote.includes(group.spec) || toolsetsToWrite.includes(group.spec) ? "+" : "=";
35711
+ io.log(` ${mark} ${group.slug}: ${group.fileTypes.map(safe2).join(", ")} \u2014 ${group.agents.map((a) => safe2(a.agent_name)).join(", ")}`);
35712
+ }
35713
+ io.log("\nConnections:");
35714
+ for (const group of groups.values()) {
35715
+ io.log(` ${group.connection ? "=" : "+"} Base Toolset "${group.slug}"${group.connection ? "" : ", with a new token bound to it on the preview base"}`);
35716
+ }
35717
+ io.log("\nAgent tools:");
35718
+ if (toolLines.length === 0) io.log(" (nothing to assign)");
35719
+ for (const line of toolLines) io.log(line);
35720
+ io.log("\nPaths rewritten to data/ (K13):");
35721
+ if (agentRewrites.length + automationRewrites.length === 0) io.log(" (none)");
35722
+ for (const { agent, patch } of agentRewrites) io.log(` agent "${safe2(agent.agent_name)}": ${Object.keys(patch).join(", ")}`);
35723
+ for (const { automation } of automationRewrites) io.log(` automation "${safe2(automation.name)}": instructions`);
35724
+ io.log("\nMoves recorded:");
35725
+ if (moves.length === 0) io.log(" (none)");
35726
+ for (const move of moves) io.log(` + ${safe2(move.label)}`);
35727
+ io.log("\nKnowledge links disabled:");
35728
+ if (links.length === 0) io.log(" (none)");
35729
+ for (const line of result.linksDisabled) io.log(` - ${safe2(line)}`);
35730
+ if (stops.length > 0) {
35731
+ io.log("\nStopped:");
35732
+ for (const stop of stops) io.log(` ${safe2(stop)}`);
35733
+ throw expected(`The cut-over stops on ${stops.length} reason(s) above; nothing was written.`);
35734
+ }
35735
+ const writes = toolsetsToWrite.length + toolsetsToPromote.length + connectionsToCreate.length + toolWrites + agentRewrites.length + automationRewrites.length + moves.length + links.length;
35736
+ const printProduction = () => printProductionSteps(io, opts, production, productionHubId, [...groups.values()]);
35737
+ if (opts.dryRun) {
35738
+ io.log(`
35739
+ Dry run: nothing written (${writes === 0 ? "nothing to write" : `${writes} write(s) planned`}).`);
35740
+ printProduction();
35741
+ return result;
35742
+ }
35743
+ if (writes === 0) {
35744
+ io.log("\nNothing to write: this hub is cut over.");
35745
+ printProduction();
35746
+ return result;
35747
+ }
35748
+ if (!opts.yes && !await io.confirm(`Make these ${writes} change(s) to the preview hub and its knowledge base?`)) {
35749
+ io.log("Cancelled.");
35750
+ return result;
35751
+ }
35752
+ for (const move of moves) await hub.recordMove(move.body);
35753
+ for (const spec of toolsetsToWrite) await kb.putToolset(preview, spec);
35754
+ if (toolsetsToPromote.length > 0) {
35755
+ const slugs = toolsetsToPromote.map((s) => s.id);
35756
+ const problems2 = promotionProblems(await kb.promoteToolsets(production, preview, slugs, true), new Set(slugs), "toolsets");
35757
+ if (problems2.length > 0) {
35758
+ throw expected(
35759
+ `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.`
35760
+ );
35761
+ }
35762
+ await kb.promoteToolsets(production, preview, slugs, false);
35763
+ }
35764
+ if (connectionsToCreate.length > 0) {
35765
+ const connectorId = toolsetConnectorId();
35766
+ for (const group of connectionsToCreate) {
35767
+ const token = await kb.mintToolsetToken(preview, group.slug, `${group.slug} (hub ${opts.hubId})`);
35768
+ const after = await hub.addToolsetConnection(opts.hubId, { connectorId, name: group.slug, toolset: group.slug, token });
35769
+ group.connection = after.find((c) => cutOverToolsetOf(c) === group.slug);
35770
+ if (!group.connection) throw new Error(`The Base Toolset connection "${group.slug}" was not found after it was created.`);
35771
+ }
35772
+ }
35773
+ const synced2 = /* @__PURE__ */ new Set();
35774
+ for (const plan of plans) {
35775
+ const group = plan.group;
35776
+ if (!group?.connection) continue;
35777
+ const connectionId = group.connection.connection_id;
35778
+ const wanted = wantedConnectionTools(group.fileTypes);
35779
+ const stale = group.created || synced2.has(connectionId);
35780
+ let tools = stale ? await hub.listAgentTools(opts.hubId, plan.agent.agent_id) : plan.tools;
35781
+ let work = toolWork(tools, wanted, connectionId);
35782
+ if (work.uncatalogued.length > 0 && !synced2.has(connectionId)) {
35783
+ await hub.syncConnection(opts.hubId, connectionId);
35784
+ synced2.add(connectionId);
35785
+ tools = await hub.listAgentTools(opts.hubId, plan.agent.agent_id);
35786
+ work = toolWork(tools, wanted, connectionId);
35787
+ }
35788
+ if (work.uncatalogued.length > 0) {
35789
+ throw new Error(
35790
+ `Connection "${group.slug}" catalogs no ${safe2(work.uncatalogued.join(", "))} after a sync, so agent "${safe2(plan.agent.agent_name)}" can't be given them.`
35791
+ );
35792
+ }
35793
+ for (const tool of [...work.native, ...work.connection]) {
35794
+ if (tool.available_action === "Enable" && tool.tool_id) await hub.enableTool(opts.hubId, plan.agent.agent_id, tool.tool_id);
35795
+ else if (tool.tool_native_id) await hub.addNativeTool(opts.hubId, plan.agent.agent_id, tool.tool_native_id);
35796
+ else if (tool.mcp_tool_id) await hub.addConnectionTool(opts.hubId, plan.agent.agent_id, tool.mcp_tool_id);
35797
+ }
35798
+ }
35799
+ for (const { agent, patch } of agentRewrites) await hub.updateAgentText(opts.hubId, agent.agent_id, patch);
35800
+ for (const { automation, action } of automationRewrites) await hub.updateAutomationAction(opts.hubId, automation.automation_id, action);
35801
+ for (const { plan, link } of links) await hub.disableLink(opts.hubId, plan.agent.agent_id, link.resource_id);
35802
+ io.log(`
35803
+ Done: the preview hub's agents read their knowledge from "${safe2(preview)}" through ${groups.size} toolset(s).`);
35804
+ printProduction();
35805
+ return { ...result, written: true };
35806
+ }
35807
+ async function filesOrNone(kb, base, fileType) {
35808
+ try {
35809
+ return await kb.listFiles(base, fileType);
35810
+ } catch (err) {
35811
+ if (err instanceof ApiError && err.status === 404) return [];
35812
+ throw err;
35813
+ }
35814
+ }
35815
+ function printProductionSteps(io, opts, production, productionHubId, groups) {
35816
+ if (groups.length === 0) return;
35817
+ io.log("\nProduction (not run by this command), once the preview passes its evals:");
35818
+ io.log(` 1. Publish the preview: wayai publish --hub ${opts.hubId}`);
35819
+ io.log(" 2. For each toolset, mint a token bound to it on the production base, and save it on production's connection:");
35820
+ for (const group of groups) {
35821
+ io.log(
35822
+ ` 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`
35823
+ );
35824
+ }
35825
+ }
35826
+ var CUT_OVER_TOOLSET_PREFIX, CUT_OVER_TOOLSET_RE, FILE_OPERATIONS, MOUNT_TOOLS, DATA_NAMESPACE, safe2, CITATION_RE;
35827
+ var init_cut_over = __esm({
35828
+ "src/knowledge/cut-over.ts"() {
35829
+ "use strict";
35830
+ init_dist();
35831
+ init_catalog();
35832
+ init_rekor_toolset();
35833
+ init_resource_paths();
35834
+ init_expected();
35835
+ init_terminal_output();
35836
+ init_api_client();
35837
+ init_migrate2();
35838
+ init_plan();
35839
+ init_source();
35840
+ CUT_OVER_TOOLSET_PREFIX = "knowledge-";
35841
+ CUT_OVER_TOOLSET_RE = /^knowledge-[0-9a-f]{12}$/;
35842
+ FILE_OPERATIONS = ["list", "get"];
35843
+ MOUNT_TOOLS = ["list_files", "read_file"];
35844
+ DATA_NAMESPACE = "data";
35845
+ safe2 = (text) => sanitizeTerminalText(text);
35846
+ CITATION_RE = /(?<![\w./-])(\/?)resources\/([a-z0-9]+(?:-[a-z0-9]+)*)(?![a-z0-9_]|-[a-z0-9]|\.\w)/gi;
35847
+ }
35848
+ });
35849
+
34800
35850
  // src/commands/knowledge.ts
34801
35851
  var knowledge_exports = {};
34802
35852
  __export(knowledge_exports, {
@@ -34818,7 +35868,7 @@ function readArgs(args2) {
34818
35868
  function parseKnowledgeArgs(args2) {
34819
35869
  const { values, positionals } = readArgs(args2);
34820
35870
  const [verb, ...extra] = positionals;
34821
- if (verb !== "migrate" && verb !== "refresh-preview") {
35871
+ if (verb !== "migrate" && verb !== "refresh-preview" && verb !== "cut-over") {
34822
35872
  throw usageError3(verb === void 0 ? "No subcommand given." : `Unknown subcommand ${JSON.stringify(verb)}.`);
34823
35873
  }
34824
35874
  if (extra.length > 0) throw usageError3(`Unexpected argument ${JSON.stringify(extra[0])}.`);
@@ -34826,13 +35876,17 @@ function parseKnowledgeArgs(args2) {
34826
35876
  if (!UUID_RE3.test(values.org)) throw usageError3(`Invalid --org ${JSON.stringify(values.org)}: expected an organization id (a UUID).`);
34827
35877
  if (!values.base) throw usageError3("--base <knowledge base> is required.");
34828
35878
  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.");
35879
+ if (values.hub !== void 0) throw usageError3("--hub is migrate's and cut-over's: a refresh copies the whole knowledge base.");
35880
+ if (values["dry-run"] !== void 0) throw usageError3("--dry-run is migrate's and cut-over's: a refresh has none.");
34831
35881
  return { verb, orgId: values.org, base: values.base, yes: values.yes === true };
34832
35882
  }
34833
35883
  if (values.hub !== void 0 && !UUID_RE3.test(values.hub)) {
34834
35884
  throw usageError3(`Invalid --hub ${JSON.stringify(values.hub)}: expected a hub id (a UUID).`);
34835
35885
  }
35886
+ if (verb === "cut-over") {
35887
+ if (values.hub === void 0) throw usageError3("--hub <preview hub_id> is required: a cut-over is one preview hub's.");
35888
+ return { verb, orgId: values.org, base: values.base, hubId: values.hub, dryRun: values["dry-run"] === true, yes: values.yes === true };
35889
+ }
34836
35890
  return {
34837
35891
  verb,
34838
35892
  orgId: values.org,
@@ -34860,10 +35914,12 @@ async function knowledgeCommand(args2) {
34860
35914
  const { config, accessToken } = await requireAuth();
34861
35915
  const api = new ApiClient({ apiUrl: config.api_url, accessToken, onUnauthorized: () => getAccessToken(config) });
34862
35916
  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
- });
35917
+ const io = { log: (line) => console.log(line), confirm };
35918
+ if (options.verb === "cut-over") {
35919
+ await cutOverHub(hubSetup(api, options.orgId), knowledgeBase(data), options, io);
35920
+ return;
35921
+ }
35922
+ await migrateKnowledge(knowledgeReader(api, options.orgId), knowledgeBase(data), options, io);
34867
35923
  }
34868
35924
  var USAGE4, OPTIONS;
34869
35925
  var init_knowledge = __esm({
@@ -34878,12 +35934,14 @@ var init_knowledge = __esm({
34878
35934
  init_migrate2();
34879
35935
  init_remote();
34880
35936
  init_refresh_preview();
35937
+ init_cut_over();
34881
35938
  USAGE4 = `
34882
35939
  wayai knowledge \u2014 the organization's knowledge base
34883
35940
 
34884
35941
  Usage:
34885
35942
  wayai knowledge migrate --org <org_id> --base <knowledge base> [--hub <hub_id>] [--dry-run] [-y]
34886
35943
  wayai knowledge refresh-preview --org <org_id> --base <knowledge base> [-y]
35944
+ wayai knowledge cut-over --org <org_id> --base <knowledge base> --hub <preview hub_id> [--dry-run] [-y]
34887
35945
 
34888
35946
  migrate: move knowledge resources into the organization's knowledge base.
34889
35947
 
@@ -34905,6 +35963,21 @@ refresh-preview: make the knowledge base's preview hold its production base's fi
34905
35963
 
34906
35964
  --base The production knowledge base, or its preview.
34907
35965
  -y, --yes Skip the question before copying.
35966
+
35967
+ cut-over: switch a preview hub's agents from their knowledge resources to the knowledge base, once
35968
+ its knowledge has moved. Each distinct set of knowledge its agents read gets one read-only
35969
+ toolset on the knowledge base, reached through a Base Toolset connection; each agent is given
35970
+ list_files, read_file and its toolset's list_ and get_ tools, its resources/<slug>/ paths become
35971
+ data/<file type>/, its knowledge links are disabled, and each move is recorded. It changes the
35972
+ preview only, and prints the production steps. The preview base must hold production's files
35973
+ first (refresh-preview): the preview's agents read them there. It stops before writing on a resource placeholder
35974
+ ({{files()}}, {{resource_content()}}, {{file_content()}}) that reads knowledge an agent is about
35975
+ to lose: move that text into its instructions or a skill first.
35976
+
35977
+ --hub The preview hub.
35978
+ --base The production knowledge base, or its preview.
35979
+ --dry-run List every change, and every reason it would stop, without writing.
35980
+ -y, --yes Skip the question before writing.
34908
35981
  `;
34909
35982
  OPTIONS = {
34910
35983
  org: { type: "string" },
@@ -34960,17 +36033,17 @@ async function adminCommand(args2) {
34960
36033
  process.exit(1);
34961
36034
  }
34962
36035
  if (group === "archive") {
34963
- const { positional: positional2, flagArgs: flagArgs2 } = splitArgs(afterGroup);
36036
+ const { positional: positional2, flagArgs: flagArgs2 } = splitArgs2(afterGroup);
34964
36037
  await runArchiveRead(positional2, flagArgs2);
34965
36038
  return;
34966
36039
  }
34967
36040
  if (group === "observability") {
34968
- const { positional: positional2, flagArgs: flagArgs2 } = splitArgs(afterGroup);
36041
+ const { positional: positional2, flagArgs: flagArgs2 } = splitArgs2(afterGroup);
34969
36042
  await runObservabilityRead(positional2, flagArgs2);
34970
36043
  return;
34971
36044
  }
34972
36045
  const [sub, ...rest] = afterGroup;
34973
- const { positional, flagArgs } = splitArgs(rest);
36046
+ const { positional, flagArgs } = splitArgs2(rest);
34974
36047
  if (group === "do") {
34975
36048
  if (!sub) {
34976
36049
  printHelp2();
@@ -35258,7 +36331,7 @@ async function adminCommand(args2) {
35258
36331
  printHelp2();
35259
36332
  process.exit(1);
35260
36333
  }
35261
- function splitArgs(args2) {
36334
+ function splitArgs2(args2) {
35262
36335
  const positional = [];
35263
36336
  const flagArgs = [];
35264
36337
  for (const a of args2) {
@@ -37383,9 +38456,9 @@ var init_update = __esm({
37383
38456
  });
37384
38457
 
37385
38458
  // src/data/commands/actions.ts
37386
- import { Command } from "commander";
38459
+ import { Command as Command2 } from "commander";
37387
38460
  function buildActionsCommand() {
37388
- const actions = new Command("actions").description(
38461
+ const actions = new Command2("actions").description(
37389
38462
  "Manage Actions \u2014 reusable, named definitions: one record_type + one operation, or a composite of atomic write steps (--config with `steps`)"
37390
38463
  );
37391
38464
  actions.command("upsert <id>").description(
@@ -37454,7 +38527,7 @@ var init_actions = __esm({
37454
38527
  });
37455
38528
 
37456
38529
  // src/data/commands/attachments.ts
37457
- import { Command as Command2 } from "commander";
38530
+ import { Command as Command3 } from "commander";
37458
38531
  import { readFileSync as readFileSync19 } from "fs";
37459
38532
  function findAttachmentByFilename(attachments, filename) {
37460
38533
  return attachments.find((a) => a.key.endsWith(`/${filename}`)) ?? null;
@@ -37481,7 +38554,7 @@ function uploadPathFrom(uploadUrl) {
37481
38554
  return uploadUrl;
37482
38555
  }
37483
38556
  function buildAttachmentsCommand() {
37484
- const attachments = new Command2("attachments").description("Manage record attachments");
38557
+ const attachments = new Command3("attachments").description("Manage record attachments");
37485
38558
  attachments.command("upload <record_type> <id>").description(
37486
38559
  "Get a presigned upload URL for a record attachment. With --file, uploads the file content in one step."
37487
38560
  ).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 +38634,9 @@ var init_attachments = __esm({
37561
38634
  });
37562
38635
 
37563
38636
  // src/data/commands/batch.ts
37564
- import { Command as Command3 } from "commander";
38637
+ import { Command as Command4 } from "commander";
37565
38638
  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) {
38639
+ 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
38640
  const base = pathSegment(requireBase(this), "--base");
37568
38641
  const operations = parseData(opts.operations, "--operations");
37569
38642
  const client = await createDataClient();
@@ -37592,7 +38665,7 @@ import { createReadStream } from "fs";
37592
38665
  import { access, constants, stat } from "fs/promises";
37593
38666
  import { createInterface as createInterface3 } from "readline";
37594
38667
  import { randomUUID } from "crypto";
37595
- import { Command as Command4 } from "commander";
38668
+ import { Command as Command5 } from "commander";
37596
38669
  function isRetryable(err) {
37597
38670
  if (isNetworkError(err)) return true;
37598
38671
  if (!(err instanceof ApiError)) return false;
@@ -37668,7 +38741,7 @@ async function beginSession(state, client, base, recordType2, requestKey) {
37668
38741
  return res.session;
37669
38742
  }
37670
38743
  function buildBasesImportCommand() {
37671
- const importCmd = withBaseOption(new Command4("import")).description(
38744
+ const importCmd = withBaseOption(new Command5("import")).description(
37672
38745
  "Bulk historical import: load an NDJSON backfill, list sessions, or undo one"
37673
38746
  );
37674
38747
  importCmd.command("run <record_type>").description(
@@ -37895,7 +38968,7 @@ var init_import = __esm({
37895
38968
  });
37896
38969
 
37897
38970
  // src/data/commands/providers.ts
37898
- import { Command as Command5 } from "commander";
38971
+ import { Command as Command6 } from "commander";
37899
38972
  import { writeFileSync as writeFileSync10 } from "fs";
37900
38973
  function providerSegment(provider) {
37901
38974
  if (!VALID_PROVIDERS.includes(provider)) {
@@ -37904,7 +38977,7 @@ function providerSegment(provider) {
37904
38977
  return pathSegment(provider, "provider");
37905
38978
  }
37906
38979
  function buildBasesProvidersCommand() {
37907
- const providers = withBaseOption(new Command5("providers")).description(
38980
+ const providers = withBaseOption(new Command6("providers")).description(
37908
38981
  "Import/export tool definitions between LLM providers and record types"
37909
38982
  );
37910
38983
  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 +39048,7 @@ var init_providers = __esm({
37975
39048
  });
37976
39049
 
37977
39050
  // src/data/commands/report.ts
37978
- import { Command as Command6 } from "commander";
39051
+ import { Command as Command7 } from "commander";
37979
39052
  import { readFileSync as readFileSync20 } from "fs";
37980
39053
  import { dirname as dirname15, join as join36 } from "path";
37981
39054
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -38019,7 +39092,7 @@ function reportActionError2(err) {
38019
39092
  throw err;
38020
39093
  }
38021
39094
  function buildBasesReportCommand() {
38022
- const report2 = new Command6("report").description(
39095
+ const report2 = new Command7("report").description(
38023
39096
  "File a report to the Data triage queue and verify the outcome"
38024
39097
  );
38025
39098
  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 +39259,7 @@ var init_report3 = __esm({
38186
39259
  });
38187
39260
 
38188
39261
  // src/data/commands/credentials.ts
38189
- import { Command as Command7 } from "commander";
39262
+ import { Command as Command8 } from "commander";
38190
39263
  import { readFileSync as readFileSync21 } from "fs";
38191
39264
  function withValueSourceOptions(cmd, what) {
38192
39265
  return cmd.option(
@@ -38221,7 +39294,7 @@ function credentialPath(base, id) {
38221
39294
  }
38222
39295
  function buildBasesCredentialsCommand() {
38223
39296
  const credentials = withBaseOption(
38224
- new Command7("credentials").description("Manage a base's integration credentials")
39297
+ new Command8("credentials").description("Manage a base's integration credentials")
38225
39298
  );
38226
39299
  withValueSourceOptions(
38227
39300
  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 +39421,10 @@ var init_credentials = __esm({
38348
39421
  });
38349
39422
 
38350
39423
  // src/data/commands/sql.ts
38351
- import { Command as Command8 } from "commander";
39424
+ import { Command as Command9 } from "commander";
38352
39425
  import { readFileSync as readFileSync22 } from "fs";
38353
39426
  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(
39427
+ 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
39428
  "--param <kv...>",
38356
39429
  "Named parameters as key=value pairs (e.g. --param status=issued)"
38357
39430
  ).action(async function(queryArg, opts) {
@@ -38393,250 +39466,6 @@ var init_sql = __esm({
38393
39466
  }
38394
39467
  });
38395
39468
 
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
39469
  // src/data/base-tags.ts
38641
39470
  async function resolveBaseTags(rawTags, client) {
38642
39471
  const refs = splitList(rawTags);
@@ -39550,7 +40379,7 @@ function isConflict(err) {
39550
40379
  return err instanceof ApiError && (err.status === 409 || err.status === 412);
39551
40380
  }
39552
40381
  function conflictLine(c, texts) {
39553
- return `${safe2(c.rel)}: ${texts[c.reason]}${c.other === void 0 ? "" : ` "${safe2(c.other)}"`}`;
40382
+ return `${safe3(c.rel)}: ${texts[c.reason]}${c.other === void 0 ? "" : ` "${safe3(c.other)}"`}`;
39554
40383
  }
39555
40384
  function remedy(conflicts, overwriteCommand) {
39556
40385
  const isClash = (c) => c.reason === "case_twin" || c.reason === "file_folder";
@@ -39571,7 +40400,7 @@ async function pullFiles(opts) {
39571
40400
  const plan = planPull({ synced: existing?.files ?? {}, local, remote, overwrite: opts.overwrite });
39572
40401
  if (plan.replacedLocal.length > 0) {
39573
40402
  console.log("These local changes would be replaced with the base's copies:");
39574
- for (const rel of plan.replacedLocal) console.log(` ${safe2(rel)}`);
40403
+ for (const rel of plan.replacedLocal) console.log(` ${safe3(rel)}`);
39575
40404
  if (!await agree(`Replace ${plan.replacedLocal.length} locally changed file(s)?`, opts.yes)) {
39576
40405
  console.log("Cancelled.");
39577
40406
  return;
@@ -39592,7 +40421,7 @@ async function pullFiles(opts) {
39592
40421
  }
39593
40422
  removeLocal(dir, rel);
39594
40423
  delete record.files[rel];
39595
- changes.push(`${MARK.delete} ${safe2(rel)}`);
40424
+ changes.push(`${MARK.delete} ${safe3(rel)}`);
39596
40425
  }
39597
40426
  for (const w of plan.writes) {
39598
40427
  const clash = diskClash(dir, w.rel);
@@ -39625,7 +40454,7 @@ async function pullFiles(opts) {
39625
40454
  const hash = computeHash(bytes);
39626
40455
  writeLocal(dir, w.rel, bytes);
39627
40456
  record.files[w.rel] = { version, content_sha256: hash };
39628
- changes.push(`${local.has(w.rel) ? MARK.update : MARK.create} ${safe2(w.rel)}`);
40457
+ changes.push(`${local.has(w.rel) ? MARK.update : MARK.create} ${safe3(w.rel)}`);
39629
40458
  }
39630
40459
  } finally {
39631
40460
  writeRecord(dir, record);
@@ -39633,13 +40462,13 @@ async function pullFiles(opts) {
39633
40462
  const count = Object.keys(record.files).length;
39634
40463
  console.log(sanitizeTerminalText(`Pulled ${describeSync(target)} \u2192 ${dir} (${count} file${count === 1 ? "" : "s"}).`));
39635
40464
  printSection("Changes:", changes);
39636
- printSection("Kept \u2014 your local changes:", plan.kept.map((k) => `${safe2(k.rel)}: ${PULL_KEPT[k.reason]}`));
40465
+ printSection("Kept \u2014 your local changes:", plan.kept.map((k) => `${safe3(k.rel)}: ${PULL_KEPT[k.reason]}`));
39637
40466
  printSection(
39638
40467
  `Conflicts \u2014 your copy is kept; to settle them, ${remedy(conflicts, pullCommand2(target, opts.dir, true))}:`,
39639
40468
  conflicts.map((c) => conflictLine(c, PULL_CONFLICT))
39640
40469
  );
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));
40470
+ printSection("Changed or deleted on the base while pulling \u2014 left as is; pull again to take it:", changedWhilePulling.map(safe3));
40471
+ printSection(`Not synced \u2014 names a synced folder leaves out (${OUTSIDE_SYNC.join(", ")}):`, excluded.map(safe3));
39643
40472
  }
39644
40473
  function isTextType(contentType) {
39645
40474
  const ct = contentType.split(";")[0].trim().toLowerCase();
@@ -39657,7 +40486,7 @@ function changedLines(before, after) {
39657
40486
  async function describeSteps(client, t, dir, steps) {
39658
40487
  const lines = [];
39659
40488
  for (const step of steps) {
39660
- const rel = safe2(step.rel);
40489
+ const rel = safe3(step.rel);
39661
40490
  if (step.op === "create") {
39662
40491
  lines.push(`${MARK.create} ${rel} (new, ${step.local.size} bytes)`);
39663
40492
  } else if (step.op === "delete") {
@@ -39684,10 +40513,10 @@ async function describeSteps(client, t, dir, steps) {
39684
40513
  return lines;
39685
40514
  }
39686
40515
  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));
40516
+ printSection("Changed on the base since your last pull \u2014 left as is; pull to take it:", plan.changedOnBase.map(safe3));
40517
+ printSection("Deleted on the base since your last pull \u2014 left as is; pull to remove your copy:", plan.deletedOnBase.map(safe3));
40518
+ printSection("On the base, not in your files \u2014 left as is; pull to fetch it:", plan.onlyOnBase.map(safe3));
40519
+ printSection("Deleted in your files \u2014 kept on the base; push with --prune to delete it:", plan.keptOnBase.map(safe3));
39691
40520
  }
39692
40521
  async function pushFiles(opts) {
39693
40522
  const label = opts.label === void 0 ? void 0 : labelValue(opts.label);
@@ -39750,14 +40579,14 @@ Push ${n} change${n === 1 ? "" : "s"} to ${describeSync(target)}?`, opts.yes)) {
39750
40579
  } else {
39751
40580
  const bytes = readLocal(dir, step.rel);
39752
40581
  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.`);
40582
+ throw expected(`${safe3(step.rel)} changed while the push ran. Nothing more was pushed; run the push again.`);
39754
40583
  }
39755
40584
  const contentType = step.op === "update" ? step.remote.content_type : guessMimeType(step.rel) ?? "application/octet-stream";
39756
40585
  const row = await client.upload(
39757
40586
  path42,
39758
40587
  bytes,
39759
40588
  contentType,
39760
- fileWriteHeaders({ ifMatch: step.op === "update" ? step.ifMatch : void 0, label })
40589
+ fileWriteHeaders({ ...step.op === "update" ? { ifMatch: step.ifMatch } : { createOnly: true }, label })
39761
40590
  );
39762
40591
  record.files[step.rel] = {
39763
40592
  version: typeof row?.version === "number" ? row.version : 0,
@@ -39765,13 +40594,14 @@ Push ${n} change${n === 1 ? "" : "s"} to ${describeSync(target)}?`, opts.yes)) {
39765
40594
  };
39766
40595
  }
39767
40596
  pushed++;
39768
- console.log(` ${MARK[step.op]} ${safe2(step.rel)}`);
40597
+ console.log(` ${MARK[step.op]} ${safe3(step.rel)}`);
39769
40598
  } catch (err) {
39770
40599
  if (step.op === "delete" && err instanceof ApiError && err.status === 404) {
39771
40600
  delete record.files[step.rel];
39772
40601
  } else if (isConflict(err)) {
39773
40602
  if (step.op === "delete") refusedDeletes.add(step.rel);
39774
- const conflict = { rel: step.rel, reason: "changed_while_pushing" };
40603
+ const reason = step.op === "create" && err.status === 412 ? "created_while_pushing" : "changed_while_pushing";
40604
+ const conflict = { rel: step.rel, reason };
39775
40605
  conflicts.push(conflict);
39776
40606
  console.log(` ! ${conflictLine(conflict, PUSH_CONFLICT)}`);
39777
40607
  continue;
@@ -39792,7 +40622,7 @@ function finishWithConflicts(t, dir, conflicts) {
39792
40622
  `${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
40623
  );
39794
40624
  }
39795
- var LIST_PAGE2, MAX_LIST_PAGES2, MAX_DIFF_BYTES, MARK, safe2, PULL_KEPT, PULL_CONFLICT, PUSH_CONFLICT;
40625
+ var LIST_PAGE2, MAX_LIST_PAGES2, MAX_DIFF_BYTES, MARK, safe3, PULL_KEPT, PULL_CONFLICT, PUSH_CONFLICT;
39796
40626
  var init_sync3 = __esm({
39797
40627
  "src/data/files-sync/sync.ts"() {
39798
40628
  "use strict";
@@ -39814,7 +40644,7 @@ var init_sync3 = __esm({
39814
40644
  MAX_LIST_PAGES2 = 1e3;
39815
40645
  MAX_DIFF_BYTES = 256 * 1024;
39816
40646
  MARK = { create: "+", update: "~", delete: "-" };
39817
- safe2 = (rel) => sanitizeTerminalText(rel);
40647
+ safe3 = (rel) => sanitizeTerminalText(rel);
39818
40648
  PULL_KEPT = {
39819
40649
  edited: "edited in your files, not pushed yet",
39820
40650
  deleted: "deleted in your files, not pushed yet (push --prune deletes it from the base)"
@@ -39834,7 +40664,8 @@ var init_sync3 = __esm({
39834
40664
  exists_on_base: "created on the base since your last pull",
39835
40665
  case_twin: "differs only in case from the base's",
39836
40666
  file_folder: "would make one path both a file and a folder with the base's",
39837
- changed_while_pushing: "changed on the base while pushing \u2014 not written"
40667
+ changed_while_pushing: "changed on the base while pushing \u2014 not written",
40668
+ created_while_pushing: "created on the base while pushing \u2014 not written"
39838
40669
  };
39839
40670
  }
39840
40671
  });
@@ -40018,7 +40849,7 @@ function buildFilesCommand() {
40018
40849
  });
40019
40850
  });
40020
40851
  files.command("push <file_type>").description(
40021
- "Push the local changes of a folder pulled with `files pull` to the base, after showing the diff. A file changed, deleted or created on the base since your pull is a conflict, not written; an update or delete also carries the version pulled, so the base refuses it if the file changed meanwhile"
40852
+ "Push the local changes of a folder pulled with `files pull` to the base, after showing the diff. A file changed, deleted or created on the base since your pull is a conflict, not written; an update or delete also carries the version pulled and a new file is sent create-only, so the base refuses a write to a file changed or created meanwhile"
40022
40853
  ).option("--folder <path>", "The folder of the file type the local folder was pulled from").option("--dir <local>", "Local folder to push (default ./<file_type>, or ./<file_type>/<folder>)").option("--dry-run", "Show the diff and push nothing").option("--prune", "Also delete files you deleted locally \u2014 only those unchanged on the base since your pull").option("--label <text>", "Label for every content version the push creates (printable ASCII)").option("-y, --yes", "Push without asking").action(async function(fileType, opts) {
40023
40854
  const base = requireBase(this);
40024
40855
  await pushFiles({
@@ -41436,6 +42267,7 @@ Commands:
41436
42267
  contacts Link a contact to its customer record in a base, or unlink it (browser sign-in only) \u2014 \`wayai contacts --help\`
41437
42268
  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
42269
  knowledge refresh-preview Make the knowledge base's preview hold its production base's files \u2014 \`wayai knowledge --help\`
42270
+ knowledge cut-over Switch a preview hub's agents from their knowledge resources to the knowledge base \u2014 \`wayai knowledge --help\`
41439
42271
  report create Report a platform bug (submits to the triage queue)
41440
42272
  report edit Amend your own pending report (title/description/error/steps/context)
41441
42273
  update Update CLI to the latest version