repoburg 1.3.219 → 1.3.220

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.
@@ -41076,6 +41076,14 @@ function createTables(database) {
41076
41076
  created_at TEXT NOT NULL
41077
41077
  );
41078
41078
 
41079
+ CREATE TABLE IF NOT EXISTS sidebar_folders (
41080
+ id TEXT PRIMARY KEY,
41081
+ workspace_path TEXT NOT NULL,
41082
+ name TEXT NOT NULL,
41083
+ sort_order INTEGER NOT NULL DEFAULT 0,
41084
+ created_at TEXT NOT NULL
41085
+ );
41086
+
41079
41087
  CREATE TABLE IF NOT EXISTS discussion_threads (
41080
41088
  id TEXT PRIMARY KEY,
41081
41089
  session_id TEXT NOT NULL,
@@ -41094,6 +41102,8 @@ function createTables(database) {
41094
41102
  CREATE INDEX IF NOT EXISTS idx_turns_session_id ON turns(session_id);
41095
41103
  CREATE INDEX IF NOT EXISTS idx_items_turn_id ON items(turn_id);
41096
41104
  CREATE INDEX IF NOT EXISTS idx_workspaces_path ON workspaces(path);
41105
+ CREATE INDEX IF NOT EXISTS idx_workspaces_path ON workspaces(path);
41106
+ CREATE INDEX IF NOT EXISTS idx_sidebar_folders_workspace_path ON sidebar_folders(workspace_path);
41097
41107
  CREATE INDEX IF NOT EXISTS idx_discussion_threads_session_id ON discussion_threads(session_id);
41098
41108
  CREATE INDEX IF NOT EXISTS idx_discussion_threads_item_id ON discussion_threads(item_id);
41099
41109
  `);
@@ -41123,6 +41133,15 @@ function createTables(database) {
41123
41133
  if (!sessColumns.some((c) => c.name === "context_usage")) {
41124
41134
  database.exec("ALTER TABLE sessions ADD COLUMN context_usage TEXT");
41125
41135
  }
41136
+ if (!sessColumns.some((c) => c.name === "folder_id")) {
41137
+ database.exec("ALTER TABLE sessions ADD COLUMN folder_id TEXT");
41138
+ }
41139
+ if (!sessColumns.some((c) => c.name === "sort_order")) {
41140
+ database.exec("ALTER TABLE sessions ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0");
41141
+ }
41142
+ if (!sessColumns.some((c) => c.name === "starred")) {
41143
+ database.exec("ALTER TABLE sessions ADD COLUMN starred INTEGER NOT NULL DEFAULT 0");
41144
+ }
41126
41145
  const turnColumns = database.prepare("PRAGMA table_info(turns)").all();
41127
41146
  if (!turnColumns.some((c) => c.name === "thread_id")) {
41128
41147
  database.exec("ALTER TABLE turns ADD COLUMN thread_id TEXT");
@@ -41304,21 +41323,30 @@ async function registerSessionRoutes(fastify) {
41304
41323
  list.push(item);
41305
41324
  itemsByTurn.set(item.turn_id, list);
41306
41325
  }
41326
+ const hydrateTurn = (row) => ({
41327
+ ...row,
41328
+ token_usage: row.token_usage ? JSON.parse(row.token_usage) : null,
41329
+ attachments: row.attachments ? JSON.parse(row.attachments) : null,
41330
+ runtime_input_ids: row.runtime_input_ids ? JSON.parse(row.runtime_input_ids) : null,
41331
+ items: itemsByTurn.get(row.id) ?? []
41332
+ });
41333
+ const mainTurns = turnRows.filter((row) => row.thread_id == null).map(hydrateTurn);
41334
+ const threadTurns = turnRows.filter((row) => row.thread_id != null).map(hydrateTurn);
41307
41335
  const detail = {
41308
41336
  ...session,
41309
- turns: turnRows.map((row) => ({
41310
- ...row,
41311
- token_usage: row.token_usage ? JSON.parse(row.token_usage) : null,
41312
- attachments: row.attachments ? JSON.parse(row.attachments) : null,
41313
- runtime_input_ids: row.runtime_input_ids ? JSON.parse(row.runtime_input_ids) : null,
41314
- items: itemsByTurn.get(row.id) ?? []
41315
- }))
41337
+ turns: mainTurns
41316
41338
  };
41317
41339
  const threadRows = db2.prepare("SELECT * FROM discussion_threads WHERE session_id = ? ORDER BY created_at ASC").all(id);
41340
+ const turnsByThread = /* @__PURE__ */ new Map();
41341
+ for (const t of threadTurns) {
41342
+ const list = turnsByThread.get(t.thread_id) ?? [];
41343
+ list.push(t);
41344
+ turnsByThread.set(t.thread_id, list);
41345
+ }
41318
41346
  if (threadRows.length > 0) {
41319
41347
  detail.threads = threadRows.map((tr) => ({
41320
41348
  ...tr,
41321
- turns: detail.turns.filter((t) => t.thread_id === tr.id)
41349
+ turns: turnsByThread.get(tr.id) ?? []
41322
41350
  }));
41323
41351
  }
41324
41352
  const cuRow = db2.prepare("SELECT context_usage FROM sessions WHERE id = ?").get(id);
@@ -41355,11 +41383,13 @@ async function registerSessionRoutes(fastify) {
41355
41383
  } = request.body || {};
41356
41384
  const id = v4_default();
41357
41385
  const now = (/* @__PURE__ */ new Date()).toISOString();
41358
- getDb().prepare(
41359
- `INSERT INTO sessions (id, title, runtime_type, model, model_reasoning_effort, cwd, external_thread_id, status, is_running, created_at, updated_at, runtime_config)
41360
- VALUES (?, ?, ?, ?, ?, ?, NULL, 'active', 0, ?, ?, ?)`
41361
- ).run(id, title, runtime_type, model, model_reasoning_effort, cwd, now, now, runtime_config);
41362
- const session = getDb().prepare("SELECT * FROM sessions WHERE id = ?").get(id);
41386
+ const db2 = getDb();
41387
+ const maxOrder = db2.prepare("SELECT COALESCE(MAX(sort_order), 0) AS m FROM sessions WHERE cwd = ?").get(cwd ?? null);
41388
+ db2.prepare(
41389
+ `INSERT INTO sessions (id, title, runtime_type, model, model_reasoning_effort, cwd, external_thread_id, status, is_running, sort_order, created_at, updated_at, runtime_config)
41390
+ VALUES (?, ?, ?, ?, ?, ?, NULL, 'active', 0, ?, ?, ?, ?)`
41391
+ ).run(id, title, runtime_type, model, model_reasoning_effort, cwd, maxOrder.m + 1, now, now, runtime_config);
41392
+ const session = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
41363
41393
  return session;
41364
41394
  });
41365
41395
  fastify.delete("/api/sessions/:id", async (request, reply) => {
@@ -41386,13 +41416,14 @@ async function registerSessionRoutes(fastify) {
41386
41416
  model_reasoning_effort: { type: ["string", "null"] },
41387
41417
  runtime_config: { type: ["string", "null"] },
41388
41418
  runtime_type: { type: "string" },
41389
- cwd: { type: ["string", "null"] }
41419
+ cwd: { type: ["string", "null"] },
41420
+ starred: { type: "number" }
41390
41421
  }
41391
41422
  }
41392
41423
  }
41393
41424
  }, async (request, reply) => {
41394
41425
  const { id } = request.params;
41395
- const { title, lifecycle, has_unreviewed, model, model_reasoning_effort, runtime_config, runtime_type, cwd } = request.body || {};
41426
+ const { title, lifecycle, has_unreviewed, model, model_reasoning_effort, runtime_config, runtime_type, cwd, starred } = request.body || {};
41396
41427
  const db2 = getDb();
41397
41428
  const existing = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
41398
41429
  if (!existing) {
@@ -41417,6 +41448,10 @@ async function registerSessionRoutes(fastify) {
41417
41448
  updates.push("has_unreviewed = ?");
41418
41449
  params.push(has_unreviewed);
41419
41450
  }
41451
+ if (starred !== void 0) {
41452
+ updates.push("starred = ?");
41453
+ params.push(starred);
41454
+ }
41420
41455
  if (runtime_type !== void 0 && runtime_type !== existing.runtime_type) {
41421
41456
  providerChanged = true;
41422
41457
  updates.push("runtime_type = ?");
@@ -41877,6 +41912,60 @@ ${message}`;
41877
41912
  return { turn };
41878
41913
  }
41879
41914
  );
