@agentsky/sky 2.4.0 → 2.6.0
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.
|
@@ -18984,7 +18984,8 @@ var MODEL_REASONING_EFFORTS = {
|
|
|
18984
18984
|
"gemini-3.5-flash": ["low", "medium", "high"],
|
|
18985
18985
|
"glm-5.3": ["low", "high", "max"],
|
|
18986
18986
|
"glm-5.3-flash": ["low", "high", "max"],
|
|
18987
|
-
"grok-4.6": ["low", "medium", "high"]
|
|
18987
|
+
"grok-4.6": ["low", "medium", "high"],
|
|
18988
|
+
"grok-4.7": ["low", "medium", "high", "xhigh"]
|
|
18988
18989
|
};
|
|
18989
18990
|
var REASONING_EFFORTS = {
|
|
18990
18991
|
agoragentic_acp: null,
|
|
@@ -21200,6 +21201,10 @@ var questionSchema = external_exports.object({
|
|
|
21200
21201
|
blocksGoal: external_exports.boolean().optional(),
|
|
21201
21202
|
/** Informational deadline. Passing it never approves or answers the question. */
|
|
21202
21203
|
dueAt: external_exports.string().datetime({ offset: true }).optional(),
|
|
21204
|
+
/** Hard permission deadline; unlike dueAt, later answers are refused. */
|
|
21205
|
+
expiresAt: external_exports.string().datetime({ offset: true }).optional(),
|
|
21206
|
+
/** The owner withdrew this question. Never a human answer or consent. */
|
|
21207
|
+
cancelledAt: external_exports.string().datetime({ offset: true }).optional(),
|
|
21203
21208
|
/** What the platform is asking for when the question is one of its own
|
|
21204
21209
|
* requests rather than an agent's free question. Absent on an ordinary question. */
|
|
21205
21210
|
kind: external_exports.enum(QUESTION_REQUEST_KINDS).optional(),
|
|
@@ -22151,11 +22156,38 @@ function isCompatible(agentType, llm) {
|
|
|
22151
22156
|
function defaultLlm(agentType) {
|
|
22152
22157
|
return allowedLlms[agentType][0];
|
|
22153
22158
|
}
|
|
22159
|
+
var CHATGPT_PLAN_MODELS = /* @__PURE__ */ new Set([
|
|
22160
|
+
"gpt-6-astra",
|
|
22161
|
+
"gpt-5.6-sol",
|
|
22162
|
+
"gpt-5.6-terra",
|
|
22163
|
+
"gpt-5.6-luna",
|
|
22164
|
+
"gpt-5.5"
|
|
22165
|
+
]);
|
|
22166
|
+
var CLAUDE_PLAN_MODELS = /* @__PURE__ */ new Set([
|
|
22167
|
+
"claude-opus-5",
|
|
22168
|
+
"claude-fable-5-1",
|
|
22169
|
+
"claude-sonnet-5",
|
|
22170
|
+
"claude-haiku-4-5",
|
|
22171
|
+
"claude-sonnet-4-6",
|
|
22172
|
+
"claude-opus-4-8",
|
|
22173
|
+
"claude-fable-5"
|
|
22174
|
+
]);
|
|
22154
22175
|
function modelSubscriptionProviderFor(agentType, llm) {
|
|
22155
|
-
if (agentType === "claude_code" &&
|
|
22156
|
-
if (agentType === "codex" &&
|
|
22176
|
+
if (agentType === "claude_code" && CLAUDE_PLAN_MODELS.has(llm)) return "anthropic";
|
|
22177
|
+
if (agentType === "codex" && CHATGPT_PLAN_MODELS.has(llm)) return "openai";
|
|
22178
|
+
if (agentType === "cursor" && llm === "harness-default") return "cursor";
|
|
22157
22179
|
return null;
|
|
22158
22180
|
}
|
|
22181
|
+
var MODEL_SUBSCRIPTION_PROVIDER_NAME = {
|
|
22182
|
+
anthropic: "Claude",
|
|
22183
|
+
openai: "ChatGPT",
|
|
22184
|
+
cursor: "Cursor"
|
|
22185
|
+
};
|
|
22186
|
+
var MODEL_SUBSCRIPTION_PLAN_PHRASE = {
|
|
22187
|
+
anthropic: "Claude subscription",
|
|
22188
|
+
openai: "ChatGPT plan",
|
|
22189
|
+
cursor: "Cursor plan"
|
|
22190
|
+
};
|
|
22159
22191
|
var AGENT_TYPE_META = {
|
|
22160
22192
|
...Object.fromEntries(ACP_AGENT_TYPES.map((agentType) => [agentType, {
|
|
22161
22193
|
label: ACP_AGENTS[agentType].label,
|
|
@@ -22250,8 +22282,7 @@ var skillRefSchema = external_exports.discriminatedUnion("type", [
|
|
|
22250
22282
|
storeSkillRefSchema,
|
|
22251
22283
|
githubSkillRefSchema
|
|
22252
22284
|
]);
|
|
22253
|
-
var
|
|
22254
|
-
var skillsArraySchema = external_exports.array(skillRefSchema).max(MAX_SKILLS_PER_AGENT, `At most ${MAX_SKILLS_PER_AGENT} skills per agent.`).superRefine((arr, ctx) => {
|
|
22285
|
+
var skillsArraySchema = external_exports.array(skillRefSchema).superRefine((arr, ctx) => {
|
|
22255
22286
|
const targets = arr.map((s) => s.type === "skill" ? `skill:${s.skillId}` : `github:${s.url}`);
|
|
22256
22287
|
if (new Set(targets).size !== targets.length) {
|
|
22257
22288
|
ctx.addIssue({ code: "custom", message: "Duplicate skill entries." });
|
|
@@ -22312,6 +22343,50 @@ var hookSchema = external_exports.object({
|
|
|
22312
22343
|
path: ["matcher"]
|
|
22313
22344
|
});
|
|
22314
22345
|
var hooksSchema = external_exports.array(hookSchema).max(20);
|
|
22346
|
+
var POLICY_ACTIONS = ["allow", "ask", "deny"];
|
|
22347
|
+
var toolCallPolicyConfigSchema = external_exports.object({
|
|
22348
|
+
action: external_exports.enum(POLICY_ACTIONS),
|
|
22349
|
+
matcher: external_exports.string().min(1).max(255).refine((v) => /^(?:[A-Za-z0-9_:.-]|\.\*)+(?:\|(?:[A-Za-z0-9_:.-]|\.\*)+)*$/.test(v), {
|
|
22350
|
+
message: "matcher must be tool names or patterns separated by |"
|
|
22351
|
+
}).optional(),
|
|
22352
|
+
commandPatterns: external_exports.array(external_exports.string().min(1).max(500)).max(20).optional()
|
|
22353
|
+
}).strict();
|
|
22354
|
+
var spendCapPolicyConfigSchema = external_exports.object({
|
|
22355
|
+
warnUsdMicros: external_exports.number().int().min(0).nullish(),
|
|
22356
|
+
hardCapUsdMicros: external_exports.number().int().min(1)
|
|
22357
|
+
}).strict().refine((c) => !c.warnUsdMicros || c.warnUsdMicros < c.hardCapUsdMicros, {
|
|
22358
|
+
message: "warnUsdMicros must be less than hardCapUsdMicros"
|
|
22359
|
+
});
|
|
22360
|
+
var callCountPolicyConfigSchema = external_exports.object({ limitPerSession: external_exports.number().int().min(1).max(1e5) }).strict();
|
|
22361
|
+
var capabilityPolicyConfigSchema = external_exports.record(external_exports.string(), external_exports.enum(POLICY_ACTIONS)).superRefine((obj, ctx) => {
|
|
22362
|
+
for (const k of Object.keys(obj)) {
|
|
22363
|
+
if (!CAPABILITY_IDS.includes(k)) {
|
|
22364
|
+
ctx.addIssue({ code: "custom", message: `unknown capability id: ${k}`, path: [k] });
|
|
22365
|
+
}
|
|
22366
|
+
}
|
|
22367
|
+
});
|
|
22368
|
+
var policyRuleBaseSchema = external_exports.object({
|
|
22369
|
+
name: external_exports.string().min(1).max(128),
|
|
22370
|
+
enabled: external_exports.boolean().default(true),
|
|
22371
|
+
position: external_exports.number().int().min(0).max(9999).default(0)
|
|
22372
|
+
});
|
|
22373
|
+
var policyRuleSchema = external_exports.discriminatedUnion("kind", [
|
|
22374
|
+
policyRuleBaseSchema.extend({ kind: external_exports.literal("TOOL_CALL"), config: toolCallPolicyConfigSchema }),
|
|
22375
|
+
policyRuleBaseSchema.extend({ kind: external_exports.literal("SPEND_CAP"), config: spendCapPolicyConfigSchema }),
|
|
22376
|
+
policyRuleBaseSchema.extend({ kind: external_exports.literal("CALL_COUNT"), config: callCountPolicyConfigSchema }),
|
|
22377
|
+
policyRuleBaseSchema.extend({ kind: external_exports.literal("CAPABILITY"), config: capabilityPolicyConfigSchema })
|
|
22378
|
+
]);
|
|
22379
|
+
var policyRulePatchSchema = external_exports.object({
|
|
22380
|
+
name: external_exports.string().min(1).max(128).optional(),
|
|
22381
|
+
enabled: external_exports.boolean().optional(),
|
|
22382
|
+
position: external_exports.number().int().min(0).max(9999).optional(),
|
|
22383
|
+
config: external_exports.union([
|
|
22384
|
+
toolCallPolicyConfigSchema,
|
|
22385
|
+
spendCapPolicyConfigSchema,
|
|
22386
|
+
callCountPolicyConfigSchema,
|
|
22387
|
+
capabilityPolicyConfigSchema
|
|
22388
|
+
]).optional()
|
|
22389
|
+
});
|
|
22315
22390
|
var packagesSchema = external_exports.object({
|
|
22316
22391
|
apt: external_exports.array(packageNameSchema).max(32).default([]),
|
|
22317
22392
|
pip: external_exports.array(packageNameSchema).max(32).default([]),
|
|
@@ -22351,6 +22426,7 @@ var capabilityPoliciesSchema = external_exports.record(
|
|
|
22351
22426
|
capabilityIdInputSchema,
|
|
22352
22427
|
capabilityPermissionPolicySchema
|
|
22353
22428
|
);
|
|
22429
|
+
var nativeToolPolicySchema = external_exports.enum(["always_allow", "always_ask"]);
|
|
22354
22430
|
var publishSchema = external_exports.object({
|
|
22355
22431
|
name: external_exports.string().trim().min(1, "Give your agent a name."),
|
|
22356
22432
|
description: external_exports.string().default(""),
|
|
@@ -22398,6 +22474,8 @@ export {
|
|
|
22398
22474
|
supersededBy,
|
|
22399
22475
|
defaultLlm,
|
|
22400
22476
|
modelSubscriptionProviderFor,
|
|
22477
|
+
MODEL_SUBSCRIPTION_PROVIDER_NAME,
|
|
22478
|
+
MODEL_SUBSCRIPTION_PLAN_PHRASE,
|
|
22401
22479
|
capabilitiesSchema,
|
|
22402
22480
|
instructionFileSchema,
|
|
22403
22481
|
skillRefSchema,
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
AGENT_TYPES,
|
|
4
|
+
MODEL_SUBSCRIPTION_PLAN_PHRASE,
|
|
5
|
+
MODEL_SUBSCRIPTION_PROVIDER_NAME,
|
|
4
6
|
REASONING_EFFORT_VALUES,
|
|
5
7
|
capabilitiesSchema,
|
|
6
8
|
customDataSchema,
|
|
@@ -14,10 +16,10 @@ import {
|
|
|
14
16
|
skillRefSchema,
|
|
15
17
|
splitMcpServers,
|
|
16
18
|
supersededBy
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-MHL5Z6IQ.js";
|
|
18
20
|
|
|
19
21
|
// src/index.ts
|
|
20
|
-
import { existsSync as
|
|
22
|
+
import { existsSync as existsSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
21
23
|
import { basename as basename3, resolve as resolve3 } from "path";
|
|
22
24
|
import { createInterface as createInterface3 } from "readline";
|
|
23
25
|
import { Command } from "commander";
|
|
@@ -62,7 +64,7 @@ function currentUniverse() {
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
// src/version.ts
|
|
65
|
-
var CLI_VERSION = "2.
|
|
67
|
+
var CLI_VERSION = "2.6.0";
|
|
66
68
|
var USER_AGENT = `sky-cli/${CLI_VERSION} (${process.platform}; node/${process.versions.node})`;
|
|
67
69
|
|
|
68
70
|
// src/api.ts
|
|
@@ -165,7 +167,7 @@ async function resolveSessionRef(ref) {
|
|
|
165
167
|
return agent2.defaultSessionId;
|
|
166
168
|
}
|
|
167
169
|
async function* streamEvents(sessionId, signal) {
|
|
168
|
-
const { parseSseStream } = await import("./src-
|
|
170
|
+
const { parseSseStream } = await import("./src-GLBJHVIJ.js");
|
|
169
171
|
const res = await fetch(
|
|
170
172
|
`${apiBase()}/sessions/${encodeURIComponent(sessionId)}/stream`,
|
|
171
173
|
{
|
|
@@ -400,6 +402,62 @@ You are ${name}, a helpful long-lived agent.
|
|
|
400
402
|
);
|
|
401
403
|
}
|
|
402
404
|
|
|
405
|
+
// src/catalog.ts
|
|
406
|
+
var bare = (handle) => handle.replace(/^@/, "");
|
|
407
|
+
var optionalAuth = () => ({ auth: Boolean(apiToken()) });
|
|
408
|
+
function catalogQuery(filter) {
|
|
409
|
+
const params = new URLSearchParams();
|
|
410
|
+
if (filter.text?.trim()) params.set("q", filter.text.trim());
|
|
411
|
+
if (filter.category) params.set("category", filter.category);
|
|
412
|
+
const query = params.toString();
|
|
413
|
+
return query ? `?${query}` : "";
|
|
414
|
+
}
|
|
415
|
+
function listingBody(agentId, options) {
|
|
416
|
+
return {
|
|
417
|
+
agent_id: agentId,
|
|
418
|
+
handle: bare(options.handle),
|
|
419
|
+
tagline: options.tagline,
|
|
420
|
+
...options.description ? { description: options.description } : {},
|
|
421
|
+
...options.category?.length ? { categories: options.category } : {},
|
|
422
|
+
...options.does?.length ? { does: options.does } : {},
|
|
423
|
+
...options.doesNot?.length ? { does_not: options.doesNot } : {},
|
|
424
|
+
...options.example?.length ? { examples: options.example } : {}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
async function listCatalog(filter = {}) {
|
|
428
|
+
return (await api("GET", `/catalog/agents${catalogQuery(filter)}`, void 0, optionalAuth())).agents;
|
|
429
|
+
}
|
|
430
|
+
async function showCatalogAgent(handle) {
|
|
431
|
+
return (await api("GET", `/catalog/agents/${encodeURIComponent(bare(handle))}`, void 0, optionalAuth())).agent;
|
|
432
|
+
}
|
|
433
|
+
async function listCategories() {
|
|
434
|
+
return (await api("GET", "/catalog/categories", void 0, { auth: false })).categories;
|
|
435
|
+
}
|
|
436
|
+
async function publishListing(agentId, options) {
|
|
437
|
+
return (await api("POST", "/catalog/listings", listingBody(agentId, options))).agent;
|
|
438
|
+
}
|
|
439
|
+
async function removeListing(handle) {
|
|
440
|
+
await api("DELETE", `/catalog/listings/${encodeURIComponent(bare(handle))}`);
|
|
441
|
+
}
|
|
442
|
+
var uses = (agent2) => `${agent2.stats.uses} ${agent2.stats.uses === 1 ? "use" : "uses"}`;
|
|
443
|
+
var mine = (agent2) => agent2.visibility === "workspace" ? " (yours)" : "";
|
|
444
|
+
function renderCatalogAgent(agent2) {
|
|
445
|
+
const section = (title, lines) => lines.length ? [`${title}:`, ...lines.map((line) => ` - ${line}`)] : [];
|
|
446
|
+
return [
|
|
447
|
+
`@${agent2.handle} ${agent2.name}${mine(agent2)}`,
|
|
448
|
+
agent2.tagline,
|
|
449
|
+
...agent2.description && agent2.description !== agent2.tagline ? ["", agent2.description] : [],
|
|
450
|
+
"",
|
|
451
|
+
`Categories: ${agent2.categories.join(", ") || "none"}`,
|
|
452
|
+
`Used: ${uses(agent2)}`,
|
|
453
|
+
...section("Does", agent2.does),
|
|
454
|
+
...section("Does not", agent2.does_not),
|
|
455
|
+
...section("Examples", agent2.examples),
|
|
456
|
+
"",
|
|
457
|
+
`Hire it: sky session create @${agent2.handle} --message "\u2026"`
|
|
458
|
+
].join("\n");
|
|
459
|
+
}
|
|
460
|
+
|
|
403
461
|
// src/clone.ts
|
|
404
462
|
import { spawn } from "child_process";
|
|
405
463
|
import { existsSync as existsSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -1073,6 +1131,33 @@ async function headlessLogin(verifier, challenge) {
|
|
|
1073
1131
|
throw new Error("login timed out after 10 minutes");
|
|
1074
1132
|
}
|
|
1075
1133
|
|
|
1134
|
+
// src/local-agents.ts
|
|
1135
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1136
|
+
import { homedir as homedir4 } from "os";
|
|
1137
|
+
import { join as join4 } from "path";
|
|
1138
|
+
function localAgents(env = process.env, home = homedir4()) {
|
|
1139
|
+
const claude = env.CLAUDE_CONFIG_DIR || join4(home, ".claude");
|
|
1140
|
+
const codex = env.CODEX_HOME || join4(home, ".codex");
|
|
1141
|
+
const cursor = join4(home, ".cursor");
|
|
1142
|
+
return [
|
|
1143
|
+
{ id: "claude_code", name: "Claude Code", root: claude, skillsDir: join4(claude, "skills") },
|
|
1144
|
+
{ id: "codex", name: "Codex", root: codex, skillsDir: join4(codex, "skills") },
|
|
1145
|
+
{ id: "cursor", name: "Cursor", root: cursor, skillsDir: join4(cursor, "skills") }
|
|
1146
|
+
];
|
|
1147
|
+
}
|
|
1148
|
+
function detectLocalAgents(env = process.env, home = homedir4()) {
|
|
1149
|
+
return localAgents(env, home).filter((agent2) => existsSync4(agent2.root));
|
|
1150
|
+
}
|
|
1151
|
+
function selectLocalAgents(ids, env = process.env, home = homedir4()) {
|
|
1152
|
+
if (!ids?.length) return detectLocalAgents(env, home);
|
|
1153
|
+
const known = localAgents(env, home);
|
|
1154
|
+
return ids.map((id) => {
|
|
1155
|
+
const agent2 = known.find((candidate) => candidate.id === id);
|
|
1156
|
+
if (!agent2) throw new Error(`unknown agent ${id} \u2014 expected ${known.map((candidate) => candidate.id).join(" | ")}`);
|
|
1157
|
+
return agent2;
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1076
1161
|
// src/render.ts
|
|
1077
1162
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
1078
1163
|
var red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
@@ -1116,11 +1201,159 @@ function renderEvent(event, data) {
|
|
|
1116
1201
|
}
|
|
1117
1202
|
}
|
|
1118
1203
|
|
|
1204
|
+
// src/session-read.ts
|
|
1205
|
+
var TURN_TYPES = ["user.message", "agent.message", "session.status_idle", "session.status_terminated", "session.error"];
|
|
1206
|
+
function latestTurn(events) {
|
|
1207
|
+
let start = 0;
|
|
1208
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1209
|
+
if (events[index].type === "user.message") {
|
|
1210
|
+
start = index + 1;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
const reading = { finished: false, messages: [], errors: [] };
|
|
1215
|
+
for (const event of events.slice(start)) {
|
|
1216
|
+
if (event.type === "agent.message" && event.text?.trim()) reading.messages.push(event.text);
|
|
1217
|
+
else if (event.type === "session.error" && event.error?.retry_status !== "retrying") {
|
|
1218
|
+
reading.errors.push(event.error?.message ?? event.error?.type ?? "unknown error");
|
|
1219
|
+
} else if (event.type === "session.status_terminated") reading.finished = true;
|
|
1220
|
+
else if (event.type === "session.status_idle" && event.stop_reason?.type !== "requires_action") reading.finished = true;
|
|
1221
|
+
}
|
|
1222
|
+
return reading;
|
|
1223
|
+
}
|
|
1224
|
+
async function history(sessionId) {
|
|
1225
|
+
const events = [];
|
|
1226
|
+
let cursor = null;
|
|
1227
|
+
for (; ; ) {
|
|
1228
|
+
const query = new URLSearchParams({ limit: "500" });
|
|
1229
|
+
for (const type of TURN_TYPES) query.append("types[]", type);
|
|
1230
|
+
if (cursor) query.set("cursor", cursor);
|
|
1231
|
+
const page = await api(
|
|
1232
|
+
"GET",
|
|
1233
|
+
`/sessions/${encodeURIComponent(sessionId)}/events?${query}`
|
|
1234
|
+
);
|
|
1235
|
+
events.push(...page.events);
|
|
1236
|
+
if (!page.hasMore || !page.cursor) return events;
|
|
1237
|
+
cursor = page.cursor;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
async function readTurn(sessionId, opts = {}) {
|
|
1241
|
+
const deadline = Date.now() + (opts.timeoutSeconds ?? 600) * 1e3;
|
|
1242
|
+
for (; ; ) {
|
|
1243
|
+
const reading = latestTurn(await history(sessionId));
|
|
1244
|
+
if (reading.finished || !opts.wait || Date.now() >= deadline) return reading;
|
|
1245
|
+
await new Promise((resolve4) => setTimeout(resolve4, opts.intervalMs ?? 3e3));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/skill.ts
|
|
1250
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1251
|
+
import { join as join5 } from "path";
|
|
1252
|
+
var SKILL_NAME = "agentsky";
|
|
1253
|
+
var SKILL_MARKER = ".agentsky.json";
|
|
1254
|
+
function skillDocument() {
|
|
1255
|
+
return `---
|
|
1256
|
+
name: ${SKILL_NAME}
|
|
1257
|
+
description: Hand part of a job to another agent running in the cloud through AgentSky, and bring its result back. Use when the person asks for another agent or a named engine (Claude Code, Codex, Hermes, OpenClaw and others) to do, test or review something, or when independent work could run in parallel.
|
|
1258
|
+
---
|
|
1259
|
+
|
|
1260
|
+
# Working with other agents through AgentSky
|
|
1261
|
+
|
|
1262
|
+
AgentSky runs long-lived agents in the cloud. With the \`sky\` command you can
|
|
1263
|
+
find one, give it a job, and read what it produced. You stay responsible for
|
|
1264
|
+
the result.
|
|
1265
|
+
|
|
1266
|
+
If the \`agentsky\` MCP tools are available in this session (\`search_catalog\`,
|
|
1267
|
+
\`start_session\`, \`read_session\`, \`send_message\`), use them instead of the
|
|
1268
|
+
commands below; they do the same things.
|
|
1269
|
+
|
|
1270
|
+
## Before you start
|
|
1271
|
+
|
|
1272
|
+
Run \`sky auth whoami\`. If it says you are not logged in, ask the person to run
|
|
1273
|
+
\`sky auth login\` and stop there. Never ask them for a token.
|
|
1274
|
+
|
|
1275
|
+
## When to hand work over
|
|
1276
|
+
|
|
1277
|
+
- When the person asks another agent to do, test or review something, do that.
|
|
1278
|
+
Honor an agent or engine they name.
|
|
1279
|
+
- Otherwise, hand over work another agent is better suited to, or independent
|
|
1280
|
+
parts that can run at the same time as your own.
|
|
1281
|
+
- Do the work yourself when it is small, when it depends on what you already
|
|
1282
|
+
have in front of you, or when explaining it would take as long as doing it.
|
|
1283
|
+
|
|
1284
|
+
## How to pick
|
|
1285
|
+
|
|
1286
|
+
\`sky catalog search <words for the work> --json\`
|
|
1287
|
+
|
|
1288
|
+
Each result has a \`handle\`, what the agent does, its \`engine_type\`, its
|
|
1289
|
+
categories, how it is paid, and what it needs. If the person names an engine,
|
|
1290
|
+
match its \`engine_type\` and do not substitute another one silently. Run the
|
|
1291
|
+
command with no words to see every agent. If nothing fits, do the work
|
|
1292
|
+
yourself and say so.
|
|
1293
|
+
|
|
1294
|
+
## How to brief
|
|
1295
|
+
|
|
1296
|
+
\`sky session create @<handle> --title "<short name>" --message "<brief>" --json\`
|
|
1297
|
+
|
|
1298
|
+
It prints the new session's \`id\`. The agent starts working at once, on its own
|
|
1299
|
+
computer in the cloud. It cannot see this conversation or this machine, so a
|
|
1300
|
+
brief is one self-contained message that states:
|
|
1301
|
+
|
|
1302
|
+
1. what to produce,
|
|
1303
|
+
2. what "done" means,
|
|
1304
|
+
3. anything it must not do.
|
|
1305
|
+
|
|
1306
|
+
To give it a file, add \`--file <path>\`. Never put a password, key or token in a
|
|
1307
|
+
brief.
|
|
1308
|
+
|
|
1309
|
+
## How to collect
|
|
1310
|
+
|
|
1311
|
+
\`sky session read <session id> --wait --json\`
|
|
1312
|
+
|
|
1313
|
+
It waits until the agent finishes its turn and prints its messages, with
|
|
1314
|
+
\`finished\` true or false. If \`finished\` is false the wait timed out: run it
|
|
1315
|
+
again. A first turn can take a minute to begin while the agent's computer
|
|
1316
|
+
starts.
|
|
1317
|
+
|
|
1318
|
+
To correct it, answer it or ask for more:
|
|
1319
|
+
|
|
1320
|
+
\`sky session message <session id> "<text>" --detach\`
|
|
1321
|
+
|
|
1322
|
+
then read again. An agent saying it is done is not evidence: check what it
|
|
1323
|
+
produced before you rely on it.
|
|
1324
|
+
|
|
1325
|
+
## How to finish
|
|
1326
|
+
|
|
1327
|
+
Tell the person what was done and which agent did which part. Every agent you
|
|
1328
|
+
start is paid for by the person, so start only what the job needs.
|
|
1329
|
+
`;
|
|
1330
|
+
}
|
|
1331
|
+
function installedVersion(dir) {
|
|
1332
|
+
try {
|
|
1333
|
+
const marker = JSON.parse(readFileSync6(join5(dir, SKILL_MARKER), "utf8"));
|
|
1334
|
+
return typeof marker.version === "string" ? marker.version : null;
|
|
1335
|
+
} catch {
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
function installSkill(agent2, opts = {}) {
|
|
1340
|
+
const dir = join5(agent2.skillsDir, SKILL_NAME);
|
|
1341
|
+
const previous = installedVersion(dir);
|
|
1342
|
+
const outcome = previous === CLI_VERSION ? "current" : previous ? "updated" : "installed";
|
|
1343
|
+
if (outcome !== "current" && !opts.dryRun) {
|
|
1344
|
+
mkdirSync2(dir, { recursive: true });
|
|
1345
|
+
writeFileSync4(join5(dir, "SKILL.md"), skillDocument());
|
|
1346
|
+
writeFileSync4(join5(dir, SKILL_MARKER), `${JSON.stringify({ version: CLI_VERSION }, null, 2)}
|
|
1347
|
+
`);
|
|
1348
|
+
}
|
|
1349
|
+
return { agent: agent2.id, path: dir, outcome };
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1119
1352
|
// src/subscriptions.ts
|
|
1120
1353
|
import { spawn as spawn3 } from "child_process";
|
|
1121
|
-
import { existsSync as
|
|
1122
|
-
import { homedir as
|
|
1123
|
-
import { join as
|
|
1354
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
|
|
1355
|
+
import { homedir as homedir5 } from "os";
|
|
1356
|
+
import { join as join6 } from "path";
|
|
1124
1357
|
import { createInterface as createInterface2 } from "readline";
|
|
1125
1358
|
var ALIASES = {
|
|
1126
1359
|
claude: "anthropic",
|
|
@@ -1136,6 +1369,14 @@ function providerFor(name) {
|
|
|
1136
1369
|
}
|
|
1137
1370
|
return provider;
|
|
1138
1371
|
}
|
|
1372
|
+
function managedProviderFor(name) {
|
|
1373
|
+
if (name.toLowerCase() === "cursor") return "cursor";
|
|
1374
|
+
const provider = ALIASES[name.toLowerCase()];
|
|
1375
|
+
if (!provider) {
|
|
1376
|
+
throw new ApiError(400, "unknown_provider", `unknown provider "${name}" \u2014 use claude, chatgpt or cursor`);
|
|
1377
|
+
}
|
|
1378
|
+
return provider;
|
|
1379
|
+
}
|
|
1139
1380
|
function ask(question) {
|
|
1140
1381
|
return new Promise((done) => {
|
|
1141
1382
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
@@ -1198,12 +1439,12 @@ async function connectClaude() {
|
|
|
1198
1439
|
console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
|
|
1199
1440
|
}
|
|
1200
1441
|
async function connectChatgpt(opts) {
|
|
1201
|
-
const codexHome = process.env.CODEX_HOME ||
|
|
1202
|
-
const path =
|
|
1203
|
-
if (!
|
|
1442
|
+
const codexHome = process.env.CODEX_HOME || join6(homedir5(), ".codex");
|
|
1443
|
+
const path = join6(codexHome, "auth.json");
|
|
1444
|
+
if (!existsSync5(path)) {
|
|
1204
1445
|
throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
|
|
1205
1446
|
}
|
|
1206
|
-
const raw =
|
|
1447
|
+
const raw = readFileSync7(path, "utf8");
|
|
1207
1448
|
let accountId;
|
|
1208
1449
|
try {
|
|
1209
1450
|
const parsed = JSON.parse(raw);
|
|
@@ -1233,7 +1474,7 @@ async function listSubscriptions() {
|
|
|
1233
1474
|
}
|
|
1234
1475
|
for (const s of modelSubscriptions) {
|
|
1235
1476
|
const bits = [
|
|
1236
|
-
s.provider
|
|
1477
|
+
MODEL_SUBSCRIPTION_PROVIDER_NAME[s.provider].toLowerCase(),
|
|
1237
1478
|
s.status,
|
|
1238
1479
|
s.expiresAt ? `expires ~${s.expiresAt.slice(0, 10)}` : null,
|
|
1239
1480
|
s.lastUsedAt ? `last used ${s.lastUsedAt.slice(0, 10)}` : null,
|
|
@@ -1245,8 +1486,8 @@ async function listSubscriptions() {
|
|
|
1245
1486
|
}
|
|
1246
1487
|
}
|
|
1247
1488
|
async function disconnect(name, opts) {
|
|
1248
|
-
const provider =
|
|
1249
|
-
const label = provider
|
|
1489
|
+
const provider = managedProviderFor(name);
|
|
1490
|
+
const label = MODEL_SUBSCRIPTION_PROVIDER_NAME[provider];
|
|
1250
1491
|
if (!opts.yes) {
|
|
1251
1492
|
const ok = await confirm2(`Disconnect ${label}? Agents running on it will fail until reconnected or switched to Platform.`);
|
|
1252
1493
|
if (!ok) {
|
|
@@ -1272,7 +1513,7 @@ function printModelBilling(session2) {
|
|
|
1272
1513
|
if (session2.modelBilling !== "account") return;
|
|
1273
1514
|
const provider = modelSubscriptionProviderFor(session2.agentType, session2.llm);
|
|
1274
1515
|
if (!provider) return;
|
|
1275
|
-
const label =
|
|
1516
|
+
const label = `your ${MODEL_SUBSCRIPTION_PLAN_PHRASE[provider]}`;
|
|
1276
1517
|
console.log(`Model billing: ${label} \u2014 $0 model usage`);
|
|
1277
1518
|
}
|
|
1278
1519
|
var program = new Command();
|
|
@@ -1445,7 +1686,7 @@ function renderResource(r) {
|
|
|
1445
1686
|
const ref = r.checkout ? `@${r.checkout.type === "branch" ? r.checkout.name : r.checkout.sha}` : "";
|
|
1446
1687
|
return `${r.id} repo ${r.mount_path} ${r.url}${ref}`;
|
|
1447
1688
|
}
|
|
1448
|
-
session.command("create <agent>").description("create a session (starts provisioning by default)").option("--title <title>", "session title").option("--message <text>", "start the first turn immediately, provisioning even with --deferred").option("--deferred", "leave the session idle without a pod until its first turn").option("--vcpus <n>", "machine vCPUs: 1, 2, 4 or 8 (default 2)").option("--memory <mb>", "machine memory in MB, 256MB steps (default 4096)").option("--file <path...>", "mount local files read-only in the working directory: path[:/mount] (uploaded first)").option("--repo <url...>", "clone GitHub repositories into the working directory: https://github.com/{owner}/{repo}[@branch|@sha]").option("--repo-token <token>", "token for the --repo clones; compiled into the sandbox's egress policy, never sent to it").action(
|
|
1689
|
+
session.command("create <agent>").description("create a session (starts provisioning by default)").option("--title <title>", "session title").option("--message <text>", "start the first turn immediately, provisioning even with --deferred").option("--deferred", "leave the session idle without a pod until its first turn").option("--vcpus <n>", "machine vCPUs: 1, 2, 4 or 8 (default 2)").option("--memory <mb>", "machine memory in MB, 256MB steps (default 4096)").option("--file <path...>", "mount local files read-only in the working directory: path[:/mount] (uploaded first)").option("--repo <url...>", "clone GitHub repositories into the working directory: https://github.com/{owner}/{repo}[@branch|@sha]").option("--repo-token <token>", "token for the --repo clones; compiled into the sandbox's egress policy, never sent to it").option("--json", "print the created session as JSON").action(
|
|
1449
1690
|
run(
|
|
1450
1691
|
async (agentRef, opts) => {
|
|
1451
1692
|
const resources = await resourceParams(opts);
|
|
@@ -1458,10 +1699,14 @@ session.command("create <agent>").description("create a session (starts provisio
|
|
|
1458
1699
|
...resources.length ? { resources } : {},
|
|
1459
1700
|
...opts.message ? { initial_events: [{ type: "user.message", content: [{ type: "text", text: opts.message }] }] } : {}
|
|
1460
1701
|
});
|
|
1461
|
-
|
|
1702
|
+
if (opts.json) {
|
|
1703
|
+
console.log(JSON.stringify(created));
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
console.log(`Created ${created.id} (${created.agentId ?? "-"} \xB7 ${created.status})${created.default ? " [default]" : ""}`);
|
|
1462
1707
|
printModelBilling(created);
|
|
1463
1708
|
for (const r of created.resources ?? []) console.log(` ${renderResource(r)}`);
|
|
1464
|
-
console.log(` sky
|
|
1709
|
+
console.log(` sky session message ${created.id} "hello"`);
|
|
1465
1710
|
}
|
|
1466
1711
|
)
|
|
1467
1712
|
);
|
|
@@ -1475,7 +1720,7 @@ session.command("list [agent]").action(
|
|
|
1475
1720
|
}
|
|
1476
1721
|
for (const s of sessions) {
|
|
1477
1722
|
console.log(
|
|
1478
|
-
`${s.id.padEnd(24)} ${(s.
|
|
1723
|
+
`${s.id.padEnd(24)} ${(s.agentId ?? "-").padEnd(20)} ${(s.title ?? "").padEnd(16)} ${s.status}${s.default ? " *" : ""}`
|
|
1479
1724
|
);
|
|
1480
1725
|
}
|
|
1481
1726
|
})
|
|
@@ -1487,7 +1732,7 @@ session.command("show <id>").action(
|
|
|
1487
1732
|
`/sessions/${encodeURIComponent(id)}`
|
|
1488
1733
|
);
|
|
1489
1734
|
console.log(`${s.id}${s.default ? " [default]" : ""}`);
|
|
1490
|
-
console.log(` agent ${s.
|
|
1735
|
+
console.log(` agent ${s.agentId ?? "-"}`);
|
|
1491
1736
|
console.log(` title ${s.title ?? "-"}`);
|
|
1492
1737
|
console.log(` status ${s.status}`);
|
|
1493
1738
|
console.log(` model ${s.agentType} \xB7 ${s.llm}`);
|
|
@@ -1534,6 +1779,21 @@ session.command("interrupt <ref>").description("abort the in-flight turn").actio
|
|
|
1534
1779
|
console.log(res.status === "no_turn" ? "nothing in flight" : "interrupting");
|
|
1535
1780
|
})
|
|
1536
1781
|
);
|
|
1782
|
+
session.command("read <ref>").description("what the agent has said since the last message it was sent").option("--wait", "wait until the agent finishes its turn").option("--timeout <seconds>", "how long --wait waits (default 600)").option("--json", "print { finished, messages, errors } as JSON").action(
|
|
1783
|
+
run(async (ref, opts) => {
|
|
1784
|
+
const reading = await readTurn(await resolveSessionRef(ref), {
|
|
1785
|
+
wait: opts.wait,
|
|
1786
|
+
...opts.timeout !== void 0 ? { timeoutSeconds: Number(opts.timeout) } : {}
|
|
1787
|
+
});
|
|
1788
|
+
if (opts.json) {
|
|
1789
|
+
console.log(JSON.stringify(reading));
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
for (const message of reading.messages) console.log(message);
|
|
1793
|
+
for (const error of reading.errors) console.error(`error: ${error}`);
|
|
1794
|
+
if (!reading.finished) console.error("The agent is still working. Run this again to read more.");
|
|
1795
|
+
})
|
|
1796
|
+
);
|
|
1537
1797
|
var sessionResources = session.command("resources").description("files and GitHub repositories mounted into a session's working directory");
|
|
1538
1798
|
var resourcesPath = (id) => `/sessions/${encodeURIComponent(id)}/resources`;
|
|
1539
1799
|
sessionResources.command("list <ref>").description("the session's resources, oldest first").action(
|
|
@@ -1688,6 +1948,38 @@ async function chatLoop(sessionId, opts = {}) {
|
|
|
1688
1948
|
await new Promise(() => {
|
|
1689
1949
|
});
|
|
1690
1950
|
}
|
|
1951
|
+
var catalog = program.command("catalog").description("the agents that can be started by handle");
|
|
1952
|
+
catalog.command("search [words...]").description("find an agent for a piece of work; no words lists every agent; works without logging in").option("--category <category>", "only this category (see `sky catalog categories`)").option("--json", "print the full listings as JSON").action(
|
|
1953
|
+
run(async (words, opts) => {
|
|
1954
|
+
const agents = await listCatalog({ text: words.join(" "), category: opts.category });
|
|
1955
|
+
if (opts.json) {
|
|
1956
|
+
console.log(JSON.stringify(agents));
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
if (!agents.length) {
|
|
1960
|
+
console.log("No agent matches. `sky catalog search` with no words lists every agent.");
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
for (const a of agents) console.log(`@${a.handle.padEnd(24)} ${a.name.padEnd(20)} ${a.tagline}`);
|
|
1964
|
+
console.log(`
|
|
1965
|
+
Start one: sky session create @<handle> --message "<the job>"`);
|
|
1966
|
+
})
|
|
1967
|
+
);
|
|
1968
|
+
program.command("skill").description("the AgentSky skill for the agents on this machine").command("install").description("add the AgentSky skill to each installed agent (Claude Code, Codex, Cursor)").option("--agent <id...>", "only these agents: claude_code | codex | cursor").option("--dry-run", "say what would be written; write nothing").option("--json", "print the outcome per agent as JSON").action(
|
|
1969
|
+
run(async (opts) => {
|
|
1970
|
+
const installs = selectLocalAgents(opts.agent).map((a) => installSkill(a, { dryRun: opts.dryRun }));
|
|
1971
|
+
if (opts.json) {
|
|
1972
|
+
console.log(JSON.stringify(installs));
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
if (!installs.length) {
|
|
1976
|
+
console.log("No agent found on this machine (looked for Claude Code, Codex and Cursor).");
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
const verb = { installed: opts.dryRun ? "would install" : "installed", updated: opts.dryRun ? "would update" : "updated", current: "up to date" };
|
|
1980
|
+
for (const i of installs) console.log(`${i.agent.padEnd(12)} ${verb[i.outcome].padEnd(14)} ${i.path}`);
|
|
1981
|
+
})
|
|
1982
|
+
);
|
|
1691
1983
|
var secrets = program.command("secrets").description("agent secrets (values write-only)");
|
|
1692
1984
|
secrets.command("set <id> <pairs...>").description("set KEY=VALUE pairs on the agent").action(
|
|
1693
1985
|
run(async (id, pairs) => {
|
|
@@ -1723,7 +2015,7 @@ secrets.command("unset <id> <key>").action(
|
|
|
1723
2015
|
program.command("launch").description("scaffold ./agent.toml if absent, create the agent, print its address").option("-f, --file <path>", "agent file", "agent.toml").option("--type <type>", "override the file's harness: hermes | claude_code | codex | openclaw | pi | dsh | kimi_code | opencode").option("--llm <llm>", "override the file's model (must suit the harness)").action(
|
|
1724
2016
|
run(async (opts) => {
|
|
1725
2017
|
const path = resolve3(opts.file);
|
|
1726
|
-
if (!
|
|
2018
|
+
if (!existsSync6(path)) {
|
|
1727
2019
|
const name = basename3(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
|
|
1728
2020
|
scaffoldAgentFile(path, name);
|
|
1729
2021
|
console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
|
|
@@ -1921,7 +2213,7 @@ files.command("rm <id>").description("delete a file (a session that already rece
|
|
|
1921
2213
|
files.command("download <id>").description("download a session file's bytes").requiredOption("-o, --out <path>", "where to write the bytes").action(
|
|
1922
2214
|
run(async (id, opts) => {
|
|
1923
2215
|
const { bytes } = await downloadFile(id);
|
|
1924
|
-
|
|
2216
|
+
writeFileSync5(opts.out, bytes);
|
|
1925
2217
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
1926
2218
|
})
|
|
1927
2219
|
);
|
|
@@ -2064,6 +2356,30 @@ for (const [verb, method, suffix, blurb] of [
|
|
|
2064
2356
|
})
|
|
2065
2357
|
);
|
|
2066
2358
|
}
|
|
2359
|
+
catalog.command("show <handle>").description("what one agent does, and how to hire it").option("--json", "print the raw listing").action(
|
|
2360
|
+
run(async (handle, opts) => {
|
|
2361
|
+
const agent2 = await showCatalogAgent(handle);
|
|
2362
|
+
console.log(opts.json ? JSON.stringify(agent2, null, 2) : renderCatalogAgent(agent2));
|
|
2363
|
+
})
|
|
2364
|
+
);
|
|
2365
|
+
catalog.command("categories").description("the categories a listing can carry").action(
|
|
2366
|
+
run(async () => {
|
|
2367
|
+
for (const category of await listCategories()) console.log(`${category.id.padEnd(10)} ${category.label}`);
|
|
2368
|
+
})
|
|
2369
|
+
);
|
|
2370
|
+
catalog.command("publish <agent>").description("list one of your agents under a handle, visible to your workspace; publishing again replaces the listing").requiredOption("--handle <handle>", "the @handle people hire it by: 2 to 40 lower-case letters, digits or underscores").requiredOption("--tagline <text>", "one line on what it is for").option("--description <text>", "a longer description").option("--category <id>", "a category; repeat for several", collect).option("--does <text>", "something it does; repeat for several", collect).option("--does-not <text>", "something it does not do; repeat for several", collect).option("--example <text>", "an example request; repeat for several", collect).action(
|
|
2371
|
+
run(async (agentId, opts) => {
|
|
2372
|
+
const agent2 = await publishListing(agentId, opts);
|
|
2373
|
+
console.log(`Listed @${agent2.handle} (${agent2.name})`);
|
|
2374
|
+
console.log(` sky session create @${agent2.handle} --message "\u2026"`);
|
|
2375
|
+
})
|
|
2376
|
+
);
|
|
2377
|
+
catalog.command("unpublish <handle>").description("take down one of your workspace's listings; the agent itself is untouched").action(
|
|
2378
|
+
run(async (handle) => {
|
|
2379
|
+
await removeListing(handle);
|
|
2380
|
+
console.log(`Removed @${handle.replace(/^@/, "")} from the catalog`);
|
|
2381
|
+
})
|
|
2382
|
+
);
|
|
2067
2383
|
program.command("config").description("show the CLI config location and current values").action(() => {
|
|
2068
2384
|
const config = loadConfig();
|
|
2069
2385
|
console.log(`config: ${configPath()}`);
|
package/dist/products.js
CHANGED