@jmanuelcorral/openteam 0.24.0 → 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.
Files changed (58) hide show
  1. package/README.es.md +20 -5
  2. package/README.md +14 -2
  3. package/dist/certificates/graph-release-certificate.json +2 -2
  4. package/dist/certificates/graph-shadow-certificate.json +2 -2
  5. package/dist/cli/setupAdapters.d.ts +2 -2
  6. package/dist/cli/setupAdapters.d.ts.map +1 -1
  7. package/dist/cli.js +1251 -554
  8. package/dist/commands/agents.d.ts +6 -4
  9. package/dist/commands/agents.d.ts.map +1 -1
  10. package/dist/commands/dispatch.d.ts +1 -1
  11. package/dist/commands/dispatch.d.ts.map +1 -1
  12. package/dist/commands/setup.d.ts +22 -14
  13. package/dist/commands/setup.d.ts.map +1 -1
  14. package/dist/console/assets.d.ts.map +1 -1
  15. package/dist/console/conversation.d.ts +5 -1
  16. package/dist/console/conversation.d.ts.map +1 -1
  17. package/dist/console/credentials.d.ts +4 -0
  18. package/dist/console/credentials.d.ts.map +1 -0
  19. package/dist/console/inspector.d.ts +1 -1
  20. package/dist/console/inspector.d.ts.map +1 -1
  21. package/dist/console/refresh.d.ts +1 -1
  22. package/dist/console/refresh.d.ts.map +1 -1
  23. package/dist/console/render.d.ts.map +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +999 -287
  26. package/dist/local/lemonade.d.ts.map +1 -1
  27. package/dist/local/lemonadeRequestGuard.d.ts +11 -0
  28. package/dist/local/lemonadeRequestGuard.d.ts.map +1 -0
  29. package/dist/local/lemonadeResidency.d.ts +22 -0
  30. package/dist/local/lemonadeResidency.d.ts.map +1 -0
  31. package/dist/local/modelResidency.d.ts +4 -0
  32. package/dist/local/modelResidency.d.ts.map +1 -0
  33. package/dist/local/slotPool.d.ts +13 -7
  34. package/dist/local/slotPool.d.ts.map +1 -1
  35. package/dist/messages/consoleDetail.d.ts +8 -5
  36. package/dist/messages/consoleDetail.d.ts.map +1 -1
  37. package/dist/messages/executionSetup.d.ts +19 -0
  38. package/dist/messages/executionSetup.d.ts.map +1 -1
  39. package/dist/messages/modelResidency.d.ts +19 -0
  40. package/dist/messages/modelResidency.d.ts.map +1 -0
  41. package/dist/orchestrator/coordinator.d.ts.map +1 -1
  42. package/dist/orchestrator/runtimeSelection.d.ts +4 -3
  43. package/dist/orchestrator/runtimeSelection.d.ts.map +1 -1
  44. package/dist/plugin/availability.d.ts +4 -1
  45. package/dist/plugin/availability.d.ts.map +1 -1
  46. package/dist/plugin/hooks.d.ts +2 -0
  47. package/dist/plugin/hooks.d.ts.map +1 -1
  48. package/dist/plugin/memoryTool.d.ts +9 -3
  49. package/dist/plugin/memoryTool.d.ts.map +1 -1
  50. package/dist/plugin/modelResidency.d.ts +47 -0
  51. package/dist/plugin/modelResidency.d.ts.map +1 -0
  52. package/dist/router/types.d.ts +2 -0
  53. package/dist/router/types.d.ts.map +1 -1
  54. package/dist/web/conversationRoute.d.ts +2 -2
  55. package/dist/web/conversationRoute.d.ts.map +1 -1
  56. package/dist/web/server.d.ts +1 -1
  57. package/dist/web/server.d.ts.map +1 -1
  58. package/package.json +1 -1
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
  };
@@ -3356,6 +3411,25 @@ var executionSetupMessages = {
3356
3411
  `),
3357
3412
  localOnlyTitle: "Local execution",
3358
3413
  localRequiredError: "local execution requires an enabled local runtime",
3414
+ lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
3415
+ lemonadeNoLoadedModels: "Lemonade has no text models verified as loaded and ready. Keep the intended model active in Lemonade and rerun setup; inactive or unknown models cannot be selected or entered manually.",
3416
+ lemonadeModelNotLoaded: (model) => `Lemonade model "${model}" is not positively verified as loaded and ready. Keep that exact text model active and rerun setup; no substitute was selected and no implicit loading is allowed.`,
3417
+ lemonadeProbeRequired: "Lemonade setup requires a fresh read-only residency probe before writing configuration. Use the CLI with its runtime probe enabled; manual model IDs cannot bypass this requirement.",
3418
+ lemonadeProbeFailed: "Lemonade residency could not be verified at the configured endpoint. Check connectivity and the read-only /models, /health, and /internal/aliases responses, then rerun setup with the intended text models already active.",
3419
+ lemonadeEndpointMismatch: "Lemonade endpoint/provider mapping differs from the selected runtime. Reconcile the configured base URL and provider before rerunning setup; no model was substituted.",
3420
+ lemonadeModelIdentityUnknown: (model) => `Lemonade model "${model}" has no usable registered wire identity in the effective opencode configuration. Fix its model declaration before rerunning setup; setup will not replace or guess an alias.`,
3421
+ lemonadeEffectiveConfigLabel: "effective setup configuration",
3422
+ lemonadeEffectiveConfigAmbiguous: "Cannot establish one effective openteam configuration from the configured plugin loaders. Reconcile their options before rerunning setup; no provider identity was guessed.",
3423
+ lemonadeRetainedProviderUnprotected: (providerID) => `Cannot safely keep retained Lemonade provider "${providerID}": the effective configuration must retain one matching Lemonade runtime, disabled when deselected. Reconcile the plugin options or machine overlay, or explicitly remove the provider registration before rerunning setup; no configuration was written.`,
3424
+ lemonadeV2InvalidProvider: (providerID) => `Lemonade provider ${JSON.stringify(providerID)} cannot be represented as an exact native V2 policy resource. Choose a non-empty provider ID without wildcards, slashes, backslashes, or whitespace; setup will not broaden a deny rule.`,
3425
+ lemonadeV2InvalidPolicies: "Cannot safely preserve experimental.policies: experimental must be an object and policies must be an array of provider.use statements with a string resource and allow/deny effect. Fix the project configuration before rerunning setup.",
3426
+ lemonadeV2Title: "Lemonade transport compatibility",
3427
+ lemonadeV2Restriction: (providers) => `Project policies disable native V2 Lemonade access for: ${providers.join(", ")}. Ordinary guarded V1 configuration remains supported; V2 is not an openteam-hook execution path. Higher-precedence or global policies can override this project restriction; it is not a server-atomic no-autoload guarantee.`,
3428
+ lemonadeInventory: (instanceID, providerID, models) => {
3429
+ const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
3430
+ return `Lemonade inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
3431
+ },
3432
+ lemonadeAgentResidency: (agent, model, state) => `Lemonade readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
3359
3433
  mixedPrimaryQuestion: "Primary coordinator model",
3360
3434
  mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
3361
3435
  mixedPrimaryFrontier: "Choose a frontier model",
@@ -4463,234 +4537,286 @@ async function handleLifecycleHistoryRoute(request, response, url, deps) {
4463
4537
  // src/console/inspector.ts
4464
4538
  var CONVERSATION_CLIENT_JS = `
4465
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;
4466
4543
  var mount=document.getElementById('lifecycle-timeline-mount');
4467
- if(!mount){return}
4468
- var msg=${JSON.stringify(consoleDetailMessages)};
4469
- var limits=${JSON.stringify(CONVERSATION_LIMITS)};
4470
- var panel=document.createElement('section');panel.className='lc-conversation';panel.hidden=true;panel.id='lc-attempt-conversation';
4471
- panel.setAttribute('data-conversation-inspector','');
4472
- panel.setAttribute('aria-labelledby','lc-conversation-heading');
4473
- mount.appendChild(panel);
4474
- var selection=null,request=null,epoch=0,retained=null;
4475
- function element(tag,text){var e=document.createElement(tag);if(text!==undefined){e.textContent=text}return e}
4476
- function selectedButton(){
4477
- if(!selection||mount.getAttribute('data-conversation-transition')==='1'||
4478
- mount.getAttribute('data-lifecycle-run')!==selection.run||
4479
- (mount.getAttribute('data-conversation-cursor')||null)!==selection.cursor){return null}
4480
- return Array.from(mount.querySelectorAll('[data-inspect-attempt],[data-inspect-root]')).find(function(button){
4481
- var attempt=button.closest('[data-lifecycle-attempt-id]');
4482
- var operation=button.closest('[data-lifecycle-operation-id]');
4483
- var session=button.closest(selection.target==='root'?'[data-root-session-id]':'[data-lifecycle-session-id]');
4484
- return !button.disabled&&operation&&session&&
4485
- (selection.target==='root'?button.hasAttribute('data-inspect-root'):attempt&&attempt.getAttribute('data-lifecycle-attempt-id')===selection.attempt&&
4486
- button.getAttribute('data-inspect-attempt')===selection.attempt)&&
4487
- operation.getAttribute('data-lifecycle-operation-id')===selection.operation&&
4488
- button.getAttribute('data-inspect-operation')===selection.operation&&
4489
- session.getAttribute(selection.target==='root'?'data-root-session-id':'data-lifecycle-session-id')===selection.session;
4490
- })||null;
4491
- }
4492
- function expanded(button,open){
4493
- if(button.getAttribute('aria-expanded')!==String(open)){button.setAttribute('aria-expanded',String(open))}
4494
- var label=open?msg.close:msg.inspectConversation;
4495
- if(button.textContent!==label){button.textContent=label}
4496
- }
4497
- function cancel(){
4498
- epoch++;if(request){request.abort();request=null}
4499
- }
4500
- function close(focus){
4501
- var button=selectedButton();
4502
- cancel();selection=null;retained=null;
4503
- mount.querySelectorAll('[data-inspect-attempt],[data-inspect-root]').forEach(function(control){expanded(control,false)});
4504
- panel.textContent='';panel.hidden=true;
4505
- ['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)});
4506
- mount.appendChild(panel);
4507
- if(focus===true&&button){button.focus({preventScroll:true})}
4508
- }
4509
- // The renderer replaces metadata, not retained content. Restore only after
4510
- // the real run/cursor commits and the same attempt/session is still visible.
4511
- function retain(){
4512
- if(!selection||panel.hidden||!selectedButton()){return}
4513
- var scrolls=[],ancestor=panel.parentElement;
4514
- while(ancestor){scrolls.push([ancestor,ancestor.scrollTop,ancestor.scrollLeft]);ancestor=ancestor.parentElement}
4515
- panel.querySelectorAll('pre,[data-conversation-tool-list]').forEach(function(node){scrolls.push([node,node.scrollTop,node.scrollLeft])});
4516
- retained={scrolls:scrolls,top:panel.getBoundingClientRect().top,focus:panel.contains(document.activeElement)?document.activeElement:null};
4517
- }
4518
- function reanchor(){
4519
- if(!selection){return}
4520
- var button=selectedButton();
4521
- if(!button){close();return}
4522
- var attempt=selection.target==='root'?button.closest('.lct-operation-card'):button.closest('[data-lifecycle-attempt-id]');
4523
- if(panel.parentElement!==attempt){attempt.appendChild(panel)}
4524
- expanded(button,true);
4525
- if(retained){
4526
- retained.scrolls.forEach(function(item){if(item[0].isConnected){item[0].scrollTop=item[1];item[0].scrollLeft=item[2]}});
4527
- var tree=mount.querySelector('[data-lifecycle-tree-mount]');
4528
- if(tree){tree.scrollTop+=panel.getBoundingClientRect().top-retained.top}
4529
- if(retained.focus){retained.focus.focus({preventScroll:true})}
4530
- 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
+ });
4531
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;
4532
4617
  }
4533
- function reset(){
4534
- panel.textContent='';
4535
- var header=element('header');header.className='lc-conversation-head';
4536
- var heading=element('h3',msg.title);heading.id='lc-conversation-heading';heading.tabIndex=-1;header.appendChild(heading);
4537
- var closeBtn=element('button',msg.close);closeBtn.type='button';
4538
- closeBtn.setAttribute('data-conversation-close','');
4539
- closeBtn.addEventListener('click',function(){close(true)});header.appendChild(closeBtn);panel.appendChild(header);
4540
- }
4541
- function showHeading(){
4542
- var heading=panel.querySelector('h3');
4543
- heading.focus({preventScroll:true});
4544
- heading.scrollIntoView({block:'start',inline:'nearest'});
4545
- }
4546
- function load(reveal){
4547
- if(!selectedButton()){return}
4548
- cancel();var current=epoch,selected=selection;
4549
- reset();
4550
- panel.setAttribute('data-conversation-kind','loading');
4551
- panel.setAttribute('aria-busy','true');
4552
- var status=element('p',msg.loading);status.setAttribute('role','status');panel.appendChild(status);
4553
- var opening=null;
4554
- if(reveal===true){
4555
- showHeading();
4556
- 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]})};
4557
- }
4558
- var controller=new AbortController();request=controller;
4559
- var query=new URLSearchParams({run:selected.run,operation:selected.operation,diagnostic:'1'});
4560
- if(selected.target==='root'){query.set('target','root')}else{query.set('attempt',selected.attempt)}
4561
- query.set('token',document.documentElement.getAttribute('data-token')||'');
4562
- if(selected.cursor!==null){query.set('cursor',selected.cursor)}
4563
- var timeout=setTimeout(function(){controller.abort()},15000);
4564
- fetch('/api/graph/conversation?'+query.toString(),{signal:controller.signal,cache:'no-store'})
4565
- .then(function(r){return r.json()}).then(function(data){
4566
- if(epoch!==current||selection!==selected||!selectedButton()){return}
4567
- var bringIntoView=opening&&document.activeElement===opening.focus&&opening.scrolls.every(function(item){return item[0].scrollTop===item[1]&&item[0].scrollLeft===item[2]});
4568
- if((data.kind==='conversation'||data.kind==='empty')&&(data.runID!==selected.run||data.operationID!==selected.operation||
4569
- (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))||
4570
- (selected.session&&data.sessionID!==selected.session))){
4571
- data={kind:'unavailable',message:msg.unavailable};
4572
- }
4573
- status.textContent=data.message||msg.unavailable;
4574
- panel.setAttribute('data-conversation-kind',data.kind||'unavailable');
4575
- if(data.kind==='conversation'||data.kind==='empty'){
4576
- panel.setAttribute('data-conversation-session',data.sessionID);
4577
- var source=element('p');source.appendChild(element('strong',data.session&&data.session.title|| (selected.target==='root'?msg.orchestrator:msg.session)));
4578
- var created=consoleLabels.utcTime(data.session&&data.session.createdAt);
4579
- source.appendChild(element('small',' · '+(created===msg.timeUnknown?msg.timeUnknown:msg.sessionCreated+': '+created)));
4580
- source.appendChild(element('code',' · '+data.sessionID));panel.appendChild(source);
4581
- 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}}))}
4582
- }
4583
- if(data.kind==='conversation'){
4584
- renderMessages(data.messages||[]);
4585
- }else if(data.kind==='empty'){
4586
- 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);
4587
4681
  }
