@agentsky/sky 2.11.0 → 2.12.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.
package/dist/index.js CHANGED
@@ -1,4 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ ApiError,
4
+ api,
5
+ apiBytes,
6
+ apiForm,
7
+ downloadFile,
8
+ postEvents,
9
+ resolveSessionRef,
10
+ streamEvents,
11
+ uploadFile
12
+ } from "./chunk-7LUCHBX6.js";
2
13
  import {
3
14
  CLI_VERSION,
4
15
  USER_AGENT,
@@ -9,7 +20,7 @@ import {
9
20
  currentUniverse,
10
21
  loadConfig,
11
22
  saveConfig
12
- } from "./chunk-RWPPIEHH.js";
23
+ } from "./chunk-RF5JICNX.js";
13
24
  import {
14
25
  AGENT_TYPES,
15
26
  MODEL_SUBSCRIPTION_PLAN_PHRASE,
@@ -28,7 +39,7 @@ import {
28
39
  skillRefSchema,
29
40
  splitMcpServers,
30
41
  supersededBy
31
- } from "./chunk-WC7GJSAJ.js";
42
+ } from "./chunk-XXDQKVPS.js";
32
43
  import "./chunk-MLKGABMK.js";
33
44
 
34
45
  // src/index.ts
@@ -37,156 +48,6 @@ import { basename as basename4, resolve as resolve6 } from "path";
37
48
  import { createInterface as createInterface4 } from "readline";
38
49
  import { Command } from "commander";
39
50
 
