@evident-ai/cli 3.0.1-dev.2950803 → 3.0.1-dev.2af6323

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.js CHANGED
@@ -1958,6 +1958,16 @@ var ChannelDriver = class {
1958
1958
  * entry = not yet resolved; `null` = resolved root (stop walking).
1959
1959
  */
1960
1960
  sessionParents = /* @__PURE__ */ new Map();
1961
+ /**
1962
+ * Per-session OpenCode title cache (#310), keyed by sessionId. Only a resolved
1963
+ * NON-EMPTY name is stored (terminal — a real session name won't later un-name),
1964
+ * so we do NOT re-GET `/session/:id` every tick. A missing entry = not yet
1965
+ * resolved OR resolved-but-still-empty → re-fetch on next need, since OpenCode
1966
+ * names sessions asynchronously mid-turn. Driver-level (not per-watcher) so both
1967
+ * the watcher completion path AND the restart-recovery re-adopt path (which has
1968
+ * no watcher) can resolve the title.
1969
+ */
1970
+ sessionTitles = /* @__PURE__ */ new Map();
1961
1971
  /** Serialises drains so a reconnect during a drain doesn't double-process. */
1962
1972
  draining = false;
1963
1973
  /**
@@ -2530,13 +2540,15 @@ var ChannelDriver = class {
2530
2540
  const latchedPaused = inFlight.pausedOnQuestion || inFlight.pausedOnPermission;
2531
2541
  const awaitingHuman = observedOpen || latchedPaused;
2532
2542
  if ((state === "running" || state === "done" || state === "failed") && !inFlight.started) {
2543
+ const title = await this.resolveSessionTitle(sessionId, watcher.conv.id);
2533
2544
  let claimed;
2534
2545
  try {
2535
2546
  claimed = await this.markProcessing(
2536
2547
  conv.id,
2537
2548
  inFlight.evidentMessageId,
2538
2549
  sessionId,
2539
- inFlight.opencodeMessageId
2550
+ inFlight.opencodeMessageId,
2551
+ title
2540
2552
  );
2541
2553
  } catch (err) {
2542
2554
  if (err instanceof ChannelAuthError) throw err;
@@ -2567,12 +2579,14 @@ var ChannelDriver = class {
2567
2579
  conversation_id: conv.id,
2568
2580
  message_id: inFlight.evidentMessageId
2569
2581
  });
2582
+ const title = await this.resolveSessionTitle(sessionId, watcher.conv.id);
2570
2583
  try {
2571
2584
  await this.markDone(
2572
2585
  conv.id,
2573
2586
  inFlight.evidentMessageId,
2574
2587
  sessionId,
2575
- inFlight.opencodeMessageId
2588
+ inFlight.opencodeMessageId,
2589
+ title
2576
2590
  );
2577
2591
  } catch (err) {
2578
2592
  if (err instanceof ChannelAuthError) throw err;
@@ -2858,7 +2872,8 @@ var ChannelDriver = class {
2858
2872
  message_id: row.id
2859
2873
  });
2860
2874
  try {
2861
- await this.markDone(row.conversation_id, row.id, sessionId, ocId);
2875
+ const title = await this.resolveSessionTitle(sessionId, row.conversation_id);
2876
+ await this.markDone(row.conversation_id, row.id, sessionId, ocId, title);
2862
2877
  } catch (err) {
2863
2878
  if (err instanceof ChannelAuthError) throw err;
2864
2879
  if (err instanceof ChannelTerminalError) {
@@ -3319,6 +3334,51 @@ var ChannelDriver = class {
3319
3334
  if (parent !== void 0) this.sessionParents.set(sessionId, parent);
3320
3335
  return parent;
3321
3336
  }
3337
+ /**
3338
+ * Resolve (and cache in `sessionTitles`) the OpenCode session TITLE (#310) so the
3339
+ * status PATCH can carry it into the "Live sessions" list. Driver-level cache so
3340
+ * BOTH the watcher completion path and the restart-recovery re-adopt path (which
3341
+ * has no watcher) can use it. `conversationId` is passed only for log context.
3342
+ * Best-effort:
3343
+ * - a resolved NON-EMPTY title is cached and terminal (a real session name
3344
+ * won't later un-name), so we do NOT re-GET `/session/:id` every tick;
3345
+ * - while the title is still absent/empty we do NOT latch it — OpenCode names
3346
+ * sessions asynchronously mid-turn, so an early call (e.g. at `processing`)
3347
+ * must leave the cache unresolved and re-fetch on the next need so a later
3348
+ * call (e.g. at `done`) picks up the name assigned in the meantime. Such a
3349
+ * call returns `null` (omit the title on THIS PATCH) without caching;
3350
+ * - a failed request likewise leaves the cache unresolved (retry next need)
3351
+ * and returns `null` — it must NEVER throw or block completion.
3352
+ * A failure is logged with agent/session context (no silent catch).
3353
+ */
3354
+ async resolveSessionTitle(sessionId, conversationId) {
3355
+ const cached = this.sessionTitles.get(sessionId);
3356
+ if (cached != null) return cached;
3357
+ try {
3358
+ const res = await this.fetchImpl(`${this.opencodeBase}/session/${sessionId}`);
3359
+ if (res.ok) {
3360
+ const body = await res.json();
3361
+ const title = body && typeof body.title === "string" ? body.title.trim() : "";
3362
+ if (title.length > 0) {
3363
+ this.sessionTitles.set(sessionId, title);
3364
+ return title;
3365
+ }
3366
+ return null;
3367
+ }
3368
+ this.log({
3369
+ level: "info",
3370
+ message: `Session title fetch for session ${sessionId.slice(0, 8)} (agent ${this.agentId.slice(0, 8)}) returned HTTP ${res.status} \u2014 omitting title`,
3371
+ conversation_id: conversationId
3372
+ });
3373
+ } catch (err) {
3374
+ this.log({
3375
+ level: "info",
3376
+ message: `Best-effort session title fetch failed for session ${sessionId.slice(0, 8)} (agent ${this.agentId.slice(0, 8)}) \u2014 omitting title: ${err instanceof Error ? err.message : String(err)}`,
3377
+ conversation_id: conversationId
3378
+ });
3379
+ }
3380
+ return null;
3381
+ }
3322
3382
  /**
3323
3383
  * DEFENSIVE cross-check for the restart-recovery path (WI-2): is any descendant
3324
3384
  * (`task` sub-agent) session under `rootSessionId` still genuinely doing work?
@@ -3528,7 +3588,7 @@ var ChannelDriver = class {
3528
3588
  * A single attempt (no internal retry): the watcher's per-tick loop is the
3529
3589
  * retry vehicle for the swap-to-running.
3530
3590
  */
3531
- async markProcessing(conversationId, messageId, sessionId, opencodeMessageId) {
3591
+ async markProcessing(conversationId, messageId, sessionId, opencodeMessageId, title) {
3532
3592
  const res = await this.fetchImpl(
3533
3593
  `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages/${messageId}`,
3534
3594
  {
@@ -3537,7 +3597,8 @@ var ChannelDriver = class {
3537
3597
  body: JSON.stringify({
3538
3598
  status: "processing",
3539
3599
  opencode_session_id: sessionId,
3540
- ...opencodeMessageId ? { opencode_message_id: opencodeMessageId } : {}
3600
+ ...opencodeMessageId ? { opencode_message_id: opencodeMessageId } : {},
3601
+ ...title ? { title } : {}
3541
3602
  })
3542
3603
  }
3543
3604
  );
@@ -3576,7 +3637,7 @@ var ChannelDriver = class {
3576
3637
  * watcher retries next tick within the
3577
3638
  * deadline, Finding 4).
3578
3639
  */
3579
- async markDone(conversationId, messageId, sessionId, opencodeMessageId) {
3640
+ async markDone(conversationId, messageId, sessionId, opencodeMessageId, title) {
3580
3641
  const res = await this.fetchImpl(
3581
3642
  `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages/${messageId}`,
3582
3643
  {
@@ -3585,7 +3646,8 @@ var ChannelDriver = class {
3585
3646
  body: JSON.stringify({
3586
3647
  status: "done",
3587
3648
  opencode_session_id: sessionId,
3588
- ...opencodeMessageId ? { opencode_message_id: opencodeMessageId } : {}
3649
+ ...opencodeMessageId ? { opencode_message_id: opencodeMessageId } : {},
3650
+ ...title ? { title } : {}
3589
3651
  })
3590
3652
  }
3591
3653
  );