@sideboard-ai/core 0.1.133 → 0.1.136

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.
Files changed (35) hide show
  1. package/dist/{agents-NHS6P25H.js → agents-ELWR7A2T.js} +5 -5
  2. package/dist/{agents-SOU5GPWI.js → agents-QNTTLMG2.js} +5 -5
  3. package/dist/{chunk-Y645NFYN.js → chunk-4XKUHP6G.js} +2 -2
  4. package/dist/{chunk-4YAVXWFR.js → chunk-CYM5DCHI.js} +39 -5
  5. package/dist/{chunk-LTP7GHIF.js → chunk-GSKRGF7B.js} +286 -5
  6. package/dist/{chunk-WEU5M7BW.js → chunk-HQQNLDVC.js} +1 -1
  7. package/dist/{chunk-IM36S4HZ.js → chunk-IFZ4MOTN.js} +3 -3
  8. package/dist/{chunk-G6X6UFJO.js → chunk-J5IBSVB3.js} +42 -12
  9. package/dist/{chunk-AK7SWE2U.js → chunk-JPBRMUM6.js} +9 -5
  10. package/dist/{chunk-YEIH7P7D.js → chunk-K5YT5GX2.js} +244 -5
  11. package/dist/{chunk-3UVVWNHF.js → chunk-MDCKV2NF.js} +2 -2
  12. package/dist/{chunk-HLJUNJBF.js → chunk-PM3C2J6K.js} +42 -12
  13. package/dist/{chunk-Q6XQXCOM.js → chunk-QAV3HGVS.js} +1 -1
  14. package/dist/{chunk-TG7YU2KG.js → chunk-TIGKDMIA.js} +144 -271
  15. package/dist/{chunk-6CRKPD4C.js → chunk-TQ4S5AGJ.js} +3 -3
  16. package/dist/{chunk-VSON7EJ6.js → chunk-WS5LFFU3.js} +140 -222
  17. package/dist/{coordinator-prompt-DXHEDRRN.js → coordinator-prompt-2OWSUAUR.js} +3 -3
  18. package/dist/{coordinator-prompt-XF7LVOUN.js → coordinator-prompt-IPL4Z6SL.js} +3 -3
  19. package/dist/{global-workspace-2LO5ATOH.js → global-workspace-JDUCUL7S.js} +4 -4
  20. package/dist/{global-workspace-MLQNULOU.js → global-workspace-NIKZAKOO.js} +4 -4
  21. package/dist/index.cjs +873 -628
  22. package/dist/index.d.cts +26 -1
  23. package/dist/index.d.ts +26 -1
  24. package/dist/index.js +42 -19
  25. package/dist/mcp/run-stdio.cjs +700 -498
  26. package/dist/mcp/run-stdio.js +22 -11
  27. package/dist/{orchestrator-P2SYHVLS.js → orchestrator-6I47JMU2.js} +7 -7
  28. package/dist/{orchestrator-KR5RA5B7.js → orchestrator-KK3CUW37.js} +7 -7
  29. package/dist/{thread-store-5MLYY35S.js → thread-store-CRLQJ2HM.js} +3 -1
  30. package/dist/{thread-store-F6BCARQG.js → thread-store-FADXSMEJ.js} +3 -1
  31. package/dist/{workspaces-MINB764G.js → workspaces-5EWNNALF.js} +5 -5
  32. package/dist/{workspaces-ARWPZG6F.js → workspaces-KZA3TCEE.js} +5 -5
  33. package/dist/{worktree-EBZMUUAL.js → worktree-BC6XDMQK.js} +2 -2
  34. package/dist/{worktree-S7MKMYAT.js → worktree-MX7XBX6Z.js} +2 -2
  35. package/package.json +1 -1
@@ -2,18 +2,22 @@
2
2
 
