skedyul 1.7.28 → 1.7.30

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", {
@@ -2861,6 +2889,17 @@ async function invokeTool(ctx, input) {
2861
2889
  timeout: input.timeout
2862
2890
  });
2863
2891
  }
2892
+ async function getDeployment(ctx, input) {
2893
+ return callCliApi(cliApi(ctx), "/app-deploy", void 0, {
2894
+ method: "GET",
2895
+ query: {
2896
+ deploymentId: input.deploymentId,
2897
+ tail: input.tail != null ? String(input.tail) : void 0,
2898
+ logs: input.includeLogs === false ? "false" : "true",
2899
+ errorsOnly: input.errorsOnly ? "true" : void 0
2900
+ }
2901
+ });
2902
+ }
2864
2903
 
2865
2904
  // src/cli/commands/invoke-remote.ts
2866
2905
  function getMimeType2(filePath) {
@@ -10218,6 +10257,22 @@ var ToolBindingZ = import_v46.z.union([
10218
10257
  executionMode: import_v46.z.enum(["ALLOW_ALL", "REQUIRE_APPROVAL"]).optional(),
10219
10258
  /** Approval timeout in ms (default: 86400000 = 1 day) */
10220
10259
  approvalTimeoutMs: import_v46.z.number().optional()
10260
+ }),
10261
+ import_v46.z.object({
10262
+ kind: import_v46.z.literal("APP_ENTITY"),
10263
+ /** App handle (e.g., 'bft') */
10264
+ appHandle: import_v46.z.string(),
10265
+ /** Entity handle (e.g., 'member', 'booking') */
10266
+ entity: import_v46.z.string(),
10267
+ /** Operation: get, list, update, create, delete */
10268
+ operation: import_v46.z.enum(["get", "list", "update", "create", "delete"]),
10269
+ handle: import_v46.z.string().optional(),
10270
+ displayName: import_v46.z.string().optional(),
10271
+ description: import_v46.z.string().optional(),
10272
+ /** Execution mode: ALLOW_ALL (default) or REQUIRE_APPROVAL */
10273
+ executionMode: import_v46.z.enum(["ALLOW_ALL", "REQUIRE_APPROVAL"]).optional(),
10274
+ /** Approval timeout in ms (default: 86400000 = 1 day) */
10275
+ approvalTimeoutMs: import_v46.z.number().optional()
10221
10276
  })
10222
10277
  ]);
