@goodandready/dsh-agent-orchestrator 0.1.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 +22 -0
- package/README.md +184 -0
- package/README.ru.md +86 -0
- package/README.zh.md +64 -0
- package/cordis.patch.yml +5 -0
- package/lib/client.js +1374 -0
- package/lib/http-guard.js +166 -0
- package/lib/index.js +950 -0
- package/lib/integrations/kanban-bridge.js +102 -0
- package/lib/pipeline/cache-prefixer.js +226 -0
- package/lib/pipeline/concurrency-gate.js +110 -0
- package/lib/pipeline/dag-engine.js +290 -0
- package/lib/pipeline/decision-trace.js +124 -0
- package/lib/pipeline/decomposer.js +102 -0
- package/lib/pipeline/delegation.js +625 -0
- package/lib/pipeline/guidance.js +94 -0
- package/lib/pipeline/intent.js +98 -0
- package/lib/pipeline/intersection.js +235 -0
- package/lib/pipeline/model-selection.js +742 -0
- package/lib/pipeline/preset-sync.js +84 -0
- package/lib/pipeline/scenarios.js +468 -0
- package/lib/pipeline/session-lifecycle.js +520 -0
- package/lib/pipeline/snapshots.js +240 -0
- package/lib/pipeline/token-watchdog.js +101 -0
- package/lib/pipeline/worker-pool.js +143 -0
- package/lib/routes.js +277 -0
- package/lib/store.js +176 -0
- package/package.json +94 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Harness Multi-Agent Orchestrator Browser Client
|
|
3
|
+
*
|
|
4
|
+
* Scoped ID: @goodandready/dsh-agent-orchestrator
|
|
5
|
+
* Namespace: dsh-agent-orchestrator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
window.__ModuleLoader__.load({
|
|
9
|
+
id: '@goodandready/dsh-agent-orchestrator',
|
|
10
|
+
factory: (require) => {
|
|
11
|
+
const module = { exports: {} }
|
|
12
|
+
const React = require('react')
|
|
13
|
+
const { useState, useEffect, useCallback } = React
|
|
14
|
+
const h = React.createElement
|
|
15
|
+
|
|
16
|
+
const NS = 'dsh-agent-orchestrator'
|
|
17
|
+
|
|
18
|
+
// ErrorBoundary to guard against any component rendering exceptions
|
|
19
|
+
class ErrorBoundary extends React.Component {
|
|
20
|
+
constructor(props) {
|
|
21
|
+
super(props)
|
|
22
|
+
this.state = { hasError: false, error: null }
|
|
23
|
+
}
|
|
24
|
+
static getDerivedStateFromError(error) {
|
|
25
|
+
return { hasError: true, error }
|
|
26
|
+
}
|
|
27
|
+
componentDidCatch(error, errorInfo) {
|
|
28
|
+
console.error('[dsh-agent-orchestrator] UI render error:', error, errorInfo)
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
if (this.state.hasError) {
|
|
32
|
+
return h('div', { style: { padding: '12px 16px', color: 'var(--dsw-alias-state-error-primary)', fontSize: 13 } },
|
|
33
|
+
h('strong', null, 'Orchestrator UI Error: '),
|
|
34
|
+
String(this.state.error?.message || this.state.error),
|
|
35
|
+
h('button', {
|
|
36
|
+
type: 'button',
|
|
37
|
+
style: { marginLeft: 12, padding: '2px 8px', fontSize: 11, cursor: 'pointer' },
|
|
38
|
+
onClick: () => this.setState({ hasError: false, error: null }),
|
|
39
|
+
}, 'Retry')
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
return this.props?.children || null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// System Chevron icon helper
|
|
47
|
+
let ChevronIcon = null
|
|
48
|
+
try {
|
|
49
|
+
const primitives = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
50
|
+
ChevronIcon = primitives && primitives.IconChevronDownOutline14
|
|
51
|
+
} catch {
|
|
52
|
+
ChevronIcon = null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const FallbackChevron = () =>
|
|
56
|
+
h('svg', {
|
|
57
|
+
width: 14,
|
|
58
|
+
height: 14,
|
|
59
|
+
viewBox: '0 0 14 14',
|
|
60
|
+
fill: 'none',
|
|
61
|
+
stroke: 'currentColor',
|
|
62
|
+
strokeWidth: 1.5,
|
|
63
|
+
}, h('path', { d: 'M3.5 5.25L7 8.75L10.5 5.25', strokeLinecap: 'round', strokeLinejoin: 'round' }))
|
|
64
|
+
|
|
65
|
+
const Chevron = ChevronIcon || FallbackChevron
|
|
66
|
+
|
|
67
|
+
// CSS Theme Styles Injection
|
|
68
|
+
const CSS = `
|
|
69
|
+
|
|
70
|
+
.dso-filter-btn { appearance: none; font: inherit; background: transparent; border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; padding: 2px 8px; font-size: 11px; color: var(--dsw-alias-label-secondary); cursor: pointer; transition: all 0.12s ease; }
|
|
71
|
+
.dso-filter-btn:hover { background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); }
|
|
72
|
+
.dso-filter-btn.active { background: var(--dsw-alias-bg-layer-3); border-color: var(--dsw-alias-state-info-border); color: var(--dsw-alias-state-info-primary); font-weight: 600; }
|
|
73
|
+
.dso-toggle-label { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary); cursor: pointer; }
|
|
74
|
+
.dso-toggle-checkbox { width: 14px; height: 14px; cursor: pointer; }
|
|
75
|
+
.dso-preset-btn { appearance: none; font: inherit; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; padding: 3px 8px; font-size: 11px; font-weight: 500; color: var(--dsw-alias-label-primary); cursor: pointer; display: inline-flex; align-items: center; gap: 4px; transition: all 0.12s ease; }
|
|
76
|
+
.dso-preset-btn:hover { background: var(--dsw-alias-bg-layer-4); border-color: var(--dsw-alias-state-info-border); }
|
|
77
|
+
|
|
78
|
+
.dso-card { border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-3); border-radius: 12px; list-style: none; margin-bottom: 12px; }
|
|
79
|
+
.dso-head { appearance: none; width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer; background: 0 0; border: 0; border-radius: 12px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
|
|
80
|
+
.dso-head-info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
|
|
81
|
+
.dso-title { color: var(--dsw-alias-label-primary); font-size: 15px; font-weight: 600; line-height: 1.4; display: flex; align-items: center; gap: 8px; }
|
|
82
|
+
.dso-badge { font-size: 11px; font-weight: 500; padding: 2px 7px; border-radius: 6px; background: var(--dsw-alias-state-info-bg); color: var(--dsw-alias-state-info-primary); border: 1px solid var(--dsw-alias-state-info-border); }
|
|
83
|
+
.dso-sub { color: var(--dsw-alias-label-secondary); font-size: 13px; }
|
|
84
|
+
.dso-chev { margin-left: auto; flex: none; color: var(--dsw-alias-label-tertiary); transition: transform 0.16s ease; }
|
|
85
|
+
.dso-chev-open { transform: rotate(180deg); }
|
|
86
|
+
.dso-body { border-top: 1px solid var(--dsw-alias-border-l2); margin: 0 16px; padding: 12px 0 16px; }
|
|
87
|
+
|
|
88
|
+
.dso-tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--dsw-alias-border-l2); padding-bottom: 10px; margin-bottom: 14px; overflow-x: auto; }
|
|
89
|
+
.dso-tab { appearance: none; background: transparent; border: 1px solid transparent; border-radius: 7px; padding: 5px 12px; font-size: 13px; color: var(--dsw-alias-label-secondary); cursor: pointer; font-weight: 500; transition: all 0.15s; }
|
|
90
|
+
.dso-tab:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); }
|
|
91
|
+
.dso-tab-active { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); border-color: var(--dsw-alias-border-l2); }
|
|
92
|
+
|
|
93
|
+
.dso-panel { display: flex; flex-direction: column; gap: 14px; }
|
|
94
|
+
.dso-agent-list { display: flex; flex-direction: column; gap: 10px; max-height: 480px; overflow-y: auto; padding-right: 4px; }
|
|
95
|
+
.dso-agent-row { border: 1px solid var(--dsw-alias-border-l2); border-radius: 9px; padding: 12px; background: var(--dsw-alias-bg-layer-2); display: flex; flex-direction: column; gap: 8px; }
|
|
96
|
+
.dso-agent-header { display: flex; align-items: center; justify-content: space-between; }
|
|
97
|
+
.dso-agent-name { font-size: 14px; font-weight: 600; color: var(--dsw-alias-label-primary); }
|
|
98
|
+
.dso-agent-role { font-size: 12px; color: var(--dsw-alias-label-secondary); }
|
|
99
|
+
|
|
100
|
+
.dso-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
|
101
|
+
.dso-field { display: flex; flex-direction: column; gap: 4px; }
|
|
102
|
+
.dso-label { font-size: 12px; font-weight: 500; color: var(--dsw-alias-label-secondary); }
|
|
103
|
+
.dso-input, .dso-select { height: 32px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); border-radius: 7px; padding: 0 10px; font-size: 13px; outline: none; }
|
|
104
|
+
.dso-textarea { border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); border-radius: 7px; padding: 8px 10px; font-size: 12px; font-family: monospace; outline: none; resize: vertical; min-height: 70px; }
|
|
105
|
+
|
|
106
|
+
.dso-stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 12px; }
|
|
107
|
+
.dso-stat-box { border: 1px solid var(--dsw-alias-border-l2); border-radius: 9px; padding: 12px; text-align: center; background: var(--dsw-alias-bg-layer-2); }
|
|
108
|
+
.dso-stat-val { font-size: 20px; font-weight: 700; color: var(--dsw-alias-state-success-primary); }
|
|
109
|
+
.dso-stat-lbl { font-size: 12px; color: var(--dsw-alias-label-secondary); margin-top: 4px; }
|
|
110
|
+
|
|
111
|
+
.dso-foot { border-top: 1px solid var(--dsw-alias-border-l2); display: flex; justify-content: flex-end; align-items: center; gap: 8px; padding-top: 12px; margin-top: 10px; }
|
|
112
|
+
.dso-btn { appearance: none; font: inherit; cursor: pointer; border: 1px solid transparent; border-radius: 8px; padding: 6px 14px; font-size: 13px; font-weight: 500; }
|
|
113
|
+
.dso-btn-primary { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-3); }
|
|
114
|
+
.dso-btn-subtle { background: transparent; border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); }
|
|
115
|
+
|
|
116
|
+
.dso-widget { display: inline-flex; align-items: center; gap: 8px; padding: 4px 10px; border-radius: 8px; background: var(--dsw-alias-state-info-bg); border: 1px solid var(--dsw-alias-state-info-border); font-size: 12px; color: var(--dsw-alias-label-primary); }
|
|
117
|
+
.dso-widget-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-state-success-primary); box-shadow: 0 0 6px var(--dsw-alias-state-success-primary); }
|
|
118
|
+
.dso-widget-badge { font-weight: 600; color: var(--dsw-alias-state-info-primary); }
|
|
119
|
+
|
|
120
|
+
.dso-dock { box-sizing: border-box; width: calc(100% - var(--dsh-composer-side-clearance, 0px) * 2 - var(--dsh-composer-dock-inset, 0px) * 4); max-width: calc(var(--dsh-composer-card-max-width, 768px) - var(--dsh-composer-dock-inset, 0px) * 4); margin: 0 auto 8px auto; display: flex; justify-content: center; }
|
|
121
|
+
.dso-dock.dso-dock-quick { justify-content: flex-start; margin-bottom: 4px; }
|
|
122
|
+
.dso-banner { box-sizing: border-box; width: 100%; display: flex; align-items: center; justify-content: space-between; background: var(--dsw-alias-bg-layer-3); border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; padding: 7px 14px; font-family: inherit; font-size: 13px; color: var(--dsw-alias-label-primary); user-select: none; box-shadow: none; transition: all 0.15s ease; gap: 8px; }
|
|
123
|
+
.dso-banner:hover { border-color: var(--dsw-alias-label-tertiary); }
|
|
124
|
+
.dso-banner.completed { border-color: var(--dsw-alias-state-success-primary); }
|
|
125
|
+
.dso-banner.failed { border-color: var(--dsw-alias-state-error-primary); }
|
|
126
|
+
.dso-banner-left { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
|
|
127
|
+
.dso-banner-badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2); display: inline-flex; align-items: center; gap: 4px; font-weight: 600; white-space: nowrap; background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); }
|
|
128
|
+
.dso-banner-badge.dso-badge-running { border-color: var(--dsw-alias-state-info-border); color: var(--dsw-alias-state-info-primary); background: var(--dsw-alias-state-info-bg); }
|
|
129
|
+
.dso-banner-badge.dso-badge-ok { border-color: var(--dsw-alias-state-success-border); color: var(--dsw-alias-state-success-primary); background: var(--dsw-alias-state-success-bg); }
|
|
130
|
+
.dso-banner-badge.dso-badge-err { border-color: var(--dsw-alias-state-error-border); color: var(--dsw-alias-state-error-primary); background: var(--dsw-alias-state-error-bg); }
|
|
131
|
+
.dso-banner-title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 260px; }
|
|
132
|
+
.dso-banner-stage { font-size: 12px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); padding: 2px 6px; border-radius: 5px; white-space: nowrap; }
|
|
133
|
+
.dso-banner-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
134
|
+
.dso-banner-time { font-size: 12px; color: var(--dsw-alias-label-secondary); font-variant-numeric: tabular-nums; }
|
|
135
|
+
.dso-dock-btn { appearance: none; font: inherit; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; padding: 3px 8px; font-size: 12px; color: var(--dsw-alias-label-primary); cursor: pointer; display: inline-flex; align-items: center; gap: 4px; transition: all 0.12s; }
|
|
136
|
+
.dso-dock-btn:hover { background: var(--dsw-alias-bg-layer-4); border-color: var(--dsw-alias-label-tertiary); }
|
|
137
|
+
.dso-dock-btn.close { padding: 3px 6px; color: var(--dsw-alias-label-secondary); }
|
|
138
|
+
.dso-dock-btn.close:hover { color: var(--dsw-alias-state-error-primary); }
|
|
139
|
+
.dso-quicklaunch-btn { appearance: none; font: inherit; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px; padding: 4px 12px; font-size: 12px; font-weight: 500; color: var(--dsw-alias-label-secondary); cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.12s ease; }
|
|
140
|
+
.dso-quicklaunch-btn:hover { background: var(--dsw-alias-bg-layer-3); border-color: var(--dsw-alias-label-tertiary); color: var(--dsw-alias-label-primary); }
|
|
141
|
+
.dso-pulse-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-state-info-primary); animation: dso-pulse 1.6s infinite ease-in-out; }
|
|
142
|
+
@keyframes dso-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.8); } }
|
|
143
|
+
.dso-modal-backdrop { position: fixed; inset: 0; background: var(--dsw-alias-bg-mask, var(--dsw-alias-bg-layer-4)); z-index: 99999; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(2px); }
|
|
144
|
+
.dso-modal { background: var(--dsw-alias-bg-layer-3); border: 1px solid var(--dsw-alias-border-l2); border-radius: 14px; width: 92%; max-width: 580px; max-height: 85vh; display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--dsw-alias-shadow-modal, 0 8px 24px var(--dsw-alias-border-l2)); font-family: inherit; color: var(--dsw-alias-label-primary); }
|
|
145
|
+
.dso-modal-head { padding: 14px 18px; border-bottom: 1px solid var(--dsw-alias-border-l2); display: flex; align-items: center; justify-content: space-between; font-weight: 600; font-size: 15px; }
|
|
146
|
+
.dso-modal-body { padding: 16px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
|
|
147
|
+
.dso-modal-foot { padding: 12px 18px; border-top: 1px solid var(--dsw-alias-border-l2); display: flex; justify-content: flex-end; gap: 8px; }
|
|
148
|
+
.dso-stage-item { border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; padding: 10px 12px; background: var(--dsw-alias-bg-layer-2); display: flex; flex-direction: column; gap: 6px; }
|
|
149
|
+
.dso-stage-head { display: flex; align-items: center; justify-content: space-between; font-size: 13px; font-weight: 500; }
|
|
150
|
+
.dso-stage-log { font-family: monospace; font-size: 11px; background: var(--dsw-alias-bg-layer-1); padding: 8px; border-radius: 6px; max-height: 120px; overflow-y: auto; white-space: pre-wrap; color: var(--dsw-alias-label-secondary); }
|
|
151
|
+
/* Issue #3: Subagent Header Utilities Chip */
|
|
152
|
+
.dso-subagent-header-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px; padding: 3px 10px; font-size: 12px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
153
|
+
.dso-chip-icon { font-size: 13px; }
|
|
154
|
+
.dso-chip-role { font-weight: 600; color: var(--dsw-alias-state-info-primary); }
|
|
155
|
+
.dso-chip-sep { color: var(--dsw-alias-label-tertiary); }
|
|
156
|
+
.dso-chip-model { font-size: 11px; color: var(--dsw-alias-label-secondary); }
|
|
157
|
+
.dso-chip-parent-link { color: var(--dsw-alias-link); text-decoration: none; font-size: 11px; padding: 1px 6px; background: var(--dsw-alias-state-info-bg); border-radius: 4px; transition: all 0.15s ease; cursor: pointer; }
|
|
158
|
+
.dso-chip-parent-link:hover { background: var(--dsw-alias-bg-layer-3); text-decoration: underline; }
|
|
159
|
+
|
|
160
|
+
/* Issue #5: Specialist Toolview Card */
|
|
161
|
+
.dso-toolview-card { background: var(--dsw-alias-bg-layer-3); border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; padding: 12px 14px; margin: 6px 0; display: flex; flex-direction: column; gap: 10px; font-family: inherit; font-size: 13px; color: var(--dsw-alias-label-primary); }
|
|
162
|
+
.dso-toolview-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
163
|
+
.dso-toolview-badge { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 13px; }
|
|
164
|
+
.dso-toolview-model-tag { font-size: 11px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); padding: 2px 7px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l1); }
|
|
165
|
+
.dso-model-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 500; padding: 2px 7px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-secondary); cursor: help; white-space: nowrap; user-select: none; }
|
|
166
|
+
.dso-model-chip.reasoner { border-color: var(--dsw-alias-state-warning-border, var(--dsw-alias-border-l2)); color: var(--dsw-alias-state-warning-text, var(--dsw-alias-label-primary)); background: var(--dsw-alias-state-warning-bg); }
|
|
167
|
+
.dso-model-chip.coder { border-color: var(--dsw-alias-state-info-border, var(--dsw-alias-border-l2)); color: var(--dsw-alias-state-info-text, var(--dsw-alias-label-primary)); background: var(--dsw-alias-state-info-bg); }
|
|
168
|
+
.dso-model-chip.fast { border-color: var(--dsw-alias-state-success-border, var(--dsw-alias-border-l2)); color: var(--dsw-alias-state-success-text, var(--dsw-alias-label-primary)); background: var(--dsw-alias-state-success-bg); }
|
|
169
|
+
.dso-model-chip.local { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); }
|
|
170
|
+
.dso-toolview-status-pill { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; }
|
|
171
|
+
.dso-toolview-status-pill.ok { background: var(--dsw-alias-state-success-bg); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-border); }
|
|
172
|
+
.dso-toolview-status-pill.running { background: var(--dsw-alias-state-info-bg); color: var(--dsw-alias-state-info-primary); border: 1px solid var(--dsw-alias-state-info-border); }
|
|
173
|
+
.dso-toolview-status-pill.err { background: var(--dsw-alias-state-error-bg); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-border); }
|
|
174
|
+
.dso-toolview-tooldiff { background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
|
|
175
|
+
.dso-tooldiff-summary { cursor: pointer; font-size: 12px; font-weight: 500; color: var(--dsw-alias-label-secondary); display: flex; align-items: center; gap: 6px; user-select: none; }
|
|
176
|
+
.dso-tooldiff-pills { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
177
|
+
.dso-pill { padding: 1px 6px; border-radius: 4px; font-size: 11px; font-family: monospace; }
|
|
178
|
+
.dso-pill.allow { background: var(--dsw-alias-state-success-bg); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-border); }
|
|
179
|
+
.dso-pill.blocked { background: var(--dsw-alias-state-error-bg); color: var(--dsw-alias-state-error-primary); text-decoration: line-through; border: 1px solid var(--dsw-alias-state-error-border); }
|
|
180
|
+
.dso-pill.denied { background: var(--dsw-alias-state-warning-bg); color: var(--dsw-alias-state-warning-primary); border: 1px solid var(--dsw-alias-state-warning-border); }
|
|
181
|
+
.dso-toolview-deliverable { background: var(--dsw-alias-bg-layer-2); border-radius: 6px; padding: 10px 12px; font-size: 13px; line-height: 1.5; max-height: 280px; overflow-y: auto; white-space: pre-wrap; color: var(--dsw-alias-label-primary); border: 1px solid var(--dsw-alias-border-l1); }
|
|
182
|
+
.dso-toolview-callout { padding: 6px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; background: var(--dsw-alias-state-success-bg); border: 1px solid var(--dsw-alias-state-success-border); color: var(--dsw-alias-state-success-primary); display: flex; align-items: center; gap: 6px; }
|
|
183
|
+
|
|
184
|
+
`
|
|
185
|
+
|
|
186
|
+
function formatModelChip(modelStr, options = {}) {
|
|
187
|
+
const m = String(modelStr || '').toLowerCase()
|
|
188
|
+
let label = 'General'
|
|
189
|
+
let alias = modelStr || 'default'
|
|
190
|
+
let badgeClass = 'general'
|
|
191
|
+
|
|
192
|
+
if (/reasoner|r1/i.test(m)) {
|
|
193
|
+
alias = 'R1'
|
|
194
|
+
label = 'Reasoner'
|
|
195
|
+
badgeClass = 'reasoner'
|
|
196
|
+
} else if (/o1/i.test(m)) {
|
|
197
|
+
alias = 'o1'
|
|
198
|
+
label = 'Reasoner'
|
|
199
|
+
badgeClass = 'reasoner'
|
|
200
|
+
} else if (/o3/i.test(m)) {
|
|
201
|
+
alias = 'o3'
|
|
202
|
+
label = 'Reasoner'
|
|
203
|
+
badgeClass = 'reasoner'
|
|
204
|
+
} else if (/deepseek-chat|v3/i.test(m)) {
|
|
205
|
+
alias = 'V3'
|
|
206
|
+
label = 'Fast Coder'
|
|
207
|
+
badgeClass = 'coder'
|
|
208
|
+
} else if (/coder/i.test(m)) {
|
|
209
|
+
alias = 'Coder'
|
|
210
|
+
label = 'Coder'
|
|
211
|
+
badgeClass = 'coder'
|
|
212
|
+
} else if (/sonnet/i.test(m)) {
|
|
213
|
+
alias = 'Sonnet 3.5'
|
|
214
|
+
label = 'Coder'
|
|
215
|
+
badgeClass = 'coder'
|
|
216
|
+
} else if (/flash|mini|haiku/i.test(m)) {
|
|
217
|
+
alias = m.includes('mini') ? 'Mini' : (m.includes('haiku') ? 'Haiku' : 'Flash')
|
|
218
|
+
label = 'Fast'
|
|
219
|
+
badgeClass = 'fast'
|
|
220
|
+
} else if (/qwen/i.test(m)) {
|
|
221
|
+
alias = 'Qwen'
|
|
222
|
+
label = 'Local'
|
|
223
|
+
badgeClass = 'local'
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const maxTokens = options.maxTokens || (/reasoner|r1|o1|o3/i.test(m) ? 8192 : (/mini|flash|fast/i.test(m) ? 2048 : 4096))
|
|
227
|
+
const reasoningText = /reasoner|r1|o1|o3/i.test(m) ? 'Supported' : 'No'
|
|
228
|
+
const tooltip = `${modelStr || 'default'} (Context: 64k, Output: ${maxTokens}, Reasoning: ${reasoningText})`
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
text: `[${label} · ${alias}]`,
|
|
232
|
+
label,
|
|
233
|
+
alias,
|
|
234
|
+
badgeClass,
|
|
235
|
+
tooltip,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function ensureStyles() {
|
|
240
|
+
if (document.querySelector('style[data-dsh-plugin="dsh-agent-orchestrator"]')) return
|
|
241
|
+
const style = document.createElement('style')
|
|
242
|
+
style.dataset.dshPlugin = 'dsh-agent-orchestrator'
|
|
243
|
+
style.textContent = CSS
|
|
244
|
+
document.head.appendChild(style)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// --- Tab 1: Agent Profiles (Issue #101 Enabled Roster / Toggle Filter) ---
|
|
248
|
+
function AgentProfilesTab({ roles = {}, onRoleChange, onAddRole }) {
|
|
249
|
+
const [filterMode, setFilterMode] = useState('all')
|
|
250
|
+
const allRoles = Object.values(roles)
|
|
251
|
+
const roleList = filterMode === 'active' ? allRoles.filter((r) => r.enabled !== false) : allRoles
|
|
252
|
+
|
|
253
|
+
return h('div', { className: 'dso-panel' },
|
|
254
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10 } },
|
|
255
|
+
h('span', { className: 'dso-sub' }, 'Self-contained agent profiles stored purely in plugin settings (no external files).'),
|
|
256
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
257
|
+
h('div', { style: { display: 'inline-flex', gap: 4 } },
|
|
258
|
+
h('button', {
|
|
259
|
+
type: 'button',
|
|
260
|
+
className: `dso-filter-btn ${filterMode === 'all' ? 'active' : ''}`,
|
|
261
|
+
onClick: () => setFilterMode('all'),
|
|
262
|
+
}, `All (${allRoles.length})`),
|
|
263
|
+
h('button', {
|
|
264
|
+
type: 'button',
|
|
265
|
+
className: `dso-filter-btn ${filterMode === 'active' ? 'active' : ''}`,
|
|
266
|
+
onClick: () => setFilterMode('active'),
|
|
267
|
+
}, `Active Only (${allRoles.filter((r) => r.enabled !== false).length})`)
|
|
268
|
+
),
|
|
269
|
+
h('button', {
|
|
270
|
+
className: 'dso-btn dso-btn-subtle',
|
|
271
|
+
type: 'button',
|
|
272
|
+
onClick: onAddRole,
|
|
273
|
+
}, '+ Add Custom Agent')
|
|
274
|
+
)
|
|
275
|
+
),
|
|
276
|
+
h('div', { className: 'dso-agent-list' },
|
|
277
|
+
roleList.map((role) => {
|
|
278
|
+
const isEnabled = role.enabled !== false
|
|
279
|
+
const modelChip = formatModelChip(role.defaultModel?.model, { maxTokens: role.maxTokens })
|
|
280
|
+
return h('div', { key: role.id, className: 'dso-agent-row', style: { opacity: isEnabled ? 1 : 0.65 } },
|
|
281
|
+
h('div', { className: 'dso-agent-header' },
|
|
282
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
283
|
+
h('label', { className: 'dso-toggle-label', title: isEnabled ? 'Agent is Active' : 'Agent is Disabled' },
|
|
284
|
+
h('input', {
|
|
285
|
+
type: 'checkbox',
|
|
286
|
+
className: 'dso-toggle-checkbox',
|
|
287
|
+
checked: isEnabled,
|
|
288
|
+
onChange: (e) => onRoleChange(role.id, { enabled: e.target.checked }),
|
|
289
|
+
}),
|
|
290
|
+
h('span', { style: { fontWeight: 500, color: isEnabled ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-tertiary)' } },
|
|
291
|
+
isEnabled ? 'Active' : 'Disabled'
|
|
292
|
+
)
|
|
293
|
+
),
|
|
294
|
+
h('span', { className: 'dso-agent-name' }, role.name),
|
|
295
|
+
h('span', { className: 'dso-agent-role' }, `(${role.category || role.id})`),
|
|
296
|
+
h('span', { className: `dso-model-chip ${modelChip.badgeClass}`, title: modelChip.tooltip }, modelChip.text)
|
|
297
|
+
),
|
|
298
|
+
h('span', { className: 'dso-badge' }, `Reasoning: ${role.reasoningEffort || 'medium'}`)
|
|
299
|
+
),
|
|
300
|
+
h('div', { className: 'dso-grid-2' },
|
|
301
|
+
h('div', { className: 'dso-field' },
|
|
302
|
+
h('label', { className: 'dso-label' }, 'Model Provider & Name'),
|
|
303
|
+
h('input', {
|
|
304
|
+
className: 'dso-input',
|
|
305
|
+
value: role.defaultModel?.model || '',
|
|
306
|
+
onChange: (e) => onRoleChange(role.id, {
|
|
307
|
+
defaultModel: { ...(role.defaultModel || { provider: 'deepseek' }), model: e.target.value },
|
|
308
|
+
}),
|
|
309
|
+
placeholder: 'e.g. deepseek-chat or claude-3-5-sonnet',
|
|
310
|
+
})
|
|
311
|
+
),
|
|
312
|
+
h('div', { className: 'dso-field' },
|
|
313
|
+
h('label', { className: 'dso-label' }, 'Max Output Tokens (0 = Default)'),
|
|
314
|
+
h('input', {
|
|
315
|
+
className: 'dso-input',
|
|
316
|
+
type: 'number',
|
|
317
|
+
value: role.maxTokens || '',
|
|
318
|
+
onChange: (e) => onRoleChange(role.id, {
|
|
319
|
+
maxTokens: e.target.value ? parseInt(e.target.value, 10) : undefined,
|
|
320
|
+
}),
|
|
321
|
+
placeholder: 'e.g. 2048, 4096, 8192',
|
|
322
|
+
})
|
|
323
|
+
)
|
|
324
|
+
),
|
|
325
|
+
h('div', { className: 'dso-field' },
|
|
326
|
+
h('label', { className: 'dso-label' }, 'Reasoning Effort (Supported on Reasoning Models)'),
|
|
327
|
+
h('select', {
|
|
328
|
+
className: 'dso-select',
|
|
329
|
+
value: role.reasoningEffort || 'medium',
|
|
330
|
+
onChange: (e) => onRoleChange(role.id, { reasoningEffort: e.target.value }),
|
|
331
|
+
},
|
|
332
|
+
h('option', { value: 'disabled' }, 'Disabled (None)'),
|
|
333
|
+
h('option', { value: 'off' }, 'Off'),
|
|
334
|
+
h('option', { value: 'low' }, 'Low (Fast)'),
|
|
335
|
+
h('option', { value: 'medium' }, 'Medium (Standard)'),
|
|
336
|
+
h('option', { value: 'high' }, 'High (Deep Thinking)'),
|
|
337
|
+
h('option', { value: 'max' }, 'Max (Exhaustive Reasoning)')
|
|
338
|
+
)
|
|
339
|
+
),
|
|
340
|
+
h('div', { className: 'dso-field' },
|
|
341
|
+
h('label', { className: 'dso-label' }, 'SOUL / Specialized Role Instructions'),
|
|
342
|
+
h('textarea', {
|
|
343
|
+
className: 'dso-textarea',
|
|
344
|
+
value: role.systemPrompt || '',
|
|
345
|
+
onChange: (e) => onRoleChange(role.id, { systemPrompt: e.target.value }),
|
|
346
|
+
placeholder: 'Describe role responsibilities, deliverables, and strict boundaries...',
|
|
347
|
+
})
|
|
348
|
+
),
|
|
349
|
+
h('div', { className: 'dso-field' },
|
|
350
|
+
h('label', { className: 'dso-label' }, 'Skills (comma-separated)'),
|
|
351
|
+
h('input', {
|
|
352
|
+
className: 'dso-input',
|
|
353
|
+
value: Array.isArray(role.skills) ? role.skills.join(', ') : '',
|
|
354
|
+
onChange: (e) => onRoleChange(role.id, {
|
|
355
|
+
skills: e.target.value.split(',').map((s) => s.trim()).filter(Boolean),
|
|
356
|
+
}),
|
|
357
|
+
placeholder: 'e.g. project-design-contract, dsh-ui-design',
|
|
358
|
+
})
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
})
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// --- Tab 2: Workflow Scenarios ---
|
|
367
|
+
function ScenariosTab({ scenarios = {}, roles = {} }) {
|
|
368
|
+
const scenarioList = Object.values(scenarios)
|
|
369
|
+
|
|
370
|
+
return h('div', { className: 'dso-panel' },
|
|
371
|
+
h('span', { className: 'dso-sub' }, 'Interactive DAG topologies: stages, blockers, and concurrency permissions.'),
|
|
372
|
+
scenarioList.map((sc) =>
|
|
373
|
+
h('div', { key: sc.id, className: 'dso-agent-row' },
|
|
374
|
+
h('div', { className: 'dso-agent-header' },
|
|
375
|
+
h('div', null,
|
|
376
|
+
h('strong', { style: { color: 'var(--dsw-alias-label-primary)' } }, sc.title),
|
|
377
|
+
h('div', { className: 'dso-sub' }, sc.description)
|
|
378
|
+
),
|
|
379
|
+
h('span', { className: 'dso-badge' }, `${sc.stages?.length || 0} Stages`)
|
|
380
|
+
),
|
|
381
|
+
h('div', { style: { marginTop: 6, display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
382
|
+
(sc.stages || []).map((st, idx) =>
|
|
383
|
+
h('div', {
|
|
384
|
+
key: st.id,
|
|
385
|
+
style: {
|
|
386
|
+
display: 'flex',
|
|
387
|
+
alignItems: 'center',
|
|
388
|
+
gap: 8,
|
|
389
|
+
padding: '6px 10px',
|
|
390
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
391
|
+
borderRadius: 6,
|
|
392
|
+
fontSize: 12,
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
h('span', { style: { fontWeight: 600, width: 20 } }, `${idx + 1}.`),
|
|
396
|
+
h('span', { style: { flex: 1, color: 'var(--dsw-alias-label-primary)' } }, st.name),
|
|
397
|
+
h('span', { className: 'dso-badge', style: { fontSize: 10 } }, `Role: ${roles[st.roleId]?.name || st.roleId}`),
|
|
398
|
+
st.dependsOn?.length > 0
|
|
399
|
+
? h('span', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 11 } }, `Blocks on: ${st.dependsOn.join(', ')}`)
|
|
400
|
+
: h('span', { style: { color: 'var(--dsw-alias-state-success-primary)', fontSize: 11 } }, 'Starts immediately')
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// --- Tab 3: Prompt Caching Telemetry ---
|
|
410
|
+
function CacheMetricsTab({ metrics = {} }) {
|
|
411
|
+
const hitRatioPct = Math.round((metrics.overallHitRatio || 0) * 100)
|
|
412
|
+
|
|
413
|
+
return h('div', { className: 'dso-panel' },
|
|
414
|
+
h('div', { className: 'dso-stats-grid' },
|
|
415
|
+
h('div', { className: 'dso-stat-box' },
|
|
416
|
+
h('div', { className: 'dso-stat-val' }, `${hitRatioPct}%`),
|
|
417
|
+
h('div', { className: 'dso-stat-lbl' }, 'Overall Cache Hit Rate')
|
|
418
|
+
),
|
|
419
|
+
h('div', { className: 'dso-stat-box' },
|
|
420
|
+
h('div', { className: 'dso-stat-val', style: { color: 'var(--dsw-alias-state-info-primary)' } }, (metrics.totalCacheHitTokens || 0).toLocaleString()),
|
|
421
|
+
h('div', { className: 'dso-stat-lbl' }, 'Cached Input Tokens Reused')
|
|
422
|
+
),
|
|
423
|
+
h('div', { className: 'dso-stat-box' },
|
|
424
|
+
h('div', { className: 'dso-stat-val', style: { color: 'var(--dsw-alias-state-warning-primary)' } }, String(metrics.completedPipelines || 0)),
|
|
425
|
+
h('div', { className: 'dso-stat-lbl' }, 'Completed Pipelines')
|
|
426
|
+
)
|
|
427
|
+
),
|
|
428
|
+
h('div', { style: { padding: 12, background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 8, fontSize: 13, lineHeight: 1.6 } },
|
|
429
|
+
h('strong', { style: { color: 'var(--dsw-alias-label-primary)' } }, 'Prompt Caching Mechanics:'),
|
|
430
|
+
h('p', { style: { margin: '6px 0 0', color: 'var(--dsw-alias-label-secondary)' } },
|
|
431
|
+
'All agents sharing the same model receive an identical canonical base anchor (>1024 tokens) and shared task context. Subsequent stages sequentially append prior artifacts, yielding near-instantaneous Time to First Token (TTFT) and an estimated 80–90% cost savings on DeepSeek and Claude models.'
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// --- Tab 4: Kanban Integration ---
|
|
438
|
+
function KanbanTab({ config = {}, onConfigChange }) {
|
|
439
|
+
const kanbanSync = config.kanbanSync || {
|
|
440
|
+
enabled: false,
|
|
441
|
+
targetColumn: 'review',
|
|
442
|
+
createChecklist: true,
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const updateSync = (patch) => {
|
|
446
|
+
onConfigChange({
|
|
447
|
+
kanbanSync: { ...kanbanSync, ...patch },
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return h('div', { className: 'dso-panel' },
|
|
452
|
+
h('span', { className: 'dso-sub' },
|
|
453
|
+
'Bidirectional synchronization with @goodandready/dsh-kanban: update task checklists and advance cards upon pipeline completion.'
|
|
454
|
+
),
|
|
455
|
+
h('div', { className: 'dso-agent-row' },
|
|
456
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } },
|
|
457
|
+
h('div', null,
|
|
458
|
+
h('strong', { style: { color: 'var(--dsw-alias-label-primary)' } }, 'Enable Kanban Synchronization'),
|
|
459
|
+
h('div', { className: 'dso-sub' }, 'Automatically link orchestrated DAG pipelines to Kanban task cards.')
|
|
460
|
+
),
|
|
461
|
+
h('input', {
|
|
462
|
+
type: 'checkbox',
|
|
463
|
+
style: { width: 18, height: 18, cursor: 'pointer' },
|
|
464
|
+
checked: !!kanbanSync.enabled,
|
|
465
|
+
onChange: (e) => updateSync({ enabled: e.target.checked }),
|
|
466
|
+
})
|
|
467
|
+
),
|
|
468
|
+
h('div', { className: 'dso-grid-2', style: { marginTop: 10 } },
|
|
469
|
+
h('div', { className: 'dso-field' },
|
|
470
|
+
h('label', { className: 'dso-label' }, 'Target Column on Completion'),
|
|
471
|
+
h('input', {
|
|
472
|
+
className: 'dso-input',
|
|
473
|
+
value: kanbanSync.targetColumn || 'review',
|
|
474
|
+
onChange: (e) => updateSync({ targetColumn: e.target.value }),
|
|
475
|
+
placeholder: 'e.g. review, deploy, in-progress',
|
|
476
|
+
}),
|
|
477
|
+
h('span', { style: { fontSize: 11, color: 'var(--dsw-alias-label-tertiary)', marginTop: 3 } },
|
|
478
|
+
'Default: "review". Automated agents finish work in Review; moving to "done" is reserved for human acceptance.'
|
|
479
|
+
)
|
|
480
|
+
),
|
|
481
|
+
h('div', { className: 'dso-field' },
|
|
482
|
+
h('label', { className: 'dso-label' }, 'Auto-create Checklist Items'),
|
|
483
|
+
h('select', {
|
|
484
|
+
className: 'dso-select',
|
|
485
|
+
value: kanbanSync.createChecklist !== false ? 'true' : 'false',
|
|
486
|
+
onChange: (e) => updateSync({ createChecklist: e.target.value === 'true' }),
|
|
487
|
+
},
|
|
488
|
+
h('option', { value: 'true' }, 'Enabled (Add DAG stages to card checklist)'),
|
|
489
|
+
h('option', { value: 'false' }, 'Disabled (Only move columns)')
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
),
|
|
493
|
+
h('div', { style: { marginTop: 8, padding: '8px 12px', background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 7, fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } },
|
|
494
|
+
'Connected to Kanban Service on port 3080/3082. Changes are applied upon clicking "Save Changes" below.'
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// --- Main Settings Card ---
|
|
501
|
+
function OrchestratorSettingsCard() {
|
|
502
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
503
|
+
const [activeTab, setActiveTab] = useState('roles')
|
|
504
|
+
const [config, setConfig] = useState(null)
|
|
505
|
+
const [metrics, setMetrics] = useState({})
|
|
506
|
+
const [saving, setSaving] = useState(false)
|
|
507
|
+
const [statusMsg, setStatusMsg] = useState('')
|
|
508
|
+
|
|
509
|
+
useEffect(() => {
|
|
510
|
+
ensureStyles()
|
|
511
|
+
fetch('/dsh-agent-orchestrator/config')
|
|
512
|
+
.then((r) => r.json())
|
|
513
|
+
.then((d) => setConfig(d.config))
|
|
514
|
+
.catch(() => {})
|
|
515
|
+
|
|
516
|
+
fetch('/dsh-agent-orchestrator/status')
|
|
517
|
+
.then((r) => r.json())
|
|
518
|
+
.then((d) => setMetrics(d.metrics || {}))
|
|
519
|
+
.catch(() => {})
|
|
520
|
+
}, [])
|
|
521
|
+
|
|
522
|
+
const handleRoleChange = useCallback((roleId, patch) => {
|
|
523
|
+
setConfig((prev) => {
|
|
524
|
+
if (!prev) return prev
|
|
525
|
+
const updatedRoles = {
|
|
526
|
+
...prev.roles,
|
|
527
|
+
[roleId]: { ...prev.roles[roleId], ...patch },
|
|
528
|
+
}
|
|
529
|
+
return { ...prev, roles: updatedRoles }
|
|
530
|
+
})
|
|
531
|
+
}, [])
|
|
532
|
+
|
|
533
|
+
const handleAddRole = useCallback(() => {
|
|
534
|
+
const newId = `custom_role_${Date.now().toString(36)}`
|
|
535
|
+
setConfig((prev) => {
|
|
536
|
+
if (!prev) return prev
|
|
537
|
+
const updatedRoles = {
|
|
538
|
+
...prev.roles,
|
|
539
|
+
[newId]: {
|
|
540
|
+
id: newId,
|
|
541
|
+
name: 'New Custom Specialist',
|
|
542
|
+
category: 'Custom Specialty',
|
|
543
|
+
defaultModel: { provider: 'deepseek', model: 'deepseek-chat' },
|
|
544
|
+
reasoningEffort: 'medium',
|
|
545
|
+
skills: [],
|
|
546
|
+
tools: ['view_file'],
|
|
547
|
+
systemPrompt: 'You are a specialized agent. Fulfill designated stage deliverables.',
|
|
548
|
+
temperature: 0.2,
|
|
549
|
+
maxTokens: 4096,
|
|
550
|
+
},
|
|
551
|
+
}
|
|
552
|
+
return { ...prev, roles: updatedRoles }
|
|
553
|
+
})
|
|
554
|
+
}, [])
|
|
555
|
+
|
|
556
|
+
const handleSave = async () => {
|
|
557
|
+
setSaving(true)
|
|
558
|
+
setStatusMsg('')
|
|
559
|
+
try {
|
|
560
|
+
const res = await fetch('/dsh-agent-orchestrator/config', {
|
|
561
|
+
method: 'POST',
|
|
562
|
+
headers: { 'Content-Type': 'application/json' },
|
|
563
|
+
body: JSON.stringify(config),
|
|
564
|
+
})
|
|
565
|
+
if (res.ok) {
|
|
566
|
+
setStatusMsg('Settings saved successfully')
|
|
567
|
+
setTimeout(() => setStatusMsg(''), 3000)
|
|
568
|
+
} else {
|
|
569
|
+
setStatusMsg('Error saving settings')
|
|
570
|
+
}
|
|
571
|
+
} catch {
|
|
572
|
+
setStatusMsg('Network error')
|
|
573
|
+
} finally {
|
|
574
|
+
setSaving(false)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return h('li', { className: 'dso-card' },
|
|
579
|
+
h('button', {
|
|
580
|
+
type: 'button',
|
|
581
|
+
className: 'dso-head',
|
|
582
|
+
onClick: () => setIsOpen(!isOpen),
|
|
583
|
+
'aria-expanded': isOpen,
|
|
584
|
+
},
|
|
585
|
+
h('div', { className: 'dso-head-info' },
|
|
586
|
+
h('div', { className: 'dso-title' },
|
|
587
|
+
'Multi-Agent Orchestrator',
|
|
588
|
+
h('span', { className: 'dso-badge' }, 'Prompt Caching Optimized')
|
|
589
|
+
),
|
|
590
|
+
h('div', { className: 'dso-sub' },
|
|
591
|
+
'Decomposes tasks into 12 specialized roles (Architecture, Spec, UI, Code, QA, Docs) with DAG execution.'
|
|
592
|
+
)
|
|
593
|
+
),
|
|
594
|
+
h('div', { className: `dso-chev ${isOpen ? 'dso-chev-open' : ''}` },
|
|
595
|
+
h(Chevron)
|
|
596
|
+
)
|
|
597
|
+
),
|
|
598
|
+
isOpen ? h('div', { className: 'dso-body' },
|
|
599
|
+
h('div', { className: 'dso-tabs' },
|
|
600
|
+
h('button', {
|
|
601
|
+
type: 'button',
|
|
602
|
+
className: `dso-tab ${activeTab === 'roles' ? 'dso-tab-active' : ''}`,
|
|
603
|
+
onClick: () => setActiveTab('roles'),
|
|
604
|
+
}, `Agent Profiles (${Object.keys(config?.roles || {}).length})`),
|
|
605
|
+
h('button', {
|
|
606
|
+
type: 'button',
|
|
607
|
+
className: `dso-tab ${activeTab === 'scenarios' ? 'dso-tab-active' : ''}`,
|
|
608
|
+
onClick: () => setActiveTab('scenarios'),
|
|
609
|
+
}, 'Workflow Scenarios (5)'),
|
|
610
|
+
h('button', {
|
|
611
|
+
type: 'button',
|
|
612
|
+
className: `dso-tab ${activeTab === 'metrics' ? 'dso-tab-active' : ''}`,
|
|
613
|
+
onClick: () => setActiveTab('metrics'),
|
|
614
|
+
}, 'Prompt Caching Telemetry'),
|
|
615
|
+
h('button', {
|
|
616
|
+
type: 'button',
|
|
617
|
+
className: `dso-tab ${activeTab === 'kanban' ? 'dso-tab-active' : ''}`,
|
|
618
|
+
onClick: () => setActiveTab('kanban'),
|
|
619
|
+
}, 'Kanban Integration')
|
|
620
|
+
),
|
|
621
|
+
activeTab === 'roles' ? h(AgentProfilesTab, {
|
|
622
|
+
roles: config?.roles || {},
|
|
623
|
+
onRoleChange: handleRoleChange,
|
|
624
|
+
onAddRole: handleAddRole,
|
|
625
|
+
}) : null,
|
|
626
|
+
activeTab === 'scenarios' ? h(ScenariosTab, {
|
|
627
|
+
scenarios: config?.scenarios || {},
|
|
628
|
+
roles: config?.roles || {},
|
|
629
|
+
}) : null,
|
|
630
|
+
activeTab === 'metrics' ? h(CacheMetricsTab, {
|
|
631
|
+
metrics,
|
|
632
|
+
}) : null,
|
|
633
|
+
activeTab === 'kanban' ? h(KanbanTab, {
|
|
634
|
+
config: config || {},
|
|
635
|
+
onConfigChange: (patch) => setConfig((prev) => ({ ...prev, ...patch })),
|
|
636
|
+
}) : null,
|
|
637
|
+
h('div', { className: 'dso-foot' },
|
|
638
|
+
statusMsg ? h('span', { style: { fontSize: 13, color: 'var(--dsw-alias-state-success-primary)', marginRight: 'auto' } }, statusMsg) : null,
|
|
639
|
+
h('button', {
|
|
640
|
+
className: 'dso-btn dso-btn-primary',
|
|
641
|
+
type: 'button',
|
|
642
|
+
disabled: saving,
|
|
643
|
+
onClick: handleSave,
|
|
644
|
+
}, saving ? 'Saving...' : 'Save Changes')
|
|
645
|
+
)
|
|
646
|
+
) : null
|
|
647
|
+
)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function formatElapsedSec(sec) {
|
|
651
|
+
if (sec < 60) return `${sec}s`
|
|
652
|
+
const m = Math.floor(sec / 60)
|
|
653
|
+
const s = sec % 60
|
|
654
|
+
return `${m}m ${s}s`
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function QuickDispatchModal({ onClose, onSuccess }) {
|
|
658
|
+
const [tab, setTab] = useState('pipeline')
|
|
659
|
+
const [taskTitle, setTaskTitle] = useState('')
|
|
660
|
+
const [taskDesc, setTaskDesc] = useState('')
|
|
661
|
+
const [scenarioId, setScenarioId] = useState('auto')
|
|
662
|
+
const [roleId, setRoleId] = useState('architecture')
|
|
663
|
+
const [loading, setLoading] = useState(false)
|
|
664
|
+
const [error, setError] = useState(null)
|
|
665
|
+
|
|
666
|
+
const handleDispatch = async () => {
|
|
667
|
+
if (!taskTitle.trim() && tab === 'pipeline') return
|
|
668
|
+
if (!taskDesc.trim() && tab === 'specialist') return
|
|
669
|
+
setLoading(true)
|
|
670
|
+
setError(null)
|
|
671
|
+
try {
|
|
672
|
+
if (tab === 'pipeline') {
|
|
673
|
+
const res = await fetch('/dsh-agent-orchestrator/dispatch', {
|
|
674
|
+
method: 'POST',
|
|
675
|
+
headers: { 'Content-Type': 'application/json' },
|
|
676
|
+
body: JSON.stringify({
|
|
677
|
+
taskTitle,
|
|
678
|
+
taskDescription: taskDesc || taskTitle,
|
|
679
|
+
scenarioId,
|
|
680
|
+
}),
|
|
681
|
+
})
|
|
682
|
+
const data = await res.json()
|
|
683
|
+
if (!res.ok) throw new Error(data.error || 'Dispatch failed')
|
|
684
|
+
onSuccess(data.pipeline)
|
|
685
|
+
} else {
|
|
686
|
+
const res = await fetch('/dsh-agent-orchestrator/delegate', {
|
|
687
|
+
method: 'POST',
|
|
688
|
+
headers: { 'Content-Type': 'application/json' },
|
|
689
|
+
body: JSON.stringify({
|
|
690
|
+
roleId,
|
|
691
|
+
task: taskDesc,
|
|
692
|
+
}),
|
|
693
|
+
})
|
|
694
|
+
const data = await res.json()
|
|
695
|
+
if (!res.ok) throw new Error(data.error || 'Delegation failed')
|
|
696
|
+
onClose()
|
|
697
|
+
}
|
|
698
|
+
} catch (e) {
|
|
699
|
+
setError(e?.message || String(e))
|
|
700
|
+
setLoading(false)
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return h('div', { className: 'dso-modal-backdrop', onClick: onClose },
|
|
705
|
+
h('div', { className: 'dso-modal', onClick: (e) => e.stopPropagation() },
|
|
706
|
+
h('div', { className: 'dso-modal-head' },
|
|
707
|
+
h('span', null, '🚀 Launch Multi-Agent Orchestrator'),
|
|
708
|
+
h('button', { className: 'dso-dock-btn close', onClick: onClose }, '✕')
|
|
709
|
+
),
|
|
710
|
+
h('div', { className: 'dso-modal-body' },
|
|
711
|
+
h('div', { className: 'dso-tabs', style: { marginBottom: 8 } },
|
|
712
|
+
h('button', {
|
|
713
|
+
type: 'button',
|
|
714
|
+
className: `dso-tab ${tab === 'pipeline' ? 'dso-tab-active' : ''}`,
|
|
715
|
+
onClick: () => setTab('pipeline'),
|
|
716
|
+
}, 'Full DAG Pipeline'),
|
|
717
|
+
h('button', {
|
|
718
|
+
type: 'button',
|
|
719
|
+
className: `dso-tab ${tab === 'specialist' ? 'dso-tab-active' : ''}`,
|
|
720
|
+
onClick: () => setTab('specialist'),
|
|
721
|
+
}, 'Direct Specialist Subagent')
|
|
722
|
+
),
|
|
723
|
+
tab === 'pipeline'
|
|
724
|
+
? h(React.Fragment, null,
|
|
725
|
+
h('div', { className: 'dso-field' },
|
|
726
|
+
h('label', { className: 'dso-label' }, 'Objective Title'),
|
|
727
|
+
h('input', {
|
|
728
|
+
className: 'dso-input',
|
|
729
|
+
placeholder: 'e.g. Implement user profile settings card with theme tokens',
|
|
730
|
+
value: taskTitle,
|
|
731
|
+
onChange: (e) => setTaskTitle(e.target.value),
|
|
732
|
+
})
|
|
733
|
+
),
|
|
734
|
+
h('div', { className: 'dso-field' },
|
|
735
|
+
h('label', { className: 'dso-label' }, 'Scope & Requirements'),
|
|
736
|
+
h('textarea', {
|
|
737
|
+
className: 'dso-textarea',
|
|
738
|
+
placeholder: 'Detailed functional specs, acceptance criteria, constraints...',
|
|
739
|
+
value: taskDesc,
|
|
740
|
+
onChange: (e) => setTaskDesc(e.target.value),
|
|
741
|
+
rows: 3,
|
|
742
|
+
})
|
|
743
|
+
),
|
|
744
|
+
h('div', { className: 'dso-field' },
|
|
745
|
+
h('label', { className: 'dso-label' }, 'Topology Scenario'),
|
|
746
|
+
h('select', {
|
|
747
|
+
className: 'dso-select',
|
|
748
|
+
value: scenarioId,
|
|
749
|
+
onChange: (e) => setScenarioId(e.target.value),
|
|
750
|
+
},
|
|
751
|
+
h('option', { value: 'auto' }, 'Auto (Infer based on prompt complexity)'),
|
|
752
|
+
h('option', { value: 'hotfix' }, 'Hotfix (1 Stage: Triage & Minimal Fix)'),
|
|
753
|
+
h('option', { value: 'simple' }, 'Simple (2 Stages: Spec + Exec)'),
|
|
754
|
+
h('option', { value: 'medium' }, 'Medium (4 Stages: Spec -> Design -> Code -> QA)'),
|
|
755
|
+
h('option', { value: 'complex' }, 'Complex (6 Stages: Full Engineering Lifecycle)'),
|
|
756
|
+
h('option', { value: 'enterprise' }, 'Enterprise (7 Stages: R&D Spike -> Fullstack -> Gate)')
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
)
|
|
760
|
+
: h(React.Fragment, null,
|
|
761
|
+
h('div', { className: 'dso-field' },
|
|
762
|
+
h('label', { className: 'dso-label' }, 'Target Specialist Role'),
|
|
763
|
+
h('select', {
|
|
764
|
+
className: 'dso-select',
|
|
765
|
+
value: roleId,
|
|
766
|
+
onChange: (e) => setRoleId(e.target.value),
|
|
767
|
+
},
|
|
768
|
+
h('option', { value: 'ui_design' }, 'UI/UX Interface Designer'),
|
|
769
|
+
h('option', { value: 'architecture' }, 'System Architect (DESIGN.md / ADR)'),
|
|
770
|
+
h('option', { value: 'spec' }, 'Technical Spec Analyst'),
|
|
771
|
+
h('option', { value: 'frontend' }, 'Senior Frontend Developer'),
|
|
772
|
+
h('option', { value: 'backend' }, 'Senior Backend Developer'),
|
|
773
|
+
h('option', { value: 'qa_tests' }, 'QA Automation Engineer'),
|
|
774
|
+
h('option', { value: 'refactoring' }, 'Refactoring & Complexity Specialist'),
|
|
775
|
+
h('option', { value: 'bugfix' }, 'Hotfix & Diagnostic Engineer'),
|
|
776
|
+
h('option', { value: 'docs' }, 'Documentation Specialist'),
|
|
777
|
+
h('option', { value: 'research' }, 'Spike & R&D Researcher'),
|
|
778
|
+
h('option', { value: 'devops' }, 'DevOps & Tooling Specialist')
|
|
779
|
+
)
|
|
780
|
+
),
|
|
781
|
+
h('div', { className: 'dso-field' },
|
|
782
|
+
h('label', { className: 'dso-label' }, 'Instructions for Subagent'),
|
|
783
|
+
h('textarea', {
|
|
784
|
+
className: 'dso-textarea',
|
|
785
|
+
placeholder: 'Specific instructions, questions, or requirements for this specialist...',
|
|
786
|
+
value: taskDesc,
|
|
787
|
+
onChange: (e) => setTaskDesc(e.target.value),
|
|
788
|
+
rows: 4,
|
|
789
|
+
})
|
|
790
|
+
)
|
|
791
|
+
),
|
|
792
|
+
error ? h('div', { style: { color: 'var(--dsw-alias-state-error-primary)', fontSize: 12 } }, error) : null
|
|
793
|
+
),
|
|
794
|
+
h('div', { className: 'dso-modal-foot' },
|
|
795
|
+
h('button', { className: 'dso-btn dso-btn-subtle', onClick: onClose }, 'Cancel'),
|
|
796
|
+
h('button', {
|
|
797
|
+
className: 'dso-btn dso-btn-primary',
|
|
798
|
+
disabled: loading,
|
|
799
|
+
onClick: handleDispatch,
|
|
800
|
+
}, loading ? 'Dispatching...' : tab === 'pipeline' ? 'Start Pipeline' : 'Delegate Subagent')
|
|
801
|
+
)
|
|
802
|
+
)
|
|
803
|
+
)
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function OrchestratorDetailsModal({ pipeline, onClose, onCancel }) {
|
|
807
|
+
const [fullData, setFullData] = useState(pipeline)
|
|
808
|
+
|
|
809
|
+
useEffect(() => {
|
|
810
|
+
if (!pipeline?.pipelineId) return
|
|
811
|
+
fetch(`/dsh-agent-orchestrator/pipelines/${encodeURIComponent(pipeline.pipelineId)}`)
|
|
812
|
+
.then((r) => r.json())
|
|
813
|
+
.then((d) => {
|
|
814
|
+
if (d.pipeline) setFullData(d.pipeline)
|
|
815
|
+
})
|
|
816
|
+
.catch(() => {})
|
|
817
|
+
}, [pipeline])
|
|
818
|
+
|
|
819
|
+
const stages = fullData?.stages || []
|
|
820
|
+
const isRunning = fullData.status === 'running' || fullData.status === 'pending'
|
|
821
|
+
|
|
822
|
+
return h('div', { className: 'dso-modal-backdrop', onClick: onClose },
|
|
823
|
+
h('div', { className: 'dso-modal', style: { maxWidth: 680 }, onClick: (e) => e.stopPropagation() },
|
|
824
|
+
h('div', { className: 'dso-modal-head' },
|
|
825
|
+
h('div', null,
|
|
826
|
+
h('span', null, `DAG Pipeline: ${fullData.taskTitle}`),
|
|
827
|
+
h('span', { className: 'dso-badge', style: { marginLeft: 8 } }, fullData.scenarioTitle || fullData.scenarioId)
|
|
828
|
+
),
|
|
829
|
+
h('button', { className: 'dso-dock-btn close', onClick: onClose }, '✕')
|
|
830
|
+
),
|
|
831
|
+
h('div', { className: 'dso-modal-body' },
|
|
832
|
+
h('div', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } },
|
|
833
|
+
fullData.taskDescription
|
|
834
|
+
),
|
|
835
|
+
h('div', { style: { fontWeight: 600, fontSize: 13, marginTop: 6 } }, 'Execution Stages:'),
|
|
836
|
+
stages.map((stg, idx) => {
|
|
837
|
+
const icon = stg.status === 'completed' ? '✅' : stg.status === 'running' ? '⚡' : stg.status === 'failed' ? '❌' : '⏳'
|
|
838
|
+
const output = fullData.artifacts?.[stg.id] || fullData.stageLogs?.[stg.id]
|
|
839
|
+
return h('div', { key: stg.id || idx, className: 'dso-stage-item' },
|
|
840
|
+
h('div', { className: 'dso-stage-head' },
|
|
841
|
+
h('span', null, `${icon} ${idx + 1}. ${stg.name} `),
|
|
842
|
+
h('span', { className: 'dso-agent-role' }, stg.roleName || stg.roleId)
|
|
843
|
+
),
|
|
844
|
+
output
|
|
845
|
+
? h('div', { className: 'dso-stage-log' }, output)
|
|
846
|
+
: null
|
|
847
|
+
)
|
|
848
|
+
})
|
|
849
|
+
),
|
|
850
|
+
h('div', { className: 'dso-modal-foot' },
|
|
851
|
+
isRunning && onCancel
|
|
852
|
+
? h('button', {
|
|
853
|
+
className: 'dso-btn dso-btn-subtle',
|
|
854
|
+
style: { color: 'var(--dsw-alias-state-error-primary)', marginRight: 'auto' },
|
|
855
|
+
onClick: () => onCancel(fullData.pipelineId),
|
|
856
|
+
}, 'Cancel Pipeline')
|
|
857
|
+
: null,
|
|
858
|
+
h('button', { className: 'dso-btn dso-btn-subtle', onClick: onClose }, 'Close')
|
|
859
|
+
)
|
|
860
|
+
)
|
|
861
|
+
)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// --- Dock Widget above Chat Composer (conversation.input.dock) ---
|
|
865
|
+
function OrchestratorTopBanner(props) {
|
|
866
|
+
const { ctx } = props || {}
|
|
867
|
+
const [activePipeline, setActivePipeline] = useState(null)
|
|
868
|
+
const [recentFinished, setRecentFinished] = useState(null)
|
|
869
|
+
const [dismissedId, setDismissedId] = useState(null)
|
|
870
|
+
const [isDetailsOpen, setIsDetailsOpen] = useState(false)
|
|
871
|
+
const [isQuickOpen, setIsQuickOpen] = useState(false)
|
|
872
|
+
const [now, setNow] = useState(() => Date.now())
|
|
873
|
+
|
|
874
|
+
useEffect(() => {
|
|
875
|
+
ensureStyles()
|
|
876
|
+
const timer = setInterval(() => setNow(Date.now()), 1000)
|
|
877
|
+
return () => clearInterval(timer)
|
|
878
|
+
}, [])
|
|
879
|
+
|
|
880
|
+
const pollStatus = useCallback(async () => {
|
|
881
|
+
try {
|
|
882
|
+
const res = await fetch('/dsh-agent-orchestrator/status')
|
|
883
|
+
if (res.ok) {
|
|
884
|
+
const data = await res.json()
|
|
885
|
+
const active = (data.activePipelines || [])[0] || null
|
|
886
|
+
setActivePipeline(active)
|
|
887
|
+
|
|
888
|
+
// If no active, check recent completed/failed within last 60 seconds
|
|
889
|
+
if (!active && data.recentPipelines && data.recentPipelines.length > 0) {
|
|
890
|
+
const latest = data.recentPipelines[0]
|
|
891
|
+
const completedAgo = latest.completedAt ? Date.now() - latest.completedAt : 999999
|
|
892
|
+
if (completedAgo < 60000 && latest.pipelineId !== dismissedId) {
|
|
893
|
+
setRecentFinished(latest)
|
|
894
|
+
} else {
|
|
895
|
+
setRecentFinished(null)
|
|
896
|
+
}
|
|
897
|
+
} else {
|
|
898
|
+
setRecentFinished(null)
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
} catch (_) {
|
|
902
|
+
/* safe ignore */
|
|
903
|
+
}
|
|
904
|
+
}, [dismissedId])
|
|
905
|
+
|
|
906
|
+
useEffect(() => {
|
|
907
|
+
pollStatus()
|
|
908
|
+
const intervalMs = activePipeline ? 2000 : 5000
|
|
909
|
+
const timer = setInterval(pollStatus, intervalMs)
|
|
910
|
+
return () => clearInterval(timer)
|
|
911
|
+
}, [activePipeline, pollStatus])
|
|
912
|
+
|
|
913
|
+
const handleCancel = async (pipelineId) => {
|
|
914
|
+
try {
|
|
915
|
+
await fetch('/dsh-agent-orchestrator/cancel', {
|
|
916
|
+
method: 'POST',
|
|
917
|
+
headers: { 'Content-Type': 'application/json' },
|
|
918
|
+
body: JSON.stringify({ pipelineId }),
|
|
919
|
+
})
|
|
920
|
+
setIsDetailsOpen(false)
|
|
921
|
+
pollStatus()
|
|
922
|
+
} catch (_) {
|
|
923
|
+
/* safe ignore */
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const currentPipeline = activePipeline || recentFinished
|
|
928
|
+
const isCompleted = currentPipeline?.status === 'completed'
|
|
929
|
+
const isFailed = currentPipeline?.status === 'failed'
|
|
930
|
+
const isRunning = currentPipeline?.status === 'running' || currentPipeline?.status === 'pending'
|
|
931
|
+
|
|
932
|
+
if (!currentPipeline || currentPipeline.pipelineId === dismissedId) {
|
|
933
|
+
// Quick Launch Pill in conversation.input.dock
|
|
934
|
+
return h(
|
|
935
|
+
React.Fragment,
|
|
936
|
+
null,
|
|
937
|
+
h(
|
|
938
|
+
'div',
|
|
939
|
+
{ className: 'dso-dock dso-dock-quick', 'data-orchestrator-dock': 'true' },
|
|
940
|
+
h(
|
|
941
|
+
'button',
|
|
942
|
+
{
|
|
943
|
+
className: 'dso-quicklaunch-btn',
|
|
944
|
+
title: 'Multi-Agent Orchestrator (DAG Pipeline & Specialist Subagents)',
|
|
945
|
+
onClick: () => setIsQuickOpen(true),
|
|
946
|
+
},
|
|
947
|
+
h('span', null, '🚀'),
|
|
948
|
+
h('span', null, 'Orchestrator')
|
|
949
|
+
)
|
|
950
|
+
),
|
|
951
|
+
isQuickOpen
|
|
952
|
+
? h(QuickDispatchModal, {
|
|
953
|
+
onClose: () => setIsQuickOpen(false),
|
|
954
|
+
onSuccess: (p) => {
|
|
955
|
+
setActivePipeline(p)
|
|
956
|
+
setIsQuickOpen(false)
|
|
957
|
+
},
|
|
958
|
+
})
|
|
959
|
+
: null
|
|
960
|
+
)
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Format elapsed time
|
|
964
|
+
const startTime = currentPipeline.startedAt || currentPipeline.createdAt || now
|
|
965
|
+
const endTime = currentPipeline.completedAt || now
|
|
966
|
+
const elapsedSec = Math.max(0, Math.floor((endTime - startTime) / 1000))
|
|
967
|
+
const elapsedStr = formatElapsedSec(elapsedSec)
|
|
968
|
+
|
|
969
|
+
// Active stage & progress
|
|
970
|
+
const stages = currentPipeline.stages || []
|
|
971
|
+
const totalStages = stages.length
|
|
972
|
+
const completedCount = stages.filter((s) => s.status === 'completed').length
|
|
973
|
+
const activeStage = stages.find((s) => s.status === 'running') || stages.find((s) => s.status === 'pending')
|
|
974
|
+
|
|
975
|
+
const bannerClass = `dso-banner ${isCompleted ? 'completed' : ''} ${isFailed ? 'failed' : ''}`
|
|
976
|
+
|
|
977
|
+
return h(
|
|
978
|
+
React.Fragment,
|
|
979
|
+
null,
|
|
980
|
+
h(
|
|
981
|
+
'div',
|
|
982
|
+
{ className: 'dso-dock', 'data-orchestrator-dock': 'true' },
|
|
983
|
+
h(
|
|
984
|
+
'div',
|
|
985
|
+
{ className: bannerClass },
|
|
986
|
+
h(
|
|
987
|
+
'div',
|
|
988
|
+
{ className: 'dso-banner-left' },
|
|
989
|
+
isRunning
|
|
990
|
+
? h('div', { className: 'dso-pulse-dot' })
|
|
991
|
+
: h('span', null, isCompleted ? '✅' : '❌'),
|
|
992
|
+
h(
|
|
993
|
+
'span',
|
|
994
|
+
{
|
|
995
|
+
className: `dso-banner-badge ${isRunning ? 'dso-badge-running' : isCompleted ? 'dso-badge-ok' : 'dso-badge-err'}`,
|
|
996
|
+
},
|
|
997
|
+
isRunning
|
|
998
|
+
? `Stage ${completedCount + 1}/${totalStages}`
|
|
999
|
+
: isCompleted
|
|
1000
|
+
? 'Done'
|
|
1001
|
+
: 'Failed'
|
|
1002
|
+
),
|
|
1003
|
+
currentPipeline.scenarioTitle
|
|
1004
|
+
? h(
|
|
1005
|
+
'span',
|
|
1006
|
+
{
|
|
1007
|
+
className: 'dso-badge',
|
|
1008
|
+
style: { fontSize: 10, padding: '1px 5px' },
|
|
1009
|
+
},
|
|
1010
|
+
currentPipeline.scenarioTitle
|
|
1011
|
+
)
|
|
1012
|
+
: null,
|
|
1013
|
+
h(
|
|
1014
|
+
'span',
|
|
1015
|
+
{
|
|
1016
|
+
className: 'dso-banner-title',
|
|
1017
|
+
title: currentPipeline.taskTitle,
|
|
1018
|
+
},
|
|
1019
|
+
currentPipeline.taskTitle
|
|
1020
|
+
),
|
|
1021
|
+
activeStage && isRunning
|
|
1022
|
+
? h(
|
|
1023
|
+
'span',
|
|
1024
|
+
{ className: 'dso-banner-stage' },
|
|
1025
|
+
`${activeStage.name} (${activeStage.roleName || activeStage.roleId})`
|
|
1026
|
+
)
|
|
1027
|
+
: null
|
|
1028
|
+
),
|
|
1029
|
+
h(
|
|
1030
|
+
'div',
|
|
1031
|
+
{ className: 'dso-banner-right' },
|
|
1032
|
+
h('span', { className: 'dso-banner-time' }, `⏱ ${elapsedStr}`),
|
|
1033
|
+
h(
|
|
1034
|
+
'button',
|
|
1035
|
+
{
|
|
1036
|
+
className: 'dso-dock-btn',
|
|
1037
|
+
title: 'View Pipeline DAG & Deliverables',
|
|
1038
|
+
onClick: () => setIsDetailsOpen(true),
|
|
1039
|
+
},
|
|
1040
|
+
'Details'
|
|
1041
|
+
),
|
|
1042
|
+
!isRunning
|
|
1043
|
+
? h(
|
|
1044
|
+
'button',
|
|
1045
|
+
{
|
|
1046
|
+
className: 'dso-dock-btn close',
|
|
1047
|
+
title: 'Dismiss Banner',
|
|
1048
|
+
onClick: () => setDismissedId(currentPipeline.pipelineId),
|
|
1049
|
+
},
|
|
1050
|
+
'✕'
|
|
1051
|
+
)
|
|
1052
|
+
: null
|
|
1053
|
+
)
|
|
1054
|
+
)
|
|
1055
|
+
),
|
|
1056
|
+
isDetailsOpen
|
|
1057
|
+
? h(OrchestratorDetailsModal, {
|
|
1058
|
+
pipeline: currentPipeline,
|
|
1059
|
+
onClose: () => setIsDetailsOpen(false),
|
|
1060
|
+
onCancel: handleCancel,
|
|
1061
|
+
})
|
|
1062
|
+
: null,
|
|
1063
|
+
isQuickOpen
|
|
1064
|
+
? h(QuickDispatchModal, {
|
|
1065
|
+
onClose: () => setIsQuickOpen(false),
|
|
1066
|
+
onSuccess: (p) => {
|
|
1067
|
+
setActivePipeline(p)
|
|
1068
|
+
setIsQuickOpen(false)
|
|
1069
|
+
},
|
|
1070
|
+
})
|
|
1071
|
+
: null
|
|
1072
|
+
)
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
// --- Issue #5: Specialist Toolview Component (tool.call.toolview) ---
|
|
1077
|
+
function SpecialistToolview(props) {
|
|
1078
|
+
const [showDiff, setShowDiff] = useState(false)
|
|
1079
|
+
const call = props.call || props.toolCall || {}
|
|
1080
|
+
const args = call.arguments || call.args || {}
|
|
1081
|
+
const result = props.result || props.output || {}
|
|
1082
|
+
const roleName = result.roleName || args.roleId || args.name || 'Specialist Worker'
|
|
1083
|
+
const modelName = result.model || 'inherited'
|
|
1084
|
+
const chip = formatModelChip(modelName, { maxTokens: result.maxTokens })
|
|
1085
|
+
const isErr = result.isError || result.status === 'failed'
|
|
1086
|
+
const isRunning = !props.result && !props.output
|
|
1087
|
+
const diff = result.toolDiff || {}
|
|
1088
|
+
const outputText = typeof result.output === 'string' ? result.output : (result.output ? JSON.stringify(result.output, null, 2) : '')
|
|
1089
|
+
|
|
1090
|
+
return h('div', { className: 'dso-toolview-card' },
|
|
1091
|
+
h('div', { className: 'dso-toolview-header' },
|
|
1092
|
+
h('div', { className: 'dso-toolview-badge' },
|
|
1093
|
+
h('span', null, '🤖'),
|
|
1094
|
+
h('span', null, roleName),
|
|
1095
|
+
h('span', { className: `dso-model-chip ${chip.badgeClass}`, title: chip.tooltip }, chip.text)
|
|
1096
|
+
),
|
|
1097
|
+
h('div', { className: `dso-toolview-status-pill ${isRunning ? 'running' : (isErr ? 'err' : 'ok')}` },
|
|
1098
|
+
isRunning ? '⏳ Running...' : (isErr ? '🔴 Failed' : '🟢 Completed')
|
|
1099
|
+
)
|
|
1100
|
+
),
|
|
1101
|
+
diff && (diff.allowed?.length > 0 || diff.strippedSecurity?.length > 0) ?
|
|
1102
|
+
h('div', { className: 'dso-toolview-tooldiff' },
|
|
1103
|
+
h('div', { className: 'dso-tooldiff-summary', onClick: () => setShowDiff(!showDiff) },
|
|
1104
|
+
h('span', null, '🛡️ Tool Permissions:'),
|
|
1105
|
+
h('span', null, `${diff.allowed?.length || 0} allowed, ${diff.strippedSecurity?.length || 0} stripped`),
|
|
1106
|
+
h(ChevronIcon || FallbackChevron, { style: { transform: showDiff ? 'rotate(180deg)' : 'none', transition: 'transform 0.15s' } })
|
|
1107
|
+
),
|
|
1108
|
+
showDiff ? h('div', { className: 'dso-tooldiff-pills' },
|
|
1109
|
+
(diff.allowed || []).map(t => h('span', { key: t, className: 'dso-pill allow', title: 'Allowed' }, t)),
|
|
1110
|
+
(diff.strippedSecurity || []).map(t => h('span', { key: t, className: 'dso-pill blocked', title: 'Blocked by Security Policy' }, t)),
|
|
1111
|
+
(diff.denied || []).map(t => h('span', { key: t, className: 'dso-pill denied', title: 'Not in Parent' }, t))
|
|
1112
|
+
) : null
|
|
1113
|
+
) : null,
|
|
1114
|
+
outputText ? h('div', { className: 'dso-toolview-deliverable' }, outputText) : null,
|
|
1115
|
+
!isRunning && !isErr ? h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1116
|
+
h('div', { className: 'dso-toolview-callout', style: { flex: 1 } },
|
|
1117
|
+
h('span', null, (ctx?.locale ? ctx.locale.bind(NS)('badge.accepted') : '🟢 Subagent result accepted by orchestrator'))
|
|
1118
|
+
),
|
|
1119
|
+
h('button', {
|
|
1120
|
+
type: 'button',
|
|
1121
|
+
className: 'dso-preset-btn',
|
|
1122
|
+
title: 'Save this deliverable configuration as a reusable workflow preset (Issue 109)',
|
|
1123
|
+
onClick: async () => {
|
|
1124
|
+
try {
|
|
1125
|
+
await fetch('/dsh-agent-orchestrator/snapshots/save-as-preset', {
|
|
1126
|
+
method: 'POST',
|
|
1127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1128
|
+
body: JSON.stringify({
|
|
1129
|
+
snapshotId: result.executionId || args.executionId || `snap-${roleName}`,
|
|
1130
|
+
name: `Preset: ${roleName}`,
|
|
1131
|
+
description: `Created from specialist deliverable [${roleName}]`,
|
|
1132
|
+
}),
|
|
1133
|
+
})
|
|
1134
|
+
} catch (_) {
|
|
1135
|
+
/* safe ignore */
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
}, '💾 Save as Preset')
|
|
1139
|
+
) : null
|
|
1140
|
+
)
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// --- Header Utility Progress Chip (Goal-style) ---
|
|
1144
|
+
// --- Header Utility Progress Chip & Subagent Child Session Badge (Issue #3) ---
|
|
1145
|
+
function HeaderOrchestratorWidget(props) {
|
|
1146
|
+
const [activePipeline, setActivePipeline] = useState(null)
|
|
1147
|
+
const session = props.useSession ? props.useSession(s => s) : props.session
|
|
1148
|
+
|
|
1149
|
+
useEffect(() => {
|
|
1150
|
+
ensureStyles()
|
|
1151
|
+
const check = () => {
|
|
1152
|
+
fetch('/dsh-agent-orchestrator/status')
|
|
1153
|
+
.then((r) => r.json())
|
|
1154
|
+
.then((d) => {
|
|
1155
|
+
const p = (d.activePipelines || [])[0]
|
|
1156
|
+
setActivePipeline(p || null)
|
|
1157
|
+
})
|
|
1158
|
+
.catch(() => {})
|
|
1159
|
+
}
|
|
1160
|
+
check()
|
|
1161
|
+
const interval = setInterval(check, 4000)
|
|
1162
|
+
return () => clearInterval(interval)
|
|
1163
|
+
}, [])
|
|
1164
|
+
|
|
1165
|
+
// Issue #3: Subagent child session header utility badge
|
|
1166
|
+
const subagentMeta = session?.subagent || session?.options?.subagent || (session?.meta && session.meta.subagent)
|
|
1167
|
+
const parentId = session?.parentId || session?.options?.parent || (session?.meta && session.meta.parent)
|
|
1168
|
+
if (subagentMeta || parentId) {
|
|
1169
|
+
const role = subagentMeta?.role || subagentMeta?.label || 'Subagent'
|
|
1170
|
+
const model = subagentMeta?.model || session?.options?.model || ''
|
|
1171
|
+
const chip = formatModelChip(model)
|
|
1172
|
+
return h('div', { className: 'dso-subagent-header-chip' },
|
|
1173
|
+
h('span', { className: 'dso-chip-icon' }, '🤖'),
|
|
1174
|
+
h('span', { className: 'dso-chip-role' }, role),
|
|
1175
|
+
model ? h('span', { className: 'dso-chip-sep' }, '·') : null,
|
|
1176
|
+
model ? h('span', { className: `dso-model-chip ${chip.badgeClass}`, title: chip.tooltip }, chip.text) : null,
|
|
1177
|
+
parentId ? h('a', {
|
|
1178
|
+
href: `#session-${parentId}`,
|
|
1179
|
+
className: 'dso-chip-parent-link',
|
|
1180
|
+
onClick: (e) => {
|
|
1181
|
+
e.preventDefault()
|
|
1182
|
+
if (props.ctx?.sessions?.activate) {
|
|
1183
|
+
props.ctx.sessions.activate(parentId)
|
|
1184
|
+
} else if (window.location) {
|
|
1185
|
+
window.location.hash = `session-${parentId}`
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1188
|
+
title: `Return to parent session #${parentId}`
|
|
1189
|
+
}, `⮌ Parent #${String(parentId).slice(0, 8)}`) : null
|
|
1190
|
+
)
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
if (!activePipeline) return null
|
|
1194
|
+
|
|
1195
|
+
const total = activePipeline.stages?.length || 0
|
|
1196
|
+
const done = activePipeline.stages?.filter((s) => s.status === 'completed').length || 0
|
|
1197
|
+
const current = activePipeline.stages?.find((s) => s.status === 'running')
|
|
1198
|
+
|
|
1199
|
+
return h('div', { className: 'dso-widget', title: `Active Pipeline: ${activePipeline.taskTitle}` },
|
|
1200
|
+
h('div', { className: 'dso-widget-dot' }),
|
|
1201
|
+
h('span', { className: 'dso-widget-badge' }, `${done}/${total}`),
|
|
1202
|
+
h('span', null, current ? current.name : 'Orchestrating...')
|
|
1203
|
+
)
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// Polling helper to ensure describe mirror sees this namespace
|
|
1207
|
+
function refreshMirrorUntilVisible(ctx) {
|
|
1208
|
+
const visible = () => {
|
|
1209
|
+
try {
|
|
1210
|
+
const s = (ctx?.get && ctx.get('lanSettings')) || (ctx?.get && ctx.get('settingsScope')) || ctx?.settingsScope
|
|
1211
|
+
const view = s?.describe?.()?.getSnapshot?.()?.view
|
|
1212
|
+
return !!view && Array.isArray(view.namespaces) && view.namespaces.some((row) => row.ns === NS)
|
|
1213
|
+
} catch (_) {
|
|
1214
|
+
return false
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if (visible()) return () => {}
|
|
1218
|
+
let tries = 0
|
|
1219
|
+
const timer = setInterval(() => {
|
|
1220
|
+
if (visible() || tries >= 15) { clearInterval(timer); return }
|
|
1221
|
+
tries += 1
|
|
1222
|
+
try {
|
|
1223
|
+
const s = (ctx?.get && ctx.get('lanSettings')) || (ctx?.get && ctx.get('settingsScope')) || ctx?.settingsScope
|
|
1224
|
+
s?.describe?.()?.load?.()
|
|
1225
|
+
} catch (_) {
|
|
1226
|
+
/* safe ignore */
|
|
1227
|
+
}
|
|
1228
|
+
}, 1000)
|
|
1229
|
+
return () => clearInterval(timer)
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// Plugin Client Exports & Lifecycle
|
|
1233
|
+
module.exports.inject = ['slots', 'locale']
|
|
1234
|
+
module.exports.apply = function apply(ctx) {
|
|
1235
|
+
// Register localized strings via ctx.effect (Issue #115 & #116)
|
|
1236
|
+
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
1237
|
+
const dicts = {
|
|
1238
|
+
en: {
|
|
1239
|
+
title: 'Multi-Agent Orchestrator',
|
|
1240
|
+
subtitle: 'Autonomous multi-agent DAG workflow & prompt caching optimizer',
|
|
1241
|
+
description: 'Decompose and execute tasks across specialized autonomous agent roles with prompt caching.',
|
|
1242
|
+
'badge.accepted': '🟢 Subagent result accepted by orchestrator',
|
|
1243
|
+
},
|
|
1244
|
+
zh: {
|
|
1245
|
+
title: '多智能体编排器',
|
|
1246
|
+
subtitle: '自主多智能体有向无环图工作流与提示词缓存优化器',
|
|
1247
|
+
description: '基于提示词缓存优化,将任务分解并在专门的智能体角色之间协同执行。',
|
|
1248
|
+
'badge.accepted': '🟢 子代理结果已被编排器接受',
|
|
1249
|
+
},
|
|
1250
|
+
}
|
|
1251
|
+
if (typeof ctx.effect === 'function') {
|
|
1252
|
+
ctx.effect(() => ctx.locale.register(NS, dicts), 'dsh-agent-orchestrator: locale')
|
|
1253
|
+
} else {
|
|
1254
|
+
ctx.locale.register(NS, dicts)
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
refreshMirrorUntilVisible(ctx)
|
|
1259
|
+
|
|
1260
|
+
const registerSlotSafe = (name, entry, comp) => {
|
|
1261
|
+
try {
|
|
1262
|
+
if (typeof ctx.slots?.inject === 'function') {
|
|
1263
|
+
ctx.slots.inject(name, () => {
|
|
1264
|
+
try {
|
|
1265
|
+
return ctx.slots.register(entry, comp)
|
|
1266
|
+
} catch (_) {
|
|
1267
|
+
/* safe ignore */
|
|
1268
|
+
}
|
|
1269
|
+
})
|
|
1270
|
+
} else if (typeof ctx.slots?.register === 'function') {
|
|
1271
|
+
ctx.slots.register(entry, comp)
|
|
1272
|
+
}
|
|
1273
|
+
} catch (_) {
|
|
1274
|
+
/* safe ignore */
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Issue #139: settings.plugin.item does not render in DSH core 0.1.6-alpha.2+.
|
|
1279
|
+
// Register into settings.plugins.tab (official tabs under Settings -> Plugins),
|
|
1280
|
+
// plugins.item (Plugin Manager page), and fallback settings.section.
|
|
1281
|
+
|
|
1282
|
+
// 1. Official tab in Settings -> Plugins
|
|
1283
|
+
registerSlotSafe(
|
|
1284
|
+
'settings.plugins.tab',
|
|
1285
|
+
{
|
|
1286
|
+
name: 'settings.plugins.tab',
|
|
1287
|
+
id: 'dsh-agent-orchestrator',
|
|
1288
|
+
order: 25,
|
|
1289
|
+
label: () => (typeof ctx.t === 'function' ? ctx.t(`${NS}.settings.title`) : 'Agent Orchestrator') || 'Agent Orchestrator',
|
|
1290
|
+
locale: NS,
|
|
1291
|
+
inject: () => ({ ctx }),
|
|
1292
|
+
},
|
|
1293
|
+
(props) => h(ErrorBoundary, null, h(OrchestratorSettingsCard, { ...props, ctx: props.ctx || ctx }))
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
// 2. Plugin item on Plugins page
|
|
1297
|
+
registerSlotSafe(
|
|
1298
|
+
'plugins.item',
|
|
1299
|
+
{
|
|
1300
|
+
name: 'plugins.item',
|
|
1301
|
+
id: '@goodandready/dsh-agent-orchestrator',
|
|
1302
|
+
order: 25,
|
|
1303
|
+
label: () => (typeof ctx.t === 'function' ? ctx.t(`${NS}.settings.title`) : 'Agent Orchestrator') || 'Agent Orchestrator',
|
|
1304
|
+
locale: NS,
|
|
1305
|
+
inject: () => ({ ctx }),
|
|
1306
|
+
},
|
|
1307
|
+
(props) => h(ErrorBoundary, null, h(OrchestratorSettingsCard, { ...props, ctx: props.ctx || ctx }))
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
// 3. Standalone settings.section fallback
|
|
1311
|
+
registerSlotSafe(
|
|
1312
|
+
'settings.section',
|
|
1313
|
+
{
|
|
1314
|
+
name: 'settings.section',
|
|
1315
|
+
id: 'dsh-agent-orchestrator',
|
|
1316
|
+
order: 95,
|
|
1317
|
+
label: () => (typeof ctx.t === 'function' ? ctx.t(`${NS}.settings.title`) : 'Agent Orchestrator') || 'Agent Orchestrator',
|
|
1318
|
+
locale: NS,
|
|
1319
|
+
inject: () => ({ ctx }),
|
|
1320
|
+
},
|
|
1321
|
+
(props) => h(ErrorBoundary, null, h(OrchestratorSettingsCard, { ...props, ctx: props.ctx || ctx }))
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
// 3. Dock Widget: conversation.input.dock (above chat input box, like dsh-goal)
|
|
1325
|
+
registerSlotSafe(
|
|
1326
|
+
'conversation.input.dock',
|
|
1327
|
+
{
|
|
1328
|
+
name: 'conversation.input.dock',
|
|
1329
|
+
id: '@goodandready/dsh-agent-orchestrator',
|
|
1330
|
+
order: 10,
|
|
1331
|
+
locale: NS,
|
|
1332
|
+
inject: (slotProps) => ({ ctx, ...slotProps }),
|
|
1333
|
+
},
|
|
1334
|
+
(props) => h(ErrorBoundary, null, h(OrchestratorTopBanner, { ...props, ctx: props.ctx || ctx }))
|
|
1335
|
+
)
|
|
1336
|
+
|
|
1337
|
+
// 4. Header Widget: conversation.session.header.utilities
|
|
1338
|
+
|
|
1339
|
+
// 5. Tool Call View: tool.call.toolview for specialist delegation & agent_run (Issue #5)
|
|
1340
|
+
registerSlotSafe(
|
|
1341
|
+
'tool.call.toolview',
|
|
1342
|
+
{
|
|
1343
|
+
name: 'tool.call.toolview',
|
|
1344
|
+
key: 'orchestrator_delegate_specialist',
|
|
1345
|
+
inject: (slotProps) => ({ ctx, ...slotProps }),
|
|
1346
|
+
},
|
|
1347
|
+
(props) => h(ErrorBoundary, null, h(SpecialistToolview, { ...props, ctx: props.ctx || ctx }))
|
|
1348
|
+
)
|
|
1349
|
+
|
|
1350
|
+
registerSlotSafe(
|
|
1351
|
+
'tool.call.toolview',
|
|
1352
|
+
{
|
|
1353
|
+
name: 'tool.call.toolview',
|
|
1354
|
+
key: 'agent_run',
|
|
1355
|
+
inject: (slotProps) => ({ ctx, ...slotProps }),
|
|
1356
|
+
},
|
|
1357
|
+
(props) => h(ErrorBoundary, null, h(SpecialistToolview, { ...props, ctx: props.ctx || ctx }))
|
|
1358
|
+
)
|
|
1359
|
+
|
|
1360
|
+
registerSlotSafe(
|
|
1361
|
+
'conversation.session.header.utilities',
|
|
1362
|
+
{
|
|
1363
|
+
name: 'conversation.session.header.utilities',
|
|
1364
|
+
id: 'dsh-agent-orchestrator-widget',
|
|
1365
|
+
order: 15,
|
|
1366
|
+
inject: () => ({ ctx }),
|
|
1367
|
+
},
|
|
1368
|
+
(props) => h(ErrorBoundary, null, h(HeaderOrchestratorWidget, { ...props, ctx: props.ctx || ctx }))
|
|
1369
|
+
)
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
return module.exports
|
|
1373
|
+
},
|
|
1374
|
+
})
|