40
- // src/api.ts
41
- var ApiError = class extends Error {
42
- constructor(status, code, message2) {
43
- super(message2);
44
- this.status = status;
45
- this.code = code;
46
- }
47
- status;
48
- code;
49
- };
50
- function authHeaders() {
51
- const token = apiToken();
52
- if (!token) {
53
- throw new ApiError(401, "not_logged_in", "not logged in \u2014 run `sky auth login`");
54
- }
55
- const headers = { authorization: `Bearer ${token}` };
56
- const universe2 = currentUniverse();
57
- if (universe2) headers["x-universe"] = universe2;
58
- return headers;
59
- }
60
- async function parseError(res) {
61
- let code = "http_error";
62
- let message2 = `${res.status} ${res.statusText}`;
63
- try {
64
- const body = await res.json();
65
- if (body.error?.message) {
66
- code = body.error.code ?? code;
67
- message2 = body.error.message;
68
- }
69
- } catch {
70
- }
71
- throw new ApiError(res.status, code, message2);
72
- }
73
- async function api(method, path, body, opts = {}) {
74
- const res = await fetch(`${apiBase()}${path}`, {
75
- method,
76
- headers: {
77
- "user-agent": USER_AGENT,
78
- ...opts.auth === false ? {} : authHeaders(),
79
- ...body !== void 0 ? { "content-type": "application/json" } : {},
80
- ...opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : {}
81
- },
82
- body: body !== void 0 ? JSON.stringify(body) : void 0
83
- });
84
- if (!res.ok) await parseError(res);
85
- if (res.status === 204) return void 0;
86
- return await res.json();
87
- }
88
- async function postEvents(sessionId, events, idempotencyKey) {
89
- const res = await fetch(`${apiBase()}/sessions/${encodeURIComponent(sessionId)}/events`, {
90
- method: "POST",
91
- headers: {
92
- "user-agent": USER_AGENT,
93
- ...authHeaders(),
94
- "content-type": "application/json",
95
- ...idempotencyKey ? { "idempotency-key": idempotencyKey } : {}
96
- },
97
- body: JSON.stringify({ events })
98
- });
99
- if (!res.ok) await parseError(res);
100
- return await res.json();
101
- }
102
- async function uploadFile(bytes, filename, mediaType, expiresInSeconds) {
103
- const form = new FormData();
104
- form.append("file", new Blob([new Uint8Array(bytes)], { type: mediaType }), filename);
105
- if (expiresInSeconds) form.append("expires_in_seconds", String(expiresInSeconds));
106
- const res = await fetch(`${apiBase()}/files`, {
107
- method: "POST",
108
- headers: { "user-agent": USER_AGENT, ...authHeaders() },
109
- body: form
110
- });
111
- if (!res.ok) await parseError(res);
112
- return await res.json();
113
- }
114
- async function apiForm(method, path, form) {
115
- const res = await fetch(`${apiBase()}${path}`, {
116
- method,
117
- headers: { "user-agent": USER_AGENT, ...authHeaders() },
118
- body: form
119
- });
120
- if (!res.ok) await parseError(res);
121
- return await res.json();
122
- }
123
- async function downloadFile(fileId) {
124
- const res = await fetch(`${apiBase()}/files/${encodeURIComponent(fileId)}/content`, {
125
- headers: { "user-agent": USER_AGENT, ...authHeaders() }
126
- });
127
- if (!res.ok) await parseError(res);
128
- return { bytes: new Uint8Array(await res.arrayBuffer()), mediaType: res.headers.get("content-type") ?? "application/octet-stream" };
129
- }
130
- async function apiBytes(method, path, body) {
131
- const res = await fetch(`${apiBase()}${path}`, {
132
- method,
133
- headers: {
134
- "user-agent": USER_AGENT,
135
- ...authHeaders(),
136
- ...body ? { "content-type": body.mediaType } : {}
137
- },
138
- body: body ? new Uint8Array(body.bytes) : void 0
139
- });
140
- if (!res.ok) await parseError(res);
141
- const bytes = new Uint8Array(await res.arrayBuffer());
142
- return {
143
- bytes,
144
- mediaType: res.headers.get("content-type") ?? "application/octet-stream",
145
- json: () => JSON.parse(new TextDecoder().decode(bytes))
146
- };
147
- }
148
- async function resolveSessionRef(ref) {
149
- if (ref.startsWith("sess-")) return ref;
150
- const { agent: agent2 } = await api(
151
- "GET",
152
- `/agents/${encodeURIComponent(ref)}`
153
- ).catch(async () => {
154
- await api("GET", `/sessions/${encodeURIComponent(ref)}`);
155
- return { agent: { id: ref, defaultSessionId: ref } };
156
- });
157
- if (!agent2.defaultSessionId) {
158
- throw new ApiError(
159
- 404,
160
- "no_sessions",
161
- `agent ${agent2.id} has no sessions \u2014 create one: sky session create ${agent2.id}`
162
- );
163
- }
164
- return agent2.defaultSessionId;
165
- }
166
- async function* streamEvents(sessionId, signal) {
167
- const { parseSseStream } = await import("./src-HVCP3PPM.js");
168
- const res = await fetch(
169
- `${apiBase()}/sessions/${encodeURIComponent(sessionId)}/stream`,
170
- {
171
- headers: { "user-agent": USER_AGENT, ...authHeaders(), accept: "text/event-stream" },
172
- signal
173
- }
174
- );
175
- if (!res.ok) await parseError(res);
176
- if (!res.body) throw new ApiError(502, "no_body", "stream returned no body");
177
- try {
178
- for await (const frame of parseSseStream(res.body)) {
179
- if (!frame.data) continue;
180
- try {
181
- yield { id: frame.id, event: frame.event, data: JSON.parse(frame.data) };
182
- } catch {
183
- }
184
- }
185
- } catch (error) {
186
- if (!(error instanceof DOMException && error.name === "AbortError")) throw error;
187
- }
188
- }
189
-
190
51
  // src/files.ts
191
52
  import { readFileSync } from "fs";
192
53
  import { basename, extname } from "path";
@@ -1511,7 +1372,7 @@ at the end; they do the same things, one agent at a time.
1511
1372
 
1512
1373
  Use the workspace the person named. If they named none, check the git root for
