@jmanuelcorral/openteam 0.24.1 → 0.25.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/cli.js CHANGED
@@ -424,6 +424,9 @@ function migrateConfig(raw) {
424
424
  var consoleDetailMessages = {
425
425
  follow: "Follow new activity",
426
426
  metadataOnly: "Lifecycle and telemetry stay metadata-only.",
427
+ logsDescription: "Current recorded conversations load automatically. Lifecycle and telemetry remain metadata-only; credentials are masked.",
428
+ credentialMasked: "[credential masked]",
429
+ offscreen: "Messages load automatically when this conversation is visible.",
427
430
  filter: "Filter sessions",
428
431
  incoming: "New activity available — show updates",
429
432
  refreshError: "Live updates unavailable; retrying.",
@@ -444,7 +447,7 @@ var consoleDetailMessages = {
444
447
  partialMetadata: "Some current session titles are unavailable or outside the bounded batch; fallback labels remain.",
445
448
  rootCurrent: "Current whole orchestrator session; may contain multiple batches. Not a historical or batch-only transcript.",
446
449
  recovered: "Read-only association verified from current opencode history; not an immutable lifecycle event.",
447
- rootUnlinked: "Orchestrator session not recorded. Reveal to try a bounded, read-only relationship search.",
450
+ rootUnlinked: "Orchestrator session not recorded. A bounded, read-only relationship search is attempted when visible.",
448
451
  rootUnavailable: "Orchestrator session unavailable: no unique, verifiable relationship was found in local history.",
449
452
  rootAmbiguous: "Orchestrator session unavailable: conflicting or copied relationships are ambiguous.",
450
453
  searchIncomplete: "Orchestrator association search incomplete or exceeded its bounded budget. No session was guessed.",
@@ -457,9 +460,9 @@ var consoleDetailMessages = {
457
460
  arguments: "Arguments",
458
461
  result: "Result",
459
462
  loading: "Loading local conversation…",
460
- current: "Present-day session, not a timeline reconstruction. May include later messages and tools. Refresh explicitly for updates.",
461
- historical: "Historical cursor: conversation content is hidden to avoid showing future mutable text. Return to live before revealing this operation.",
462
- disabled: "Content is disabled. Set console.sessionHistory.contentEnabled to true in .opencode/openteam.json and restart the Console. Then reveal content here.",
463
+ current: "Present-day whole session, not a timeline reconstruction or per-attempt transcript. Visible messages update automatically while live.",
464
+ historical: "Historical cursor: conversation content is hidden to avoid showing future mutable text. Return to live to read current messages.",
465
+ disabled: "Content is disabled by console.sessionHistory.contentEnabled: false. Lifecycle and telemetry remain metadata-only.",
463
466
  unavailable: "Conversation unavailable. Start an operator-owned local opencode serve against the same workspace and user history, configure console.sessionHistory.endpoint, then retry. No inference was requested.",
464
467
  empty: "No readable user messages, assistant output or tool calls remain in this session.",
465
468
  invalid: "This attempt has no recorded session in the selected run, or its history is incomplete.",
@@ -469,7 +472,7 @@ var consoleDetailMessages = {
469
472
  invalidEndpoint: "Expected a loopback HTTP origin without credentials, path, query or fragment.",
470
473
  readFailed: "Local session history could not be read.",
471
474
  oversized: "Conversation exceeds the local size limit (4 MiB). No content was loaded.",
472
- truncated: "Conversation display is truncated by local safety limits; omitted content is not empty history.",
475
+ truncated: "Conversation display is truncated by local safety limits. The newest bounded message window is shown; older messages or oversized fields may be omitted, not empty history.",
473
476
  truncatedMarker: `
474
477
  [truncated]`,
475
478
  terminalGap: "Live collection was interrupted; events during the gap are unavailable. This is not complete session history.",
@@ -546,7 +549,7 @@ var ConsoleConfigSchema = z2.object({
546
549
  openBrowser: z2.boolean().default(false),
547
550
  remoteStorage: z2.boolean().default(false),
548
551
  sessionHistory: z2.object({
549
- contentEnabled: z2.boolean().default(false),
552
+ contentEnabled: z2.boolean().default(true),
550
553
  endpoint: z2.string().refine(isLocalHistoryEndpoint, consoleDetailMessages.invalidEndpoint).optional()
551
554
  }).strict().optional(),
552
555
  terminal: z2.object({
@@ -891,6 +894,27 @@ import { isAbsolute, normalize, parse, resolve, sep } from "node:path";
891
894
  import { createOpencodeClient } from "@opencode-ai/sdk";
892
895
  import { z as z4 } from "zod";
893
896
 
897
+ // src/console/credentials.ts
898
+ var URL_WITH_AUTHORITY = /https?:\/\/[^\s/?#]+/giu;
899
+ function maskUrlCredentials(value) {
900
+ return value.replace(URL_WITH_AUTHORITY, (candidate) => {
901
+ const schemeEnd = candidate.indexOf("://") + 3;
902
+ const at = candidate.indexOf("@", schemeEnd);
903
+ if (at === -1)
904
+ return candidate;
905
+ const colon = candidate.indexOf(":", schemeEnd);
906
+ if (colon === -1 || colon <= schemeEnd || colon + 1 >= at || colon > at)
907
+ return candidate;
908
+ return `${candidate.slice(0, schemeEnd)}${consoleDetailMessages.credentialMasked}${candidate.slice(at)}`;
909
+ });
910
+ }
911
+ function maskConversationCredentials(value) {
912
+ return maskUrlCredentials(value.replace(/-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----[\s\S]*?(?:-----END (?:[A-Z]+ )?PRIVATE KEY-----|$)/gu, consoleDetailMessages.credentialMasked).replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/giu, consoleDetailMessages.credentialMasked).replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)\b/gu, consoleDetailMessages.credentialMasked).replace(/(\b(?:password|passwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|token|authorization|cookie|set-cookie)\b["']?\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;}\]]+)/giu, (_match, prefix) => prefix + consoleDetailMessages.credentialMasked));
913
+ }
914
+ function isCredentialKey(key) {
915
+ return /^(?:password|passwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|token|authorization|cookie|set-cookie|private[_-]?key)$/iu.test(key);
916
+ }
917
+
894
918
  // src/console/historyBoundary.ts
895
919
  function only(keys) {
896
920
  return (value) => {
@@ -1148,7 +1172,7 @@ class ConversationTooLargeError extends Error {
1148
1172
  }
1149
1173
  }
1150
1174
  var Info = z4.preprocess(only(["id", "sessionID", "role", "time"]), z4.object({
1151
- id: z4.string(),
1175
+ id: z4.string().min(1).max(256),
1152
1176
  sessionID: z4.string(),
1153
1177
  role: z4.enum(["user", "assistant"]),
1154
1178
  time: z4.preprocess(only(["created", "completed"]), z4.object({
@@ -1168,7 +1192,7 @@ var Text = PartIdentity.extend({
1168
1192
  });
1169
1193
  var Tool = PartIdentity.extend({
1170
1194
  type: z4.literal("tool"),
1171
- callID: z4.string(),
1195
+ callID: z4.string().min(1).max(256),
1172
1196
  tool: z4.string(),
1173
1197
  state: z4.preprocess(only(["status", "input", "output", "error", "time"]), z4.object({
1174
1198
  status: z4.enum(["pending", "running", "completed", "error"]),
@@ -1191,6 +1215,7 @@ function projectConversation(raw, sessionID) {
1191
1215
  let remaining = limits.totalChars;
1192
1216
  let toolsRemaining = limits.tools;
1193
1217
  function text(value) {
1218
+ value = maskConversationCredentials(value);
1194
1219
  const length = Math.min(value.length, limits.fieldChars, remaining);
1195
1220
  remaining -= length;
1196
1221
  if (length === value.length)
@@ -1222,7 +1247,7 @@ function projectConversation(raw, sessionID) {
1222
1247
  truncated = true;
1223
1248
  break;
1224
1249
  }
1225
- const child = bounded(value[key], depth + 1);
1250
+ const child = isCredentialKey(key) ? consoleDetailMessages.credentialMasked : bounded(value[key], depth + 1);
1226
1251
  if (Array.isArray(value))
1227
1252
  array.push(child);
1228
1253
  else
@@ -1233,7 +1258,7 @@ function projectConversation(raw, sessionID) {
1233
1258
  return text(JSON.stringify(bounded(input, 0), null, 2));
1234
1259
  }
1235
1260
  const messages = [];
1236
- for (const rawRow of raw.slice(0, limits.messages)) {
1261
+ for (const rawRow of raw.slice(-limits.messages).reverse()) {
1237
1262
  if (remaining <= 0) {
1238
1263
  truncated = true;
1239
1264
  break;
@@ -1242,12 +1267,13 @@ function projectConversation(raw, sessionID) {
1242
1267
  if (row.info.sessionID !== sessionID)
1243
1268
  throw new Error(consoleDetailMessages.readFailed);
1244
1269
  const message = {
1245
- id: text(row.info.id),
1270
+ id: row.info.id,
1246
1271
  role: row.info.role,
1247
1272
  createdAt: row.info.time.created,
1248
1273
  ...row.info.time.completed !== undefined ? { completedAt: row.info.time.completed } : {},
1249
1274
  text: [],
1250
- tools: []
1275
+ tools: [],
1276
+ order: []
1251
1277
  };
1252
1278
  if (row.parts.length > limits.parts)
1253
1279
  truncated = true;
@@ -1271,16 +1297,19 @@ function projectConversation(raw, sessionID) {
1271
1297
  if (part.sessionID !== sessionID || part.messageID !== row.info.id)
1272
1298
  throw new Error(consoleDetailMessages.readFailed);
1273
1299
  if (part.type === "text") {
1274
- if (!part.ignored)
1300
+ if (!part.ignored) {
1301
+ message.order?.push({ type: "text", index: message.text.length });
1275
1302
  message.text.push(text(part.text));
1303
+ }
1276
1304
  } else {
1277
1305
  if (toolsRemaining-- <= 0) {
1278
1306
  truncated = true;
1279
1307
  continue;
1280
1308
  }
1281
1309
  const state = part.state;
1310
+ message.order?.push({ type: "tool", index: message.tools.length });
1282
1311
  message.tools.push({
1283
- id: text(part.callID),
1312
+ id: part.callID,
1284
1313
  name: text(part.tool),
1285
1314
  status: state.status,
1286
1315
  arguments: argumentsText(state.input),
@@ -1385,7 +1414,7 @@ function createConversationReader(deps) {
1385
1414
  if (workspaceKey(session.directory) !== workspaceKey(directory))
1386
1415
  throw new Error(consoleDetailMessages.readFailed);
1387
1416
  const time = z4.preprocess(only(["created"]), z4.object({ created: z4.number().finite().min(0).max(8640000000000000) }).strict()).safeParse(session.time);
1388
- const title = boundedTitle(session.title);
1417
+ const title = boundedTitle(typeof session.title === "string" ? maskConversationCredentials(session.title) : session.title);
1389
1418
  return {
1390
1419
  id: session.id,
1391
1420
  directory: session.directory,
@@ -1435,10 +1464,13 @@ function createConversationReader(deps) {
1435
1464
  labels.delete(oldest);
1436
1465
  return session;
1437
1466
  },
1438
- messages: async (id) => {
1467
+ messages: async (id, limit) => {
1439
1468
  if (!verified.has(id))
1440
1469
  throw new Error(consoleDetailMessages.readFailed);
1441
- const result = await client.session.messages({ path: { id } });
1470
+ const result = await client.session.messages({
1471
+ path: { id },
1472
+ ...limit === undefined ? {} : { query: { limit } }
1473
+ });
1442
1474
  if (!result.response.ok || result.error !== undefined)
1443
1475
  throw new Error(consoleDetailMessages.readFailed);
1444
1476
  return result.data;
@@ -1475,14 +1507,37 @@ function createConversationReader(deps) {
1475
1507
  const read = async (endpoint, sessionID, signal) => withHistory(endpoint, signal, async (port) => {
1476
1508
  const session = await port.get(sessionID);
1477
1509
  return {
1478
- ...projectConversation(await port.messages(sessionID), sessionID),
1510
+ ...projectConversation(await port.messages(sessionID, CONVERSATION_LIMITS.messages + 1), sessionID),
1479
1511
  session: session.label
1480
1512
  };
1481
1513
  });
1514
+ const roots = new Map;
1482
1515
  read.root = (endpoint, projection, signal) => withHistory(endpoint, signal, async (port) => {
1483
- const recovered = await recoverRootSession(port, projection);
1516
+ const key = JSON.stringify([
1517
+ endpoint,
1518
+ projection.runID,
1519
+ projection.rootOperationID,
1520
+ projection.operations.flatMap((operation) => operation.attempts.map((attempt) => [
1521
+ operation.operationID,
1522
+ attempt.attemptID,
1523
+ attempt.sessionID
1524
+ ]))
1525
+ ]);
1526
+ const cached = roots.get(key);
1527
+ const recovered = cached ? {
1528
+ session: await port.get(cached),
1529
+ association: "sdk-parent"
1530
+ } : await recoverRootSession(port, projection);
1531
+ roots.delete(key);
1532
+ if (recovered.association === "sdk-parent")
1533
+ roots.set(key, recovered.session.id);
1534
+ if (roots.size > 32) {
1535
+ const oldest = roots.keys().next().value;
1536
+ if (oldest !== undefined)
1537
+ roots.delete(oldest);
1538
+ }
1484
1539
  return {
1485
- ...projectConversation(await port.messages(recovered.session.id), recovered.session.id),
1540
+ ...projectConversation(await port.messages(recovered.session.id, CONVERSATION_LIMITS.messages + 1), recovered.session.id),
1486
1541
  session: recovered.session.label,
1487
1542
  association: recovered.association
1488
1543
  };
@@ -4482,234 +4537,286 @@ async function handleLifecycleHistoryRoute(request, response, url, deps) {
4482
4537
  // src/console/inspector.ts
4483
4538
  var CONVERSATION_CLIENT_JS = `
4484
4539
  function wireConversationInspector(){
4540
+ if(document.__conversationsWired){return}document.__conversationsWired=true;
4541
+ var msg=${JSON.stringify(consoleDetailMessages)},limits=${JSON.stringify(CONVERSATION_LIMITS)};
4542
+ var records=new Map(),recoveries=new Map(),stopped=false,timer=null,scheduled=null,active=0,serial=0,retained=null;
4485
4543
  var mount=document.getElementById('lifecycle-timeline-mount');
4486
- if(!mount){return}
4487
- var msg=${JSON.stringify(consoleDetailMessages)};
4488
- var limits=${JSON.stringify(CONVERSATION_LIMITS)};
4489
- var panel=document.createElement('section');panel.className='lc-conversation';panel.hidden=true;panel.id='lc-attempt-conversation';
4490
- panel.setAttribute('data-conversation-inspector','');
4491
- panel.setAttribute('aria-labelledby','lc-conversation-heading');
4492
- mount.appendChild(panel);
4493
- var selection=null,request=null,epoch=0,retained=null;
4494
- function element(tag,text){var e=document.createElement(tag);if(text!==undefined){e.textContent=text}return e}
4495
- function selectedButton(){
4496
- if(!selection||mount.getAttribute('data-conversation-transition')==='1'||
4497
- mount.getAttribute('data-lifecycle-run')!==selection.run||
4498
- (mount.getAttribute('data-conversation-cursor')||null)!==selection.cursor){return null}
4499
- return Array.from(mount.querySelectorAll('[data-inspect-attempt],[data-inspect-root]')).find(function(button){
4500
- var attempt=button.closest('[data-lifecycle-attempt-id]');
4501
- var operation=button.closest('[data-lifecycle-operation-id]');
4502
- var session=button.closest(selection.target==='root'?'[data-root-session-id]':'[data-lifecycle-session-id]');
4503
- return !button.disabled&&operation&&session&&
4504
- (selection.target==='root'?button.hasAttribute('data-inspect-root'):attempt&&attempt.getAttribute('data-lifecycle-attempt-id')===selection.attempt&&
4505
- button.getAttribute('data-inspect-attempt')===selection.attempt)&&
4506
- operation.getAttribute('data-lifecycle-operation-id')===selection.operation&&
4507
- button.getAttribute('data-inspect-operation')===selection.operation&&
4508
- session.getAttribute(selection.target==='root'?'data-root-session-id':'data-lifecycle-session-id')===selection.session;
4509
- })||null;
4510
- }
4511
- function expanded(button,open){
4512
- if(button.getAttribute('aria-expanded')!==String(open)){button.setAttribute('aria-expanded',String(open))}
4513
- var label=open?msg.close:msg.inspectConversation;
4514
- if(button.textContent!==label){button.textContent=label}
4515
- }
4516
- function cancel(){
4517
- epoch++;if(request){request.abort();request=null}
4518
- }
4519
- function close(focus){
4520
- var button=selectedButton();
4521
- cancel();selection=null;retained=null;
4522
- mount.querySelectorAll('[data-inspect-attempt],[data-inspect-root]').forEach(function(control){expanded(control,false)});
4523
- panel.textContent='';panel.hidden=true;
4524
- ['data-conversation-kind','data-conversation-run','data-conversation-operation','data-conversation-attempt','data-conversation-session','data-conversation-target','aria-busy'].forEach(function(name){panel.removeAttribute(name)});
4525
- mount.appendChild(panel);
4526
- if(focus===true&&button){button.focus({preventScroll:true})}
4527
- }
4528
- // The renderer replaces metadata, not retained content. Restore only after
4529
- // the real run/cursor commits and the same attempt/session is still visible.
4530
- function retain(){
4531
- if(!selection||panel.hidden||!selectedButton()){return}
4532
- var scrolls=[],ancestor=panel.parentElement;
4533
- while(ancestor){scrolls.push([ancestor,ancestor.scrollTop,ancestor.scrollLeft]);ancestor=ancestor.parentElement}
4534
- panel.querySelectorAll('pre,[data-conversation-tool-list]').forEach(function(node){scrolls.push([node,node.scrollTop,node.scrollLeft])});
4535
- retained={scrolls:scrolls,top:panel.getBoundingClientRect().top,focus:panel.contains(document.activeElement)?document.activeElement:null};
4536
- }
4537
- function reanchor(){
4538
- if(!selection){return}
4539
- var button=selectedButton();
4540
- if(!button){close();return}
4541
- var attempt=selection.target==='root'?button.closest('.lct-operation-card'):button.closest('[data-lifecycle-attempt-id]');
4542
- if(panel.parentElement!==attempt){attempt.appendChild(panel)}
4543
- expanded(button,true);
4544
- if(retained){
4545
- retained.scrolls.forEach(function(item){if(item[0].isConnected){item[0].scrollTop=item[1];item[0].scrollLeft=item[2]}});
4546
- var tree=mount.querySelector('[data-lifecycle-tree-mount]');
4547
- if(tree){tree.scrollTop+=panel.getBoundingClientRect().top-retained.top}
4548
- if(retained.focus){retained.focus.focus({preventScroll:true})}
4549
- retained=null;
4544
+ function el(tag,text){var node=document.createElement(tag);if(text!==undefined){node.textContent=text}return node}
4545
+ function setText(node,text){if(node.textContent!==text){node.textContent=text}}
4546
+ function visible(node){
4547
+ if(document.hidden||!node.isConnected){return false}
4548
+ var parent=node;
4549
+ while(parent){if(parent.hidden){return false}parent=parent.parentElement}
4550
+ var rect=node.getBoundingClientRect();
4551
+ return rect.bottom>=0&&rect.top<(window.innerHeight||1000);
4552
+ }
4553
+ function cancel(record){
4554
+ if(record.request){record.request.abort();record.request=null}
4555
+ if(record.finish){record.finish();record.finish=null}
4556
+ record.version++;
4557
+ }
4558
+ function discard(record){
4559
+ cancel(record);record.panel.remove();record.body.textContent='';record.source.textContent='';record.rows.clear();records.delete(record.key);
4560
+ }
4561
+ function clear(scope){
4562
+ Array.from(records.values()).forEach(function(record){
4563
+ if(scope==='native'&&record.selection.target==='session'){return}
4564
+ discard(record);
4565
+ });
4566
+ if(scope!=='native'){retained=null}
4567
+ }
4568
+ function recovery(record){
4569
+ var saved=recoveries.get(record.key);
4570
+ if(saved&&saved.until<=Date.now()){recoveries.delete(record.key);return null}
4571
+ return saved||null;
4572
+ }
4573
+ function rootRecovery(record,data){
4574
+ return record.selection.target==='root'&&!record.selection.session&&data.kind==='unavailable'&&
4575
+ [msg.rootUnavailable,msg.rootAmbiguous,msg.searchIncomplete].includes(data.message)?
4576
+ {message:data.message,until:Date.now()+30000}:null;
4577
+ }
4578
+ function rememberRecovery(record,saved){
4579
+ if(!saved){recoveries.delete(record.key);return}
4580
+ recoveries.delete(record.key);recoveries.set(record.key,saved);
4581
+ if(recoveries.size>128){recoveries.delete(recoveries.keys().next().value)}
4582
+ }
4583
+ function desired(){
4584
+ var result=[];
4585
+ if(stopped||document.hidden){return result}
4586
+ var explicitHistorical=!!((mount&&mount.getAttribute('data-conversation-cursor'))||new URLSearchParams(window.location.search).has('cursor'));
4587
+ var transitioning=mount&&mount.getAttribute('data-conversation-transition')==='1';
4588
+ if(mount){
4589
+ var notice=mount.querySelector('[data-conversation-historical]');
4590
+ if(explicitHistorical){
4591
+ if(!notice){notice=el('p',msg.historical);notice.setAttribute('data-conversation-historical','');mount.appendChild(notice)}
4592
+ }else if(notice){notice.remove()}
4593
+ }
4594
+ if(mount&&!explicitHistorical&&!transitioning){
4595
+ mount.querySelectorAll('[data-inspect-root],[data-inspect-attempt]').forEach(function(button){
4596
+ if(button.disabled){return}
4597
+ var root=button.hasAttribute('data-inspect-root');
4598
+ var session=button.closest(root?'[data-root-session-id]':'[data-lifecycle-session-id]');
4599
+ var host=root?button.closest('.lct-operation-card'):button.closest('[data-lifecycle-attempt-id]');
4600
+ if(!session||!host){return}
4601
+ var selection={target:root?'root':'attempt',run:mount.getAttribute('data-lifecycle-run'),
4602
+ operation:button.getAttribute('data-inspect-operation'),attempt:root?null:button.getAttribute('data-inspect-attempt'),
4603
+ session:session.getAttribute(root?'data-root-session-id':'data-lifecycle-session-id')};
4604
+ if(!selection.run||!selection.operation){return}
4605
+ result.push({key:JSON.stringify(selection),selection:selection,host:host,button:button});
4606
+ });
4550
4607
  }
4608
+ document.querySelectorAll('[data-session-conversation]').forEach(function(host){
4609
+ var selection={target:'session',session:host.getAttribute('data-session-conversation')};
4610
+ if(explicitHistorical){
4611
+ if(visible(host)){setText(host,msg.historical)}
4612
+ return;
4613
+ }
4614
+ result.push({key:JSON.stringify(selection),selection:selection,host:host});
4615
+ });
4616
+ return result;
4551
4617
  }
4552
- function reset(){
4553
- panel.textContent='';
4554
- var header=element('header');header.className='lc-conversation-head';
4555
- var heading=element('h3',msg.title);heading.id='lc-conversation-heading';heading.tabIndex=-1;header.appendChild(heading);
4556
- var closeBtn=element('button',msg.close);closeBtn.type='button';
4557
- closeBtn.setAttribute('data-conversation-close','');
4558
- closeBtn.addEventListener('click',function(){close(true)});header.appendChild(closeBtn);panel.appendChild(header);
4559
- }
4560
- function showHeading(){
4561
- var heading=panel.querySelector('h3');
4562
- heading.focus({preventScroll:true});
4563
- heading.scrollIntoView({block:'start',inline:'nearest'});
4564
- }
4565
- function load(reveal){
4566
- if(!selectedButton()){return}
4567
- cancel();var current=epoch,selected=selection;
4568
- reset();
4569
- panel.setAttribute('data-conversation-kind','loading');
4570
- panel.setAttribute('aria-busy','true');
4571
- var status=element('p',msg.loading);status.setAttribute('role','status');panel.appendChild(status);
4572
- var opening=null;
4573
- if(reveal===true){
4574
- showHeading();
4575
- opening={focus:document.activeElement,scrolls:[mount.querySelector('[data-lifecycle-tree-mount]'),mount.closest('.scroll')].filter(Boolean).map(function(node){return [node,node.scrollTop,node.scrollLeft]})};
4576
- }
4577
- var controller=new AbortController();request=controller;
4578
- var query=new URLSearchParams({run:selected.run,operation:selected.operation,diagnostic:'1'});
4579
- if(selected.target==='root'){query.set('target','root')}else{query.set('attempt',selected.attempt)}
4580
- query.set('token',document.documentElement.getAttribute('data-token')||'');
4581
- if(selected.cursor!==null){query.set('cursor',selected.cursor)}
4582
- var timeout=setTimeout(function(){controller.abort()},15000);
4583
- fetch('/api/graph/conversation?'+query.toString(),{signal:controller.signal,cache:'no-store'})
4584
- .then(function(r){return r.json()}).then(function(data){
4585
- if(epoch!==current||selection!==selected||!selectedButton()){return}
4586
- var bringIntoView=opening&&document.activeElement===opening.focus&&opening.scrolls.every(function(item){return item[0].scrollTop===item[1]&&item[0].scrollLeft===item[2]});
4587
- if((data.kind==='conversation'||data.kind==='empty')&&(data.runID!==selected.run||data.operationID!==selected.operation||
4588
- (selected.target==='root'?(data.target!=='root'||data.attemptID!==undefined||!data.sessionID||(!selected.session&&!['sdk-parent','tool-result'].includes(data.association))):(data.target!=='attempt'||data.attemptID!==selected.attempt))||
4589
- (selected.session&&data.sessionID!==selected.session))){
4590
- data={kind:'unavailable',message:msg.unavailable};
4591
- }
4592
- status.textContent=data.message||msg.unavailable;
4593
- panel.setAttribute('data-conversation-kind',data.kind||'unavailable');
4594
- if(data.kind==='conversation'||data.kind==='empty'){
4595
- panel.setAttribute('data-conversation-session',data.sessionID);
4596
- var source=element('p');source.appendChild(element('strong',data.session&&data.session.title|| (selected.target==='root'?msg.orchestrator:msg.session)));
4597
- var created=consoleLabels.utcTime(data.session&&data.session.createdAt);
4598
- source.appendChild(element('small',' · '+(created===msg.timeUnknown?msg.timeUnknown:msg.sessionCreated+': '+created)));
4599
- source.appendChild(element('code',' · '+data.sessionID));panel.appendChild(source);
4600
- if(data.session&&data.session.id===data.sessionID){document.dispatchEvent(new CustomEvent('console-session-metadata',{detail:{session:data.session,runID:data.runID,target:selected.target}}))}
4601
- }
4602
- if(data.kind==='conversation'){
4603
- renderMessages(data.messages||[]);
4604
- }else if(data.kind==='empty'){
4605
- renderMessages([]);
4618
+ function create(item){
4619
+ var panel=el('section');panel.className='lc-conversation';panel.id='lc-conversation-'+(++serial);
4620
+ panel.setAttribute('data-conversation-inspector','');
4621
+ panel.setAttribute('data-conversation-target',item.selection.target);
4622
+ panel.setAttribute('data-conversation-session',item.selection.session||'');
4623
+ if(item.selection.run){panel.setAttribute('data-conversation-run',item.selection.run)}
4624
+ if(item.selection.attempt){panel.setAttribute('data-conversation-attempt',item.selection.attempt)}
4625
+ var heading=el('h3',msg.title);panel.appendChild(heading);
4626
+ var status=el('p',msg.loading);status.setAttribute('role','status');panel.appendChild(status);
4627
+ var source=el('p');panel.appendChild(source);
4628
+ var body=el('div');body.setAttribute('data-conversation-messages','');panel.appendChild(body);
4629
+ var refresh=el('button',msg.refresh);refresh.type='button';refresh.setAttribute('data-conversation-refresh','');panel.appendChild(refresh);
4630
+ var record={key:item.key,selection:item.selection,host:item.host,panel:panel,status:status,source:source,body:body,rows:new Map(),
4631
+ request:null,finish:null,version:0,due:0,button:item.button};
4632
+ refresh.addEventListener('click',function(){record.due=0;recoveries.delete(record.key);sync()});
4633
+ item.host.appendChild(panel);records.set(item.key,record);
4634
+ return record;
4635
+ }
4636
+ function matches(record,data){
4637
+ var s=record.selection;
4638
+ return data.sessionID&&(s.session?data.sessionID===s.session:['sdk-parent','tool-result'].includes(data.association))&&
4639
+ (s.target==='session'?data.target==='session'&&!data.runID:
4640
+ data.runID===s.run&&data.operationID===s.operation&&data.target===s.target&&
4641
+ (s.target==='root'?data.attemptID===undefined:data.attemptID===s.attempt));
4642
+ }
4643
+ function render(record,messages){
4644
+ var remaining=limits.totalChars,tools=0,truncated=false,used=new Set();
4645
+ function text(value){
4646
+ value=String(value===undefined?'':value);
4647
+ var length=Math.min(value.length,limits.fieldChars+msg.truncatedMarker.length,remaining);
4648
+ remaining-=length;if(length<value.length){truncated=true}return value.slice(0,length);
4649
+ }
4650
+ if(messages.length>limits.messages){truncated=true}
4651
+ messages.slice(0,limits.messages).forEach(function(message){
4652
+ if(!message||!['user','assistant'].includes(message.role)||typeof message.id!=='string'||used.has(message.id)){return}
4653
+ used.add(message.id);
4654
+ var row=record.rows.get(message.id);
4655
+ if(!row){
4656
+ var article=el('article');article.setAttribute('data-conversation-role',message.role);
4657
+ article.appendChild(el('h4',message.role==='user'?msg.prompt:msg.assistant));
4658
+ var time=el('time');article.appendChild(time);
4659
+ row={node:article,time:time,texts:[],tools:new Map()};record.rows.set(message.id,row);record.body.appendChild(article);
4660
+ }
4661
+ var selection=window.getSelection&&window.getSelection();
4662
+ if(selection&&!selection.isCollapsed&&row.node.contains(selection.anchorNode)){return}
4663
+ setText(row.time,consoleLabels.utcTime(message.createdAt));
4664
+ var parts=(message.text||[]).slice(0,limits.parts);
4665
+ parts.forEach(function(value,index){
4666
+ if(!row.texts[index]){row.texts[index]=el('pre');row.node.insertBefore(row.texts[index],row.node.querySelector('details'))}
4667
+ setText(row.texts[index],text(value));
4668
+ });
4669
+ while(row.texts.length>parts.length){row.texts.pop().remove()}
4670
+ var seenTools=new Set();
4671
+ (message.tools||[]).slice(0,limits.parts).forEach(function(tool){
4672
+ if(tools++>=limits.tools||remaining<=0){truncated=true;return}
4673
+ seenTools.add(tool.id);
4674
+ var call=row.tools.get(tool.id);
4675
+ if(!call){
4676
+ var detail=el('details');detail.setAttribute('data-tool-call',tool.id);detail.open=true;
4677
+ var summary=el('summary');detail.appendChild(summary);
4678
+ detail.appendChild(el('h5',msg.arguments));var args=el('pre');detail.appendChild(args);
4679
+ detail.appendChild(el('h5',msg.result));var result=el('pre');detail.appendChild(result);
4680
+ call={node:detail,summary:summary,args:args,result:result};row.tools.set(tool.id,call);row.node.appendChild(detail);
4606
4681
  }
4607
- // The short loading card may not have enough scroll range. Complete
4608
- // the explicit reveal after layout grows, unless the reader moved on.
4609
- if(bringIntoView){panel.querySelector('h3').scrollIntoView({block:'start',inline:'nearest'})}
4610
- }).catch(function(){
4611
- if(epoch===current){status.textContent=msg.unavailable;panel.setAttribute('data-conversation-kind','unavailable')}
4612
- }).finally(function(){
4613
- clearTimeout(timeout);
4614
- if(epoch!==current){return}
4615
- request=null;
4616
- panel.setAttribute('aria-busy','false');
4617
- var refresh=element('button',msg.refresh);refresh.type='button';refresh.setAttribute('data-conversation-refresh','');
4618
- refresh.addEventListener('click',load);panel.appendChild(refresh);
4682
+ setText(call.summary,text(tool.name)+' · '+text(tool.status));
4683
+ setText(call.args,text(tool.arguments));setText(call.result,text(tool.result===undefined?msg.pending:tool.result));
4619
4684
  });
4620
- }
4621
- function renderMessages(messages){
4622
- var truncated=messages.length>limits.messages,remaining=limits.totalChars,toolCount=0;
4623
- messages=messages.slice(0,limits.messages);
4624
- var tools=element('details');tools.setAttribute('data-conversation-tools','');tools.open=true;
4625
- var count=messages.reduce(function(total,m){return total+(m.tools||[]).length},0);
4626
- tools.appendChild(element('summary',msg.tools.replace('{count}',String(count))));
4627
- var calls=element('div');calls.setAttribute('data-conversation-tool-list','');tools.appendChild(calls);
4628
- panel.appendChild(tools);
4629
- function text(value){
4630
- value=String(value);
4631
- var size=Math.min(value.length,limits.fieldChars+msg.truncatedMarker.length,remaining);
4632
- remaining-=size;
4633
- if(size<value.length){truncated=true}
4634
- return value.slice(0,size);
4635
- }
4636
- ['user','assistant'].forEach(function(role){
4637
- var section=element('section');section.setAttribute('data-conversation-role',role);
4638
- section.appendChild(element('h4',role==='user'?msg.prompt:msg.assistant));
4639
- var hasText=false;
4640
- messages.filter(function(m){return m.role===role}).forEach(function(m){
4641
- var item=element('article');item.appendChild(element('time',consoleLabels.utcTime(m.createdAt)));
4642
- if((m.text||[]).length>limits.parts){truncated=true}
4643
- (m.text||[]).slice(0,limits.parts).forEach(function(value){
4644
- if(!value){return}
4645
- hasText=true;
4646
- if(remaining>0){item.appendChild(element('pre',text(value)))}else{truncated=true}
4685
+ row.tools.forEach(function(call,id){if(!seenTools.has(id)){call.node.remove();row.tools.delete(id)}});
4686
+ if(Array.isArray(message.order)){
4687
+ var previous=row.time;
4688
+ message.order.slice(0,limits.parts).forEach(function(part){
4689
+ var node=part.type==='text'?row.texts[part.index]:row.tools.get((message.tools[part.index]||{}).id);
4690
+ node=node&&node.node||node;
4691
+ if(node){if(previous.nextSibling!==node){row.node.insertBefore(node,previous.nextSibling||null)}previous=node}
4647
4692
  });
4648
- if(item.querySelector('pre')){section.appendChild(item)}
4649
- });panel.appendChild(section);
4650
- if(!hasText){section.appendChild(element('p',role==='user'?msg.noPrompts:msg.noOutput))}
4693
+ }
4694
+ });
4695
+ record.rows.forEach(function(row,id){if(!used.has(id)){row.node.remove();record.rows.delete(id)}});
4696
+ if(truncated){setText(record.status,msg.current+'\\n'+msg.truncated)}
4697
+ }
4698
+ function load(record){
4699
+ var selected=record.selection,controller=new AbortController(),version=++record.version;
4700
+ record.request=controller;active++;record.panel.setAttribute('aria-busy','true');
4701
+ var finished=false;
4702
+ record.finish=function(){if(!finished){finished=true;active--;clearTimeout(timeout)}};
4703
+ var query=new URLSearchParams({token:document.documentElement.getAttribute('data-token')||''});
4704
+ var path='/api/graph/conversation';
4705
+ if(selected.target==='session'){path='/api/console/conversation';query.set('session',selected.session)}
4706
+ else{query.set('run',selected.run);query.set('operation',selected.operation);
4707
+ if(selected.target==='root'){query.set('target','root')}else{query.set('attempt',selected.attempt)}}
4708
+ var timeout=setTimeout(function(){
4709
+ if(record.version!==version){return}
4710
+ cancel(record);record.due=Date.now()+30000;setText(record.status,msg.unavailable);
4711
+ record.panel.setAttribute('aria-busy','false');schedule();
4712
+ },15000);
4713
+ fetch(path+'?'+query,{signal:controller.signal,cache:'no-store'}).then(function(response){return response.json()}).then(function(data){
4714
+ if(record.version!==version||!records.has(record.key)||!visible(record.host)||controller.signal.aborted){return}
4715
+ if((data.kind==='conversation'||data.kind==='empty')&&!matches(record,data)){data={kind:'unavailable',message:msg.unavailable}}
4716
+ var negative=rootRecovery(record,data);
4717
+ record.panel.setAttribute('data-conversation-kind',data.kind||'unavailable');
4718
+ setText(record.status,data.message||((data.kind==='conversation'||data.kind==='empty')?msg.current:msg.unavailable));
4719
+ if(data.kind==='conversation'||data.kind==='empty'){
4720
+ recoveries.delete(record.key);
4721
+ record.panel.setAttribute('data-conversation-session',data.sessionID);
4722
+ setText(record.source,(data.session&&data.session.title||msg.session)+' · '+data.sessionID);
4723
+ var focus=record.panel.contains(document.activeElement)?document.activeElement:null;
4724
+ render(record,data.messages||[]);
4725
+ if(focus&&focus.isConnected&&focus!==document.activeElement&&
4726
+ (!document.activeElement||document.activeElement===document.body)){focus.focus({preventScroll:true})}
4727
+ if(data.session&&data.session.id===data.sessionID){
4728
+ document.dispatchEvent(new CustomEvent('console-session-metadata',{detail:{session:data.session,runID:data.runID,target:selected.target}}));
4729
+ }
4730
+ }else{rememberRecovery(record,negative);record.body.textContent='';record.source.textContent='';record.rows.clear()}
4731
+ record.due=['disabled','unauthorized','historical','invalid','oversized'].includes(data.kind)?Infinity:Date.now()+(data.kind==='unavailable'?30000:2000);
4732
+ }).catch(function(){
4733
+ if(record.version===version&&!controller.signal.aborted){
4734
+ recoveries.delete(record.key);
4735
+ setText(record.status,msg.unavailable);record.panel.setAttribute('data-conversation-kind','unavailable');
4736
+ record.body.textContent='';record.source.textContent='';record.rows.clear();record.due=Date.now()+30000;
4737
+ }
4738
+ }).finally(function(){
4739
+ if(!finished){finished=true;clearTimeout(timeout);active--}
4740
+ if(record.version===version){record.request=null;record.panel.setAttribute('aria-busy','false');if(controller.signal.aborted){record.due=Date.now()+30000;setText(record.status,msg.unavailable)}}
4741
+ schedule();
4742
+ });
4743
+ }
4744
+ function sync(){
4745
+ var items=desired(),byKey=new Map(items.map(function(item){return [item.key,item]}));
4746
+ Array.from(records.values()).forEach(function(record){
4747
+ var item=byKey.get(record.key);
4748
+ if(!item||!visible(item.host)){
4749
+ if(item&&item.host.isConnected){item.host.style.minHeight=item.host.getBoundingClientRect().height+'px'}
4750
+ discard(record);return;
4751
+ }
4752
+ record.host=item.host;
4753
+ if(record.panel.parentElement!==item.host){item.host.appendChild(record.panel)}
4651
4754
  });
4652
- messages.forEach(function(m){
4653
- if((m.tools||[]).length>limits.parts){truncated=true}
4654
- (m.tools||[]).slice(0,limits.parts).forEach(function(tool){
4655
- if(toolCount++>=limits.tools||remaining<=0){truncated=true;return}
4656
- var call=element('details');call.setAttribute('data-tool-call',tool.id);
4657
- call.appendChild(element('summary',text(tool.name)+' · '+text(tool.status)));
4658
- if(tool.startedAt!==undefined){call.appendChild(element('time',consoleLabels.utcTime(tool.startedAt)))}
4659
- call.appendChild(element('h5',msg.arguments));call.appendChild(element('pre',text(tool.arguments)));
4660
- call.appendChild(element('h5',msg.result));call.appendChild(element('pre',text(tool.result===undefined?msg.pending:tool.result)));
4661
- calls.appendChild(call);
4755
+ items.forEach(function(item){
4756
+ if(!visible(item.host)){return}
4757
+ var record=records.get(item.key);
4758
+ if(!record&&records.size<8){
4759
+ if(item.selection.target==='session'){item.host.textContent=''}
4760
+ record=create(item);
4761
+ }
4762
+ if(!record){return}
4763
+ if(item.button){item.button.hidden=true;item.button.setAttribute('aria-controls',record.panel.id)}
4764
+ var saved=recovery(record);
4765
+ if(saved&&!record.rows.size&&!record.request){record.panel.setAttribute('data-conversation-kind','unavailable');setText(record.status,saved.message)}
4766
+ if(!record.request&&active<2&&record.due<=Date.now()&&!saved){load(record)}
4767
+ });
4768
+ }
4769
+ function schedule(){
4770
+ if(stopped||scheduled!==null){return}
4771
+ scheduled=setTimeout(function(){scheduled=null;sync()},50);
4772
+ }
4773
+ document.addEventListener('conversation-selection',function(event){
4774
+ clear(event&&event.detail&&event.detail.scope==='native'?'native':'all');
4775
+ sync();
4776
+ });
4777
+ document.addEventListener('conversation-tree-replacing',function(){
4778
+ var scroll=document.querySelector('.scroll'),tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
4779
+ retained={focus:document.activeElement,scroll:scroll,scrollTop:scroll&&scroll.scrollTop,treeTop:tree&&tree.scrollTop,positions:[]};
4780
+ records.forEach(function(record){
4781
+ [record.body].concat(Array.from(record.panel.querySelectorAll('pre'))).forEach(function(node){
4782
+ retained.positions.push([node,node.scrollTop,node.scrollLeft]);
4662
4783
  });
4663
4784
  });
4664
- if(count===0){calls.appendChild(element('p',msg.noTools))}
4665
- if(truncated){panel.appendChild(element('p',msg.truncated))}
4666
- }
4667
- mount.addEventListener('click',function(event){
4668
- var button=event.target.closest('[data-inspect-attempt],[data-inspect-root]');
4669
- if(!button||button.disabled||mount.getAttribute('data-conversation-transition')==='1'){return}
4670
- if(button===selectedButton()){close(true);return}
4671
- close();
4672
- var root=button.hasAttribute('data-inspect-root');
4673
- var session=button.closest(root?'[data-root-session-id]':'[data-lifecycle-session-id]');
4674
- if(!session){return}
4675
- selection={
4676
- run:mount.getAttribute('data-lifecycle-run'),
4677
- operation:button.getAttribute('data-inspect-operation'),
4678
- attempt:button.getAttribute('data-inspect-attempt'),
4679
- target:root?'root':'attempt',
4680
- session:session.getAttribute(root?'data-root-session-id':'data-lifecycle-session-id'),
4681
- cursor:mount.getAttribute('data-conversation-cursor')||null
4682
- };
4683
- if(!selectedButton()){close();return}
4684
- panel.setAttribute('data-conversation-run',selection.run);
4685
- panel.setAttribute('data-conversation-operation',selection.operation);
4686
- if(selection.attempt){panel.setAttribute('data-conversation-attempt',selection.attempt)}
4687
- panel.setAttribute('data-conversation-target',selection.target);
4688
- panel.setAttribute('data-conversation-session',selection.session);
4689
- reanchor();
4690
- panel.hidden=false;
4691
- if(selection.cursor!==null){reset();panel.setAttribute('data-conversation-kind','historical');panel.appendChild(element('p',msg.historical));showHeading()}
4692
- else{load(true)}
4785
+ records.forEach(function(record){if(record.selection.target!=='session'){record.panel.remove()}});
4693
4786
  });
4694
- document.addEventListener('conversation-selection',close);
4695
- document.addEventListener('conversation-tree-replacing',retain);
4696
- document.addEventListener('conversation-tree-committed',reanchor);
4697
- window.addEventListener('pagehide',close);
4787
+ document.addEventListener('conversation-tree-committed',function(){
4788
+ sync();
4789
+ if(retained){
4790
+ retained.positions.forEach(function(item){if(item[0].isConnected){item[0].scrollTop=item[1];item[0].scrollLeft=item[2]}});
4791
+ var tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
4792
+ if(tree&&retained.treeTop!==null){tree.scrollTop=retained.treeTop}
4793
+ if(retained.scroll){retained.scroll.scrollTop=retained.scrollTop}
4794
+ if(retained.focus&&retained.focus.isConnected&&retained.focus!==document.activeElement&&
4795
+ (!document.activeElement||document.activeElement===document.body||document.activeElement===document.documentElement)){
4796
+ retained.focus.focus({preventScroll:true});
4797
+ }
4798
+ retained=null;
4799
+ }
4800
+ });
4801
+ document.addEventListener('console-conversation-selection',sync);
4802
+ document.addEventListener('visibilitychange',function(){if(document.hidden){clear()}else{sync()}});
4803
+ document.addEventListener('scroll',schedule,true);
4804
+ window.addEventListener('resize',schedule);
4805
+ window.addEventListener('pagehide',function(){stopped=true;clear();clearInterval(timer);clearTimeout(scheduled);scheduled=null});
4806
+ window.addEventListener('pageshow',function(event){if(event.persisted){stopped=false;timer=setInterval(sync,2000);sync()}});
4807
+ timer=setInterval(sync,2000);sync();
4698
4808
  }
4699
4809
  `;
4700
4810
  var CONVERSATION_STYLE = `
4701
- .lc-conversation{margin-top:12px;padding:12px;min-width:0;border:1px solid var(--cp-border);border-radius:16px;background:var(--cp-surface);color:var(--cp-text);overflow-anchor:none}
4811
+ .lc-conversation{margin-top:12px;padding:12px;min-width:0;border:1px solid var(--cp-border,var(--border));border-radius:12px;background:var(--cp-surface,var(--surface));color:var(--cp-text,var(--fg));overflow-anchor:none}
4702
4812
  .lc-conversation[hidden]{display:none}
4703
- .lc-conversation-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}
4704
4813
  .lc-conversation h3,.lc-conversation h4,.lc-conversation p{margin:8px 0}
4705
- .lc-conversation h3{scroll-margin-top:12px}
4706
- .lc-conversation pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:400px;overflow:auto;background:var(--cp-surface-soft);padding:12px;font-family:Consolas,"Courier New",Courier,monospace}
4707
- .lc-conversation [data-conversation-role="user"] pre{max-height:180px}
4708
- .lc-conversation [data-conversation-tool-list]{max-height:160px;overflow:auto}
4709
- .lc-conversation details{margin-top:12px;padding:8px;border:1px solid var(--cp-border);border-radius:.625rem}
4710
- .lc-conversation summary{cursor:pointer;color:var(--cp-accent)}
4711
- .lc-conversation time{font-size:11px;color:var(--cp-text-muted)}
4712
- .lc-conversation button,[data-inspect-attempt],[data-inspect-root]{padding:6px 10px;margin:6px;border:1px solid var(--cp-border);border-radius:.625rem;background:var(--cp-surface);color:var(--cp-accent)}
4814
+ .lc-conversation [data-conversation-messages]{max-height:520px;overflow:auto;overflow-anchor:none}
4815
+ .lc-conversation pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:400px;overflow:auto;background:var(--cp-surface-soft,var(--bg));padding:12px;font-family:var(--font-mono)}
4816
+ .lc-conversation details{margin:12px 0;padding:8px;border:1px solid var(--cp-border,var(--border));border-radius:8px}
4817
+ .lc-conversation summary{cursor:pointer;color:var(--cp-accent,var(--accent))}
4818
+ .lc-conversation time{font-size:11px;color:var(--cp-text-muted,var(--muted))}
4819
+ .lc-conversation button,[data-inspect-attempt],[data-inspect-root]{padding:6px 10px;margin:6px;border:1px solid var(--cp-border,var(--border));border-radius:8px;background:var(--cp-surface,var(--surface));color:var(--cp-accent,var(--accent))}
4713
4820
  .lct-operation-name{display:block;font-size:16px}
4714
4821
  .lct-operation-head details,.lct-session details{font-size:11px;color:var(--cp-text-muted)}
4715
4822
  .lct-session small,.session-btn small{display:block;color:var(--cp-text-muted)}
@@ -4743,7 +4850,7 @@ var CONSOLE_REFRESH_JS = `
4743
4850
  if(old.getAttribute('data-label-fallback')!==next.textContent){old.setAttribute('data-label-fallback',next.textContent)}
4744
4851
  return;
4745
4852
  }
4746
- if(old.matches('input,textarea,select,.term,.pty,.graph-drill,[data-config-mount]')){return}
4853
+ if(old.matches('input,textarea,select,.term,.pty,.graph-drill,[data-config-mount],[data-session-conversation]')){return}
4747
4854
  Array.from(old.attributes).forEach(function(a){
4748
4855
  if(a.name!=='open'&&a.name!=='hidden'&&a.name!=='aria-selected'&&!next.hasAttribute(a.name)){old.removeAttribute(a.name)}
4749
4856
  });
@@ -4957,6 +5064,8 @@ svg.graph{display:block}
4957
5064
  .graph-drill .drilllog .tl.error{color:#f85149}
4958
5065
  `;
4959
5066
  var CONSOLE_CLIENT_JS = `
5067
+ ${LABELS_CLIENT_JS}
5068
+ ${CONVERSATION_CLIENT_JS}
4960
5069
  (function(){
4961
5070
  var detailMsg=${JSON.stringify(consoleDetailMessages)};
4962
5071
  var terminalCollectors=[];
@@ -5011,6 +5120,7 @@ var CONSOLE_CLIENT_JS = `
5011
5120
  if(show){matched=true;if(!screen.hidden){connectTerminal(cards[j],id)}}
5012
5121
  }
5013
5122
  updateTerminalLeases();
5123
+ document.dispatchEvent(new CustomEvent('console-conversation-selection'));
5014
5124
  return matched;
5015
5125
  }
5016
5126
  if(!screen.__logsWired){
@@ -5071,6 +5181,7 @@ var CONSOLE_CLIENT_JS = `
5071
5181
  if(name==='config'&&activeScreen){loadConfig(activeScreen)}
5072
5182
  if(name==='logs'&&activeScreen){wireLogsScreen(activeScreen)}
5073
5183
  updateTerminalLeases();
5184
+ document.dispatchEvent(new CustomEvent('console-conversation-selection'));
5074
5185
  try{localStorage.setItem('openteam.screen',name)}catch(e){}
5075
5186
  document.body.classList.remove('nav-open');
5076
5187
  return true;
@@ -5472,8 +5583,6 @@ var LIFECYCLE_THEME_BOOT = `
5472
5583
  })();
5473
5584
  `;
5474
5585
  var LIFECYCLE_CLIENT_JS = `
5475
- ${LABELS_CLIENT_JS}
5476
- ${CONVERSATION_CLIENT_JS}
5477
5586
  function wireLifecycleTimeline(){
5478
5587
  var mount=document.getElementById('lifecycle-timeline-mount');
5479
5588
  if(!mount){return}
@@ -5520,7 +5629,7 @@ function wireLifecycleTimeline(){
5520
5629
  treeMountEl.setAttribute('inert','');
5521
5630
  treeMountEl.querySelectorAll('[data-inspect-attempt],[data-inspect-root]').forEach(function(button){button.disabled=true});
5522
5631
  }
5523
- document.dispatchEvent(new CustomEvent('conversation-selection'));
5632
+ document.dispatchEvent(new CustomEvent('conversation-selection',{detail:{scope:'native'}}));
5524
5633
  }
5525
5634
  function commitConversation(run,cursor){
5526
5635
  cursor=cursor===null?null:String(cursor);
@@ -5982,6 +6091,7 @@ function wireLifecycleTimeline(){
5982
6091
  inspect.setAttribute('aria-expanded','false');
5983
6092
  inspect.setAttribute('aria-controls','lc-attempt-conversation');
5984
6093
  inspect.setAttribute('data-inspect-attempt',attempt.attemptID);
6094
+ inspect.hidden=true;
5985
6095
  inspect.setAttribute('data-inspect-operation',attempt.operationID);
5986
6096
  session.appendChild(inspect);
5987
6097
  if(attempt.parentSessionID){
@@ -6134,7 +6244,7 @@ function wireLifecycleTimeline(){
6134
6244
  var time=makeElement('small','',${JSON.stringify(consoleDetailMessages.timeUnknown)});time.setAttribute('data-session-time',rootID);rootSession.appendChild(time);
6135
6245
  var id=makeElement('code','',rootID);rootSession.appendChild(id);
6136
6246
  }else{rootSession.appendChild(makeElement('span','',${JSON.stringify(consoleDetailMessages.rootUnlinked)}))}
6137
- var inspect=makeElement('button','',message('inspect'));inspect.type='button';inspect.setAttribute('data-inspect-root','');inspect.setAttribute('data-inspect-operation',op.operationID);inspect.setAttribute('aria-expanded','false');inspect.setAttribute('aria-controls','lc-attempt-conversation');rootSession.appendChild(inspect);
6247
+ var inspect=makeElement('button','',message('inspect'));inspect.type='button';inspect.hidden=true;inspect.setAttribute('data-inspect-root','');inspect.setAttribute('data-inspect-operation',op.operationID);rootSession.appendChild(inspect);
6138
6248
  card.insertBefore(rootSession,card.querySelector('.lct-attempts'));
6139
6249
  }
6140
6250
  var children=childrenByParent.get(op.operationID)||[];
@@ -6746,6 +6856,7 @@ function sessionDetailCard(tab, terminalEnabled, ptyEnabled, visible) {
6746
6856
  const idSuffix = domIdPart(tab.sessionID);
6747
6857
  const body = [
6748
6858
  sessionHeaderPanel(tab),
6859
+ `<div data-session-conversation="${escapeHtml(tab.sessionID)}"></div>`,
6749
6860
  terminalEnabled ? terminalPanel(tab) : "",
6750
6861
  terminalEnabled && ptyEnabled ? ptyPanel(tab) : "",
6751
6862
  routesPanel(tab.recentRoutes, idSuffix),
@@ -7131,7 +7242,7 @@ function lcSessionHtml(attempt, role) {
7131
7242
  sessionNameHtml(attempt.sessionID, role),
7132
7243
  `<small data-session-time="${escapeHtml(attempt.sessionID)}">${escapeHtml(consoleDetailMessages.timeUnknown)}</small>`,
7133
7244
  `<details><summary>${escapeHtml(consoleDetailMessages.technicalID)}</summary><code>${escapeHtml(attempt.sessionID)}</code></details>`,
7134
- `<button type="button" aria-expanded="false" aria-controls="lc-attempt-conversation" data-inspect-operation="${escapeHtml(attempt.operationID)}" data-inspect-attempt="${escapeHtml(attempt.attemptID)}">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
7245
+ `<button type="button" hidden data-inspect-operation="${escapeHtml(attempt.operationID)}" data-inspect-attempt="${escapeHtml(attempt.attemptID)}">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
7135
7246
  attempt.parentSessionID === undefined ? "" : `<span class="lct-parent-session">${escapeHtml(lifecycleMessages.parentSessionLabel)} <code>${escapeHtml(attempt.parentSessionID)}</code></span>`,
7136
7247
  "</div>"
7137
7248
  ].join("");
@@ -7218,7 +7329,7 @@ function lcOperationHtml(operation, operationIDs, childrenHtml, depth, rootSessi
7218
7329
  isRoot && operation.kind === "coordinator" ? [
7219
7330
  `<div class="lct-session" data-root-session-id="${escapeHtml(rootSessionID ?? "")}">`,
7220
7331
  rootSessionID ? `${sessionNameHtml(rootSessionID, consoleDetailMessages.orchestrator)} <small data-session-time="${escapeHtml(rootSessionID)}">${escapeHtml(consoleDetailMessages.timeUnknown)}</small><details><summary>${escapeHtml(consoleDetailMessages.technicalID)}</summary><code>${escapeHtml(rootSessionID)}</code></details>` : `<span>${escapeHtml(consoleDetailMessages.rootUnlinked)}</span>`,
7221
- `<button type="button" data-inspect-root data-inspect-operation="${escapeHtml(operation.operationID)}" aria-expanded="false" aria-controls="lc-attempt-conversation">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
7332
+ `<button type="button" hidden data-inspect-root data-inspect-operation="${escapeHtml(operation.operationID)}">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
7222
7333
  "</div>"
7223
7334
  ].join("") : "",
7224
7335
  `<section class="lct-attempts" aria-label="${escapeHtml(lifecycleMessages.attemptsLabel)}">${attempts}</section>`,
@@ -7597,11 +7708,11 @@ function renderConsoleHtml(state, options = {}) {
7597
7708
  const body = [
7598
7709
  screenSection("tree", treeCopy.label, treeCopy.heading, treeCopy.description, treeBody, true),
7599
7710
  screenSection("routing", "Routing and telemetry", "Routing + telemetry", "Aggregate routing and cost telemetry from <code>GET /api/state</code> across all sessions.", routingScreenBody(state)),
7600
- screenSection("logs", "Logs and session activity", "Logs / activity", "Per-session activity. Prompts are referenced by hash only and never rendered in clear text.", `<div data-log-controls><label>${escapeHtml(consoleDetailMessages.filter)} <input type="search" data-log-filter></label> <label><input type="checkbox" data-log-follow> ${escapeHtml(consoleDetailMessages.follow)}</label> <button type="button" data-log-incoming hidden>${escapeHtml(consoleDetailMessages.incoming)}</button></div><div data-log-content>${logsScreenBody(state, terminalEnabled, ptyEnabled)}</div>`),
7711
+ screenSection("logs", "Logs and session activity", "Logs / activity", consoleDetailMessages.logsDescription, `<div data-log-controls><label>${escapeHtml(consoleDetailMessages.filter)} <input type="search" data-log-filter></label> <label><input type="checkbox" data-log-follow> ${escapeHtml(consoleDetailMessages.follow)}</label> <button type="button" data-log-incoming hidden>${escapeHtml(consoleDetailMessages.incoming)}</button></div><div data-log-content>${logsScreenBody(state, terminalEnabled, ptyEnabled)}</div>`),
7601
7712
  screenSection("config", "Editable configuration", 'Configuration <span class="pill warn">editable</span>', "Edit the whole openteam JSON configuration. Roles are editable inside the JSON when present; unsupported surfaces are not shown.", configScreenBody(options.configEditor?.hasToken === true))
7602
7713
  ].join("");
7603
7714
  const session = state.session.id === undefined ? "" : ` · session ${escapeHtml(state.session.id)}`;
7604
- const tokenAttr = (terminalEnabled || graphDrilldown || configEnabled || lifecycleEnabled) && options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}"${terminalEnabled ? ' data-terminal="1"' : ""}${ptyEnabled ? ' data-pty="1"' : ""}${graphDrilldown ? ' data-drilldown="1"' : ""}${configEnabled ? ' data-config="1"' : ""}` : "";
7715
+ const tokenAttr = options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}"${terminalEnabled ? ' data-terminal="1"' : ""}${terminalEnabled && ptyEnabled ? ' data-pty="1"' : ""}${graphDrilldown ? ' data-drilldown="1"' : ""}${configEnabled ? ' data-config="1"' : ""}` : "";
7605
7716
  return [
7606
7717
  "<!doctype html>",
7607
7718
  `<html lang="en" data-generated="${escapeHtml(state.generatedAt)}" data-refresh="${refreshMs}"${tokenAttr}>`,
@@ -7610,7 +7721,7 @@ function renderConsoleHtml(state, options = {}) {
7610
7721
  '<meta name="viewport" content="width=device-width,initial-scale=1">',
7611
7722
  "<title>openteam console</title>",
7612
7723
  lifecycleEnabled ? `<script>${LIFECYCLE_THEME_BOOT}</script>` : "",
7613
- `<style>${CONSOLE_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE + CONVERSATION_STYLE : ""}</style>`,
7724
+ `<style>${CONSOLE_STYLE}${CONVERSATION_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE : ""}</style>`,
7614
7725
  "</head>",
7615
7726
  "<body>",
7616
7727
  '<div class="app">',
@@ -8387,7 +8498,8 @@ async function untilAborted(read, signal) {
8387
8498
  }
8388
8499
  async function handleConversationRoute(request, response, url, deps) {
8389
8500
  const labelsOnly = url.pathname === "/api/graph/session-labels";
8390
- if (url.pathname !== "/api/graph/conversation" && !labelsOnly)
8501
+ const direct = url.pathname === "/api/console/conversation";
8502
+ if (url.pathname !== "/api/graph/conversation" && !labelsOnly && !direct)
8391
8503
  return false;
8392
8504
  const send = (status, kind, message, extra = {}) => {
8393
8505
  response.writeHead(status, {
@@ -8407,11 +8519,60 @@ async function handleConversationRoute(request, response, url, deps) {
8407
8519
  send(405, "unavailable", consoleDetailMessages.unauthorized);
8408
8520
  return true;
8409
8521
  }
8410
- if (!deps.enabled || !deps.contentEnabled || url.searchParams.get("diagnostic") !== "1") {
8522
+ if (!direct && !deps.enabled || !deps.contentEnabled) {
8411
8523
  send(403, "disabled", consoleDetailMessages.disabled);
8412
8524
  return true;
8413
8525
  }
8414
8526
  const params = url.searchParams;
8527
+ if (direct) {
8528
+ const sessionID = params.get("session");
8529
+ if (!sessionID || sessionID.length > 256 || params.getAll("session").length !== 1 || ["run", "operation", "attempt", "target"].some((key) => params.has(key))) {
8530
+ send(400, "invalid", consoleDetailMessages.invalid);
8531
+ return true;
8532
+ }
8533
+ if (params.has("cursor")) {
8534
+ send(409, "historical", consoleDetailMessages.historical);
8535
+ return true;
8536
+ }
8537
+ const controller2 = new AbortController;
8538
+ const cancel2 = () => controller2.abort();
8539
+ response.on("close", cancel2);
8540
+ request.on("aborted", cancel2);
8541
+ const timeout2 = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
8542
+ try {
8543
+ const observed = await untilAborted(deps.observedSessionIDs?.() ?? Promise.resolve([]), controller2.signal);
8544
+ if (!observed.includes(sessionID)) {
8545
+ send(404, "invalid", consoleDetailMessages.invalid);
8546
+ return true;
8547
+ }
8548
+ const endpoint = await untilAborted(deps.resolveEndpoint(sessionID), controller2.signal);
8549
+ if (!endpoint) {
8550
+ send(503, "unavailable", consoleDetailMessages.unavailable);
8551
+ return true;
8552
+ }
8553
+ const conversation = await untilAborted(deps.readConversation(endpoint, sessionID, controller2.signal), controller2.signal);
8554
+ send(200, conversation.messages.length || conversation.truncated ? "conversation" : "empty", `${consoleDetailMessages.current}${conversation.truncated ? `
8555
+ ${consoleDetailMessages.truncated}` : conversation.messages.length ? "" : `
8556
+ ${consoleDetailMessages.empty}`}`, {
8557
+ scope: "present-day-session",
8558
+ target: "session",
8559
+ sessionID,
8560
+ ...conversation
8561
+ });
8562
+ } catch (error) {
8563
+ if (!response.destroyed)
8564
+ send(error instanceof ConversationTooLargeError ? 413 : 503, error instanceof ConversationTooLargeError ? "oversized" : "unavailable", error instanceof ConversationTooLargeError ? consoleDetailMessages.oversized : consoleDetailMessages.unavailable);
8565
+ } finally {
8566
+ clearTimeout(timeout2);
8567
+ response.off("close", cancel2);
8568
+ request.off("aborted", cancel2);
8569
+ }
8570
+ return true;
8571
+ }
8572
+ if (!deps.reader) {
8573
+ send(503, "unavailable", consoleDetailMessages.unavailable);
8574
+ return true;
8575
+ }
8415
8576
  const rootTarget = params.get("target") === "root";
8416
8577
  if ((labelsOnly ? params.has("run") ? ["run"] : [] : rootTarget ? ["run", "operation"] : ["run", "operation", "attempt"]).some((key) => params.getAll(key).length !== 1 || !params.get(key)) || params.has("session") || params.getAll("target").length > 1 || params.has("target") && !rootTarget && params.get("target") !== "attempt" || rootTarget && params.has("attempt") || labelsOnly && (params.has("operation") || params.has("attempt") || params.has("target"))) {
8417
8578
  send(400, "invalid", consoleDetailMessages.invalid);
@@ -8435,7 +8596,7 @@ async function handleConversationRoute(request, response, url, deps) {
8435
8596
  if (request.aborted || request.socket.destroyed)
8436
8597
  cancel2();
8437
8598
  const deadline = Date.now() + SESSION_HISTORY_LIMITS.timeoutMs;
8438
- const timeout = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
8599
+ const timeout2 = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
8439
8600
  try {
8440
8601
  const history2 = selected?.kind === "view" ? selected : undefined;
8441
8602
  const selectedRoot = history2?.runs.find((run) => run.runID === history2.selection.runID)?.rootSessionID;
@@ -8501,7 +8662,7 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
8501
8662
  if (!response.destroyed && !request.socket.destroyed)
8502
8663
  send(503, "unavailable", consoleDetailMessages.unavailable);
8503
8664
  } finally {
8504
- clearTimeout(timeout);
8665
+ clearTimeout(timeout2);
8505
8666
  response.off("close", cancel2);
8506
8667
  request.off("aborted", cancel2);
8507
8668
  request.socket.off("close", cancel2);
@@ -8526,21 +8687,23 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
8526
8687
  const controller = new AbortController;
8527
8688
  const cancel = () => controller.abort();
8528
8689
  response.on("close", cancel);
8690
+ request.on("aborted", cancel);
8691
+ const timeout = setTimeout(cancel, SESSION_HISTORY_LIMITS.timeoutMs);
8529
8692
  try {
8530
8693
  const run = history.runs.find((item) => item.runID === history.selection.runID);
8531
8694
  const recordedRoot = run?.rootSessionID;
8532
8695
  const sessionID = rootTarget ? recordedRoot : attempt?.sessionID;
8533
8696
  const endpointID = sessionID ?? history.projection.operations.flatMap((op) => op.attempts).filter((item) => item.sessionID).at(-1)?.sessionID ?? "";
8534
- const endpoint = await deps.resolveEndpoint(endpointID);
8697
+ const endpoint = await untilAborted(deps.resolveEndpoint(endpointID), controller.signal);
8535
8698
  if (!endpoint) {
8536
8699
  send(503, "unavailable", consoleDetailMessages.unavailable);
8537
8700
  return true;
8538
8701
  }
8539
- const recovered = !sessionID && rootTarget && deps.readConversation.root ? await deps.readConversation.root(endpoint, history.projection, controller.signal) : undefined;
8702
+ const recovered = !sessionID && rootTarget && deps.readConversation.root ? await untilAborted(deps.readConversation.root(endpoint, history.projection, controller.signal), controller.signal) : undefined;
8540
8703
  const resolvedID = sessionID ?? recovered?.session.id;
8541
8704
  if (!resolvedID)
8542
8705
  throw new AssociationError("unavailable");
8543
- const conversation = recovered ?? await deps.readConversation(endpoint, resolvedID, controller.signal);
8706
+ const conversation = recovered ?? await untilAborted(deps.readConversation(endpoint, resolvedID, controller.signal), controller.signal);
8544
8707
  const current = rootTarget ? `${consoleDetailMessages.rootCurrent}
8545
8708
  ${consoleDetailMessages.current}${recovered ? `
8546
8709
  ${consoleDetailMessages.recovered}` : ""}` : consoleDetailMessages.current;
@@ -8556,10 +8719,12 @@ ${consoleDetailMessages.empty}`, {
8556
8719
  ...conversation
8557
8720
  });
8558
8721
  } catch (error) {
8559
- if (!controller.signal.aborted)
8722
+ if (!response.destroyed)
8560
8723
  send(error instanceof ConversationTooLargeError ? 413 : 503, error instanceof ConversationTooLargeError ? "oversized" : "unavailable", error instanceof AssociationError ? error.message : error instanceof ConversationTooLargeError ? consoleDetailMessages.oversized : consoleDetailMessages.unavailable);
8561
8724
  } finally {
8725
+ clearTimeout(timeout);
8562
8726
  response.off("close", cancel);
8727
+ request.off("aborted", cancel);
8563
8728
  }
8564
8729
  return true;
8565
8730
  }
@@ -8839,15 +9004,19 @@ async function createConsoleServer(deps) {
8839
9004
  };
8840
9005
  const handle = async (request, response) => {
8841
9006
  const url2 = new URL(request.url ?? "/", "http://localhost");
8842
- if (["/api/graph/conversation", "/api/graph/session-labels"].includes(url2.pathname) && deps.storageApi && !readAuthorized2(request, url2, deps.storageApi.token)) {
9007
+ if ([
9008
+ "/api/graph/conversation",
9009
+ "/api/graph/session-labels",
9010
+ "/api/console/conversation"
9011
+ ].includes(url2.pathname) && deps.storageApi && !readAuthorized2(request, url2, deps.storageApi.token)) {
8843
9012
  response.writeHead(401, { ...JSON_HEADERS5, "cache-control": "no-store" }).end(JSON.stringify({ version: 1, kind: "unauthorized" }));
8844
9013
  return;
8845
9014
  }
8846
- if (deps.console?.readConversation && deps.lifecycleHistoryApi && await handleConversationRoute(request, response, url2, {
9015
+ if (deps.console?.readConversation && await handleConversationRoute(request, response, url2, {
8847
9016
  token: deps.console.token,
8848
9017
  enabled: deps.config.graphView?.enabled === true,
8849
- contentEnabled: deps.console.conversationContentEnabled === true,
8850
- reader: deps.lifecycleHistoryApi.reader,
9018
+ contentEnabled: deps.console.conversationContentEnabled ?? deps.config.sessionHistory?.contentEnabled ?? true,
9019
+ ...deps.lifecycleHistoryApi ? { reader: deps.lifecycleHistoryApi.reader } : {},
8851
9020
  resolveEndpoint: async (id) => deps.console?.historyEndpoint ?? await resolveEndpoint(id),
8852
9021
  observedSessionIDs: async () => buildConsoleState(await deps.readSnapshot()).sessions.map((session) => session.sessionID),
8853
9022
  readConversation: deps.console.readConversation
@@ -11150,7 +11319,7 @@ async function runConsoleServe(deps, options) {
11150
11319
  ptyEnabled,
11151
11320
  createClient: makeClient,
11152
11321
  drilldownEnabled: graphViewEnabled,
11153
- conversationContentEnabled: config.console.sessionHistory?.contentEnabled === true,
11322
+ conversationContentEnabled: config.console.sessionHistory?.contentEnabled !== false,
11154
11323
  ...config.console.sessionHistory?.endpoint !== undefined ? { historyEndpoint: config.console.sessionHistory.endpoint } : {},
11155
11324
  readConversation: createConversationReader({
11156
11325
  directory: deps.directory ?? process.cwd(),
@@ -18046,7 +18215,7 @@ import { posix as pathPosix, win32 as pathWin32 } from "node:path";
18046
18215
  // package.json
18047
18216
  var package_default = {
18048
18217
  name: "@jmanuelcorral/openteam",
18049
- version: "0.24.1",
18218
+ version: "0.25.0",
18050
18219
  packageManager: "bun@1.3.14",
18051
18220
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
18052
18221
  license: "MIT",