@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" && llm.startsWith("claude-")) return "anthropic";
22156
- if (agentType === "codex" && llm.startsWith("gpt-")) return "openai";
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 MAX_SKILLS_PER_AGENT = 20;
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-FSAF6HIL.js";
19
+ } from "./chunk-MHL5Z6IQ.js";
18
20
 
19
21
  // src/index.ts
20
- import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
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.4.0";
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-67OBSB2Q.js");
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 existsSync4, readFileSync as readFileSync6 } from "fs";
1122
- import { homedir as homedir4 } from "os";
1123
- import { join as join4 } from "path";
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 || join4(homedir4(), ".codex");
1202
- const path = join4(codexHome, "auth.json");
1203
- if (!existsSync4(path)) {
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 = readFileSync6(path, "utf8");
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 === "anthropic" ? "claude" : "chatgpt",
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 = providerFor(name);
1249
- const label = provider === "anthropic" ? "Claude" : "ChatGPT";
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 = provider === "anthropic" ? "your Claude subscription" : "your ChatGPT plan";
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
- console.log(`Created ${created.id} (${created.agent} \xB7 ${created.status})${created.default ? " [default]" : ""}`);
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 agent message ${created.agent} "hello"`);
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.agent ?? "-").padEnd(20)} ${(s.title ?? "").padEnd(16)} ${s.status}${s.default ? " *" : ""}`
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.agent ?? "-"}`);
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 (!existsSync5(path)) {
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
- writeFileSync4(opts.out, bytes);
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
@@ -8,7 +8,7 @@ import {
8
8
  skillsArraySchema,
9
9
  systemMessageEventSchema,
10
10
  userMessageEventSchema
11
- } from "./chunk-FSAF6HIL.js";
11
+ } from "./chunk-MHL5Z6IQ.js";
12
12
 
13
13
  // ../../packages/spec/src/compatibility.ts
14
14
  var capabilityInputsSchema = capabilitiesSchema;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  external_exports,
3
3
  messagePartSchema
4
- } from "./chunk-FSAF6HIL.js";
4
+ } from "./chunk-MHL5Z6IQ.js";
5
5
 
6
6
  // ../../packages/chat/src/stream.ts
7
7
  async function* parseSseStream(body) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsky/sky",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "description": "sky — the AgentSky developer CLI: launch and talk to long-lived agents from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",