diff --git a/packages/session-ui/src/storybook/current-session-fixtures.ts b/packages/session-ui/src/storybook/current-session-fixtures.ts index 10cdadccec..bb8c75ca7f 100644 --- a/packages/session-ui/src/storybook/current-session-fixtures.ts +++ b/packages/session-ui/src/storybook/current-session-fixtures.ts @@ -784,6 +784,50 @@ export const skillWorkflowDocument = document([ }), ] satisfies SessionMessageInfo[]) +export const instructionsUpdatedSingleDocument = document([ + user("msg_user_instructions_single", "Check if beta service reports the shared session as running.", 85_000), + assistant({ + id: "msg_assistant_instructions_single", + offset: 86_000, + completed: 88_000, + content: [ + { + type: "text", + text: "The beta service is healthy and already reports this shared session as running. I found unrelated desktop changes in the worktree and will leave them untouched; next I'm narrowing the beta-only capabilities to features that can be demonstrated safely in this session rather than invoking every administrative API.", + }, + ], + }), + { + id: "msg_instructions_updated_single", + type: "system", + text: "Updated instructions for api/v2-demo", + description: "Instructions updated: api/v2-demo", + time: { created: STORY_TIME + 89_000 }, + }, +] satisfies SessionMessageInfo[]) + +export const instructionsUpdatedMultipleDocument = document([ + user("msg_user_instructions_multi", "Check if beta service reports the shared session as running.", 85_000), + assistant({ + id: "msg_assistant_instructions_multi", + offset: 86_000, + completed: 88_000, + content: [ + { + type: "text", + text: "The beta service is healthy and already reports this shared session as running. I found unrelated desktop changes in the worktree and will leave them untouched; next I'm narrowing the beta-only capabilities to features that can be demonstrated safely in this session rather than invoking every administrative API.", + }, + ], + }), + { + id: "msg_instructions_updated_multi", + type: "system", + text: "Updated instructions for api/v2-demo and api/session", + description: "Instructions updated: api/v2-demo, api/session", + time: { created: STORY_TIME + 89_000 }, + }, +] satisfies SessionMessageInfo[]) + export const permissionPendingDocument = document( [ user("msg_user_permission_pending", "Publish the verified preview build to the canary channel.", 83_000), diff --git a/packages/session-ui/src/timeline/session-timeline-row.tsx b/packages/session-ui/src/timeline/session-timeline-row.tsx index 870d6f8442..4f91d50d94 100644 --- a/packages/session-ui/src/timeline/session-timeline-row.tsx +++ b/packages/session-ui/src/timeline/session-timeline-row.tsx @@ -8,7 +8,7 @@ import { Card } from "@opencode-ai/ui/card" import { useI18n } from "@opencode-ai/ui/context/i18n" import { TextReveal } from "@opencode-ai/ui/text-reveal" import { TextShimmer } from "@opencode-ai/ui/text-shimmer" -import { Show, createMemo, type Accessor, type JSX } from "solid-js" +import { For, Show, createMemo, type Accessor, type JSX } from "solid-js" import type { SessionUserActions, SessionUserComment } from "../actions" import { MessageDivider, @@ -152,7 +152,17 @@ export function createSessionTimelineRowRenderer(input: { if (message.type === "location-switched") return { label: i18n.t("ui.patch.action.moved"), data: message.location.directory } if (message.type === "skill") return { label: i18n.t("ui.tool.skill"), data: message.name } - if (message.type === "system") return { label: message.description ?? message.text } + if (message.type === "system") { + const prefix = "Instructions updated: " + if (message.description?.startsWith(prefix)) { + const keys = message.description.slice(prefix.length).split(",").map((s) => s.trim()).filter(Boolean) + return { + label: i18n.t("ui.sessionTimeline.notice.instructionsUpdated"), + items: keys, + } + } + return { label: message.description ?? message.text } + } if (message.type === "compaction") return { label: i18n.t("ui.messagePart.compaction"), data: message.status } if (message.type !== "synthetic") return undefined if (message.description === "Continuing after restart") return { label: message.description } @@ -274,19 +284,30 @@ export function createSessionTimelineRowRenderer(input: { {(content) => (
- - {content().label} - - - {(data) => ( - - {" "} - · {data()} - - )} - +
+ + {content().label} + + + + {(item) => ( + + {item} + + )} + + + + {(data) => ( + + {" "} + · {data()} + + )} + +
)} diff --git a/packages/session-ui/src/timeline/timeline-row.stories.tsx b/packages/session-ui/src/timeline/timeline-row.stories.tsx index 4880b26fe3..dac196af01 100644 --- a/packages/session-ui/src/timeline/timeline-row.stories.tsx +++ b/packages/session-ui/src/timeline/timeline-row.stories.tsx @@ -4,6 +4,8 @@ import { attachmentsAndCommentsDocument, attachmentsAndCommentsPresentation, compactionDocument, + instructionsUpdatedMultipleDocument, + instructionsUpdatedSingleDocument, requestHistoryDocument, retryDocument, revertDocument, @@ -129,3 +131,25 @@ export const MixedDirectionRtl = { /> ), } + +export const InstructionsUpdatedSingle = { + render: () => ( + + ), +} + +export const InstructionsUpdatedMultiple = { + render: () => ( + + ), +} diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts index ae10ca6ce9..4dc1087366 100644 --- a/packages/ui/src/i18n/en.ts +++ b/packages/ui/src/i18n/en.ts @@ -210,6 +210,7 @@ const source = { "ui.sessionTimeline.notice.failed": "{{actor}} failed", "ui.sessionTimeline.notice.cancelled": "{{actor}} cancelled", "ui.sessionTimeline.notice.finished": "{{actor}} finished", + "ui.sessionTimeline.notice.instructionsUpdated": "Instructions updated", "ui.message.queued": "Queued", "ui.message.attachment.alt": "attachment",