3
3
  import {
4
4
  ensureGlobalCoordinatorCwd
5
- } from "./chunk-6CRKPD4C.js";
5
+ } from "./chunk-TQ4S5AGJ.js";
6
6
  import {
7
7
  allocateTeamName,
8
8
  takenSlugsFromThread,
9
9
  teamSlugFromName
10
- } from "./chunk-Q6XQXCOM.js";
10
+ } from "./chunk-QAV3HGVS.js";
11
+ import {
12
+ ATTACHMENTS_DIR,
13
+ attachmentsGitignoreBody
14
+ } from "./chunk-B3SJXYIJ.js";
11
15
  import {
12
16
  createEmptyThread,
13
17
  listThreads,
14
18
  updateThread,
15
19
  writeThread
16
- } from "./chunk-G6X6UFJO.js";
20
+ } from "./chunk-J5IBSVB3.js";
17
21
  import {
18
22
  resolveNewThreadOptions,
19
23
  resolveThreadDefaults
@@ -44,6 +48,233 @@ var CLOUD_COORDINATOR_TIMEOUT_REPLY = [
44
48
  "What do you want me to do? (retry later, wait, force-stop, rephrase, or cancel)"
45
49
  ].join(" ");
46
50
 
51
+ // src/composer/stage-files.ts
52
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
53
+ import { basename, extname, join } from "path";
54
+ import { randomUUID } from "crypto";
55
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
56
+ "png",
57
+ "jpg",
58
+ "jpeg",
59
+ "gif",
60
+ "webp",
61
+ "svg",
62
+ "bmp",
63
+ "ico"
64
+ ]);
65
+ var IMAGE_MIME_BY_EXT = {
66
+ png: "image/png",
67
+ jpg: "image/jpeg",
68
+ jpeg: "image/jpeg",
69
+ gif: "image/gif",
70
+ webp: "image/webp",
71
+ svg: "image/svg+xml",
72
+ bmp: "image/bmp",
73
+ ico: "image/x-icon"
74
+ };
75
+ var MAX_INLINE_BYTES = 4e5;
76
+ var MAX_PREVIEW_BYTES = 5e6;
77
+ function fileExtension(filePath) {
78
+ const base = basename(filePath).toLowerCase();
79
+ return base.includes(".") ? base.split(".").pop() || "" : "";
80
+ }
81
+ function isImageFilePath(filePath) {
82
+ return IMAGE_EXTENSIONS.has(fileExtension(filePath));
83
+ }
84
+ function imageMimeType(filePath) {
85
+ return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
86
+ }
87
+ function ensureAttachmentsDir(worktreePath) {
88
+ const dir = join(worktreePath, ATTACHMENTS_DIR);
89
+ mkdirSync(dir, { recursive: true });
90
+ const gi = join(dir, ".gitignore");
91
+ if (!existsSync(gi)) {
92
+ writeFileSync(gi, attachmentsGitignoreBody(), "utf8");
93
+ }
94
+ return dir;
95
+ }
96
+ function uniqueAttachmentName(dir, originalName) {
97
+ const safe = originalName.replace(/[/\\]/g, "_") || "file";
98
+ if (!existsSync(join(dir, safe))) return safe;
99
+ const ext = extname(safe);
100
+ const stem = ext ? safe.slice(0, -ext.length) : safe;
101
+ for (let i = 1; i < 1e4; i++) {
102
+ const candidate = `${stem}-${i}${ext}`;
103
+ if (!existsSync(join(dir, candidate))) return candidate;
104
+ }
105
+ return `${stem}-${randomUUID()}${ext}`;
106
+ }
107
+ function previewDataUrlFromBuf(filePath, buf) {
108
+ if (!isImageFilePath(filePath)) return void 0;
109
+ if (buf.length > MAX_PREVIEW_BYTES) return void 0;
110
+ return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
111
+ }
112
+ function attachmentFromBuffer(name, buf, opts) {
113
+ const previewDataUrl = previewDataUrlFromBuf(name, buf);
114
+ if (isImageFilePath(name)) {
115
+ const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
116
+ return {
117
+ id: randomUUID(),
118
+ name,
119
+ kind: "file",
120
+ path: opts.path,
121
+ previewDataUrl,
122
+ content: [
123
+ `Image attached: ${pathHint}`,
124
+ opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
125
+ ].join("\n")
126
+ };
127
+ }
128
+ if (buf.length > MAX_INLINE_BYTES) {
129
+ return {
130
+ id: randomUUID(),
131
+ name,
132
+ kind: "file",
133
+ path: opts.path,
134
+ content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
135
+ };
136
+ }
137
+ if (buf.includes(0)) {
138
+ return {
139
+ id: randomUUID(),
140
+ name,
141
+ kind: "file",
142
+ path: opts.path,
143
+ content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
144
+ };
145
+ }
146
+ return {
147
+ id: randomUUID(),
148
+ name,
149
+ kind: "file",
150
+ path: opts.path,
151
+ content: buf.toString("utf8")
152
+ };
153
+ }
154
+ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
155
+ if (absolutePaths.length === 0) return [];
156
+ const dir = ensureAttachmentsDir(worktreePath);
157
+ const out = [];
158
+ for (const abs of absolutePaths) {
159
+ const originalName = basename(abs);
160
+ try {
161
+ const st = statSync(abs);
162
+ if (!st.isFile()) continue;
163
+ const name = uniqueAttachmentName(dir, originalName);
164
+ const destAbs = join(dir, name);
165
+ copyFileSync(abs, destAbs);
166
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
167
+ const buf = readFileSync(destAbs);
168
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
169
+ } catch (err) {
170
+ out.push({
171
+ id: randomUUID(),
172
+ name: originalName,
173
+ kind: "file",
174
+ content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
175
+ });
176
+ }
177
+ }
178
+ return out;
179
+ }
180
+ function stageBuffersAsAttachments(worktreePath, buffers) {
181
+ if (buffers.length === 0) return [];
182
+ const dir = ensureAttachmentsDir(worktreePath);
183
+ const out = [];
184
+ for (const item of buffers) {
185
+ const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
186
+ try {
187
+ const buf = Buffer.from(item.dataBase64, "base64");
188
+ const name = uniqueAttachmentName(dir, originalName);
189
+ const destAbs = join(dir, name);
190
+ writeFileSync(destAbs, buf);
191
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
192
+ out.push(attachmentFromBuffer(name, buf, { path: rel }));
193
+ } catch (err) {
194
+ out.push({
195
+ id: randomUUID(),
196
+ name: originalName,
197
+ kind: "file",
198
+ content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
199
+ });
200
+ }
201
+ }
202
+ return out;
203
+ }
204
+ function isWorktreeRelativePath(p) {
205
+ if (!p || p.includes("..")) return false;
206
+ if (p.startsWith("/")) return false;
207
+ if (/^[A-Za-z]:[\\/]/.test(p)) return false;
208
+ return true;
209
+ }
210
+ function dataUrlToBase64(url) {
211
+ if (!url) return null;
212
+ const m = /^data:[^;]+;base64,(.+)$/s.exec(url);
213
+ return m?.[1] ?? null;
214
+ }
215
+ var IMAGE_HINT_RE = /^Image attached:/;
216
+ var PLACEHOLDER_CONTENT_RE = /^\((could not |file too large|binary file|not a file|invalid path)/;
217
+ function persistPendingFileAttachments(worktreePath, attachments) {
218
+ if (attachments.length === 0) return attachments;
219
+ const keep = [];
220
+ const buffers = [];
221
+ for (const att of attachments) {
222
+ if (att.kind !== "file") {
223
+ keep.push(att);
224
+ continue;
225
+ }
226
+ if (att.path && isWorktreeRelativePath(att.path)) {
227
+ keep.push(att);
228
+ continue;
229
+ }
230
+ const fromPreview = dataUrlToBase64(att.previewDataUrl);
231
+ if (fromPreview) {
232
+ buffers.push({ name: att.name, dataBase64: fromPreview });
233
+ continue;
234
+ }
235
+ if (att.content && !IMAGE_HINT_RE.test(att.content) && !PLACEHOLDER_CONTENT_RE.test(att.content)) {
236
+ buffers.push({
237
+ name: att.name,
238
+ dataBase64: Buffer.from(att.content, "utf8").toString("base64")
239
+ });
240
+ continue;
241
+ }
242
+ keep.push(att);
243
+ }
244
+ if (buffers.length === 0) return attachments;
245
+ return [...keep, ...stageBuffersAsAttachments(worktreePath, buffers)];
246
+ }
247
+ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
248
+ const out = [];
249
+ for (const rel of relativePaths) {
250
+ if (!rel || rel.includes("..") || rel.startsWith("/")) {
251
+ out.push({
252
+ id: randomUUID(),
253
+ name: basename(rel) || "file",
254
+ kind: "file",
255
+ content: `(invalid path: ${rel})`
256
+ });
257
+ continue;
258
+ }
259
+ const name = basename(rel);
260
+ try {
261
+ const abs = join(worktreePath, rel);
262
+ const st = statSync(abs);
263
+ if (!st.isFile()) continue;
264
+ const buf = readFileSync(abs);
265
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
266
+ } catch (err) {
267
+ out.push({
268
+ id: randomUUID(),
269
+ name,
270
+ kind: "file",
271
+ content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
272
+ });
273
+ }
274
+ }
275
+ return out;
276
+ }
277
+
47
278
  // src/agents/orchestrator-capable.ts
