@oar-sh/oar 0.9.23 → 0.9.25

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 (110) hide show
  1. package/.github/extensions/web-relay/skills/reasoning-stream.mjs +3 -1
  2. package/CHANGELOG.md +307 -0
  3. package/README.md +12 -11
  4. package/bin/oar.js +54 -4
  5. package/package.json +11 -7
  6. package/server/agent-sdk-loader.mjs +116 -0
  7. package/server/agent-sdks/claude/entry.mjs +4 -0
  8. package/server/agent-sdks/claude/package-lock.json +1438 -0
  9. package/server/agent-sdks/claude/package.json +13 -0
  10. package/server/agent-sdks/cursor/entry-sqlite.mjs +2 -0
  11. package/server/agent-sdks/cursor/entry.mjs +3 -0
  12. package/server/agent-sdks/cursor/package-lock.json +171 -0
  13. package/server/agent-sdks/cursor/package.json +11 -0
  14. package/server/agent-sdks/koffi/entry.cjs +2 -0
  15. package/server/agent-sdks/koffi/package-lock.json +368 -0
  16. package/server/agent-sdks/koffi/package.json +10 -0
  17. package/server/claude-worker/claude-preview-tool.mjs +5 -2
  18. package/server/claude-worker/claude-remote-relay-tool.mjs +20 -2
  19. package/server/claude-worker/claude-sdk-adapter.mjs +4 -1
  20. package/server/claude-worker/sdk-message-normalizer.mjs +29 -1
  21. package/server/copilot-worker/copilot-sdk-session-process.mjs +13 -8
  22. package/server/cursor-worker/cursor-sdk-adapter.mjs +5 -3
  23. package/server/cursor-worker/sdk-message-normalizer.mjs +13 -0
  24. package/server/db-schema.mjs +19 -0
  25. package/server/grok-worker/sdk-message-normalizer.mjs +3 -0
  26. package/server/migrations/0039-media-index.mjs +167 -0
  27. package/server/migrations/0040-image-session-defaults.mjs +93 -0
  28. package/server/public/app/agent-sdk-ui.js +349 -0
  29. package/server/public/app/api-client.js +149 -8
  30. package/server/public/app/ask-user-view.js +2 -1
  31. package/server/public/app/attachments-view.js +66 -9
  32. package/server/public/app/bootstrap.js +289 -94
  33. package/server/public/app/chat-actions-menu-state.mjs +1 -0
  34. package/server/public/app/context-media-tab.js +143 -0
  35. package/server/public/app/conversation-view.js +90 -5
  36. package/server/public/app/image-composer-ui.js +518 -0
  37. package/server/public/app/image-turn-placeholder.mjs +233 -0
  38. package/server/public/app/journal-view.js +12 -2
  39. package/server/public/app/media-gallery-model.mjs +202 -0
  40. package/server/public/app/media-gallery-ui.js +524 -0
  41. package/server/public/app/media-view.js +79 -0
  42. package/server/public/app/model-refresh-ui.mjs +413 -0
  43. package/server/public/app/no-conversation-state.mjs +5 -2
  44. package/server/public/app/openai-image-composer.mjs +492 -0
  45. package/server/public/app/openai-image-cost-ui.js +426 -0
  46. package/server/public/app/openai-image-spend.mjs +443 -0
  47. package/server/public/app/plan-usage-view.mjs +38 -6
  48. package/server/public/app/provider-readiness.mjs +67 -4
  49. package/server/public/app/relay-error-ctas.mjs +5 -0
  50. package/server/public/app/session-history-ui.js +2 -0
  51. package/server/public/app/settings-modal.js +171 -0
  52. package/server/public/app/socket-handlers.js +47 -0
  53. package/server/public/app/tab-strip-fade.mjs +1 -1
  54. package/server/public/app/viewer-gallery.mjs +23 -1
  55. package/server/public/index.html +478 -9
  56. package/server/relay-tools.md +1 -0
  57. package/server/repositories/image-conversation-repository.mjs +69 -0
  58. package/server/repositories/media-repository.mjs +305 -0
  59. package/server/routes/agent-sdk-routes.mjs +73 -0
  60. package/server/routes/media-routes.mjs +361 -0
  61. package/server/routes/messages-routes.mjs +1467 -343
  62. package/server/routes/sessions-routes.mjs +596 -123
  63. package/server/server-runtime.mjs +414 -81
  64. package/server/services/about-service.mjs +26 -6
  65. package/server/services/agent-sdk-launch-gate.mjs +124 -0
  66. package/server/services/agent-sdk-paths.mjs +307 -0
  67. package/server/services/agent-sdk-store.mjs +1211 -0
  68. package/server/services/claude-api-settings-service.mjs +54 -17
  69. package/server/services/cli-install-service.mjs +3 -2
  70. package/server/services/copilot-model-discovery-service.mjs +7 -1
  71. package/server/services/database-file.mjs +23 -5
  72. package/server/services/deepseek-settings-service.mjs +58 -20
  73. package/server/services/earlier-database-service.mjs +4 -2
  74. package/server/services/generated-image-cleanup-service.mjs +2 -1
  75. package/server/services/generated-image-files.mjs +110 -0
  76. package/server/services/image-input-paths.mjs +389 -0
  77. package/server/services/image-operation-service.mjs +6 -2
  78. package/server/services/media-extract.mjs +653 -0
  79. package/server/services/media-index-service.mjs +221 -0
  80. package/server/services/model-refresh-scheduler.mjs +153 -0
  81. package/server/services/model-variant-catalog-service.mjs +141 -9
  82. package/server/services/oar-cli-helpers.mjs +72 -9
  83. package/server/services/oar-state-migration-service.mjs +2 -12
  84. package/server/services/oar-state-root.mjs +17 -0
  85. package/server/services/openai-image-preview-turns.mjs +330 -0
  86. package/server/services/openai-image-spend-service.mjs +516 -0
  87. package/server/services/openai-image-stream.mjs +170 -0
  88. package/server/services/plan-usage-openai-image.mjs +148 -0
  89. package/server/services/provider-model-refresh.mjs +326 -0
  90. package/server/services/provider-model-state.mjs +395 -0
  91. package/server/services/relay-singleton-guard.mjs +72 -10
  92. package/server/services/remote-relay-caller-context.mjs +6 -0
  93. package/server/services/remote-relay-dispatcher.mjs +386 -22
  94. package/server/services/remote-relay-registry-service.mjs +3 -1
  95. package/server/services/session-worker-launch-service.mjs +13 -0
  96. package/server/services/session-worker-supervisor-service.mjs +19 -1
  97. package/server/services/session-worker-websocket-service.mjs +15 -0
  98. package/server/services/token-usage-service.mjs +15 -0
  99. package/server/services/update-install-service.mjs +2 -2
  100. package/server/services/worker-launch-note-service.mjs +14 -2
  101. package/server/services/worker-recycle-service.mjs +145 -11
  102. package/shared/image-header.mjs +90 -0
  103. package/shared/model-refresh-schedule.mjs +120 -0
  104. package/shared/openai-image-options.mjs +299 -0
  105. package/shared/openai-image-presets.mjs +175 -0
  106. package/shared/openai-image-previews.mjs +40 -0
  107. package/shared/openai-image-prices.mjs +291 -0
  108. package/shared/openai-image-spend.mjs +90 -0
  109. package/shared/remote-relay-contract.mjs +126 -4
  110. package/shared/worker-runtime/direct-openai-image.mjs +98 -48
