Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f718095d78 | ||
|
|
bcc9f1b5db |
@@ -341,6 +341,19 @@ function OutboxPanel() {
|
||||
)
|
||||
}
|
||||
|
||||
export function NoticeMessage({ msg }: { msg: Message }) {
|
||||
const text = msg.parts.filter(p => p.type === 'text').map(p => p.text).join('\n')
|
||||
const preview = text.replace(/<\/?system-reminder>/g, '').trim().split('\n').find(line => line.trim()) ?? '系统通知'
|
||||
return (
|
||||
<div className="msg notice" data-msg-id={msg.id}>
|
||||
<Collapsible id={`notice:${msg.id}`} triggerClassName="notice-summary"
|
||||
trigger={<span className="notice-label">通知 · {preview}</span>}>
|
||||
<div className="notice-body">{text}</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Conversation() {
|
||||
const currentConvId = useStore(s => s.currentConvId)
|
||||
const conversation = useStore(s => s.tasks.find(c => c.id === s.currentConvId))
|
||||
@@ -447,6 +460,7 @@ export function Conversation() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (m.role === 'system') return <NoticeMessage key={m.id} msg={m} />
|
||||
return <AssistantMessage key={m.id} msg={m} live={live?.runId === m.runId ? live : null} />
|
||||
})}
|
||||
{live && !messages.some(m => m.runId === live.runId) &&
|
||||
|
||||
@@ -210,6 +210,8 @@ export interface PromptModule {
|
||||
body: string; outputContract?: string;
|
||||
source?: 'builtin' | 'plugin' | 'user'; owner?: string;
|
||||
basedOnVersion?: number; drifted?: boolean; disabled?: boolean;
|
||||
/** 子代理 profile。只对 agent.sub.* 有意义;空字段表示沿用内置。 */
|
||||
tools?: string[]; model?: string; maxTurns?: number;
|
||||
}
|
||||
|
||||
export interface PromptBlock { moduleId: string; version: number; layer: string; body: string; }
|
||||
|
||||
+2
-2
@@ -487,7 +487,7 @@ export const store = {
|
||||
},
|
||||
|
||||
async cancelActive() {
|
||||
const active = state.runs.find(r => !isTerminal(r.status))
|
||||
const active = activeRun(state.runs)
|
||||
if (!active) return
|
||||
await api.cancelRun(active.id).catch(() => undefined)
|
||||
setTimeout(() => store.reconcile(), 200)
|
||||
@@ -721,5 +721,5 @@ function isConversationBusy(err: unknown): boolean {
|
||||
}
|
||||
|
||||
export function activeRun(runs: Run[]): Run | undefined {
|
||||
return runs.find(r => !isTerminal(r.status))
|
||||
return runs.find(r => r.kind !== 'subagent' && !isTerminal(r.status))
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ button { font-family: inherit; font-size: inherit; color: inherit; }
|
||||
.msg { margin-bottom: 20px; animation: mk-rise-in .18s var(--ease-out); }
|
||||
.msg-role { font-size: var(--fs-ui-sm); color: var(--text-faint); margin-bottom: 6px; display: flex; gap: 8px; align-items: center; }
|
||||
.msg-role .role-mark { width: 18px; height: 18px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; color: var(--text-dim); background: var(--bg-raised); border: 1px solid var(--border); }
|
||||
.msg.notice { margin-bottom: 12px; }
|
||||
.notice-summary { font-size: var(--fs-ui-sm); color: var(--text-dim); background: none; border: none; padding: 0; cursor: pointer; }
|
||||
.notice-body { font-size: var(--fs-ui-sm); color: var(--text-dim); white-space: pre-wrap; margin-top: 4px; }
|
||||
.msg.user .bubble {
|
||||
background: var(--accent-soft); border: 1px solid transparent;
|
||||
border-radius: var(--radius-lg); padding: 11px 16px; white-space: pre-wrap;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { createElement } from 'react'
|
||||
import { activeRun } from '../src/store'
|
||||
import { NoticeMessage } from '../src/components/Conversation'
|
||||
import type { Message, Run } from '../src/contracts'
|
||||
|
||||
function run(kind: Run['kind'], status: Run['status']): Run {
|
||||
return {
|
||||
id: `${kind}-${status}`, conversationId: 'c', projectId: 'p',
|
||||
kind, status, createdAt: '2026-10-01T00:00:00Z',
|
||||
}
|
||||
}
|
||||
|
||||
describe('activeRun', () => {
|
||||
it('ignores background subagents so they do not block sending', () => {
|
||||
const runs = [run('subagent', 'running'), run('conversation', 'completed')]
|
||||
expect(activeRun(runs)).toBeUndefined()
|
||||
expect(activeRun([run('conversation', 'running'), run('subagent', 'running')])?.kind).toBe('conversation')
|
||||
})
|
||||
})
|
||||
|
||||
describe('NoticeMessage', () => {
|
||||
it('renders a compact notice row', () => {
|
||||
const msg: Message = {
|
||||
id: 'msg_notice', conversationId: 'c', role: 'system', createdAt: '2026-10-01T00:00:00Z',
|
||||
parts: [{ type: 'text', text: '<system-reminder>\n后台子代理「检索」已结束。\n</system-reminder>' }],
|
||||
}
|
||||
const html = renderToStaticMarkup(createElement(NoticeMessage, { msg }))
|
||||
expect(html).toContain('通知 · 后台子代理「检索」已结束。')
|
||||
expect(html).toContain('msg notice')
|
||||
expect(html).not.toContain('<details')
|
||||
})
|
||||
})
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/App.tsx","./src/api.ts","./src/auth.ts","./src/contracts.ts","./src/live.ts","./src/main.tsx","./src/messageparts.ts","./src/outbox.ts","./src/passwords.ts","./src/previewsetup.ts","./src/previewtabs.ts","./src/store.ts","./src/tasklist.ts","./src/toolkind.ts","./src/components/AdminUsersModal.tsx","./src/components/AdminZone.tsx","./src/components/AgentTracker.tsx","./src/components/ArtifactsPanel.tsx","./src/components/Conversation.tsx","./src/components/DocViewer.tsx","./src/components/LoginPage.tsx","./src/components/PreviewPane.tsx","./src/components/ProcessTimeline.tsx","./src/components/ProjectSettingsModal.tsx","./src/components/SettingsModal.tsx","./src/components/TaskSidebar.tsx","./src/components/Workbench.tsx","./src/components/Workspaces.tsx","./src/components/adminSections.tsx","./test/auth.test.ts","./test/contracts.test.ts","./test/design-guard.test.ts","./test/live.test.ts","./test/messageparts.test.ts","./test/outbox.test.ts","./test/previewtabs.test.ts","./test/tasklist.test.ts","./test/toolkind.test.ts"],"version":"5.9.3"}
|
||||
{"root":["./src/App.tsx","./src/api.ts","./src/auth.ts","./src/contracts.ts","./src/live.ts","./src/main.tsx","./src/messageparts.ts","./src/outbox.ts","./src/passwords.ts","./src/pluginHost.ts","./src/previewsetup.ts","./src/previewtabs.ts","./src/store.ts","./src/tasklist.ts","./src/toolkind.ts","./src/components/AdminUsersModal.tsx","./src/components/AdminZone.tsx","./src/components/AgentTracker.tsx","./src/components/ArtifactsPanel.tsx","./src/components/Conversation.tsx","./src/components/DocViewer.tsx","./src/components/LoginPage.tsx","./src/components/PreviewPane.tsx","./src/components/ProcessTimeline.tsx","./src/components/ProjectSettingsModal.tsx","./src/components/SettingsModal.tsx","./src/components/TaskSidebar.tsx","./src/components/Workbench.tsx","./src/components/Workspaces.tsx","./src/components/adminSections.tsx","./test/auth.test.ts","./test/contracts.test.ts","./test/design-guard.test.ts","./test/live.test.ts","./test/messageparts.test.ts","./test/notice.test.ts","./test/outbox.test.ts","./test/previewtabs.test.ts","./test/tasklist.test.ts","./test/toolkind.test.ts"],"version":"5.9.3"}
|
||||
Reference in New Issue
Block a user