41915
+ fastify.post(
41916
+ "/api/sessions/:sessionId/threads/:threadId/summarize",
41917
+ {
41918
+ schema: {
41919
+ body: {
41920
+ type: "object",
41921
+ properties: {
41922
+ directives: { type: "string" }
41923
+ }
41924
+ }
41925
+ }
41926
+ },
41927
+ async (request, reply) => {
41928
+ const { sessionId, threadId } = request.params;
41929
+ const { directives } = request.body || {};
41930
+ const db2 = getDb();
41931
+ const session = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(sessionId);
41932
+ if (!session) {
41933
+ return reply.code(404).send({ error: "Session not found" });
41934
+ }
41935
+ const thread = db2.prepare("SELECT * FROM discussion_threads WHERE id = ? AND session_id = ?").get(threadId, sessionId);
41936
+ if (!thread) {
41937
+ return reply.code(404).send({ error: "Thread not found" });
41938
+ }
41939
+ if (thread.status !== "open") {
41940
+ return reply.code(409).send({ error: "Thread is already resolved" });
41941
+ }
41942
+ const runningTurn = db2.prepare(`SELECT id FROM turns WHERE thread_id = ? AND status IN ('pending', 'running') LIMIT 1`).get(threadId);
41943
+ if (runningTurn) {
41944
+ return reply.code(409).send({ error: "A thread turn is still running \u2014 wait for it to finish first" });
41945
+ }
41946
+ const provider = getProvider(session.runtime_type);
41947
+ if (!provider.summarizeThread) {
41948
+ return reply.code(501).send({ error: `Summarization is not supported for runtime "${session.runtime_type}" yet` });
41949
+ }
41950
+ const turnRows = db2.prepare("SELECT user_prompt, agent_response, status FROM turns WHERE thread_id = ? ORDER BY created_at ASC").all(threadId);
41951
+ const transcript = turnRows.map((t) => {
41952
+ const agent = t.agent_response?.trim() || "(no response)";
41953
+ return `User: ${t.user_prompt}
41954
+
41955
+ Agent: ${agent}`;
41956
+ }).join("\n\n---\n\n");
41957
+ const summary = await provider.summarizeThread(session, thread, transcript, directives ?? "");
41958
+ if (!summary) {
41959
+ return reply.code(500).send({ error: "Summarization failed \u2014 thread was not resolved. Try again or resolve without sharing." });
41960
+ }
41961
+ const now = (/* @__PURE__ */ new Date()).toISOString();
41962
+ db2.prepare(
41963
+ "UPDATE discussion_threads SET status = ?, resolved_at = ?, updated_at = ? WHERE id = ?"
41964
+ ).run("resolved", now, now, threadId);
41965
+ broadcast("thread:resolved", { sessionId, threadId, status: "resolved" });
41966
+ return { summary, resolved: true };
41967
+ }
41968
+ );
41880
41969
  fastify.patch(
41881
41970
  "/api/sessions/:sessionId/threads/:threadId",
41882
41971
  {
@@ -42066,6 +42155,137 @@ async function registerWorkspaceRoutes(fastify) {
42066
42155
  });
42067
42156
  }
