minimal-agent 0.18.5 → 0.18.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/workflow-runner/src/runtime/context.js +5 -17
- package/plugins/workflow-runner/src/runtime/context.ts +4 -15
- package/plugins/workflow-runner/src/runtime/model.js +4 -2
- package/plugins/workflow-runner/src/runtime/model.ts +3 -2
- package/plugins/workflow-runner/test/api.test.ts +4 -2
- package/plugins/workflow-runner/test/embeddedExecution.test.ts +5 -6
- package/plugins/workflow-runner/test/modelOutcome.test.ts +36 -0
- package/skills/dynamic-ui/SKILL.md +38 -278
- package/skills/dynamic-ui/mermaid.md +32 -0
- package/skills/dynamic-ui/scenes/architecture-and-flow.md +4 -1
- package/skills/dynamic-ui/scenes/data-visualization.md +5 -2
- package/skills/dynamic-ui/svg-design.md +251 -0
- package/skills/dynamic-ui/templates/README.md +2 -1
- package/skills/dynamic-ui/tokens/visual-tokens.md +4 -5
- package/src/advisor/README.md +24 -0
- package/src/advisor/advisorCoordinator.js +101 -45
- package/src/advisor/advisorCoordinator.ts +90 -51
- package/src/advisor/deliveryReview.js +35 -0
- package/src/advisor/deliveryReview.ts +30 -0
- package/src/advisor/presentation.js +10 -0
- package/src/advisor/presentation.ts +12 -0
- package/src/advisor/reviewContext.js +60 -0
- package/src/advisor/reviewContext.ts +72 -0
- package/src/context/contextBundle.js +23 -3
- package/src/context/contextBundle.ts +28 -7
- package/src/context/tokenCounter.js +3 -0
- package/src/context/tokenCounter.ts +2 -0
- package/src/llm/messageProvenance.js +1 -1
- package/src/llm/messageProvenance.ts +1 -1
- package/src/loop.js +203 -30
- package/src/loop.ts +183 -30
- package/src/memory/experienceStore.js +1 -0
- package/src/memory/experienceStore.ts +1 -0
- package/src/memory/memoryService.js +3 -2
- package/src/memory/memoryService.ts +3 -0
- package/src/memory/projectKnowledge.js +1 -0
- package/src/memory/projectKnowledge.ts +1 -0
- package/src/memory/retrieval.js +4 -4
- package/src/memory/retrieval.ts +4 -4
- package/src/memory/types.ts +1 -0
- package/src/prompts/policyRegistry.js +31 -17
- package/src/prompts/policyRegistry.ts +31 -16
- package/src/prompts/promptAssembly.js +3 -9
- package/src/prompts/promptAssembly.ts +3 -9
- package/src/runtime/answerPreparation.js +9 -0
- package/src/runtime/answerPreparation.ts +11 -0
- package/src/runtime/eventSemantics.js +2 -0
- package/src/runtime/eventSemantics.ts +2 -0
- package/src/runtime/finalAnswer.js +7 -0
- package/src/runtime/finalAnswer.ts +9 -0
- package/src/runtime/lifecycleTrace.js +16 -0
- package/src/runtime/lifecycleTrace.ts +22 -0
- package/src/session/runTurn.js +15 -1
- package/src/session/runTurn.ts +13 -1
- package/src/tools/advisor/advisor.js +56 -40
- package/src/tools/advisor/advisor.ts +57 -39
- package/src/tools/advisor/advisorDecision.js +6 -3
- package/src/tools/advisor/advisorDecision.ts +6 -3
- package/src/tools/agent/agent.js +7 -1
- package/src/tools/agent/agent.ts +7 -1
- package/src/tools/bash/semantics.js +91 -26
- package/src/tools/bash/semantics.ts +63 -33
- package/src/tools/index.js +4 -1
- package/src/tools/index.ts +4 -1
- package/src/types.ts +15 -6
- package/src/ui/LiveArea.js +2 -1
- package/src/ui/LiveArea.tsx +2 -1
- package/src/ui/MessageList.js +18 -5
- package/src/ui/MessageList.tsx +25 -3
- package/src/ui/draftNotice.js +1 -8
- package/src/ui/draftNotice.ts +1 -10
- package/src/ui/hooks/useChat.js +25 -1
- package/src/ui/hooks/useChat.ts +22 -1
package/src/types.ts
CHANGED
|
@@ -163,6 +163,8 @@ export type Message = (
|
|
|
163
163
|
_ephemeral?: true;
|
|
164
164
|
/** Display-only milestone, persisted locally but excluded from model requests. */
|
|
165
165
|
_uiProgress?: true;
|
|
166
|
+
/** Local reading history only; never a model instruction or final answer. */
|
|
167
|
+
draftPresentation?: { reason: string; label: string };
|
|
166
168
|
workflowPresentation?: import('./runtime/workflowPresentation.js').WorkflowPresentation;
|
|
167
169
|
}
|
|
168
170
|
| {
|
|
@@ -174,6 +176,8 @@ export type Message = (
|
|
|
174
176
|
}
|
|
175
177
|
| {
|
|
176
178
|
role: 'assistant';
|
|
179
|
+
/** Persisted UI semantics; not a provider instruction or a success claim. */
|
|
180
|
+
presentation?: 'progress' | 'final';
|
|
177
181
|
/** Host-only review boundary for a delivered text answer; never model authority. */
|
|
178
182
|
advisorTaskVersion?: number;
|
|
179
183
|
/** 当 tool_calls 非空时 content 通常为 null(OpenAI 协议) */
|
|
@@ -593,6 +597,8 @@ export interface ToolCallContext {
|
|
|
593
597
|
advisorInvocation?: 'agent' | 'manual';
|
|
594
598
|
/** Advisor 协调器运行时桥;只存在于主循环,不暴露给模型或持久化。 */
|
|
595
599
|
advisorRuntime?: AdvisorRuntimeBridge;
|
|
600
|
+
/** Live host context used by the main request, not tool-provided instructions. */
|
|
601
|
+
advisorReviewContext?: () => import('./advisor/reviewContext.js').AdvisorReviewContext;
|
|
596
602
|
/**
|
|
597
603
|
* 主循环使用的 provider —— Agent 等「工具内部再起子查询」的派生工具用它
|
|
598
604
|
* 跑嵌套 runQuery(子 agent 与主 agent 同一模型,不另配凭据)。
|
|
@@ -1122,8 +1128,8 @@ export type LlmStreamEvent =
|
|
|
1122
1128
|
* 工作过程显示阶段标签(UI 的 LiveArea 据此选择文字 / 决定是否隐藏整面板)。
|
|
1123
1129
|
*
|
|
1124
1130
|
* idle —— 当前没有任何工作(隐藏 LiveArea)
|
|
1125
|
-
* thinking ——
|
|
1126
|
-
* streaming ——
|
|
1131
|
+
* thinking —— 正在准备候选答案或等待工具调用,候选正文不公开
|
|
1132
|
+
* streaming —— 内部检查结束后,最终表达请求的真实正文流
|
|
1127
1133
|
* tool_executing —— 至少一个工具在跑(并行时栈式显示多个)
|
|
1128
1134
|
* compacting —— 正在跑自动 / reactive / 手动压缩
|
|
1129
1135
|
*/
|
|
@@ -1221,8 +1227,9 @@ export type LoopEvent =
|
|
|
1221
1227
|
message: Extract<Message, { role: 'assistant' }>;
|
|
1222
1228
|
}
|
|
1223
1229
|
/**
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1230
|
+
* 内部候选正文尚不能交付,需要进入检查或补全阶段。
|
|
1231
|
+
* 新内核候选正文不广播;textLength=0,仅表示准备/复核阶段变化。
|
|
1232
|
+
* textLength>0 仅供旧事件兼容,不应作为正常展示路径。
|
|
1226
1233
|
*/
|
|
1227
1234
|
| {
|
|
1228
1235
|
type: 'assistant_draft_discarded';
|
|
@@ -1315,7 +1322,8 @@ export type LoopEvent =
|
|
|
1315
1322
|
| 'completed'
|
|
1316
1323
|
| 'delivery_incomplete'
|
|
1317
1324
|
| 'delivery_unverified'
|
|
1318
|
-
| 'claims_unverified'
|
|
1325
|
+
| 'claims_unverified'
|
|
1326
|
+
| 'no_answer';
|
|
1319
1327
|
warning?: string;
|
|
1320
1328
|
/** 本回合的资源账本。见 TurnResourceUsage —— 消费方务必先看 `trustworthy`。 */
|
|
1321
1329
|
usage?: TurnResourceUsage;
|
|
@@ -1336,7 +1344,7 @@ export type LoopEvent =
|
|
|
1336
1344
|
* - reason: 'manual' 用户按ESC/Ctrl+C手动中断
|
|
1337
1345
|
* - reason: 'queued_input' 有新的排队输入需要在安全点处理
|
|
1338
1346
|
*/
|
|
1339
|
-
| { type: 'interrupted'; reason?: 'manual' | 'queued_input' }
|
|
1347
|
+
| { type: 'interrupted'; reason?: 'manual' | 'queued_input'; usage?: TurnResourceUsage }
|
|
1340
1348
|
/**
|
|
1341
1349
|
* 出错(API 报错 / 网络异常等)。
|
|
1342
1350
|
*
|
|
@@ -1357,6 +1365,7 @@ export type LoopEvent =
|
|
|
1357
1365
|
| {
|
|
1358
1366
|
type: 'error';
|
|
1359
1367
|
error: string;
|
|
1368
|
+
usage?: TurnResourceUsage;
|
|
1360
1369
|
code?:
|
|
1361
1370
|
| 'llm_error'
|
|
1362
1371
|
| 'max_turns'
|
package/src/ui/LiveArea.js
CHANGED
|
@@ -37,7 +37,7 @@ import { figures } from './figures.js';
|
|
|
37
37
|
import { humanizeToolProgress } from './toolProgress.js';
|
|
38
38
|
const STAGE_LABELS = {
|
|
39
39
|
thinking: '思考中',
|
|
40
|
-
streaming: '
|
|
40
|
+
streaming: '正在输出最终答案',
|
|
41
41
|
tool_executing: '工作中',
|
|
42
42
|
compacting: '压缩历史',
|
|
43
43
|
};
|
|
@@ -179,6 +179,7 @@ export function buildLiveView(props) {
|
|
|
179
179
|
contentDim = true;
|
|
180
180
|
}
|
|
181
181
|
else if (stage === 'streaming' && streamingText.length > 0) {
|
|
182
|
+
// Compatibility for older event producers; new loops publish only approved answers.
|
|
182
183
|
contentLines = lastN(wrapToDisplayLines(streamingText, maxWidth), maxLines);
|
|
183
184
|
contentDim = false; // 答案 = 主角,默认亮色
|
|
184
185
|
}
|
package/src/ui/LiveArea.tsx
CHANGED
|
@@ -66,7 +66,7 @@ export interface LiveAreaProps {
|
|
|
66
66
|
|
|
67
67
|
const STAGE_LABELS: Record<Exclude<ProgressStage, 'idle'>, string> = {
|
|
68
68
|
thinking: '思考中',
|
|
69
|
-
streaming: '
|
|
69
|
+
streaming: '正在输出最终答案',
|
|
70
70
|
tool_executing: '工作中',
|
|
71
71
|
compacting: '压缩历史',
|
|
72
72
|
};
|
|
@@ -262,6 +262,7 @@ export function buildLiveView(
|
|
|
262
262
|
);
|
|
263
263
|
contentDim = true;
|
|
264
264
|
} else if (stage === 'streaming' && streamingText.length > 0) {
|
|
265
|
+
// Compatibility for older event producers; new loops publish only approved answers.
|
|
265
266
|
contentLines = lastN(wrapToDisplayLines(streamingText, maxWidth), maxLines);
|
|
266
267
|
contentDim = false; // 答案 = 主角,默认亮色
|
|
267
268
|
} else if (stage === 'tool_executing' && runningTools.length > 0) {
|
package/src/ui/MessageList.js
CHANGED
|
@@ -35,6 +35,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
35
35
|
import React from 'react';
|
|
36
36
|
import { Box, Static, Text } from 'ink';
|
|
37
37
|
import { conciseToolDescription } from './toolProgress.js';
|
|
38
|
+
import { advisorOpinionText, advisorDecisionLabel, advisorEvidenceLabel } from '../advisor/presentation.js';
|
|
38
39
|
import { contentToDisplayText, contentToPlainText } from '../multimodal.js';
|
|
39
40
|
import { splitThinkBlocks } from '../utils/stripThink.js';
|
|
40
41
|
import { figures } from './figures.js';
|
|
@@ -285,9 +286,11 @@ function MessageRow({ message, turnMeta, toolMeta, advisorDecision, foldReasonin
|
|
|
285
286
|
const ephemeral = message._ephemeral === true;
|
|
286
287
|
if (!ephemeral)
|
|
287
288
|
return null;
|
|
288
|
-
const lines =
|
|
289
|
+
const lines = message.draftPresentation
|
|
290
|
+
? renderMarkdownLines(terminalText(message.content)).map(line => line.text)
|
|
291
|
+
: terminalText(message.content).split('\n');
|
|
289
292
|
const milestone = message._uiProgress === true;
|
|
290
|
-
return (_jsx(Box, { marginBottom: 1, paddingLeft: 2, children:
|
|
293
|
+
return (_jsx(Box, { marginBottom: 1, paddingLeft: 2, children: _jsxs(Box, { flexDirection: "column", children: [message.draftPresentation && _jsx(Text, { color: theme.subtle, children: message.draftPresentation.label }), lines.map((line, i) => (_jsx(Text, { color: milestone ? theme.answer : theme.subtle, dimColor: !milestone, wrap: "end", children: line }, `eph-${i}`)))] }) }));
|
|
291
294
|
}
|
|
292
295
|
case 'user': {
|
|
293
296
|
const rule = figures.hr.repeat(6);
|
|
@@ -326,7 +329,7 @@ function MessageRow({ message, turnMeta, toolMeta, advisorDecision, foldReasonin
|
|
|
326
329
|
const hiddenReasoningCount = toolOnly
|
|
327
330
|
? 0
|
|
328
331
|
: reasoningLines.length - visibleReasoningLines.length;
|
|
329
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [visibleReasoningLines.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [visibleReasoningLines.map((line, i) => (_jsx(Text, { color: theme.subtle, dimColor: true, wrap: "end", children: i === 0 ? `${figures.thought} ${line}` : ` ${line}` }, `r-${i}`))), hiddenReasoningCount > 0 && (_jsx(Text, { color: theme.subtle, dimColor: true, children: ` ${figures.fold} ${hiddenReasoningCount} 行 reasoning 折叠(终端高度 < ${NARROW_TERMINAL_ROWS})` })), truncatedCharsHint.length > 0 && (_jsx(Text, { color: theme.subtle, dimColor: true, children: ` ${truncatedCharsHint}` }))] })), buildAssistantDisplayLines(answer).map((line, i) => {
|
|
332
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [visibleReasoningLines.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [visibleReasoningLines.map((line, i) => (_jsx(Text, { color: theme.subtle, dimColor: true, wrap: "end", children: i === 0 ? `${figures.thought} ${line}` : ` ${line}` }, `r-${i}`))), hiddenReasoningCount > 0 && (_jsx(Text, { color: theme.subtle, dimColor: true, children: ` ${figures.fold} ${hiddenReasoningCount} 行 reasoning 折叠(终端高度 < ${NARROW_TERMINAL_ROWS})` })), truncatedCharsHint.length > 0 && (_jsx(Text, { color: theme.subtle, dimColor: true, children: ` ${truncatedCharsHint}` }))] })), answer.trim() && (message.presentation || tcCount > 0) && (_jsx(Text, { color: theme.subtle, dimColor: true, children: message.presentation === 'final' ? '最终回答' : '工作进展 · 执行中说明' })), buildAssistantDisplayLines(answer).map((line, i) => {
|
|
330
333
|
if (line.kind === 'fence') {
|
|
331
334
|
return (_jsx(Text, { color: theme.subtle, dimColor: true, wrap: line.wrap, children: line.content }, `a-${i}`));
|
|
332
335
|
}
|
|
@@ -355,11 +358,16 @@ function MessageRow({ message, turnMeta, toolMeta, advisorDecision, foldReasonin
|
|
|
355
358
|
?? (message.display_metadata?.kind === 'agent' ? 'Agent' : undefined);
|
|
356
359
|
const isAdvisor = inferredToolName === 'Advisor';
|
|
357
360
|
const isAdvisorDecision = inferredToolName === 'AdvisorDecision';
|
|
361
|
+
if (isAdvisor)
|
|
362
|
+
lines.splice(0, lines.length, ...terminalText(advisorOpinionText(message.content)).split('\n'));
|
|
358
363
|
const visibility = toolHistoryVisibility(inferredToolName, message.content);
|
|
359
364
|
const previewCap = toolPreviewCap(inferredToolName);
|
|
360
365
|
const previewLines = lines.slice(0, previewCap);
|
|
361
366
|
const hidden = Math.max(0, lines.length - previewCap);
|
|
362
367
|
const shortId = message.tool_call_id.slice(-8);
|
|
368
|
+
if (message.display_metadata?.kind === 'advisor_checkpoint') {
|
|
369
|
+
return _jsx(Box, { marginBottom: 1, paddingLeft: 2, children: _jsx(Text, { color: theme.subtle, wrap: "end", children: `已暂缓 · 顾问检查点 · 操作尚未执行(/show ${shortId} 查看详情,复核进展见后续记录)` }) });
|
|
370
|
+
}
|
|
363
371
|
// 头行升级:有 toolMeta(发起方反查命中)显示人话描述;Agent 子任务
|
|
364
372
|
// 再解析统计尾巴显示 ✓ N 轮 · M 次工具。无 meta(历史重载等)兜底旧格式。
|
|
365
373
|
const stats = inferredToolName === 'Agent' ? agentResultStats(message.content) : null;
|
|
@@ -377,12 +385,17 @@ function MessageRow({ message, turnMeta, toolMeta, advisorDecision, foldReasonin
|
|
|
377
385
|
: `[tool result · #${shortId}]`;
|
|
378
386
|
if (visibility === 'hidden')
|
|
379
387
|
return null;
|
|
388
|
+
if (isAdvisorDecision) {
|
|
389
|
+
return _jsx(Box, { marginBottom: 1, paddingLeft: 2, children: _jsxs(Text, { color: theme.subtle, wrap: "end", children: [decisionMeta
|
|
390
|
+
? `顾问意见已处理 · ${advisorDecisionLabel(decisionMeta.decision)} · ${advisorEvidenceLabel(decisionMeta.verificationStatus)}`
|
|
391
|
+
: '顾问意见登记未完成 · 可重试', `(/show ${shortId} 查看详情)`] }) });
|
|
392
|
+
}
|
|
380
393
|
if (visibility === 'compact') {
|
|
381
394
|
return (_jsx(Box, { marginBottom: 1, paddingLeft: 2, children: _jsx(Text, { color: theme.subtle, dimColor: true, wrap: "end", children: terminalText(headText) }) }));
|
|
382
395
|
}
|
|
383
396
|
return (_jsx(Box, { marginBottom: 1, paddingLeft: 2, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.subtle, ...(isAdvisor ? {} : { dimColor: true }), wrap: "end", children: terminalText(headText) }), advisorMeta && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.subtle, wrap: "end", children: terminalText(advisorMeta.status === 'completed'
|
|
384
|
-
?
|
|
385
|
-
:
|
|
397
|
+
? '顾问意见已返回,尚不代表事实核验通过'
|
|
398
|
+
: '顾问咨询未完成,已降级继续') }), advisorMeta.status === 'completed' && !advisorDecision && (_jsx(Text, { color: theme.subtle, wrap: "end", children: "\u987E\u95EE\u610F\u89C1\u5DF2\u8FD4\u56DE\uFF1B\u4E3B Agent \u7684\u6838\u9A8C\u4E0E\u91C7\u7EB3\u7ED3\u8BBA\u89C1\u540E\u7EED\u8BB0\u5F55\u3002" })), advisorDecision && (_jsx(Text, { color: theme.subtle, wrap: "end", children: terminalText(`${advisorDecision.decision} · ${advisorDecision.rationale}`) })), advisorDecision?.verificationPlan.map((item) => (_jsx(Text, { color: theme.subtle, wrap: "end", children: terminalText(`验证:${item}`) }, `advisor-card-verify-${item}`)))] })), decisionMeta && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.subtle, wrap: "end", children: terminalText(`关联咨询 ${decisionMeta.consultationId} · ${decisionMeta.rationale}`) }), decisionMeta.verificationPlan.map((item) => (_jsx(Text, { color: theme.subtle, wrap: "end", children: terminalText(`验证:${item}`) }, `advisor-verify-${item}`)))] })), previewLines.map((line, i) => (_jsx(Text, { ...(isAdvisor ? {} : { color: theme.subtle }), wrap: "end", children: line.length > 0 ? line : ' ' }, `p-${i}`))), hidden > 0 && (_jsx(Text, { color: theme.subtle, dimColor: true, children: `... ${figures.fold} 完整 ${lines.length} 行(输入 /show ${shortId} 查看)` }))] }) }));
|
|
386
399
|
}
|
|
387
400
|
}
|
|
388
401
|
}
|
package/src/ui/MessageList.tsx
CHANGED
|
@@ -36,6 +36,7 @@ import React from 'react';
|
|
|
36
36
|
import { Box, Static, Text } from 'ink';
|
|
37
37
|
|
|
38
38
|
import { conciseToolDescription } from './toolProgress.js';
|
|
39
|
+
import { advisorOpinionText, advisorDecisionLabel, advisorEvidenceLabel } from '../advisor/presentation.js';
|
|
39
40
|
import { contentToDisplayText, contentToPlainText } from '../multimodal.js';
|
|
40
41
|
import type { AdvisorDecisionMetadata, Message } from '../types.js';
|
|
41
42
|
import { splitThinkBlocks } from '../utils/stripThink.js';
|
|
@@ -406,11 +407,14 @@ function MessageRow({
|
|
|
406
407
|
const ephemeral =
|
|
407
408
|
(message as { _ephemeral?: true })._ephemeral === true;
|
|
408
409
|
if (!ephemeral) return null;
|
|
409
|
-
const lines =
|
|
410
|
+
const lines = message.draftPresentation
|
|
411
|
+
? renderMarkdownLines(terminalText(message.content)).map(line => line.text)
|
|
412
|
+
: terminalText(message.content).split('\n');
|
|
410
413
|
const milestone = message._uiProgress === true;
|
|
411
414
|
return (
|
|
412
415
|
<Box marginBottom={1} paddingLeft={2}>
|
|
413
416
|
<Box flexDirection="column">
|
|
417
|
+
{message.draftPresentation && <Text color={theme.subtle}>{message.draftPresentation.label}</Text>}
|
|
414
418
|
{lines.map((line, i) => (
|
|
415
419
|
<Text key={`eph-${i}`} color={milestone ? theme.answer : theme.subtle} dimColor={!milestone} wrap="end">
|
|
416
420
|
{line}
|
|
@@ -500,6 +504,11 @@ function MessageRow({
|
|
|
500
504
|
第一条非空 prose 行 + 默认亮色正文(答案是主角)。代码块「零装饰」:
|
|
501
505
|
fence 行 dim、code 行顶格无前缀无染色逐字节原文 —— 复制粘贴即原文。
|
|
502
506
|
纯 <Static> 渲染(write-once),零刷屏风险,不碰 LiveArea 的 INV-1/INV-2。 */}
|
|
507
|
+
{answer.trim() && (message.presentation || tcCount > 0) && (
|
|
508
|
+
<Text color={theme.subtle} dimColor>
|
|
509
|
+
{message.presentation === 'final' ? '最终回答' : '工作进展 · 执行中说明'}
|
|
510
|
+
</Text>
|
|
511
|
+
)}
|
|
503
512
|
{buildAssistantDisplayLines(answer).map((line, i) => {
|
|
504
513
|
if (line.kind === 'fence') {
|
|
505
514
|
return (
|
|
@@ -557,11 +566,17 @@ function MessageRow({
|
|
|
557
566
|
?? (message.display_metadata?.kind === 'agent' ? 'Agent' : undefined);
|
|
558
567
|
const isAdvisor = inferredToolName === 'Advisor';
|
|
559
568
|
const isAdvisorDecision = inferredToolName === 'AdvisorDecision';
|
|
569
|
+
if (isAdvisor) lines.splice(0, lines.length, ...terminalText(advisorOpinionText(message.content)).split('\n'));
|
|
560
570
|
const visibility = toolHistoryVisibility(inferredToolName, message.content);
|
|
561
571
|
const previewCap = toolPreviewCap(inferredToolName);
|
|
562
572
|
const previewLines = lines.slice(0, previewCap);
|
|
563
573
|
const hidden = Math.max(0, lines.length - previewCap);
|
|
564
574
|
const shortId = message.tool_call_id.slice(-8);
|
|
575
|
+
if (message.display_metadata?.kind === 'advisor_checkpoint') {
|
|
576
|
+
return <Box marginBottom={1} paddingLeft={2}><Text color={theme.subtle} wrap="end">
|
|
577
|
+
{`已暂缓 · 顾问检查点 · 操作尚未执行(/show ${shortId} 查看详情,复核进展见后续记录)`}
|
|
578
|
+
</Text></Box>;
|
|
579
|
+
}
|
|
565
580
|
// 头行升级:有 toolMeta(发起方反查命中)显示人话描述;Agent 子任务
|
|
566
581
|
// 再解析统计尾巴显示 ✓ N 轮 · M 次工具。无 meta(历史重载等)兜底旧格式。
|
|
567
582
|
const stats =
|
|
@@ -579,6 +594,13 @@ function MessageRow({
|
|
|
579
594
|
? formatToolHead(toolMeta, shortId, statsTail)
|
|
580
595
|
: `[tool result · #${shortId}]`;
|
|
581
596
|
if (visibility === 'hidden') return null;
|
|
597
|
+
if (isAdvisorDecision) {
|
|
598
|
+
return <Box marginBottom={1} paddingLeft={2}><Text color={theme.subtle} wrap="end">
|
|
599
|
+
{decisionMeta
|
|
600
|
+
? `顾问意见已处理 · ${advisorDecisionLabel(decisionMeta.decision)} · ${advisorEvidenceLabel(decisionMeta.verificationStatus)}`
|
|
601
|
+
: '顾问意见登记未完成 · 可重试'}{`(/show ${shortId} 查看详情)`}
|
|
602
|
+
</Text></Box>;
|
|
603
|
+
}
|
|
582
604
|
if (visibility === 'compact') {
|
|
583
605
|
return (
|
|
584
606
|
<Box marginBottom={1} paddingLeft={2}>
|
|
@@ -598,8 +620,8 @@ function MessageRow({
|
|
|
598
620
|
<Box flexDirection="column">
|
|
599
621
|
<Text color={theme.subtle} wrap="end">
|
|
600
622
|
{terminalText(advisorMeta.status === 'completed'
|
|
601
|
-
?
|
|
602
|
-
:
|
|
623
|
+
? '顾问意见已返回,尚不代表事实核验通过'
|
|
624
|
+
: '顾问咨询未完成,已降级继续')}
|
|
603
625
|
</Text>
|
|
604
626
|
{advisorMeta.status === 'completed' && !advisorDecision && (
|
|
605
627
|
<Text color={theme.subtle} wrap="end">顾问意见已返回;主 Agent 的核验与采纳结论见后续记录。</Text>
|
package/src/ui/draftNotice.js
CHANGED
|
@@ -1,8 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
switch (reason) {
|
|
3
|
-
case 'advisor_checkpoint': return '交付前复核:草稿尚未交付,接下来咨询顾问,再由主 Agent 核验并定稿。';
|
|
4
|
-
case 'formal_delivery': return '整理交付:草稿尚未交付,接下来核对委派结果或补齐正式成果提交。';
|
|
5
|
-
case 'interactive_question': return '等待关键决策:刚才的提问草稿将改为交互问题,请在输入区作答。';
|
|
6
|
-
case 'empty_response': return '本轮未产生可显示的正文,未生成新的最终答复。';
|
|
7
|
-
}
|
|
8
|
-
}
|
|
1
|
+
export { answerPreparationNotice as draftNotice } from '../runtime/answerPreparation.js';
|
package/src/ui/draftNotice.ts
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export function draftNotice(reason: Extract<LoopEvent, { type: 'assistant_draft_discarded' }>['reason']): string {
|
|
4
|
-
switch (reason) {
|
|
5
|
-
case 'advisor_checkpoint': return '交付前复核:草稿尚未交付,接下来咨询顾问,再由主 Agent 核验并定稿。';
|
|
6
|
-
case 'formal_delivery': return '整理交付:草稿尚未交付,接下来核对委派结果或补齐正式成果提交。';
|
|
7
|
-
case 'interactive_question': return '等待关键决策:刚才的提问草稿将改为交互问题,请在输入区作答。';
|
|
8
|
-
case 'empty_response': return '本轮未产生可显示的正文,未生成新的最终答复。';
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
export { answerPreparationNotice as draftNotice } from '../runtime/answerPreparation.js';
|
package/src/ui/hooks/useChat.js
CHANGED
|
@@ -1055,6 +1055,13 @@ export function handleEvent(ev, setters) {
|
|
|
1055
1055
|
// V2 工作流发结构化 `workflow_progress`;旧插件仍可发 `plugin_progress`。
|
|
1056
1056
|
// 收口到 LoopEvent:未识别的 PluginEvent type 走 default no-op
|
|
1057
1057
|
const e = ev;
|
|
1058
|
+
if (e.type === 'turn_done' || e.type === 'interrupted' || (e.type === 'error' && !isRecoverableLoopError(e))) {
|
|
1059
|
+
setters.cancelStreamingFlush();
|
|
1060
|
+
setters.setStreamingText(() => '');
|
|
1061
|
+
setters.cancelReasoningFlush();
|
|
1062
|
+
setters.setReasoningPreview(() => '');
|
|
1063
|
+
setters.setProgressStage('idle');
|
|
1064
|
+
}
|
|
1058
1065
|
if (['assistant_message', 'tool_start', 'turn_done', 'interrupted', 'error'].includes(e.type))
|
|
1059
1066
|
setters.setProgressNotice?.(null);
|
|
1060
1067
|
switch (e.type) {
|
|
@@ -1067,6 +1074,8 @@ export function handleEvent(ev, setters) {
|
|
|
1067
1074
|
setters.appendStreamingDelta(e.delta);
|
|
1068
1075
|
break;
|
|
1069
1076
|
case 'assistant_message':
|
|
1077
|
+
if (e.message.presentation === 'final')
|
|
1078
|
+
setters.setPluginProgress(null);
|
|
1070
1079
|
// history 已被 runQuery 直接 push。先 flush 残留 streaming buffer
|
|
1071
1080
|
// 把末段 text 落到 streamingText state(avoid 视觉跳跃),再清空
|
|
1072
1081
|
// streamingText —— Static 已 commit 完整 assistant message 接管显示。
|
|
@@ -1085,13 +1094,26 @@ export function handleEvent(ev, setters) {
|
|
|
1085
1094
|
setters.setTransientError?.(null);
|
|
1086
1095
|
setters.bump();
|
|
1087
1096
|
break;
|
|
1097
|
+
case 'injected_message':
|
|
1098
|
+
// Milestones preserve live previews; legacy drafts transfer them to history.
|
|
1099
|
+
if (e.message.role === 'system' && e.message._uiProgress) {
|
|
1100
|
+
if (e.message.draftPresentation) {
|
|
1101
|
+
setters.flushStreamingBuffer();
|
|
1102
|
+
setters.setStreamingText(() => '');
|
|
1103
|
+
setters.cancelReasoningFlush();
|
|
1104
|
+
setters.setReasoningPreview(() => '');
|
|
1105
|
+
}
|
|
1106
|
+
setters.bump();
|
|
1107
|
+
}
|
|
1108
|
+
break;
|
|
1088
1109
|
case 'assistant_draft_discarded':
|
|
1089
1110
|
setters.flushStreamingBuffer();
|
|
1090
1111
|
setters.setStreamingText(() => '');
|
|
1091
1112
|
setters.cancelReasoningFlush();
|
|
1092
1113
|
setters.setReasoningPreview(() => '');
|
|
1093
1114
|
setters.setProgressStage('thinking');
|
|
1094
|
-
|
|
1115
|
+
if (e.textLength > 0)
|
|
1116
|
+
setters.emitProgressLine?.(draftNotice(e.reason));
|
|
1095
1117
|
setters.setProgressNotice?.(draftNotice(e.reason));
|
|
1096
1118
|
break;
|
|
1097
1119
|
case 'tool_start':
|
|
@@ -1177,6 +1199,8 @@ export function handleEvent(ev, setters) {
|
|
|
1177
1199
|
// Diagnostics are intentionally local-safe and currently consumed by CLI/stream hosts.
|
|
1178
1200
|
break;
|
|
1179
1201
|
case 'turn_done':
|
|
1202
|
+
if (e.completionStatus === 'no_answer')
|
|
1203
|
+
setters.setError(e.warning ?? '本轮未生成最终答复,可以重试。');
|
|
1180
1204
|
setters.setProgressStage('idle');
|
|
1181
1205
|
setters.setTransientError?.(null);
|
|
1182
1206
|
setters.bump();
|
package/src/ui/hooks/useChat.ts
CHANGED
|
@@ -1466,6 +1466,13 @@ export function handleEvent(
|
|
|
1466
1466
|
|
|
1467
1467
|
// 收口到 LoopEvent:未识别的 PluginEvent type 走 default no-op
|
|
1468
1468
|
const e = ev as LoopEvent;
|
|
1469
|
+
if (e.type === 'turn_done' || e.type === 'interrupted' || (e.type === 'error' && !isRecoverableLoopError(e))) {
|
|
1470
|
+
setters.cancelStreamingFlush();
|
|
1471
|
+
setters.setStreamingText(() => '');
|
|
1472
|
+
setters.cancelReasoningFlush();
|
|
1473
|
+
setters.setReasoningPreview(() => '');
|
|
1474
|
+
setters.setProgressStage('idle');
|
|
1475
|
+
}
|
|
1469
1476
|
if (['assistant_message', 'tool_start', 'turn_done', 'interrupted', 'error'].includes(e.type)) setters.setProgressNotice?.(null);
|
|
1470
1477
|
switch (e.type) {
|
|
1471
1478
|
case 'user_message_committed':
|
|
@@ -1477,6 +1484,7 @@ export function handleEvent(
|
|
|
1477
1484
|
setters.appendStreamingDelta(e.delta);
|
|
1478
1485
|
break;
|
|
1479
1486
|
case 'assistant_message':
|
|
1487
|
+
if (e.message.presentation === 'final') setters.setPluginProgress(null);
|
|
1480
1488
|
// history 已被 runQuery 直接 push。先 flush 残留 streaming buffer
|
|
1481
1489
|
// 把末段 text 落到 streamingText state(avoid 视觉跳跃),再清空
|
|
1482
1490
|
// streamingText —— Static 已 commit 完整 assistant message 接管显示。
|
|
@@ -1495,13 +1503,25 @@ export function handleEvent(
|
|
|
1495
1503
|
setters.setTransientError?.(null);
|
|
1496
1504
|
setters.bump();
|
|
1497
1505
|
break;
|
|
1506
|
+
case 'injected_message':
|
|
1507
|
+
// Milestones preserve live previews; legacy drafts transfer them to history.
|
|
1508
|
+
if (e.message.role === 'system' && e.message._uiProgress) {
|
|
1509
|
+
if (e.message.draftPresentation) {
|
|
1510
|
+
setters.flushStreamingBuffer();
|
|
1511
|
+
setters.setStreamingText(() => '');
|
|
1512
|
+
setters.cancelReasoningFlush();
|
|
1513
|
+
setters.setReasoningPreview(() => '');
|
|
1514
|
+
}
|
|
1515
|
+
setters.bump();
|
|
1516
|
+
}
|
|
1517
|
+
break;
|
|
1498
1518
|
case 'assistant_draft_discarded':
|
|
1499
1519
|
setters.flushStreamingBuffer();
|
|
1500
1520
|
setters.setStreamingText(() => '');
|
|
1501
1521
|
setters.cancelReasoningFlush();
|
|
1502
1522
|
setters.setReasoningPreview(() => '');
|
|
1503
1523
|
setters.setProgressStage('thinking');
|
|
1504
|
-
setters.emitProgressLine?.(draftNotice(e.reason));
|
|
1524
|
+
if (e.textLength > 0) setters.emitProgressLine?.(draftNotice(e.reason));
|
|
1505
1525
|
setters.setProgressNotice?.(draftNotice(e.reason));
|
|
1506
1526
|
break;
|
|
1507
1527
|
case 'tool_start':
|
|
@@ -1585,6 +1605,7 @@ export function handleEvent(
|
|
|
1585
1605
|
// Diagnostics are intentionally local-safe and currently consumed by CLI/stream hosts.
|
|
1586
1606
|
break;
|
|
1587
1607
|
case 'turn_done':
|
|
1608
|
+
if (e.completionStatus === 'no_answer') setters.setError(e.warning ?? '本轮未生成最终答复,可以重试。');
|
|
1588
1609
|
setters.setProgressStage('idle');
|
|
1589
1610
|
setters.setTransientError?.(null);
|
|
1590
1611
|
setters.bump();
|