bertrand 0.25.1 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bertrand.js CHANGED
@@ -681,7 +681,7 @@ var init_schema = __esm(() => {
681
681
  slug: text("slug").notNull(),
682
682
  name: text("name").notNull(),
683
683
  status: text("status", {
684
- enum: ["active", "waiting", "paused", "archived"]
684
+ enum: ["active", "waiting", "blocked", "paused", "archived"]
685
685
  }).notNull().default("paused"),
686
686
  summary: text("summary"),
687
687
  rating: integer("rating"),
@@ -890,7 +890,7 @@ var init_parse_session_name = __esm(() => {
890
890
  });
891
891
 
892
892
  // src/db/queries/sessions.ts
893
- import { eq as eq2, and, inArray, sql as sql2 } from "drizzle-orm";
893
+ import { eq as eq2, and, inArray, sql as sql2, desc } from "drizzle-orm";
894
894
  function resolveSessionByName(name) {
895
895
  const flat = parseSessionName(name);
896
896
  const flatCategory = getCategoryByPath(flat.categoryPath);
@@ -942,9 +942,9 @@ function selectSessions(db, opts) {
942
942
  "active",
943
943
  "waiting",
944
944
  "paused"
945
- ])).all();
945
+ ])).orderBy(desc(sessions.updatedAt)).all();
946
946
  }
947
- return query.all();
947
+ return query.orderBy(desc(sessions.updatedAt)).all();
948
948
  }