42068
42157
 
42158
+ // src/routes/folders.ts
42159
+ async function registerFolderRoutes(fastify) {
42160
+ fastify.get("/api/folders", async () => {
42161
+ const rows = getDb().prepare("SELECT * FROM sidebar_folders ORDER BY workspace_path ASC, sort_order ASC").all();
42162
+ return rows;
42163
+ });
42164
+ fastify.post("/api/folders", {
42165
+ schema: {
42166
+ body: {
42167
+ type: "object",
42168
+ required: ["workspace_path", "name"],
42169
+ properties: {
42170
+ workspace_path: { type: "string" },
42171
+ name: { type: "string" }
42172
+ }
42173
+ }
42174
+ }
42175
+ }, async (request, reply) => {
42176
+ const { workspace_path, name } = request.body || {};
42177
+ if (!workspace_path?.trim() || !name?.trim()) {
42178
+ return reply.code(400).send({ error: "workspace_path and name are required" });
42179
+ }
42180
+ const db2 = getDb();
42181
+ const id = v4_default();
42182
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42183
+ const maxOrder = db2.prepare("SELECT COALESCE(MAX(sort_order), -1) AS m FROM sidebar_folders WHERE workspace_path = ?").get(workspace_path.trim());
42184
+ db2.prepare(
42185
+ "INSERT INTO sidebar_folders (id, workspace_path, name, sort_order, created_at) VALUES (?, ?, ?, ?, ?)"
42186
+ ).run(id, workspace_path.trim(), name.trim(), maxOrder.m + 1, now);
42187
+ const folder = db2.prepare("SELECT * FROM sidebar_folders WHERE id = ?").get(id);
42188
+ return folder;
42189
+ });
42190
+ fastify.patch("/api/folders/:id", {
42191
+ schema: {
42192
+ body: {
42193
+ type: "object",
42194
+ properties: {
42195
+ name: { type: "string" },
42196
+ sort_order: { type: "number" }
42197
+ }
42198
+ }
42199
+ }
42200
+ }, async (request, reply) => {
42201
+ const { id } = request.params;
42202
+ const { name, sort_order } = request.body || {};
42203
+ const db2 = getDb();
42204
+ const existing = db2.prepare("SELECT * FROM sidebar_folders WHERE id = ?").get(id);
42205
+ if (!existing) {
42206
+ return reply.code(404).send({ error: "Folder not found" });
42207
+ }
42208
+ if (name !== void 0 && !name.trim()) {
42209
+ return reply.code(400).send({ error: "name cannot be empty" });
42210
+ }
42211
+ const updates = [];
42212
+ const params = [];
42213
+ if (name !== void 0) {
42214
+ updates.push("name = ?");
42215
+ params.push(name.trim());
42216
+ }
42217
+ if (sort_order !== void 0) {
42218
+ updates.push("sort_order = ?");
42219
+ params.push(sort_order);
42220
+ }
42221
+ if (updates.length > 0) {
42222
+ db2.prepare(`UPDATE sidebar_folders SET ${updates.join(", ")} WHERE id = ?`).run(...params, id);
42223
+ }
42224
+ return db2.prepare("SELECT * FROM sidebar_folders WHERE id = ?").get(id);
42225
+ });
42226
+ fastify.delete("/api/folders/:id", async (request, reply) => {
42227
+ const { id } = request.params;
42228
+ const db2 = getDb();
42229
+ const existing = db2.prepare("SELECT id FROM sidebar_folders WHERE id = ?").get(id);
42230
+ if (!existing) {
42231
+ return reply.code(404).send({ error: "Folder not found" });
42232
+ }
42233
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42234
+ const move = db2.transaction(() => {
42235
+ db2.prepare(
42236
+ "UPDATE sessions SET folder_id = NULL, updated_at = ? WHERE folder_id = ?"
42237
+ ).run(now, id);
42238
+ db2.prepare("DELETE FROM sidebar_folders WHERE id = ?").run(id);
42239
+ });
42240
+ move();
42241
+ return { success: true };
42242
+ });
42243
+ fastify.post("/api/sessions/reorder", {
42244
+ schema: {
42245
+ body: {
42246
+ type: "object",
42247
+ required: ["moves"],
42248
+ properties: {
42249
+ moves: {
42250
+ type: "array",
42251
+ items: {
42252
+ type: "object",
42253
+ required: ["id", "sort_order"],
42254
+ properties: {
42255
+ id: { type: "string" },
42256
+ folder_id: { type: ["string", "null"] },
42257
+ sort_order: { type: "number" }
42258
+ }
42259
+ }
42260
+ }
42261
+ }
42262
+ }
42263
+ }
42264
+ }, async (request, reply) => {
42265
+ const { moves } = request.body || {};
42266
+ const db2 = getDb();
42267
+ for (const m of moves) {
42268
+ if (!db2.prepare("SELECT id FROM sessions WHERE id = ?").get(m.id)) {
42269
+ return reply.code(404).send({ error: `Session not found: ${m.id}` });
42270
+ }
42271
+ if (m.folder_id && !db2.prepare("SELECT id FROM sidebar_folders WHERE id = ?").get(m.folder_id)) {
42272
+ return reply.code(404).send({ error: `Folder not found: ${m.folder_id}` });
42273
+ }
42274
+ }
42275
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42276
+ const stmt = db2.prepare(
42277
+ "UPDATE sessions SET folder_id = ?, sort_order = ?, updated_at = ? WHERE id = ?"
42278
+ );
42279
+ const apply = db2.transaction(() => {
42280
+ for (const m of moves) {
42281
+ stmt.run(m.folder_id ?? null, m.sort_order, now, m.id);
42282
+ }
42283
+ });
42284
+ apply();
42285
+ return { success: true, updated: moves.length };
42286
+ });
42287
+ }
42288
+
42069
42289
  // src/routes/providers.ts