4588
- // The short loading card may not have enough scroll range. Complete
4589
- // the explicit reveal after layout grows, unless the reader moved on.
4590
- if(bringIntoView){panel.querySelector('h3').scrollIntoView({block:'start',inline:'nearest'})}
4591
- }).catch(function(){
4592
- if(epoch===current){status.textContent=msg.unavailable;panel.setAttribute('data-conversation-kind','unavailable')}
4593
- }).finally(function(){
4594
- clearTimeout(timeout);
4595
- if(epoch!==current){return}
4596
- request=null;
4597
- panel.setAttribute('aria-busy','false');
4598
- var refresh=element('button',msg.refresh);refresh.type='button';refresh.setAttribute('data-conversation-refresh','');
4599
- 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));
4600
4684
  });
4601
- }
4602
- function renderMessages(messages){
4603
- var truncated=messages.length>limits.messages,remaining=limits.totalChars,toolCount=0;
4604
- messages=messages.slice(0,limits.messages);
4605
- var tools=element('details');tools.setAttribute('data-conversation-tools','');tools.open=true;
4606
- var count=messages.reduce(function(total,m){return total+(m.tools||[]).length},0);
4607
- tools.appendChild(element('summary',msg.tools.replace('{count}',String(count))));
4608
- var calls=element('div');calls.setAttribute('data-conversation-tool-list','');tools.appendChild(calls);
4609
- panel.appendChild(tools);
4610
- function text(value){
4611
- value=String(value);
4612
- var size=Math.min(value.length,limits.fieldChars+msg.truncatedMarker.length,remaining);
4613
- remaining-=size;
4614
- if(size<value.length){truncated=true}
4615
- return value.slice(0,size);
4616
- }
4617
- ['user','assistant'].forEach(function(role){
4618
- var section=element('section');section.setAttribute('data-conversation-role',role);
4619
- section.appendChild(element('h4',role==='user'?msg.prompt:msg.assistant));
4620
- var hasText=false;
4621
- messages.filter(function(m){return m.role===role}).forEach(function(m){
4622
- var item=element('article');item.appendChild(element('time',consoleLabels.utcTime(m.createdAt)));
4623
- if((m.text||[]).length>limits.parts){truncated=true}
4624
- (m.text||[]).slice(0,limits.parts).forEach(function(value){
4625
- if(!value){return}
4626
- hasText=true;
4627
- 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}
4628
4692
  });
4629
- if(item.querySelector('pre')){section.appendChild(item)}
4630
- });panel.appendChild(section);
4631
- 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)}
4632
4754
  });
4633
- messages.forEach(function(m){
4634
- if((m.tools||[]).length>limits.parts){truncated=true}
4635
- (m.tools||[]).slice(0,limits.parts).forEach(function(tool){
4636
- if(toolCount++>=limits.tools||remaining<=0){truncated=true;return}
4637
- var call=element('details');call.setAttribute('data-tool-call',tool.id);
4638
- call.appendChild(element('summary',text(tool.name)+' · '+text(tool.status)));
4639
- if(tool.startedAt!==undefined){call.appendChild(element('time',consoleLabels.utcTime(tool.startedAt)))}
4640
- call.appendChild(element('h5',msg.arguments));call.appendChild(element('pre',text(tool.arguments)));
4641
- call.appendChild(element('h5',msg.result));call.appendChild(element('pre',text(tool.result===undefined?msg.pending:tool.result)));
4642
- 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]);
4643
4783
  });
4644
4784
  });
4645
- if(count===0){calls.appendChild(element('p',msg.noTools))}
4646
- if(truncated){panel.appendChild(element('p',msg.truncated))}
4647
- }
4648
- mount.addEventListener('click',function(event){
4649
- var button=event.target.closest('[data-inspect-attempt],[data-inspect-root]');
4650
- if(!button||button.disabled||mount.getAttribute('data-conversation-transition')==='1'){return}
4651
- if(button===selectedButton()){close(true);return}
4652
- close();
4653
- var root=button.hasAttribute('data-inspect-root');
4654
- var session=button.closest(root?'[data-root-session-id]':'[data-lifecycle-session-id]');
4655
- if(!session){return}
4656
- selection={
4657
- run:mount.getAttribute('data-lifecycle-run'),
4658
- operation:button.getAttribute('data-inspect-operation'),
4659
- attempt:button.getAttribute('data-inspect-attempt'),
4660
- target:root?'root':'attempt',
4661
- session:session.getAttribute(root?'data-root-session-id':'data-lifecycle-session-id'),
4662
- cursor:mount.getAttribute('data-conversation-cursor')||null
4663
- };
4664
- if(!selectedButton()){close();return}
4665
- panel.setAttribute('data-conversation-run',selection.run);
4666
- panel.setAttribute('data-conversation-operation',selection.operation);
4667
- if(selection.attempt){panel.setAttribute('data-conversation-attempt',selection.attempt)}
4668
- panel.setAttribute('data-conversation-target',selection.target);
4669
- panel.setAttribute('data-conversation-session',selection.session);
4670
- reanchor();
4671
- panel.hidden=false;
4672
- if(selection.cursor!==null){reset();panel.setAttribute('data-conversation-kind','historical');panel.appendChild(element('p',msg.historical));showHeading()}
4673
- else{load(true)}
4785
+ records.forEach(function(record){if(record.selection.target!=='session'){record.panel.remove()}});
4674
4786
  });
4675
- document.addEventListener('conversation-selection',close);
4676
- document.addEventListener('conversation-tree-replacing',retain);
4677
- document.addEventListener('conversation-tree-committed',reanchor);
4678
- 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();
4679
4808
  }
4680
4809
  `;
4681
4810
  var CONVERSATION_STYLE = `
4682
- .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}
4683
4812
  .lc-conversation[hidden]{display:none}
4684
- .lc-conversation-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}
4685
4813
  .lc-conversation h3,.lc-conversation h4,.lc-conversation p{margin:8px 0}
4686
- .lc-conversation h3{scroll-margin-top:12px}
4687
- .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}
4688
- .lc-conversation [data-conversation-role="user"] pre{max-height:180px}
4689
- .lc-conversation [data-conversation-tool-list]{max-height:160px;overflow:auto}
4690
- .lc-conversation details{margin-top:12px;padding:8px;border:1px solid var(--cp-border);border-radius:.625rem}
4691
- .lc-conversation summary{cursor:pointer;color:var(--cp-accent)}
4692
- .lc-conversation time{font-size:11px;color:var(--cp-text-muted)}
4693
- .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))}
4694
4820
  .lct-operation-name{display:block;font-size:16px}
4695
4821
  .lct-operation-head details,.lct-session details{font-size:11px;color:var(--cp-text-muted)}
4696
4822
  .lct-session small,.session-btn small{display:block;color:var(--cp-text-muted)}
@@ -4724,7 +4850,7 @@ var CONSOLE_REFRESH_JS = `
4724
4850
  if(old.getAttribute('data-label-fallback')!==next.textContent){old.setAttribute('data-label-fallback',next.textContent)}
4725
4851
  return;
4726
4852
  }
4727
- 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}
4728
4854
  Array.from(old.attributes).forEach(function(a){
4729
4855
  if(a.name!=='open'&&a.name!=='hidden'&&a.name!=='aria-selected'&&!next.hasAttribute(a.name)){old.removeAttribute(a.name)}
4730
4856
  });
@@ -4938,6 +5064,8 @@ svg.graph{display:block}
4938
5064
  .graph-drill .drilllog .tl.error{color:#f85149}
4939
5065
  `;
4940
5066
  var CONSOLE_CLIENT_JS = `
5067
+ ${LABELS_CLIENT_JS}
5068
+ ${CONVERSATION_CLIENT_JS}
4941
5069
  (function(){
4942
5070
  var detailMsg=${JSON.stringify(consoleDetailMessages)};
4943
5071
  var terminalCollectors=[];
@@ -4992,6 +5120,7 @@ var CONSOLE_CLIENT_JS = `
4992
5120
  if(show){matched=true;if(!screen.hidden){connectTerminal(cards[j],id)}}
4993
5121
  }
4994
5122
  updateTerminalLeases();
5123
+ document.dispatchEvent(new CustomEvent('console-conversation-selection'));
4995
5124
  return matched;
4996
5125
  }
4997
5126
  if(!screen.__logsWired){
@@ -5052,6 +5181,7 @@ var CONSOLE_CLIENT_JS = `
5052
5181
  if(name==='config'&&activeScreen){loadConfig(activeScreen)}
5053
5182
  if(name==='logs'&&activeScreen){wireLogsScreen(activeScreen)}
5054
5183
  updateTerminalLeases();
5184
+ document.dispatchEvent(new CustomEvent('console-conversation-selection'));
5055
5185
  try{localStorage.setItem('openteam.screen',name)}catch(e){}
5056
5186
  document.body.classList.remove('nav-open');
5057
5187
  return true;
@@ -5453,8 +5583,6 @@ var LIFECYCLE_THEME_BOOT = `
5453
5583
  })();
5454
5584
  `;
5455
5585
  var LIFECYCLE_CLIENT_JS = `
5456
- ${LABELS_CLIENT_JS}
5457
- ${CONVERSATION_CLIENT_JS}
5458
5586
  function wireLifecycleTimeline(){
5459
5587
  var mount=document.getElementById('lifecycle-timeline-mount');
5460
5588
  if(!mount){return}
@@ -5501,7 +5629,7 @@ function wireLifecycleTimeline(){
5501
5629
  treeMountEl.setAttribute('inert','');
5502
5630
  treeMountEl.querySelectorAll('[data-inspect-attempt],[data-inspect-root]').forEach(function(button){button.disabled=true});
5503
5631
  }
5504
- document.dispatchEvent(new CustomEvent('conversation-selection'));
5632
+ document.dispatchEvent(new CustomEvent('conversation-selection',{detail:{scope:'native'}}));
5505
5633
  }
