@meiyukichan/dsh-sandore 0.1.0-rc.6
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/LICENSE +201 -0
- package/cordis.patch.yml +39 -0
- package/dsh.plugin.json +16 -0
- package/lib/client/CommandEditor.d.ts +19 -0
- package/lib/client/DagEditor.d.ts +22 -0
- package/lib/client/DefinitionDetail.d.ts +13 -0
- package/lib/client/DefinitionList.d.ts +19 -0
- package/lib/client/DiscussionInput.d.ts +63 -0
- package/lib/client/DragHandle.d.ts +16 -0
- package/lib/client/EditorPane.d.ts +13 -0
- package/lib/client/FlowEditor.d.ts +31 -0
- package/lib/client/InstanceList.d.ts +24 -0
- package/lib/client/NewInstanceDialog.d.ts +19 -0
- package/lib/client/PreviewPanel.d.ts +13 -0
- package/lib/client/RunView.d.ts +79 -0
- package/lib/client/StepChatPanel.d.ts +34 -0
- package/lib/client/TreeNav.d.ts +27 -0
- package/lib/client/UnifiedTimeline.d.ts +11 -0
- package/lib/client/WorkflowButton.d.ts +11 -0
- package/lib/client/WorkflowList.d.ts +18 -0
- package/lib/client/WorkflowOverlay.d.ts +6 -0
- package/lib/client/chat/AssistantMarkdown.d.ts +22 -0
- package/lib/client/chat/CompactionItem.d.ts +24 -0
- package/lib/client/chat/ContextBody.d.ts +136 -0
- package/lib/client/chat/ContextInjectionRow.d.ts +25 -0
- package/lib/client/chat/GenericCommandCard.d.ts +10 -0
- package/lib/client/chat/MessageIconActions.d.ts +41 -0
- package/lib/client/chat/MessageItem.d.ts +65 -0
- package/lib/client/chat/ReasoningRow.d.ts +13 -0
- package/lib/client/chat/TurnTailNodeView.d.ts +29 -0
- package/lib/client/chat/UserMessageRow.d.ts +18 -0
- package/lib/client/chat/attachment/ImageLightbox.d.ts +26 -0
- package/lib/client/chat/attachment/MessageImage.d.ts +56 -0
- package/lib/client/chat/attachment/image-attachment.d.ts +41 -0
- package/lib/client/chat/attachment/image-labels.d.ts +21 -0
- package/lib/client/chat/message-chrome.d.ts +51 -0
- package/lib/client/chat/reference/ReferenceIcon.d.ts +20 -0
- package/lib/client/chat/turn-assistant.d.ts +12 -0
- package/lib/client/chat/turn-metrics.d.ts +42 -0
- package/lib/client/chat/use-calendar-day.d.ts +5 -0
- package/lib/client/chat/use-throttled-visual-update.d.ts +7 -0
- package/lib/client/chat-flow.d.ts +110 -0
- package/lib/client/composer-selects.d.ts +17 -0
- package/lib/client/dag-editor-model.d.ts +141 -0
- package/lib/client/describe-error.d.ts +9 -0
- package/lib/client/index.d.ts +6 -0
- package/lib/client/locales.d.ts +777 -0
- package/lib/client/model-faces.d.ts +156 -0
- package/lib/client/open-discussion.d.ts +24 -0
- package/lib/client/panel-model.d.ts +34 -0
- package/lib/client/remote.d.ts +34 -0
- package/lib/client/run-view-model.d.ts +304 -0
- package/lib/client/sandore-save.d.ts +41 -0
- package/lib/client/sandore-sse.d.ts +15 -0
- package/lib/client/step-chat-retry.d.ts +44 -0
- package/lib/client/tool/GenericToolCard.d.ts +28 -0
- package/lib/client/tool/ToolRow.d.ts +95 -0
- package/lib/client/tool/diff-card-model.d.ts +63 -0
- package/lib/client/tool/read-card-model.d.ts +67 -0
- package/lib/client/tool/search-card-model.d.ts +94 -0
- package/lib/client/tool/terminal-card-model.d.ts +86 -0
- package/lib/client/tool/tool-call-model.d.ts +74 -0
- package/lib/client/tool/web-card-model.d.ts +44 -0
- package/lib/client/use-focus-refresh.d.ts +1 -0
- package/lib/client/workflow-face.d.ts +68 -0
- package/lib/client/workflow-store.d.ts +80 -0
- package/lib/client/workspace-path.d.ts +15 -0
- package/lib/client.js +38389 -0
- package/lib/index.js +8854 -0
- package/lib/microflow/branch-evaluator.d.ts +290 -0
- package/lib/microflow/concurrency-limiter.d.ts +39 -0
- package/lib/microflow/errors.d.ts +39 -0
- package/lib/microflow/file-utils.d.ts +210 -0
- package/lib/microflow/finalize-manager.d.ts +414 -0
- package/lib/microflow/fork-session.d.ts +151 -0
- package/lib/microflow/index.d.ts +17 -0
- package/lib/microflow/internal.d.ts +56 -0
- package/lib/microflow/loop-manager.d.ts +127 -0
- package/lib/microflow/rules-resolver.d.ts +28 -0
- package/lib/microflow/stage-executor.d.ts +362 -0
- package/lib/microflow/step-navigator.d.ts +108 -0
- package/lib/microflow/step-runner.d.ts +190 -0
- package/lib/microflow/subagents.d.ts +380 -0
- package/lib/tool/index.d.ts +65 -0
- package/lib/typert.host.d.ts +16 -0
- package/lib/typert.host.js +25 -0
- package/lib/typert.remote-client.d.ts +114 -0
- package/lib/typert.remote-client.js +399 -0
- package/lib/workflow/ask-tool.d.ts +78 -0
- package/lib/workflow/dag-navigator.d.ts +136 -0
- package/lib/workflow/domain.d.ts +304 -0
- package/lib/workflow/engine-surface.d.ts +70 -0
- package/lib/workflow/errors.d.ts +13 -0
- package/lib/workflow/host-agent.d.ts +53 -0
- package/lib/workflow/index.d.ts +23 -0
- package/lib/workflow/interaction-manager.d.ts +170 -0
- package/lib/workflow/lifecycle-manager.d.ts +111 -0
- package/lib/workflow/persistence.d.ts +35 -0
- package/lib/workflow/plugin.d.ts +27 -0
- package/lib/workflow/reactive-state.d.ts +27 -0
- package/lib/workflow/recovery-manager.d.ts +158 -0
- package/lib/workflow/remote.d.ts +139 -0
- package/lib/workflow/schema.d.ts +1917 -0
- package/lib/workflow/seam.d.ts +254 -0
- package/lib/workflow/shell.d.ts +127 -0
- package/lib/workflow/sse.d.ts +16 -0
- package/lib/workflow/typert/typert.host.d.ts +16 -0
- package/lib/workflow/typert/typert.remote-client.d.ts +114 -0
- package/lib/workflow/types.d.ts +187 -0
- package/lib/workflow/wire.d.ts +256 -0
- package/lib/workflow/workflow-engine.d.ts +373 -0
- package/lib/workflow/yaml-error.d.ts +22 -0
- package/lib/workflow/yaml-parser.d.ts +23 -0
- package/package.json +140 -0
- package/packages/client/CommandEditor.tsx +73 -0
- package/packages/client/DagEditor.module.css +711 -0
- package/packages/client/DagEditor.tsx +1203 -0
- package/packages/client/DefinitionDetail.module.css +110 -0
- package/packages/client/DefinitionDetail.tsx +126 -0
- package/packages/client/DefinitionList.tsx +173 -0
- package/packages/client/DiscussionInput.tsx +763 -0
- package/packages/client/DragHandle.tsx +92 -0
- package/packages/client/EditorPane.tsx +70 -0
- package/packages/client/FlowEditor.tsx +227 -0
- package/packages/client/InstanceList.tsx +250 -0
- package/packages/client/NewInstanceDialog.module.css +244 -0
- package/packages/client/NewInstanceDialog.tsx +478 -0
- package/packages/client/PreviewPanel.tsx +55 -0
- package/packages/client/RunView.module.css +246 -0
- package/packages/client/RunView.tsx +314 -0
- package/packages/client/StepChatPanel.tsx +486 -0
- package/packages/client/TreeNav.tsx +294 -0
- package/packages/client/UnifiedTimeline.tsx +484 -0
- package/packages/client/WorkflowButton.module.css +72 -0
- package/packages/client/WorkflowButton.tsx +50 -0
- package/packages/client/WorkflowList.tsx +158 -0
- package/packages/client/WorkflowOverlay.module.css +390 -0
- package/packages/client/WorkflowOverlay.tsx +136 -0
- package/packages/client/WorkflowPanel.module.css +247 -0
- package/packages/client/chat/AssistantMarkdown.module.css +38 -0
- package/packages/client/chat/AssistantMarkdown.tsx +120 -0
- package/packages/client/chat/ChatView.module.css +198 -0
- package/packages/client/chat/CompactionItem.tsx +85 -0
- package/packages/client/chat/ContextBody.module.css +162 -0
- package/packages/client/chat/ContextBody.tsx +598 -0
- package/packages/client/chat/ContextInjectionRow.module.css +63 -0
- package/packages/client/chat/ContextInjectionRow.tsx +78 -0
- package/packages/client/chat/GenericCommandCard.module.css +87 -0
- package/packages/client/chat/GenericCommandCard.tsx +76 -0
- package/packages/client/chat/MessageIconActions.module.css +84 -0
- package/packages/client/chat/MessageIconActions.tsx +147 -0
- package/packages/client/chat/MessageItem.module.css +293 -0
- package/packages/client/chat/MessageItem.tsx +384 -0
- package/packages/client/chat/ReasoningRow.module.css +82 -0
- package/packages/client/chat/ReasoningRow.tsx +70 -0
- package/packages/client/chat/TurnTailNodeView.module.css +10 -0
- package/packages/client/chat/TurnTailNodeView.tsx +63 -0
- package/packages/client/chat/UserMessageRow.module.css +75 -0
- package/packages/client/chat/UserMessageRow.tsx +92 -0
- package/packages/client/chat/accessibility.module.css +9 -0
- package/packages/client/chat/attachment/ImageLightbox.module.css +45 -0
- package/packages/client/chat/attachment/ImageLightbox.tsx +70 -0
- package/packages/client/chat/attachment/MessageImage.module.css +70 -0
- package/packages/client/chat/attachment/MessageImage.tsx +136 -0
- package/packages/client/chat/attachment/image-attachment.ts +98 -0
- package/packages/client/chat/attachment/image-labels.ts +34 -0
- package/packages/client/chat/message-chrome.ts +96 -0
- package/packages/client/chat/reference/ReferenceIcon.tsx +40 -0
- package/packages/client/chat/turn-assistant.ts +15 -0
- package/packages/client/chat/turn-metrics.ts +102 -0
- package/packages/client/chat/use-calendar-day.ts +27 -0
- package/packages/client/chat/use-throttled-visual-update.ts +44 -0
- package/packages/client/chat-flow.spec.ts +357 -0
- package/packages/client/chat-flow.ts +278 -0
- package/packages/client/composer-selects.module.css +376 -0
- package/packages/client/composer-selects.tsx +531 -0
- package/packages/client/dag-editor-model.spec.ts +173 -0
- package/packages/client/dag-editor-model.ts +359 -0
- package/packages/client/describe-error.spec.ts +18 -0
- package/packages/client/describe-error.ts +12 -0
- package/packages/client/index.ts +144 -0
- package/packages/client/input.module.css +854 -0
- package/packages/client/is-conditional-check.spec.ts +34 -0
- package/packages/client/locales.ts +789 -0
- package/packages/client/model-faces.ts +225 -0
- package/packages/client/open-discussion.spec.ts +39 -0
- package/packages/client/open-discussion.ts +32 -0
- package/packages/client/panel-model.spec.ts +152 -0
- package/packages/client/panel-model.ts +72 -0
- package/packages/client/preview.module.css +95 -0
- package/packages/client/react-dom.d.ts +11 -0
- package/packages/client/reactflow-theme.css +91 -0
- package/packages/client/remote.spec.ts +41 -0
- package/packages/client/remote.ts +46 -0
- package/packages/client/run-view-model.spec.ts +641 -0
- package/packages/client/run-view-model.ts +636 -0
- package/packages/client/sandore-save.spec.ts +146 -0
- package/packages/client/sandore-save.ts +97 -0
- package/packages/client/sandore-sse.ts +59 -0
- package/packages/client/step-chat-retry.spec.ts +70 -0
- package/packages/client/step-chat-retry.ts +65 -0
- package/packages/client/timeline.module.css +503 -0
- package/packages/client/tool/GenericToolCard.tsx +99 -0
- package/packages/client/tool/ToolRow.module.css +308 -0
- package/packages/client/tool/ToolRow.tsx +328 -0
- package/packages/client/tool/diff-card-model.ts +103 -0
- package/packages/client/tool/read-card-model.ts +86 -0
- package/packages/client/tool/search-card-model.ts +166 -0
- package/packages/client/tool/terminal-card-model.ts +225 -0
- package/packages/client/tool/tool-call-model.ts +267 -0
- package/packages/client/tool/web-card-model.ts +80 -0
- package/packages/client/tree-nav.module.css +282 -0
- package/packages/client/use-focus-refresh.ts +23 -0
- package/packages/client/workflow-face.ts +72 -0
- package/packages/client/workflow-store.spec.ts +49 -0
- package/packages/client/workflow-store.ts +161 -0
- package/packages/client/workspace-path.ts +27 -0
- package/packages/css-modules.d.ts +6 -0
- package/packages/microflow/branch-evaluator.spec.ts +745 -0
- package/packages/microflow/branch-evaluator.ts +1037 -0
- package/packages/microflow/concurrency-limiter.ts +70 -0
- package/packages/microflow/errors.ts +64 -0
- package/packages/microflow/file-utils.spec.ts +604 -0
- package/packages/microflow/file-utils.ts +521 -0
- package/packages/microflow/finalize-manager.spec.ts +1977 -0
- package/packages/microflow/finalize-manager.ts +1464 -0
- package/packages/microflow/fork-session.spec.ts +184 -0
- package/packages/microflow/fork-session.ts +316 -0
- package/packages/microflow/index.ts +17 -0
- package/packages/microflow/internal.ts +166 -0
- package/packages/microflow/loop-manager.spec.ts +294 -0
- package/packages/microflow/loop-manager.ts +255 -0
- package/packages/microflow/rules-resolver.spec.ts +147 -0
- package/packages/microflow/rules-resolver.ts +58 -0
- package/packages/microflow/stage-executor.spec.ts +1577 -0
- package/packages/microflow/stage-executor.ts +1478 -0
- package/packages/microflow/step-navigator.spec.ts +301 -0
- package/packages/microflow/step-navigator.ts +300 -0
- package/packages/microflow/step-runner.spec.ts +1132 -0
- package/packages/microflow/step-runner.ts +752 -0
- package/packages/microflow/subagents.spec.ts +215 -0
- package/packages/microflow/subagents.ts +422 -0
- package/packages/tests/fixtures/test-workflow.yaml +128 -0
- package/packages/tests/mocks/agents.ts +206 -0
- package/packages/tests/mocks/llm.ts +46 -0
- package/packages/tests/mocks/session-title.ts +31 -0
- package/packages/tests/mocks/sessions.ts +131 -0
- package/packages/tests/mocks/shell.ts +60 -0
- package/packages/tests/mocks/storage-domain.ts +101 -0
- package/packages/tests/mocks/subagents.ts +197 -0
- package/packages/tests/mocks/subprocess.ts +59 -0
- package/packages/tests/mocks/user-questions.ts +33 -0
- package/packages/tests/mocks/workspace.ts +52 -0
- package/packages/tool/index.spec.ts +287 -0
- package/packages/tool/index.ts +348 -0
- package/packages/workflow/ask-tool.ts +173 -0
- package/packages/workflow/dag-navigator.spec.ts +236 -0
- package/packages/workflow/dag-navigator.ts +425 -0
- package/packages/workflow/domain.ts +26 -0
- package/packages/workflow/engine-surface.ts +71 -0
- package/packages/workflow/errors.ts +13 -0
- package/packages/workflow/host-agent.spec.ts +92 -0
- package/packages/workflow/host-agent.ts +62 -0
- package/packages/workflow/index.ts +25 -0
- package/packages/workflow/interaction-manager.spec.ts +390 -0
- package/packages/workflow/interaction-manager.ts +397 -0
- package/packages/workflow/lifecycle-manager.spec.ts +683 -0
- package/packages/workflow/lifecycle-manager.ts +288 -0
- package/packages/workflow/persistence.spec.ts +109 -0
- package/packages/workflow/persistence.ts +64 -0
- package/packages/workflow/plugin.ts +39 -0
- package/packages/workflow/reactive-state.spec.ts +69 -0
- package/packages/workflow/reactive-state.ts +52 -0
- package/packages/workflow/recovery-manager.spec.ts +606 -0
- package/packages/workflow/recovery-manager.ts +471 -0
- package/packages/workflow/remote.spec.ts +1045 -0
- package/packages/workflow/remote.ts +479 -0
- package/packages/workflow/schema.spec.ts +303 -0
- package/packages/workflow/schema.ts +562 -0
- package/packages/workflow/seam.ts +293 -0
- package/packages/workflow/shell.ts +142 -0
- package/packages/workflow/sse.ts +110 -0
- package/packages/workflow/typert/typert.host.d.ts +16 -0
- package/packages/workflow/typert/typert.host.js +25 -0
- package/packages/workflow/typert/typert.remote-client.d.ts +114 -0
- package/packages/workflow/typert/typert.remote-client.js +399 -0
- package/packages/workflow/types.ts +258 -0
- package/packages/workflow/wire.ts +267 -0
- package/packages/workflow/workflow-engine-e2e.spec.ts +262 -0
- package/packages/workflow/workflow-engine-real-e2e.spec.ts +922 -0
- package/packages/workflow/workflow-engine.spec.ts +4389 -0
- package/packages/workflow/workflow-engine.ts +2133 -0
- package/packages/workflow/yaml-error.ts +29 -0
- package/packages/workflow/yaml-parser.spec.ts +757 -0
- package/packages/workflow/yaml-parser.ts +287 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunView 的可测纯逻辑(无 React 依赖)。
|
|
3
|
+
*
|
|
4
|
+
* 从 `dsh-sandore/schema`(浏览器安全子路径)导入
|
|
5
|
+
* `WorkflowTaskState` 等 wire 类型,并以其派生 `DagRunStatus`/`StepStatus`/
|
|
6
|
+
* `ExecutedStep`/`StageInfo`(避免从包根 `types.ts` 导入带出 `dsh-llm` 依赖)。
|
|
7
|
+
* 此处提供:7 态 → StateDotState 穷尽映射、运行中/终态判定(驱动 5s 轮询)、
|
|
8
|
+
* pending 交互分类、retry message 校验与 `RetryMode` 词汇。
|
|
9
|
+
* @module @deepseek-ai/dsh-client-ui-sandore/src/client/run-view-model
|
|
10
|
+
*/
|
|
11
|
+
import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
12
|
+
import type {
|
|
13
|
+
PendingPermissionEntry, PendingQuestionEntry, WorkflowTaskState,
|
|
14
|
+
} from 'dsh-sandore/schema'
|
|
15
|
+
import type { RevertBoundaryOption } from 'dsh-sandore/types'
|
|
16
|
+
|
|
17
|
+
/** 实例生命周期状态(从 `WorkflowTaskState` 派生,避免 dsh-llm 依赖)。 */
|
|
18
|
+
export type DagRunStatus = WorkflowTaskState['status']
|
|
19
|
+
|
|
20
|
+
/** 单个 Step/Stage 执行状态(从 `WorkflowTaskState` 派生)。 */
|
|
21
|
+
export type StepStatus = WorkflowTaskState['executedSteps'][number]['status']
|
|
22
|
+
|
|
23
|
+
/** 已执行 Step 的快照(从 `WorkflowTaskState` 派生)。 */
|
|
24
|
+
export type ExecutedStep = WorkflowTaskState['executedSteps'][number]
|
|
25
|
+
|
|
26
|
+
/** Stage 状态快照(从 `WorkflowTaskState` 派生)。 */
|
|
27
|
+
export type StageInfo = WorkflowTaskState['stageInfos'][number]
|
|
28
|
+
|
|
29
|
+
/** 重试模式(对齐 `retryStep` 入参 `mode` 的 zod 枚举)。 */
|
|
30
|
+
export type RetryMode = 'continue' | 'revert' | 'fork'
|
|
31
|
+
|
|
32
|
+
/** revert 回退点候选(对齐 `listRevertBoundaries` 返回的 `RevertBoundaryOption`)。 */
|
|
33
|
+
export type { RevertBoundaryOption }
|
|
34
|
+
|
|
35
|
+
/** replyQuestion 单个问题的回答(对齐 Host 的 `AskUserQuestionAnswerItem`)。 */
|
|
36
|
+
export interface AnswerItem {
|
|
37
|
+
readonly id: string
|
|
38
|
+
readonly selected: readonly string[]
|
|
39
|
+
readonly custom?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 三种重试模式(供重试表单下拉/选项渲染)。 */
|
|
43
|
+
export const RETRY_MODES: readonly RetryMode[] = ['continue', 'revert', 'fork']
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* StepStatus 7 态 → StateDotState 4 态穷尽映射:
|
|
47
|
+
* `completed→done`、`failed→error`、`running→ongoing`,其余(等待用户注意的
|
|
48
|
+
* blocked/ready/interacting/paused)折入 `warning`。
|
|
49
|
+
* @param status - Step 执行状态。
|
|
50
|
+
* @returns 四态语义点。
|
|
51
|
+
*/
|
|
52
|
+
export function stepDotState(status: StepStatus): StateDotState {
|
|
53
|
+
switch (status) {
|
|
54
|
+
case 'completed':
|
|
55
|
+
return 'done'
|
|
56
|
+
case 'failed':
|
|
57
|
+
return 'error'
|
|
58
|
+
case 'running':
|
|
59
|
+
return 'ongoing'
|
|
60
|
+
case 'blocked':
|
|
61
|
+
case 'ready':
|
|
62
|
+
case 'interacting':
|
|
63
|
+
case 'paused':
|
|
64
|
+
return 'warning'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* DagRunStatus 7 态 → StateDotState 4 态穷尽映射:
|
|
70
|
+
* `completed→done`、`failed→error`、`running→ongoing`,其余(pending/interacting/
|
|
71
|
+
* aborted/paused)折入 `warning`。
|
|
72
|
+
* @param status - 实例生命周期状态。
|
|
73
|
+
* @returns 四态语义点。
|
|
74
|
+
*/
|
|
75
|
+
export function runDotState(status: DagRunStatus): StateDotState {
|
|
76
|
+
switch (status) {
|
|
77
|
+
case 'completed':
|
|
78
|
+
return 'done'
|
|
79
|
+
case 'failed':
|
|
80
|
+
return 'error'
|
|
81
|
+
case 'running':
|
|
82
|
+
return 'ongoing'
|
|
83
|
+
case 'pending':
|
|
84
|
+
case 'interacting':
|
|
85
|
+
case 'aborted':
|
|
86
|
+
case 'paused':
|
|
87
|
+
return 'warning'
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 实例是否处于「运行中」状态(`running`/`interacting`),决定是否启动 5s 轮询。
|
|
93
|
+
* @param status - 实例生命周期状态。
|
|
94
|
+
* @returns 运行中(需轮询)为 true。
|
|
95
|
+
*/
|
|
96
|
+
export function isRunningStatus(status: DagRunStatus): boolean {
|
|
97
|
+
return status === 'running' || status === 'interacting'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 实例是否已进入终态(`completed`/`failed`/`aborted`),终态停止轮询。
|
|
102
|
+
* @param status - 实例生命周期状态。
|
|
103
|
+
* @returns 终态为 true。
|
|
104
|
+
*/
|
|
105
|
+
export function isTerminalStatus(status: DagRunStatus): boolean {
|
|
106
|
+
return status === 'completed' || status === 'failed' || status === 'aborted'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** pending 交互分类结果。 */
|
|
110
|
+
export interface PendingInteractionPartition {
|
|
111
|
+
/** 待答问题条目。 */
|
|
112
|
+
readonly questions: readonly PendingQuestionEntry[]
|
|
113
|
+
/** 待批权限条目。 */
|
|
114
|
+
readonly permissions: readonly PendingPermissionEntry[]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 按 `kind` 把 `pendingInteractions` 分为问答与权限两组。
|
|
119
|
+
*
|
|
120
|
+
* 探索修正(2026-08):仅取 `status === 'pending'` 的条目——后端已只持久化 pending,
|
|
121
|
+
* 此处过滤是防御旧快照(含 replied/rejected 全量历史时避免讨论页复弹接管面板)。
|
|
122
|
+
* @param interactions - 实例的 pending 交互列表。
|
|
123
|
+
* @returns 问答与权限两个分组。
|
|
124
|
+
*/
|
|
125
|
+
export function partitionInteractions(
|
|
126
|
+
interactions: readonly (PendingQuestionEntry | PendingPermissionEntry)[],
|
|
127
|
+
): PendingInteractionPartition {
|
|
128
|
+
const questions: PendingQuestionEntry[] = []
|
|
129
|
+
const permissions: PendingPermissionEntry[] = []
|
|
130
|
+
for (const interaction of interactions) {
|
|
131
|
+
if (interaction.status !== 'pending') continue
|
|
132
|
+
if (interaction.kind === 'question') {
|
|
133
|
+
questions.push(interaction)
|
|
134
|
+
} else {
|
|
135
|
+
permissions.push(interaction)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { questions, permissions }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 判断字符串是否为空或仅含空白(用于 retry message 等必填文本校验)。
|
|
143
|
+
* @param value - 待校验文本。
|
|
144
|
+
* @returns 空白为 true。
|
|
145
|
+
*/
|
|
146
|
+
export function isBlank(value: string): boolean {
|
|
147
|
+
return value.trim() === ''
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Step 对外交互标识:定义 `stepId` 优先,缺省回退唯一执行 `id`。
|
|
152
|
+
* 与引擎侧 `findStep`(先 `stepId` 后 `id` 反查)保持一致,供
|
|
153
|
+
* `pauseStep`/`resumeStep`/`retryStep` 入参使用。
|
|
154
|
+
* @param step - 含 `id` 与可选 `stepId` 的 Step 快照。
|
|
155
|
+
* @returns 可反查该 Step 的稳定标识。
|
|
156
|
+
*/
|
|
157
|
+
export function stepIdOf(step: { readonly id: string; readonly stepId?: string | undefined }): string {
|
|
158
|
+
return step.stepId ?? step.id
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 判定 Step 是否为可续接的子代理 Step(agent/skill/mcp/prompt),非 shell 合成 session。
|
|
163
|
+
*
|
|
164
|
+
* 对齐引擎侧 `recovery-manager.ts` 的 `isSubagentStep`:优先 `sessionMeta.command`
|
|
165
|
+
* (运行时 session 元数据),缺省回退 `command.type`(定义态命令类型)。shell
|
|
166
|
+
* 合成 session 无真实子代理,引擎对暂停/重试明确抛 `INVALID_OPERATION`,故 UI 不应
|
|
167
|
+
* 对其渲染 pause/resume/retry 控件;prompt 为真实子代理会话(决策 D3),与
|
|
168
|
+
* agent/skill/mcp 同语义。
|
|
169
|
+
* @param step - 已执行 Step 快照。
|
|
170
|
+
* @returns 子代理 Step(可暂停/恢复/重试)为 true。
|
|
171
|
+
*/
|
|
172
|
+
export function isSubagentStep(step: ExecutedStep): boolean {
|
|
173
|
+
const command = step.sessionMeta?.command ?? step.command?.type
|
|
174
|
+
return command === 'agent' || command === 'skill' || command === 'mcp' || command === 'prompt'
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── 树形导航 / 统一时间线派生(纯函数,无 React 依赖) ─────────────────────
|
|
178
|
+
|
|
179
|
+
/** 树形导航节点类型。 */
|
|
180
|
+
export type RunTreeNodeType = 'workflow' | 'stage' | 'step' | 'result' | 'artifact'
|
|
181
|
+
|
|
182
|
+
/** 树形导航节点。 */
|
|
183
|
+
export interface RunTreeNode {
|
|
184
|
+
readonly id: string
|
|
185
|
+
readonly name: string
|
|
186
|
+
readonly type: RunTreeNodeType
|
|
187
|
+
/** 节点状态(workflow 为实例生命周期态;stage/step 为 Step 态;叶子无)。 */
|
|
188
|
+
readonly status?: StepStatus | DagRunStatus | undefined
|
|
189
|
+
/** 归属 Step 的稳定标识(stepIdOf;供预览/操作反查)。 */
|
|
190
|
+
readonly stepId?: string | undefined
|
|
191
|
+
/** 子代理会话 id(sessionMeta.childId,供「跳子会话」)。 */
|
|
192
|
+
readonly childId?: string | undefined
|
|
193
|
+
/** 副信息(artifact 的 outputToId / 文件路径)。 */
|
|
194
|
+
readonly meta?: string | undefined
|
|
195
|
+
/** 预览正文(result 的输出文本 / artifact 的文件路径)。 */
|
|
196
|
+
readonly text?: string | undefined
|
|
197
|
+
readonly children?: readonly RunTreeNode[] | undefined
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** agent 内嵌消息框的展示行(who + 正文 + 类型)。 */
|
|
201
|
+
export interface ChatLine {
|
|
202
|
+
/** 行归属(`{stepName} · agent` / `{stepName} · tool`)。 */
|
|
203
|
+
readonly who: string
|
|
204
|
+
/** 行正文(pre-wrap)。 */
|
|
205
|
+
readonly text: string
|
|
206
|
+
/** assistant 正文行 / tool mono 行。 */
|
|
207
|
+
readonly kind: 'assistant' | 'tool'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 统一时间线的 Step 卡片项。 */
|
|
211
|
+
export interface TimelineStepItem {
|
|
212
|
+
readonly kind: 'step'
|
|
213
|
+
readonly stepId: string
|
|
214
|
+
readonly name: string
|
|
215
|
+
readonly status: StepStatus
|
|
216
|
+
/** 循环迭代标签(如 `#1.2`)。 */
|
|
217
|
+
readonly loopLabel?: string | undefined
|
|
218
|
+
/** 段开始时间(ISO,createdAt 优先回退 updatedAt/startedAt;旧实例可能缺)。 */
|
|
219
|
+
readonly time?: string | undefined
|
|
220
|
+
/** completed 态输出文本(text 块拼接)。 */
|
|
221
|
+
readonly outputText?: string | undefined
|
|
222
|
+
/** failed 态错误详情。 */
|
|
223
|
+
readonly error?: string | undefined
|
|
224
|
+
/** 收尾阶段(finalizeMeta.currentPhase:assess/ask/finalize/gate/completed;供状态按钮徽标)。 */
|
|
225
|
+
readonly finalizePhase?: string | undefined
|
|
226
|
+
/** 收尾前一阶段(finalizeMeta.previousPhase;供 UI 展示阶段流转痕迹)。 */
|
|
227
|
+
readonly finalizePreviousPhase?: string | undefined
|
|
228
|
+
/** 收尾错误详情(finalizeMeta.errorDetail,如门禁失败原因;failed 态附加展示)。 */
|
|
229
|
+
readonly finalizeErrorDetail?: string | undefined
|
|
230
|
+
/** Phase B 收尾评估降级原因(finalizeMeta.assessResult.errorDetail;非 undefined 表示压缩降级,completed 态回退显示 output)。 */
|
|
231
|
+
readonly finalizeAssessErrorDetail?: string | undefined
|
|
232
|
+
/** Phase B 收尾评估 fork 会话 id(finalizeMeta.assessResult.forkSessionId,供「收尾中」徽标点击打开)。 */
|
|
233
|
+
readonly finalizeAssessSessionId?: string | undefined
|
|
234
|
+
/** Phase C 门禁 fork 会话 id(finalizeMeta.gateResult.gateSessionId,供「门禁中」徽标点击打开)。 */
|
|
235
|
+
readonly finalizeGateSessionId?: string | undefined
|
|
236
|
+
/** Phase B 收尾评估结果(finalizeMeta.assessResult;completed 态展示 compressedOutput)。 */
|
|
237
|
+
readonly finalizeAssessResult?: { readonly assessmentRaw: string; readonly compressedOutput?: string; readonly status?: 'running' | 'done' } | undefined
|
|
238
|
+
/** Phase C 门禁结果(finalizeMeta.gateResult)。 */
|
|
239
|
+
readonly finalizeGateResult?: { readonly gatesPassed: boolean; readonly status?: 'running' | 'done' | 'failed' } | undefined
|
|
240
|
+
/** Phase A ask 判定结果(finalizeMeta.askResult)。 */
|
|
241
|
+
readonly finalizeAskResult?: { readonly hasAsk: boolean; readonly askQuestion?: unknown; readonly status?: 'running' | 'done' } | undefined
|
|
242
|
+
/** Phase D 路由判断结果(finalizeMeta.routingResult)。 */
|
|
243
|
+
readonly finalizeRoutingResult?: { readonly branchMeta?: unknown; readonly loopMeta?: unknown; readonly status?: 'running' | 'done' } | undefined
|
|
244
|
+
/** Phase D 路由判断 fork 会话 id(finalizeMeta.routingResult.routingSessionId)。 */
|
|
245
|
+
readonly finalizeRoutingSessionId?: string | undefined
|
|
246
|
+
// Round 11:各阶段独立 status 字段
|
|
247
|
+
readonly askStatus?: 'running' | 'done'
|
|
248
|
+
readonly assessStatus?: 'running' | 'done'
|
|
249
|
+
readonly gateStatus?: 'running' | 'done' | 'failed'
|
|
250
|
+
readonly routingStatus?: 'running' | 'done'
|
|
251
|
+
/** 子代理会话 id(跳「查看详情」)。 */
|
|
252
|
+
readonly childId?: string | undefined
|
|
253
|
+
/** 命令类型(step.sessionMeta.command:'shell'/'prompt'/'agent'/'skill'/'mcp';shell completed 直接显示命令输出)。 */
|
|
254
|
+
readonly commandType?: string | undefined
|
|
255
|
+
/** 是否合成会话(shell 无真实子代理,标题不可点击跳子代理页)。 */
|
|
256
|
+
readonly synthetic?: boolean | undefined
|
|
257
|
+
/** sessionMessages 展示行(内嵌消息框 / 失败·暂停最近 N 条)。 */
|
|
258
|
+
readonly messages?: readonly ChatLine[] | undefined
|
|
259
|
+
readonly visitCount: number
|
|
260
|
+
/** 执行段序号(同 stepId 第几次执行段;0-based,缺省 0 = 首次执行)。 */
|
|
261
|
+
readonly attemptIndex?: number | undefined
|
|
262
|
+
/** 暂停定格时间(paused 段展示暂停提示时用)。 */
|
|
263
|
+
readonly pausedAt?: string | undefined
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** 统一时间线的用户气泡项。 */
|
|
267
|
+
export interface TimelineUserItem {
|
|
268
|
+
readonly kind: 'user'
|
|
269
|
+
readonly content: string
|
|
270
|
+
/** 消息时间(ISO,createdAt)。 */
|
|
271
|
+
readonly time: string
|
|
272
|
+
/** 消息种类(input/resume/retry/answer;旧实例可能缺,回退 input 语义)。 */
|
|
273
|
+
readonly messageKind?: string | undefined
|
|
274
|
+
/** 目标:'broadcast' 或 Step 定义 id(定点);input 时为 ''。 */
|
|
275
|
+
readonly target?: string | undefined
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** 统一时间线条目(Step 卡片 / 用户气泡,按时间顺序)。 */
|
|
279
|
+
export type TimelineItem = TimelineStepItem | TimelineUserItem
|
|
280
|
+
|
|
281
|
+
/** 相对时间展示结果(由组件用字典文案渲染)。 */
|
|
282
|
+
export type RelativeTime =
|
|
283
|
+
| { readonly kind: 'justNow' }
|
|
284
|
+
| { readonly kind: 'minutes'; readonly count: number }
|
|
285
|
+
| { readonly kind: 'hours'; readonly count: number }
|
|
286
|
+
| { readonly kind: 'days'; readonly count: number }
|
|
287
|
+
| { readonly kind: 'date'; readonly text: string }
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* 计算相对时间(ISO → 刚刚 / N 分钟前 / N 小时前 / N 天前;跨 7 天回退短日期)。
|
|
291
|
+
* @param iso - 目标时间(ISO 串),undefined 返回 undefined。
|
|
292
|
+
* @param nowIso - 基准「现在」(ISO 串)。
|
|
293
|
+
* @returns 相对时间结果或 undefined。
|
|
294
|
+
*/
|
|
295
|
+
export function relativeTimeOf(iso: string | undefined, nowIso: string): RelativeTime | undefined {
|
|
296
|
+
if (iso === undefined) return undefined
|
|
297
|
+
const then = Date.parse(iso)
|
|
298
|
+
const now = Date.parse(nowIso)
|
|
299
|
+
if (Number.isNaN(then) || Number.isNaN(now)) return undefined
|
|
300
|
+
const diffMs = now - then
|
|
301
|
+
if (diffMs < 60_000) return { kind: 'justNow' }
|
|
302
|
+
const minutes = Math.floor(diffMs / 60_000)
|
|
303
|
+
if (minutes < 60) return { kind: 'minutes', count: minutes }
|
|
304
|
+
const hours = Math.floor(minutes / 60)
|
|
305
|
+
if (hours < 24) return { kind: 'hours', count: hours }
|
|
306
|
+
const days = Math.floor(hours / 24)
|
|
307
|
+
if (days < 7) return { kind: 'days', count: days }
|
|
308
|
+
const date = new Date(then)
|
|
309
|
+
const pad = (value: number): string => String(value).padStart(2, '0')
|
|
310
|
+
return {
|
|
311
|
+
kind: 'date',
|
|
312
|
+
text: `${date.getFullYear()}/${pad(date.getMonth() + 1)}/${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** 把任意载荷值缩略为单行文本(供 tool 消息行展示)。 */
|
|
317
|
+
function summarizeValue(value: unknown): string | undefined {
|
|
318
|
+
if (value === undefined || value === null) return undefined
|
|
319
|
+
if (typeof value === 'string') return value === '' ? undefined : value
|
|
320
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value)
|
|
321
|
+
try {
|
|
322
|
+
const text = JSON.stringify(value)
|
|
323
|
+
if (text === undefined) return undefined
|
|
324
|
+
return text.length > 160 ? `${text.slice(0, 160)}…` : text
|
|
325
|
+
} catch {
|
|
326
|
+
return String(value)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 把 step 的 sessionMessages 派生为展示行(agent 内嵌消息框 / 失败列表用)。
|
|
332
|
+
* tool 消息 → `┌ tool\n└ 输出摘要`(mono 行);text/reasoning → 正文行。
|
|
333
|
+
* @param step - 已执行 Step 快照。
|
|
334
|
+
* @returns 展示行列表;无消息返回 undefined。
|
|
335
|
+
*/
|
|
336
|
+
export function chatLinesOf(step: ExecutedStep): ChatLine[] | undefined {
|
|
337
|
+
const messages = step.sessionMessages
|
|
338
|
+
if (messages === undefined || messages.length === 0) return undefined
|
|
339
|
+
const lines: ChatLine[] = []
|
|
340
|
+
for (const message of messages) {
|
|
341
|
+
if (message.type === 'tool') {
|
|
342
|
+
const head = `┌ ${message.tool ?? 'tool'}`
|
|
343
|
+
const output = summarizeValue(message.output)
|
|
344
|
+
lines.push({
|
|
345
|
+
who: `${step.name} · tool`,
|
|
346
|
+
text: output === undefined ? head : `${head}\n└ ${output}`,
|
|
347
|
+
kind: 'tool',
|
|
348
|
+
})
|
|
349
|
+
} else {
|
|
350
|
+
const text = message.text
|
|
351
|
+
if (text === undefined || text === '') continue
|
|
352
|
+
lines.push({ who: `${step.name} · agent`, text, kind: 'assistant' })
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return lines.length === 0 ? undefined : lines
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 把 `output: ContentBlock[]` 的 text 块拼接为纯文本(无 text 块返回 undefined)。
|
|
360
|
+
* @param output - Step 执行输出块(引擎在 step-completed 事件/快照中内联)。
|
|
361
|
+
* @returns 拼接文本或 undefined。
|
|
362
|
+
*/
|
|
363
|
+
export function outputTextOf(output: readonly { readonly type: string; readonly text?: string }[] | undefined): string | undefined {
|
|
364
|
+
if (output === undefined || output.length === 0) return undefined
|
|
365
|
+
const parts: string[] = []
|
|
366
|
+
for (const block of output) {
|
|
367
|
+
if (block.type === 'text' && block.text !== undefined) parts.push(block.text)
|
|
368
|
+
}
|
|
369
|
+
return parts.length === 0 ? undefined : parts.join('\n')
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** 循环迭代路径 → 展示标签(`#1.2`),非循环返回 undefined。 */
|
|
373
|
+
export function loopLabelOf(loopIteration: readonly number[] | undefined): string | undefined {
|
|
374
|
+
return loopIteration === undefined || loopIteration.length === 0
|
|
375
|
+
? undefined
|
|
376
|
+
: `#${loopIteration.join('.')}`
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** 取文件路径的 basename(兼容 / 与 \ 分隔;无分隔符时原样返回)。 */
|
|
380
|
+
export function fileBaseName(file: string): string {
|
|
381
|
+
const parts = file.split(/[\\/]/)
|
|
382
|
+
return parts[parts.length - 1] ?? file
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* 从 `WorkflowTaskState` 构建树形导航数据:workflow → stage → step → result/artifact 叶子。
|
|
387
|
+
* result 叶子来自 step 输出文本;artifact 叶子来自 `stageContracts` 产物契约的 files。
|
|
388
|
+
*
|
|
389
|
+
* Round 26:**完全按 `state.executedSteps` 渲染**——
|
|
390
|
+
* ① `executedSteps` 里**每条执行段**都展示(含 paused/failed/同 stepId 多次 visit 的
|
|
391
|
+
* 每一段,不做 attemptIndex 聚合);
|
|
392
|
+
* ② step 按自身 `stageId` 归组到 stage 下;
|
|
393
|
+
* ③ **只展示「有 step 执行记录」的 stage**(空 stage / 尚未执行的 stage 不展示);
|
|
394
|
+
* ④ stage 与 step 均按**创建顺序**(executedSteps 数组序 = 引擎追加序)排列。
|
|
395
|
+
* ⑤ **返回结果按执行段挂载**(每段只挂自己这段的 output 文本);**产物按 step 级
|
|
396
|
+
* 归并、只挂到该 stepId 的「最后一段」**(loop 多次 visit 产出同一 outputTo 文件,
|
|
397
|
+
* 挂一次展示最新产物状态,避免每段重复)。
|
|
398
|
+
* 无 `stageId` 的异常段归入合成 stage「未分组」兜底(保证所有 step 可见)。
|
|
399
|
+
* @param state - 实例状态快照。
|
|
400
|
+
* @returns 树形导航根节点列表(单个 workflow 根)。
|
|
401
|
+
*/
|
|
402
|
+
export function buildRunTree(state: WorkflowTaskState): RunTreeNode[] {
|
|
403
|
+
// 返回结果:按「执行段 id(step.id)」挂载——每段只显示自己这段的 output。
|
|
404
|
+
const resultByExecId = new Map<string, RunTreeNode[]>()
|
|
405
|
+
for (const step of state.executedSteps) {
|
|
406
|
+
const output = outputTextOf(step.output)
|
|
407
|
+
if (output === undefined) continue
|
|
408
|
+
const current = resultByExecId.get(step.id) ?? []
|
|
409
|
+
resultByExecId.set(step.id, [...current, {
|
|
410
|
+
id: `${step.id}:result`,
|
|
411
|
+
name: '',
|
|
412
|
+
type: 'result',
|
|
413
|
+
stepId: stepIdOf(step),
|
|
414
|
+
text: output,
|
|
415
|
+
}])
|
|
416
|
+
}
|
|
417
|
+
// 产物(Round 26):优先**段快照** `executedStep.artifacts`(step 完成收尾时引擎
|
|
418
|
+
// collectStepArtifacts 收集写入——运行中即可展示,真正按执行段归属);
|
|
419
|
+
// 旧数据/历史实例无该字段时**回退 stage 契约**(stageContracts 的 artifacts 无执行段
|
|
420
|
+
// id,按定义 stepId 归并、只挂到该 stepId 的「最后一段」)。
|
|
421
|
+
const artifactByExecId = new Map<string, RunTreeNode[]>()
|
|
422
|
+
const artifactByStepId = new Map<string, RunTreeNode[]>()
|
|
423
|
+
for (const step of state.executedSteps) {
|
|
424
|
+
for (const file of step.artifacts ?? []) {
|
|
425
|
+
const current = artifactByExecId.get(step.id) ?? []
|
|
426
|
+
artifactByExecId.set(step.id, [...current, {
|
|
427
|
+
id: `${step.id}:file:${file}`,
|
|
428
|
+
// 导航只展示文件名(basename);完整路径保留在 text(预览用)。
|
|
429
|
+
name: fileBaseName(file),
|
|
430
|
+
type: 'artifact',
|
|
431
|
+
stepId: stepIdOf(step),
|
|
432
|
+
text: file,
|
|
433
|
+
}])
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const contract of Object.values(state.stageContracts)) {
|
|
437
|
+
for (const artifact of contract.artifacts) {
|
|
438
|
+
for (const file of artifact.files) {
|
|
439
|
+
const current = artifactByStepId.get(artifact.stepId) ?? []
|
|
440
|
+
artifactByStepId.set(artifact.stepId, [...current, {
|
|
441
|
+
id: `${artifact.stepId}:file:${file}`,
|
|
442
|
+
name: fileBaseName(file),
|
|
443
|
+
type: 'artifact',
|
|
444
|
+
stepId: artifact.stepId,
|
|
445
|
+
meta: artifact.outputToId,
|
|
446
|
+
text: file,
|
|
447
|
+
}])
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// 每个 stepId 的「最后一段」执行 id(数组顺序 = 创建顺序 → 最后一个为最新段)。
|
|
452
|
+
const lastSegmentByStepId = new Map<string, string>()
|
|
453
|
+
for (const step of state.executedSteps) {
|
|
454
|
+
lastSegmentByStepId.set(stepIdOf(step), step.id)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Round 26:stage/step 全部从 executedSteps 派生(创建顺序 = 数组顺序);
|
|
458
|
+
// stage 元信息(name/status)从 stageInfos 查表,查不到回退 stageId。
|
|
459
|
+
const stageMeta = new Map(state.stageInfos.map(stage => [stage.id, stage]))
|
|
460
|
+
const stageOrder: string[] = []
|
|
461
|
+
const stepsByStage = new Map<string, RunTreeNode[]>()
|
|
462
|
+
for (const step of state.executedSteps) {
|
|
463
|
+
const stageId = step.stageId ?? UNASSIGNED_STAGE_ID
|
|
464
|
+
if (!stageOrder.includes(stageId)) stageOrder.push(stageId)
|
|
465
|
+
const key = stepIdOf(step)
|
|
466
|
+
const children = [
|
|
467
|
+
...(resultByExecId.get(step.id) ?? []),
|
|
468
|
+
// 段快照产物(按段)优先;无段快照(旧数据)时回退 stage 契约产物(挂最后一段)。
|
|
469
|
+
...(artifactByExecId.get(step.id) ?? []),
|
|
470
|
+
...(artifactByExecId.has(step.id) ? [] : (
|
|
471
|
+
lastSegmentByStepId.get(key) === step.id ? (artifactByStepId.get(key) ?? []) : []
|
|
472
|
+
)),
|
|
473
|
+
]
|
|
474
|
+
const node: RunTreeNode = {
|
|
475
|
+
id: step.id,
|
|
476
|
+
name: step.name,
|
|
477
|
+
type: 'step',
|
|
478
|
+
status: step.status,
|
|
479
|
+
stepId: key,
|
|
480
|
+
childId: step.sessionMeta?.childId,
|
|
481
|
+
children: children.length > 0 ? children : undefined,
|
|
482
|
+
}
|
|
483
|
+
const list = stepsByStage.get(stageId) ?? []
|
|
484
|
+
list.push(node)
|
|
485
|
+
stepsByStage.set(stageId, list)
|
|
486
|
+
}
|
|
487
|
+
const stageNodes = stageOrder.map(stageId => {
|
|
488
|
+
const meta = stageMeta.get(stageId)
|
|
489
|
+
return {
|
|
490
|
+
id: stageId,
|
|
491
|
+
name: meta?.name ?? stageId,
|
|
492
|
+
type: 'stage' as const,
|
|
493
|
+
status: meta?.status,
|
|
494
|
+
children: stepsByStage.get(stageId),
|
|
495
|
+
}
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
return [{
|
|
499
|
+
id: state.workflowId,
|
|
500
|
+
name: state.workflowName ?? state.workflowId,
|
|
501
|
+
type: 'workflow' as const,
|
|
502
|
+
status: state.status,
|
|
503
|
+
children: stageNodes,
|
|
504
|
+
}]
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Round 26:无 stageId 的异常执行段归入的合成 stage id(名称「未分组」)。 */
|
|
508
|
+
export const UNASSIGNED_STAGE_ID = '_unassigned'
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* 段模型下按「最后活跃段」聚合 Step 列表(DiscussionInput @Step 定点恢复用)。
|
|
512
|
+
*
|
|
513
|
+
* 同 stepId 多次执行段(暂停续跑/重试/ask 续问)只保留 attemptIndex 最大的段,
|
|
514
|
+
* 避免 @Step 选择器重复展示同一 Step 的多个执行段。
|
|
515
|
+
* @param steps - 实例的 executedSteps(可能含同 stepId 多段)。
|
|
516
|
+
* @returns 按 stepId 去重后的 Step 快照列表(取最后活跃段;非子代理 Step 原样保留)。
|
|
517
|
+
*/
|
|
518
|
+
export function lastActiveSegmentsOf(steps: readonly ExecutedStep[]): ExecutedStep[] {
|
|
519
|
+
const byStepId = new Map<string, ExecutedStep>()
|
|
520
|
+
for (const step of steps) {
|
|
521
|
+
const key = stepIdOf(step)
|
|
522
|
+
const current = byStepId.get(key)
|
|
523
|
+
if (current === undefined || (step.attemptIndex ?? 0) >= (current.attemptIndex ?? 0)) {
|
|
524
|
+
byStepId.set(key, step)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return [...byStepId.values()]
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 从 `WorkflowTaskState` 构建统一时间线(段模型 + 用户消息统一排序)。
|
|
532
|
+
*
|
|
533
|
+
* 三路来源严格按 `createdAt` 排序合并:
|
|
534
|
+
* 1. `userMessages`(input/resume/retry/message,含原始需求——初始 input 消息
|
|
535
|
+
* 由引擎 makeInitialState 写入,前端不再单独拼 `state.originalInput` 气泡;
|
|
536
|
+
* Round 26:**过滤 kind:'answer'**——Phase A ask 的回答是给子代理的内部闭环
|
|
537
|
+
* 消息,不注入 discussion 时间轴;历史实例已落盘的 answer 记录同样隐藏)
|
|
538
|
+
* 2. executedSteps 各**执行段**(`createdAt` 为段开始时间;同 stepId 多次执行段
|
|
539
|
+
* 各自成卡,`attemptIndex` 标注第几次)
|
|
540
|
+
* 3. running 卡无实时消息流(引擎不写 sessionMessages、无 step-message 事件),
|
|
541
|
+
* body 按状态展示输出文本 / 错误 / 运行中占位。
|
|
542
|
+
* @param state - 实例状态快照。
|
|
543
|
+
* @returns 时间线条目列表(按 createdAt 升序)。
|
|
544
|
+
*/
|
|
545
|
+
export function buildTimeline(state: WorkflowTaskState): TimelineItem[] {
|
|
546
|
+
const items: TimelineItem[] = []
|
|
547
|
+
|
|
548
|
+
for (const message of state.userMessages ?? []) {
|
|
549
|
+
// Round 26:回答不进讨论页(Phase A ask 回答已 followup 注回子代理)。
|
|
550
|
+
if (message.kind === 'answer') continue
|
|
551
|
+
items.push({
|
|
552
|
+
kind: 'user',
|
|
553
|
+
content: message.content,
|
|
554
|
+
time: message.createdAt,
|
|
555
|
+
messageKind: message.kind,
|
|
556
|
+
target: message.target,
|
|
557
|
+
})
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (const step of state.executedSteps) {
|
|
561
|
+
items.push({
|
|
562
|
+
kind: 'step',
|
|
563
|
+
stepId: stepIdOf(step),
|
|
564
|
+
name: step.name,
|
|
565
|
+
status: step.status,
|
|
566
|
+
loopLabel: loopLabelOf(step.loopIteration),
|
|
567
|
+
time: step.createdAt ?? step.updatedAt ?? step.startedAt,
|
|
568
|
+
outputText: outputTextOf(step.output),
|
|
569
|
+
error: step.error,
|
|
570
|
+
finalizePhase: step.finalizeMeta?.currentPhase,
|
|
571
|
+
finalizePreviousPhase: step.finalizeMeta?.previousPhase,
|
|
572
|
+
finalizeErrorDetail: step.finalizeMeta?.errorDetail ?? step.error,
|
|
573
|
+
finalizeAssessErrorDetail: step.finalizeMeta?.assessResult?.errorDetail,
|
|
574
|
+
finalizeAssessSessionId: step.finalizeMeta?.assessResult?.forkSessionId,
|
|
575
|
+
finalizeGateSessionId: step.finalizeMeta?.gateResult?.gateSessionId,
|
|
576
|
+
finalizeAssessResult: step.finalizeMeta?.assessResult !== undefined
|
|
577
|
+
? {
|
|
578
|
+
assessmentRaw: step.finalizeMeta.assessResult.assessmentRaw,
|
|
579
|
+
...(step.finalizeMeta.assessResult.compressedOutput !== undefined
|
|
580
|
+
? { compressedOutput: step.finalizeMeta.assessResult.compressedOutput }
|
|
581
|
+
: {}),
|
|
582
|
+
...(step.finalizeMeta.assessResult.status !== undefined
|
|
583
|
+
? { status: step.finalizeMeta.assessResult.status }
|
|
584
|
+
: {}),
|
|
585
|
+
}
|
|
586
|
+
: undefined,
|
|
587
|
+
finalizeGateResult: step.finalizeMeta?.gateResult !== undefined
|
|
588
|
+
? {
|
|
589
|
+
gatesPassed: step.finalizeMeta.gateResult.gatesPassed,
|
|
590
|
+
...(step.finalizeMeta.gateResult.status !== undefined
|
|
591
|
+
? { status: step.finalizeMeta.gateResult.status }
|
|
592
|
+
: {}),
|
|
593
|
+
}
|
|
594
|
+
: undefined,
|
|
595
|
+
finalizeAskResult: step.finalizeMeta?.askResult !== undefined
|
|
596
|
+
? {
|
|
597
|
+
hasAsk: step.finalizeMeta.askResult.hasAsk,
|
|
598
|
+
...(step.finalizeMeta.askResult.askQuestion !== undefined ? { askQuestion: step.finalizeMeta.askResult.askQuestion } : {}),
|
|
599
|
+
...(step.finalizeMeta.askResult.status !== undefined
|
|
600
|
+
? { status: step.finalizeMeta.askResult.status }
|
|
601
|
+
: {}),
|
|
602
|
+
}
|
|
603
|
+
: undefined,
|
|
604
|
+
finalizeRoutingResult: step.finalizeMeta?.routingResult !== undefined
|
|
605
|
+
? {
|
|
606
|
+
...(step.finalizeMeta.routingResult.branchMeta !== undefined ? { branchMeta: step.finalizeMeta.routingResult.branchMeta } : {}),
|
|
607
|
+
...(step.finalizeMeta.routingResult.loopMeta !== undefined ? { loopMeta: step.finalizeMeta.routingResult.loopMeta } : {}),
|
|
608
|
+
...(step.finalizeMeta.routingResult.status !== undefined
|
|
609
|
+
? { status: step.finalizeMeta.routingResult.status }
|
|
610
|
+
: {}),
|
|
611
|
+
}
|
|
612
|
+
: undefined,
|
|
613
|
+
finalizeRoutingSessionId: step.finalizeMeta?.routingResult?.routingSessionId,
|
|
614
|
+
// Round 11:各阶段独立 status 字段(顶层,供 PhaseBadge 直接读取)
|
|
615
|
+
...(step.finalizeMeta?.askResult?.status !== undefined ? { askStatus: step.finalizeMeta.askResult.status } : {}),
|
|
616
|
+
...(step.finalizeMeta?.assessResult?.status !== undefined ? { assessStatus: step.finalizeMeta.assessResult.status } : {}),
|
|
617
|
+
...(step.finalizeMeta?.gateResult?.status !== undefined ? { gateStatus: step.finalizeMeta.gateResult.status } : {}),
|
|
618
|
+
...(step.finalizeMeta?.routingResult?.status !== undefined ? { routingStatus: step.finalizeMeta.routingResult.status } : {}),
|
|
619
|
+
childId: step.sessionMeta?.childId,
|
|
620
|
+
commandType: step.sessionMeta?.command,
|
|
621
|
+
synthetic: step.sessionMeta?.synthetic,
|
|
622
|
+
messages: chatLinesOf(step),
|
|
623
|
+
visitCount: step.visitCount,
|
|
624
|
+
attemptIndex: step.attemptIndex,
|
|
625
|
+
pausedAt: step.pausedAt,
|
|
626
|
+
})
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// 统一时间轴:严格按 createdAt 排序(旧实例缺 createdAt 的段排最后)。
|
|
630
|
+
items.sort((a, b) => {
|
|
631
|
+
const ta = a.time === undefined ? '' : a.time
|
|
632
|
+
const tb = b.time === undefined ? '' : b.time
|
|
633
|
+
return ta < tb ? -1 : ta > tb ? 1 : 0
|
|
634
|
+
})
|
|
635
|
+
return items
|
|
636
|
+
}
|