skedyul 1.7.29 → 1.7.31

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/cli/index.js CHANGED
@@ -2811,6 +2811,34 @@ async function getWorkflowRun(ctx, workplace, workflowRunId) {
2811
2811
  });
2812
2812
  }
2813
2813
 
2814
+ // src/cli/api/playground.ts
2815
+ async function sendPlaygroundTurn(ctx, workplace, input) {
2816
+ const workplaceToken = await getWorkplaceToken(ctx, workplace);
2817
+ return callCliApi(cliApi(ctx), "/playground", {
2818
+ workplaceId: workplaceToken.workplaceId,
2819
+ action: "send",
2820
+ handle: input.handle,
2821
+ agentId: input.agentId,
2822
+ threadId: input.threadId,
2823
+ content: input.message,
2824
+ currentTime: input.currentTime,
2825
+ contactId: input.contactId,
2826
+ appInstallationId: input.appInstallationId,
2827
+ title: input.title,
2828
+ sandbox: input.sandbox
2829
+ });
2830
+ }
2831
+ async function inspectPlaygroundSession(ctx, workplace, threadId) {
2832
+ const workplaceToken = await getWorkplaceToken(ctx, workplace);
2833
+ return callCliApi(cliApi(ctx), "/playground", void 0, {
2834
+ method: "GET",
2835
+ query: {
2836
+ workplaceId: workplaceToken.workplaceId,
2837
+ threadId
2838
+ }
2839
+ });
2840
+ }
2841
+
2814
2842
  // src/cli/api/apps.ts
2815
2843
  async function linkApp(ctx, input) {
2816
2844
  return callCliApi(cliApi(ctx), "/link", {
@@ -3618,6 +3646,14 @@ var AppAuthInvalidError = class extends Error {
3618
3646
  this.name = "AppAuthInvalidError";
3619
3647
  }
3620
3648
  };
3649
+ var TokenRefreshRequiredError = class extends Error {
3650
+ constructor(message = "Access token expired", options) {
3651
+ super(message);
3652
+ this.code = "TOKEN_REFRESH_REQUIRED";
3653
+ this.name = "TokenRefreshRequiredError";
3654
+ this.tokenKey = options?.tokenKey;
3655
+ }
3656
+ };
3621
3657
 
3622
3658
  // src/ratelimit/errors.ts
3623
3659
  var RateLimitExceededError = class extends Error {
@@ -3933,6 +3969,24 @@ function createCallToolHandler(registry, state, onMaxRequests) {
3933
3969
  }
3934
3970
  };
3935
3971
  }
3972
+ if (error instanceof TokenRefreshRequiredError) {
3973
+ return {
3974
+ success: false,
3975
+ output: null,
3976
+ billing: { credits: 0 },
3977
+ meta: {
3978
+ success: false,
3979
+ message: error.message,
3980
+ toolName
3981
+ },
3982
+ error: {
3983
+ code: error.code,
3984
+ message: error.message,
3985
+ category: "auth",
3986
+ ...error.tokenKey ? { details: { tokenKey: error.tokenKey } } : {}
3987
+ }
3988
+ };
3989
+ }
3936
3990
  if (error instanceof AppAuthInvalidError) {
3937
3991
  return {
3938
3992
  output: null,
@@ -4686,6 +4740,76 @@ async function handleOAuthCallback(parsedBody, hooks) {
4686
4740
  }
4687
4741
  }
4688
4742
 
4743
+ // src/server/handlers/refresh-token-handler.ts
4744
+ async function handleRefreshToken(parsedBody, hooks) {
4745
+ if (!hooks?.refresh_token) {
4746
+ return {
4747
+ status: 404,
4748
+ body: { error: "Refresh token handler not configured" }
4749
+ };
4750
+ }
4751
+ const body = parsedBody ?? {};
4752
+ if (!body.env || typeof body.env !== "object" || Array.isArray(body.env)) {
4753
+ return {
4754
+ status: 400,
4755
+ body: {
4756
+ error: {
4757
+ code: -32602,
4758
+ message: "Missing envelope format: expected { env }"
4759
+ }
4760
+ }
4761
+ };
4762
+ }
4763
+ const refreshContext = {
4764
+ env: body.env,
4765
+ invocation: body.invocation,
4766
+ log: createContextLogger()
4767
+ };
4768
+ const requestConfig = {
4769
+ baseUrl: body.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
4770
+ apiToken: body.env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
4771
+ };
4772
+ try {
4773
+ const refreshHook = hooks.refresh_token;
4774
+ const refreshHandler = typeof refreshHook === "function" ? refreshHook : refreshHook.handler;
4775
+ const result = await withRequestEnv(body.env, async () => {
4776
+ return await runWithConfig(requestConfig, async () => {
4777
+ return await runWithLogContext({ invocation: body.invocation }, async () => {
4778
+ return await refreshHandler(refreshContext);
4779
+ });
4780
+ });
4781
+ });
4782
+ return {
4783
+ status: 200,
4784
+ body: {
4785
+ env: result.env ?? {}
4786
+ }
4787
+ };
4788
+ } catch (err) {
4789
+ if (err instanceof AppAuthInvalidError) {
4790
+ return {
4791
+ status: 401,
4792
+ body: {
4793
+ error: {
4794
+ code: err.code,
4795
+ message: err.message
4796
+ }
4797
+ }
4798
+ };
4799
+ }
4800
+ const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
4801
+ return {
4802
+ status: 500,
4803
+ body: {
4804
+ error: {
4805
+ code: -32603,
4806
+ message: errorMessage
4807
+ }
4808
+ }
4809
+ };
4810
+ }
4811
+ }
4812
+
4689
4813
  // src/types/webhook.ts
4690
4814
  function isRuntimeWebhookContext(ctx) {
4691
4815
  return "appInstallationId" in ctx && ctx.appInstallationId !== void 0;
@@ -4789,6 +4913,33 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
4789
4913
  });
4790
4914
  });
