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/README.md +2 -2
- package/dist/.build-stamp +1 -1
- package/dist/cli/api/index.d.ts +3 -0
- package/dist/cli/api/index.js +49 -0
- package/dist/cli/api/playground.d.ts +92 -0
- package/dist/cli/index.js +708 -65
- package/dist/config/app-config.d.ts +1 -1
- package/dist/config/types/entity.d.ts +23 -0
- package/dist/config/types/form.d.ts +13 -1
- package/dist/dedicated/server.js +149 -0
- package/dist/errors.d.ts +18 -0
- package/dist/esm/index.mjs +740 -355
- package/dist/index.d.ts +3 -3
- package/dist/index.js +747 -355
- package/dist/schemas/agent-schema-v3.d.ts +200 -0
- package/dist/schemas/agent-schema-v3.js +206 -21
- package/dist/schemas/agent-schema-v3.mjs +203 -21
- package/dist/schemas/agent-schema.d.ts +14 -14
- package/dist/schemas/crm-schema.d.ts +16 -16
- package/dist/schemas.d.ts +226 -20
- package/dist/server/handlers/index.d.ts +1 -0
- package/dist/server/handlers/refresh-token-handler.d.ts +13 -0
- package/dist/server/route-handlers/handlers.d.ts +4 -0
- package/dist/server/route-handlers/index.d.ts +1 -1
- package/dist/server.js +149 -0
- package/dist/serverless/server.mjs +149 -0
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/types.d.ts +178 -0
- package/dist/skills/types.js +75 -5
- package/dist/skills/types.mjs +71 -5
- package/dist/types/handlers.d.ts +28 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/invocation.d.ts +2 -2
- package/dist/types/tool-response.d.ts +12 -0
- package/dist/types/tool.d.ts +1 -1
- package/package.json +2 -2
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
|
-
/**
|
|
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
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
10894
|
-
* When true, the
|
|
10895
|
-
*
|
|
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
|
-
*
|
|
10900
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
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.
|
|
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
|
|
11050
|
-
|
|
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
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
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 (
|
|
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
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
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
|
-
|
|
11413
|
-
|
|
11811
|
+
filePath,
|
|
11812
|
+
content,
|
|
11414
11813
|
publish: !isDraft
|
|
11415
|
-
})
|
|
11416
|
-
}
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
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
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13243
|
-
|
|
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
|
-
|
|
13837
|
+
filePath,
|
|
13838
|
+
content,
|
|
13249
13839
|
publish: !isDraft,
|
|
13250
13840
|
versionLabel
|
|
13251
|
-
})
|
|
13252
|
-
}
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
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,
|
|
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)
|