10223
10278
  var StageBaseZ = import_v46.z.object({
@@ -10525,6 +10580,40 @@ var CRMModelFieldRequirementsSchema = import_v47.z.object({
10525
10580
  var CRMContextSchema = import_v47.z.object({
10526
10581
  models: import_v47.z.record(import_v47.z.string(), CRMModelFieldRequirementsSchema)
10527
10582
  });
10583
+ var EntityOperationSchema = import_v47.z.enum(["get", "list", "update", "create", "delete"]);
10584
+ var EntityPoliciesSchema = import_v47.z.record(
10585
+ import_v47.z.string(),
10586
+ // entity handle (e.g., "member", "booking")
10587
+ import_v47.z.array(EntityOperationSchema)
10588
+ // allowed operations
10589
+ );
10590
+ var EvaluatorPreconditionsSchema = import_v47.z.object({
10591
+ toolSucceeded: import_v47.z.boolean().optional(),
10592
+ anyWrittenFields: import_v47.z.array(import_v47.z.string()).optional(),
10593
+ noneOfWrittenFields: import_v47.z.array(import_v47.z.string()).optional(),
10594
+ crm: import_v47.z.array(
10595
+ import_v47.z.object({
10596
+ field: import_v47.z.string(),
10597
+ op: import_v47.z.enum(["eq", "in", "empty", "notEmpty"]),
10598
+ value: import_v47.z.unknown().optional()
10599
+ })
10600
+ ).optional(),
10601
+ skillsLoaded: import_v47.z.array(import_v47.z.string()).optional(),
10602
+ skillsNotLoaded: import_v47.z.array(import_v47.z.string()).optional()
10603
+ });
10604
+ var SkillEvaluatorSchema = import_v47.z.object({
10605
+ name: import_v47.z.string(),
10606
+ description: import_v47.z.string().optional(),
10607
+ target: import_v47.z.enum(["message", "tool", "entity"]),
10608
+ selector: import_v47.z.string().optional(),
10609
+ criteria: import_v47.z.string(),
10610
+ passThreshold: import_v47.z.number().optional(),
10611
+ checks: import_v47.z.object({
10612
+ requiredArgs: import_v47.z.array(import_v47.z.string()).optional(),
10613
+ forbiddenArgValues: import_v47.z.record(import_v47.z.string(), import_v47.z.array(import_v47.z.unknown())).optional()
10614
+ }).optional(),
10615
+ preconditions: EvaluatorPreconditionsSchema.optional()
10616
+ });
10528
10617
  var SkillYAMLSchema = import_v47.z.object({
10529
10618
  // Schema version
10530
10619
  $schema: import_v47.z.string().optional(),
@@ -10535,10 +10624,17 @@ var SkillYAMLSchema = import_v47.z.object({
10535
10624
  description: import_v47.z.string().optional(),
10536
10625
  // Instructions injected into agent system prompt
10537
10626
  instructions: import_v47.z.string(),
10627
+ // Entity policies - declares which entity operations this skill needs
10628
+ // At runtime, these are resolved to CRM tools via AppCrmMap
10629
+ entityPolicies: EntityPoliciesSchema.optional(),
10538
10630
  // Tool configuration - supports both v1 and v2 formats
10631
+ // Use for non-entity tools (MCP tools, system tools)
10539
10632
  tools: SkillToolsSchema.optional(),
10540
10633
  // CRM context - specifies which models/fields to include in schema
10541
10634
  crmContext: CRMContextSchema.optional(),
10635
+ ownedFields: import_v47.z.array(import_v47.z.string()).optional(),
10636
+ // Quality evaluators owned by this skill
10637
+ evaluators: import_v47.z.array(SkillEvaluatorSchema).optional(),
10542
10638
  // Few-shot examples
10543
10639
  examples: import_v47.z.array(SkillExampleSchema).optional()
10544
10640
  });
@@ -10549,8 +10645,11 @@ var SkillYAMLV2Schema = import_v47.z.object({
10549
10645
  version: import_v47.z.string().optional(),
10550
10646
  description: import_v47.z.string().optional(),
10551
10647
  instructions: import_v47.z.string(),
10648
+ entityPolicies: EntityPoliciesSchema.optional(),
10552
10649
  tools: import_v47.z.array(SkillToolDefinitionSchema).optional(),
10553
10650
  crmContext: CRMContextSchema.optional(),
10651
+ ownedFields: import_v47.z.array(import_v47.z.string()).optional(),
10652
+ evaluators: import_v47.z.array(SkillEvaluatorSchema).optional(),
10554
10653
  examples: import_v47.z.array(SkillExampleSchema).optional()
10555
10654
  });
10556
10655
  var SkillVersionWeightSchema = import_v47.z.object({
@@ -10564,7 +10663,10 @@ var SkillRefSchema = import_v47.z.union([
10564
10663
  skill: import_v47.z.string(),
10565
10664
  description: import_v47.z.string().optional(),
10566
10665
  // For AI SDK Agent Skills discovery
10567
- /** Auto-inject this skill's instructions and tools on every agent turn */
10666
+ /**
10667
+ * @deprecated Ignored. The agent must call system:skill:load each turn.
10668
+ * Kept so existing agent YAML still validates.
10669
+ */
10568
10670
  alwaysLoad: import_v47.z.boolean().optional(),
10569
10671
  // Version selection (pick one):
10570
10672
  version: import_v47.z.number().optional(),
@@ -10603,6 +10705,7 @@ var LoadedSkillSchema = import_v47.z.object({
10603
10705
  name: import_v47.z.string(),
10604
10706
  instructions: import_v47.z.string().optional(),
10605
10707
  description: import_v47.z.string().optional(),
10708
+ entityPolicies: EntityPoliciesSchema.optional(),
10606
10709
  tools: import_v47.z.array(SkillToolDefinitionSchema).optional(),
10607
10710
  examples: import_v47.z.array(SkillExampleSchema).optional()
10608
10711
  });
@@ -10857,6 +10960,13 @@ var ResponsesBehaviorConfigSchema = import_v49.z.object({
10857
10960
  * @default false
10858
10961
  */
10859
10962
  allowSchedule: import_v49.z.boolean().optional(),
10963
+ /**
10964
+ * Whether the agent may skip sending when no reply is warranted.
10965
+ * Platform injects silence rules and exposes system:message:skip.
10966
+ * An explicit skip satisfies requireFinal. Off by default.
10967
+ * @default false
10968
+ */
10969
+ allowSilent: import_v49.z.boolean().optional(),
10860
10970
  /**
10861
10971
  * Message splitting configuration.
10862
10972
  * Controls whether and how the agent splits responses into multiple messages.
@@ -11004,6 +11114,7 @@ Commands:
11004
11114
  versions List versions of an agent
11005
11115
  ab Set A/B testing traffic weights
11006
11116
  rollback Rollback to a previous version
11117
+ test Run a playground turn (same path as the web playground)
11007
11118
 
11008
11119
  List Options:
11009
11120
  --workplace, -w Workplace subdomain (required)
@@ -11043,6 +11154,16 @@ Rollback Options:
11043
11154
  --to Version number to rollback to
11044
11155
  --json Output as JSON
11045
11156
 
11157
+ Test Options:
11158
+ --workplace, -w Workplace subdomain (required)
11159
+ --agent, -a Agent handle (required to start a session)
11160
+ --thread, -t Continue an existing playground thread
11161
+ --message, -m User turn to send (omit to inspect the thread)
11162
+ --now ISO clock the agent should treat as now
11163
+ --contact Contact ID to impersonate
11164
+ --installation App installation ID (APP agents)
11165
+ --json Output as JSON
11166
+
11046
11167
  Examples:
11047
11168
  # List all agents in a workplace
11048
11169
  skedyul agents list --workplace gym-demo
@@ -11067,6 +11188,15 @@ Examples:
11067
11188
 
11068
11189
  # Rollback to previous version
11069
11190
  skedyul agents rollback sales-agent --workplace gym-demo --to 1
11191
+
11192
+ # Playground turn (TESTING thread + runThreadAgentWorkflow)
11193
+ skedyul agents test --agent sales-agent --workplace gym-demo --message "Hey"
11194
+
11195
+ # Continue the thread and pin studio now
11196
+ skedyul agents test --thread th_xxx --workplace gym-demo --message "I'm sick" --now 2026-09-11T22:00:00+10:00
11197
+
11198
+ # Inspect scheduled messages
11199
+ skedyul agents test --thread th_xxx --workplace gym-demo
11070
11200
  `);
11071
11201
  }
11072
11202
  async function getWorkplaceToken4(workplaceSubdomain, serverUrl, cliToken) {
@@ -11719,6 +11849,132 @@ async function handleRollback(args2) {
11719
11849
  process.exit(1);
11720
11850
  }
11721
11851
  }
11852
+ async function handleTest(args2) {
11853
+ const { flags } = parseArgs(args2);
11854
+ const workplace = flags.workplace || flags.w;
11855
+ const handle = flags.agent || flags.a;
11856
+ const threadId = flags.thread || flags.t;
11857
+ const message = flags.message || flags.m;
11858
+ const currentTime = flags.now;
11859
+ const contactId = flags.contact;
11860
+ const appInstallationId = flags.installation;
11861
+ const jsonOutput = Boolean(flags.json);
11862
+ if (!workplace) {
11863
+ console.error("Error: --workplace (-w) is required");
11864
+ console.error(
11865
+ 'Usage: skedyul agents test --workplace gym-demo --agent sales-agent --message "Hey"'
11866
+ );
11867
+ process.exit(1);
11868
+ }
11869
+ if (!threadId && !handle) {
11870
+ console.error("Error: --agent (-a) is required when starting a session");
11871
+ console.error(
11872
+ 'Usage: skedyul agents test --workplace gym-demo --agent sales-agent --message "Hey"'
11873
+ );
11874
+ process.exit(1);
11875
+ }
11876
+ if (!message && !threadId) {
11877
+ console.error("Error: --message (-m) is required when starting a session");
11878
+ process.exit(1);
11879
+ }
11880
+ const { token, serverUrl } = ensureAuth3();
11881
+ const ctx = { token, serverUrl };
11882
+ try {
11883
+ if (!message) {
11884
+ const result2 = await inspectPlaygroundSession(ctx, workplace, threadId);
11885
+ if (!result2.success) {
11886
+ throw new Error(result2.error || "Failed to inspect playground session");
11887
+ }
11888
+ if (jsonOutput) {
11889
+ console.log(JSON.stringify(result2, null, 2));
11890
+ return;
11891
+ }
11892
+ console.log("");
11893
+ console.log(`Playground ${result2.threadId}`);
11894
+ if (result2.agentHandle || result2.agentName) {
11895
+ console.log(`Agent ${result2.agentHandle ?? result2.agentName}`);
11896
+ }
11897
+ console.log("");
11898
+ for (const row of result2.messages) {
11899
+ const who = row.role === "user" ? "USER " : "AGENT";
11900
+ console.log(`${who} ${row.content}`);
11901
+ }
11902
+ if (result2.scheduledMessages.length === 0) {
11903
+ console.log("");
11904
+ console.log("SCHED (none)");
11905
+ } else {
11906
+ console.log("");
11907
+ for (const row of result2.scheduledMessages) {
11908
+ console.log(`SCHED ${row.status} ${row.scheduledAt} ${row.content}`);
11909
+ }
11910
+ }
11911
+ for (const row of result2.sendCalls) {
11912
+ if (row.sendAt != null) {
11913
+ console.log(`SENDAT ${String(row.sendAt)} ${row.content ?? ""}`);
11914
+ }
11915
+ }
11916
+ if (result2.schedulingCheck) {
11917
+ console.log(
11918
+ `CHECK ${result2.schedulingCheck.passed ? "pass" : "fail"} ${result2.schedulingCheck.reason}`
11919
+ );
11920
+ }
11921
+ console.log("");
11922
+ return;
11923
+ }
11924
+ const result = await sendPlaygroundTurn(ctx, workplace, {
11925
+ handle,
11926
+ threadId,
11927
+ message,
11928
+ currentTime,
11929
+ contactId,
11930
+ appInstallationId
11931
+ });
11932
+ if (!result.success) {
11933
+ throw new Error(result.error || "Failed to run playground turn");
11934
+ }
11935
+ if (jsonOutput) {
11936
+ console.log(JSON.stringify(result, null, 2));
11937
+ return;
11938
+ }
11939
+ console.log("");
11940
+ console.log(`THREAD ${result.threadId}`);
11941
+ console.log(`STATUS ${result.status}`);
11942
+ if (result.currentTime) {
11943
+ console.log(`NOW ${result.currentTime}`);
11944
+ }
11945
+ if (result.agentResponse) {
11946
+ console.log(`AGENT ${result.agentResponse}`);
11947
+ } else {
11948
+ console.log("AGENT (no immediate SMS)");
11949
+ }
11950
+ if (result.scheduledMessages.length === 0) {
11951
+ console.log("SCHED (none)");
11952
+ } else {
11953
+ for (const row of result.scheduledMessages) {
11954
+ console.log(`SCHED ${row.status} ${row.scheduledAt} ${row.content}`);
11955
+ }
11956
+ }
11957
+ if (result.schedulingCheck) {
11958
+ console.log(
11959
+ `CHECK ${result.schedulingCheck.passed ? "pass" : "fail"} ${result.schedulingCheck.reason}`
11960
+ );
11961
+ }
11962
+ console.log("");
11963
+ } catch (error) {
11964
+ if (jsonOutput) {
11965
+ console.log(
11966
+ JSON.stringify({
11967
+ error: error instanceof Error ? error.message : String(error)
11968
+ })
11969
+ );
11970
+ } else {
11971
+ console.error(
11972
+ `Error: ${error instanceof Error ? error.message : String(error)}`
11973
+ );
11974
+ }
11975
+ process.exit(1);
11976
+ }
11977
+ }
11722
11978
  async function agentsCommand(args2) {
11723
11979
  const subcommand = args2[0];
11724
11980
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
@@ -11754,6 +12010,9 @@ async function agentsCommand(args2) {
11754
12010
  case "rollback":
11755
12011
  await handleRollback(subArgs);
11756
12012
  break;
12013
+ case "test":
12014
+ await handleTest(subArgs);
12015
+ break;
11757
12016
  default:
11758
12017
  console.error(`Error: Unknown subcommand: ${subcommand}`);
11759
12018
  console.error("Run 'skedyul agents --help' for usage information.");
@@ -14195,6 +14454,137 @@ async function eventCommand(args2) {
14195
14454
  }
14196
14455
  }
14197
14456
 
14457
+ // src/cli/commands/apps.ts
14458
+ init_utils();
14459
+ function printHelp24() {
14460
+ console.log(`
14461
+ skedyul apps - Inspect app installations and deployments
14462
+
14463
+ Usage:
14464
+ skedyul apps deployment --id <deploymentId> [options]
14465
+
14466
+ Required:
14467
+ --id, -i Deployment ID (dpl_...)
14468
+
14469
+ Options:
14470
+ --tail, -n Newest log lines (default: 100, max: 500)
14471
+ --errors-only Only print error/warn lines
14472
+ --no-logs Skip the log tail (FAILED still returns errors)
14473
+ --json Output the full JSON response
14474
+ --help, -h Show this help message
14475
+
14476
+ Examples:
14477
+ skedyul apps deployment --id dpl_abc123
14478
+ skedyul apps deployment --id dpl_abc123 --errors-only
14479
+ skedyul apps deployment --id dpl_abc123 --tail 200 --json
14480
+
14481
+ MCP equivalent: apps_get_deployment
14482
+ `);
14483
+ }
14484
+ function formatLogLine(entry) {
14485
+ const time = entry.timestamp ?? "";
14486
+ const level = (entry.level ?? "info").toUpperCase().padEnd(5);
14487
+ const message = entry.message ?? "";
14488
+ return [time, level, message].filter(Boolean).join(" ");
14489
+ }
14490
+ async function appsCommand(args2) {
14491
+ const { flags, positional } = parseArgs(args2);
14492
+ if (flags.help || flags.h) {
14493
+ printHelp24();
14494
+ return;
14495
+ }
14496
+ const subcommand = positional[0];
14497
+ if (subcommand !== "deployment") {
14498
+ printHelp24();
14499
+ process.exit(subcommand ? 1 : 0);
14500
+ }
14501
+ const deploymentId = String(flags.id ?? flags.i ?? positional[1] ?? "");
14502
+ if (!deploymentId) {
14503
+ console.error("Error: --id <deploymentId> is required");
14504
+ printHelp24();
14505
+ process.exit(1);
14506
+ }
14507
+ const credentials = getCredentials();
14508
+ if (!credentials?.token) {
14509
+ console.error("Not logged in. Run 'skedyul auth login' first.");
14510
+ process.exit(1);
14511
+ }
14512
+ const ctx = {
14513
+ token: credentials.token,
14514
+ serverUrl: credentials.serverUrl || getServerUrl()
14515
+ };
14516
+ const tailFlag = flags.tail ?? flags.n;
14517
+ const tail = typeof tailFlag === "string" && tailFlag.length > 0 ? Number(tailFlag) : void 0;
14518
+ const result = await getDeployment(ctx, {
14519
+ deploymentId,
14520
+ tail: Number.isFinite(tail) ? tail : void 0,
14521
+ includeLogs: flags["no-logs"] ? false : true,
14522
+ errorsOnly: Boolean(flags["errors-only"])
14523
+ });
14524
+ if (flags.json) {
14525
+ console.log(formatJson(result));
14526
+ if (result.error && !result.deploymentId) {
14527
+ process.exit(1);
14528
+ }
14529
+ return;
14530
+ }
14531
+ if (result.error && !result.deploymentId) {
14532
+ console.error(result.error);
14533
+ process.exit(1);
14534
+ }
14535
+ console.log(`Deployment ${result.deploymentId ?? deploymentId}`);
14536
+ console.log(`Status ${result.status ?? "unknown"}`);
14537
+ if (result.appHandle) {
14538
+ console.log(`App ${result.appHandle}`);
14539
+ }
14540
+ if (result.readyToInvoke != null) {
14541
+ console.log(`Ready ${result.readyToInvoke ? "yes" : "no"}`);
14542
+ }
14543
+ if (result.invokeEndpoint) {
14544
+ console.log(`Endpoint ${result.invokeEndpoint}`);
14545
+ }
14546
+ if (result.provision?.status) {
14547
+ console.log(
14548
+ `Provision ${result.provision.status}${result.provision.currentStep ? ` (${result.provision.currentStep})` : ""}`
14549
+ );
14550
+ }
14551
+ if (result.provision?.errorMessage) {
14552
+ console.error(`Provision error: ${result.provision.errorMessage}`);
14553
+ }
14554
+ if (result.pendingMigration) {
14555
+ console.error(
14556
+ "Pending CRM migration \u2014 approve it in Skedyul or via crm.approveMigration before the deploy can finish."
14557
+ );
14558
+ }
14559
+ if (result.lastError) {
14560
+ console.error(`Queue error: ${result.lastError}`);
14561
+ }
14562
+ if (result.hint) {
14563
+ console.error(result.hint);
14564
+ }
14565
+ if (result.logsError) {
14566
+ console.error(`Log fetch: ${result.logsError}`);
14567
+ }
14568
+ const errors = result.errors ?? [];
14569
+ if (errors.length > 0) {
14570
+ console.log("");
14571
+ console.log(`Errors (${errors.length})`);
14572
+ for (const entry of errors) {
14573
+ console.error(formatLogLine(entry));
14574
+ }
14575
+ }
14576
+ const logs = result.logs ?? [];
14577
+ if (logs.length > 0 && !flags["errors-only"]) {
14578
+ const total = result.logsTotal ?? logs.length;
14579
+ const label = result.logsTruncated ? `Logs (newest ${logs.length} of ${total})` : `Logs (${logs.length})`;
14580
+ console.log("");
14581
+ console.log(label);
14582
+ for (const entry of logs) {
14583
+ console.log(formatLogLine(entry));
14584
+ }
14585
+ }
14586
+ }
14587
+
14198
14588
  // src/cli/index.ts
14199
14589
  var args = process.argv.slice(2);
14200
14590
  function printUsage2() {
@@ -14223,10 +14613,11 @@ COMMANDS
14223
14613
  invoke Invoke a tool on a hosted app version
14224
14614
  instances Manage CRM instances (list, get, create, update, delete)
14225
14615
  crm Manage CRM schemas (push, pull, diff, models)
14226
- agents Manage agents (list, get, push)
14616
+ agents Manage agents (list, get, deploy, test)
14227
14617
  skills Manage skills (list, get, deploy, publish, versions, delete)
14228
14618
  workflows Manage workflows (list, get, deploy, run, publish, pull)
14229
14619
  event Emit test app events to the event bus (CLI testing)
14620
+ apps Inspect app deployments and build logs
14230
14621
  dev Development tools for building and testing apps locally
14231
14622
 
14232
14623
  GETTING STARTED
@@ -14286,6 +14677,7 @@ MORE HELP
14286
14677
  $ skedyul agents --help Show agent management commands
14287
14678
  $ skedyul skills --help Show skill management commands
14288
14679
  $ skedyul workflows --help Show workflow management commands
14680
+ $ skedyul apps --help Show app deployment log commands
14289
14681
  $ skedyul chat --help Show chat command options
14290
14682
  $ skedyul dev --help Show development commands
14291
14683
  $ skedyul <cmd> --help Show help for specific command
@@ -14444,6 +14836,10 @@ async function main() {
14444
14836
  await eventCommand(args.slice(1));
14445
14837
  return;
14446
14838
  }
14839
+ if (command === "apps") {
14840
+ await appsCommand(args.slice(1));
14841
+ return;
14842
+ }
14447
14843
  if (command === "build") {
14448
14844
  await buildCommand(args.slice(1));
14449
14845
  return;
@@ -99,4 +99,45 @@ export interface EntityDefinition extends BaseDefinition {
99
99
  * Used by install suggest/analyze — not applied automatically.
100
100
  */
101
101
  crmMapDefaults?: EntityCrmMapDefaults;
102
+ /**
103
+ * Platform situations this entity participates in, and the short MCP tool
104
+ * name to invoke for each. Omit a key to skip that situation.
105
+ * Core builds a typed payload (see CalendarWindowPullInput) — same pattern
106
+ * as channel `capabilities.messaging.send` + MessageSendInput.
107
+ */
108
+ tools?: EntityRuntimeTools;
109
+ /**
110
+ * Generic pre-save dialogs. Core/web render configured fields; the app
111
+ * declares copy, options, and when to show. Answers go on the CRM event
112
+ * envelope under the app handle (not into CRM scalar fields).
113
+ */
114
+ saveInteractions?: EntitySaveInteractionDefinition[];
115
+ }
116
+ export interface EntitySaveInteractionDefinition {
117
+ handle: string;
118
+ when: {
119
+ onCreate?: boolean;
120
+ onUpdate?: boolean;
121
+ fieldsChanged?: string[];
122
+ relationshipsChanged?: string[];
123
+ };
124
+ dialog: {
125
+ title: string;
126
+ message: string;
127
+ severity?: 'warning' | 'info';
128
+ /** Entity / contextField handles to render (type + options come from the entity). */
129
+ fields: string[];
130
+ defaults?: Record<string, string>;
131
+ };
132
+ }
133
+ /**
134
+ * Situation → short MCP tool name. Keys are platform contracts; values are
135
+ * the app's tool handle (not `app:{handle}:…`).
136
+ */
137
+ export interface EntityRuntimeTools {
138
+ /**
139
+ * CRM LIST calendar views, before reading instances.
140
+ * Invoked with CalendarWindowPullInput.
141
+ */
142
+ calendarWindowPull?: string;
102
143
  }