Просмотр исходного кода

Merge origin/master into codex/sandbox-policy-context

NI0317 1 месяц назад
Родитель
Сommit
a7c4e8424c

+ 23 - 0
apps/web/tests/live-interactions.e2e.ts

@@ -175,6 +175,29 @@ describe('web e2e: live-turn interactions (cancel / error / retry)', () => {
     expect(tripwire.warnings).toEqual([])
   }, 120_000)
 
+  it.skipIf(MODE === 'record')('keeps a terminal request marker inside the trajectory table', async () => {
+    await launch(() => ({
+      patches: [{ at: 0, entry: { kind: 'throw', chunks: [], message: 'invalid api key', code: 'AUTH' } }],
+    }))
+    const { settled } = await sendPrompt()
+    await settled
+    await page.getByRole('tab', { name: 'Trajectory' }).click()
+    const tailRequest = page.locator('tr[data-request-only="true"]').last()
+    await tailRequest.waitFor({ timeout: 10_000 })
+    const requestMarker = tailRequest.getByRole('button', { name: /Request #/ })
+
+    const markerWithinTable = await requestMarker.evaluate((element) => {
+      const marker = element.getBoundingClientRect()
+      const table = element.closest('table')?.getBoundingClientRect()
+      if (table === undefined) throw new Error('request marker has no table')
+      return marker.bottom <= table.bottom
+    })
+
+    expect(markerWithinTable).toBe(true)
+    expect(tripwire.pageErrors).toEqual([])
+    expect(tripwire.warnings).toEqual([])
+  }, 120_000)
+
   it.skipIf(MODE === 'record')('recovers a transient SERVER failure through llm-retry and completes', async () => {
     const derived = deriveReplayScript(parseSessionLog(await readFile(FIXTURE, 'utf8')))
     expect(derived).toHaveLength(1)

+ 5 - 0
packages/client/ui-trajectory/src/client/TrajectoryTable.module.css

@@ -103,6 +103,11 @@
   border-bottom: 0;
 }
 
+.table tbody tr[data-request-only='true']:last-child td {
+  /* Retain the lower half of the 16px boundary marker at the table's end. */
+  height: 9px;
+}
+
 .table tbody tr[data-request-only='true'] .turnRail {
   top: -15px;
   bottom: 0;

+ 3 - 0
packages/client/ui-trajectory/src/client/TrajectoryTimeline.module.css

@@ -1,4 +1,7 @@
 .root {
+  position: relative;
+  z-index: 0;
+  isolation: isolate;
   flex: none;
   border-bottom: 1px solid var(--dsw-alias-border-l2);
   user-select: none;

+ 11 - 5
packages/client/ui-trajectory/src/client/views.module.css

@@ -13,11 +13,17 @@
   background: var(--dsw-alias-bg-layer-1);
 }
 
-/* Under the active conversation column (`[data-conversation-scroll]`) the
- * parent owns overflow so the sticky composer stays in the same scrollport. */
-:global([data-conversation-scroll]) .root {
-  overflow: visible;
-  height: auto;
+/* Trajectory keeps the ledger and details panel inside the remaining
+ * conversation height. Only the ledger pane scrolls; the composer remains
+ * the fixed flex sibling below this view. */
+:global([data-conversation-scroll]):has(.root) {
+  overflow: hidden;
+}
+
+:global([data-conversation-scroll]):has(.root) > :first-child {
+  flex: 1 1 0;
+  min-height: 0;
+  overflow: hidden;
 }
 
 .ledger {