1513
1374
  \`.agentsky.json\` \u2014 if it has a \`"workspace"\` key, use that. Otherwise use the
1514
- account default (personal). Never ask. Pass the chosen slug as \`workspace\` on
1375
+ account default workspace. Never ask. Pass the chosen slug as \`workspace\` on
1515
1376
  every MCP tool that accepts it, or as \`--workspace <slug>\` on sky commands.
1516
1377
  Details: \`${REFERENCES_DIR}/workspaces.md\` in this skill's folder.
1517
1378
 
@@ -2990,7 +2851,7 @@ auth.command("whoami").description("who the current token acts as").action(
2990
2851
  run(async () => {
2991
2852
  const who = await api("GET", "/whoami");
2992
2853
  console.log(
2993
- `${who.user.email ?? "unknown"} \xB7 universe ${who.universe.slug}${who.universe.isPersonal ? " (personal)" : ""} \xB7 scopes ${who.scopes.join(",")}`
2854
+ `${who.user.email ?? "unknown"} \xB7 universe ${who.universe.slug}${who.universe.isPersonal ? " (default)" : ""} \xB7 scopes ${who.scopes.join(",")}`
2994
2855
  );
2995
2856
  })
2996
2857
  );
@@ -3010,7 +2871,7 @@ universe.command("list").action(
3010
2871
  const current = currentUniverse();
3011
2872
  for (const u of universes) {
3012
2873
  const marker = u.slug === current || !current && u.isPersonal ? "*" : " ";
3013
- console.log(`${marker} ${u.slug}${u.isPersonal ? " (personal)" : ""}`);
2874
+ console.log(`${marker} ${u.slug}${u.isPersonal ? " (default)" : ""}`);
3014
2875
  }
3015
2876
  })
3016
2877
  );
@@ -3036,7 +2897,7 @@ universe.command("show").action(
3036
2897
  console.log(`${u.slug} \u2014 ${u.name} \xB7 ${u.agents} agents \xB7 ${u.members} members`);
3037
2898
  } else {
3038
2899
  const who = await api("GET", path);
3039
- console.log(`${who.universe.slug} (personal)`);
2900
+ console.log(`${who.universe.slug} (default)`);
3040
2901
  }
3041
2902
  })
3042
2903
  );
@@ -3084,7 +2945,7 @@ workspace.command("list").action(
3084
2945
  const current = currentUniverse();
3085
2946
  for (const u of universes) {
3086
2947
  const marker = u.slug === current || !current && u.isPersonal ? "*" : " ";
3087
- console.log(`${marker} ${u.slug}${u.isPersonal ? " (personal)" : ""}`);
2948
+ console.log(`${marker} ${u.slug}${u.isPersonal ? " (default)" : ""}`);
3088
2949
  }
3089
2950
  })
3090
2951
  );
@@ -4096,7 +3957,7 @@ program.command("config").description("show the CLI config location and current
4096
3957
  console.log(`config: ${configPath()}`);
4097
3958
  console.log(` apiBase: ${config.apiBase ?? "(default)"}`);
4098
3959
  console.log(` token: ${config.token ? `${config.token.slice(0, 10)}\u2026` : "(none)"}`);
4099
- console.log(` universe: ${config.currentUniverse ?? "(personal)"}`);
3960
+ console.log(` universe: ${config.currentUniverse ?? "(default)"}`);
4100
3961
  });
4101
3962
  var harness = program.command("harness").description("workspace harnesses: init, verify, push");
4102
3963
  harness.command("init [dir]").description("write a harness.json profile skeleton in the given directory (default: .)").action(run(async (dir) => {
@@ -4121,7 +3982,7 @@ harness.command("connect").description("run an ACP agent (the command after --)
4121
3982
  }));
4122
3983
  var traceCmd = program.command("trace").description("correlate this machine's agent work with the cloud sessions it starts (metadata only unless you choose content)");
4123
3984
  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) => {
4124
- const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-MPGQYXNR.js");
3985
+ const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-UJTFSXNM.js");
4125
3986
  if (readState()) throw new Error("a trace is already running here \u2014 `sky trace stop` it first");
4126
3987
  const scopes = parseContentScopes(opts.capture);
4127
3988
  const chosen = chosenPaths(opts.file ?? [], opts.dir ?? []);
@@ -4156,7 +4017,7 @@ traceCmd.command("start").description("start a trace: a grant for this machine,
4156
4017
  }));
4157
4018
  traceCmd.command("hook").description("record one Claude Code hook event read from stdin (run by the installed hooks; never fails, prints nothing)").action(async () => {
4158
4019
  try {
4159
- const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-MPGQYXNR.js");
4020
+ const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-UJTFSXNM.js");
4160
4021
  const chunks = [];
4161
4022
  for await (const chunk of process.stdin) chunks.push(chunk);
4162
4023
  const input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
@@ -4169,14 +4030,14 @@ traceCmd.command("hook").description("record one Claude Code hook event read fro
4169
4030
  }
4170
4031
  });
4171
4032
  traceCmd.command("flush").description("send what the spool holds, retrying with backoff").action(run(async () => {
4172
- const { readState, flush, grantSender } = await import("./trace-MPGQYXNR.js");
4033
+ const { readState, flush, grantSender } = await import("./trace-UJTFSXNM.js");
4173
4034
  const state = readState();
4174
4035
  if (!state) throw new Error("no trace is running here");
4175
4036
  const outcome = await flush(grantSender(state), { attempts: 4 });
4176
4037
  console.log(`sent ${outcome.sent}, refused ${outcome.refused}, given up ${outcome.expired}, still pending ${outcome.pending}${outcome.stopped ? ` (stopped: ${outcome.stopped})` : ""}`);
4177
4038
  }));
4178
4039
  traceCmd.command("status").description("the running trace and its spool").action(run(async () => {
4179
- const { readState, spoolStatus } = await import("./trace-MPGQYXNR.js");
4040
+ const { readState, spoolStatus } = await import("./trace-UJTFSXNM.js");
4180
4041
  const state = readState();
4181
4042
  if (!state) {
4182
4043
  console.log("No trace is running here.");
@@ -4186,7 +4047,7 @@ traceCmd.command("status").description("the running trace and its spool").action
4186
4047
  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`);