48
279
  var ORCHESTRATOR_AGENT_KINDS = [
49
280
  "claude",
@@ -139,6 +370,7 @@ function createGlobalChat(opts) {
139
370
  fast: opts.fast
140
371
  });
141
372
  const agent = assertOrchestratorCapableAgent(resolved.agent);
373
+ const worktreePath = globalAgentCwd();
142
374
  const thread = createEmptyThread({
143
375
  title,
144
376
  // Stick nicknames the same way chat tabs do (avoid later sync overwrites).
@@ -146,7 +378,7 @@ function createGlobalChat(opts) {
146
378
  sourceType: "orchestration",
147
379
  sourceRef,
148
380
  branchName: "global",
149
- worktreePath: globalAgentCwd(),
381
+ worktreePath,
150
382
  repoPath: GLOBAL_WORKSPACE_ID,
151
383
  agent,
152
384
  autonomy: opts.autonomy ?? "default",
@@ -154,7 +386,10 @@ function createGlobalChat(opts) {
154
386
  effort: resolved.effort,
155
387
  fast: resolved.fast,
156
388
  planMode: Boolean(opts.planMode),
157
- attachments: opts.attachments ?? [],
389
+ attachments: persistPendingFileAttachments(
390
+ worktreePath,
391
+ opts.attachments ?? []
392
+ ),
158
393
  parentThreadId: opts.parentThreadId ?? null,
159
394
  status: "idle"
160
395
  });
@@ -276,6 +511,10 @@ function ensureCloudCoordinator(agent) {
276
511
  }
277
512
 
278
513
  export {
514
+ stageAbsolutePathsAsAttachments,
515
+ stageBuffersAsAttachments,
516
+ persistPendingFileAttachments,
517
+ attachmentsFromWorktreePaths,
279
518
  ORCHESTRATOR_AGENT_KINDS,
280
519
  isOrchestratorCapableAgent,
281
520
  assertOrchestratorCapableAgent,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  isGlobalRepoPath
3
- } from "./chunk-LTP7GHIF.js";
3
+ } from "./chunk-GSKRGF7B.js";
4
4
  import {
5
5
  ensureGhPreferOrigin,
6
6
  resolveRepoRoot
7
- } from "./chunk-WEU5M7BW.js";
7
+ } from "./chunk-HQQNLDVC.js";
8
8
  import {
9
9
  appDataDir
10
10
  } from "./chunk-QZQEXME2.js";