42070
42290
  var CODEX_PROVIDER = {
42071
42291
  id: "codex",
@@ -42966,6 +43186,75 @@ Agent responded: "${truncatedResponse}"`;
42966
43186
  }
42967
43187
  }
42968
43188
  }
43189
+ async function summarizeCodexThread(session, _thread, transcript, directives) {
43190
+ let client = null;
43191
+ try {
43192
+ client = new AppServerClient({ cwd: session.cwd ?? process.cwd() });
43193
+ await client.initialize();
43194
+ const initResult = await client.request("thread/start", {
43195
+ cwd: session.cwd ?? process.cwd(),
43196
+ approvalPolicy: "never",
43197
+ sandbox: "read-only"
43198
+ });
43199
+ const threadId = initResult.thread.id;
43200
+ const directiveLine = directives.trim() ? `
43201
+
43202
+ The user has given these summarization directives \u2014 follow them exactly:
43203
+ "${directives.trim()}"` : "";
43204
+ const summaryPrompt = `You are summarizing a side discussion thread that forked from a main agent session. The discussion begins at the FIRST message of the transcript below \u2014 summarize ONLY the discussion itself, from its beginning up to now.${directiveLine}
43205
+
43206
+ The discussion transcript (from the discussion's beginning):
43207
+
43208
+ ${transcript}
43209
+
43210
+ Produce a concise summary of the discussion that preserves: concrete findings, file paths, decisions, and any unresolved questions. Reply with only the summary text, nothing else.`;
43211
+ let summaryResolve;
43212
+ let summaryReject;
43213
+ const summaryPromise = new Promise((resolve, reject) => {
43214
+ summaryResolve = resolve;
43215
+ summaryReject = reject;
43216
+ });
43217
+ let summaryResponse = "";
43218
+ client.onNotification("item/completed", (params) => {
43219
+ const p = params;
43220
+ if (p.threadId !== threadId) return;
43221
+ if ((p.item.type === "agentMessage" || p.item.type === "agent_message") && p.item.text) {
43222
+ summaryResponse = p.item.text;
43223
+ }
43224
+ });
43225
+ client.onNotification("turn/completed", (params) => {
43226
+ const p = params;
43227
+ if (p.threadId !== threadId) return;
43228
+ summaryResolve(summaryResponse);
43229
+ });
43230
+ client.onNotification("turn/failed", (params) => {
43231
+ const p = params;
43232
+ if (p.threadId !== threadId) return;
43233
+ summaryReject(new Error(p.error.message));
43234
+ });
43235
+ const timeout = setTimeout(() => {
43236
+ summaryReject(new Error("Thread summarization timed out"));
43237
+ }, 6e4);
43238
+ await client.request("turn/start", {
43239
+ threadId,
43240
+ input: [{ type: "text", text: summaryPrompt }],
43241
+ approvalPolicy: "never",
43242
+ sandbox: { type: "read-only" }
43243
+ });
43244
+ const summary = await summaryPromise;
43245
+ clearTimeout(timeout);
43246
+ const clean = summary.trim();
43247
+ return clean || null;
43248
+ } catch (err) {
43249
+ console.error("[codex] summarizeThread failed:", err);
43250
+ return null;
43251
+ } finally {
43252
+ if (client) {
43253
+ await client.close().catch(() => {
43254
+ });
43255
+ }
43256
+ }
43257
+ }
42969
43258
  var EFFORT_LABELS = {
42970
43259
  minimal: "Minimal",
42971
43260
  low: "Low",
@@ -43002,6 +43291,7 @@ var codexProvider = {
43002
43291
  clearThread,
43003
43292
  clearAllThreads,
43004
43293
  getModels,
43294
+ summarizeThread: summarizeCodexThread,
43005
43295
  shutdown
43006
43296
  };
43007
43297
 
@@ -43209,6 +43499,7 @@ var PromptQueue = class {
43209
43499
  };
43210
43500
  }
43211
43501
  };
43502
+ var CLI_OVERRIDE = process.env.CLAUDE_BIN ? { pathToClaudeCodeExecutable: process.env.CLAUDE_BIN } : {};
43212
43503
  var sessionMap2 = /* @__PURE__ */ new Map();
43213
43504
  var titleGenerating2 = /* @__PURE__ */ new Set();
43214
43505
  var cachedModels = null;
@@ -43242,7 +43533,7 @@ async function ensureSessionStarted(session, mapKey, resumeSessionId) {
43242
43533
  permissionMode: "bypassPermissions",
43243
43534
  allowDangerouslySkipPermissions: true,
43244
43535
  includePartialMessages: true,
43245
- ...process.env.CLAUDE_BIN ? { pathToClaudeCodeExecutable: process.env.CLAUDE_BIN } : {},
43536
+ ...CLI_OVERRIDE,
43246
43537
  ...session.model_reasoning_effort ? { effort: session.model_reasoning_effort } : {},
43247
43538
  ...resumeId ? { resume: resumeId } : {}
43248
43539
  };
@@ -43853,7 +44144,8 @@ async function getModels2() {
43853
44144
  options: {
43854
44145
  cwd: process.cwd(),
43855
44146
  permissionMode: "bypassPermissions",
43856
- allowDangerouslySkipPermissions: true
44147
+ allowDangerouslySkipPermissions: true,
44148
+ ...CLI_OVERRIDE
43857
44149
  }
43858
44150
  });
43859
44151
  const initResult = await q.initializationResult();
@@ -43917,7 +44209,8 @@ Agent responded: "${truncatedResponse}"`;
43917
44209
  options: {
43918
44210
  cwd: process.cwd(),
43919
44211
  permissionMode: "bypassPermissions",
43920
- allowDangerouslySkipPermissions: true
44212
+ allowDangerouslySkipPermissions: true,
44213
+ ...CLI_OVERRIDE
43921
44214
  }
43922
44215
  });