4187
4048
  }));
4188
4049
  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) => {
4189
- const { readState } = await import("./trace-MPGQYXNR.js");
4050
+ const { readState } = await import("./trace-UJTFSXNM.js");
4190
4051
  const ref = id ?? readState()?.trace_id;
4191
4052
  if (!ref) throw new Error("name a trace, or start one here");
4192
4053
  const bundle = await api("GET", `/traces/${ref}`);
@@ -4213,7 +4074,7 @@ traceCmd.command("show [id]").description("the trace bundle: spans, the sessions
4213
4074
  for (const gap of bundle.gaps) console.log(` gap: ${gap.span_id} ${gap.reason}`);
4214
4075
  }));
4215
4076
  traceCmd.command("stop").description("end the root span, send what is left, and revoke this machine's grant").action(run(async () => {
4216
- const { readState, record, flush, grantSender, clearState } = await import("./trace-MPGQYXNR.js");
4077
+ const { readState, record, flush, grantSender, clearState } = await import("./trace-UJTFSXNM.js");
4217
4078
  const state = readState();
4218
4079
  if (!state) throw new Error("no trace is running here");
4219
4080
  if (state.root_span) record(() => [{ span_id: state.root_span, kind: "end", outcome: "completed" }]);
@@ -4222,6 +4083,135 @@ traceCmd.command("stop").description("end the root span, send what is left, and
4222
4083
  clearState();
4223
4084
  console.log(`Stopped ${state.trace_id}${outcome.pending ? ` (${outcome.pending} events could not be sent)` : ""}`);
4224
4085
  }));
4086
+ 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) => {
4087
+ const lib = await import("./trace-library-474QJTAZ.js");
4088
+ if (opts.hook) {
4089
+ try {
4090
+ let raw = payload ?? "";
4091
+ if (!raw && !process.stdin.isTTY) {
4092
+ const chunks = [];
4093
+ for await (const chunk of process.stdin) chunks.push(chunk);
4094
+ raw = Buffer.concat(chunks).toString("utf8");
4095
+ }
4096
+ if (!lib.loadSyncConfig().enabled) return;
4097
+ const ref = lib.hookSessionRef(raw);
4098
+ const { spawn: spawn4 } = await import("child_process");
4099
+ spawn4(process.execPath, [process.argv[1], "trace", "sync", "--quiet", ...ref ? ["--session", ref] : []], { detached: true, stdio: "ignore" }).unref();
4100
+ } catch {
4101
+ }
4102
+ return;
4103
+ }
4104
+ await run(async () => {
4105
+ const config = lib.loadSyncConfig();
4106
+ if (opts.disable) {
4107
+ lib.saveSyncConfig({ ...config, enabled: false });
4108
+ console.log("Trace sync is off on this machine. Uploaded traces stay in their workspaces until deleted there.");
4109
+ return;
4110
+ }
4111
+ const changedSettings = opts.enable || opts.route || opts.default || opts.keepLocal;
4112
+ if (changedSettings) {
4113
+ if (opts.enable && !apiToken()) throw new Error("not logged in \u2014 run `sky auth login` first");
4114
+ const rules = opts.route ? opts.route.map((spec) => {
4115
+ const at = spec.lastIndexOf("=");
4116
+ if (at <= 0 || at === spec.length - 1) throw new Error(`--route takes match=workspace, got "${spec}"`);
4117
+ return { match: spec.slice(0, at), workspace: spec.slice(at + 1) };
4118
+ }) : config.rules;
4119
+ const next = {
4120
+ ...config,
4121
+ rules,
4122
+ enabled: opts.enable ? true : config.enabled,
4123
+ enabledAt: opts.enable ? (/* @__PURE__ */ new Date()).toISOString() : config.enabledAt,
4124
+ fallback: opts.keepLocal ? void 0 : opts.default ?? config.fallback
4125
+ };
4126
+ lib.saveSyncConfig(next);
4127
+ console.log(`Trace sync is ${next.enabled ? "on" : "off"} on this machine. It reads only:`);
4128
+ for (const folder of lib.sourceFolders()) console.log(` ${folder}`);
4129
+ console.log("and sends each session, redacted here first, to:");
4130
+ for (const rule of next.rules) console.log(` folder path contains "${rule.match}" \u2192 ${rule.workspace}`);
4131
+ console.log(` anything else \u2192 ${next.fallback ?? "stays on this machine"}`);
4132
+ console.log(`A repository's .agentsky.json "workspace" wins over these rules.`);
4133
+ }
4134
+ if (opts.installSchedule) {
4135
+ const { join: join10 } = await import("path");
4136
+ const { homedir: homedir6 } = await import("os");
4137
+ const { execFileSync } = await import("child_process");
4138
+ const log = join10(homedir6(), ".config", "sky", "trace-sync.log");
4139
+ const plist = lib.installLaunchd([process.execPath, process.argv[1], "trace", "sync", "--quiet"], log);
4140
+ try {
4141
+ execFileSync("launchctl", ["unload", plist], { stdio: "ignore" });
4142
+ } catch {
4143
+ }
4144
+ execFileSync("launchctl", ["load", plist]);
4145
+ console.log(`Sweeping every 15 minutes (${plist}); a full re-read runs weekly. Log: ${log}`);
4146
+ }
4147
+ if (opts.status) {
4148
+ const { readFileSync: readFileSync12 } = await import("fs");
4149
+ const { join: join10 } = await import("path");
4150
+ let state = { sessions: {} };
4151
+ try {
4152
+ state = JSON.parse(readFileSync12(join10(lib.syncConfigPath(), "..", "trace-sync-state.json"), "utf8"));
4153
+ } catch {
4154
+ }
4155
+ const counts = /* @__PURE__ */ new Map();
4156
+ for (const entry2 of Object.values(state.sessions)) {
4157
+ const label = entry2.status === "stored" ? `stored in ${entry2.workspace}` : entry2.status;
4158
+ counts.set(label, (counts.get(label) ?? 0) + 1);
4159
+ }
4160
+ console.log(`Trace sync is ${config.enabled ? "on" : "off"}${config.enabledAt ? ` (since ${config.enabledAt})` : ""}. Last full sweep: ${state.lastFullAt ?? "never"}.`);
4161
+ for (const [label, count] of [...counts].sort((a, b) => b[1] - a[1])) console.log(` ${count} ${label}`);
4162
+ return;
4163
+ }
4164
+ if (changedSettings && !opts.full && !opts.session) return;
4165
+ const report = await lib.syncTraces({
4166
+ full: opts.full,
4167
+ only: opts.session,
4168
+ dryRun: opts.dryRun,
4169
+ limit: opts.limit,
4170
+ onProgress: opts.quiet ? void 0 : (line) => console.log(line)
4171
+ });
4172
+ if (!report) {
4173
+ if (!opts.quiet) console.log("Another sync is running on this machine; it will pick these up.");
4174
+ return;
4175
+ }
4176
+ if (!opts.quiet || report.failed) {
4177
+ const where = Object.entries(report.byWorkspace).map(([workspace2, count]) => `${count} \u2192 ${workspace2}`).join(", ");
4178
+ console.log(`${opts.dryRun ? "Would upload" : "Uploaded"} ${report.uploaded}${where ? ` (${where})` : ""}; unchanged ${report.unchanged}; kept local ${report.local}; empty ${report.empty}; another member's ${report.owned}; failed ${report.failed}. Scanned ${report.scanned}, ${report.changed} changed.`);
4179
+ for (const error of report.errors.slice(0, 20)) console.error(` ${error}`);
4180
+ }
4181
+ if (report.failed) process.exitCode = 1;
4182
+ })();
4183
+ });
4184
+ 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) => {
4185
+ const { libraryRequest } = await import("./trace-library-474QJTAZ.js");
4186
+ const workspace2 = opts.workspace ?? currentUniverse();
4187
+ if (!workspace2) throw new Error("name a workspace with --workspace");
4188
+ const params = new URLSearchParams({ q: words.join(" "), scope: "traces", limit: String(opts.limit), ...opts.cursor ? { cursor: opts.cursor } : {} });
4189
+ const page = await libraryRequest("GET", `/context/search?${params}`, workspace2);
4190
+ if (opts.json) {
4191
+ console.log(JSON.stringify(page, null, 2));
4192
+ return;
4193
+ }
4194
+ if (!page.hits.length) console.log(page.partial ? `No hits, but the search was cut short (${page.coverage}).` : "No hits.");
4195
+ for (const { hit } of page.hits) {
4196
+ console.log(`${hit.occurredAt?.slice(0, 16).replace("T", " ") ?? " "} ${hit.title}`);
4197
+ console.log(` ${hit.snippet.replace(/\s+/g, " ")}`);
4198
+ }
4199
+ if (page.nextCursor) console.log(`More: sky trace search ${JSON.stringify(words.join(" "))} --workspace ${workspace2} --cursor ${page.nextCursor}`);
4200
+ }));
4201
+ 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) => {
4202
+ const { libraryRequest } = await import("./trace-library-474QJTAZ.js");
4203
+ const workspace2 = opts.workspace ?? currentUniverse();
4204
+ if (!workspace2) throw new Error("name a workspace with --workspace");
4205
+ const page = await libraryRequest("GET", `/trace-library?limit=${opts.limit}`, workspace2);
4206
+ if (opts.json) {
4207
+ console.log(JSON.stringify(page, null, 2));
4208
+ return;
4209
+ }
4210
+ console.log(`${page.total} sessions in ${workspace2}'s trace library`);
4211
+ for (const trace of page.traces) {
4212
+ console.log(`${trace.updated_at.slice(0, 16).replace("T", " ")} ${trace.harness.padEnd(11)} ${(trace.project ?? "").padEnd(16)} ${String(trace.messages).padStart(5)} msgs ${trace.title ?? trace.session_id}`);
4213
+ }
4214
+ }));
4225
4215
  var workerCmd = program.command("worker").description("run a pod on this machine and connect it to a worker pool");