@@ -81,6 +81,8 @@ import {
81
81
  galleryNeighborIndex,
82
82
  galleryNeighbors,
83
83
  galleryPositionLabel,
84
+ galleryCanStep,
85
+ extendGallery,
84
86
  } from './viewer-gallery.mjs';
85
87
  import {
86
88
  deepestExistingAncestor,
@@ -672,9 +674,10 @@ function updateFilePreviewUiState() {
672
674
  if (bottomChrome) {
673
675
  bottomChrome.hidden = !gallery;
674
676
  document.getElementById('file-preview-position').textContent = galleryPositionLabel(gallery);
675
- document.getElementById('file-preview-prev').disabled = galleryNeighborIndex(gallery, -1) === -1;
676
- document.getElementById('file-preview-next').disabled = galleryNeighborIndex(gallery, 1) === -1;
677
+ document.getElementById('file-preview-prev').disabled = !galleryCanStep(gallery, -1);
678
+ document.getElementById('file-preview-next').disabled = !galleryCanStep(gallery, 1);
677
679
  }
680
+ renderGalleryDetails(gallery);
678
681
  }
679
682
 
680
683
  /**
@@ -1231,7 +1234,7 @@ export function openUploadedAttachmentViewer(name, contentUrl, mimeType, options
1231
1234
  kind: uploadPreviewKind(mimeType),
1232
1235
  name: String(name || 'attachment'),
1233
1236
  rawUrl: String(contentUrl || ''),
1234
- size: 0,
1237
+ size: Number(options?.size) > 0 ? Number(options.size) : 0,
1235
1238
  contentType: String(mimeType || '').toLowerCase(),
1236
1239
  },
1237
1240
  });
@@ -1249,7 +1252,7 @@ export function openGalleryItem(item, gallery) {
1249
1252
  if (!item) return Promise.resolve(false);
1250
1253
  const source = item.source || gallery?.source || 'workspace';
1251
1254
  if (source === 'upload') {
1252
- openUploadedAttachmentViewer(item.name, item.href, item.mime, { gallery });
1255
+ openUploadedAttachmentViewer(item.name, item.href, item.mime, { gallery, size: item.size });
1253
1256
  return Promise.resolve(true);
1254
1257
  }
1255
1258
  const open = source === 'workspace' ? openWorkspaceFilePreview : openDriveFilePreview;
@@ -1335,7 +1338,9 @@ export function renderFilePreview() {
1335
1338
  const kindLabel = String(payload.kind || 'text').toUpperCase();
1336
1339
  const langLabel = payload.language ? ` · ${payload.language}` : '';
1337
1340
  const truncatedLabel = payload.truncated ? ` · truncated to ${formatBytes(FILE_PREVIEW_MAX_BYTES)}` : '';
1338
- metaEl.textContent = `${kindLabel}${langLabel} · ${formatBytes(payload.size || 0)}${truncatedLabel}`;
1341
+ // An attachment opened by its address may come without a size: no "0 B" then.
1342
+ const sizeLabel = payload.size > 0 || filePreviewState.source !== 'upload' ? ` · ${formatBytes(payload.size || 0)}` : '';
1343
+ metaEl.textContent = `${kindLabel}${langLabel}${sizeLabel}${truncatedLabel}`;
1339
1344
 
1340
1345
  const cardMeta = payload.size > 0 ? formatBytes(payload.size) : '';
1341
1346
 
@@ -1560,13 +1565,65 @@ export async function openWorkspaceFilePreviewFromRepo(rawPath, options = {}) {
1560
1565
 
1561
1566
  /** Show the file `delta` steps along the gallery; false at either end. */
1562
1567
  export async function stepFilePreviewGallery(delta) {
1563
- const gallery = filePreviewState.gallery;
1564
- const index = galleryNeighborIndex(gallery, delta);
1568
+ let gallery = filePreviewState.gallery;
1569
+ let index = galleryNeighborIndex(gallery, delta);
1570
+ if (index === -1 && galleryCanStep(gallery, delta)) {
1571
+ // A paged gallery: the next page is fetched and the step goes on into it.
1572
+ let page = null;
1573
+ try {
1574
+ page = await gallery.loadMore();
1575
+ } catch {
1576
+ page = null;
1577
+ }
1578
+ if (filePreviewState.gallery !== gallery) return false;
1579
+ gallery = extendGallery(gallery, page || { items: [], hasMore: false });
1580
+ filePreviewState.gallery = gallery;
1581
+ index = galleryNeighborIndex(gallery, delta);
1582
+ if (index === -1) {
1583
+ renderFilePreview();
1584
+ return false;
1585
+ }
1586
+ }
1565
1587
  if (index === -1) return false;
1566
1588
  const next = galleryAt(gallery, index);
1567
1589
  return openGalleryItem(next.items[index], next);
1568
1590
  }
1569
1591
 
1592
+ // What a gallery says about its open item (the media gallery: session,
1593
+ // date, who sent it) and the buttons it adds ("Open in session", "Copy
1594
+ // path"), built here so the viewer markup stays the same for every gallery.
1595
+ function renderGalleryDetails(gallery) {
1596
+ const toolbar = document.querySelector('#file-preview-chrome-top .file-preview-toolbar');
1597
+ let caption = document.getElementById('file-preview-gallery-caption');
1598
+ if (!caption && toolbar) {
1599
+ caption = document.createElement('div');
1600
+ caption.id = 'file-preview-gallery-caption';
1601
+ caption.className = 'file-preview-meta';
1602
+ toolbar.insertAdjacentElement('afterend', caption);
1603
+ }
1604
+ document.querySelectorAll('[data-gallery-action]').forEach((button) => button.remove());
1605
+ const item = gallery?.items?.[gallery.index];
1606
+ const details = item && typeof gallery.describe === 'function' ? gallery.describe(item) : null;
1607
+ if (caption) {
1608
+ caption.textContent = details?.caption || '';
1609
+ caption.hidden = !details?.caption;
1610
+ }
1611
+ if (!toolbar || !Array.isArray(details?.actions)) return;
1612
+ const anchor = document.getElementById('file-preview-copy-btn');
1613
+ for (const action of details.actions) {
1614
+ const button = document.createElement('button');
1615
+ button.type = 'button';
1616
+ button.className = 'file-preview-btn';
1617
+ button.textContent = action.label;
1618
+ button.dataset.galleryAction = action.id;
1619
+ button.addEventListener('click', () => {
1620
+ viewerControls.interaction();
1621
+ void action.run(item);
1622
+ });
1623
+ toolbar.insertBefore(button, anchor || null);
1624
+ }
1625
+ }
1626
+
1570
1627
  // Preview payloads of the open file's neighbours, fetched ahead so a swipe
1571
1628
  // lands on the file and not on "Fetching…"; an image's bytes are asked for
1572
1629
  // too, under the very address the viewer will use. Entries are promises.
@@ -2935,8 +2992,8 @@ const viewerSwipe = createSwipeRecognizer({
2935
2992
  return;
2936
2993
  }
2937
2994
  // Past either end the content gives a little, then holds.
2938
- const toward = galleryNeighborIndex(filePreviewState.gallery, dx < 0 ? 1 : -1);
2939
- const shift = toward === -1 ? dx * 0.25 : dx;
2995
+ // A page still to load counts as somewhere to go.
2996
+ const shift = galleryCanStep(filePreviewState.gallery, dx < 0 ? 1 : -1) ? dx : dx * 0.25;
2940
2997
  bodyEl.style.transform = `translateX(${shift}px)`;
2941
2998
  },
2942
2999
  onEnd: ({ axis, swipe }) => {