repoburg 1.3.268 → 1.3.270

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.
@@ -11520,7 +11520,7 @@ var require_thread_stream = __commonJS({
11520
11520
  var { version } = require_package();
11521
11521
  var { EventEmitter: EventEmitter2 } = __require("events");
11522
11522
  var { Worker } = __require("worker_threads");
11523
- var { join: join3 } = __require("path");
11523
+ var { join: join4 } = __require("path");
11524
11524
  var { pathToFileURL } = __require("url");
11525
11525
  var { wait } = require_wait();
11526
11526
  var {
@@ -11571,7 +11571,7 @@ var require_thread_stream = __commonJS({
11571
11571
  function createWorker(stream, opts) {
11572
11572
  const { filename, workerData } = opts;
11573
11573
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
11574
- const toExecute = bundlerOverrides["thread-stream-worker"] || join3(__dirname, "lib", "worker.js");
11574
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join4(__dirname, "lib", "worker.js");
11575
11575
  const worker = new Worker(toExecute, {
11576
11576
  ...opts.workerOpts,
11577
11577
  name: opts.workerOpts?.name || "thread-stream",
@@ -12037,9 +12037,9 @@ var require_transport = __commonJS({
12037
12037
  "node_modules/pino/lib/transport.js"(exports, module) {
12038
12038
  "use strict";
12039
12039
  var { createRequire } = __require("module");
12040
- var { existsSync: existsSync2 } = __require("node:fs");
12040
+ var { existsSync: existsSync3 } = __require("node:fs");
12041
12041
  var getCallers = require_caller();
12042
- var { join: join3, isAbsolute, sep } = __require("node:path");
12042
+ var { join: join4, isAbsolute, sep } = __require("node:path");
12043
12043
  var { fileURLToPath: fileURLToPath3 } = __require("node:url");
12044
12044
  var sleep = require_atomic_sleep();
12045
12045
  var onExit = require_on_exit_leak_free();
@@ -12111,7 +12111,7 @@ var require_transport = __commonJS({
12111
12111
  return false;
12112
12112
  }
12113
12113
  }
12114
- return isAbsolute(path9) && !existsSync2(path9);
12114
+ return isAbsolute(path9) && !existsSync3(path9);
12115
12115
  }
12116
12116
  function stripQuotes(value) {
12117
12117
  const first = value[0];
@@ -12192,7 +12192,7 @@ var require_transport = __commonJS({
12192
12192
  throw new Error("only one of target or targets can be specified");
12193
12193
  }
12194
12194
  if (targets) {
12195
- target = bundlerOverrides["pino-worker"] || join3(__dirname, "worker.js");
12195
+ target = bundlerOverrides["pino-worker"] || join4(__dirname, "worker.js");
12196
12196
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
12197
12197
  return {
12198
12198
  ...dest,
@@ -12210,7 +12210,7 @@ var require_transport = __commonJS({
12210
12210
  });
12211
12211
  });
12212
12212
  } else if (pipeline) {
12213
- target = bundlerOverrides["pino-worker"] || join3(__dirname, "worker.js");
12213
+ target = bundlerOverrides["pino-worker"] || join4(__dirname, "worker.js");
12214
12214
  options.pipelines = [pipeline.map((dest) => {
12215
12215
  return {
12216
12216
  ...dest,
@@ -12233,7 +12233,7 @@ var require_transport = __commonJS({
12233
12233
  return origin;
12234
12234
  }
12235
12235
  if (origin === "pino/file") {
12236
- return join3(__dirname, "..", "file.js");
12236
+ return join4(__dirname, "..", "file.js");
12237
12237
  }
12238
12238
  let fixTarget2;
12239
12239
  for (const filePath of callers) {
@@ -13213,7 +13213,7 @@ var require_safe_stable_stringify = __commonJS({
13213
13213
  return circularValue;
13214
13214
  }
13215
13215
  let res = "";
13216
- let join3 = ",";
13216
+ let join4 = ",";
13217
13217
  const originalIndentation = indentation;
13218
13218
  if (Array.isArray(value)) {
13219
13219
  if (value.length === 0) {
@@ -13227,7 +13227,7 @@ var require_safe_stable_stringify = __commonJS({
13227
13227
  indentation += spacer;
13228
13228
  res += `
13229
13229
  ${indentation}`;
13230
- join3 = `,
13230
+ join4 = `,
13231
13231
  ${indentation}`;
13232
13232
  }
13233
13233
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13235,13 +13235,13 @@ ${indentation}`;
13235
13235
  for (; i < maximumValuesToStringify - 1; i++) {
13236
13236
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13237
13237
  res += tmp2 !== void 0 ? tmp2 : "null";
13238
- res += join3;
13238
+ res += join4;
13239
13239
  }
13240
13240
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13241
13241
  res += tmp !== void 0 ? tmp : "null";
13242
13242
  if (value.length - 1 > maximumBreadth) {
13243
13243
  const removedKeys = value.length - maximumBreadth - 1;
13244
- res += `${join3}"... ${getItemCount(removedKeys)} not stringified"`;
13244
+ res += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
13245
13245
  }
13246
13246
  if (spacer !== "") {
13247
13247
  res += `
@@ -13262,7 +13262,7 @@ ${originalIndentation}`;
13262
13262
  let separator = "";
13263
13263
  if (spacer !== "") {
13264
13264
  indentation += spacer;
13265
- join3 = `,
13265
+ join4 = `,
13266
13266
  ${indentation}`;
13267
13267
  whitespace = " ";
13268
13268
  }
@@ -13276,13 +13276,13 @@ ${indentation}`;
13276
13276
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
13277
13277
  if (tmp !== void 0) {
13278
13278
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13279
- separator = join3;
13279
+ separator = join4;
13280
13280
  }
13281
13281
  }
13282
13282
  if (keyLength > maximumBreadth) {
13283
13283
  const removedKeys = keyLength - maximumBreadth;
13284
13284
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
13285
- separator = join3;
13285
+ separator = join4;
13286
13286
  }
13287
13287
  if (spacer !== "" && separator.length > 1) {
13288
13288
  res = `
@@ -13323,7 +13323,7 @@ ${originalIndentation}`;
13323
13323
  }
13324
13324
  const originalIndentation = indentation;
13325
13325
  let res = "";
13326
- let join3 = ",";
13326
+ let join4 = ",";
13327
13327
  if (Array.isArray(value)) {
13328
13328
  if (value.length === 0) {
13329
13329
  return "[]";
@@ -13336,7 +13336,7 @@ ${originalIndentation}`;
13336
13336
  indentation += spacer;
13337
13337
  res += `
13338
13338
  ${indentation}`;
13339
- join3 = `,
13339
+ join4 = `,
13340
13340
  ${indentation}`;
13341
13341
  }
13342
13342
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13344,13 +13344,13 @@ ${indentation}`;
13344
13344
  for (; i < maximumValuesToStringify - 1; i++) {
13345
13345
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13346
13346
  res += tmp2 !== void 0 ? tmp2 : "null";
13347
- res += join3;
13347
+ res += join4;
13348
13348
  }
13349
13349
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13350
13350
  res += tmp !== void 0 ? tmp : "null";
13351
13351
  if (value.length - 1 > maximumBreadth) {
13352
13352
  const removedKeys = value.length - maximumBreadth - 1;
13353
- res += `${join3}"... ${getItemCount(removedKeys)} not stringified"`;
13353
+ res += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
13354
13354
  }
13355
13355
  if (spacer !== "") {
13356
13356
  res += `
@@ -13363,7 +13363,7 @@ ${originalIndentation}`;
13363
13363
  let whitespace = "";
13364
13364
  if (spacer !== "") {
13365
13365
  indentation += spacer;
13366
- join3 = `,
13366
+ join4 = `,
13367
13367
  ${indentation}`;
13368
13368
  whitespace = " ";
13369
13369
  }
@@ -13372,7 +13372,7 @@ ${indentation}`;
13372
13372
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
13373
13373
  if (tmp !== void 0) {
13374
13374
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13375
- separator = join3;
13375
+ separator = join4;
13376
13376
  }
13377
13377
  }
13378
13378
  if (spacer !== "" && separator.length > 1) {
@@ -13430,20 +13430,20 @@ ${originalIndentation}`;
13430
13430
  indentation += spacer;
13431
13431
  let res2 = `
13432
13432
  ${indentation}`;
13433
- const join4 = `,
13433
+ const join5 = `,
13434
13434
  ${indentation}`;
13435
13435
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
13436
13436
  let i = 0;
13437
13437
  for (; i < maximumValuesToStringify - 1; i++) {
13438
13438
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13439
13439
  res2 += tmp2 !== void 0 ? tmp2 : "null";
13440
- res2 += join4;
13440
+ res2 += join5;
13441
13441
  }
13442
13442
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13443
13443
  res2 += tmp !== void 0 ? tmp : "null";
13444
13444
  if (value.length - 1 > maximumBreadth) {
13445
13445
  const removedKeys = value.length - maximumBreadth - 1;
13446
- res2 += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
13446
+ res2 += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
13447
13447
  }
13448
13448
  res2 += `
13449
13449
  ${originalIndentation}`;
@@ -13459,16 +13459,16 @@ ${originalIndentation}`;
13459
13459
  return '"[Object]"';
13460
13460
  }
13461
13461
  indentation += spacer;
13462
- const join3 = `,
13462
+ const join4 = `,
13463
13463
  ${indentation}`;
13464
13464
  let res = "";
13465
13465
  let separator = "";
13466
13466
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
13467
13467
  if (isTypedArrayWithEntries(value)) {
13468
- res += stringifyTypedArray(value, join3, maximumBreadth);
13468
+ res += stringifyTypedArray(value, join4, maximumBreadth);
13469
13469
  keys = keys.slice(value.length);
13470
13470
  maximumPropertiesToStringify -= value.length;
13471
- separator = join3;
13471
+ separator = join4;
13472
13472
  }
13473
13473
  if (deterministic) {
13474
13474
  keys = sort(keys, comparator);
@@ -13479,13 +13479,13 @@ ${indentation}`;
13479
13479
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
13480
13480
  if (tmp !== void 0) {
13481
13481
  res += `${separator}${strEscape(key2)}: ${tmp}`;
13482
- separator = join3;
13482
+ separator = join4;
13483
13483
  }
13484
13484
  }
13485
13485
  if (keyLength > maximumBreadth) {
13486
13486
  const removedKeys = keyLength - maximumBreadth;
13487
13487
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
13488
- separator = join3;
13488
+ separator = join4;
13489
13489
  }
13490
13490
  if (separator !== "") {
13491
13491
  res = `
@@ -41109,6 +41109,7 @@ function createTables(database) {
41109
41109
  status TEXT NOT NULL DEFAULT 'open',
41110
41110
  resolved_at TEXT,
41111
41111
  external_thread_id TEXT,
41112
+ kind TEXT NOT NULL DEFAULT 'discussion',
41112
41113
  created_at TEXT NOT NULL,
41113
41114
  updated_at TEXT NOT NULL,
41114
41115
  FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
@@ -41172,6 +41173,9 @@ CREATE INDEX IF NOT EXISTS idx_workspaces_path ON workspaces(path);
41172
41173
  if (!sessColumns.some((c) => c.name === "origin_app_id")) {
41173
41174
  database.exec("ALTER TABLE sessions ADD COLUMN origin_app_id TEXT");
41174
41175
  }
41176
+ if (!sessColumns.some((c) => c.name === "user_touched")) {
41177
+ database.exec("ALTER TABLE sessions ADD COLUMN user_touched INTEGER NOT NULL DEFAULT 0");
41178
+ }
41175
41179
  const turnColumns = database.prepare("PRAGMA table_info(turns)").all();
41176
41180
  if (!turnColumns.some((c) => c.name === "thread_id")) {
41177
41181
  database.exec("ALTER TABLE turns ADD COLUMN thread_id TEXT");
@@ -41253,6 +41257,10 @@ CREATE INDEX IF NOT EXISTS idx_workspaces_path ON workspaces(path);
41253
41257
  CREATE INDEX IF NOT EXISTS idx_discussion_threads_item_id ON discussion_threads(item_id);
41254
41258
  `);
41255
41259
  }
41260
+ const threadKindCols = database.prepare("PRAGMA table_info(discussion_threads)").all();
41261
+ if (!threadKindCols.some((c) => c.name === "kind")) {
41262
+ database.exec("ALTER TABLE discussion_threads ADD COLUMN kind TEXT NOT NULL DEFAULT 'discussion'");
41263
+ }
41256
41264
  }
41257
41265
 
41258
41266
  // node_modules/uuid/dist/esm/stringify.js
@@ -41780,6 +41788,16 @@ var import_cors = __toESM(require_cors(), 1);
41780
41788
 
41781
41789
  // src/routes/sessions.ts
41782
41790
  import path from "node:path";
41791
+
41792
+ // src/routes/session-touch.ts
41793
+ function markSessionTouched(db2, sessionId) {
41794
+ const { changes } = db2.prepare("UPDATE sessions SET user_touched = 1 WHERE id = ? AND user_touched = 0").run(sessionId);
41795
+ if (changes === 0) return false;
41796
+ broadcast("session:touched", { sessionId });
41797
+ return true;
41798
+ }
41799
+
41800
+ // src/routes/sessions.ts
41783
41801
  async function registerSessionRoutes(fastify) {
41784
41802
  fastify.get("/api/sessions", async () => {
41785
41803
  const rows = getDb().prepare("SELECT * FROM sessions ORDER BY created_at DESC").all();
@@ -41983,6 +42001,7 @@ async function registerSessionRoutes(fastify) {
41983
42001
  params.push(runtime_config);
41984
42002
  }
41985
42003
  db2.prepare(`UPDATE sessions SET ${updates.join(", ")} WHERE id = ?`).run(...params, id);
42004
+ if (title !== void 0) markSessionTouched(db2, id);
41986
42005
  if (providerChanged || workspaceChanged) {
41987
42006
  if (existing.external_thread_id) {
41988
42007
  db2.prepare("UPDATE sessions SET external_thread_id = NULL, rewound_at_turn_id = NULL, pre_rewind_thread_id = NULL, updated_at = ? WHERE id = ?").run(now, id);
@@ -42037,6 +42056,7 @@ async function registerSessionRoutes(fastify) {
42037
42056
  db2.prepare(
42038
42057
  "UPDATE sessions SET rewound_at_turn_id = NULL, pre_rewind_thread_id = NULL, updated_at = ? WHERE id = ?"
42039
42058
  ).run(now2, id);
42059
+ markSessionTouched(db2, id);
42040
42060
  const truncated = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
42041
42061
  return truncated;
42042
42062
  }
@@ -42066,6 +42086,7 @@ async function registerSessionRoutes(fastify) {
42066
42086
  }
42067
42087
  }
42068
42088
  provider.clearThread(id);
42089
+ markSessionTouched(db2, id);
42069
42090
  const updated = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
42070
42091
  return updated;
42071
42092
  }
@@ -42090,6 +42111,7 @@ async function registerSessionRoutes(fastify) {
42090
42111
  "UPDATE sessions SET rewound_at_turn_id = NULL, pre_rewind_thread_id = NULL, external_thread_id = COALESCE(pre_rewind_thread_id, external_thread_id), updated_at = ? WHERE id = ?"
42091
42112
  ).run(now, id);
42092
42113
  getProvider(session.runtime_type).clearThread(id);
42114
+ markSessionTouched(db2, id);
42093
42115
  const updated = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
42094
42116
  return updated;
42095
42117
  });
@@ -42218,14 +42240,15 @@ async function registerTurnRoutes(fastify) {
42218
42240
  data: { type: "string" }
42219
42241
  }
42220
42242
  }
42221
- }
42243
+ },
42244
+ origin_app_id: { type: ["string", "null"] }
42222
42245
  }
42223
42246
  }
42224
42247
  }
42225
42248
  },
42226
42249
  async (request, reply) => {
42227
42250
  const { id } = request.params;
42228
- const { user_prompt, attachments } = request.body || {};
42251
+ const { user_prompt, attachments, origin_app_id } = request.body || {};
42229
42252
  if (!user_prompt || !user_prompt.trim()) {
42230
42253
  return reply.code(400).send({ error: "user_prompt is required" });
42231
42254
  }
@@ -42245,6 +42268,7 @@ async function registerTurnRoutes(fastify) {
42245
42268
  VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 'pending', NULL, ?, ?, ?)`
42246
42269
  ).run(turnId, id, user_prompt, attachmentsJson, now, now);
42247
42270
  const turn = db2.prepare("SELECT * FROM turns WHERE id = ?").get(turnId);
42271
+ if (!origin_app_id) markSessionTouched(db2, id);
42248
42272
  const rawTurn = turn;
42249
42273
  const parsedTurn = {
42250
42274
  ...turn,
@@ -42291,6 +42315,7 @@ async function registerTurnRoutes(fastify) {
42291
42315
  if (!cancelled) {
42292
42316
  return reply.code(409).send({ error: "No running turn to cancel" });
42293
42317
  }
42318
+ markSessionTouched(db2, id);
42294
42319
  return { success: true };
42295
42320
  });
42296
42321
  fastify.post(
@@ -42311,7 +42336,7 @@ async function registerTurnRoutes(fastify) {
42311
42336
  );
42312
42337
  }
42313
42338
 
42314
- // src/routes/threads.ts
42339
+ // src/routes/thread-turns.ts
42315
42340
  function parseTurnRow(row, items = []) {
42316
42341
  return {
42317
42342
  ...row,
@@ -42320,6 +42345,22 @@ function parseTurnRow(row, items = []) {
42320
42345
  items
42321
42346
  };
42322
42347
  }
42348
+ function startThreadTurn(db2, session, threadId, prompt, attachments) {
42349
+ const turnId = v4_default();
42350
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42351
+ const attachmentsJson = attachments && attachments.length > 0 ? JSON.stringify(attachments) : null;
42352
+ db2.prepare(
42353
+ `INSERT INTO turns (id, session_id, user_prompt, agent_response, thoughts, token_usage, external_turn_id, status, error, thread_id, attachments, created_at, updated_at)
42354
+ VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 'pending', NULL, ?, ?, ?, ?)`
42355
+ ).run(turnId, session.id, prompt, threadId, attachmentsJson, now, now);
42356
+ const rawTurn = db2.prepare("SELECT * FROM turns WHERE id = ?").get(turnId);
42357
+ getProvider(session.runtime_type).startTurn(session, rawTurn).catch((err) => {
42358
+ console.error(`[threads] turn error for session ${session.id} (thread ${threadId}):`, err);
42359
+ });
42360
+ return parseTurnRow(rawTurn);
42361
+ }
42362
+
42363
+ // src/routes/threads.ts
42323
42364
  async function registerThreadRoutes(fastify) {
42324
42365
  fastify.post(
42325
42366
  "/api/sessions/:sessionId/threads",
@@ -42415,22 +42456,13 @@ async function registerThreadRoutes(fastify) {
42415
42456
  > ${selectedText}
42416
42457
 
42417
42458
  ${message}` : message;
42418
- const newTurnId = v4_default();
42419
- const attachmentsJson = attachments && attachments.length > 0 ? JSON.stringify(attachments) : null;
42420
- db2.prepare(
42421
- `INSERT INTO turns (id, session_id, user_prompt, agent_response, thoughts, token_usage, external_turn_id, status, error, thread_id, attachments, created_at, updated_at)
42422
- VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 'pending', NULL, ?, ?, ?, ?)`
42423
- ).run(newTurnId, sessionId, formattedPrompt, threadId, attachmentsJson, now, now);
42424
- const rawTurn = db2.prepare("SELECT * FROM turns WHERE id = ?").get(newTurnId);
42425
- provider.startTurn(session, rawTurn).catch((err) => {
42426
- console.error(`[threads] Unhandled error for session ${sessionId}:`, err);
42427
- });
42459
+ const turn = startThreadTurn(db2, session, threadId, formattedPrompt, attachments);
42428
42460
  const threadRow = db2.prepare("SELECT * FROM discussion_threads WHERE id = ?").get(threadId);
42429
- const turn = parseTurnRow(rawTurn);
42430
42461
  const thread = {
42431
42462
  ...threadRow,
42432
42463
  turns: [turn]
42433
42464
  };
42465
+ markSessionTouched(db2, sessionId);
42434
42466
  broadcast("thread:created", { sessionId, thread, turn });
42435
42467
  return { thread, turn };
42436
42468
  }
@@ -42479,18 +42511,8 @@ ${message}` : message;
42479
42511
  if (thread.status !== "open") {
42480
42512
  return reply.code(409).send({ error: "Thread is resolved \u2014 reopen it first" });
42481
42513
  }
42482
- const newTurnId = v4_default();
42483
- const now = (/* @__PURE__ */ new Date()).toISOString();
42484
- const attachmentsJson = attachments && attachments.length > 0 ? JSON.stringify(attachments) : null;
42485
- db2.prepare(
42486
- `INSERT INTO turns (id, session_id, user_prompt, agent_response, thoughts, token_usage, external_turn_id, status, error, thread_id, attachments, created_at, updated_at)
42487
- VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 'pending', NULL, ?, ?, ?, ?)`
42488
- ).run(newTurnId, sessionId, message, threadId, attachmentsJson, now, now);
42489
- const rawTurn = db2.prepare("SELECT * FROM turns WHERE id = ?").get(newTurnId);
42490
- getProvider(session.runtime_type).startTurn(session, rawTurn).catch((err) => {
42491
- console.error(`[threads] followup error for session ${sessionId}:`, err);
42492
- });
42493
- const turn = parseTurnRow(rawTurn);
42514
+ const turn = startThreadTurn(db2, session, threadId, message, attachments);
42515
+ markSessionTouched(db2, sessionId);
42494
42516
  broadcast("thread:followup", { sessionId, threadId, turn });
42495
42517
  return { turn };
42496
42518
  }
@@ -42613,7 +42635,7 @@ Agent: ${agent}`;
42613
42635
  if (!session) {
42614
42636
  return reply.code(404).send({ error: "Session not found" });
42615
42637
  }
42616
- const threadRows = db2.prepare("SELECT * FROM discussion_threads WHERE session_id = ? ORDER BY created_at ASC").all(sessionId);
42638
+ const threadRows = db2.prepare("SELECT * FROM discussion_threads WHERE session_id = ? AND kind != 'sidecar' ORDER BY created_at ASC").all(sessionId);
42617
42639
  const result = [];
42618
42640
  for (const tr of threadRows) {
42619
42641
  const turnRows = db2.prepare("SELECT * FROM turns WHERE thread_id = ? ORDER BY created_at ASC").all(tr.id);
@@ -44983,8 +45005,8 @@ async function registerAppRoutes(fastify, opts) {
44983
45005
  const now = (/* @__PURE__ */ new Date()).toISOString();
44984
45006
  const maxOrder = db2.prepare("SELECT COALESCE(MAX(sort_order), 0) AS m FROM sessions WHERE cwd = ?").get(dir);
44985
45007
  db2.prepare(
44986
- `INSERT INTO sessions (id, title, runtime_type, model, model_reasoning_effort, cwd, external_thread_id, status, is_running, sort_order, created_at, updated_at, origin_app_id)
44987
- VALUES (?, ?, ?, ?, ?, ?, NULL, 'active', 0, ?, ?, ?, ?)`
45008
+ `INSERT INTO sessions (id, title, runtime_type, model, model_reasoning_effort, cwd, external_thread_id, status, is_running, sort_order, created_at, updated_at, origin_app_id, user_touched)
45009
+ VALUES (?, ?, ?, ?, ?, ?, NULL, 'active', 0, ?, ?, ?, ?, 1)`
44988
45010
  ).run(
44989
45011
  sessionId,
44990
45012
  `Edit: ${manifest.title}`,
@@ -45002,6 +45024,135 @@ async function registerAppRoutes(fastify, opts) {
45002
45024
  );
45003
45025
  }
45004
45026
 
45027
+ // src/sidecar/transcript-locator.ts
45028
+ import { existsSync as existsSync2, readdirSync } from "node:fs";
45029
+ import { homedir as homedir2 } from "node:os";
45030
+ import { join as join2 } from "node:path";
45031
+ function claudeConfigDir() {
45032
+ return process.env.CLAUDE_CONFIG_DIR || join2(homedir2(), ".claude");
45033
+ }
45034
+ function codexHome() {
45035
+ return process.env.CODEX_HOME || join2(homedir2(), ".codex");
45036
+ }
45037
+ function encodeClaudeProjectDir(cwd) {
45038
+ return cwd.replace(/[^a-zA-Z0-9]/g, "-");
45039
+ }
45040
+ function listDir(dir) {
45041
+ try {
45042
+ return readdirSync(dir);
45043
+ } catch {
45044
+ return [];
45045
+ }
45046
+ }
45047
+ function findClaudeTranscript(externalId, cwd) {
45048
+ const projects = join2(claudeConfigDir(), "projects");
45049
+ const fileName = `${externalId}.jsonl`;
45050
+ if (cwd) {
45051
+ const direct = join2(projects, encodeClaudeProjectDir(cwd), fileName);
45052
+ if (existsSync2(direct)) return direct;
45053
+ }
45054
+ for (const dir of listDir(projects)) {
45055
+ const candidate = join2(projects, dir, fileName);
45056
+ if (existsSync2(candidate)) return candidate;
45057
+ }
45058
+ return null;
45059
+ }
45060
+ function findCodexTranscript(externalId) {
45061
+ const root = join2(codexHome(), "sessions");
45062
+ const suffix = `-${externalId}.jsonl`;
45063
+ const desc = (dir) => listDir(dir).sort().reverse();
45064
+ for (const year of desc(root)) {
45065
+ for (const month of desc(join2(root, year))) {
45066
+ for (const day of desc(join2(root, year, month))) {
45067
+ const dayDir = join2(root, year, month, day);
45068
+ const match = listDir(dayDir).find((f) => f.endsWith(suffix));
45069
+ if (match) return join2(dayDir, match);
45070
+ }
45071
+ }
45072
+ }
45073
+ return null;
45074
+ }
45075
+ function locateTranscript(session) {
45076
+ const externalId = session.external_thread_id;
45077
+ if (!externalId) return null;
45078
+ switch (session.runtime_type) {
45079
+ case "claude":
45080
+ return findClaudeTranscript(externalId, session.cwd);
45081
+ case "codex":
45082
+ return findCodexTranscript(externalId);
45083
+ default:
45084
+ return null;
45085
+ }
45086
+ }
45087
+
45088
+ // src/sidecar/prompt.ts
45089
+ var SIDECAR_QUESTION_MARKER = "Understand the user's question below, read the session file given above, and answer.";
45090
+ function buildSidecarPrompt(externalSessionId, transcriptPath, question) {
45091
+ return [
45092
+ `Your job is to answer questions about this session: ${externalSessionId}`,
45093
+ `Session file: ${transcriptPath}`,
45094
+ "",
45095
+ SIDECAR_QUESTION_MARKER,
45096
+ "",
45097
+ question
45098
+ ].join("\n");
45099
+ }
45100
+
45101
+ // src/routes/sidecar.ts
45102
+ var SIDECAR_RUNTIMES = /* @__PURE__ */ new Set(["claude"]);
45103
+ async function registerSidecarRoutes(fastify) {
45104
+ fastify.post(
45105
+ "/api/sessions/:sessionId/sidecar",
45106
+ {
45107
+ schema: {
45108
+ body: {
45109
+ type: "object",
45110
+ required: ["message"],
45111
+ properties: { message: { type: "string", minLength: 1 } }
45112
+ }
45113
+ }
45114
+ },
45115
+ async (request, reply) => {
45116
+ const { sessionId } = request.params;
45117
+ const message = request.body.message.trim();
45118
+ if (!message) return reply.code(400).send({ error: "Message is empty" });
45119
+ const db2 = getDb();
45120
+ const session = db2.prepare("SELECT * FROM sessions WHERE id = ?").get(sessionId);
45121
+ if (!session) return reply.code(404).send({ error: "Session not found" });
45122
+ if (!SIDECAR_RUNTIMES.has(session.runtime_type)) {
45123
+ return reply.code(422).send({ error: `Sidecar is not available for ${session.runtime_type} sessions yet` });
45124
+ }
45125
+ const existing = db2.prepare("SELECT * FROM discussion_threads WHERE session_id = ? AND kind = 'sidecar' ORDER BY created_at DESC LIMIT 1").get(sessionId);
45126
+ if (existing) {
45127
+ if (isSessionBusy(db2, session, { threadId: existing.id })) {
45128
+ return reply.code(409).send({ error: "The sidecar is still answering" });
45129
+ }
45130
+ const turn2 = startThreadTurn(db2, session, existing.id, message);
45131
+ markSessionTouched(db2, sessionId);
45132
+ broadcast("thread:followup", { sessionId, threadId: existing.id, turn: turn2 });
45133
+ return { threadId: existing.id, turn: turn2 };
45134
+ }
45135
+ const transcriptPath = locateTranscript(session);
45136
+ if (!session.external_thread_id || !transcriptPath) {
45137
+ return reply.code(409).send({ error: "This session has no transcript yet \u2014 the sidecar can answer once it has run a turn" });
45138
+ }
45139
+ const threadId = v4_default();
45140
+ const now = (/* @__PURE__ */ new Date()).toISOString();
45141
+ db2.prepare(
45142
+ `INSERT INTO discussion_threads (id, session_id, kind, status, created_at, updated_at)
45143
+ VALUES (?, ?, 'sidecar', 'open', ?, ?)`
45144
+ ).run(threadId, sessionId, now, now);
45145
+ const prompt = buildSidecarPrompt(session.external_thread_id, transcriptPath, message);
45146
+ const turn = startThreadTurn(db2, session, threadId, prompt);
45147
+ const threadRow = db2.prepare("SELECT * FROM discussion_threads WHERE id = ?").get(threadId);
45148
+ const thread = { ...threadRow, turns: [turn] };
45149
+ markSessionTouched(db2, sessionId);
45150
+ broadcast("thread:created", { sessionId, thread, turn });
45151
+ return { threadId, turn };
45152
+ }
45153
+ );
45154
+ }
45155
+
45005
45156
  // src/codex/item-mapper.ts
45006
45157
  function mapItemData(item) {
45007
45158
  switch (item.type) {
@@ -45735,7 +45886,7 @@ var codexProvider = {
45735
45886
  import { query, forkSession as sdkForkSession } from "@anthropic-ai/claude-agent-sdk";
45736
45887
  import { readFileSync as readFileSync2 } from "fs";
45737
45888
  import { tmpdir } from "os";
45738
- import { join as join2 } from "path";
45889
+ import { join as join3 } from "path";
45739
45890
 
45740
45891
  // src/claude/claude-message-mapper.ts
45741
45892
  function mapAssistantContent(content, messageId, thinkingTexts) {
@@ -46449,6 +46600,10 @@ async function handleMessage(message, sessionId, turnId, entry, db2) {
46449
46600
  now,
46450
46601
  sessionId
46451
46602
  );
46603
+ } else {
46604
+ db2.prepare(
46605
+ "UPDATE discussion_threads SET external_thread_id = ?, updated_at = ? WHERE id = ? AND external_thread_id IS NULL"
46606
+ ).run(message.session_id, (/* @__PURE__ */ new Date()).toISOString(), entry.threadId);
46452
46607
  }
46453
46608
  } else if (sub === "compact_boundary") {
46454
46609
  handleCompactBoundary(
@@ -46672,7 +46827,7 @@ function constructMonitorOutputPath(entry, taskId) {
46672
46827
  if (!entry.sessionId) return "";
46673
46828
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
46674
46829
  const encodedCwd = entry.cwd.replace(/\//g, "-");
46675
- return join2(tmpdir(), `claude-${uid}`, encodedCwd, entry.sessionId, "tasks", `${taskId}.output`);
46830
+ return join3(tmpdir(), `claude-${uid}`, encodedCwd, entry.sessionId, "tasks", `${taskId}.output`);
46676
46831
  }
46677
46832
  function handleTaskStarted(message, sessionId, turnId, entry, db2) {
46678
46833
  const m = message;
@@ -48854,6 +49009,7 @@ async function createAgentPaneApp(dbPath) {
48854
49009
  await registerMaintenanceRoutes(fastify);
48855
49010
  await registerVscodeWebRoutes(fastify);
48856
49011
  await registerAppRoutes(fastify);
49012
+ await registerSidecarRoutes(fastify);
48857
49013
  const wss2 = initWs();
48858
49014
  wss2.on("connection", (ws) => {
48859
49015
  ws.send(JSON.stringify({ event: "background_tasks:snapshot", data: { sessions: listAllBackgroundTasks() } }));