4226
4216
  workerCmd.command("start <pool>").description("start a worker connected to the given pool (wpool_\u2026 id)").option("--token <token>", "bearer token (default: $SKY_WORKER_TOKEN)").option("--label <label>", "a human-readable name for this machine", "cli").action(async (poolId, options) => {
4227
4217
  const { startWorker } = await import("./worker-6Q4K4HZW.js");
@@ -95,6 +95,8 @@ declare const agentAddonsSchema: z.ZodObject<{
95
95
  "meta.transcribe": "meta.transcribe";
96
96
  "minimax.tts": "minimax.tts";
97
97
  "elevenlabs.sound": "elevenlabs.sound";
98
+ "elevenlabs.tts": "elevenlabs.tts";
99
+ "elevenlabs.music": "elevenlabs.music";
98
100
  "zai.transcribe": "zai.transcribe";
99
101
  "openai.transcribe": "openai.transcribe";
100
102
  "fish-audio.tts": "fish-audio.tts";
@@ -235,6 +237,8 @@ declare const createProductAgentSchema: z.ZodObject<{
235
237
  "meta.transcribe": "meta.transcribe";
236
238
  "minimax.tts": "minimax.tts";
237
239
  "elevenlabs.sound": "elevenlabs.sound";
240
+ "elevenlabs.tts": "elevenlabs.tts";
241
+ "elevenlabs.music": "elevenlabs.music";
238
242
  "zai.transcribe": "zai.transcribe";
239
243
  "openai.transcribe": "openai.transcribe";
240
244
  "fish-audio.tts": "fish-audio.tts";
@@ -375,6 +379,8 @@ declare const prepareProductSessionSchema: z.ZodObject<{
375
379
  "meta.transcribe": "meta.transcribe";
376
380
  "minimax.tts": "minimax.tts";
377
381
  "elevenlabs.sound": "elevenlabs.sound";
382
+ "elevenlabs.tts": "elevenlabs.tts";
383
+ "elevenlabs.music": "elevenlabs.music";
378
384
  "zai.transcribe": "zai.transcribe";
379
385
  "openai.transcribe": "openai.transcribe";
380
386
  "fish-audio.tts": "fish-audio.tts";
package/dist/products.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  skillsArraySchema,
9
9
  systemMessageEventSchema,
10
10
  userMessageEventSchema
11
- } from "./chunk-WC7GJSAJ.js";
11
+ } from "./chunk-XXDQKVPS.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-WC7GJSAJ.js";
4
+ } from "./chunk-XXDQKVPS.js";
5
5
  import "./chunk-MLKGABMK.js";
6
6
 
7
7
  // ../../packages/chat/src/stream.ts
@@ -97,6 +97,16 @@ var internalEventSchema = external_exports.object({
97
97
  * session.deleted) — emitted verbatim; the host already persisted it. */
98
98
  lifecycle: external_exports.object({ id: external_exports.string().min(1), type: external_exports.string().min(1) }).passthrough().optional()
99
99
  });
100
+ function personalContextForPod(body, supported) {
101
+ const { personal_context: grant, ...rest } = body;
102
+ if (!grant || supported) return body;
103
+ const text = `This runtime cannot receive the private personal-context credential file. Use this value instead, as X-Personal-Context on memory and context API requests and as ACTL_PERSONAL_CONTEXT for actl grep: ${grant}. Never print, persist or share it.`;
104
+ const parts = [...rest.parts, { type: "text", index: Math.max(-1, ...rest.parts.map((part) => part.index)) + 1, text }];
105
+ if (!rest.events) return { ...rest, parts };
106
+ const previous = rest.events.find((event) => event.type === "system.message");
107
+ const system = { type: "system.message", content: [{ type: "text", text: [...previous?.type === "system.message" ? previous.content.map((block) => block.text) : [], text].join("\n\n") }] };
108
+ return { ...rest, parts, events: [...rest.events.filter((event) => event.type !== "system.message"), system] };
109
+ }
100
110
  function podMessageBody(messageId, turn, resourcesRevision = 0) {
101
111
  if ("events" in turn) {
102
112
  const envelope = {
@@ -144,6 +154,7 @@ function classifyPodSendStatus(status, detail) {
144
154
  if (status >= 500) return { kind: "uncertain", reason: "server_error", status, detail };
145
155
  return { kind: "rejected", status, detail };
146
156
  }
157
+ var PERSONAL_CONTEXT_PODS = /* @__PURE__ */ new Set();
147
158
  async function sendPodMessage(baseUrl, body, options = {}) {
148
159
  const headers = {
149
160
  "content-type": "application/json",
@@ -152,6 +163,11 @@ async function sendPodMessage(baseUrl, body, options = {}) {
152
163
  if (options.idempotencyKey) headers["idempotency-key"] = options.idempotencyKey;
153
164
  let res;
154
165
  try {
166
+ if (body.personal_context && !PERSONAL_CONTEXT_PODS.has(baseUrl)) {
167
+ const health = await getPodHealth(baseUrl, options);
168
+ if (health.personalContext) PERSONAL_CONTEXT_PODS.add(baseUrl);
169
+ else if (health.ok) body = personalContextForPod(body, false);
170
+ }
155
171
  const user = body.events?.find((event) => event.type === "user.message");
156
172
  const mode = user?.type === "user.message" ? user.interaction_mode : body.interaction_mode;
157
173
  if (mode) {
@@ -197,7 +213,8 @@ async function getPodHealth(baseUrl, options = {}) {
197
213
  status: res.status,
198
214
  state: typeof body.state === "string" ? body.state : void 0,
199
215
  openMessageId: typeof open === "string" && open ? open : null,
200
- interactionModes: Array.isArray(body.interaction_modes) ? body.interaction_modes.filter((mode) => mode === "plan" || mode === "build") : void 0
216
+ interactionModes: Array.isArray(body.interaction_modes) ? body.interaction_modes.filter((mode) => mode === "plan" || mode === "build") : void 0,
217
+ personalContext: body.personal_context === true
201
218
  };
202
219
  } catch {
203
220
  return { ok: false, openMessageId: null };
@@ -295,6 +312,7 @@ export {
295
312
  interruptSession,
296
313
  legacyPartsOf,
297
314
  parseSseStream,
315
+ personalContextForPod,
298
316
  podMessageBody,
299
317
  postSessionEvents,
300
318
  sendPodMessage,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  USER_AGENT,
3
3
  configPath
4
- } from "./chunk-RWPPIEHH.js";
4
+ } from "./chunk-RF5JICNX.js";
5
5
  import {
6
6
  TRACE_CONTENT_SCOPES,
7
7
  redactTraceText
8
- } from "./chunk-WC7GJSAJ.js";
8
+ } from "./chunk-XXDQKVPS.js";
9
9
  import "./chunk-MLKGABMK.js";
10
10
 
11
11
  // src/trace.ts