@@ -106,11 +106,29 @@ async function withThreadLock(id, fn) {
106
106
  if (release) await release();
107
107
  }
108
108
  }
109
+ var listCache = null;
110
+ function cacheForDir() {
111
+ const dir = threadsDir();
112
+ if (!listCache || listCache.dir !== dir) {
113
+ listCache = { dir, byId: /* @__PURE__ */ new Map(), listed: false };
114
+ }
115
+ return listCache;
116
+ }
117
+ function invalidateThreadListCache() {
118
+ listCache = null;
119
+ }
120
+ function rememberThread(thread) {
121
+ cacheForDir().byId.set(thread.id, thread);
122
+ }
109
123
  function readThread(id) {
124
+ const cached = cacheForDir().byId.get(id);
125
+ if (cached) return cached;
110
126
  const path = threadFilePath(id);
111
127
  if (!existsSync(path)) return null;
112
128
  const raw = readFileSync(path, "utf8");
113
- return normalizeThread(JSON.parse(raw));
129
+ const thread = normalizeThread(JSON.parse(raw));
130
+ rememberThread(thread);
131
+ return thread;
114
132
  }
115
133
  function writeThread(thread) {
116
134
  const path = threadFilePath(idPath(thread.id));
@@ -118,6 +136,7 @@ function writeThread(thread) {
118
136
  const next = { ...thread, updatedAt: nowIso() };
119
137
  writeFileSync(tmp, JSON.stringify(next, null, 2), "utf8");
120
138
  renameSync(tmp, path);
139
+ rememberThread(next);
121
140
  }
122
141
  function idPath(id) {
123
142
  return id;
@@ -127,22 +146,32 @@ function isThreadRecordFile(nameOrPath) {
127
146
  return name.endsWith(".json") && !name.endsWith(".live.json");
128
147
  }
129
148
  function listThreads(opts) {
130
- const files = readdirSync(threadsDir()).filter(isThreadRecordFile);
131
- const threads = files.map((f) => {
132
- try {
133
- return normalizeThread(
134
- JSON.parse(readFileSync(threadFilePath(f.replace(/\.json$/, "")), "utf8"))
135
- );
136
- } catch {
137
- return null;
149
+ const cache = cacheForDir();
150
+ if (!cache.listed) {
151
+ const files = readdirSync(threadsDir()).filter(isThreadRecordFile);
152
+ const byId = /* @__PURE__ */ new Map();
153
+ for (const f of files) {
154
+ try {
155
+ const thread = normalizeThread(
156
+ JSON.parse(readFileSync(threadFilePath(f.replace(/\.json$/, "")), "utf8"))
157
+ );
158
+ if (typeof thread.id === "string" && thread.id.length > 0) {
159
+ byId.set(thread.id, thread);
160
+ }
161
+ } catch {
162
+ }
138
163
  }
139
- }).filter(
140
- (t) => t !== null && typeof t.id === "string" && t.id.length > 0
141
- ).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
164
+ cache.byId = byId;
165
+ cache.listed = true;
166
+ }
167
+ const threads = [...cache.byId.values()].sort(
168
+ (a, b) => b.updatedAt.localeCompare(a.updatedAt)
169
+ );
142
170
  if (opts?.includeArchived) return threads;
143
171
  return threads.filter((t) => t.status !== "archived");
144
172
  }
145
173
  function deleteThreadRecord(id) {
174
+ cacheForDir().byId.delete(id);
146
175
  const path = threadFilePath(id);
147
176
  if (existsSync(path)) unlinkSync(path);
148
177
  const lock = threadLockPath(id);
@@ -180,6 +209,7 @@ export {
180
209
  normalizeThread,
181
210
  createEmptyThread,
182
211
  withThreadLock,
212
+ invalidateThreadListCache,
183
213
  readThread,
184
214
  writeThread,
185
215
  isThreadRecordFile,
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-B3SJXYIJ.js";
6
6
  import {
7
7
  listThreads
8
- } from "./chunk-G6X6UFJO.js";
8
+ } from "./chunk-J5IBSVB3.js";
9
9
  import {
10
10
  gh,
11
11
  git,