4791
4915
  } catch (err) {
4916
+ if (err instanceof TokenRefreshRequiredError) {
4917
+ return {
4918
+ status: 200,
4919
+ body: {
4920
+ success: false,
4921
+ error: {
4922
+ code: err.code,
4923
+ message: err.message,
4924
+ category: "auth",
4925
+ ...err.tokenKey ? { details: { tokenKey: err.tokenKey } } : {}
4926
+ }
4927
+ }
4928
+ };
4929
+ }
4930
+ if (err instanceof AppAuthInvalidError) {
4931
+ return {
4932
+ status: 200,
4933
+ body: {
4934
+ success: false,
4935
+ error: {
4936
+ code: err.code,
4937
+ message: err.message,
4938
+ category: "auth"
4939
+ }
4940
+ }
4941
+ };
4942
+ }
4792
4943
  console.error(`Webhook handler '${handle}' error:`, err);
4793
4944
  return {
4794
4945
  status: 500,
@@ -4991,6 +5142,14 @@ async function handleOAuthCallbackRoute(req, ctx) {
4991
5142
  const result = await handleOAuthCallback(parseResult.data, ctx.config.hooks);
4992
5143
  return { status: result.status, body: result.body };
4993
5144
  }
5145
+ async function handleRefreshTokenRoute(req, ctx) {
5146
+ const parseResult = parseJsonBody(req);
5147
+ if (!parseResult.success) {
5148
+ return parseResult.error;
5149
+ }
5150
+ const result = await handleRefreshToken(parseResult.data, ctx.config.hooks);
5151
+ return { status: result.status, body: result.body };
5152
+ }
4994
5153
  async function handleWebhookRoute(req, handle, ctx) {
4995
5154
  if (!ctx.webhookRegistry) {
4996
5155
  return { status: 404, body: { error: `Webhook handler '${handle}' not found` } };
@@ -5482,6 +5641,21 @@ async function handleBatchOperationRoute(req, ctx) {
5482
5641
  { allowed: true, afterMs: err.retryAfterMs }
5483
5642
  );
5484
5643
  }
5644
+ if (err instanceof TokenRefreshRequiredError) {
5645
+ return softFailureResponse({
5646
+ code: err.code,
5647
+ message: err.message,
5648
+ category: "auth",
5649
+ ...err.tokenKey ? { details: { tokenKey: err.tokenKey } } : {}
5650
+ });
5651
+ }
5652
+ if (err instanceof AppAuthInvalidError) {
5653
+ return softFailureResponse({
5654
+ code: err.code,
5655
+ message: err.message,
5656
+ category: "auth"
5657
+ });
5658
+ }
5485
5659
  log.error("Batch operation failed", err);
5486
5660
  return {
5487
5661
  status: 500,
@@ -5566,6 +5740,9 @@ async function routeRequest(req, ctx) {
5566
5740
  if (req.path === "/oauth_callback" && req.method === "POST") {
5567
5741
  return handleOAuthCallbackRoute(req, ctx);
5568
5742
  }
5743
+ if (req.path === "/refresh_token" && req.method === "POST") {
5744
+ return handleRefreshTokenRoute(req, ctx);
5745
+ }
5569
5746
  if (req.path === "/health" && req.method === "GET") {
5570
5747
  return handleHealthRoute(ctx);
5571
5748
  }
@@ -10552,6 +10729,40 @@ var CRMModelFieldRequirementsSchema = import_v47.z.object({
10552
10729
  var CRMContextSchema = import_v47.z.object({
10553
10730
  models: import_v47.z.record(import_v47.z.string(), CRMModelFieldRequirementsSchema)
10554
10731
  });
10732
+ var EntityOperationSchema = import_v47.z.enum(["get", "list", "update", "create", "delete"]);
10733
+ var EntityPoliciesSchema = import_v47.z.record(
10734
+ import_v47.z.string(),
10735
+ // entity handle (e.g., "member", "booking")
10736
+ import_v47.z.array(EntityOperationSchema)
10737
+ // allowed operations
10738
+ );
10739
+ var EvaluatorPreconditionsSchema = import_v47.z.object({
10740
+ toolSucceeded: import_v47.z.boolean().optional(),
10741
+ anyWrittenFields: import_v47.z.array(import_v47.z.string()).optional(),
10742
+ noneOfWrittenFields: import_v47.z.array(import_v47.z.string()).optional(),
10743
+ crm: import_v47.z.array(
10744
+ import_v47.z.object({
10745
+ field: import_v47.z.string(),
10746
+ op: import_v47.z.enum(["eq", "in", "empty", "notEmpty"]),
10747
+ value: import_v47.z.unknown().optional()
10748
+ })
10749
+ ).optional(),
10750
+ skillsLoaded: import_v47.z.array(import_v47.z.string()).optional(),
10751
+ skillsNotLoaded: import_v47.z.array(import_v47.z.string()).optional()
10752
+ });
10753
+ var SkillEvaluatorSchema = import_v47.z.object({
10754
+ name: import_v47.z.string(),
10755
+ description: import_v47.z.string().optional(),
10756
+ target: import_v47.z.enum(["message", "tool", "entity"]),
10757
+ selector: import_v47.z.string().optional(),
10758
+ criteria: import_v47.z.string(),
10759
+ passThreshold: import_v47.z.number().optional(),
10760
+ checks: import_v47.z.object({
10761
+ requiredArgs: import_v47.z.array(import_v47.z.string()).optional(),
10762
+ forbiddenArgValues: import_v47.z.record(import_v47.z.string(), import_v47.z.array(import_v47.z.unknown())).optional()
10763
+ }).optional(),
10764
+ preconditions: EvaluatorPreconditionsSchema.optional()
10765
+ });
10555
10766
  var SkillYAMLSchema = import_v47.z.object({
10556
10767
  // Schema version
10557
10768
  $schema: import_v47.z.string().optional(),
@@ -10562,10 +10773,26 @@ var SkillYAMLSchema = import_v47.z.object({
10562
10773
  description: import_v47.z.string().optional(),
10563
10774
  // Instructions injected into agent system prompt
10564
10775
  instructions: import_v47.z.string(),
10776
+ // Entity policies - declares which entity operations this skill needs
10777
+ // At runtime, these are resolved to CRM tools via AppCrmMap
10778
+ entityPolicies: EntityPoliciesSchema.optional(),
10565
10779
  // Tool configuration - supports both v1 and v2 formats
10780
+ // Use for non-entity tools (MCP tools, system tools)
10566
10781
  tools: SkillToolsSchema.optional(),
10567
10782
  // CRM context - specifies which models/fields to include in schema
10568
10783
  crmContext: CRMContextSchema.optional(),
10784
+ /**
10785
+ * Entity field handles this skill is allowed to write while loaded.
10786
+ * Writing outside this list is reported as an unowned write.
10787
+ */
10788
+ ownedFields: import_v47.z.array(import_v47.z.string()).optional(),
10789
+ /**
10790
+ * Entity field handles this skill must have written before it replies.
10791
+ * Only handles mapped in the install's AppCrmMap are enforced.
10792
+ */
10793
+ requiredWrites: import_v47.z.array(import_v47.z.string()).optional(),
10794
+ // Quality evaluators owned by this skill
10795
+ evaluators: import_v47.z.array(SkillEvaluatorSchema).optional(),
10569
10796
  // Few-shot examples
10570
10797
  examples: import_v47.z.array(SkillExampleSchema).optional()
10571
10798
  });
@@ -10576,8 +10803,12 @@ var SkillYAMLV2Schema = import_v47.z.object({
10576
10803
  version: import_v47.z.string().optional(),
10577
10804
  description: import_v47.z.string().optional(),
10578
10805
  instructions: import_v47.z.string(),
10806
+ entityPolicies: EntityPoliciesSchema.optional(),
10579
10807
  tools: import_v47.z.array(SkillToolDefinitionSchema).optional(),
10580
10808
  crmContext: CRMContextSchema.optional(),
10809
+ ownedFields: import_v47.z.array(import_v47.z.string()).optional(),
10810
+ requiredWrites: import_v47.z.array(import_v47.z.string()).optional(),
10811
+ evaluators: import_v47.z.array(SkillEvaluatorSchema).optional(),
10581
10812
  examples: import_v47.z.array(SkillExampleSchema).optional()
10582
10813
  });
10583
10814
  var SkillVersionWeightSchema = import_v47.z.object({
@@ -10591,17 +10822,24 @@ var SkillRefSchema = import_v47.z.union([
10591
10822
  skill: import_v47.z.string(),
10592
10823
  description: import_v47.z.string().optional(),
10593
10824
  // For AI SDK Agent Skills discovery
10594
- /** Auto-inject this skill's instructions and tools on every agent turn */
10825
+ /**
10826
+ * Pre-seed this skill as loaded before the first model step, so the agent
10827
+ * does not have to call system:skill:load for it.
10828
+ */
10595
10829
  alwaysLoad: import_v47.z.boolean().optional(),
10596
10830
  // Version selection (pick one):
10597
10831
  version: import_v47.z.number().optional(),
10598
10832
  // Pin to specific version number
10599
10833
  versions: import_v47.z.array(SkillVersionWeightSchema).optional(),
10600
10834
  // A/B testing weights
10601
- // Legacy support:
10602
- instructions: import_v47.z.string().optional(),
10603
- // Inline instructions (deprecated)
10604
- enabled: import_v47.z.boolean().optional()
10835
+ /**
10836
+ * @deprecated Not implemented. Instructions come from the skill file.
10837
+ */
10838
+ instructions: import_v47.z.string().optional().describe("Not implemented"),
10839
+ /**
10840
+ * @deprecated Not implemented. Remove the skill from `skills` instead.
10841
+ */
10842
+ enabled: import_v47.z.boolean().optional().describe("Not implemented")
10605
10843
  })
10606
10844
  ]);
10607
10845
  var SkillMetadataSchema = import_v47.z.object({
@@ -10630,6 +10868,7 @@ var LoadedSkillSchema = import_v47.z.object({
10630
10868
  name: import_v47.z.string(),
10631
10869
  instructions: import_v47.z.string().optional(),
10632
10870
  description: import_v47.z.string().optional(),
10871
+ entityPolicies: EntityPoliciesSchema.optional(),
10633
10872
  tools: import_v47.z.array(SkillToolDefinitionSchema).optional(),
10634
10873
  examples: import_v47.z.array(SkillExampleSchema).optional()
10635
10874
  });
@@ -10719,7 +10958,11 @@ var ContextValidationResultSchema = import_v48.z.object({
10719
10958
  issues: import_v48.z.array(ContextIssueSchema)
10720
10959
  });
10721
10960
  var SandboxConfigSchema = import_v48.z.object({
10722
- enabled: import_v48.z.boolean().optional(),
10961
+ /**
10962
+ * @deprecated Not implemented. Sandbox mode is chosen by the caller
10963
+ * (playground, scenario replay, CLI), never by the agent file.
10964
+ */
10965
+ enabled: import_v48.z.boolean().optional().describe("Not implemented"),
10723
10966
  /** @deprecated Use 'context' instead of 'mockContext' */
10724
10967
  mockContext: AgentContextSchema.optional(),
10725
10968
  context: AgentContextSchema.optional()
@@ -10884,24 +11127,94 @@ var ResponsesBehaviorConfigSchema = import_v49.z.object({
10884
11127
  * @default false
10885
11128
  */
10886
11129
  allowSchedule: import_v49.z.boolean().optional(),
11130
+ /**
11131
+ * Whether the agent may skip sending when no reply is warranted.
11132
+ * Platform injects silence rules and exposes system:message:skip.
11133
+ * An explicit skip satisfies requireFinal. Off by default.
11134
+ * @default false
11135
+ */
11136
+ allowSilent: import_v49.z.boolean().optional(),
11137
+ /**
11138
+ * Strip em dashes, smart quotes and bullet leaders from outbound messages.
11139
+ * @default false
11140
+ */
11141
+ humanize: import_v49.z.boolean().optional(),
10887
11142
  /**
10888
11143
  * Message splitting configuration.
10889
- * Controls whether and how the agent splits responses into multiple messages.
11144
+ * @deprecated Not implemented — accepted but never applied at runtime.
10890
11145
  */
10891
11146
  messageSplitting: import_v49.z.object({
11147
+ enabled: import_v49.z.boolean(),
11148
+ prompt: import_v49.z.string().optional()
11149
+ }).optional().describe("Not implemented"),
11150
+ /**
11151
+ * Response gate configuration.
11152
+ * Automatically detects acknowledgment messages and recommends skipping
11153
+ * responses when a scheduled follow-up already exists.
11154
+ */
11155
+ responseGate: import_v49.z.object({
10892
11156
  /**
10893
- * Whether to allow natural message splitting.
10894
- * When true, the agent may split responses into multiple messages
10895
- * when it improves conversational flow.
11157
+ * Whether the response gate is enabled.
11158
+ * When true, the platform evaluates incoming messages for acknowledgment
11159
+ * patterns and may recommend skipping a response if scheduled follow-ups exist.
11160
+ * @default true (when allowSilent is true)
10896
11161
  */
10897
- enabled: import_v49.z.boolean(),
11162
+ enabled: import_v49.z.boolean().optional(),
10898
11163
  /**
10899
- * Custom prompt to override the default message splitting guidance.
10900
- * If not provided, uses sensible defaults for when to split vs. keep together.
11164
+ * Whether to hard-block sends when gate recommends skip.
11165
+ * When true, system:message:send is blocked unless overrideGate is provided.
11166
+ * When false (default), returns an error encouraging skip but allows override.
11167
+ * @default false
10901
11168
  */
10902
- prompt: import_v49.z.string().optional()
11169
+ strictMode: import_v49.z.boolean().optional(),
11170
+ /**
11171
+ * Minimum confidence threshold for acknowledgment detection.
11172
+ * Only recommend skip when confidence meets this threshold.
11173
+ * @default 0.7
11174
+ */
11175
+ minAckConfidence: import_v49.z.number().min(0).max(1).optional()
10903
11176
  }).optional()
10904
11177
  });
11178
+ var ConversationPolicyLevelSchema = import_v49.z.enum(["off", "prompt", "reject"]);
11179
+ var ConversationBehaviorConfigSchema = import_v49.z.object({
11180
+ /** Do not re-ask something the customer already answered. */
11181
+ repeatQuestions: ConversationPolicyLevelSchema.optional(),
11182
+ /** Do not send skill example wording verbatim. */
11183
+ exampleCopy: ConversationPolicyLevelSchema.optional(),
11184
+ /**
11185
+ * A named delay or a promised check-in should produce a scheduled send.
11186
+ * `prompt` reprompts once and accepts the turn either way; `reject` fails
11187
+ * the turn when no scheduled send follows.
11188
+ */
11189
+ namedDelayFollowUp: ConversationPolicyLevelSchema.optional(),
11190
+ /**
11191
+ * Keep the turn to one message. Prompt-only — the hard count comes from
11192
+ * `behavior.responses.maxImmediate`.
11193
+ */
11194
+ oneMessagePerTurn: ConversationPolicyLevelSchema.optional(),
11195
+ /**
11196
+ * Skip rather than send filler. Prompt-only, and only stated when
11197
+ * `behavior.responses.allowSilent` is set.
11198
+ */
11199
+ fillerBan: ConversationPolicyLevelSchema.optional(),
11200
+ /**
11201
+ * A bare acknowledgement does not cancel a pending follow-up. Prompt-only,
11202
+ * and only stated when the thread has pending scheduled messages.
11203
+ */
11204
+ cancelPendingOnAck: ConversationPolicyLevelSchema.optional(),
11205
+ /**
11206
+ * Ask one thing at a time. `reject` blocks a send that asks more than one
11207
+ * question. Defaults to `off` at runtime: an agent that collects several
11208
+ * answers in one message is a legitimate design.
11209
+ */
11210
+ oneQuestionPerMessage: ConversationPolicyLevelSchema.optional(),
11211
+ /**
11212
+ * Fallback when every send this turn was rejected by a policy gate.
11213
+ * `send` releases the last rejected message, `skip` records a skip.
11214
+ * @default "send"
11215
+ */
11216
+ onPolicyRejectExhausted: import_v49.z.enum(["send", "skip"]).optional()
11217
+ });
10905
11218
  var SchedulingDelaySchema = import_v49.z.object({
10906
11219
  /** Time unit amount */
10907
11220
  amount: import_v49.z.number(),
@@ -10949,15 +11262,53 @@ var BehaviorConfigV3Schema = import_v49.z.object({
10949
11262
  * Scheduling behavior - controls when the agent schedules follow-up messages.
10950
11263
  * Patterns define triggers like "user indicates they'll return later".
10951
11264
  */
10952
- scheduling: SchedulingBehaviorConfigSchema.optional()
11265
+ scheduling: SchedulingBehaviorConfigSchema.optional(),
11266
+ /**
11267
+ * Conversation policy - which conversational rules this agent wants, and
11268
+ * whether each is stated in the prompt or enforced at runtime.
11269
+ */
11270
+ conversation: ConversationBehaviorConfigSchema.optional(),
11271
+ /**
11272
+ * Mention a long gap since the last message when one has passed.
11273
+ * @default false
11274
+ */
11275
+ acknowledgeTimeGaps: import_v49.z.boolean().optional(),
11276
+ /**
11277
+ * Gap that counts as long for `acknowledgeTimeGaps`, e.g. "3 days".
11278
+ */
11279
+ timeGapThreshold: import_v49.z.string().optional()
11280
+ });
11281
+ var AgentSettingV3Schema = import_v49.z.object({
11282
+ /** Setting key, resolved against the install's AgentConfig and env. */
11283
+ key: import_v49.z.string().min(1),
11284
+ /** Human label shown to the model and in the console. */
11285
+ label: import_v49.z.string().optional(),
11286
+ /**
11287
+ * Prompt guidance for this setting. `{{value}}` interpolates the install
11288
+ * value and `{{workplace}}` the workplace name.
11289
+ */
11290
+ guidance: import_v49.z.string().optional(),
11291
+ /**
11292
+ * Marks a setting as supplying a platform value rather than prose.
11293
+ * `persona_name` overrides the agent's persona name for this install.
11294
+ */
11295
+ role: import_v49.z.enum(["persona_name"]).optional()
10953
11296
  });
10954
11297
  var PromptsConfigV3Schema = import_v49.z.object({
10955
11298
  /** Main system prompt with workflow instructions */
10956
11299
  system: import_v49.z.string().optional(),
10957
- /** Injected during second pass when skills were loaded but tools not used */
10958
- recovery: import_v49.z.string().optional(),
10959
- /** Injected during follow-up passes when context needs updating */
10960
- followUp: import_v49.z.string().optional(),
11300
+ /**
11301
+ * Injected during second pass when skills were loaded but tools not used.
11302
+ * @deprecated Ignored on the durable turn path; only the legacy stream path
11303
+ * reads it. Put the guidance in `system` instead.
11304
+ */
11305
+ recovery: import_v49.z.string().optional().describe("Not implemented"),
11306
+ /**
11307
+ * Injected during follow-up passes when context needs updating.
11308
+ * @deprecated Ignored on the durable turn path; only the legacy stream path
11309
+ * reads it. Put the guidance in `system` instead.
11310
+ */
11311
+ followUp: import_v49.z.string().optional().describe("Not implemented"),
10961
11312
  /** Thread list title generation (system + user template with {{var}} placeholders) */
10962
11313
  titleEnrichment: import_v49.z.object({
10963
11314
  system: import_v49.z.string().optional(),
@@ -10977,18 +11328,23 @@ var AgentYAMLV3Schema = import_v49.z.object({
10977
11328
  // Tools - Always-available tools before any skill loads
10978
11329
  // Examples: system:settings:business_information:get
10979
11330
  tools: import_v49.z.array(AgentToolRefSchema).optional(),
11331
+ /**
11332
+ * Settings - Install-scoped values the agent asks for by key.
11333
+ * Rendered into the prompt with the label and guidance declared here.
11334
+ */
11335
+ settings: import_v49.z.array(AgentSettingV3Schema).optional(),
10980
11336
  /**
10981
11337
  * Events - When the agent activates
10982
11338
  * @deprecated Not yet implemented - this is a planned feature for event-driven agents.
10983
11339
  * Fields are accepted but not used at runtime.
10984
11340
  */
10985
- events: EventsConfigSchema.optional(),
11341
+ events: EventsConfigSchema.optional().describe("Not implemented"),
10986
11342
  /**
10987
11343
  * Memory - How the agent remembers
10988
11344
  * @deprecated Not yet implemented - this is a planned feature.
10989
11345
  * Fields are accepted but not used at runtime.
10990
11346
  */
10991
- memory: MemoryConfigV3Schema.optional(),
11347
+ memory: MemoryConfigV3Schema.optional().describe("Not implemented"),
10992
11348
  // Policies - Business rules for the agent
10993
11349
  policies: PoliciesConfigV3Schema.optional(),
10994
11350
  // Runtime - Execution configuration (model + personaModel)
@@ -11031,6 +11387,7 @@ Commands:
11031
11387
  versions List versions of an agent
11032
11388
  ab Set A/B testing traffic weights
11033
11389
  rollback Rollback to a previous version
11390
+ test Run a playground turn (same path as the web playground)
11034
11391
 
11035
11392
  List Options:
11036
11393
  --workplace, -w Workplace subdomain (required)
@@ -11041,13 +11398,15 @@ Get Options:
11041
11398
  --json Output as JSON
11042
11399
 
11043
11400
  Deploy Options:
11044
- --file, -f Path to agent definition file (.agent.yml or .agent.json)
11401
+ --file, -f Path to agent definition file (.agent.yml, .agent.json, or .agent-pack.yaml)
11045
11402
  --workplace, -w Workplace subdomain (required)
11046
11403
  --draft Deploy as draft (not published)
11404
+ --upload Force multipart upload (for large files >500KB)
11047
11405
  --json Output as JSON
11048
11406
 
11049
- Supports both legacy (multi-stage) and v3 (skills-based) agent formats.
11050
- V3 agents use skills, events, and memory instead of stages.
11407
+ Supports v3 (skills-based) agents and agent-pack bundles.
11408
+ Agent-pack files (.agent-pack.yaml) include embedded skills, evaluators, and scenarios.
11409
+ Files larger than 500KB automatically use multipart upload.
11051
11410
 
11052
11411
  Publish Options:
11053
11412
  --workplace, -w Workplace subdomain (required)
@@ -11070,6 +11429,16 @@ Rollback Options:
11070
11429
  --to Version number to rollback to
11071
11430
  --json Output as JSON
11072
11431
 
11432
+ Test Options:
11433
+ --workplace, -w Workplace subdomain (required)
11434
+ --agent, -a Agent handle (required to start a session)
11435
+ --thread, -t Continue an existing playground thread
11436
+ --message, -m User turn to send (omit to inspect the thread)
11437
+ --now ISO clock the agent should treat as now
11438
+ --contact Contact ID to impersonate
11439
+ --installation App installation ID (APP agents)
11440
+ --json Output as JSON
11441
+
11073
11442
  Examples:
11074
11443
  # List all agents in a workplace
11075
11444
  skedyul agents list --workplace gym-demo
@@ -11094,6 +11463,15 @@ Examples:
11094
11463
 
11095
11464
  # Rollback to previous version
11096
11465
  skedyul agents rollback sales-agent --workplace gym-demo --to 1
11466
+
11467
+ # Playground turn (TESTING thread + runThreadAgentWorkflow)
11468
+ skedyul agents test --agent sales-agent --workplace gym-demo --message "Hey"
11469
+
11470
+ # Continue the thread and pin studio now
11471
+ skedyul agents test --thread th_xxx --workplace gym-demo --message "I'm sick" --now 2026-09-11T22:00:00+10:00
11472
+
11473
+ # Inspect scheduled messages
11474
+ skedyul agents test --thread th_xxx --workplace gym-demo
11097
11475
  `);
11098
11476
  }
11099
11477
  async function getWorkplaceToken4(workplaceSubdomain, serverUrl, cliToken) {
@@ -11344,12 +11722,14 @@ async function handleUpdate2(args2) {
11344
11722
  console.log('Note: "update" is deprecated. Use "deploy" instead.');
11345
11723
  await handleDeploy(args2);
11346
11724
  }
11725
+ var MULTIPART_THRESHOLD = 500 * 1024;
11347
11726
  async function handleDeploy(args2) {
11348
11727
  const { flags } = parseArgs(args2);
11349
11728
  const filePath = flags.file || flags.f;
11350
11729
  const workplace = flags.workplace || flags.w;
11351
11730
  const isDraft = Boolean(flags.draft);
11352
11731
  const jsonOutput = Boolean(flags.json);
11732
+ const forceUpload = Boolean(flags.upload);
11353
11733
  if (!filePath) {
11354
11734
  console.error("Error: --file (-f) is required");
11355
11735
  console.error("Usage: skedyul agents deploy --file ./agent.yml --workplace gym-demo");
@@ -11363,11 +11743,25 @@ async function handleDeploy(args2) {
11363
11743
  let agent;
11364
11744
  let content;
11365
11745
  let isV3;
11746
+ let isAgentPack = false;
11366
11747
  try {
11367
- const result = await loadAgentFile(filePath);
11368
- agent = result.agent;
11369
- content = result.content;
11370
- isV3 = result.isV3;
11748
+ const absolutePath = path22.resolve(filePath);
11749
+ content = fs22.readFileSync(absolutePath, "utf-8");
11750
+ const rawContent = (0, import_yaml2.parse)(content);
11751
+ isAgentPack = rawContent?.kind === "skedyul.agent-pack";
11752
+ if (isAgentPack) {
11753
+ const agentObj = rawContent.agent;
11754
+ if (!agentObj) {
11755
+ throw new Error('Agent-pack missing "agent" field');
11756
+ }
11757
+ agent = agentObj;
11758
+ isV3 = true;
11759
+ } else {
11760
+ const result = await loadAgentFile(filePath);
11761
+ agent = result.agent;
11762
+ content = result.content;
11763
+ isV3 = result.isV3;
11764
+ }
11371
11765
  } catch (error) {
11372
11766
  if (jsonOutput) {
11373
11767
  console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
@@ -11388,37 +11782,46 @@ async function handleDeploy(args2) {
11388
11782
  }
11389
11783
  process.exit(1);
11390
11784
  }
11785
+ const fileSize = Buffer.byteLength(content, "utf8");
11786
+ const useMultipart = forceUpload || fileSize > MULTIPART_THRESHOLD || isAgentPack;
11391
11787
  if (!jsonOutput) {
11392
11788
  let agentType;
11393
- if (isV3) {
11789
+ if (isAgentPack) {
11790
+ agentType = "agent-pack (bundled)";
11791
+ } else if (isV3) {
11394
11792
  agentType = "v3 (skills-based)";
11395
11793
  } else {
11396
11794
  agentType = isMultiStageAgent(agent) ? "multi-stage" : "single-stage";
11397
11795
  }
11398
11796
  console.log("");
11399
11797
  console.log(`Deploying ${agentType} agent "${agent.name}" to ${workplace}${isDraft ? " (draft)" : ""}`);
11798
+ if (useMultipart) {
11799
+ console.log(` Using multipart upload (${(fileSize / 1024).toFixed(1)} KB)`);
11800
+ }
11400
11801
  console.log("");
11401
11802
  }
11402
11803
  try {
11403
- const response = await fetch(`${serverUrl}/api/cli/agents`, {
11404
- method: "POST",
11405
- headers: {
11406
- "Content-Type": "application/json",
11407
- Authorization: `Bearer ${token}`
11408
- },
11409
- body: JSON.stringify({
11804
+ let result;
11805
+ if (useMultipart) {
11806
+ result = await deployAgentMultipart({
11807
+ serverUrl,
11808
+ token,
11410
11809
  workplaceId: workplaceToken.workplaceId,
11411
11810
  subdomain: workplaceToken.workplaceSubdomain,
11412
- action: "deploy",
11413
- yamlContent: content,
11811
+ filePath,
11812
+ content,
11414
11813
  publish: !isDraft
11415
- })
11416
- });
11417
- if (!response.ok) {
11418
- const errorData = await response.json().catch(() => ({}));
11419
- throw new Error(errorData.error || `Request failed: ${response.statusText}`);
11814
+ });
11815
+ } else {
11816
+ result = await deployAgentJson({
11817
+ serverUrl,
11818
+ token,
11819
+ workplaceId: workplaceToken.workplaceId,
11820
+ subdomain: workplaceToken.workplaceSubdomain,
11821
+ content,
11822
+ publish: !isDraft
11823
+ });
11420
11824
  }
11421
- const result = await response.json();
11422
11825
  if (!result.success) {
11423
11826
  throw new Error(result.error || "Failed to deploy agent");
11424
11827
  }
@@ -11446,6 +11849,12 @@ async function handleDeploy(args2) {
11446
11849
  console.log(` Tools: ${result.agent.tools.length} bound`);
11447
11850
  }
11448
11851
  }
11852
+ if (result.deployedSkills?.length) {
11853
+ console.log(` Skills: ${result.deployedSkills.length} deployed`);
11854
+ for (const skill of result.deployedSkills) {
11855
+ console.log(` - ${skill.handle} v${skill.version}`);
11856
+ }
11857
+ }
11449
11858
  console.log("");
11450
11859
  } catch (error) {
11451
11860
  if (jsonOutput) {
@@ -11456,6 +11865,50 @@ async function handleDeploy(args2) {
11456
11865
  process.exit(1);
11457
11866
  }
11458
11867
  }
11868
+ async function deployAgentJson(params) {
11869
+ const response = await fetch(`${params.serverUrl}/api/cli/agents`, {
11870
+ method: "POST",
11871
+ headers: {
11872
+ "Content-Type": "application/json",
11873
+ Authorization: `Bearer ${params.token}`
11874
+ },
11875
+ body: JSON.stringify({
11876
+ workplaceId: params.workplaceId,
11877
+ subdomain: params.subdomain,
11878
+ action: "deploy",
11879
+ yamlContent: params.content,
11880
+ publish: params.publish
11881
+ })
11882
+ });
11883
+ if (!response.ok) {
11884
+ const errorData = await response.json().catch(() => ({}));
11885
+ throw new Error(errorData.error || `Request failed: ${response.statusText}`);
11886
+ }
11887
+ return response.json();
11888
+ }
11889
+ async function deployAgentMultipart(params) {
11890
+ const formData = new FormData();
11891
+ const blob = new Blob([params.content], { type: "text/yaml" });
11892
+ const fileName = path22.basename(params.filePath);
11893
+ formData.append("file", blob, fileName);
11894
+ formData.append("metadata", JSON.stringify({
11895
+ workplaceId: params.workplaceId,
11896
+ subdomain: params.subdomain,
11897
+ publish: params.publish
11898
+ }));
11899
+ const response = await fetch(`${params.serverUrl}/api/cli/agents/upload`, {
11900
+ method: "POST",
11901
+ headers: {
11902
+ Authorization: `Bearer ${params.token}`
11903
+ },
11904
+ body: formData
11905
+ });
11906
+ if (!response.ok) {
11907
+ const errorData = await response.json().catch(() => ({}));
11908
+ throw new Error(errorData.error || `Request failed: ${response.statusText}`);
11909
+ }
11910
+ return response.json();
11911
+ }
11459
11912
  async function handlePublish(args2) {
11460
11913
  const { flags, positional } = parseArgs(args2);
11461
11914
  const handle = positional[0];
@@ -11746,6 +12199,132 @@ async function handleRollback(args2) {
11746
12199
  process.exit(1);
11747
12200
  }
11748
12201
  }
12202
+ async function handleTest(args2) {
12203
+ const { flags } = parseArgs(args2);
12204
+ const workplace = flags.workplace || flags.w;
12205
+ const handle = flags.agent || flags.a;
12206
+ const threadId = flags.thread || flags.t;
12207
+ const message = flags.message || flags.m;
12208
+ const currentTime = flags.now;
12209
+ const contactId = flags.contact;
12210
+ const appInstallationId = flags.installation;
12211
+ const jsonOutput = Boolean(flags.json);
12212
+ if (!workplace) {
12213
+ console.error("Error: --workplace (-w) is required");
12214
+ console.error(
12215
+ 'Usage: skedyul agents test --workplace gym-demo --agent sales-agent --message "Hey"'
12216
+ );
12217
+ process.exit(1);
12218
+ }
12219
+ if (!threadId && !handle) {
12220
+ console.error("Error: --agent (-a) is required when starting a session");
12221
+ console.error(
12222
+ 'Usage: skedyul agents test --workplace gym-demo --agent sales-agent --message "Hey"'
12223
+ );
12224
+ process.exit(1);
12225
+ }
12226
+ if (!message && !threadId) {
12227
+ console.error("Error: --message (-m) is required when starting a session");
12228
+ process.exit(1);
12229
+ }
12230
+ const { token, serverUrl } = ensureAuth3();
12231
+ const ctx = { token, serverUrl };
12232
+ try {
12233
+ if (!message) {
12234
+ const result2 = await inspectPlaygroundSession(ctx, workplace, threadId);
12235
+ if (!result2.success) {
12236
+ throw new Error(result2.error || "Failed to inspect playground session");
12237
+ }
12238
+ if (jsonOutput) {
12239
+ console.log(JSON.stringify(result2, null, 2));
12240
+ return;
12241
+ }
12242
+ console.log("");
12243
+ console.log(`Playground ${result2.threadId}`);
12244
+ if (result2.agentHandle || result2.agentName) {
12245
+ console.log(`Agent ${result2.agentHandle ?? result2.agentName}`);
12246
+ }
12247
+ console.log("");
12248
+ for (const row of result2.messages) {
12249
+ const who = row.role === "user" ? "USER " : "AGENT";
12250
+ console.log(`${who} ${row.content}`);
12251
+ }
12252
+ if (result2.scheduledMessages.length === 0) {
12253
+ console.log("");
12254
+ console.log("SCHED (none)");
12255
+ } else {
12256
+ console.log("");
12257
+ for (const row of result2.scheduledMessages) {
12258
+ console.log(`SCHED ${row.status} ${row.scheduledAt} ${row.content}`);
12259
+ }
12260
+ }
12261
+ for (const row of result2.sendCalls) {
12262
+ if (row.sendAt != null) {
12263
+ console.log(`SENDAT ${String(row.sendAt)} ${row.content ?? ""}`);
12264
+ }
12265
+ }
12266
+ if (result2.schedulingCheck) {
12267
+ console.log(
12268
+ `CHECK ${result2.schedulingCheck.passed ? "pass" : "fail"} ${result2.schedulingCheck.reason}`
12269
+ );
12270
+ }
12271
+ console.log("");
12272
+ return;
12273
+ }
12274
+ const result = await sendPlaygroundTurn(ctx, workplace, {
12275
+ handle,
12276
+ threadId,
12277
+ message,
12278
+ currentTime,
12279
+ contactId,
12280
+ appInstallationId
12281
+ });
12282
+ if (!result.success) {
12283
+ throw new Error(result.error || "Failed to run playground turn");
12284
+ }
12285
+ if (jsonOutput) {
12286
+ console.log(JSON.stringify(result, null, 2));
12287
+ return;
12288
+ }
12289
+ console.log("");
12290
+ console.log(`THREAD ${result.threadId}`);
12291
+ console.log(`STATUS ${result.status}`);
12292
+ if (result.currentTime) {
12293
+ console.log(`NOW ${result.currentTime}`);
12294
+ }
12295
+ if (result.agentResponse) {
12296
+ console.log(`AGENT ${result.agentResponse}`);
12297
+ } else {
12298
+ console.log("AGENT (no immediate SMS)");
12299
+ }
12300
+ if (result.scheduledMessages.length === 0) {
12301
+ console.log("SCHED (none)");
12302
+ } else {
12303
+ for (const row of result.scheduledMessages) {
12304
+ console.log(`SCHED ${row.status} ${row.scheduledAt} ${row.content}`);
12305
+ }
12306
+ }
12307
+ if (result.schedulingCheck) {
12308
+ console.log(
12309
+ `CHECK ${result.schedulingCheck.passed ? "pass" : "fail"} ${result.schedulingCheck.reason}`
12310
+ );
12311
+ }
12312
+ console.log("");
12313
+ } catch (error) {
12314
+ if (jsonOutput) {
12315
+ console.log(
12316
+ JSON.stringify({
12317
+ error: error instanceof Error ? error.message : String(error)
12318
+ })
12319
+ );
12320
+ } else {
12321
+ console.error(
12322
+ `Error: ${error instanceof Error ? error.message : String(error)}`
12323
+ );
12324
+ }
12325
+ process.exit(1);
12326
+ }
12327
+ }
11749
12328
  async function agentsCommand(args2) {
11750
12329
  const subcommand = args2[0];
11751
12330
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
@@ -11781,6 +12360,9 @@ async function agentsCommand(args2) {
11781
12360
  case "rollback":
11782
12361
  await handleRollback(subArgs);
11783
12362
  break;
12363
+ case "test":
12364
+ await handleTest(subArgs);
12365
+ break;
11784
12366
  default:
11785
12367
  console.error(`Error: Unknown subcommand: ${subcommand}`);
11786
12368
  console.error("Run 'skedyul agents --help' for usage information.");
@@ -12979,8 +13561,11 @@ Deploy Options:
12979
13561
  --workplace, -w Workplace subdomain (required)
12980
13562
  --draft Deploy as draft (not published)
12981
13563
  --label Version label (e.g., "experiment-shorter-prompts")
13564
+ --upload Force multipart upload (for large files >500KB)
12982
13565
  --json Output as JSON
12983
13566
 
13567
+ Note: Files larger than 500KB automatically use multipart upload.
13568
+
12984
13569
  Publish Options:
12985
13570
  --workplace, -w Workplace subdomain (required)
12986
13571
  --version, -v Version number to publish (required)
@@ -13187,6 +13772,7 @@ async function handleGet3(args2) {
13187
13772
  process.exit(1);
13188
13773
  }
13189
13774
  }
13775
+ var MULTIPART_THRESHOLD2 = 500 * 1024;
13190
13776
  async function handleDeploy2(args2) {
13191
13777
  const { flags } = parseArgs(args2);
13192
13778
  const filePath = flags.file || flags.f;
@@ -13194,6 +13780,7 @@ async function handleDeploy2(args2) {
13194
13780
  const isDraft = Boolean(flags.draft);
13195
13781
  const versionLabel = flags.label;
13196
13782
  const jsonOutput = Boolean(flags.json);
13783
+ const forceUpload = Boolean(flags.upload);
13197
13784
  if (!filePath) {
13198
13785
  console.error("Error: --file (-f) is required");
13199
13786
  console.error("Usage: skedyul skills deploy --file ./skill.yml --workplace crux");
@@ -13230,36 +13817,38 @@ async function handleDeploy2(args2) {
13230
13817
  }
13231
13818
  process.exit(1);
13232
13819
  }
13820
+ const fileSize = Buffer.byteLength(content, "utf8");
13821
+ const useMultipart = forceUpload || fileSize > MULTIPART_THRESHOLD2;
13233
13822
  if (!jsonOutput) {
13234
13823
  console.log("");
13235
13824
  console.log(`Deploying skill "${skill.name}" to ${workplace}${isDraft ? " (draft)" : ""}`);
13825
+ if (useMultipart) {
13826
+ console.log(` Using multipart upload (${(fileSize / 1024).toFixed(1)} KB)`);
13827
+ }
13236
13828
  console.log("");
13237
13829
  }
13238
13830
  try {
13239
- const response = await fetch(`${serverUrl}/api/cli/skills`, {
13240
- method: "POST",
13241
- headers: {
13242
- "Content-Type": "application/json",
13243
- Authorization: `Bearer ${token}`
13244
- },
13245
- body: JSON.stringify({
13246
- action: "deploy",
13831
+ let result;
13832
+ if (useMultipart) {
13833
+ result = await deploySkillMultipart({
13834
+ serverUrl,
13835
+ token,
13247
13836
  workplaceId: workplaceToken.workplaceId,
13248
- yamlContent: content,
13837
+ filePath,
13838
+ content,
13249
13839
  publish: !isDraft,
13250
13840
  versionLabel
13251
- })
13252
- });
13253
- if (!response.ok) {
13254
- const errorData = await response.json().catch(() => ({}));
13255
- if (errorData.details) {
13256
- const detailsStr = errorData.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
13257
- throw new Error(`${errorData.error}
13258
- ${detailsStr}`);
13259
- }
13260
- throw new Error(errorData.error || `Request failed: ${response.statusText}`);
13841
+ });
13842
+ } else {
13843
+ result = await deploySkillJson({
13844
+ serverUrl,
13845
+ token,
13846
+ workplaceId: workplaceToken.workplaceId,
13847
+ content,
13848
+ publish: !isDraft,
13849
+ versionLabel
13850
+ });
13261
13851
  }
13262
- const result = await response.json();
13263
13852
  if (!result.success) {
13264
13853
  throw new Error(result.error || "Failed to deploy skill");
13265
13854
  }
@@ -13286,6 +13875,60 @@ ${detailsStr}`);
13286
13875
  process.exit(1);
13287
13876
  }
13288
13877
  }
13878
+ async function deploySkillJson(params) {
13879
+ const response = await fetch(`${params.serverUrl}/api/cli/skills`, {
13880
+ method: "POST",
13881
+ headers: {
13882
+ "Content-Type": "application/json",
13883
+ Authorization: `Bearer ${params.token}`
13884
+ },
13885
+ body: JSON.stringify({
13886
+ action: "deploy",
13887
+ workplaceId: params.workplaceId,
13888
+ yamlContent: params.content,
13889
+ publish: params.publish,
13890
+ versionLabel: params.versionLabel
13891
+ })
13892
+ });
13893
+ if (!response.ok) {
13894
+ const errorData = await response.json().catch(() => ({}));
13895
+ if (errorData.details) {
13896
+ const detailsStr = errorData.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
13897
+ throw new Error(`${errorData.error}
13898
+ ${detailsStr}`);
13899
+ }
13900
+ throw new Error(errorData.error || `Request failed: ${response.statusText}`);
13901
+ }
13902
+ return response.json();
13903
+ }
13904
+ async function deploySkillMultipart(params) {
13905
+ const formData = new FormData();
13906
+ const blob = new Blob([params.content], { type: "text/yaml" });
13907
+ const fileName = path24.basename(params.filePath);
13908
+ formData.append("file", blob, fileName);
13909
+ formData.append("metadata", JSON.stringify({
13910
+ workplaceId: params.workplaceId,
13911
+ publish: params.publish,
13912
+ versionLabel: params.versionLabel
13913
+ }));
13914
+ const response = await fetch(`${params.serverUrl}/api/cli/skills/upload`, {
13915
+ method: "POST",
13916
+ headers: {
13917
+ Authorization: `Bearer ${params.token}`
13918
+ },
13919
+ body: formData
13920
+ });
13921
+ if (!response.ok) {
13922
+ const errorData = await response.json().catch(() => ({}));
13923
+ if (errorData.details) {
13924
+ const detailsStr = errorData.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
13925
+ throw new Error(`${errorData.error}
13926
+ ${detailsStr}`);
13927
+ }
13928
+ throw new Error(errorData.error || `Request failed: ${response.statusText}`);
13929
+ }
13930
+ return response.json();
13931
+ }
13289
13932
  async function handlePublish2(args2) {
13290
13933
  const { flags, positional } = parseArgs(args2);
13291
13934
  const handle = positional[0];
@@ -14381,7 +15024,7 @@ COMMANDS
14381
15024
  invoke Invoke a tool on a hosted app version
14382
15025
  instances Manage CRM instances (list, get, create, update, delete)
14383
15026
  crm Manage CRM schemas (push, pull, diff, models)
14384
- agents Manage agents (list, get, push)
15027
+ agents Manage agents (list, get, deploy, test)
14385
15028
  skills Manage skills (list, get, deploy, publish, versions, delete)
14386
15029
  workflows Manage workflows (list, get, deploy, run, publish, pull)
14387
15030
  event Emit test app events to the event bus (CLI testing)