949
949
  function getAllSessions(opts) {
950
950
  return selectSessions(getDb(), opts);
@@ -973,7 +973,7 @@ var init_sessions = __esm(() => {
973
973
  });
974
974
 
975
975
  // src/db/queries/conversations.ts
976
- import { eq as eq3, and as and2, desc, sql as sql3 } from "drizzle-orm";
976
+ import { eq as eq3, and as and2, desc as desc2, sql as sql3 } from "drizzle-orm";
977
977
  function createConversation(opts) {
978
978
  return getDb().insert(conversations).values(opts).returning().get();
979
979
  }
@@ -981,7 +981,7 @@ function getConversation(id) {
981
981
  return getDb().select().from(conversations).where(eq3(conversations.id, id)).get();
982
982
  }
983
983
  function getConversationsBySession(sessionId) {
984
- return getDb().select().from(conversations).where(and2(eq3(conversations.sessionId, sessionId), eq3(conversations.discarded, false))).orderBy(desc(conversations.startedAt)).all();
984
+ return getDb().select().from(conversations).where(and2(eq3(conversations.sessionId, sessionId), eq3(conversations.discarded, false))).orderBy(desc2(conversations.startedAt)).all();
985
985
  }
986
986
  function endConversation(id) {
987
987
  return getDb().update(conversations).set({ endedAt: sql3`(datetime('now'))` }).where(eq3(conversations.id, id)).returning().get();
@@ -1038,7 +1038,7 @@ function normalizeAnsweredMeta(meta) {
1038
1038
  }
1039
1039
 
1040
1040
  // src/db/queries/events.ts
1041
- import { eq as eq4, and as and3, desc as desc2 } from "drizzle-orm";
1041
+ import { eq as eq4, and as and3, desc as desc3 } from "drizzle-orm";
1042
1042
  function insertEvent(opts) {
1043
1043
  return getDb().insert(events).values({
1044
1044
  sessionId: opts.sessionId,
@@ -1062,7 +1062,7 @@ function getLatestEventOfType(sessionId, eventType, conversationId) {
1062
1062
  if (conversationId) {
1063
1063
  conditions.push(eq4(events.conversationId, conversationId));
1064
1064
  }
1065
- return getDb().select().from(events).where(and3(...conditions)).orderBy(desc2(events.createdAt)).limit(1).get();
1065
+ return getDb().select().from(events).where(and3(...conditions)).orderBy(desc3(events.createdAt)).limit(1).get();
1066
1066
  }
1067
1067
  var init_events = __esm(() => {
1068
1068
  init_client();
@@ -1216,7 +1216,7 @@ __export(exports_update, {
1216
1216
  function shouldIgnoreStatusFlip(newStatus, sessionPid) {
1217
1217
  if (!newStatus)
1218
1218
  return false;
1219
- if (newStatus !== "active" && newStatus !== "waiting")
1219
+ if (newStatus !== "active" && newStatus !== "waiting" && newStatus !== "blocked")
1220
1220
  return false;
1221
1221
  return sessionPid === null;
1222
1222
  }
@@ -1291,7 +1291,9 @@ var init_update = __esm(() => {
1291
1291
  init_emit();
1292
1292
  EVENT_STATUS_MAP = {
1293
1293
  "session.waiting": "waiting",
1294
+ "session.blocked": "blocked",
1294
1295
  "session.answered": "active",
1296
+ "session.active": "active",
1295
1297
  "session.paused": "paused"
1296
1298
  };
1297
1299
  register("update", async (args) => {
@@ -2015,7 +2017,7 @@ function archiveAllPaused() {
2015
2017
  var ACTIVE_STATUSES;
2016
2018
  var init_session_archive = __esm(() => {
2017
2019
  init_sessions();
2018
- ACTIVE_STATUSES = ["active", "waiting"];
2020
+ ACTIVE_STATUSES = ["active", "waiting", "blocked"];
2019
2021
  });
2020
2022
 
2021
2023
  // src/server/index.ts
@@ -2199,7 +2201,7 @@ var PORT, listSessions = (_params, url) => {
2199
2201
  for (const project of resolveProjectScope(url)) {
2200
2202
  const db = getDbForProject(project.slug);
2201
2203
  for (const { session } of getAllSessionsForProject(project)) {
2202
- const isLive = session.status === "active" || session.status === "waiting";
2204
+ const isLive = session.status === "active" || session.status === "waiting" || session.status === "blocked";
2203
2205
  if (isLive) {
2204
2206
  result[session.id] = liveStats(session.id, db);
2205
2207
  continue;
@@ -2213,7 +2215,7 @@ var PORT, listSessions = (_params, url) => {
2213
2215
  const session = getSession(sessionId, db);
2214
2216
  if (!session)
2215
2217
  return null;
2216
- const isLive = session.status === "active" || session.status === "waiting";
2218
+ const isLive = session.status === "active" || session.status === "waiting" || session.status === "blocked";
2217
2219
  if (isLive)
2218
2220
  return liveStats(sessionId, db);
2219
2221
  return getSessionStats(sessionId, db) ?? liveStats(sessionId, db);
@@ -3702,6 +3704,12 @@ ${EXTRACT_TOOL}
3702
3704
  # would look identical to an auto-approved one.
3703
3705
  touch "${runtimeDir2}/perm-pending-$sid"
3704
3706
 
3707
+ # Flip the session to \`blocked\` so the sidebar shows the distinct
3708
+ # permission-request state (orange) instead of looking like Claude is still
3709
+ # working. on-permission-done.sh flips it back to active once the approved
3710
+ # tool runs.
3711
+ bq update --session-id "$sid" --event session.blocked
3712
+
3705
3713
  # Badge + notify in background
3706
3714
  bq badge bell-exclamation --color '#ff6b35' --priority 25 --beep &
3707
3715
  bq notify bertrand "Needs permission: $tool" &
@@ -3738,6 +3746,9 @@ if [ -f "$marker" ]; then
3738
3746
  had_marker=1
3739
3747
  rm -f "$marker"
3740
3748
  bq badge --clear &
3749
+ # Approved tool is now running \u2192 clear the blocked state back to active.
3750
+ # No-op if the session already moved on (update dedupes same-status flips).
3751
+ bq update --session-id "$sid" --event session.active &
3741
3752
  fi
3742
3753
 
3743
3754
  cid="\${BERTRAND_CLAUDE_ID:-}"
@@ -4796,7 +4807,7 @@ var init_stats2 = __esm(() => {
4796
4807
  init_events();
4797
4808
  init_timing();
4798
4809
  init_format();
4799
- ACTIVE_STATUSES2 = ["active", "waiting"];
4810
+ ACTIVE_STATUSES2 = ["active", "waiting", "blocked"];
4800
4811
  register("stats", async (args) => {
4801
4812
  const isJson = args.includes("--json");
4802
4813
  const filteredArgs = args.filter((a) => !a.startsWith("--"));
@@ -4941,7 +4952,7 @@ function countSessions(slug) {
4941
4952
  const all = db.select({ status: sessions.status }).from(sessions).all();
4942
4953
  return {
4943
4954
  total: all.length,
4944
- active: all.filter((s) => s.status === "active" || s.status === "waiting").length
4955
+ active: all.filter((s) => s.status === "active" || s.status === "waiting" || s.status === "blocked").length
4945
4956
  };
4946
4957
  } catch {
4947
4958
  return UNREADABLE_COUNTS;