('system-message')
+ const system = systemContext?.state.effective
const location = match.location.kind === 'step'
? { turn: match.location.turn.turn, step: match.location.step.step }
: {}
const inspection = inspect(previous?.prompt, match.event, system)
const change = inspection.change?.kind
- // An in-history update committed in this same step already shows the
- // prompt at its own position.
- const shownByUpdate = system?.update === true
+ // Appended prompts own their cards; a same-step header must not repeat them.
+ const systemEvent = systemContext?.matches[0]?.event
+ const shownByUpdate = system !== undefined
+ && systemEvent?.type === 'system/message' && systemEvent.surfaceOp === 'append'
+ && (system.update || previous === undefined)
&& system.turn === location.turn
&& system.step === location.step
return {
diff --git a/packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts b/packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts
index c9303c0612..a6846aca36 100644
--- a/packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts
+++ b/packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts
@@ -1475,7 +1475,7 @@ describe('built-in conversation node Definitions', () => {
const prompts = current.nodes.values()
.filter(candidate => candidate.kind === 'system-prompt')
expect(prompts.map(prompt => ({ anchorSeq: prompt.anchorSeq, data: prompt.data }))).toEqual([
- { anchorSeq: 2, data: { text: '# Initial' } },
+ { anchorSeq: 1, data: { text: '# Initial' } },
{ anchorSeq: 5, data: { text: '# Initial' } },
{ anchorSeq: 6, data: { text: '# Initial' } },
{ anchorSeq: 8, data: { text: '# Updated' } },
@@ -1483,6 +1483,25 @@ describe('built-in conversation node Definitions', () => {
expect(current.nodes.values().filter(candidate => candidate.kind === 'unknown')).toEqual([])
})
+ it('shows a complete appended prompt at the start of a headerless window', () => {
+ const value = assembler([
+ systemUpdateAt(10, '# Known prompt', 2, 1),
+ at(11, 'user/message', textMessage('window-user', 'continue'), { surfaceOp: 'append' }),
+ ], true)
+ const current = snapshot(value)
+ expect(current.nodes.values().filter(candidate => candidate.kind === 'system-prompt')
+ .map(candidate => candidate.data)).toEqual([{ text: '# Known prompt' }])
+ expect(current.nodes.values().filter(candidate => candidate.kind === 'unknown')).toEqual([])
+ value.prepend([
+ systemAt(1, '# Original'),
+ at(2, 'request/header', { reason: 'initial', header: { config: { provider: 'fake', model: 'fake' } } }),
+ ], false)
+ value.flush()
+ const restored = snapshot(value)
+ expect(restored.order.map(key => restored.nodes.get(key)).filter(candidate => candidate?.kind === 'system-prompt')
+ .map(candidate => candidate?.data)).toEqual([{ text: '# Original' }, { text: '# Known prompt', update: true }])
+ })
+
it('withholds windowed replacement prompts until prepend resolves their positions', () => {
const windowed = assembler([
systemAt(10, '# Resumed prompt', 5),
@@ -1584,7 +1603,8 @@ describe('built-in conversation node Definitions', () => {
if (mode === 'partial') {
value.replaceWindow(history.slice(7), true)
value.flush()
- expect(snapshot(value).nodes.values().filter(candidate => candidate.kind === 'system-prompt')).toEqual([])
+ expect(snapshot(value).nodes.values().filter(candidate => candidate.kind === 'system-prompt').map(candidate => candidate.data))
+ .toEqual([{ text: 'B' }])
value.prepend(history.slice(0, 7), false)
value.flush()
}
@@ -1594,8 +1614,9 @@ describe('built-in conversation node Definitions', () => {
value.flush()
}
}
- expect(snapshot(value).nodes.values().filter(candidate => candidate.kind === 'system-prompt')
- .map(candidate => candidate.data)).toEqual([
+ const current = snapshot(value)
+ expect(current.order.map(key => current.nodes.get(key)).filter(candidate => candidate?.kind === 'system-prompt')
+ .map(candidate => candidate?.data)).toEqual([
{ text: 'A' }, { text: 'B', update: true }, { text: 'A' },
])
})
@@ -1606,10 +1627,12 @@ describe('built-in conversation node Definitions', () => {
at(8, 'request/header', { reason: 'resume', header: { config: { provider: 'test', model: 'test' } } }),
], true)
expect(node(snapshot(value), 'system-prompt')).toBeUndefined()
+ const uncertain = assembler([systemAt(6, 'C', 3), systemUpdateAt(7, 'Known but unordered', 1, 2)], true)
+ expect(node(snapshot(uncertain), 'system-prompt')).toBeUndefined()
value.prepend([systemAt(1, 'A'), systemAt(3, 'B'), systemAt(5, 'A2', 1)], false)
value.flush()
expect(snapshot(value).nodes.values().filter(candidate => candidate.kind === 'system-prompt')
- .map(candidate => candidate.data)).toEqual([{ text: 'B', update: true }, { text: 'C' }])
+ .map(candidate => candidate.data)).toEqual([{ text: 'A' }, { text: 'B', update: true }, { text: 'C' }])
})
it('never renders a system/message as a transcript bubble', () => {
@@ -1621,12 +1644,12 @@ describe('built-in conversation node Definitions', () => {
])
const current = snapshot(value)
- expect(current.order.map(key => current.nodes.get(key)?.kind)).toEqual(['user'])
+ expect(current.order.map(key => current.nodes.get(key)?.kind)).toEqual(['system-prompt', 'user'])
value.append(systemAt(5, '# Replaced', 3))
value.flush()
const replaced = snapshot(value)
- expect(replaced.order.map(key => replaced.nodes.get(key)?.kind)).toEqual(['user'])
+ expect(replaced.order.map(key => replaced.nodes.get(key)?.kind)).toEqual(['system-prompt', 'user'])
})
it('presents an in-history prompt update as its own card and lets no same-step header repeat it', () => {
diff --git a/packages/client/ui-trajectory/README.i18n.yaml b/packages/client/ui-trajectory/README.i18n.yaml
index 7c9e199013..922513b970 100644
--- a/packages/client/ui-trajectory/README.i18n.yaml
+++ b/packages/client/ui-trajectory/README.i18n.yaml
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-trajectory/README.md
-README.md: 2dd236ecec41137e39b017b79bfbec84d536bebc
-README.zh.md: a7a521bfe0a0ce2b76633e3145e67d87b344c20f
+README.md: ea7e90221317dd62beb259773a8d9872e7b01ddb
+README.zh.md: 1e875c48d66bbd80057ccaecb86219f8de6540e8
diff --git a/packages/client/ui-trajectory/README.md b/packages/client/ui-trajectory/README.md
index 2dd236ecec..ea7e902213 100644
--- a/packages/client/ui-trajectory/README.md
+++ b/packages/client/ui-trajectory/README.md
@@ -45,7 +45,7 @@ A fixed Overview above the ledger projects real record start/duration timing fro
The view is a pure projection: Trajectory-owned Definitions assemble business records from the shared Session window — including durable cancellation-finalized prefixes, chunk-only interruption fallbacks, and interrupted Tool records — so Trajectory neither reads nor changes the Chat conversation snapshot. Its steering classifier retains only next-step Inbox IDs through persistent splice state and shares each current claimed batch across later Contexts.
-In-history system prompt changes compare against the most recent request state, including earlier prompt updates without a new request header. Each request retains the prompt and change that applied at its own position. Surface replacements, including compaction, restore the last nonempty surviving system prompt even without a new system event; an unloaded prompt remains unavailable until its page arrives.
+A complete appended prompt without a loaded request header appears as a standalone system row; only its known text is available, with no inferred request options or tool catalog. Prepending its request history replaces that standalone presentation without duplicating the prompt. In-history system prompt changes compare against the most recent request state, including earlier prompt updates without a new request header. Each request retains the prompt and change that applied at its own position. Surface replacements, including compaction, restore the last nonempty surviving system prompt even without a new system event; an unloaded prompt remains unavailable until its page arrives.
### Virtual rows
diff --git a/packages/client/ui-trajectory/README.zh.md b/packages/client/ui-trajectory/README.zh.md
index a7a521bfe0..1e875c48d6 100644
--- a/packages/client/ui-trajectory/README.zh.md
+++ b/packages/client/ui-trajectory/README.zh.md
@@ -45,7 +45,7 @@ kind: "package-reference"
视图是纯投影:Trajectory 自有的 Definition 从共享 Session 窗口组装业务记录——包括持久化的取消定稿前缀、只能从分片恢复的打断前缀与被打断的工具记录——因此 Trajectory 既不读取也不改变 Chat 会话快照。其 steering 分类器通过持久 splice state 只保留 next-step Inbox ID,并让后续 Context 共享当前 claimed batch。
-历史中的系统提示词变更与最近的请求状态比较,包括没有新请求头的先前提示词更新。每个请求保留其所在位置生效的提示词与变更。包括压缩在内的 surface 替换会恢复最后一个非空的存活系统提示词,即使没有新的系统事件;未加载的提示词在对应分页到达前仍不可用。
+完整的追加提示词在请求头未加载时显示为独立系统行;仅提供已知文本,不推断请求选项或工具目录。补入其请求历史后,该独立展示被替代而不重复提示词。历史中的系统提示词变更与最近的请求状态比较,包括没有新请求头的先前提示词更新。每个请求保留其所在位置生效的提示词与变更。包括压缩在内的 surface 替换会恢复最后一个非空的存活系统提示词,即使没有新的系统事件;未加载的提示词在对应分页到达前仍不可用。
### 虚拟行
diff --git a/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx b/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx
index c7c592e235..d2a952704f 100644
--- a/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx
+++ b/packages/client/ui-trajectory/src/client/TrajectoryTable.tsx
@@ -954,6 +954,9 @@ function markdownSource(record: TableRecord): string | undefined {
function detailTabs(record: TableRecord): readonly DetailTabItem[] {
if (record.cell.kind === 'system') {
+ if (record.cell.promptDetail === undefined && record.cell.systemPromptDetail !== undefined) {
+ return SYSTEM_PROMPT_TABS.filter(tab => tab.id === 'system-prompt')
+ }
return record.cell.previousPromptDetail === undefined
? SYSTEM_PROMPT_TABS
: SYSTEM_UPDATE_TABS
@@ -1961,7 +1964,8 @@ export function TrajectoryTable({
const selectedPreviousPrompt = selected?.cell.kind === 'system'
? selected.cell.previousPromptDetail
: undefined
- const promptSelected = selectedPrompt !== undefined
+ const selectedSystemPrompt = selectedPrompt?.system ?? selected?.cell.systemPromptDetail
+ const promptSelected = selectedSystemPrompt !== undefined
const selectedState = selected === undefined ? undefined : stateOf(selected)
const selectedRequestInfo = selectedRequest === null
? undefined
@@ -2947,7 +2951,7 @@ export function TrajectoryTable({
t={t}
/>
)}
- {promptSelected
+ {selectedPrompt !== undefined
&& selectedPreviousPrompt !== undefined
&& activeTab === 'diff' && (
)}
{promptSelected && activeTab === 'system-prompt' && (
- selectedPrompt.system === ''
+ selectedSystemPrompt === ''
? {t('record.systemPromptMissing')}
: (
-
+
)
)}
- {promptSelected && activeTab === 'tools' && (
+ {selectedPrompt !== undefined && activeTab === 'tools' && (
)}
{!promptSelected
diff --git a/packages/client/ui-trajectory/src/client/TrajectoryView.tsx b/packages/client/ui-trajectory/src/client/TrajectoryView.tsx
index a9f1df1b36..4f8bc63d9c 100644
--- a/packages/client/ui-trajectory/src/client/TrajectoryView.tsx
+++ b/packages/client/ui-trajectory/src/client/TrajectoryView.tsx
@@ -319,12 +319,13 @@ export function TrajectoryView({
: { turn: partialTurn, step: partialStep, blocks: [] },
runningCalls,
requests,
+ systemPrompts: inspection.systemPrompts,
callSchemas,
}, t)
return { turns, lastIndex: lastCellIndex(turns) }
}, [
nodes, eventLocations, partialTurn, partialStep,
- runningCalls, requests, callSchemas, t,
+ runningCalls, requests, inspection.systemPrompts, callSchemas, t,
])
const timelinePartialSignature = partialStructureSignature(partial)
const timelinePartial = useMemo(() => partial === null
diff --git a/packages/client/ui-trajectory/src/client/layout.ts b/packages/client/ui-trajectory/src/client/layout.ts
index d8076b5797..fd361899d0 100644
--- a/packages/client/ui-trajectory/src/client/layout.ts
+++ b/packages/client/ui-trajectory/src/client/layout.ts
@@ -37,6 +37,7 @@ export interface TrajectoryTurnModel {
/** Snapshot slice the trajectory view folds. */
export interface TrajectoryLayoutInput {
+ systemPrompts?: TrajectorySnapshot['systemPrompts']
nodes: TrajectorySnapshot['eventNodes']
eventLocations?: ReadonlyMap
partial: TrajectorySnapshot['partial']
@@ -94,7 +95,8 @@ type OrderedLayoutEntry =
| {
kind: 'system'
seq: number
- request: AssistantRequestView
+ request?: AssistantRequestView
+ systemPrompt?: string
change: RequestPromptChange
}
| {
@@ -234,6 +236,10 @@ export function deriveTrajectoryLayout(
}
const entries: OrderedLayoutEntry[] = [
+ ...(input.systemPrompts ?? []).map(prompt => ({
+ kind: 'system' as const, seq: prompt.seq, systemPrompt: prompt.text,
+ change: { seq: prompt.seq, time: prompt.time, kind: prompt.update ? 'system' as const : 'initial' as const },
+ })),
...nodes.map((node, nodeIndex) => ({
kind: 'node' as const,
seq: node.seq,
@@ -306,7 +312,8 @@ export function deriveTrajectoryLayout(
kind: 'system',
text: promptChangeLabel(change, t),
sourceSeq: change.seq,
- ...(request.prompt === undefined ? {} : { promptDetail: request.prompt }),
+ ...(request?.prompt === undefined ? {} : { promptDetail: request.prompt }),
+ ...(entry.systemPrompt === undefined ? {} : { systemPromptDetail: entry.systemPrompt }),
...(change.previous === undefined
? {}
: { previousPromptDetail: change.previous }),
diff --git a/packages/client/ui-trajectory/src/client/trajectory-contract.ts b/packages/client/ui-trajectory/src/client/trajectory-contract.ts
index 37571f2b83..5507fd18ea 100644
--- a/packages/client/ui-trajectory/src/client/trajectory-contract.ts
+++ b/packages/client/ui-trajectory/src/client/trajectory-contract.ts
@@ -1,7 +1,7 @@
import type {
AssistantMessageNode, ConversationLocation, ConversationNode, ConversationPromptSnapshot,
ConversationViewNode, MessageImagesOwnerProps, PartialAssistant, RequestPromptChange,
- RequestView, RunningToolCall, ToolCallBlock,
+ RequestView, RunningToolCall, SystemPromptNode, ToolCallBlock,
} from '@deepseek-ai/dsh-client-ui-conversation/client'
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
@@ -16,6 +16,7 @@ export interface TrajectoryRequestHeaderState {
/** One independently assembled contribution to the legacy Trajectory ledger. */
export type TrajectoryContribution =
+ | { readonly kind: 'system-prompt'; readonly prompt: SystemPromptNode }
| {
readonly kind: 'node'
readonly node: ConversationNode
@@ -61,6 +62,8 @@ export interface TrajectoryConversationViewNode extends ConversationViewNode {
/** Stage-oriented Trajectory data assembled from registered business Contexts. */
export interface TrajectorySnapshot {
+ /** Complete loaded prompt text whose request header is outside the window. */
+ readonly systemPrompts?: readonly SystemPromptNode[]
readonly eventNodes: readonly ConversationNode[]
readonly eventLocations: ReadonlyMap
readonly requests: readonly RequestView[]
diff --git a/packages/client/ui-trajectory/src/client/trajectory-record.ts b/packages/client/ui-trajectory/src/client/trajectory-record.ts
index da330f2ae4..2bcb268f22 100644
--- a/packages/client/ui-trajectory/src/client/trajectory-record.ts
+++ b/packages/client/ui-trajectory/src/client/trajectory-record.ts
@@ -58,6 +58,8 @@ export interface TrajectoryCellProps extends HTMLAttributes {
inputDetail?: string
/** Complete system-prompt/tool-catalog state introduced by a SYSTEM record. */
promptDetail?: ConversationPromptSnapshot
+ /** Known prompt text without a loaded request config or tool catalog. */
+ systemPromptDetail?: string
/** System-prompt/tool-catalog state replaced by a SYSTEM update. */
previousPromptDetail?: ConversationPromptSnapshot
/** Full assistant/tool result content for the details panel. */
diff --git a/packages/client/ui-trajectory/src/client/trajectory-request-header-definition.ts b/packages/client/ui-trajectory/src/client/trajectory-request-header-definition.ts
index d8e8a754dc..488dca8775 100644
--- a/packages/client/ui-trajectory/src/client/trajectory-request-header-definition.ts
+++ b/packages/client/ui-trajectory/src/client/trajectory-request-header-definition.ts
@@ -21,7 +21,7 @@ export interface TrajectorySystemMessageState extends SystemPromptState {
* Definition retaining each `system/message` surface node for the Trajectory
* request-header Definition, which reads it through `reader.previous` and
* presents the prompt through the request's `system` cell. A node that
- * introduces or replaces the prompt contributes nothing itself; an in-history
+ * lacks a loaded header contributes known text without request config or tools; an in-history
* update contributes a request-header fact at its own position, since no
* `request/header` follows a prompt change that keeps the cached history.
* Surface replacements also contribute prompt changes when they remove the
@@ -74,13 +74,17 @@ function trajectorySystemMessageDefinition(inspect: SystemPromptInspector): Conv
}
},
update: context => context.state,
- buildViewNode: context => context.state?.header === undefined
- || context.state.header.seq !== context.start?.event.seq
- ? null
- : trajectoryNode(context, context.state.header.seq, {
- kind: 'request-header',
- header: context.state.header,
- }),
+ buildViewNode: (context) => {
+ const state = context.state
+ if (state?.header !== undefined && state.header.seq === context.start?.event.seq) {
+ return trajectoryNode(context, state.header.seq, { kind: 'request-header', header: state.header })
+ }
+ const prompt = state?.introduced
+ return prompt !== undefined && prompt.text !== ''
+ && context.start?.event.type === 'system/message' && context.start.event.surfaceOp === 'append'
+ ? trajectoryNode(context, prompt.seq, { kind: 'system-prompt', prompt })
+ : null
+ },
}
}
/* jscpd:ignore-end */
diff --git a/packages/client/ui-trajectory/src/client/trajectory-snapshot-builder.ts b/packages/client/ui-trajectory/src/client/trajectory-snapshot-builder.ts
index 60aed23058..07f6e5d2f3 100644
--- a/packages/client/ui-trajectory/src/client/trajectory-snapshot-builder.ts
+++ b/packages/client/ui-trajectory/src/client/trajectory-snapshot-builder.ts
@@ -199,6 +199,9 @@ export class TrajectorySnapshotBuilder implements ConversationViewBuilder<
: previous?.change === undefined ? {} : { change: previous.change }),
})
}
+ const representedPrompts = new Set(this.contributions.flatMap(node =>
+ node.data.kind === 'request-header' && node.data.header.change !== undefined ? [node.data.header.change.seq] : []))
+ const systemPrompts: NonNullable[number][] = []
const finalized: ConversationNode[] = []
const eventLocations = new Map()
const requests: RequestView[] = []
@@ -218,6 +221,10 @@ export class TrajectorySnapshotBuilder implements ConversationViewBuilder<
for (const contribution of this.contributions) {
const data = contribution.data
+ if (data.kind === 'system-prompt') {
+ if (!representedPrompts.has(data.prompt.seq)) systemPrompts.push(data.prompt)
+ continue
+ }
if (data.kind === 'request-header') {
previousHeader = data.header
previousTools = indexTools(data.header.prompt.tools)
@@ -274,6 +281,7 @@ export class TrajectorySnapshotBuilder implements ConversationViewBuilder<
const eventNodes = finalized
return {
eventNodes,
+ ...systemPrompts.length > 0 ? { systemPrompts } : {},
eventLocations,
requests,
callSchemas,
diff --git a/packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts b/packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts
index 54aaefd3f0..864e599d1a 100644
--- a/packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts
+++ b/packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts
@@ -637,6 +637,31 @@ describe('Trajectory conversation Definitions', () => {
])
})
+ it('shows a complete appended prompt at the start of a headerless window', () => {
+ const value = assembler([
+ at(10, 'system/message', {
+ turn: 2, step: 1, message: systemMessage('known prompt'),
+ }, { surfaceOp: 'append' }),
+ at(11, 'step/end', { turn: 2, step: 1 }),
+ at(12, 'step/start', { turn: 2, step: 2 }),
+ at(13, 'assistant/message', {
+ turn: 2, step: 2, message: assistantMessage('window-assistant', 'answer'),
+ }),
+ ], true)
+ const request = snapshot(value).requests.find(request => request.purpose === 'assistant')
+ expect(request?.purpose === 'assistant' && request.prompt).toBeUndefined()
+ expect(request?.requestConfig).toBeUndefined()
+ expect(snapshot(value).systemPrompts).toMatchObject([{ seq: 10, text: 'known prompt', update: false }])
+ value.prepend([
+ at(1, 'system/message', { turn: 1, step: 1, message: systemMessage('original') }, { surfaceOp: 'append' }),
+ at(2, 'request/header', { reason: 'initial', header: { config: { provider: 'test', model: 'test' } } }),
+ ], false)
+ value.flush()
+ expect(snapshot(value).systemPrompts).toBeUndefined()
+ expect(snapshot(value).requests.find(request => request.purpose === 'assistant'))
+ .toMatchObject({ prompt: { system: 'known prompt', config: { provider: 'test', model: 'test' } } })
+ })
+
it('carries an in-history prompt update into later requests as a system change at its own position', () => {
const value = assembler([
at(1, 'turn/start', { turn: 1 }),
@@ -804,6 +829,8 @@ describe('Trajectory conversation Definitions', () => {
at(10, 'assistant/message', { turn: 1, step: 1, message: assistantMessage('reply', 'reply') }),
])
expect(snapshot(value).requests.at(-1)).toMatchObject({ prompt: { system: '' } })
+ const uncertain = assembler([system(6, 'C', 3), system(7, 'Known but unordered')], true)
+ expect(snapshot(uncertain).systemPrompts).toBeUndefined()
value.prepend([system(1, 'A'), system(3, 'B'), system(5, 'A2', 1)], false)
value.flush()
expect(snapshot(value).requests.at(-1)).toMatchObject({ prompt: { system: 'C' } })
diff --git a/packages/client/ui-trajectory/tests/table.client.spec.tsx b/packages/client/ui-trajectory/tests/table.client.spec.tsx
index 89a8165a29..b16475162a 100644
--- a/packages/client/ui-trajectory/tests/table.client.spec.tsx
+++ b/packages/client/ui-trajectory/tests/table.client.spec.tsx
@@ -6,7 +6,7 @@ import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/re
import type { ComponentProps } from 'react'
import type { RenderMessageImages } from '@deepseek-ai/dsh-client-ui-conversation/client'
import { TrajectoryTable as LocalizedTrajectoryTable } from '../src/client/TrajectoryTable.tsx'
-import type { TrajectoryTurnModel } from '../src/client/layout.ts'
+import { deriveTrajectoryLayout, type TrajectoryTurnModel } from '../src/client/layout.ts'
import { trajectoryRecordId } from '../src/client/trajectory-record.ts'
import { t, tZh } from './locale.client.ts'
@@ -124,6 +124,20 @@ const FOLD_PROPS = {
}
describe('TrajectoryTable', () => {
+ it('shows known standalone prompt text without a fabricated tool catalog or request options', () => {
+ const turns = deriveTrajectoryLayout({
+ nodes: [], partial: null, runningCalls: [],
+ systemPrompts: [{ seq: 10, time: 10, turn: 2, step: 1, text: '# Known instructions', update: false }],
+ }, t)
+ expect(turns.flatMap(turn => turn.groups.flatMap(group => group.cells))).toMatchObject([
+ { kind: 'system', text: 'Initial System Prompt', systemPromptDetail: '# Known instructions' },
+ ])
+ render()
+ fireEvent.click(screen.getByRole('row', { name: /SYSTEM/ }))
+ expect(screen.getByRole('heading', { name: 'Known instructions' })).toBeTruthy()
+ expect(screen.getAllByRole('tab').map(tab => tab.textContent)).toEqual(['System Prompt'])
+ })
+
it('shows a muted placeholder for an assistant response containing only tool calls', () => {
const turns: readonly TrajectoryTurnModel[] = [{
turn: 1,
diff --git a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
index 78b9f15ce0..18e60de284 100644
--- a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
+++ b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
@@ -1271,7 +1271,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.trajectory.images\', () => ctx.slots.register(\n { name: \'conversation.trajectory.images\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
- source: 'packages/client/ui-trajectory/src/client/trajectory-contract.ts:95',
+ source: 'packages/client/ui-trajectory/src/client/trajectory-contract.ts:98',
},
{
key: 'conversation.view',