@agentsky/sky 2.19.0 → 2.20.1

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.
@@ -3,7 +3,7 @@ import {
3
3
  apiBase,
4
4
  apiToken,
5
5
  currentUniverse
6
- } from "./chunk-MOYCAI4S.js";
6
+ } from "./chunk-ICTCB4VT.js";
7
7
 
8
8
  // src/api.ts
9
9
  import { createWriteStream, writeFileSync } from "fs";
@@ -144,7 +144,7 @@ async function resolveSessionRef(ref) {
144
144
  return agent.defaultSessionId;
145
145
  }
146
146
  async function* streamEvents(sessionId, signal) {
147
- const { parseSseStream } = await import("./src-5HKXRXLU.js");
147
+ const { parseSseStream } = await import("./src-KP6YIFNP.js");
148
148
  const res = await fetch(
149
149
  `${apiBase()}/sessions/${encodeURIComponent(sessionId)}/stream`,
150
150
  {
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSessionRef,
10
10
  streamEvents,
11
11
  uploadFile
12
- } from "./chunk-4MVWXHZY.js";
12
+ } from "./chunk-S6A5V7XR.js";
13
13
  import {
14
14
  CLI_VERSION,
15
15
  USER_AGENT,
@@ -20,7 +20,7 @@ import {
20
20
  currentUniverse,
21
21
  loadConfig,
22
22
  saveConfig
23
- } from "./chunk-MOYCAI4S.js";
23
+ } from "./chunk-ICTCB4VT.js";
24
24
  import {
25
25
  AGENT_TYPES,
26
26
  MODEL_SUBSCRIPTION_PLAN_PHRASE,
@@ -42,7 +42,7 @@ import {
42
42
  skillRefSchema,
43
43
  splitMcpServers,
44
44
  supersededBy
45
- } from "./chunk-UJ3D24IG.js";
45
+ } from "./chunk-NAD26CDY.js";
46
46
  import "./chunk-MLKGABMK.js";
47
47
 
48
48
  // src/index.ts
@@ -2831,6 +2831,25 @@ Details: \`${REFERENCES_DIR}/workspaces.md\` in this skill's folder.
2831
2831
  - Do the work yourself when it is small, when it depends on what you already have
2832
2832
  in front of you, or when explaining it would take as long as doing it.
2833
2833
 
2834
+ ## Local first, cloud when it is not here, never a stand-in
2835
+
2836
+ - When the person names an engine that is installed and signed in on this
2837
+ computer, run it here: it costs them nothing on AgentSky.
2838
+ - When it is not installed here, start that same engine in the cloud: its handle
2839
+ (\`@codex\`, \`@claude_code\`, \u2026) with \`start_session\` and a \`budget_cents\`, and say so
2840
+ in one line ("Codex is not installed on this Mac, so it ran in the cloud").
2841
+ Do not pick another engine instead.
2842
+ - Every refused tool call carries a stable error \`code\` (in the error text and in
2843
+ \`structuredContent.error\`) and, when there is one, a \`next_step\`. Read the code:
2844
+ \`insufficient_credits\`, \`workspace_wallet_empty\` and \`payment_setup_required\` mean
2845
+ the account cannot pay; \`budget_reached\` means that session's cap is spent.
2846
+ Stop that piece of work and tell the person what was refused and what unblocks
2847
+ it (credit or a card under Settings \u203A Billing, a larger \`budget_cents\`), then
2848
+ offer the engines that are installed here as the free alternative. Never hire a
2849
+ cheaper or different agent to get around a refusal.
2850
+ - \`get_usage\` shows the balance; check it before starting several cloud sessions
2851
+ at once so the person is told once, up front, instead of after the first fails.
2852
+
2834
2853
  ## How to pick
2835
2854
 
2836
2855
  Read \`${CATALOG_FILE}\` in this skill's folder. It lists every agent you can hire,
@@ -4130,7 +4149,7 @@ function textOf2(event) {
4130
4149
  return "";
4131
4150
  }
4132
4151
  function firstAsk(record) {
4133
- const root = record.threads.find((thread) => thread.relation === "primary") ?? record.threads[0];
4152
+ const root = record.threads.find((thread2) => thread2.relation === "primary") ?? record.threads[0];
4134
4153
  if (!root) return null;
4135
4154
  for (const event of record.events[root.id] ?? []) {
4136
4155
  if (event.type === "user.message") {
@@ -4142,9 +4161,9 @@ function firstAsk(record) {
4142
4161
  }
4143
4162
  function decisionsOf(record) {
4144
4163
  const out = [];
4145
- for (const thread of record.threads) {
4146
- for (const event of record.events[thread.id] ?? []) {
4147
- if (event.type === "human.decision" || event.type === "outcome.observed") out.push({ ...event, thread_id: thread.id });
4164
+ for (const thread2 of record.threads) {
4165
+ for (const event of record.events[thread2.id] ?? []) {
4166
+ if (event.type === "human.decision" || event.type === "outcome.observed") out.push({ ...event, thread_id: thread2.id });
4148
4167
  }
4149
4168
  }
4150
4169
  return out.sort((a, b) => (a.at ?? "").localeCompare(b.at ?? ""));
@@ -4158,7 +4177,7 @@ function adoptedSession(record) {
4158
4177
  return null;
4159
4178
  }
4160
4179
  function stepsOf(record, names2) {
4161
- const root = record.threads.find((thread) => thread.relation === "primary") ?? record.threads[0];
4180
+ const root = record.threads.find((thread2) => thread2.relation === "primary") ?? record.threads[0];
4162
4181
  if (!root) return [];
4163
4182
  const events = record.events[root.id] ?? [];
4164
4183
  const steps = [];
@@ -4277,8 +4296,8 @@ function buildHarborTask(record, opts) {
4277
4296
  const out = /* @__PURE__ */ new Map();
4278
4297
  const ask2 = record.goal?.objective ?? firstAsk(record) ?? record.title ?? "Complete the task described by the workspace.";
4279
4298
  const instruction = scrub(ask2, names2);
4280
- const root = record.threads.find((thread) => thread.relation === "primary") ?? record.threads[0];
4281
- const harnesses = [...new Set(record.threads.map((thread) => thread.agent.harness))];
4299
+ const root = record.threads.find((thread2) => thread2.relation === "primary") ?? record.threads[0];
4300
+ const harnesses = [...new Set(record.threads.map((thread2) => thread2.agent.harness))];
4282
4301
  const decisions = decisionsOf(record);
4283
4302
  const adopted = adoptedSession(record);
4284
4303
  const steps = stepsOf(record, names2);
@@ -4370,7 +4389,7 @@ async function fetchTaskRecord(taskRef) {
4370
4389
  const { task: task2 } = await api("GET", `/tasks/${encodeURIComponent(taskRef)}`);
4371
4390
  const { threads } = await api("GET", `/sessions/${encodeURIComponent(task2.id)}/threads`);
4372
4391
  const events = {};
4373
- for (const thread of threads) events[thread.id] = await pagedEvents(thread.id);
4392
+ for (const thread2 of threads) events[thread2.id] = await pagedEvents(thread2.id);
4374
4393
  let goal2 = null;
4375
4394
  try {
4376
4395
  goal2 = (await api("GET", `/sessions/${encodeURIComponent(task2.id)}/goal`)).goal;
@@ -4383,7 +4402,7 @@ async function fetchTaskRecord(taskRef) {
4383
4402
  } catch (error) {
4384
4403
  if (!(error instanceof ApiError && (error.status === 404 || error.status === 503))) throw error;
4385
4404
  }
4386
- const root = threads.find((thread) => thread.relation === "primary") ?? threads[0];
4405
+ const root = threads.find((thread2) => thread2.relation === "primary") ?? threads[0];
4387
4406
  return { task_id: task2.id, exported_at: (/* @__PURE__ */ new Date()).toISOString(), title: task2.title ?? null, threads, events, goal: goal2, files: files2, completion_answer: task2.completion_answer ?? null, environment: root?.environment ?? null };
4388
4407
  }
4389
4408
  var FILE_BYTES_MAX = 50 * 1024 * 1024;
@@ -4421,10 +4440,10 @@ var DECISION_KINDS = ["adopt", "rerun", "revise", "discard", "vote", "did_the_jo
4421
4440
  async function listThreads(taskRef) {
4422
4441
  return (await api("GET", `/sessions/${encodeURIComponent(taskRef)}/threads`)).threads;
4423
4442
  }
4424
- function renderThread(thread) {
4425
- const spend = (Number(thread.usage.spent_usd_micros) / 1e6).toFixed(4);
4426
- const env = thread.environment?.repo ? ` ${thread.environment.repo}${thread.environment.commit ? `@${thread.environment.commit.slice(0, 7)}` : ""}` : "";
4427
- return `${thread.id} ${thread.relation.padEnd(7)} ${thread.source.padEnd(12)} ${thread.agent.harness}/${thread.agent.model} ${thread.status} $${spend}${env}`;
4443
+ function renderThread(thread2) {
4444
+ const spend = (Number(thread2.usage.spent_usd_micros) / 1e6).toFixed(4);
4445
+ const env = thread2.environment?.repo ? ` ${thread2.environment.repo}${thread2.environment.commit ? `@${thread2.environment.commit.slice(0, 7)}` : ""}` : "";
4446
+ return `${thread2.id} ${thread2.relation.padEnd(7)} ${thread2.source.padEnd(12)} ${thread2.agent.harness}/${thread2.agent.model} ${thread2.status} $${spend}${env}`;
4428
4447
  }
4429
4448
  async function recordDecision(sessionRef, kind, opts = {}) {
4430
4449
  if (!DECISION_KINDS.includes(kind)) throw new Error(`kind must be one of ${DECISION_KINDS.join(", ")}`);
@@ -4704,6 +4723,38 @@ function renderBrowserProfile(profile) {
4704
4723
  return lines.join("\n");
4705
4724
  }
4706
4725
 
4726
+ // src/compare.ts
4727
+ async function listComparisons(opts = {}) {
4728
+ const qs = opts.archived ? "?archived=true" : "";
4729
+ const { comparisons } = await api("GET", `/comparisons${qs}`);
4730
+ return comparisons;
4731
+ }
4732
+ async function createComparison(body) {
4733
+ return api("POST", "/comparisons", body);
4734
+ }
4735
+ async function getComparison(id) {
4736
+ return api("GET", `/comparisons/${encodeURIComponent(id)}`);
4737
+ }
4738
+ async function pickWinner(id, opts) {
4739
+ return api("POST", `/comparisons/${encodeURIComponent(id)}/pick`, opts);
4740
+ }
4741
+ async function listThreads2(opts = {}) {
4742
+ const qs = opts.filter ? `?filter=${opts.filter}` : "";
4743
+ const { threads } = await api("GET", `/threads${qs}`);
4744
+ return threads;
4745
+ }
4746
+ function renderComparisonSummary(c) {
4747
+ const agents = c.agents.map((a) => `${a.agentType}/${a.llm}`).join(" vs ");
4748
+ const status = c.statusLine ?? c.attention ?? (c.archived ? "archived" : "active");
4749
+ return `${c.id.slice(0, 12)} ${c.title.slice(0, 40).padEnd(42)} [${agents}] ${status}`;
4750
+ }
4751
+ function renderThreadSummary(t) {
4752
+ const agents = t.agents.map((a) => `${a.agentType}/${a.llm}`).join(", ");
4753
+ const flag = t.pinned ? "\u{1F4CC} " : "";
4754
+ const status = t.working ? "working" : t.attention ?? (t.archived ? "archived" : "idle");
4755
+ return `${t.id.slice(0, 12)} ${flag}${t.title.slice(0, 40).padEnd(44)} [${agents}] ${status}`;
4756
+ }
4757
+
4707
4758
  // src/harness.ts
4708
4759
  import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
4709
4760
  import { dirname as dirname8, join as join16, resolve as resolve8 } from "path";
@@ -6029,7 +6080,7 @@ task.command("threads <task>").description("the task's threads from the session
6029
6080
  console.log(JSON.stringify({ threads }));
6030
6081
  return;
6031
6082
  }
6032
- for (const thread of threads) console.log(renderThread(thread));
6083
+ for (const thread2 of threads) console.log(renderThread(thread2));
6033
6084
  })
6034
6085
  );
6035
6086
  task.command("decide <session> <kind>").description("record what you did with a session's result: adopt | rerun | revise | discard | vote | did_the_job | approve").option("--target <session>", "the session the decision names (a vote's winner, the option you adopted)").option("--yes", "the value for did_the_job and approve").option("--no", "the value for did_the_job and approve").option("--reason <text>", "your own words, kept with the decision").option("--key <key>", "an idempotency key, so a repeat records nothing new").action(
@@ -6733,7 +6784,7 @@ vendor.command("usage <id>").description("the agents and tasks that used this ve
6733
6784
  })
6734
6785
  );
6735
6786
  var browserProfile = program.command("browser-profile").description("saved-login profiles: the sites your agents stay signed in to");
6736
- browserProfile.command("list").description("your workspace's browser profiles (requires BROWSER_PROFILES to be enabled)").option("--json", "print as JSON").action(
6787
+ browserProfile.command("list").description("your workspace's browser profiles: the sites your agents stay signed in to").option("--json", "print as JSON").action(
6737
6788
  run(async (opts) => {
6738
6789
  const profiles = await listBrowserProfiles();
6739
6790
  if (opts.json) {
@@ -6956,7 +7007,7 @@ harness.command("connect").description("run an ACP agent (the command after --)
6956
7007
  }));
6957
7008
  var traceCmd = program.command("trace").description("correlate this machine's agent work with the cloud sessions it starts (metadata only unless you choose content)");
6958
7009
  traceCmd.command("start").description("start a trace: a grant for this machine, a root span, and the spool the hooks write to").option("--name <name>", "a name for the task").option("--source <source>", "how this machine reports itself", "claude-code").option("--hooks [settings]", "also install the Claude Code hooks (default: ./.claude/settings.local.json)").option("--capture <scopes>", "also send content, redacted on this machine first: any of prompts,tool_output,files").option("--file <path>", "a file the files scope may send (repeatable)", collect).option("--dir <path>", "a folder whose files the files scope may send when a file tool touches them (repeatable)", collect).action(run(async (opts) => {
6959
- const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-VZX6UIOF.js");
7010
+ const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-SJHQ66JA.js");
6960
7011
  if (readState()) throw new Error("a trace is already running here \u2014 `sky trace stop` it first");
6961
7012
  const scopes = parseContentScopes(opts.capture);
6962
7013
  const chosen = chosenPaths(opts.file ?? [], opts.dir ?? []);
@@ -6991,7 +7042,7 @@ traceCmd.command("start").description("start a trace: a grant for this machine,
6991
7042
  }));
6992
7043
  traceCmd.command("hook").description("record one Claude Code hook event read from stdin (run by the installed hooks; never fails, prints nothing)").action(async () => {
6993
7044
  try {
6994
- const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-VZX6UIOF.js");
7045
+ const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-SJHQ66JA.js");
6995
7046
  const chunks = [];
6996
7047
  for await (const chunk of process.stdin) chunks.push(chunk);
6997
7048
  const input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
@@ -7004,14 +7055,14 @@ traceCmd.command("hook").description("record one Claude Code hook event read fro
7004
7055
  }
7005
7056
  });
7006
7057
  traceCmd.command("flush").description("send what the spool holds, retrying with backoff").action(run(async () => {
7007
- const { readState, flush, grantSender } = await import("./trace-VZX6UIOF.js");
7058
+ const { readState, flush, grantSender } = await import("./trace-SJHQ66JA.js");
7008
7059
  const state = readState();
7009
7060
  if (!state) throw new Error("no trace is running here");
7010
7061
  const outcome = await flush(grantSender(state), { attempts: 4 });
7011
7062
  console.log(`sent ${outcome.sent}, refused ${outcome.refused}, given up ${outcome.expired}, still pending ${outcome.pending}${outcome.stopped ? ` (stopped: ${outcome.stopped})` : ""}`);
7012
7063
  }));
7013
7064
  traceCmd.command("status").description("the running trace and its spool").action(run(async () => {
7014
- const { readState, spoolStatus } = await import("./trace-VZX6UIOF.js");
7065
+ const { readState, spoolStatus } = await import("./trace-SJHQ66JA.js");
7015
7066
  const state = readState();
7016
7067
  if (!state) {
7017
7068
  console.log("No trace is running here.");
@@ -7021,7 +7072,7 @@ traceCmd.command("status").description("the running trace and its spool").action
7021
7072
  console.log(`${state.trace_id} as ${state.source}: ${spool.pending} pending, ${spool.dropped_full} dropped (spool full), ${spool.dropped_expired} given up, ${spool.refused} refused`);
7022
7073
  }));
7023
7074
  traceCmd.command("show [id]").description("the trace bundle: spans, the sessions they reach, cost counted once per session").option("--json", "print the bundle as JSON").action(run(async (id, opts) => {
7024
- const { readState } = await import("./trace-VZX6UIOF.js");
7075
+ const { readState } = await import("./trace-SJHQ66JA.js");
7025
7076
  const ref = id ?? readState()?.trace_id;
7026
7077
  if (!ref) throw new Error("name a trace, or start one here");
7027
7078
  const bundle = await api("GET", `/traces/${ref}`);
@@ -7048,7 +7099,7 @@ traceCmd.command("show [id]").description("the trace bundle: spans, the sessions
7048
7099
  for (const gap of bundle.gaps) console.log(` gap: ${gap.span_id} ${gap.reason}`);
7049
7100
  }));
7050
7101
  traceCmd.command("stop").description("end the root span, send what is left, and revoke this machine's grant").action(run(async () => {
7051
- const { readState, record, flush, grantSender, clearState } = await import("./trace-VZX6UIOF.js");
7102
+ const { readState, record, flush, grantSender, clearState } = await import("./trace-SJHQ66JA.js");
7052
7103
  const state = readState();
7053
7104
  if (!state) throw new Error("no trace is running here");
7054
7105
  if (state.root_span) record(() => [{ span_id: state.root_span, kind: "end", outcome: "completed" }]);
@@ -7058,7 +7109,7 @@ traceCmd.command("stop").description("end the root span, send what is left, and
7058
7109
  console.log(`Stopped ${state.trace_id}${outcome.pending ? ` (${outcome.pending} events could not be sent)` : ""}`);
7059
7110
  }));
7060
7111
  traceCmd.command("sync [payload]").description("upload this machine's Claude Code and Codex sessions to the matching workspace's trace library, redacted here first").option("--enable", "turn sync on for this machine").option("--disable", "turn sync off; nothing more is uploaded").option("--route <match=workspace>", "send sessions whose folder path contains <match> to <workspace> (repeatable; replaces the saved rules)", collect).option("--default <workspace>", "where a session no rule matches goes").option("--keep-local", "keep sessions no rule matches on this machine (the default)").option("--session <ref>", "only this session: its id or transcript path (repeatable)", collect).option("--full", "re-read every session, not only files that changed").option("--limit <n>", "at most this many changed sessions this run", (v) => Number.parseInt(v, 10)).option("--dry-run", "show what would be uploaded without uploading").option("--hook", "run from a Claude Code SessionEnd hook or Codex notify: upload that session in the background and return at once").option("--install-schedule", "also sweep every 15 minutes (macOS launchd)").option("--status", "what sync has done on this machine").option("--quiet", "print only failures").action(async (payload, opts) => {
7061
- const lib = await import("./trace-library-EBH2GYDA.js");
7112
+ const lib = await import("./trace-library-IWF7BVNB.js");
7062
7113
  if (opts.hook) {
7063
7114
  try {
7064
7115
  let raw = payload ?? "";
@@ -7156,7 +7207,7 @@ traceCmd.command("sync [payload]").description("upload this machine's Claude Cod
7156
7207
  })();
7157
7208
  });
7158
7209
  traceCmd.command("search <query...>").description("search every session in a workspace's trace library (literal substring)").option("--workspace <slug>", "the workspace to search (default: the current one)").option("--limit <n>", "hits per page", (v) => Number.parseInt(v, 10), 20).option("--cursor <cursor>", "the next page of an earlier search").option("--json", "print the page as JSON").action(run(async (words, opts) => {
7159
- const { libraryRequest } = await import("./trace-library-EBH2GYDA.js");
7210
+ const { libraryRequest } = await import("./trace-library-IWF7BVNB.js");
7160
7211
  const workspace2 = opts.workspace ?? currentUniverse();
7161
7212
  if (!workspace2) throw new Error("name a workspace with --workspace");
7162
7213
  const params = new URLSearchParams({ q: words.join(" "), scope: "traces", limit: String(opts.limit), ...opts.cursor ? { cursor: opts.cursor } : {} });
@@ -7173,7 +7224,7 @@ traceCmd.command("search <query...>").description("search every session in a wor
7173
7224
  if (page.nextCursor) console.log(`More: sky trace search ${JSON.stringify(words.join(" "))} --workspace ${workspace2} --cursor ${page.nextCursor}`);
7174
7225
  }));
7175
7226
  traceCmd.command("library").description("list the sessions in a workspace's trace library, most recently updated first").option("--workspace <slug>", "the workspace (default: the current one)").option("--limit <n>", "how many", (v) => Number.parseInt(v, 10), 20).option("--json", "print the page as JSON").action(run(async (opts) => {
7176
- const { libraryRequest } = await import("./trace-library-EBH2GYDA.js");
7227
+ const { libraryRequest } = await import("./trace-library-IWF7BVNB.js");
7177
7228
  const workspace2 = opts.workspace ?? currentUniverse();
7178
7229
  if (!workspace2) throw new Error("name a workspace with --workspace");
7179
7230
  const page = await libraryRequest("GET", `/trace-library?limit=${opts.limit}`, workspace2);
@@ -7203,5 +7254,162 @@ workerCmd.command("start <pool>").description("start a worker connected to the g
7203
7254
  process.on("SIGTERM", () => ac.abort());
7204
7255
  await startWorker({ poolId, token, label: options.label, apiBase: base, signal: ac.signal });
7205
7256
  });
7257
+ var compare = program.command("compare").description("side-by-side agent comparisons: create, list, read, pick a winner");
7258
+ compare.command("create").description("run one task on two or more agents simultaneously and start a comparison").requiredOption("--task <text>", "the task to send to every agent").requiredOption("--agent <type>", "the main agent's harness type (e.g. claude_code, hermes)").requiredOption("--llm <model>", "the main agent's model").option("--vs <type:model...>", "one or more competing agents in type:model form (e.g. codex:gpt-6)", (val, acc) => [...acc, val], []).option("--effort <level>", "reasoning effort for every agent (low|medium|high)").option("--json", "print the created comparison as JSON").action(
7259
+ run(async (opts) => {
7260
+ const competitors = (opts.vs ?? []).map((v) => {
7261
+ const [agentType, llm] = v.split(":");
7262
+ if (!agentType || !llm) throw new ApiError(400, "invalid_request", `--vs must be type:model, got "${v}"`);
7263
+ return { agentType, llm, ...opts.effort ? { reasoningEffort: opts.effort } : {} };
7264
+ });
7265
+ if (!competitors.length) throw new ApiError(400, "invalid_request", "Provide at least one --vs type:model competitor.");
7266
+ const requestId = `sky-compare-${Date.now()}`;
7267
+ const result = await createComparison({
7268
+ requestId,
7269
+ agentType: opts.agent,
7270
+ llm: opts.llm,
7271
+ task: opts.task,
7272
+ compare: competitors,
7273
+ ...opts.effort ? { reasoningEffort: opts.effort } : {}
7274
+ });
7275
+ if (opts.json) {
7276
+ console.log(JSON.stringify(result, null, 2));
7277
+ return;
7278
+ }
7279
+ console.log(`created ${result.comparisonId}`);
7280
+ for (const col of result.columns) console.log(` column ${col.columnId} session ${col.sessionId}`);
7281
+ })
7282
+ );
7283
+ compare.command("list").description("your workspace comparisons, most recently updated first").option("--archived", "list archived comparisons instead").option("--json", "print as JSON").action(
7284
+ run(async (opts) => {
7285
+ const comparisons = await listComparisons({ archived: opts.archived });
7286
+ if (opts.json) {
7287
+ console.log(JSON.stringify(comparisons, null, 2));
7288
+ return;
7289
+ }
7290
+ if (!comparisons.length) console.log("no comparisons");
7291
+ for (const c of comparisons) console.log(renderComparisonSummary(c));
7292
+ })
7293
+ );
7294
+ compare.command("show <id>").description("one comparison: task, agents, status, and the vote verdict").option("--json", "print as JSON").action(
7295
+ run(async (id, opts) => {
7296
+ const detail = await getComparison(id);
7297
+ if (opts.json) {
7298
+ console.log(JSON.stringify(detail, null, 2));
7299
+ return;
7300
+ }
7301
+ console.log(`comparison ${detail.id}`);
7302
+ if (detail.title) console.log(` title: ${detail.title}`);
7303
+ if (detail.openerText) console.log(` task: ${detail.openerText}`);
7304
+ console.log(` created ${detail.createdAt}`);
7305
+ for (const col of detail.columns) {
7306
+ const masked = !col.agentType;
7307
+ const identity = masked ? "(hidden \u2014 Arena match pending)" : `${col.agentType ?? "?"}/${col.llm ?? "?"}`;
7308
+ console.log(` column ${col.columnId} session ${col.sessionId ?? "-"} ${identity} ${col.podStatus ?? ""}`);
7309
+ }
7310
+ if (detail.arena) console.log(` arena kind=${detail.arena.kind} status=${detail.arena.status} hidden=${detail.arena.hidden}`);
7311
+ if (detail.vote) console.log(` vote outcome=${detail.vote.outcome} winner=${detail.vote.winnerSessionId ?? "none"} sealed=${detail.vote.sealed}`);
7312
+ })
7313
+ );
7314
+ compare.command("pick <id>").description('record the winning column \u2014 or a tie or "both_bad" \u2014 for this comparison').option("--winner <columnId>", "the columnId of the winning column").option("--tie", "record a tie instead of a winner").option("--both-bad", "record that neither agent did the job").option("--json", "print the verdict as JSON").action(
7315
+ run(async (id, opts) => {
7316
+ const choices = [opts.winner, opts.tie, opts.bothBad].filter(Boolean).length;
7317
+ if (choices !== 1) throw new ApiError(400, "invalid_request", "Provide exactly one of --winner, --tie or --both-bad.");
7318
+ const outcome = opts.tie ? "tie" : opts.bothBad ? "both_bad" : void 0;
7319
+ const result = await pickWinner(id, { winnerColumnId: opts.winner, outcome });
7320
+ if (opts.json) {
7321
+ console.log(JSON.stringify(result, null, 2));
7322
+ return;
7323
+ }
7324
+ if (result.winnerSessionId) console.log(`winner: session ${result.winnerSessionId}`);
7325
+ else console.log("recorded: " + (outcome ?? "pick"));
7326
+ if (result.revealed) console.log("identities revealed:");
7327
+ for (const s of result.seats) console.log(` ${s.sessionId} ${s.agentType}/${s.llm}`);
7328
+ })
7329
+ );
7330
+ var thread = program.command("thread").description("workspace threads: the conversation history across all agents");
7331
+ thread.command("list").description("your workspace threads, most recently updated first (includes comparisons)").option("--archived", "list archived threads only").option("--all", "include both active and archived").option("--json", "print as JSON").action(
7332
+ run(async (opts) => {
7333
+ const filter = opts.all ? "all" : opts.archived ? "archived" : "active";
7334
+ const threads = await listThreads2({ filter });
7335
+ if (opts.json) {
7336
+ console.log(JSON.stringify(threads, null, 2));
7337
+ return;
7338
+ }
7339
+ if (!threads.length) console.log("no threads");
7340
+ for (const t of threads) console.log(renderThreadSummary(t));
7341
+ })
7342
+ );
7343
+ session.command("feedback <id>").description("read the thumbs-up/down feedback on this session's messages").option("--json", "print as JSON").action(
7344
+ run(async (id, opts) => {
7345
+ const result = await api(
7346
+ "GET",
7347
+ `/sessions/${encodeURIComponent(id)}/feedback`
7348
+ );
7349
+ if (opts.json) {
7350
+ console.log(JSON.stringify(result, null, 2));
7351
+ return;
7352
+ }
7353
+ const entries = Object.entries(result.reports);
7354
+ if (!entries.length) {
7355
+ console.log("no feedback recorded");
7356
+ return;
7357
+ }
7358
+ for (const [msgId, r] of entries) console.log(`${msgId.slice(0, 12)} ${r.vote} ${(r.reasons ?? []).join(", ")}`);
7359
+ })
7360
+ );
7361
+ session.command("side-chat <id>").description("ask a question about this session's transcript (answered beside it, never sent to the agent)").requiredOption("--question <text>", "the question to ask").requiredOption("--transcript <text>", "the session transcript or relevant excerpt").option("--quote <text>", "a specific passage the question targets").option("--json", "print as JSON").action(
7362
+ run(async (id, opts) => {
7363
+ const result = await api("POST", `/sessions/${encodeURIComponent(id)}/side-chat`, {
7364
+ transcript: opts.transcript,
7365
+ history: [],
7366
+ question: opts.question,
7367
+ ...opts.quote ? { quote: opts.quote } : {}
7368
+ });
7369
+ if (opts.json) {
7370
+ console.log(JSON.stringify(result, null, 2));
7371
+ return;
7372
+ }
7373
+ console.log(result.answer);
7374
+ if (result.looked?.length) console.log(`
7375
+ (sources: ${result.looked.join(", ")})`);
7376
+ })
7377
+ );
7378
+ session.command("code-review <id>").description("run the code-review assistant on this session's diff or file").requiredOption("--diff <text>", "the diff or file content to review").option("--filename <name>", "the filename for language-aware review").option("--json", "print as JSON").action(
7379
+ run(async (id, opts) => {
7380
+ const result = await api(
7381
+ "POST",
7382
+ `/sessions/${encodeURIComponent(id)}/code-review`,
7383
+ { diff: opts.diff, ...opts.filename ? { filename: opts.filename } : {} }
7384
+ );
7385
+ if (opts.json) {
7386
+ console.log(JSON.stringify(result, null, 2));
7387
+ return;
7388
+ }
7389
+ console.log(result.review);
7390
+ if (result.issues?.length) {
7391
+ console.log("\nIssues:");
7392
+ for (const issue of result.issues) {
7393
+ const loc = issue.line ? ` line ${issue.line}` : "";
7394
+ console.log(` [${issue.severity}]${loc} ${issue.message}`);
7395
+ }
7396
+ }
7397
+ })
7398
+ );
7399
+ session.command("snapshot <id>").description("read or revoke the public share link for a session").requiredOption("--thread <threadId>", "the thread/comparison that owns this session").option("--revoke", "revoke the snapshot link instead of reading it").option("--json", "print as JSON").action(
7400
+ run(async (id, opts) => {
7401
+ const path = `/sessions/${encodeURIComponent(id)}/snapshot?thread=${encodeURIComponent(opts.thread)}`;
7402
+ const result = await api(opts.revoke ? "DELETE" : "GET", path);
7403
+ if (opts.json) {
7404
+ console.log(JSON.stringify(result, null, 2));
7405
+ return;
7406
+ }
7407
+ if (opts.revoke) {
7408
+ console.log(result.url === null ? "snapshot link revoked" : `still set: ${result.url}`);
7409
+ return;
7410
+ }
7411
+ console.log(result.url ?? "no snapshot link set");
7412
+ })
7413
+ );
7206
7414
  var ARGUMENTS_FOLLOW_THE_SCRIPT_PATH_ON_EVERY_RUNTIME = { from: "node" };
7207
7415
  program.parseAsync(process.argv, ARGUMENTS_FOLLOW_THE_SCRIPT_PATH_ON_EVERY_RUNTIME).catch(fail);
package/dist/products.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  skillsArraySchema,
9
9
  systemMessageEventSchema,
10
10
  userMessageEventSchema
11
- } from "./chunk-UJ3D24IG.js";
11
+ } from "./chunk-NAD26CDY.js";
12
12
  import "./chunk-MLKGABMK.js";
13
13
 
14
14
  // ../../packages/spec/src/compatibility.ts
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  external_exports,
3
3
  messagePartSchema
4
- } from "./chunk-UJ3D24IG.js";
4
+ } from "./chunk-NAD26CDY.js";
5
5
  import "./chunk-MLKGABMK.js";
6
6
 
7
7
  // ../../packages/chat/src/stream.ts
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  USER_AGENT,
3
3
  configPath
4
- } from "./chunk-MOYCAI4S.js";
4
+ } from "./chunk-ICTCB4VT.js";
5
5
  import {
6
6
  TRACE_CONTENT_SCOPES,
7
7
  redactTraceText
8
- } from "./chunk-UJ3D24IG.js";
8
+ } from "./chunk-NAD26CDY.js";
9
9
  import "./chunk-MLKGABMK.js";
10
10
 
11
11
  // src/trace.ts
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  ApiError
3
- } from "./chunk-4MVWXHZY.js";
3
+ } from "./chunk-S6A5V7XR.js";
4
4
  import {
5
5
  USER_AGENT,
6
6
  apiBase,
7
7
  apiToken
8
- } from "./chunk-MOYCAI4S.js";
8
+ } from "./chunk-ICTCB4VT.js";
9
9
  import {
10
10
  SessionSourceCollector,
11
11
  publicRemote,
12
12
  redactTraceText
13
- } from "./chunk-UJ3D24IG.js";
13
+ } from "./chunk-NAD26CDY.js";
14
14
  import "./chunk-MLKGABMK.js";
15
15
 
16
16
  // src/trace-library.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsky/sky",
3
- "version": "2.19.0",
3
+ "version": "2.20.1",
4
4
  "description": "sky \u2014 the AgentSky developer CLI: launch and talk to long-lived agents from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",