@sideboard-ai/core 0.1.23 → 0.1.30

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.cjs CHANGED
@@ -5241,6 +5241,9 @@ __export(index_exports, {
5241
5241
  applyAppEnvironment: () => applyAppEnvironment,
5242
5242
  applyCompaction: () => applyCompaction,
5243
5243
  applyThreadIntoMain: () => applyThreadIntoMain,
5244
+ attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
5245
+ attachmentsFromBuffers: () => attachmentsFromBuffers,
5246
+ attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
5244
5247
  autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
5245
5248
  autoRenameBranchEnabled: () => autoRenameBranchEnabled,
5246
5249
  autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
@@ -5363,6 +5366,7 @@ __export(index_exports, {
5363
5366
  isGhRateLimitError: () => isGhRateLimitError,
5364
5367
  isGlobalRepoPath: () => isGlobalRepoPath,
5365
5368
  isGlobalThread: () => isGlobalThread,
5369
+ isImageFilePath: () => isImageFilePath,
5366
5370
  isLinearConnected: () => isLinearConnected,
5367
5371
  isOrchestratorThread: () => isOrchestratorThread,
5368
5372
  isPlaceholderBranch: () => isPlaceholderBranch,
@@ -5451,6 +5455,8 @@ __export(index_exports, {
5451
5455
  slugify: () => slugify,
5452
5456
  spawnAgentTurn: () => spawnAgentTurn,
5453
5457
  splitForCompaction: () => splitForCompaction,
5458
+ stageAbsolutePathsAsAttachments: () => stageAbsolutePathsAsAttachments,
5459
+ stageBuffersAsAttachments: () => stageBuffersAsAttachments,
5454
5460
  startDevServer: () => startDevServer,
5455
5461
  startMcpServer: () => startMcpServer,
5456
5462
  startOrchestration: () => startOrchestration,
@@ -7257,6 +7263,9 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
7257
7263
  parts.push("");
7258
7264
  parts.push(`## Attachment: ${att.name}`);
7259
7265
  parts.push(`Kind: ${att.kind}`);
7266
+ if (att.path) {
7267
+ parts.push(`Path in worktree: \`${att.path}\``);
7268
+ }
7260
7269
  parts.push("");
7261
7270
  parts.push(att.content);
7262
7271
  }
@@ -7342,10 +7351,239 @@ function buildDiffCommentAttachment(input) {
7342
7351
  id: input.id ?? `diff-comment-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
7343
7352
  name,
7344
7353
  kind: "diff-comment",
7354
+ path: input.path.trim(),
7345
7355
  content
7346
7356
  };
7347
7357
  }
7348
7358
 
7359
+ // src/composer/stage-files.ts
7360
+ var import_node_fs19 = require("fs");
7361
+ var import_node_path19 = require("path");
7362
+ var import_node_crypto2 = require("crypto");
7363
+ var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
7364
+ "png",
7365
+ "jpg",
7366
+ "jpeg",
7367
+ "gif",
7368
+ "webp",
7369
+ "svg",
7370
+ "bmp",
7371
+ "ico"
7372
+ ]);
7373
+ var IMAGE_MIME_BY_EXT = {
7374
+ png: "image/png",
7375
+ jpg: "image/jpeg",
7376
+ jpeg: "image/jpeg",
7377
+ gif: "image/gif",
7378
+ webp: "image/webp",
7379
+ svg: "image/svg+xml",
7380
+ bmp: "image/bmp",
7381
+ ico: "image/x-icon"
7382
+ };
7383
+ var ATTACHMENTS_DIR = ".sideboard/attachments";
7384
+ var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
7385
+ *
7386
+ !.gitignore
7387
+ `;
7388
+ var MAX_INLINE_BYTES = 4e5;
7389
+ var MAX_PREVIEW_BYTES = 5e6;
7390
+ function fileExtension(filePath) {
7391
+ const base = (0, import_node_path19.basename)(filePath).toLowerCase();
7392
+ return base.includes(".") ? base.split(".").pop() || "" : "";
7393
+ }
7394
+ function isImageFilePath(filePath) {
7395
+ return IMAGE_EXTENSIONS2.has(fileExtension(filePath));
7396
+ }
7397
+ function imageMimeType(filePath) {
7398
+ return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
7399
+ }
7400
+ function ensureAttachmentsDir(worktreePath) {
7401
+ const dir = (0, import_node_path19.join)(worktreePath, ATTACHMENTS_DIR);
7402
+ (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
7403
+ const gi = (0, import_node_path19.join)(dir, ".gitignore");
7404
+ if (!(0, import_node_fs19.existsSync)(gi)) {
7405
+ (0, import_node_fs19.writeFileSync)(gi, ATTACHMENTS_GITIGNORE, "utf8");
7406
+ }
7407
+ return dir;
7408
+ }
7409
+ function uniqueAttachmentName(dir, originalName) {
7410
+ const safe = originalName.replace(/[/\\]/g, "_") || "file";
7411
+ if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
7412
+ const ext = (0, import_node_path19.extname)(safe);
7413
+ const stem = ext ? safe.slice(0, -ext.length) : safe;
7414
+ for (let i = 1; i < 1e4; i++) {
7415
+ const candidate = `${stem}-${i}${ext}`;
7416
+ if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
7417
+ }
7418
+ return `${stem}-${(0, import_node_crypto2.randomUUID)()}${ext}`;
7419
+ }
7420
+ function previewDataUrlFromBuf(filePath, buf) {
7421
+ if (!isImageFilePath(filePath)) return void 0;
7422
+ if (buf.length > MAX_PREVIEW_BYTES) return void 0;
7423
+ return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
7424
+ }
7425
+ function attachmentFromBuffer(name, buf, opts) {
7426
+ const previewDataUrl = previewDataUrlFromBuf(name, buf);
7427
+ if (isImageFilePath(name)) {
7428
+ const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
7429
+ return {
7430
+ id: (0, import_node_crypto2.randomUUID)(),
7431
+ name,
7432
+ kind: "file",
7433
+ path: opts.path,
7434
+ previewDataUrl,
7435
+ content: [
7436
+ `Image attached: ${pathHint}`,
7437
+ 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."
7438
+ ].join("\n")
7439
+ };
7440
+ }
7441
+ if (buf.length > MAX_INLINE_BYTES) {
7442
+ return {
7443
+ id: (0, import_node_crypto2.randomUUID)(),
7444
+ name,
7445
+ kind: "file",
7446
+ path: opts.path,
7447
+ 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)`
7448
+ };
7449
+ }
7450
+ if (buf.includes(0)) {
7451
+ return {
7452
+ id: (0, import_node_crypto2.randomUUID)(),
7453
+ name,
7454
+ kind: "file",
7455
+ path: opts.path,
7456
+ content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
7457
+ };
7458
+ }
7459
+ return {
7460
+ id: (0, import_node_crypto2.randomUUID)(),
7461
+ name,
7462
+ kind: "file",
7463
+ path: opts.path,
7464
+ content: buf.toString("utf8")
7465
+ };
7466
+ }
7467
+ function attachmentFromAbsolutePath(absolutePath) {
7468
+ const name = (0, import_node_path19.basename)(absolutePath);
7469
+ try {
7470
+ const st = (0, import_node_fs19.statSync)(absolutePath);
7471
+ if (!st.isFile()) {
7472
+ return {
7473
+ id: (0, import_node_crypto2.randomUUID)(),
7474
+ name,
7475
+ kind: "file",
7476
+ content: `(not a file: ${absolutePath})`
7477
+ };
7478
+ }
7479
+ const buf = (0, import_node_fs19.readFileSync)(absolutePath);
7480
+ return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
7481
+ } catch (err) {
7482
+ return {
7483
+ id: (0, import_node_crypto2.randomUUID)(),
7484
+ name,
7485
+ kind: "file",
7486
+ content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
7487
+ };
7488
+ }
7489
+ }
7490
+ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
7491
+ if (absolutePaths.length === 0) return [];
7492
+ const dir = ensureAttachmentsDir(worktreePath);
7493
+ const out = [];
7494
+ for (const abs of absolutePaths) {
7495
+ const originalName = (0, import_node_path19.basename)(abs);
7496
+ try {
7497
+ const st = (0, import_node_fs19.statSync)(abs);
7498
+ if (!st.isFile()) continue;
7499
+ const name = uniqueAttachmentName(dir, originalName);
7500
+ const destAbs = (0, import_node_path19.join)(dir, name);
7501
+ (0, import_node_fs19.copyFileSync)(abs, destAbs);
7502
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
7503
+ const buf = (0, import_node_fs19.readFileSync)(destAbs);
7504
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
7505
+ } catch (err) {
7506
+ out.push({
7507
+ id: (0, import_node_crypto2.randomUUID)(),
7508
+ name: originalName,
7509
+ kind: "file",
7510
+ content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
7511
+ });
7512
+ }
7513
+ }
7514
+ return out;
7515
+ }
7516
+ function stageBuffersAsAttachments(worktreePath, buffers) {
7517
+ if (buffers.length === 0) return [];
7518
+ const dir = ensureAttachmentsDir(worktreePath);
7519
+ const out = [];
7520
+ for (const item of buffers) {
7521
+ const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
7522
+ try {
7523
+ const buf = Buffer.from(item.dataBase64, "base64");
7524
+ const name = uniqueAttachmentName(dir, originalName);
7525
+ const destAbs = (0, import_node_path19.join)(dir, name);
7526
+ (0, import_node_fs19.writeFileSync)(destAbs, buf);
7527
+ const rel = `${ATTACHMENTS_DIR}/${name}`;
7528
+ out.push(attachmentFromBuffer(name, buf, { path: rel }));
7529
+ } catch (err) {
7530
+ out.push({
7531
+ id: (0, import_node_crypto2.randomUUID)(),
7532
+ name: originalName,
7533
+ kind: "file",
7534
+ content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
7535
+ });
7536
+ }
7537
+ }
7538
+ return out;
7539
+ }
7540
+ function attachmentsFromBuffers(buffers) {
7541
+ return buffers.map((item) => {
7542
+ const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
7543
+ try {
7544
+ const buf = Buffer.from(item.dataBase64, "base64");
7545
+ return attachmentFromBuffer(name, buf, { sourceLabel: name });
7546
+ } catch (err) {
7547
+ return {
7548
+ id: (0, import_node_crypto2.randomUUID)(),
7549
+ name,
7550
+ kind: "file",
7551
+ content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
7552
+ };
7553
+ }
7554
+ });
7555
+ }
7556
+ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
7557
+ const out = [];
7558
+ for (const rel of relativePaths) {
7559
+ if (!rel || rel.includes("..") || rel.startsWith("/")) {
7560
+ out.push({
7561
+ id: (0, import_node_crypto2.randomUUID)(),
7562
+ name: (0, import_node_path19.basename)(rel) || "file",
7563
+ kind: "file",
7564
+ content: `(invalid path: ${rel})`
7565
+ });
7566
+ continue;
7567
+ }
7568
+ const name = (0, import_node_path19.basename)(rel);
7569
+ try {
7570
+ const abs = (0, import_node_path19.join)(worktreePath, rel);
7571
+ const st = (0, import_node_fs19.statSync)(abs);
7572
+ if (!st.isFile()) continue;
7573
+ const buf = (0, import_node_fs19.readFileSync)(abs);
7574
+ out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
7575
+ } catch (err) {
7576
+ out.push({
7577
+ id: (0, import_node_crypto2.randomUUID)(),
7578
+ name,
7579
+ kind: "file",
7580
+ content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
7581
+ });
7582
+ }
7583
+ }
7584
+ return out;
7585
+ }
7586
+
7349
7587
  // src/composer/summarize.ts
7350
7588
  init_run();
7351
7589
  init_path();
@@ -7784,14 +8022,14 @@ async function confirmLand(thread, opts) {
7784
8022
  }
7785
8023
 
7786
8024
  // src/threads/create.ts
7787
- var import_node_fs19 = require("fs");
8025
+ var import_node_fs20 = require("fs");
7788
8026
  init_worktree();
7789
8027
  init_thread_store();
7790
8028
  init_workspaces();
7791
8029
  async function createThread(input, onSetupLine) {
7792
8030
  await requireAgent(input.agent);
7793
8031
  const repoPath = await resolveRepoRoot(input.repoPath);
7794
- if (!(0, import_node_fs19.existsSync)(repoPath)) {
8032
+ if (!(0, import_node_fs20.existsSync)(repoPath)) {
7795
8033
  throw new Error(`Repo not found: ${repoPath}`);
7796
8034
  }
7797
8035
  let sourceRef = input.sourceRef;
@@ -7874,7 +8112,7 @@ async function listLinearIssues(agent, repoPath) {
7874
8112
  }
7875
8113
 
7876
8114
  // src/threads/chat-tabs.ts
7877
- var import_node_crypto2 = require("crypto");
8115
+ var import_node_crypto3 = require("crypto");
7878
8116
  init_teams();
7879
8117
  init_worktree_labels();
7880
8118
  init_global_workspace();
@@ -7930,7 +8168,7 @@ function forkMessageSlice(from, throughIndex) {
7930
8168
  function buildForkTranscriptAttachment(baseTitle, messages) {
7931
8169
  const title = baseTitle || "Chat";
7932
8170
  return {
7933
- id: (0, import_node_crypto2.randomUUID)(),
8171
+ id: (0, import_node_crypto3.randomUUID)(),
7934
8172
  name: `Transcript of ${title}.md`,
7935
8173
  kind: "transcript",
7936
8174
  content: formatTranscriptMarkdown(title, messages)
@@ -7989,30 +8227,34 @@ async function forkThreadWorktree(input, onSetupLine) {
7989
8227
  repoPath: from.repoPath,
7990
8228
  agent: input.agent ?? from.agent,
7991
8229
  autonomy: from.autonomy,
8230
+ model: from.model,
8231
+ fast: from.fast,
8232
+ planMode: from.planMode,
7992
8233
  title: input.title?.trim() || void 0,
7993
- parentThreadId: from.id
8234
+ parentThreadId: from.id,
8235
+ attachments: [attachment]
7994
8236
  },
7995
8237
  onSetupLine
7996
8238
  );
7997
- return updateThread(thread.id, { attachments: [attachment] });
8239
+ return thread;
7998
8240
  }
7999
8241
 
8000
8242
  // src/threads/adopt.ts
8001
8243
  var import_node_child_process = require("child_process");
8002
- var import_node_fs20 = require("fs");
8244
+ var import_node_fs21 = require("fs");
8003
8245
  var import_node_os9 = require("os");
8004
- var import_node_path19 = require("path");
8246
+ var import_node_path20 = require("path");
8005
8247
  var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
8006
8248
  init_worktree();
8007
8249
  init_thread_store();
8008
- var CONDUCTOR_APP_SUPPORT = (0, import_node_path19.join)(
8250
+ var CONDUCTOR_APP_SUPPORT = (0, import_node_path20.join)(
8009
8251
  process.env.HOME ?? "",
8010
8252
  "Library",
8011
8253
  "Application Support",
8012
8254
  "com.conductor.app"
8013
8255
  );
8014
- var CONDUCTOR_DB = (0, import_node_path19.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
8015
- var CURSOR_SDK_STORE = (0, import_node_path19.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
8256
+ var CONDUCTOR_DB = (0, import_node_path20.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
8257
+ var CURSOR_SDK_STORE = (0, import_node_path20.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
8016
8258
  function mapAgentType(raw) {
8017
8259
  if (!raw) return null;
8018
8260
  const v = raw.toLowerCase();
@@ -8024,21 +8266,21 @@ function mapAgentType(raw) {
8024
8266
  return null;
8025
8267
  }
8026
8268
  function resolveConductorCursorAgentId(workspacePath) {
8027
- if (!workspacePath || !(0, import_node_fs20.existsSync)(CURSOR_SDK_STORE)) return null;
8269
+ if (!workspacePath || !(0, import_node_fs21.existsSync)(CURSOR_SDK_STORE)) return null;
8028
8270
  const normalized = workspacePath.replace(/\/$/, "");
8029
8271
  let best = null;
8030
8272
  let hashes;
8031
8273
  try {
8032
- hashes = (0, import_node_fs20.readdirSync)(CURSOR_SDK_STORE);
8274
+ hashes = (0, import_node_fs21.readdirSync)(CURSOR_SDK_STORE);
8033
8275
  } catch {
8034
8276
  return null;
8035
8277
  }
8036
8278
  for (const hash of hashes) {
8037
- const agentsFile = (0, import_node_path19.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
8038
- if (!(0, import_node_fs20.existsSync)(agentsFile)) continue;
8279
+ const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
8280
+ if (!(0, import_node_fs21.existsSync)(agentsFile)) continue;
8039
8281
  let text;
8040
8282
  try {
8041
- text = (0, import_node_fs20.readFileSync)(agentsFile, "utf8");
8283
+ text = (0, import_node_fs21.readFileSync)(agentsFile, "utf8");
8042
8284
  } catch {
8043
8285
  continue;
8044
8286
  }
@@ -8062,7 +8304,7 @@ function resolveConductorCursorAgentId(workspacePath) {
8062
8304
  return best?.agentId ?? null;
8063
8305
  }
8064
8306
  async function adoptThread(input) {
8065
- if (!(0, import_node_fs20.existsSync)(input.worktreePath)) {
8307
+ if (!(0, import_node_fs21.existsSync)(input.worktreePath)) {
8066
8308
  throw new Error(`Worktree not found: ${input.worktreePath}`);
8067
8309
  }
8068
8310
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -8089,18 +8331,18 @@ function conductorDbPath() {
8089
8331
  return CONDUCTOR_DB;
8090
8332
  }
8091
8333
  function listConductorWorkspaces() {
8092
- if (!(0, import_node_fs20.existsSync)(CONDUCTOR_DB)) {
8334
+ if (!(0, import_node_fs21.existsSync)(CONDUCTOR_DB)) {
8093
8335
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
8094
8336
  }
8095
- const tmp = (0, import_node_fs20.mkdtempSync)((0, import_node_path19.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
8096
- const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
8337
+ const tmp = (0, import_node_fs21.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
8338
+ const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
8097
8339
  try {
8098
- (0, import_node_fs20.copyFileSync)(CONDUCTOR_DB, snapshot);
8340
+ (0, import_node_fs21.copyFileSync)(CONDUCTOR_DB, snapshot);
8099
8341
  for (const suffix of ["-wal", "-shm"]) {
8100
8342
  const src = `${CONDUCTOR_DB}${suffix}`;
8101
- if ((0, import_node_fs20.existsSync)(src)) {
8343
+ if ((0, import_node_fs21.existsSync)(src)) {
8102
8344
  try {
8103
- (0, import_node_fs20.copyFileSync)(src, `${snapshot}${suffix}`);
8345
+ (0, import_node_fs21.copyFileSync)(src, `${snapshot}${suffix}`);
8104
8346
  } catch {
8105
8347
  }
8106
8348
  }
@@ -8176,22 +8418,22 @@ function listConductorWorkspaces() {
8176
8418
  db.close();
8177
8419
  }
8178
8420
  } finally {
8179
- (0, import_node_fs20.rmSync)(tmp, { recursive: true, force: true });
8421
+ (0, import_node_fs21.rmSync)(tmp, { recursive: true, force: true });
8180
8422
  }
8181
8423
  }
8182
8424
  function importConductorWorkspace(workspaceId) {
8183
- if (!(0, import_node_fs20.existsSync)(CONDUCTOR_DB)) {
8425
+ if (!(0, import_node_fs21.existsSync)(CONDUCTOR_DB)) {
8184
8426
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
8185
8427
  }
8186
- const tmp = (0, import_node_fs20.mkdtempSync)((0, import_node_path19.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
8187
- const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
8428
+ const tmp = (0, import_node_fs21.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
8429
+ const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
8188
8430
  try {
8189
- (0, import_node_fs20.copyFileSync)(CONDUCTOR_DB, snapshot);
8431
+ (0, import_node_fs21.copyFileSync)(CONDUCTOR_DB, snapshot);
8190
8432
  for (const suffix of ["-wal", "-shm"]) {
8191
8433
  const src = `${CONDUCTOR_DB}${suffix}`;
8192
- if ((0, import_node_fs20.existsSync)(src)) {
8434
+ if ((0, import_node_fs21.existsSync)(src)) {
8193
8435
  try {
8194
- (0, import_node_fs20.copyFileSync)(src, `${snapshot}${suffix}`);
8436
+ (0, import_node_fs21.copyFileSync)(src, `${snapshot}${suffix}`);
8195
8437
  } catch {
8196
8438
  }
8197
8439
  }
@@ -8209,7 +8451,7 @@ function importConductorWorkspace(workspaceId) {
8209
8451
  ).get(workspaceId);
8210
8452
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
8211
8453
  const worktreePath = String(row.workspacePath);
8212
- if (!(0, import_node_fs20.existsSync)(worktreePath)) {
8454
+ if (!(0, import_node_fs21.existsSync)(worktreePath)) {
8213
8455
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
8214
8456
  }
8215
8457
  let sessionId = null;
@@ -8272,7 +8514,7 @@ function importConductorWorkspace(workspaceId) {
8272
8514
  db.close();
8273
8515
  }
8274
8516
  } finally {
8275
- (0, import_node_fs20.rmSync)(tmp, { recursive: true, force: true });
8517
+ (0, import_node_fs21.rmSync)(tmp, { recursive: true, force: true });
8276
8518
  }
8277
8519
  }
8278
8520
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -8281,13 +8523,13 @@ async function importConductorWorkspaceAsync(workspaceId) {
8281
8523
 
8282
8524
  // src/orchestrator/orchestrator.ts
8283
8525
  var import_node_events = require("events");
8284
- var import_node_fs23 = require("fs");
8526
+ var import_node_fs24 = require("fs");
8285
8527
  init_agents();
8286
8528
  init_worktree();
8287
8529
 
8288
8530
  // src/git/orphan-cleanup.ts
8289
- var import_node_fs21 = require("fs");
8290
- var import_node_path20 = require("path");
8531
+ var import_node_fs22 = require("fs");
8532
+ var import_node_path21 = require("path");
8291
8533
  init_worktree();
8292
8534
  init_thread_store();
8293
8535
  init_paths();
@@ -8302,9 +8544,9 @@ async function findOrphanWorktrees(repoPaths) {
8302
8544
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
8303
8545
  );
8304
8546
  const homeRoot = sideboardWorkspacesDir();
8305
- if ((0, import_node_fs21.existsSync)(homeRoot)) {
8547
+ if ((0, import_node_fs22.existsSync)(homeRoot)) {
8306
8548
  try {
8307
- for (const entry of (0, import_node_fs21.readdirSync)(homeRoot, { withFileTypes: true })) {
8549
+ for (const entry of (0, import_node_fs22.readdirSync)(homeRoot, { withFileTypes: true })) {
8308
8550
  if (!entry.isDirectory()) continue;
8309
8551
  void entry;
8310
8552
  }
@@ -8314,7 +8556,7 @@ async function findOrphanWorktrees(repoPaths) {
8314
8556
  const orphans = [];
8315
8557
  const seen = /* @__PURE__ */ new Set();
8316
8558
  for (const repoPath of repos) {
8317
- if (!repoPath || !(0, import_node_fs21.existsSync)(repoPath)) continue;
8559
+ if (!repoPath || !(0, import_node_fs22.existsSync)(repoPath)) continue;
8318
8560
  try {
8319
8561
  const wts = await listWorktrees(repoPath);
8320
8562
  for (const wt of wts) {
@@ -8325,7 +8567,7 @@ async function findOrphanWorktrees(repoPaths) {
8325
8567
  seen.add(path);
8326
8568
  let mtimeMs = 0;
8327
8569
  try {
8328
- mtimeMs = (0, import_node_fs21.statSync)(path).mtimeMs;
8570
+ mtimeMs = (0, import_node_fs22.statSync)(path).mtimeMs;
8329
8571
  } catch {
8330
8572
  mtimeMs = 0;
8331
8573
  }
@@ -8335,16 +8577,16 @@ async function findOrphanWorktrees(repoPaths) {
8335
8577
  }
8336
8578
  try {
8337
8579
  const root = worktreesRoot(repoPath);
8338
- if ((0, import_node_fs21.existsSync)(root)) {
8339
- for (const entry of (0, import_node_fs21.readdirSync)(root, { withFileTypes: true })) {
8580
+ if ((0, import_node_fs22.existsSync)(root)) {
8581
+ for (const entry of (0, import_node_fs22.readdirSync)(root, { withFileTypes: true })) {
8340
8582
  if (!entry.isDirectory()) continue;
8341
- const path = (0, import_node_path20.join)(root, entry.name).replace(/\/$/, "");
8583
+ const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
8342
8584
  if (known.has(path) || seen.has(path)) continue;
8343
- if (!(0, import_node_fs21.existsSync)((0, import_node_path20.join)(path, ".git"))) continue;
8585
+ if (!(0, import_node_fs22.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
8344
8586
  seen.add(path);
8345
8587
  let mtimeMs = 0;
8346
8588
  try {
8347
- mtimeMs = (0, import_node_fs21.statSync)(path).mtimeMs;
8589
+ mtimeMs = (0, import_node_fs22.statSync)(path).mtimeMs;
8348
8590
  } catch {
8349
8591
  mtimeMs = Date.now();
8350
8592
  }
@@ -8490,8 +8732,8 @@ async function applyThreadIntoMain(thread, opts) {
8490
8732
  }
8491
8733
 
8492
8734
  // src/git/clone-repo.ts
8493
- var import_node_fs22 = require("fs");
8494
- var import_node_path21 = require("path");
8735
+ var import_node_fs23 = require("fs");
8736
+ var import_node_path22 = require("path");
8495
8737
  var import_execa6 = require("execa");
8496
8738
  init_paths();
8497
8739
  init_workspaces();
@@ -8501,12 +8743,12 @@ async function cloneRepoIntoSideboard(opts) {
8501
8743
  if (!url) throw new Error("Clone URL is required");
8502
8744
  let name = opts.name?.trim();
8503
8745
  if (!name) {
8504
- const leaf = (0, import_node_path21.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
8746
+ const leaf = (0, import_node_path22.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
8505
8747
  name = leaf || "repo";
8506
8748
  }
8507
8749
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
8508
- const dest = (0, import_node_path21.join)(sideboardReposDir(), name);
8509
- if ((0, import_node_fs22.existsSync)(dest)) {
8750
+ const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
8751
+ if ((0, import_node_fs23.existsSync)(dest)) {
8510
8752
  const repoPath2 = await resolveRepoRoot(dest);
8511
8753
  const workspace2 = await ensureWorkspace(repoPath2);
8512
8754
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -8618,7 +8860,7 @@ var Orchestrator = class {
8618
8860
  }
8619
8861
  continue;
8620
8862
  }
8621
- if (!(0, import_node_fs23.existsSync)(thread.worktreePath)) {
8863
+ if (!(0, import_node_fs24.existsSync)(thread.worktreePath)) {
8622
8864
  setStatus(thread.id, "broken", "Worktree missing on disk");
8623
8865
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
8624
8866
  continue;
@@ -9430,6 +9672,23 @@ var Orchestrator = class {
9430
9672
  setAttachments(threadRef, attachments) {
9431
9673
  return updateThread(this.requireThread(threadRef).id, { attachments });
9432
9674
  }
9675
+ /**
9676
+ * Stage OS / worktree files into composer attachments (copies external files
9677
+ * into `.sideboard/attachments/` so agents can Read images and binaries).
9678
+ */
9679
+ attachComposerFiles(threadRef, opts) {
9680
+ const thread = this.requireThread(threadRef);
9681
+ const fromAbs = stageAbsolutePathsAsAttachments(
9682
+ thread.worktreePath,
9683
+ opts.absolutePaths ?? []
9684
+ );
9685
+ const fromRel = attachmentsFromWorktreePaths(
9686
+ thread.worktreePath,
9687
+ opts.relativePaths ?? []
9688
+ );
9689
+ const fromBuf = stageBuffersAsAttachments(thread.worktreePath, opts.buffers ?? []);
9690
+ return [...fromAbs, ...fromRel, ...fromBuf];
9691
+ }
9433
9692
  listWorktreeChats(threadRef) {
9434
9693
  const thread = this.requireThread(threadRef);
9435
9694
  return threadsSharingWorktree(thread.worktreePath);
@@ -9489,7 +9748,7 @@ var Orchestrator = class {
9489
9748
  updateThread(thread.id, { worktreePath: globalAgentCwd2() });
9490
9749
  return setStatus(thread.id, "idle");
9491
9750
  }
9492
- if (!(0, import_node_fs23.existsSync)(thread.worktreePath)) {
9751
+ if (!(0, import_node_fs24.existsSync)(thread.worktreePath)) {
9493
9752
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
9494
9753
  const { execa: execa7 } = await import("execa");
9495
9754
  const slug = thread.worktreePath.split("/").pop();
@@ -9591,7 +9850,7 @@ init_coordinator_prompt();
9591
9850
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
9592
9851
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
9593
9852
  var import_zod = require("zod");
9594
- var import_node_path22 = require("path");
9853
+ var import_node_path23 = require("path");
9595
9854
  init_worktree();
9596
9855
  init_global_workspace();
9597
9856
 
@@ -9639,7 +9898,7 @@ async function startMcpServer() {
9639
9898
  async () => {
9640
9899
  const threads = orch.getThreads(true);
9641
9900
  const lines = threads.map((t) => {
9642
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path22.basename)(t.repoPath) || t.repoPath;
9901
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path23.basename)(t.repoPath) || t.repoPath;
9643
9902
  return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}`;
9644
9903
  });
9645
9904
  return {
@@ -10535,6 +10794,9 @@ init_injected_mcp();
10535
10794
  applyAppEnvironment,
10536
10795
  applyCompaction,
10537
10796
  applyThreadIntoMain,
10797
+ attachmentFromAbsolutePath,
10798
+ attachmentsFromBuffers,
10799
+ attachmentsFromWorktreePaths,
10538
10800
  autoCleanupOrphansEnabled,
10539
10801
  autoRenameBranchEnabled,
10540
10802
  autoRunAfterSetupEnabled,
@@ -10657,6 +10919,7 @@ init_injected_mcp();
10657
10919
  isGhRateLimitError,
10658
10920
  isGlobalRepoPath,
10659
10921
  isGlobalThread,
10922
+ isImageFilePath,
10660
10923
  isLinearConnected,
10661
10924
  isOrchestratorThread,
10662
10925
  isPlaceholderBranch,
@@ -10745,6 +11008,8 @@ init_injected_mcp();
10745
11008
  slugify,
10746
11009
  spawnAgentTurn,
10747
11010
  splitForCompaction,
11011
+ stageAbsolutePathsAsAttachments,
11012
+ stageBuffersAsAttachments,
10748
11013
  startDevServer,
10749
11014
  startMcpServer,
10750
11015
  startOrchestration,