5506
5634
  function commitConversation(run,cursor){
5507
5635
  cursor=cursor===null?null:String(cursor);
@@ -5963,6 +6091,7 @@ function wireLifecycleTimeline(){
5963
6091
  inspect.setAttribute('aria-expanded','false');
5964
6092
  inspect.setAttribute('aria-controls','lc-attempt-conversation');
5965
6093
  inspect.setAttribute('data-inspect-attempt',attempt.attemptID);
6094
+ inspect.hidden=true;
5966
6095
  inspect.setAttribute('data-inspect-operation',attempt.operationID);
5967
6096
  session.appendChild(inspect);
5968
6097
  if(attempt.parentSessionID){
@@ -6115,7 +6244,7 @@ function wireLifecycleTimeline(){
6115
6244
  var time=makeElement('small','',${JSON.stringify(consoleDetailMessages.timeUnknown)});time.setAttribute('data-session-time',rootID);rootSession.appendChild(time);
6116
6245
  var id=makeElement('code','',rootID);rootSession.appendChild(id);
6117
6246
  }else{rootSession.appendChild(makeElement('span','',${JSON.stringify(consoleDetailMessages.rootUnlinked)}))}
6118
- 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);
6119
6248
  card.insertBefore(rootSession,card.querySelector('.lct-attempts'));
6120
6249
  }
6121
6250
  var children=childrenByParent.get(op.operationID)||[];
@@ -6727,6 +6856,7 @@ function sessionDetailCard(tab, terminalEnabled, ptyEnabled, visible) {
6727
6856
  const idSuffix = domIdPart(tab.sessionID);
6728
6857
  const body = [
6729
6858
  sessionHeaderPanel(tab),
6859
+ `<div data-session-conversation="${escapeHtml(tab.sessionID)}"></div>`,
6730
6860
  terminalEnabled ? terminalPanel(tab) : "",
6731
6861
  terminalEnabled && ptyEnabled ? ptyPanel(tab) : "",
6732
6862
  routesPanel(tab.recentRoutes, idSuffix),
@@ -7112,7 +7242,7 @@ function lcSessionHtml(attempt, role) {
7112
7242
  sessionNameHtml(attempt.sessionID, role),
7113
7243
  `<small data-session-time="${escapeHtml(attempt.sessionID)}">${escapeHtml(consoleDetailMessages.timeUnknown)}</small>`,
7114
7244
  `<details><summary>${escapeHtml(consoleDetailMessages.technicalID)}</summary><code>${escapeHtml(attempt.sessionID)}</code></details>`,
7115
- `<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>`,
7116
7246
  attempt.parentSessionID === undefined ? "" : `<span class="lct-parent-session">${escapeHtml(lifecycleMessages.parentSessionLabel)} <code>${escapeHtml(attempt.parentSessionID)}</code></span>`,
7117
7247
  "</div>"
7118
7248
  ].join("");
@@ -7199,7 +7329,7 @@ function lcOperationHtml(operation, operationIDs, childrenHtml, depth, rootSessi
7199
7329
  isRoot && operation.kind === "coordinator" ? [
7200
7330
  `<div class="lct-session" data-root-session-id="${escapeHtml(rootSessionID ?? "")}">`,
7201
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>`,
7202
- `<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>`,
7203
7333
  "</div>"
7204
7334
  ].join("") : "",
7205
7335
  `<section class="lct-attempts" aria-label="${escapeHtml(lifecycleMessages.attemptsLabel)}">${attempts}</section>`,
@@ -7578,11 +7708,11 @@ function renderConsoleHtml(state, options = {}) {
7578
7708
  const body = [
7579
7709
  screenSection("tree", treeCopy.label, treeCopy.heading, treeCopy.description, treeBody, true),
7580
7710
  screenSection("routing", "Routing and telemetry", "Routing + telemetry", "Aggregate routing and cost telemetry from <code>GET /api/state</code> across all sessions.", routingScreenBody(state)),
7581
- 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>`),
7582
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))
7583
7713
  ].join("");
7584
7714
  const session = state.session.id === undefined ? "" : ` · session ${escapeHtml(state.session.id)}`;
7585
- 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"' : ""}` : "";
7586
7716
  return [
7587
7717
  "<!doctype html>",
7588
7718
  `<html lang="en" data-generated="${escapeHtml(state.generatedAt)}" data-refresh="${refreshMs}"${tokenAttr}>`,
@@ -7591,7 +7721,7 @@ function renderConsoleHtml(state, options = {}) {
7591
7721
  '<meta name="viewport" content="width=device-width,initial-scale=1">',
7592
7722
  "<title>openteam console</title>",
7593
7723
  lifecycleEnabled ? `<script>${LIFECYCLE_THEME_BOOT}</script>` : "",
7594
- `<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>`,
7595
7725
  "</head>",
7596
7726
  "<body>",
7597
7727
  '<div class="app">',
@@ -8368,7 +8498,8 @@ async function untilAborted(read, signal) {
8368
8498
  }
8369
8499
  async function handleConversationRoute(request, response, url, deps) {
8370
8500
  const labelsOnly = url.pathname === "/api/graph/session-labels";
8371
- if (url.pathname !== "/api/graph/conversation" && !labelsOnly)
8501
+ const direct = url.pathname === "/api/console/conversation";
8502
+ if (url.pathname !== "/api/graph/conversation" && !labelsOnly && !direct)
8372
8503
  return false;
8373
8504
  const send = (status, kind, message, extra = {}) => {
8374
8505
  response.writeHead(status, {
@@ -8388,11 +8519,60 @@ async function handleConversationRoute(request, response, url, deps) {
8388
8519
  send(405, "unavailable", consoleDetailMessages.unauthorized);
8389
8520
  return true;
8390
8521
  }
8391
- if (!deps.enabled || !deps.contentEnabled || url.searchParams.get("diagnostic") !== "1") {
8522
+ if (!direct && !deps.enabled || !deps.contentEnabled) {
8392
8523
  send(403, "disabled", consoleDetailMessages.disabled);
8393
8524
  return true;
8394
8525
  }
8395
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
+ }
8396
8576
  const rootTarget = params.get("target") === "root";
8397
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"))) {
8398
8578
  send(400, "invalid", consoleDetailMessages.invalid);
@@ -8416,7 +8596,7 @@ async function handleConversationRoute(request, response, url, deps) {
8416
8596
  if (request.aborted || request.socket.destroyed)
8417
8597
  cancel2();
8418
8598
  const deadline = Date.now() + SESSION_HISTORY_LIMITS.timeoutMs;
8419
- const timeout = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
8599
+ const timeout2 = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
8420
8600
  try {
8421
8601
  const history2 = selected?.kind === "view" ? selected : undefined;
8422
8602
  const selectedRoot = history2?.runs.find((run) => run.runID === history2.selection.runID)?.rootSessionID;
@@ -8482,7 +8662,7 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
8482
8662
  if (!response.destroyed && !request.socket.destroyed)
8483
8663
  send(503, "unavailable", consoleDetailMessages.unavailable);
8484
8664
  } finally {
8485
- clearTimeout(timeout);
8665
+ clearTimeout(timeout2);
8486
8666
  response.off("close", cancel2);
8487
8667
  request.off("aborted", cancel2);
8488
8668
  request.socket.off("close", cancel2);
@@ -8507,21 +8687,23 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
8507
8687
  const controller = new AbortController;
8508
8688
  const cancel = () => controller.abort();
8509
8689
  response.on("close", cancel);
8690
+ request.on("aborted", cancel);
8691
+ const timeout = setTimeout(cancel, SESSION_HISTORY_LIMITS.timeoutMs);
8510
8692
  try {
8511
8693
  const run = history.runs.find((item) => item.runID === history.selection.runID);
8512
8694
  const recordedRoot = run?.rootSessionID;
8513
8695
  const sessionID = rootTarget ? recordedRoot : attempt?.sessionID;
8514
8696
  const endpointID = sessionID ?? history.projection.operations.flatMap((op) => op.attempts).filter((item) => item.sessionID).at(-1)?.sessionID ?? "";
8515
- const endpoint = await deps.resolveEndpoint(endpointID);
8697
+ const endpoint = await untilAborted(deps.resolveEndpoint(endpointID), controller.signal);
8516
8698
  if (!endpoint) {
8517
8699
  send(503, "unavailable", consoleDetailMessages.unavailable);
8518
8700
  return true;
8519
8701
  }
8520
- 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;
8521
8703
  const resolvedID = sessionID ?? recovered?.session.id;
8522
8704
  if (!resolvedID)
8523
8705
  throw new AssociationError("unavailable");
8524
- const conversation = recovered ?? await deps.readConversation(endpoint, resolvedID, controller.signal);
8706
+ const conversation = recovered ?? await untilAborted(deps.readConversation(endpoint, resolvedID, controller.signal), controller.signal);
8525
8707
  const current = rootTarget ? `${consoleDetailMessages.rootCurrent}
8526
8708
  ${consoleDetailMessages.current}${recovered ? `
8527
8709
  ${consoleDetailMessages.recovered}` : ""}` : consoleDetailMessages.current;
@@ -8537,10 +8719,12 @@ ${consoleDetailMessages.empty}`, {
8537
8719
  ...conversation
8538
8720
  });
8539
8721
  } catch (error) {
8540
- if (!controller.signal.aborted)
8722
+ if (!response.destroyed)
8541
8723
  send(error instanceof ConversationTooLargeError ? 413 : 503, error instanceof ConversationTooLargeError ? "oversized" : "unavailable", error instanceof AssociationError ? error.message : error instanceof ConversationTooLargeError ? consoleDetailMessages.oversized : consoleDetailMessages.unavailable);
8542
8724
  } finally {
8725
+ clearTimeout(timeout);
8543
8726
  response.off("close", cancel);
8727
+ request.off("aborted", cancel);
8544
8728
  }
8545
8729
  return true;
8546
8730
  }
@@ -8820,15 +9004,19 @@ async function createConsoleServer(deps) {
8820
9004
  };
8821
9005
  const handle = async (request, response) => {
8822
9006
  const url2 = new URL(request.url ?? "/", "http://localhost");
8823
- 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)) {
8824
9012
  response.writeHead(401, { ...JSON_HEADERS5, "cache-control": "no-store" }).end(JSON.stringify({ version: 1, kind: "unauthorized" }));
8825
9013
  return;
8826
9014
  }
8827
- if (deps.console?.readConversation && deps.lifecycleHistoryApi && await handleConversationRoute(request, response, url2, {
9015
+ if (deps.console?.readConversation && await handleConversationRoute(request, response, url2, {
8828
9016
  token: deps.console.token,
8829
9017
  enabled: deps.config.graphView?.enabled === true,
8830
- contentEnabled: deps.console.conversationContentEnabled === true,
8831
- reader: deps.lifecycleHistoryApi.reader,
9018
+ contentEnabled: deps.console.conversationContentEnabled ?? deps.config.sessionHistory?.contentEnabled ?? true,
9019
+ ...deps.lifecycleHistoryApi ? { reader: deps.lifecycleHistoryApi.reader } : {},
8832
9020
  resolveEndpoint: async (id) => deps.console?.historyEndpoint ?? await resolveEndpoint(id),
8833
9021
  observedSessionIDs: async () => buildConsoleState(await deps.readSnapshot()).sessions.map((session) => session.sessionID),
8834
9022
  readConversation: deps.console.readConversation
@@ -9248,7 +9436,7 @@ function parseOpencodeProviders(opencodeConfig) {
9248
9436
  function localRuntimeStates(config) {
9249
9437
  const map = new Map;
9250
9438
  for (const runtime of config.local.runtimes) {
9251
- map.set(runtime.id, { enabled: runtime.enabled });
9439
+ map.set(runtime.defaultModel.providerID, { enabled: runtime.enabled });
9252
9440
  }
9253
9441
  return map;
9254
9442
  }
@@ -9256,7 +9444,7 @@ function disabledLocalProviderIDs(config) {
9256
9444
  const ids = new Set;
9257
9445
  for (const runtime of config.local.runtimes) {
9258
9446
  if (!runtime.enabled) {
9259
- ids.add(runtime.id);
9447
+ ids.add(runtime.defaultModel.providerID);
9260
9448
  }
9261
9449
  }
9262
9450
  return ids;
@@ -9272,7 +9460,11 @@ function computeLive(model, isLocalProvider, liveModels) {
9272
9460
  if (!snapshot.reachable) {
9273
9461
  return "unreachable";
9274
9462
  }
9275
- return snapshot.modelIDs.has(model.modelID) ? "confirmed" : "not-advertised";
9463
+ if (snapshot.modelIDs.has(model.modelID))
9464
+ return "confirmed";
9465
+ if (snapshot.residency?.has(model.modelID))
9466
+ return snapshot.residency.get(model.modelID) === false ? "not-loaded" : "residency-unknown";
9467
+ return "not-advertised";
9276
9468
  }
9277
9469
  function resolveAgentModel(model, context) {
9278
9470
  const isLocalProvider = KNOWN_LOCAL_PROVIDER_IDS.has(model.providerID) || context.localRuntimes.has(model.providerID);
@@ -11127,7 +11319,7 @@ async function runConsoleServe(deps, options) {
11127
11319
  ptyEnabled,
11128
11320
  createClient: makeClient,
11129
11321
  drilldownEnabled: graphViewEnabled,
11130
- conversationContentEnabled: config.console.sessionHistory?.contentEnabled === true,
11322
+ conversationContentEnabled: config.console.sessionHistory?.contentEnabled !== false,
11131
11323
  ...config.console.sessionHistory?.endpoint !== undefined ? { historyEndpoint: config.console.sessionHistory.endpoint } : {},
11132
11324
  readConversation: createConversationReader({
11133
11325
  directory: deps.directory ?? process.cwd(),
@@ -16012,6 +16204,243 @@ function ensureFoundryV1BaseURL(endpoint) {
16012
16204
  }
16013
16205
  }
16014
16206
 
16207
+ // src/local/lemonadeResidency.ts
16208
+ import { z as z22 } from "zod";
16209
+
16210
+ // src/messages/modelResidency.ts
16211
+ var reasons = {
16212
+ unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
16213
+ inactive: "the requested model is not verified as resident and serving",
16214
+ identity: "the requested model identity can be rewritten or is ambiguous",
16215
+ destination: "the request destination does not match its configured runtime",
16216
+ transport: "this provider execution path cannot enforce the residency guard",
16217
+ request: "this is not a supported, explicit model inference request",
16218
+ cancelled: "the request was cancelled before residency could be verified",
16219
+ timeout: "the residency metadata deadline expired",
16220
+ configuration: "the residency guard configuration is invalid"
16221
+ };
16222
+ var modelResidencyMessages = {
16223
+ blocked: (reason) => `[openteam] Lemonade model request blocked: ${reasons[reason]}.`,
16224
+ providerDisabled: (providerID) => `[openteam] Lemonade provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
16225
+ extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
16226
+ };
16227
+
16228
+ // src/local/lemonadeResidency.ts
16229
+ class ModelResidencyError extends Error {
16230
+ code;
16231
+ statusCode = 400;
16232
+ isRetryable = false;
16233
+ constructor(code) {
16234
+ super(modelResidencyMessages.blocked(code));
16235
+ this.code = code;
16236
+ this.name = "ModelResidencyError";
16237
+ }
16238
+ }
16239
+ var modelName = z22.string().min(1).refine((name) => name === name.trim());
16240
+ var LoadedModelSchema = z22.object({
16241
+ model_name: modelName,
16242
+ loaded: z22.boolean().optional(),
16243
+ status: z22.string().optional(),
16244
+ backend_alive: z22.boolean().optional(),
16245
+ backend_health: z22.string().optional(),
16246
+ watchdog_reset: z22.boolean().optional(),
16247
+ watchdog_reset_reason: z22.string().optional(),
16248
+ residency_class: z22.string().optional(),
16249
+ slot_pool: z22.string().optional(),
16250
+ checkpoint: z22.string().optional(),
16251
+ type: z22.string().optional(),
16252
+ device: z22.string().optional(),
16253
+ backend_url: z22.string().optional(),
16254
+ pid: z22.number().int().optional(),
16255
+ launch_command: z22.unknown().optional(),
16256
+ pinned: z22.boolean().optional(),
16257
+ recipe: z22.string().optional(),
16258
+ recipe_options: z22.unknown().optional(),
16259
+ is_busy: z22.boolean().optional(),
16260
+ is_streaming: z22.boolean().optional(),
16261
+ max_context_window: z22.number().optional(),
16262
+ cost_input_per_million: z22.number().optional(),
16263
+ cost_output_per_million: z22.number().optional(),
16264
+ last_use: z22.number().optional()
16265
+ }).strict();
16266
+ var TelemetrySchema = z22.object({
16267
+ enabled: z22.boolean(),
16268
+ captures: z22.array(z22.string()).optional()
16269
+ }).strict();
16270
+ var HealthSchema = z22.object({
16271
+ status: z22.literal("ok"),
16272
+ all_models_loaded: z22.array(LoadedModelSchema),
16273
+ version: z22.string().optional(),
16274
+ model_loaded: z22.string().nullable().optional(),
16275
+ telemetry: TelemetrySchema.optional(),
16276
+ max_models: z22.record(z22.string(), z22.number().int()).optional(),
16277
+ pinned_models: z22.record(z22.string(), z22.number().int()).optional(),
16278
+ pinned_helper_models: z22.record(z22.string(), z22.number().int()).optional(),
16279
+ websocket_port: z22.number().int().optional(),
16280
+ update_check_done: z22.boolean().optional()
16281
+ }).strict();
16282
+ var AliasSchema = z22.object({
16283
+ alias: modelName,
16284
+ target: modelName,
16285
+ downloaded: z22.boolean().optional(),
16286
+ recipe: z22.string().optional()
16287
+ }).strict();
16288
+ var AliasesSchema = z22.object({
16289
+ aliases: z22.array(AliasSchema)
16290
+ }).strict();
16291
+ var MetadataRecordSchema = z22.record(z22.string(), z22.unknown());
16292
+ var inactiveStates = new Set([
16293
+ "loading",
16294
+ "downsizing",
16295
+ "downsized",
16296
+ "evicting",
16297
+ "unloaded"
16298
+ ]);
16299
+ var inactiveHealth = new Set(["stopped", "exited", "watchdog_reset"]);
16300
+ var standardPools = new Set([
16301
+ "standard/llm",
16302
+ "standard/embedding",
16303
+ "standard/reranking",
16304
+ "standard/transcription",
16305
+ "standard/image",
16306
+ "standard/tts",
16307
+ "standard/audio-generation",
16308
+ "standard/classification",
16309
+ "standard/mesh"
16310
+ ]);
16311
+ function usableState(entry) {
16312
+ if (entry.loaded === false || entry.backend_alive === false || entry.watchdog_reset === true || typeof entry.status === "string" && inactiveStates.has(entry.status) || typeof entry.backend_health === "string" && inactiveHealth.has(entry.backend_health) || entry.residency_class === "routing_helper" || typeof entry.slot_pool === "string" && entry.slot_pool.startsWith("routing_helper/")) {
16313
+ return false;
16314
+ }
16315
+ if (entry.loaded !== true || entry.backend_alive !== true || entry.watchdog_reset !== false || entry.status !== "ready" && entry.status !== "in_use" || entry.backend_health !== "ready" && entry.backend_health !== "busy" || entry.watchdog_reset_reason !== undefined && entry.watchdog_reset_reason !== "" || entry.residency_class !== undefined && entry.residency_class !== "standard" || entry.slot_pool !== undefined && (typeof entry.slot_pool !== "string" || !standardPools.has(entry.slot_pool))) {
16316
+ return;
16317
+ }
16318
+ return true;
16319
+ }
16320
+ function projectMetadataObject(payload, keys) {
16321
+ const record2 = MetadataRecordSchema.safeParse(payload);
16322
+ if (!record2.success)
16323
+ throw new ModelResidencyError("unknown");
16324
+ return Object.fromEntries(keys.filter((key) => Object.hasOwn(record2.data, key)).map((key) => [key, record2.data[key]]));
16325
+ }
16326
+ function parseResidency(healthPayload, aliasPayload) {
16327
+ const healthFields = projectMetadataObject(healthPayload, Object.keys(HealthSchema.shape));
16328
+ if (Array.isArray(healthFields.all_models_loaded)) {
16329
+ healthFields.all_models_loaded = healthFields.all_models_loaded.map((entry) => projectMetadataObject(entry, Object.keys(LoadedModelSchema.shape)));
16330
+ }
16331
+ if (healthFields.telemetry !== undefined) {
16332
+ healthFields.telemetry = projectMetadataObject(healthFields.telemetry, Object.keys(TelemetrySchema.shape));
16333
+ }
16334
+ const aliasFields = projectMetadataObject(aliasPayload, Object.keys(AliasesSchema.shape));
16335
+ if (Array.isArray(aliasFields.aliases)) {
16336
+ aliasFields.aliases = aliasFields.aliases.map((entry) => projectMetadataObject(entry, Object.keys(AliasSchema.shape)));
16337
+ }
16338
+ const health = HealthSchema.safeParse(healthFields);
16339
+ const aliases = AliasesSchema.safeParse(aliasFields);
16340
+ if (!health.success || !aliases.success)
16341
+ throw new ModelResidencyError("unknown");
16342
+ const result = new Map;
16343
+ for (const entry of health.data.all_models_loaded) {
16344
+ if (result.has(entry.model_name))
16345
+ throw new ModelResidencyError("unknown");
16346
+ result.set(entry.model_name, {
16347
+ loaded: entry.model_name.endsWith(":latest") ? undefined : usableState(entry),
16348
+ metadata: entry
16349
+ });
16350
+ }
16351
+ const seenAliases = new Set;
16352
+ for (const entry of aliases.data.aliases) {
16353
+ if (seenAliases.has(entry.alias))
16354
+ throw new ModelResidencyError("unknown");
16355
+ seenAliases.add(entry.alias);
16356
+ result.set(entry.alias, {
16357
+ loaded: undefined,
16358
+ metadata: result.get(entry.alias)?.metadata ?? {}
16359
+ });
16360
+ }
16361
+ return result;
16362
+ }
16363
+ function lemonadeBaseURL(value) {
16364
+ let url;
16365
+ try {
16366
+ url = new URL(value);
16367
+ } catch {
16368
+ throw new ModelResidencyError("destination");
16369
+ }
16370
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !/^\/(?:api\/)?v[01]\/?$/.test(url.pathname)) {
16371
+ throw new ModelResidencyError("destination");
16372
+ }
16373
+ return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
16374
+ }
16375
+ async function withResidencyDeadline(operation, options) {
16376
+ const timeoutMs = options.timeoutMs ?? 5000;
16377
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
16378
+ throw new ModelResidencyError("configuration");
16379
+ }
16380
+ const controller = new AbortController;
16381
+ const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
16382
+ if (options.signal?.aborted)
16383
+ throw new ModelResidencyError("cancelled");
16384
+ options.signal?.addEventListener("abort", cancel, { once: true });
16385
+ const schedule = options.scheduleTimeout ?? ((callback, ms2) => {
16386
+ const timer = setTimeout(callback, ms2);
16387
+ return () => clearTimeout(timer);
16388
+ });
16389
+ let onAbort = () => {};
16390
+ const aborted = new Promise((_, reject) => {
16391
+ onAbort = () => reject(controller.signal.reason);
16392
+ controller.signal.addEventListener("abort", onAbort, { once: true });
16393
+ });
16394
+ const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
16395
+ try {
16396
+ controller.signal.throwIfAborted();
16397
+ return await Promise.race([operation(controller.signal), aborted]);
16398
+ } finally {
16399
+ clearTimer();
16400
+ options.signal?.removeEventListener("abort", cancel);
16401
+ controller.signal.removeEventListener("abort", onAbort);
16402
+ controller.abort();
16403
+ }
16404
+ }
16405
+ async function metadata(url, fetch2, signal, headers) {
16406
+ signal.throwIfAborted();
16407
+ let payload;
16408
+ try {
16409
+ const response = await fetch2(url, {
16410
+ method: "GET",
16411
+ headers,
16412
+ signal,
16413
+ redirect: "error",
16414
+ cache: "no-store"
16415
+ });
16416
+ signal.throwIfAborted();
16417
+ if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
16418
+ throw new ModelResidencyError("unknown");
16419
+ }
16420
+ payload = await response.json();
16421
+ } catch {
16422
+ signal.throwIfAborted();
16423
+ throw new ModelResidencyError("unknown");
16424
+ }
16425
+ signal.throwIfAborted();
16426
+ return payload;
16427
+ }
16428
+ async function readLemonadeResidency(baseURL, fetch2, options = {}) {
16429
+ const base = lemonadeBaseURL(baseURL);
16430
+ const headers = new Headers(options.headers);
16431
+ headers.delete("content-type");
16432
+ headers.delete("content-length");
16433
+ headers.set("accept", "application/json");
16434
+ headers.set("cache-control", "no-cache, no-store");
16435
+ headers.delete("if-none-match");
16436
+ headers.delete("if-modified-since");
16437
+ return withResidencyDeadline(async (signal) => {
16438
+ const aliases = await metadata(`${new URL(base).origin}/internal/aliases`, fetch2, signal, headers);
16439
+ const health = await metadata(`${base}/health`, fetch2, signal, headers);
16440
+ return parseResidency(health, aliases);
16441
+ }, options);
16442
+ }
16443
+
16015
16444
  // src/local/lemonade.ts
16016
16445
  var LEMONADE_DEFAULT_BASE_URL = "http://localhost:13305/api/v1";
16017
16446
  var NON_TEXT_LABELS = new Set([
@@ -16098,36 +16527,19 @@ function isNonTextModel(entry) {
16098
16527
  return entry.labels.some((label) => typeof label === "string" && NON_TEXT_LABELS.has(label));
16099
16528
  }
16100
16529
  async function readLoadedModels(baseURL, fetch2) {
16101
- let payload;
16102
16530
  try {
16103
- const response = await fetch2(`${baseURL}/health`, {
16104
- method: "GET",
16105
- headers: { accept: "application/json" }
16106
- });
16107
- if (!response.ok) {
16108
- return;
16109
- }
16110
- payload = await response.json();
16531
+ const residency = await readLemonadeResidency(baseURL, fetch2);
16532
+ return new Map([...residency].map(([name, entry]) => [
16533
+ name,
16534
+ {
16535
+ loaded: entry.loaded,
16536
+ slots: entry.loaded === true ? readSlotCount(entry.metadata) : undefined,
16537
+ totalContextWindow: entry.loaded === true ? readLoadedContextWindow(entry.metadata) : undefined
16538
+ }
16539
+ ]));
16111
16540
  } catch {
16112
16541
  return;
16113
16542
  }
16114
- if (!isObject2(payload) || !Array.isArray(payload.all_models_loaded)) {
16115
- return;
16116
- }
16117
- const loaded = new Map;
16118
- for (const entry of payload.all_models_loaded) {
16119
- if (!isObject2(entry) || typeof entry.model_name !== "string") {
16120
- continue;
16121
- }
16122
- if (entry.loaded === false) {
16123
- continue;
16124
- }
16125
- loaded.set(entry.model_name, {
16126
- slots: readSlotCount(entry),
16127
- totalContextWindow: readLoadedContextWindow(entry)
16128
- });
16129
- }
16130
- return loaded;
16131
16543
  }
16132
16544
  function readSlotCount(entry) {
16133
16545
  return slotsFromLlamacppArgs(entry) ?? slotsFromLaunchCommand(entry);
@@ -16274,6 +16686,9 @@ function enrichWithLoadState(models, loaded) {
16274
16686
  if (hit === undefined) {
16275
16687
  return { ...base, loaded: false };
16276
16688
  }
16689
+ if (hit.loaded !== true) {
16690
+ return hit.loaded === false ? { ...base, loaded: false } : base;
16691
+ }
16277
16692
  const contextWindow = derivedConservativeContextWindow(model.contextWindow, hit);
16278
16693
  return {
16279
16694
  ...base,
@@ -16290,6 +16705,11 @@ function isObject2(value) {
16290
16705
  return typeof value === "object" && value !== null && !Array.isArray(value);
16291
16706
  }
16292
16707
 
16708
+ // src/local/modelResidency.ts
16709
+ function hasRequiredModelResidency(kind, model) {
16710
+ return kind !== "lemonade" || model?.loaded === true;
16711
+ }
16712
+
16293
16713
  // src/local/lmstudio.ts
16294
16714
  var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
16295
16715
  function createLMStudioAdapter() {
@@ -16507,50 +16927,36 @@ function createDetect(exec) {
16507
16927
  foundryDiscovery: foundryDiscoveryFromExec(exec)
16508
16928
  });
16509
16929
  const snapshots = await registry.probe(DETECTION_RUNTIMES);
16510
- return snapshots.map((snapshot) => {
16511
- const detected = {
16512
- id: snapshot.id,
16513
- baseURL: snapshot.baseURL,
16514
- reachable: snapshot.reachable,
16515
- models: snapshot.models.map((model) => model.modelID)
16516
- };
16517
- if (snapshot.models.some((model) => model.loaded !== undefined)) {
16518
- detected.modelInfo = snapshot.models;
16519
- }
16520
- if (snapshot.error !== undefined) {
16521
- detected.error = snapshot.error;
16522
- }
16523
- return detected;
16524
- });
16930
+ return DETECTION_RUNTIMES.map((runtime) => detectedRuntime(runtime, snapshots.find((snapshot) => snapshot.id === runtime.id)));
16931
+ };
16932
+ }
16933
+ function detectedRuntime(runtime, snapshot) {
16934
+ const modelInfo = snapshot?.models ?? [];
16935
+ return {
16936
+ id: runtime.id,
16937
+ baseURL: snapshot?.baseURL ?? runtime.baseURL ?? "",
16938
+ reachable: snapshot?.reachable ?? false,
16939
+ models: modelInfo.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model)).map((model) => model.modelID),
16940
+ modelInfo,
16941
+ ...snapshot?.error === undefined ? {} : { error: snapshot.error }
16525
16942
  };
16526
16943
  }
16527
16944
  function createProbeRuntime(exec) {
16528
- return async (id, baseURL) => {
16945
+ return async (kind, baseURL) => {
16529
16946
  const registry = new RuntimeRegistry({
16530
16947
  fetch: globalThis.fetch,
16531
16948
  clock: () => new Date().toISOString(),
16532
16949
  foundryDiscovery: foundryDiscoveryFromExec(exec)
16533
16950
  });
16534
16951
  const runtime = {
16535
- id,
16952
+ id: kind,
16953
+ kind,
16536
16954
  enabled: true,
16537
16955
  baseURL,
16538
- defaultModel: { providerID: id, modelID: "probe" }
16956
+ defaultModel: { providerID: kind, modelID: "probe" }
16539
16957
  };
16540
16958
  const [snapshot] = await registry.probe([runtime]);
16541
- const detected = {
16542
- id,
16543
- baseURL: snapshot?.baseURL ?? baseURL,
16544
- reachable: snapshot?.reachable ?? false,
16545
- models: snapshot?.models.map((model) => model.modelID) ?? []
16546
- };
16547
- if (snapshot?.models.some((model) => model.loaded !== undefined) === true) {
16548
- detected.modelInfo = snapshot.models;
16549
- }
16550
- if (snapshot?.error !== undefined) {
16551
- detected.error = snapshot.error;
16552
- }
16553
- return detected;
16959
+ return detectedRuntime(runtime, snapshot);
16554
16960
  };
16555
16961
  }
16556
16962
 
@@ -16663,11 +17069,11 @@ function planWorktreeReconciliation(input) {
16663
17069
  }
16664
17070
 
16665
17071
  // src/telemetry/otelConfig.ts
16666
- import { z as z22 } from "zod";
16667
- var OtelBackendConfigSchema = z22.object({
16668
- backend: z22.literal("opentelemetry"),
16669
- connectionEnv: z22.string().min(1),
16670
- serviceName: z22.string().min(1).optional()
17072
+ import { z as z23 } from "zod";
17073
+ var OtelBackendConfigSchema = z23.object({
17074
+ backend: z23.literal("opentelemetry"),
17075
+ connectionEnv: z23.string().min(1),
17076
+ serviceName: z23.string().min(1).optional()
16671
17077
  }).strict();
16672
17078
  function parseConnectionString(raw) {
16673
17079
  const pairs = new Map;
@@ -17809,7 +18215,7 @@ import { posix as pathPosix, win32 as pathWin32 } from "node:path";
17809
18215
  // package.json
17810
18216
  var package_default = {
17811
18217
  name: "@jmanuelcorral/openteam",
17812
- version: "0.24.0",
18218
+ version: "0.25.0",
17813
18219
  packageManager: "bun@1.3.14",
17814
18220
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
17815
18221
  license: "MIT",
@@ -17953,31 +18359,31 @@ function compareSemver(a, b) {
17953
18359
  }
17954
18360
 
17955
18361
  // src/graph/soakLedger.ts
17956
- import { z as z23 } from "zod";
17957
- var Sha256Schema2 = z23.string().regex(/^[0-9a-f]{64}$/);
17958
- var SoakPlatformSchema = z23.enum(["linux", "win32", "darwin"]);
17959
- var PrivacyPairSchema = z23.object({
17960
- surfacesScanned: z23.number().int().nonnegative(),
17961
- rawFindings: z23.number().int().nonnegative()
18362
+ import { z as z24 } from "zod";
18363
+ var Sha256Schema2 = z24.string().regex(/^[0-9a-f]{64}$/);
18364
+ var SoakPlatformSchema = z24.enum(["linux", "win32", "darwin"]);
18365
+ var PrivacyPairSchema = z24.object({
18366
+ surfacesScanned: z24.number().int().nonnegative(),
18367
+ rawFindings: z24.number().int().nonnegative()
17962
18368
  }).strict();
17963
- var DuplicateEffectPairSchema = z23.object({
17964
- effectsExamined: z23.number().int().nonnegative(),
17965
- duplicatesFound: z23.number().int().nonnegative()
18369
+ var DuplicateEffectPairSchema = z24.object({
18370
+ effectsExamined: z24.number().int().nonnegative(),
18371
+ duplicatesFound: z24.number().int().nonnegative()
17966
18372
  }).strict();
17967
- var ModelVerificationPairSchema = z23.object({
17968
- nodesChecked: z23.number().int().nonnegative(),
17969
- unverified: z23.number().int().nonnegative(),
17970
- mismatched: z23.number().int().nonnegative()
18373
+ var ModelVerificationPairSchema = z24.object({
18374
+ nodesChecked: z24.number().int().nonnegative(),
18375
+ unverified: z24.number().int().nonnegative(),
18376
+ mismatched: z24.number().int().nonnegative()
17971
18377
  }).strict();
17972
- var SoakObservationSchema = z23.object({
17973
- version: z23.literal(1),
17974
- seq: z23.number().int().nonnegative(),
17975
- timestamp: z23.string().datetime(),
18378
+ var SoakObservationSchema = z24.object({
18379
+ version: z24.literal(1),
18380
+ seq: z24.number().int().nonnegative(),
18381
+ timestamp: z24.string().datetime(),
17976
18382
  platform: SoakPlatformSchema,
17977
- opencodeVersion: z23.string().min(1),
17978
- provenance: z23.enum(["genuine-usage", "ci-synthetic"]),
18383
+ opencodeVersion: z24.string().min(1),
18384
+ provenance: z24.enum(["genuine-usage", "ci-synthetic"]),
17979
18385
  traceDigest: Sha256Schema2,
17980
- criticalDivergences: z23.number().int().nonnegative().nullable(),
18386
+ criticalDivergences: z24.number().int().nonnegative().nullable(),
17981
18387
  privacy: PrivacyPairSchema.nullable(),
17982
18388
  duplicateEffects: DuplicateEffectPairSchema.nullable(),
17983
18389
  modelVerification: ModelVerificationPairSchema.nullable(),
@@ -18259,6 +18665,12 @@ var KNOWN_RUNTIMES = [
18259
18665
  dynamicPort: true
18260
18666
  }
18261
18667
  ];
18668
+ var LEMONADE_PROJECT_CONFIG_CANDIDATES = [
18669
+ ".opencode/opencode.jsonc",
18670
+ ".opencode/opencode.json",
18671
+ "opencode.jsonc",
18672
+ "opencode.json"
18673
+ ];
18262
18674
  function knownRuntime(id) {
18263
18675
  const found = KNOWN_RUNTIMES.find((runtime) => runtime.id === id);
18264
18676
  if (found === undefined) {
@@ -18275,7 +18687,44 @@ function choiceKind(choice) {
18275
18687
  function choiceProviderID(choice) {
18276
18688
  return choice.providerID ?? choice.id;
18277
18689
  }
18690
+ function selectableModelIDs(models, modelInfo, kind) {
18691
+ const byID = new Map(modelInfo?.map((model) => [model.modelID, model]));
18692
+ return models.filter((id) => hasRequiredModelResidency(kind, byID.get(id)));
18693
+ }
18694
+ function assertRuntimeResidency(choice, primary) {
18695
+ const kind = localRuntimeKind(choice);
18696
+ const references2 = [
18697
+ choice.defaultModelID,
18698
+ choice.thinkingModelID,
18699
+ choice.coderModelID,
18700
+ ...primary.providerID === choiceProviderID(choice) ? [primary.modelID] : []
18701
+ ];
18702
+ for (const id of references2) {
18703
+ if (id === undefined)
18704
+ continue;
18705
+ const info = choice.models.includes(id) ? choice.modelInfo?.find((model) => model.modelID === id) : undefined;
18706
+ if (!hasRequiredModelResidency(kind, info))
18707
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${choiceProviderID(choice)}/${id}`));
18708
+ }
18709
+ }
18710
+ function assertSetupResidency(answers) {
18711
+ for (const choice of answers.runtimes) {
18712
+ if (choice.enabled) {
18713
+ assertRuntimeResidency(choice, answers.primaryModel);
18714
+ } else if (choiceProviderID(choice) === answers.primaryModel.providerID && !hasRequiredModelResidency(localRuntimeKind(choice), undefined)) {
18715
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
18716
+ }
18717
+ }
18718
+ }
18719
+ function lemonadeV2Policies(providers) {
18720
+ return [...new Set(providers)].sort().map((providerID) => {
18721
+ if (providerID.length === 0 || /[*?\\/\s]/.test(providerID))
18722
+ throw new Error(executionSetupMessages.lemonadeV2InvalidProvider(providerID));
18723
+ return { action: "provider.use", resource: providerID, effect: "deny" };
18724
+ });
18725
+ }
18278
18726
  function buildOpenTeamConfig(answers) {
18727
+ assertSetupResidency(answers);
18279
18728
  const runtimes = answers.runtimes.filter((choice) => choice.enabled).map((choice) => {
18280
18729
  const runtime = {
18281
18730
  id: choice.id,
@@ -18344,6 +18793,8 @@ function orchestratorAgentModel(answers) {
18344
18793
  return { ...answers.primaryModel };
18345
18794
  }
18346
18795
  function buildOpencodeConfig(answers) {
18796
+ assertSetupResidency(answers);
18797
+ const policies = lemonadeV2Policies(answers.runtimes.filter((choice) => choice.enabled && choiceKind(choice) === "lemonade").map(choiceProviderID));
18347
18798
  const provider = {};
18348
18799
  const providerOwner = new Map;
18349
18800
  for (const choice of answers.runtimes) {
@@ -18356,7 +18807,7 @@ function buildOpencodeConfig(answers) {
18356
18807
  continue;
18357
18808
  }
18358
18809
  const providerID = choiceProviderID(choice);
18359
- const modelIDs = new Set(choice.models);
18810
+ const modelIDs = new Set(selectableModelIDs(choice.models, choice.modelInfo, meta.id));
18360
18811
  modelIDs.add(choice.defaultModelID);
18361
18812
  if (choice.thinkingModelID !== undefined) {
18362
18813
  modelIDs.add(choice.thinkingModelID);
@@ -18412,6 +18863,8 @@ function buildOpencodeConfig(answers) {
18412
18863
  if (answers.yolo) {
18413
18864
  config.permission = { "*": "allow" };
18414
18865
  }
18866
+ if (policies.length > 0)
18867
+ config.experimental = { policies };
18415
18868
  return config;
18416
18869
  }
18417
18870
  function serializeOpencodeConfig(config) {
@@ -18511,10 +18964,11 @@ async function chooseFrontier(prompt, profiles) {
18511
18964
  }
18512
18965
  return parseFrontierRef(selected);
18513
18966
  }
18514
- function orderModelChoices(models, modelInfo) {
18967
+ function orderModelChoices(models, modelInfo, kind) {
18515
18968
  const byID = new Map((modelInfo ?? []).map((model) => [model.modelID, model]));
18516
18969
  const anyLoadState = (modelInfo ?? []).some((model) => model.loaded !== undefined);
18517
- const decorated = models.map((modelID, index) => {
18970
+ const offered = kind === undefined ? models : selectableModelIDs(models, modelInfo, kind);
18971
+ const decorated = offered.map((modelID, index) => {
18518
18972
  const info = byID.get(modelID);
18519
18973
  return {
18520
18974
  modelID,
@@ -18540,27 +18994,34 @@ function modelStateSuffix(loaded, slots) {
18540
18994
  }
18541
18995
  return slots === undefined ? "(loaded)" : `(loaded · ${slots} slot${slots === 1 ? "" : "s"})`;
18542
18996
  }
18543
- function preferredModelID(models, modelInfo) {
18544
- const loaded = (modelInfo ?? []).find((model) => model.loaded === true && models.includes(model.modelID));
18545
- return loaded?.modelID ?? models[0];
18997
+ function preferredModelID(models, modelInfo, kind) {
18998
+ const offered = kind === undefined ? models : selectableModelIDs(models, modelInfo, kind);
18999
+ const loaded = (modelInfo ?? []).find((model) => model.loaded === true && offered.includes(model.modelID));
19000
+ return loaded?.modelID ?? offered[0];
18546
19001
  }
18547
19002
  async function chooseRuntimeModel(prompt, models, meta, modelInfo) {
18548
- if (models.length === 0) {
19003
+ const offered = selectableModelIDs(models, modelInfo, meta.id);
19004
+ if (offered.length === 0) {
19005
+ if (meta.id === "lemonade")
19006
+ throw new Error(executionSetupMessages.lemonadeNoLoadedModels);
18549
19007
  return prompt.text({
18550
19008
  message: `Default model for ${meta.label} (no models detected)`,
18551
19009
  placeholder: meta.fallbackModelID,
18552
19010
  initial: meta.fallbackModelID
18553
19011
  });
18554
19012
  }
18555
- const first = preferredModelID(models, modelInfo);
19013
+ const first = preferredModelID(offered, modelInfo, meta.id);
18556
19014
  const opts = {
18557
19015
  message: `Default local model for ${meta.label}`,
18558
- choices: orderModelChoices(models, modelInfo)
19016
+ choices: orderModelChoices(offered, modelInfo, meta.id)
18559
19017
  };
18560
19018
  if (first !== undefined) {
18561
19019
  opts.initial = first;
18562
19020
  }
18563
- return prompt.select(opts);
19021
+ const selected = await prompt.select(opts);
19022
+ if (meta.id === "lemonade" && !offered.includes(selected))
19023
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(selected));
19024
+ return selected;
18564
19025
  }
18565
19026
  function remoteBaseURLExample(meta) {
18566
19027
  const base = meta.defaultBaseURL ?? "http://localhost:11434/v1";
@@ -18615,6 +19076,8 @@ async function resolveRuntimeConnection(deps, meta, info) {
18615
19076
  initial: RUNTIME_LOCATION_LOCAL
18616
19077
  });
18617
19078
  if (location === RUNTIME_LOCATION_LOCAL) {
19079
+ if (meta.id === "lemonade" && info?.reachable !== true)
19080
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
18618
19081
  const baseURL2 = info?.baseURL !== undefined && info.baseURL.length > 0 ? info.baseURL : meta.defaultBaseURL;
18619
19082
  return { baseURL: baseURL2, ...connectionOf(info) };
18620
19083
  }
@@ -18624,16 +19087,26 @@ async function resolveRuntimeConnection(deps, meta, info) {
18624
19087
  });
18625
19088
  const baseURL = normalizeRemoteBaseURL(entered, meta.defaultBaseURL);
18626
19089
  let probedRuntime;
19090
+ if (meta.id === "lemonade" && deps.probeRuntime === undefined)
19091
+ throw new Error(executionSetupMessages.lemonadeProbeRequired);
18627
19092
  if (deps.probeRuntime !== undefined) {
18628
19093
  try {
18629
19094
  probedRuntime = await deps.probeRuntime(meta.id, baseURL);
18630
- } catch {}
19095
+ } catch (cause) {
19096
+ if (meta.id === "lemonade")
19097
+ throw new Error(executionSetupMessages.lemonadeProbeFailed, { cause });
19098
+ }
19099
+ if (meta.id === "lemonade" && probedRuntime?.reachable !== true)
19100
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
18631
19101
  }
18632
19102
  return { baseURL, ...connectionOf(probedRuntime) };
18633
19103
  }
18634
19104
  async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass, modelInfo) {
18635
19105
  const label = modelClass === "thinking" ? `Thinking model for ${meta.label} (design, analysis, review reasoning)` : `Coder model for ${meta.label} (writing and editing code)`;
18636
- if (models.length === 0) {
19106
+ const offered = selectableModelIDs(models, modelInfo, meta.id);
19107
+ if (offered.length === 0) {
19108
+ if (meta.id === "lemonade")
19109
+ throw new Error(executionSetupMessages.lemonadeNoLoadedModels);
18637
19110
  const raw = await prompt.text({
18638
19111
  message: `${label} — enter to reuse the default`,
18639
19112
  placeholder: defaultModelID,
@@ -18641,14 +19114,17 @@ async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass
18641
19114
  });
18642
19115
  return raw.trim().length > 0 ? raw.trim() : defaultModelID;
18643
19116
  }
18644
- return prompt.select({
19117
+ const selected = await prompt.select({
18645
19118
  message: label,
18646
- choices: orderModelChoices(models, modelInfo).map((choice) => ({
19119
+ choices: orderModelChoices(offered, modelInfo, meta.id).map((choice) => ({
18647
19120
  value: choice.value,
18648
19121
  label: choice.value === defaultModelID ? `${choice.label} — default` : choice.label
18649
19122
  })),
18650
19123
  initial: defaultModelID
18651
19124
  });
19125
+ if (meta.id === "lemonade" && !offered.includes(selected))
19126
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(selected));
19127
+ return selected;
18652
19128
  }
18653
19129
  async function resolveEnabledRuntime(deps, meta, info) {
18654
19130
  const connection = await resolveRuntimeConnection(deps, meta, info);
@@ -18661,7 +19137,8 @@ async function resolveEnabledRuntime(deps, meta, info) {
18661
19137
  defaultModelID,
18662
19138
  thinkingModelID,
18663
19139
  coderModelID,
18664
- models: connection.models
19140
+ models: selectableModelIDs(connection.models, connection.modelInfo, meta.id),
19141
+ ...connection.modelInfo === undefined ? {} : { modelInfo: connection.modelInfo }
18665
19142
  };
18666
19143
  if (connection.baseURL !== undefined) {
18667
19144
  choice.baseURL = connection.baseURL;
@@ -19161,17 +19638,52 @@ async function readExistingOpencodeConfigFiles(paths, deps) {
19161
19638
  function opencodeFileAt(files, path2) {
19162
19639
  return files.find((file) => file.path === path2);
19163
19640
  }
19164
- function mergeExistingOpencodeConfigFiles(files) {
19641
+ function mergeExistingOpencodeConfigFiles(files, paths, planned = new Map) {
19165
19642
  let merged;
19166
- for (const path2 of [...OPENCODE_CONFIG_CANDIDATES].reverse()) {
19167
- const file = opencodeFileAt(files, path2);
19168
- if (file === undefined) {
19643
+ for (const path2 of [...paths].reverse()) {
19644
+ const raw = planned.get(path2) ?? opencodeFileAt(files, path2)?.raw;
19645
+ if (raw === undefined) {
19169
19646
  continue;
19170
19647
  }
19171
- merged = merged === undefined ? file.raw : mergeOpencodeConfigs(merged, file.raw);
19648
+ merged = merged === undefined ? raw : mergeOpencodeConfigs(merged, raw);
19172
19649
  }
19173
19650
  return merged;
19174
19651
  }
19652
+ async function effectiveSetupConfig(portable, files, deps) {
19653
+ const optionsByLoader = new Map;
19654
+ for (const file of files) {
19655
+ if (!Array.isArray(file.raw.plugin))
19656
+ continue;
19657
+ for (const entry of [...file.raw.plugin].reverse()) {
19658
+ const spec = extractPluginSpec(entry);
19659
+ if (spec === undefined)
19660
+ continue;
19661
+ let identity;
19662
+ if (isOpenteamNpmSpec(spec)) {
19663
+ identity = OPENTEAM_PACKAGE_NAME;
19664
+ } else if (isLocalFileSpec(spec) && await classifyLocalPluginSpecForSetup(spec, file.path, deps.classifyLocalPluginSpec) === "ours") {
19665
+ const resolved = resolvePluginSpecPath(spec, file.path);
19666
+ if (resolved === undefined)
19667
+ throw new Error(executionSetupMessages.lemonadeEffectiveConfigAmbiguous);
19668
+ identity = resolved;
19669
+ } else {
19670
+ continue;
19671
+ }
19672
+ if (!optionsByLoader.has(identity)) {
19673
+ optionsByLoader.set(identity, Array.isArray(entry) ? entry[1] : undefined);
19674
+ }
19675
+ }
19676
+ }
19677
+ const overlayText = await deps.readFile(DEFAULT_LOCAL_OVERLAY_PATH);
19678
+ const overlay = overlayText === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayText);
19679
+ const resolutions = [
19680
+ ...optionsByLoader.size > 0 ? optionsByLoader.values() : [undefined]
19681
+ ].map((options) => validateConfigInput(executionSetupMessages.lemonadeEffectiveConfigLabel, mergeConfigInputs(mergeConfigInputs(portable, configOptionsFromPluginOptions(options)), overlay)));
19682
+ const effective = resolutions[0];
19683
+ if (effective === undefined || resolutions.some((config) => JSON.stringify(config) !== JSON.stringify(effective)))
19684
+ throw new Error(executionSetupMessages.lemonadeEffectiveConfigAmbiguous);
19685
+ return effective;
19686
+ }
19175
19687
  function relevantLocalProviderIDs(generated, existingOpenTeamConfig) {
19176
19688
  const ids = new Set(Object.keys(generated.provider ?? {}));
19177
19689
  for (const runtime of existingOpenTeamConfig?.local.runtimes ?? []) {
@@ -19348,15 +19860,42 @@ function highestExistingPinVersion(files) {
19348
19860
  }
19349
19861
  return highest;
19350
19862
  }
19863
+ function reconcileLemonadeV2Policies(raw, denials) {
19864
+ if (denials.length === 0)
19865
+ return raw;
19866
+ const experimental = raw.experimental;
19867
+ if (experimental !== undefined && !isRecord6(experimental))
19868
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19869
+ const policies = experimental?.policies;
19870
+ if (policies !== undefined && !Array.isArray(policies))
19871
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19872
+ const existing = policies ?? [];
19873
+ for (const policy of existing) {
19874
+ if (!isRecord6(policy) || policy.action !== "provider.use" || typeof policy.resource !== "string" || policy.effect !== "allow" && policy.effect !== "deny")
19875
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19876
+ }
19877
+ const terminal = denials.every((denial, index) => {
19878
+ const policy = existing[existing.length - denials.length + index];
19879
+ return isRecord6(policy) && Object.keys(policy).length === 3 && policy.action === denial.action && policy.resource === denial.resource && policy.effect === denial.effect;
19880
+ });
19881
+ if (terminal)
19882
+ return raw;
19883
+ return {
19884
+ ...raw,
19885
+ experimental: { ...experimental, policies: [...existing, ...denials] }
19886
+ };
19887
+ }
19351
19888
  function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
19352
19889
  const effectiveRaw = options.effectiveRaw ?? targetRaw;
19353
19890
  const includePlugin = options.includePlugin ?? true;
19891
+ const managesV2Policies = (generated.experimental?.policies.length ?? 0) > 0;
19354
19892
  const managedKeys = new Set([
19355
19893
  "$schema",
19356
19894
  "provider",
19357
19895
  "model",
19358
19896
  "small_model",
19359
19897
  "permission",
19898
+ ...managesV2Policies ? ["experimental"] : [],
19360
19899
  ...includePlugin ? ["plugin"] : []
19361
19900
  ]);
19362
19901
  const unknownFields = {};
@@ -19419,7 +19958,10 @@ function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
19419
19958
  if (permission !== undefined) {
19420
19959
  result.permission = permission;
19421
19960
  }
19422
- return result;
19961
+ if (managesV2Policies && hasOwn2(targetRaw, "experimental")) {
19962
+ result.experimental = targetRaw.experimental;
19963
+ }
19964
+ return reconcileLemonadeV2Policies(result, generated.experimental?.policies ?? []);
19423
19965
  }
19424
19966
  function mergeOpenTeamConfigForRerun(generated, existing) {
19425
19967
  const existingOrch = existing.orchestrator;
@@ -19469,6 +20011,21 @@ function mergeOpenTeamConfigForRerun(generated, existing) {
19469
20011
  assertEffectiveConfigModelDomains(merged);
19470
20012
  return merged;
19471
20013
  }
20014
+ function preserveRetainedLemonadeRuntimes(generated, previous, opencode) {
20015
+ const providers = recordAt2(opencode, "provider") ?? {};
20016
+ const retained = (previous?.local.runtimes ?? []).filter((runtime) => localRuntimeKind(runtime) === "lemonade" && hasOwn2(providers, runtime.defaultModel.providerID));
20017
+ const deselected = retained.filter((runtime) => !generated.local.runtimes.some((selected) => selected.defaultModel.providerID === runtime.defaultModel.providerID)).map((runtime) => ({ ...runtime, enabled: false }));
20018
+ return {
20019
+ config: deselected.length === 0 ? generated : OpenTeamConfigSchema.parse({
20020
+ ...generated,
20021
+ local: {
20022
+ ...generated.local,
20023
+ runtimes: [...generated.local.runtimes, ...deselected]
20024
+ }
20025
+ }),
20026
+ providerIDs: retained.map((runtime) => runtime.defaultModel.providerID)
20027
+ };
20028
+ }
19472
20029
  async function existingPluginOwnerPath(files, classifyLocalPluginSpec) {
19473
20030
  let unknownLocalPlugin;
19474
20031
  for (const file of files) {
@@ -19496,6 +20053,86 @@ async function confirmJsonNormalization(prompt, path2, format) {
19496
20053
  initial: false
19497
20054
  });
19498
20055
  }
20056
+ function assertPreviousLemonadePrimary(answers, previousConfig) {
20057
+ const priorLemonadePrimary = previousConfig?.local.runtimes.some((runtime) => runtime.defaultModel.providerID === answers.primaryModel.providerID && localRuntimeKind(runtime) === "lemonade");
20058
+ if (priorLemonadePrimary && !answers.runtimes.some((choice) => choice.enabled && choiceProviderID(choice) === answers.primaryModel.providerID)) {
20059
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
20060
+ }
20061
+ }
20062
+ async function verifyLemonadeBeforeWrite(deps, answers, config, opencode) {
20063
+ for (const choice of answers.runtimes) {
20064
+ if (choice.enabled && localRuntimeKind(choice) === "lemonade" && !config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === choiceProviderID(choice) && localRuntimeKind(runtime) === "lemonade"))
20065
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
20066
+ }
20067
+ for (const runtime of config.local.runtimes) {
20068
+ if (localRuntimeKind(runtime) !== "lemonade")
20069
+ continue;
20070
+ const providerID = runtime.defaultModel.providerID;
20071
+ const provider = isRecord6(opencode.provider) ? opencode.provider[providerID] : undefined;
20072
+ if (!runtime.enabled && provider === undefined)
20073
+ continue;
20074
+ const endpoint = isRecord6(provider) ? (isRecord6(provider.options) ? provider.options.baseURL : undefined) ?? provider.api : undefined;
20075
+ if (typeof endpoint !== "string" || lemonadeBaseURL(endpoint) !== lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL) || config.local.runtimes.filter((candidate) => candidate.defaultModel.providerID === providerID).length !== 1)
20076
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
20077
+ if (!runtime.enabled)
20078
+ continue;
20079
+ if (deps.probeRuntime === undefined)
20080
+ throw new Error(executionSetupMessages.lemonadeProbeRequired);
20081
+ const references2 = new Set([
20082
+ runtime.defaultModel.modelID,
20083
+ runtime.thinkingModel,
20084
+ runtime.coderModel
20085
+ ]);
20086
+ for (const choice of answers.runtimes) {
20087
+ if (choice.enabled && choiceProviderID(choice) === providerID) {
20088
+ for (const id of [
20089
+ ...choice.models,
20090
+ choice.defaultModelID,
20091
+ choice.thinkingModelID,
20092
+ choice.coderModelID
20093
+ ])
20094
+ references2.add(id);
20095
+ }
20096
+ }
20097
+ for (const ref of [
20098
+ answers.primaryModel,
20099
+ config.orchestrator?.primary?.model,
20100
+ config.router.localDefault
20101
+ ]) {
20102
+ if (ref !== null && typeof ref === "object" && ref.providerID === providerID)
20103
+ references2.add(ref.modelID);
20104
+ }
20105
+ for (const key of ["model", "small_model"]) {
20106
+ const ref = opencode[key];
20107
+ if (typeof ref === "string" && ref.startsWith(`${providerID}/`))
20108
+ references2.add(ref.slice(providerID.length + 1));
20109
+ }
20110
+ const models = isRecord6(provider) ? recordAt2(provider, "models") : undefined;
20111
+ const wireIDs = new Set;
20112
+ for (const alias of references2) {
20113
+ if (alias === undefined)
20114
+ continue;
20115
+ const declaration = recordAt2(models, alias);
20116
+ const wireID = declaration !== undefined && hasOwn2(declaration, "id") ? declaration.id : alias;
20117
+ if (declaration === undefined || typeof wireID !== "string" || wireID.length === 0 || wireID !== wireID.trim())
20118
+ throw new Error(executionSetupMessages.lemonadeModelIdentityUnknown(`${providerID}/${alias}`));
20119
+ wireIDs.add(wireID);
20120
+ }
20121
+ let fresh;
20122
+ try {
20123
+ fresh = await deps.probeRuntime(localRuntimeKind(runtime), endpoint);
20124
+ } catch (cause) {
20125
+ throw new Error(executionSetupMessages.lemonadeProbeFailed, { cause });
20126
+ }
20127
+ if (!fresh.reachable || fresh.id !== localRuntimeKind(runtime) || fresh.baseURL.replace(/\/+$/, "") !== endpoint.replace(/\/+$/, ""))
20128
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
20129
+ for (const wireID of wireIDs) {
20130
+ const info = fresh.models.includes(wireID) ? fresh.modelInfo?.find((model) => model.modelID === wireID) : undefined;
20131
+ if (!hasRequiredModelResidency("lemonade", info))
20132
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${providerID}/${wireID}`));
20133
+ }
20134
+ }
20135
+ }
19499
20136
  async function runSetup(deps) {
19500
20137
  const { prompt } = deps;
19501
20138
  try {
@@ -19507,7 +20144,7 @@ async function runSetup(deps) {
19507
20144
  return {
19508
20145
  value: meta.id,
19509
20146
  label: meta.label,
19510
- hint: info?.reachable ? `detected · ${info.models.length} model(s)` : "not detected (you can point it to another machine)",
20147
+ hint: info?.reachable ? meta.id === "lemonade" ? executionSetupMessages.lemonadeDetectedHint(selectableModelIDs(info.models, info.modelInfo, meta.id).length, info.modelInfo?.length ?? info.models.length) : `detected · ${info.models.length} model(s)` : "not detected (you can point it to another machine)",
19511
20148
  selected: info?.reachable ?? false
19512
20149
  };
19513
20150
  });
@@ -19624,17 +20261,18 @@ async function runSetup(deps) {
19624
20261
  };
19625
20262
  const openTeamConfig = buildOpenTeamConfig(answers);
19626
20263
  const opencodeConfig = buildOpencodeConfig(answers);
20264
+ const opencodeConfigPaths = LEMONADE_PROJECT_CONFIG_CANDIDATES;
19627
20265
  const orchestratorAgent = buildOrchestratorAgent(orchestratorAgentModel(answers), { yolo });
19628
20266
  const slashCommands = buildOpenteamCommands();
19629
20267
  const openTeamPath = DEFAULT_CONFIG_PATH;
19630
- const existingOpencodePath = await deps.resolveOpencodeWriteTarget(OPENCODE_CONFIG_CANDIDATES);
20268
+ const existingOpencodePath = await deps.resolveOpencodeWriteTarget(opencodeConfigPaths);
19631
20269
  const opencodeDataPath = existingOpencodePath ?? OPENCODE_CONFIG_CANDIDATES[0];
19632
20270
  const agentPath = ORCHESTRATOR_AGENT_PATH;
19633
20271
  let existingOpenTeamConfig;
19634
20272
  let existingOpencodeFiles;
19635
20273
  try {
19636
20274
  existingOpenTeamConfig = await readExistingOpenTeamConfig(openTeamPath, deps);
19637
- existingOpencodeFiles = await readExistingOpencodeConfigFiles(OPENCODE_CONFIG_CANDIDATES, deps);
20275
+ existingOpencodeFiles = await readExistingOpencodeConfigFiles(opencodeConfigPaths, deps);
19638
20276
  } catch (error) {
19639
20277
  if (error instanceof ConfigParseError) {
19640
20278
  return {
@@ -19644,7 +20282,29 @@ async function runSetup(deps) {
19644
20282
  }
19645
20283
  throw error;
19646
20284
  }
19647
- const effectiveExistingOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles);
20285
+ assertPreviousLemonadePrimary(answers, existingOpenTeamConfig);
20286
+ const effectiveExistingOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths);
20287
+ const {
20288
+ config: finalOpenTeamConfig,
20289
+ providerIDs: retainedLemonadeProviderIDs
20290
+ } = preserveRetainedLemonadeRuntimes(existingOpenTeamConfig !== undefined ? mergeOpenTeamConfigForRerun(openTeamConfig, existingOpenTeamConfig) : openTeamConfig, existingOpenTeamConfig, effectiveExistingOpencode);
20291
+ assertEffectiveConfigModelDomains(finalOpenTeamConfig);
20292
+ const effectiveOpenTeamConfig = await effectiveSetupConfig(finalOpenTeamConfig, existingOpencodeFiles, deps);
20293
+ assertEffectiveConfigModelDomains(effectiveOpenTeamConfig);
20294
+ for (const providerID of retainedLemonadeProviderIDs) {
20295
+ const matches = effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
20296
+ if (matches.length !== 1 || matches[0] === undefined || localRuntimeKind(matches[0]) !== "lemonade")
20297
+ throw new Error(executionSetupMessages.lemonadeRetainedProviderUnprotected(providerID));
20298
+ }
20299
+ const v2Denials = lemonadeV2Policies([
20300
+ ...opencodeConfig.experimental?.policies.map((policy) => policy.resource) ?? [],
20301
+ ...effectiveOpenTeamConfig.local.runtimes.filter((runtime) => localRuntimeKind(runtime) === "lemonade").map((runtime) => runtime.defaultModel.providerID)
20302
+ ]);
20303
+ if (v2Denials.length > 0)
20304
+ opencodeConfig.experimental = { policies: v2Denials };
20305
+ const v2Notice = v2Denials.length > 0 ? executionSetupMessages.lemonadeV2Restriction(v2Denials.map((policy) => policy.resource)) : undefined;
20306
+ if (v2Notice !== undefined)
20307
+ prompt.note(v2Notice, executionSetupMessages.lemonadeV2Title);
19648
20308
  const relevantProviderIDs = relevantLocalProviderIDs(opencodeConfig, existingOpenTeamConfig);
19649
20309
  for (const file of existingOpencodeFiles) {
19650
20310
  assertRelevantLimitNumbers(file, relevantProviderIDs);
@@ -19673,8 +20333,6 @@ async function runSetup(deps) {
19673
20333
  const pluginOwnerFile = opencodeFileAt(existingOpencodeFiles, pluginOwnerPath);
19674
20334
  const pluginPlan = await mergePluginEntriesForRerun(Array.isArray(pluginOwnerFile?.raw.plugin) ? pluginOwnerFile.raw.plugin : [], pluginOwnerPath, preservePlugin, deps.classifyLocalPluginSpec);
19675
20335
  const includePluginInDataFile = pluginOwnerPath === opencodeDataPath;
19676
- const finalOpenTeamConfig = existingOpenTeamConfig !== undefined ? mergeOpenTeamConfigForRerun(openTeamConfig, existingOpenTeamConfig) : openTeamConfig;
19677
- assertEffectiveConfigModelDomains(finalOpenTeamConfig);
19678
20336
  const dataTargetRaw = opencodeFileAt(existingOpencodeFiles, opencodeDataPath)?.raw ?? {};
19679
20337
  const mergedOpencodeRaw = mergeOpencodeConfigForRerun(opencodeConfig, dataTargetRaw, {
19680
20338
  ...effectiveExistingOpencode !== undefined ? { effectiveRaw: effectiveExistingOpencode } : {},
@@ -19689,7 +20347,7 @@ async function runSetup(deps) {
19689
20347
  providerIDs: relevantProviderIDs
19690
20348
  });
19691
20349
  const opencodeWritePlan = new Map([
19692
- [opencodeDataPath, serializeOpencodeConfig(repairedDataTarget.raw)]
20350
+ [opencodeDataPath, repairedDataTarget.raw]
19693
20351
  ]);
19694
20352
  for (const file of existingOpencodeFiles) {
19695
20353
  if (file.path === opencodeDataPath) {
@@ -19701,14 +20359,14 @@ async function runSetup(deps) {
19701
20359
  generated: opencodeConfig,
19702
20360
  providerIDs: relevantProviderIDs
19703
20361
  });
19704
- let plannedRaw = repaired.raw;
19705
- let changed = repaired.changed;
20362
+ let plannedRaw = reconcileLemonadeV2Policies(repaired.raw, v2Denials);
20363
+ let changed = repaired.changed || plannedRaw !== repaired.raw;
19706
20364
  if (file.path === pluginOwnerPath && !includePluginInDataFile && pluginPlan.changed) {
19707
20365
  plannedRaw = { ...plannedRaw, plugin: [...pluginPlan.plugins] };
19708
20366
  changed = true;
19709
20367
  }
19710
20368
  if (changed) {
19711
- opencodeWritePlan.set(file.path, serializeOpencodeConfig(plannedRaw));
20369
+ opencodeWritePlan.set(file.path, plannedRaw);
19712
20370
  }
19713
20371
  }
19714
20372
  const opencodeGitignorePath = OPENCODE_GITIGNORE_PATH;
@@ -19772,9 +20430,13 @@ async function runSetup(deps) {
19772
20430
  stdout: shouldWriteOpencodeGitignore ? `setup cancelled by user; ${OPENCODE_GITIGNORE_PATH} ensured` : "setup cancelled by user"
19773
20431
  };
19774
20432
  }
20433
+ const effectivePlannedOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths, opencodeWritePlan);
20434
+ if (effectivePlannedOpencode === undefined)
20435
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
20436
+ await verifyLemonadeBeforeWrite(deps, answers, effectiveOpenTeamConfig, effectivePlannedOpencode);
19775
20437
  await deps.writeFile(openTeamPath, serializeOpenTeamConfig(finalOpenTeamConfig));
19776
20438
  for (const [path2, contents] of opencodeWritePlan) {
19777
- await deps.writeFile(path2, contents);
20439
+ await deps.writeFile(path2, serializeOpencodeConfig(contents));
19778
20440
  }
19779
20441
  await deps.writeFile(agentPath, orchestratorAgent);
19780
20442
  for (const command of slashCommands) {
@@ -19805,7 +20467,11 @@ async function runSetup(deps) {
19805
20467
  prompt.outro(executionSetupMessages.done);
19806
20468
  return {
19807
20469
  exitCode: 0,
19808
- stdout: executionSetupMessages.complete(`${writtenOpencodePaths}, ${DEFAULT_CONFIG_PATH}, ${ORCHESTRATOR_AGENT_PATH}, ${OPENTEAM_COMMAND_DIR}/*.md, ${OPENCODE_GITIGNORE_PATH}`)
20470
+ stdout: [
20471
+ executionSetupMessages.complete(`${writtenOpencodePaths}, ${DEFAULT_CONFIG_PATH}, ${ORCHESTRATOR_AGENT_PATH}, ${OPENTEAM_COMMAND_DIR}/*.md, ${OPENCODE_GITIGNORE_PATH}`),
20472
+ ...v2Notice === undefined ? [] : [v2Notice]
20473
+ ].join(`
20474
+ `)
19809
20475
  };
19810
20476
  } catch (error) {
19811
20477
  const message = error instanceof Error ? error.message : String(error);
@@ -20460,7 +21126,7 @@ import {
20460
21126
  import { basename as basename3, dirname as dirname8, join as join19, relative as relative2, resolve as resolve11, win32 } from "node:path";
20461
21127
  import { fileURLToPath } from "node:url";
20462
21128
  import { promisify as promisify2 } from "node:util";
20463
- import { z as z24 } from "zod";
21129
+ import { z as z25 } from "zod";
20464
21130
  var NPM_ORIGIN = "https://registry.npmjs.org";
20465
21131
  var NPM_PACKAGE_PATH = "/@jmanuelcorral%2Fopenteam";
20466
21132
  var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
@@ -20479,9 +21145,9 @@ var UPGRADE_BACKUP_DIRECTORY = ".opencode/openteam-local/upgrade-backups";
20479
21145
  function npmVersionUrl(version) {
20480
21146
  return `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/${version}`;
20481
21147
  }
20482
- var RegistryPackageIdentitySchema = z24.object({
20483
- name: z24.string(),
20484
- version: z24.string()
21148
+ var RegistryPackageIdentitySchema = z25.object({
21149
+ name: z25.string(),
21150
+ version: z25.string()
20485
21151
  }).strict();
20486
21152
 
20487
21153
  class TransientFetchError extends Error {
@@ -22140,12 +22806,19 @@ function defaultModelFromOpencode(opencodeConfig) {
22140
22806
  }
22141
22807
  return;
22142
22808
  }
22143
- function liveModelsFromSnapshots(snapshots) {
22809
+ function liveModelsFromSnapshots(snapshots, config) {
22144
22810
  const map = new Map;
22145
22811
  for (const snapshot of snapshots) {
22146
- map.set(snapshot.id, {
22812
+ const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
22813
+ if (runtime === undefined || !runtime.enabled)
22814
+ continue;
22815
+ const kind = localRuntimeKind(runtime);
22816
+ map.set(runtime.defaultModel.providerID, {
22147
22817
  reachable: snapshot.reachable,
22148
- modelIDs: new Set(snapshot.models.map((model) => model.modelID))
22818
+ modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
22819
+ ...kind === "lemonade" ? {
22820
+ residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
22821
+ } : {}
22149
22822
  });
22150
22823
  }
22151
22824
  return map;
@@ -22431,7 +23104,7 @@ async function runCli(argv, deps) {
22431
23104
  const agentModels = diagnoseAgentModels(agentFiles, {
22432
23105
  opencodeProviders: parseOpencodeProviders(opencodeConfig),
22433
23106
  localRuntimes: localRuntimeStates(config),
22434
- liveModels: liveModelsFromSnapshots(snapshots),
23107
+ liveModels: liveModelsFromSnapshots(snapshots, config),
22435
23108
  sourceDir: deps.agentDir,
22436
23109
  ...defaultModel !== undefined ? { defaultModel } : {}
22437
23110
  });
@@ -22470,29 +23143,53 @@ async function runCli(argv, deps) {
22470
23143
  const rosterMissing = deps.loadRosterForDoctor !== undefined && roster === undefined && rosterLoadError === undefined;
22471
23144
  const orchestratorAgentName = ORCHESTRATOR_AGENT_PATH.slice(ORCHESTRATOR_AGENT_PATH.lastIndexOf("/") + 1).replace(/\.md$/, "");
22472
23145
  const orchestratorAgentMissing = !agentFiles.some((file) => file.name === orchestratorAgentName);
23146
+ const inventoryNotes = [];
23147
+ const readySnapshots = snapshots.map((snapshot) => {
23148
+ const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
23149
+ if (runtime === undefined || localRuntimeKind(runtime) !== "lemonade")
23150
+ return snapshot;
23151
+ inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
23152
+ return {
23153
+ ...snapshot,
23154
+ models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
23155
+ };
23156
+ });
23157
+ const residencyNotes = agentModels.flatMap((agent) => {
23158
+ const live = agent.resolution?.live;
23159
+ if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
23160
+ return [];
23161
+ return [
23162
+ executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
23163
+ ];
23164
+ });
22473
23165
  return {
22474
23166
  exitCode: 0,
22475
- stdout: renderDoctor({
22476
- config,
22477
- snapshots,
22478
- telemetryPath,
22479
- telemetryRecords: records.length,
22480
- agentModels,
22481
- opencodeConfigPaths,
22482
- ...opencodeConfig !== undefined ? { opencodeConfig } : {},
22483
- ...queueWait !== undefined ? { queueWait } : {},
22484
- ...rosterAudit !== undefined ? { rosterAudit } : {},
22485
- ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
22486
- ...rosterLoadError !== undefined ? { rosterLoadError } : {},
22487
- ...rosterMissing ? { rosterMissing } : {},
22488
- ...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
22489
- ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
22490
- ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
22491
- ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
22492
- ...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
22493
- ...otelBackend !== undefined ? { otelBackend } : {},
22494
- ...gitignoreCoverage !== undefined ? { gitignore: gitignoreCoverage } : {}
22495
- })
23167
+ stdout: [
23168
+ renderDoctor({
23169
+ config,
23170
+ snapshots: readySnapshots,
23171
+ telemetryPath,
23172
+ telemetryRecords: records.length,
23173
+ agentModels,
23174
+ opencodeConfigPaths,
23175
+ ...opencodeConfig !== undefined ? { opencodeConfig } : {},
23176
+ ...queueWait !== undefined ? { queueWait } : {},
23177
+ ...rosterAudit !== undefined ? { rosterAudit } : {},
23178
+ ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
23179
+ ...rosterLoadError !== undefined ? { rosterLoadError } : {},
23180
+ ...rosterMissing ? { rosterMissing } : {},
23181
+ ...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
23182
+ ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
23183
+ ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
23184
+ ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
23185
+ ...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
23186
+ ...otelBackend !== undefined ? { otelBackend } : {},
23187
+ ...gitignoreCoverage !== undefined ? { gitignore: gitignoreCoverage } : {}
23188
+ }),
23189
+ ...inventoryNotes,
23190
+ ...residencyNotes
23191
+ ].join(`
23192
+ `)
22496
23193
  };
22497
23194
  }
22498
23195
  if (command === "migrate") {
@@ -22609,14 +23306,14 @@ ${HELP}`
22609
23306
  }
22610
23307
 
22611
23308
  // src/orchestrator/roster.ts
22612
- import { z as z25 } from "zod";
22613
- var RosterEntrySchema = z25.object({
22614
- roleID: z25.string().min(1),
22615
- agentName: z25.string().min(1)
23309
+ import { z as z26 } from "zod";
23310
+ var RosterEntrySchema = z26.object({
23311
+ roleID: z26.string().min(1),
23312
+ agentName: z26.string().min(1)
22616
23313
  }).strict();
22617
- var RosterSchema = z25.object({
22618
- universe: z25.string().min(1),
22619
- entries: z25.array(RosterEntrySchema)
23314
+ var RosterSchema = z26.object({
23315
+ universe: z26.string().min(1),
23316
+ entries: z26.array(RosterEntrySchema)
22620
23317
  }).strict();
22621
23318
  var ROSTER_JSON_FENCE = /```json\s*([\s\S]*?)```/;
22622
23319
 
@@ -23170,7 +23867,7 @@ import { readFile as readFile4 } from "node:fs/promises";
23170
23867
  import { basename as basename4, dirname as dirname9 } from "node:path";
23171
23868
 
23172
23869
  // src/graph/certificate.ts
23173
- import { z as z26 } from "zod";
23870
+ import { z as z27 } from "zod";
23174
23871
 
23175
23872
  // src/contract/opencode.ts
23176
23873
  var SUPPORTED_OPENCODE_VERSIONS = [
@@ -23211,7 +23908,7 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
23211
23908
 
23212
23909
  // src/graph/certificate.ts
23213
23910
  var SHADOW_CERTIFICATE_MIN_RUNS = 1000;
23214
- var DivergenceCodeSchema = z26.enum([
23911
+ var DivergenceCodeSchema = z27.enum([
23215
23912
  "status",
23216
23913
  "order",
23217
23914
  "roles",
@@ -23219,50 +23916,50 @@ var DivergenceCodeSchema = z26.enum([
23219
23916
  "fixes",
23220
23917
  "outcome"
23221
23918
  ]);
23222
- var ProvenanceSchema = z26.enum([
23919
+ var ProvenanceSchema = z27.enum([
23223
23920
  "recomputed",
23224
23921
  "compiled",
23225
23922
  "validated",
23226
23923
  "carried"
23227
23924
  ]);
23228
- var EvidenceSchema = z26.object({
23229
- opencodeVersion: z26.string().min(1),
23230
- parity: z26.object({
23231
- runs: z26.number().int().nonnegative(),
23232
- match: z26.number().int().nonnegative(),
23233
- divergent: z26.number().int().nonnegative(),
23234
- inconclusive: z26.number().int().nonnegative(),
23235
- divergences: z26.array(DivergenceCodeSchema)
23925
+ var EvidenceSchema = z27.object({
23926
+ opencodeVersion: z27.string().min(1),
23927
+ parity: z27.object({
23928
+ runs: z27.number().int().nonnegative(),
23929
+ match: z27.number().int().nonnegative(),
23930
+ divergent: z27.number().int().nonnegative(),
23931
+ inconclusive: z27.number().int().nonnegative(),
23932
+ divergences: z27.array(DivergenceCodeSchema)
23236
23933
  }).strict(),
23237
- legacy: z26.object({
23238
- observed: z26.number().int().nonnegative(),
23239
- duplicated: z26.number().int().nonnegative()
23934
+ legacy: z27.object({
23935
+ observed: z27.number().int().nonnegative(),
23936
+ duplicated: z27.number().int().nonnegative()
23240
23937
  }).strict(),
23241
- privacy: z26.object({
23242
- surfacesScanned: z26.number().int().nonnegative(),
23243
- rawFindings: z26.number().int().nonnegative()
23938
+ privacy: z27.object({
23939
+ surfacesScanned: z27.number().int().nonnegative(),
23940
+ rawFindings: z27.number().int().nonnegative()
23244
23941
  }).strict(),
23245
- overhead: z26.object({
23246
- p95Millis: z26.number().nonnegative(),
23247
- budgetMillis: z26.number().positive(),
23248
- samples: z26.number().int().nonnegative()
23942
+ overhead: z27.object({
23943
+ p95Millis: z27.number().nonnegative(),
23944
+ budgetMillis: z27.number().positive(),
23945
+ samples: z27.number().int().nonnegative()
23249
23946
  }).strict(),
23250
- provenance: z26.record(DivergenceCodeSchema, ProvenanceSchema)
23947
+ provenance: z27.record(DivergenceCodeSchema, ProvenanceSchema)
23251
23948
  }).strict();
23252
- var CertificateSchema = z26.object({
23253
- version: z26.literal(1),
23254
- certificate: z26.literal("graph-shadow"),
23255
- opencodeVersion: z26.string().min(1),
23949
+ var CertificateSchema = z27.object({
23950
+ version: z27.literal(1),
23951
+ certificate: z27.literal("graph-shadow"),
23952
+ opencodeVersion: z27.string().min(1),
23256
23953
  evidence: EvidenceSchema,
23257
- verdict: z26.enum(["pass", "fail"]),
23258
- failedGates: z26.array(z26.enum([
23954
+ verdict: z27.enum(["pass", "fail"]),
23955
+ failedGates: z27.array(z27.enum([
23259
23956
  "sample",
23260
23957
  "parity",
23261
23958
  "one-legacy-execution",
23262
23959
  "privacy",
23263
23960
  "overhead"
23264
23961
  ])),
23265
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
23962
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23266
23963
  }).strict();
23267
23964
 
23268
23965
  class ShadowCertificateError extends Error {
@@ -23377,37 +24074,37 @@ function parseShadowCertificate(value, opencodeVersion) {
23377
24074
  }
23378
24075
  return certificate;
23379
24076
  }
23380
- var ReleaseDeterminismSchema = z26.object({ runs: z26.number().int().nonnegative(), allMatch: z26.boolean() }).strict();
23381
- var ReleaseCrashRecoverySchema = z26.object({
23382
- scenarios: z26.number().int().nonnegative(),
23383
- allConverge: z26.boolean()
24077
+ var ReleaseDeterminismSchema = z27.object({ runs: z27.number().int().nonnegative(), allMatch: z27.boolean() }).strict();
24078
+ var ReleaseCrashRecoverySchema = z27.object({
24079
+ scenarios: z27.number().int().nonnegative(),
24080
+ allConverge: z27.boolean()
23384
24081
  }).strict();
23385
- var ReleaseReplayEquivalenceSchema = z26.object({
23386
- checks: z26.number().int().nonnegative(),
23387
- allEquivalent: z26.boolean()
24082
+ var ReleaseReplayEquivalenceSchema = z27.object({
24083
+ checks: z27.number().int().nonnegative(),
24084
+ allEquivalent: z27.boolean()
23388
24085
  }).strict();
23389
- var ReleasePrivacySchema = z26.object({
23390
- surfacesScanned: z26.number().int().nonnegative(),
23391
- rawFindings: z26.number().int().nonnegative()
24086
+ var ReleasePrivacySchema = z27.object({
24087
+ surfacesScanned: z27.number().int().nonnegative(),
24088
+ rawFindings: z27.number().int().nonnegative()
23392
24089
  }).strict();
23393
- var ReleaseContractSchema = z26.object({
23394
- roundTrips: z26.number().int().nonnegative(),
23395
- allSettled: z26.boolean()
24090
+ var ReleaseContractSchema = z27.object({
24091
+ roundTrips: z27.number().int().nonnegative(),
24092
+ allSettled: z27.boolean()
23396
24093
  }).strict();
23397
- var ReleaseHealthSchema = z26.object({
23398
- samples: z26.number().int().nonnegative(),
23399
- bounded: z26.boolean(),
23400
- idempotent: z26.boolean()
24094
+ var ReleaseHealthSchema = z27.object({
24095
+ samples: z27.number().int().nonnegative(),
24096
+ bounded: z27.boolean(),
24097
+ idempotent: z27.boolean()
23401
24098
  }).strict();
23402
- var ReleasePerformanceSchema = z26.object({
23403
- p95Millis: z26.number().nonnegative(),
23404
- budgetMillis: z26.number().positive(),
23405
- samples: z26.number().int().nonnegative()
24099
+ var ReleasePerformanceSchema = z27.object({
24100
+ p95Millis: z27.number().nonnegative(),
24101
+ budgetMillis: z27.number().positive(),
24102
+ samples: z27.number().int().nonnegative()
23406
24103
  }).strict();
23407
- var ReleaseEvidenceSchema = z26.object({
23408
- opencodeVersion: z26.string().min(1),
23409
- platform: z26.enum(["linux", "win32"]),
23410
- shadowCertificateDigest: z26.string().regex(/^[0-9a-f]{64}$/),
24104
+ var ReleaseEvidenceSchema = z27.object({
24105
+ opencodeVersion: z27.string().min(1),
24106
+ platform: z27.enum(["linux", "win32"]),
24107
+ shadowCertificateDigest: z27.string().regex(/^[0-9a-f]{64}$/),
23411
24108
  determinism: ReleaseDeterminismSchema,
23412
24109
  crashRecovery: ReleaseCrashRecoverySchema,
23413
24110
  replayEquivalence: ReleaseReplayEquivalenceSchema,
@@ -23416,15 +24113,15 @@ var ReleaseEvidenceSchema = z26.object({
23416
24113
  health: ReleaseHealthSchema,
23417
24114
  performance: ReleasePerformanceSchema
23418
24115
  }).strict();
23419
- var ReleaseCertificateSchema = z26.object({
23420
- version: z26.literal(2),
23421
- certificate: z26.literal("graph-release"),
23422
- platform: z26.enum(["linux", "win32"]),
23423
- opencodeVersion: z26.string().min(1),
23424
- shadowCertificateDigest: z26.string().regex(/^[0-9a-f]{64}$/),
24116
+ var ReleaseCertificateSchema = z27.object({
24117
+ version: z27.literal(2),
24118
+ certificate: z27.literal("graph-release"),
24119
+ platform: z27.enum(["linux", "win32"]),
24120
+ opencodeVersion: z27.string().min(1),
24121
+ shadowCertificateDigest: z27.string().regex(/^[0-9a-f]{64}$/),
23425
24122
  evidence: ReleaseEvidenceSchema,
23426
- verdict: z26.enum(["pass", "fail"]),
23427
- failedGates: z26.array(z26.enum([
24123
+ verdict: z27.enum(["pass", "fail"]),
24124
+ failedGates: z27.array(z27.enum([
23428
24125
  "shadow-valid",
23429
24126
  "determinism",
23430
24127
  "crash-recovery",
@@ -23434,7 +24131,7 @@ var ReleaseCertificateSchema = z26.object({
23434
24131
  "health",
23435
24132
  "performance"
23436
24133
  ])),
23437
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
24134
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23438
24135
  }).strict();
23439
24136
 
23440
24137
  class ReleaseCertificateError extends Error {
@@ -23562,7 +24259,7 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
23562
24259
  }
23563
24260
  return certificate;
23564
24261
  }
23565
- var ShadowDivergenceCodeSchema = z26.enum([
24262
+ var ShadowDivergenceCodeSchema = z27.enum([
23566
24263
  "status",
23567
24264
  "order",
23568
24265
  "roles",
@@ -23571,13 +24268,13 @@ var ShadowDivergenceCodeSchema = z26.enum([
23571
24268
  "outcome"
23572
24269
  ]);
23573
24270
  var RECOMPUTED_DIVERGENCE_CODES = ["fixes"];
23574
- var RecomputedDivergenceCodeSchema = z26.enum(RECOMPUTED_DIVERGENCE_CODES);
23575
- var SoakPolicySchema = z26.object({
23576
- minimumGenuineObservations: z26.number().int().positive(),
23577
- minimumDistinctDays: z26.number().int().positive(),
23578
- requireGenuineUsageOnAllPlatforms: z26.boolean(),
23579
- supportedOpencodeVersions: z26.array(z26.string().min(1)).min(1),
23580
- criticalDivergenceCodes: z26.array(RecomputedDivergenceCodeSchema).min(1)
24271
+ var RecomputedDivergenceCodeSchema = z27.enum(RECOMPUTED_DIVERGENCE_CODES);
24272
+ var SoakPolicySchema = z27.object({
24273
+ minimumGenuineObservations: z27.number().int().positive(),
24274
+ minimumDistinctDays: z27.number().int().positive(),
24275
+ requireGenuineUsageOnAllPlatforms: z27.boolean(),
24276
+ supportedOpencodeVersions: z27.array(z27.string().min(1)).min(1),
24277
+ criticalDivergenceCodes: z27.array(RecomputedDivergenceCodeSchema).min(1)
23581
24278
  }).strict();
23582
24279
  var DEFAULT_SOAK_POLICY = {
23583
24280
  minimumGenuineObservations: 100,
@@ -23586,46 +24283,46 @@ var DEFAULT_SOAK_POLICY = {
23586
24283
  supportedOpencodeVersions: [...SUPPORTED_OPENCODE_VERSIONS],
23587
24284
  criticalDivergenceCodes: ["fixes"]
23588
24285
  };
23589
- var SoakPlatformEnumSchema = z26.enum(["linux", "win32", "darwin"]);
23590
- var SoakPrivacyPairEvidenceSchema = z26.object({
23591
- surfacesScanned: z26.number().int().nonnegative(),
23592
- rawFindings: z26.number().int().nonnegative()
24286
+ var SoakPlatformEnumSchema = z27.enum(["linux", "win32", "darwin"]);
24287
+ var SoakPrivacyPairEvidenceSchema = z27.object({
24288
+ surfacesScanned: z27.number().int().nonnegative(),
24289
+ rawFindings: z27.number().int().nonnegative()
23593
24290
  }).strict();
23594
- var SoakDuplicateEffectPairEvidenceSchema = z26.object({
23595
- effectsExamined: z26.number().int().nonnegative(),
23596
- duplicatesFound: z26.number().int().nonnegative()
24291
+ var SoakDuplicateEffectPairEvidenceSchema = z27.object({
24292
+ effectsExamined: z27.number().int().nonnegative(),
24293
+ duplicatesFound: z27.number().int().nonnegative()
23597
24294
  }).strict();
23598
- var SoakModelVerificationPairEvidenceSchema = z26.object({
23599
- nodesChecked: z26.number().int().nonnegative(),
23600
- unverified: z26.number().int().nonnegative(),
23601
- mismatched: z26.number().int().nonnegative()
24295
+ var SoakModelVerificationPairEvidenceSchema = z27.object({
24296
+ nodesChecked: z27.number().int().nonnegative(),
24297
+ unverified: z27.number().int().nonnegative(),
24298
+ mismatched: z27.number().int().nonnegative()
23602
24299
  }).strict();
23603
- var SoakEvidenceSchema = z26.object({
23604
- totalObservations: z26.number().int().nonnegative(),
23605
- genuineUsageObservations: z26.number().int().nonnegative(),
23606
- ciSyntheticObservations: z26.number().int().nonnegative(),
23607
- recorderCount: z26.number().int().nonnegative(),
23608
- ineligibleObservations: z26.number().int().nonnegative(),
23609
- firstTimestamp: z26.string().datetime().nullable(),
23610
- lastTimestamp: z26.string().datetime().nullable(),
23611
- distinctDays: z26.number().int().nonnegative(),
23612
- criticalDivergences: z26.number().int().nonnegative().nullable(),
24300
+ var SoakEvidenceSchema = z27.object({
24301
+ totalObservations: z27.number().int().nonnegative(),
24302
+ genuineUsageObservations: z27.number().int().nonnegative(),
24303
+ ciSyntheticObservations: z27.number().int().nonnegative(),
24304
+ recorderCount: z27.number().int().nonnegative(),
24305
+ ineligibleObservations: z27.number().int().nonnegative(),
24306
+ firstTimestamp: z27.string().datetime().nullable(),
24307
+ lastTimestamp: z27.string().datetime().nullable(),
24308
+ distinctDays: z27.number().int().nonnegative(),
24309
+ criticalDivergences: z27.number().int().nonnegative().nullable(),
23613
24310
  privacy: SoakPrivacyPairEvidenceSchema.nullable(),
23614
24311
  duplicateEffects: SoakDuplicateEffectPairEvidenceSchema.nullable(),
23615
24312
  modelVerification: SoakModelVerificationPairEvidenceSchema.nullable(),
23616
- platformsGenuine: z26.array(SoakPlatformEnumSchema),
23617
- platformsSynthetic: z26.array(SoakPlatformEnumSchema),
23618
- opencodeVersionsObserved: z26.array(z26.string().min(1)),
23619
- invalidChains: z26.number().int().nonnegative()
24313
+ platformsGenuine: z27.array(SoakPlatformEnumSchema),
24314
+ platformsSynthetic: z27.array(SoakPlatformEnumSchema),
24315
+ opencodeVersionsObserved: z27.array(z27.string().min(1)),
24316
+ invalidChains: z27.number().int().nonnegative()
23620
24317
  }).strict();
23621
- var SoakCertificateSchema = z26.object({
23622
- version: z26.literal(1),
23623
- certificate: z26.literal("graph-soak"),
23624
- trustBoundary: z26.literal("contributors"),
24318
+ var SoakCertificateSchema = z27.object({
24319
+ version: z27.literal(1),
24320
+ certificate: z27.literal("graph-soak"),
24321
+ trustBoundary: z27.literal("contributors"),
23625
24322
  policy: SoakPolicySchema,
23626
24323
  evidence: SoakEvidenceSchema,
23627
- verdict: z26.enum(["pass", "fail"]),
23628
- failedGates: z26.array(z26.enum([
24324
+ verdict: z27.enum(["pass", "fail"]),
24325
+ failedGates: z27.array(z27.enum([
23629
24326
  "minimum-observations",
23630
24327
  "minimum-duration",
23631
24328
  "chain-integrity",
@@ -23636,8 +24333,8 @@ var SoakCertificateSchema = z26.object({
23636
24333
  "os-diversity",
23637
24334
  "opencode-pin"
23638
24335
  ])),
23639
- ledgerDigest: z26.string().regex(/^[0-9a-f]{64}$/),
23640
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
24336
+ ledgerDigest: z27.string().regex(/^[0-9a-f]{64}$/),
24337
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23641
24338
  }).strict();
23642
24339
 
23643
24340
  class SoakCertificateError extends Error {
@@ -24285,62 +24982,62 @@ var realCacheAdapter = {
24285
24982
  };
24286
24983
 
24287
24984
  // src/memory/types.ts
24288
- import { z as z27 } from "zod";
24985
+ import { z as z28 } from "zod";
24289
24986
  var SHARED_OWNER_KEY = "*";
24290
- var OwnerKeySchema = z27.string().min(1);
24291
- var MemoryKindSchema = z27.enum(["fact", "preference", "entity"]);
24292
- var MemoryBaseSchema = z27.object({
24293
- id: z27.string().min(1),
24987
+ var OwnerKeySchema = z28.string().min(1);
24988
+ var MemoryKindSchema = z28.enum(["fact", "preference", "entity"]);
24989
+ var MemoryBaseSchema = z28.object({
24990
+ id: z28.string().min(1),
24294
24991
  ownerKey: OwnerKeySchema,
24295
- confidence: z27.number().min(0).max(1),
24296
- validFrom: z27.number().finite(),
24297
- validUntil: z27.number().finite().nullable().default(null),
24298
- createdAt: z27.number().finite(),
24299
- invalidatedAt: z27.number().finite().nullable().default(null),
24300
- sourceHash: z27.string().min(1),
24301
- supersededBy: z27.string().min(1).nullable().default(null)
24992
+ confidence: z28.number().min(0).max(1),
24993
+ validFrom: z28.number().finite(),
24994
+ validUntil: z28.number().finite().nullable().default(null),
24995
+ createdAt: z28.number().finite(),
24996
+ invalidatedAt: z28.number().finite().nullable().default(null),
24997
+ sourceHash: z28.string().min(1),
24998
+ supersededBy: z28.string().min(1).nullable().default(null)
24302
24999
  }).strict();
24303
25000
  var FactSchema = MemoryBaseSchema.extend({
24304
- kind: z27.literal("fact"),
24305
- subject: z27.string().min(1),
24306
- predicate: z27.string().min(1),
24307
- object: z27.string().min(1),
24308
- category: z27.string().min(1).nullable().default(null)
25001
+ kind: z28.literal("fact"),
25002
+ subject: z28.string().min(1),
25003
+ predicate: z28.string().min(1),
25004
+ object: z28.string().min(1),
25005
+ category: z28.string().min(1).nullable().default(null)
24309
25006
  });
24310
25007
  var PreferenceSchema = MemoryBaseSchema.extend({
24311
- kind: z27.literal("preference"),
24312
- category: z27.string().min(1),
24313
- preference: z27.string().min(1),
24314
- context: z27.string().min(1).nullable().default(null),
24315
- lastAccessedAt: z27.number().finite().nullable().default(null),
24316
- accessCount: z27.number().int().min(0).default(0)
25008
+ kind: z28.literal("preference"),
25009
+ category: z28.string().min(1),
25010
+ preference: z28.string().min(1),
25011
+ context: z28.string().min(1).nullable().default(null),
25012
+ lastAccessedAt: z28.number().finite().nullable().default(null),
25013
+ accessCount: z28.number().int().min(0).default(0)
24317
25014
  });
24318
25015
  var EntitySchema = MemoryBaseSchema.extend({
24319
- kind: z27.literal("entity"),
24320
- canonicalName: z27.string().min(1),
24321
- type: z27.string().min(1),
24322
- aliases: z27.array(z27.string().min(1)).default([])
25016
+ kind: z28.literal("entity"),
25017
+ canonicalName: z28.string().min(1),
25018
+ type: z28.string().min(1),
25019
+ aliases: z28.array(z28.string().min(1)).default([])
24323
25020
  });
24324
25021
  var RelationSchema = MemoryBaseSchema.extend({
24325
- kind: z27.literal("relation"),
24326
- from: z27.string().min(1),
24327
- to: z27.string().min(1),
24328
- predicate: z27.string().min(1),
24329
- annotation: z27.string().min(1).nullable().default(null)
25022
+ kind: z28.literal("relation"),
25023
+ from: z28.string().min(1),
25024
+ to: z28.string().min(1),
25025
+ predicate: z28.string().min(1),
25026
+ annotation: z28.string().min(1).nullable().default(null)
24330
25027
  });
24331
- var MemoryRecordSchema = z27.discriminatedUnion("kind", [
25028
+ var MemoryRecordSchema = z28.discriminatedUnion("kind", [
24332
25029
  FactSchema,
24333
25030
  PreferenceSchema,
24334
25031
  EntitySchema,
24335
25032
  RelationSchema
24336
25033
  ]);
24337
- var RecallQuerySchema = z27.object({
25034
+ var RecallQuerySchema = z28.object({
24338
25035
  ownerKey: OwnerKeySchema,
24339
- kinds: z27.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
24340
- asOf: z27.number().finite().nullable().default(null),
24341
- limit: z27.number().int().positive().default(8),
24342
- minSimilarity: z27.number().min(0).max(1).default(0.2),
24343
- includeShared: z27.boolean().default(true)
25036
+ kinds: z28.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
25037
+ asOf: z28.number().finite().nullable().default(null),
25038
+ limit: z28.number().int().positive().default(8),
25039
+ minSimilarity: z28.number().min(0).max(1).default(0.2),
25040
+ includeShared: z28.boolean().default(true)
24344
25041
  }).strict();
24345
25042
 
24346
25043
  // src/memory/rank.ts