feat(web): name context sources and mark steering in the transcript
Every logged non-user user/message collapsed into one identical 「上下文注入」 row, and mid-turn steering rendered in exactly the bubble a turn-opening prompt uses, so the transcript could not say what had been added or which message interrupted a running turn. TranscriptAdapter and the history fold now attach a provenance view to each context node, computed by contextProvenance() from the durable source alone: a role (inject, or recall for a cross-session snapshot) and a producer name read out of the log — instruction paths for workspace-instructions, session titles for session-reference, the plugin id for a plugin source, and the bare kind for any other. No client-side table of producer names, so a renamed or newly mounted producer stays identifiable without a client release and a foreign log projects like a live one. ContextInjectionRow titles itself from the role and shows the name beside it; MessageItem captions durable and pending steering bubbles. The caption reverses one clause of the no-interjection-chrome decision, which removed it because the composer could not steer; composer steering shipped afterwards without amending that note, so this change supplies the product decision its reintroduction clause required and corrects the stale facts left in it. Fixes #1291
This commit is contained in:
@@ -236,7 +236,10 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
||||
}],
|
||||
},
|
||||
}))
|
||||
await page.getByRole('button', { name: 'Context injection' }).waitFor({ timeout: 10_000 })
|
||||
// The header names the producer the durable source records, so the
|
||||
// reconciled instruction file is readable without expanding the row.
|
||||
await page.getByRole('button', { name: 'Context injection · AGENTS.md', exact: true })
|
||||
.waitFor({ timeout: 10_000 })
|
||||
}, 60_000)
|
||||
|
||||
it.skipIf(MODE === 'record')('matches the historical conversation aria golden', async () => {
|
||||
@@ -254,7 +257,7 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
||||
|
||||
it.skipIf(MODE === 'record')('matches the Figma context disclosure geometry', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-context-injection'))
|
||||
const disclosure = page.getByRole('button', { name: 'Context injection' })
|
||||
const disclosure = page.getByRole('button', { name: 'Context injection · AGENTS.md', exact: true })
|
||||
expect(await disclosure.getAttribute('aria-expanded')).toBe('false')
|
||||
const collapsedIcon = disclosure.locator('svg').first()
|
||||
const collapsedIconBox = await collapsedIcon.boundingBox()
|
||||
@@ -365,9 +368,8 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
||||
source: { kind: 'plugin', plugin: 'fixture' },
|
||||
}))
|
||||
|
||||
const disclosures = page.getByRole('button', { name: 'Context injection' })
|
||||
await expect.poll(() => disclosures.count(), { timeout: 10_000 }).toBe(2)
|
||||
const disclosure = disclosures.nth(1)
|
||||
const disclosure = page.getByRole('button', { name: 'Context injection · fixture', exact: true })
|
||||
await disclosure.waitFor({ timeout: 10_000 })
|
||||
await disclosure.click()
|
||||
await expect.poll(() => disclosure.getAttribute('aria-expanded')).toBe('true')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user