43923
44216
  let titleResponse = "";
@@ -43944,6 +44237,66 @@ Agent responded: "${truncatedResponse}"`;
43944
44237
  }
43945
44238
  }
43946
44239
  }
44240
+ function buildThreadSummaryPrompt(transcript, directives, hasNativeContext) {
44241
+ const directiveLine = directives.trim() ? `
44242
+
44243
+ The user has given these summarization directives \u2014 follow them exactly:
44244
+ "${directives.trim()}"` : "";
44245
+ const contextBlock = hasNativeContext ? "" : `
44246
+
44247
+ The discussion transcript (from the discussion's beginning):
44248
+
44249
+ ${transcript}`;
44250
+ return `You are summarizing a side discussion thread that forked from a main agent session. The discussion begins at the message that quotes a passage from a previous response (prefixed "[Quoting from your previous response]") \u2014 everything in the conversation BEFORE that message is the main session's earlier context and must NOT be included in the summary. Summarize ONLY the discussion itself, from its first message up to now \u2014 do not summarize the full conversation history.${contextBlock}${directiveLine}
44251
+
44252
+ Produce a concise summary of the discussion that preserves: concrete findings, file paths, decisions, and any unresolved questions. The summary will be quoted back into the main session's conversation. Reply with only the summary text, nothing else.`;
44253
+ }
44254
+ async function summarizeClaudeThread(session, thread, transcript, directives) {
44255
+ let q = null;
44256
+ try {
44257
+ let resumeId;
44258
+ if (thread.external_thread_id) {
44259
+ try {
44260
+ const forked = await sdkForkSession(thread.external_thread_id, {
44261
+ dir: session.cwd ?? process.cwd()
44262
+ });
44263
+ resumeId = forked.sessionId;
44264
+ } catch (err) {
44265
+ console.error("[claude] summarizeThread fork failed \u2014 falling back to embedded transcript:", err);
44266
+ }
44267
+ }
44268
+ const prompt = buildThreadSummaryPrompt(transcript, directives, !!resumeId);
44269
+ q = query({
44270
+ prompt,
44271
+ options: {
44272
+ cwd: session.cwd ?? process.cwd(),
44273
+ permissionMode: "bypassPermissions",
44274
+ allowDangerouslySkipPermissions: true,
44275
+ ...CLI_OVERRIDE,
44276
+ ...resumeId ? { resume: resumeId } : {}
44277
+ }
44278
+ });
44279
+ let summary = "";
44280
+ for await (const message of q) {
44281
+ if (message.type === "assistant") {
44282
+ for (const block of message.message.content) {
44283
+ if (block.type === "text") {
44284
+ summary += block.text;
44285
+ }
44286
+ }
44287
+ }
44288
+ }
44289
+ const clean = summary.trim();
44290
+ return clean || null;
44291
+ } catch (err) {
44292
+ console.error("[claude] summarizeThread failed:", err);
44293
+ return null;
44294
+ } finally {
44295
+ if (q) {
44296
+ q.close();
44297
+ }
44298
+ }
44299
+ }
43947
44300
  async function forkClaudeSession(session, targetTurn) {
43948
44301
  if (!session.external_thread_id) return null;
43949
44302
  try {
@@ -43966,6 +44319,7 @@ var claudeProvider = {
43966
44319
  getModels: getModels2,
43967
44320
  getCommands,
43968
44321
  forkSession: forkClaudeSession,
44322
+ summarizeThread: summarizeClaudeThread,
43969
44323
  setModel,
43970
44324
  stopTask,
43971
44325
  shutdown: shutdown2
@@ -45484,6 +45838,7 @@ async function createAgentPaneApp(dbPath) {
45484
45838
  await registerTurnRoutes(fastify);
45485
45839
  await registerThreadRoutes(fastify);
45486
45840
  await registerWorkspaceRoutes(fastify);
45841
+ await registerFolderRoutes(fastify);
45487
45842
  await registerProviderRoutes(fastify);
45488
45843
  await registerModelRoutes(fastify);
45489
45844
  await registerSkillRoutes(fastify);