dsh-wsr-execution 0.2.1 → 0.2.3
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/README.md +1 -1
- package/lib/client.js +415 -112
- package/package.json +4 -4
- package/src/action-presentation/model.js +29 -12
- package/src/action-presentation/view.js +178 -20
- package/src/client/browser-entry.js +23 -4
- package/src/client/delivery/control-plane-port.js +11 -2
- package/src/client/delivery/session-delivery-view.js +148 -37
- package/src/client/delivery-inventory/model.js +17 -3
- package/src/host/delivery-control-plane.js +39 -12
- package/src/intake/binding-repository.js +118 -41
- package/src/intake/command.js +17 -1
- package/src/intake/plugin.js +140 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-wsr-execution",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "DeepSeek Harness adapter bundle for WSR Intake, Delivery resources, Session state, Actions, and final results.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"displayName": "WSR",
|
|
40
40
|
"role": "execution-adapter",
|
|
41
41
|
"foundationOnly": false,
|
|
42
|
-
"ownerRevision": "
|
|
42
|
+
"ownerRevision": "b4b3b487af7f163c5934aa758d86b183d64115ec",
|
|
43
43
|
"ownerAsset": {
|
|
44
|
-
"url": "https://github.com/firestige/wsr-execution/releases/download/0.2.
|
|
45
|
-
"sha256": "
|
|
44
|
+
"url": "https://github.com/firestige/wsr-execution/releases/download/0.2.2/wsr-execution-0.2.2.tgz",
|
|
45
|
+
"sha256": "d07eb0aaa4e0498e9e3f5f9bbf3ae4c6a1a9a7ea6c648d15cdfdcccbf53bb41e"
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
@@ -15,7 +15,7 @@ const KINDS = new Set([
|
|
|
15
15
|
"terminal-result",
|
|
16
16
|
"error",
|
|
17
17
|
]);
|
|
18
|
-
const ACTION_STATES = new Set(["running", "completed", "failed", "cancelled", "waiting", "recovering"]);
|
|
18
|
+
const ACTION_STATES = new Set(["running", "completed", "failed", "cancelled", "waiting", "recovering", "uncertain", "unresolved"]);
|
|
19
19
|
const TERMINAL_OUTCOMES = new Set(["SUCCEEDED", "FAILED", "CANCELLED"]);
|
|
20
20
|
const DELIVERY_STATES = new Set([
|
|
21
21
|
"BOUND", "START_UNCERTAIN", "RUNNING_CORRELATED", "START_FAILED", "RESULT_UNRESOLVED", "TERMINAL_HANDLING",
|
|
@@ -61,14 +61,22 @@ function hasValidTypedData(value) {
|
|
|
61
61
|
&& (channel === "action" || channel === "tool");
|
|
62
62
|
}
|
|
63
63
|
if (value.kind === "terminal-result") {
|
|
64
|
-
const
|
|
64
|
+
const hasFinalOutput = Object.hasOwn(value.data, "finalOutput");
|
|
65
|
+
const hasSummary = Object.hasOwn(value.data, "summary");
|
|
66
|
+
const outputValid = hasFinalOutput
|
|
65
67
|
? typeof value.data.finalOutput === "string" && value.data.finalOutput.length > 0
|
|
66
|
-
: typeof value.data.summary === "string" && value.data.summary.length > 0
|
|
68
|
+
: hasSummary ? typeof value.data.summary === "string" && value.data.summary.length > 0
|
|
69
|
+
: value.data.outcome === "SUCCEEDED";
|
|
67
70
|
return typeof value.data.outcome === "string" && TERMINAL_OUTCOMES.has(value.data.outcome) && outputValid;
|
|
68
71
|
}
|
|
69
72
|
if (value.kind === "delivery-running" || value.kind === "delivery-status") {
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
const diagnostic = value.data.diagnostic;
|
|
74
|
+
const diagnosticValid = diagnostic === undefined || (diagnostic !== null && typeof diagnostic === "object" && !Array.isArray(diagnostic)
|
|
75
|
+
&& Object.keys(diagnostic).sort().join(",") === "causeCode,stage"
|
|
76
|
+
&& typeof diagnostic.stage === "string" && /^[A-Z][A-Z0-9_]{0,63}$/u.test(diagnostic.stage)
|
|
77
|
+
&& typeof diagnostic.causeCode === "string" && /^[A-Z][A-Z0-9_]{0,127}$/u.test(diagnostic.causeCode));
|
|
78
|
+
return diagnosticValid && (value.data.state === undefined
|
|
79
|
+
|| (typeof value.data.state === "string" && DELIVERY_STATES.has(value.data.state)));
|
|
72
80
|
}
|
|
73
81
|
return true;
|
|
74
82
|
}
|
|
@@ -117,8 +125,10 @@ function normalizedLifecycle(value, fallback) {
|
|
|
117
125
|
if (["cancelled", "canceled"].includes(normalized)) return "cancelled";
|
|
118
126
|
if (["waiting", "awaiting-input"].includes(normalized)) return "waiting";
|
|
119
127
|
if (["start-failed"].includes(normalized)) return "failed";
|
|
120
|
-
if (
|
|
121
|
-
if (
|
|
128
|
+
if (normalized === "start-uncertain") return "uncertain";
|
|
129
|
+
if (normalized === "result-unresolved") return "unresolved";
|
|
130
|
+
if (["recovering", "recovery", "terminal-handling"].includes(normalized)) return "recovering";
|
|
131
|
+
if (["running", "accepted", "running-correlated", "bound"].includes(normalized)) return "running";
|
|
122
132
|
return fallback;
|
|
123
133
|
}
|
|
124
134
|
|
|
@@ -129,6 +139,8 @@ const STATE_LABELS = Object.freeze({
|
|
|
129
139
|
cancelled: "Cancelled",
|
|
130
140
|
waiting: "Waiting for input",
|
|
131
141
|
recovering: "Recovering",
|
|
142
|
+
uncertain: "Start uncertain",
|
|
143
|
+
unresolved: "Result unresolved",
|
|
132
144
|
});
|
|
133
145
|
|
|
134
146
|
function model(input) {
|
|
@@ -145,7 +157,7 @@ export function projectExecutionPresentation(event) {
|
|
|
145
157
|
return model({
|
|
146
158
|
correlation, layer: "final", state, title: "Final result",
|
|
147
159
|
summary: data.outcome[0] + data.outcome.slice(1).toLowerCase(), body,
|
|
148
|
-
defaultOpen:
|
|
160
|
+
defaultOpen: false, focusPolicy: "none", role: "article",
|
|
149
161
|
compatibility: typeof data.finalOutput === "string" ? "current" : "legacy-summary",
|
|
150
162
|
});
|
|
151
163
|
}
|
|
@@ -163,7 +175,7 @@ export function projectExecutionPresentation(event) {
|
|
|
163
175
|
correlation, layer: data.channel === "tool" ? "tool" : "action", state,
|
|
164
176
|
title: typeof data.label === "string" ? data.label : "Workflow Action",
|
|
165
177
|
summary: STATE_LABELS[state], body: text(data.content) ?? "WSR content unavailable",
|
|
166
|
-
defaultOpen:
|
|
178
|
+
defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
|
|
167
179
|
});
|
|
168
180
|
}
|
|
169
181
|
if (event.kind === "error") {
|
|
@@ -171,7 +183,7 @@ export function projectExecutionPresentation(event) {
|
|
|
171
183
|
correlation, layer: "progress", state: "failed", title: "Workflow presentation",
|
|
172
184
|
summary: typeof data.code === "string" ? data.code : "WSR_ERROR",
|
|
173
185
|
body: typeof data.message === "string" ? data.message : "WSR presentation unavailable",
|
|
174
|
-
defaultOpen:
|
|
186
|
+
defaultOpen: false, focusPolicy: "none", role: "alert", compatibility: "current",
|
|
175
187
|
});
|
|
176
188
|
}
|
|
177
189
|
|
|
@@ -179,10 +191,12 @@ export function projectExecutionPresentation(event) {
|
|
|
179
191
|
? normalizedLifecycle(data.state, "running")
|
|
180
192
|
: normalizedLifecycle(data.state, event.kind === "command-accepted" ? "running" : "running");
|
|
181
193
|
const deliveryId = typeof data.deliveryId === "string" ? data.deliveryId : undefined;
|
|
194
|
+
const diagnostic = data.diagnostic;
|
|
182
195
|
return model({
|
|
183
196
|
correlation, layer: "progress", state, title: "Workflow delivery",
|
|
184
197
|
summary: `${STATE_LABELS[state]}${deliveryId === undefined ? "" : ` · ${deliveryId}`}`,
|
|
185
|
-
body:
|
|
198
|
+
body: diagnostic === undefined ? undefined : `${diagnostic.stage} · ${diagnostic.causeCode}`,
|
|
199
|
+
defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
|
|
186
200
|
});
|
|
187
201
|
}
|
|
188
202
|
|
|
@@ -190,7 +204,6 @@ export function projectExecutionPresentation(event) {
|
|
|
190
204
|
export function resolveDisclosureOpen({ current, previousState, nextState, containsFocus }) {
|
|
191
205
|
if (nextState === "waiting") return true;
|
|
192
206
|
if (nextState === "completed" && previousState !== "completed") return containsFocus ? true : false;
|
|
193
|
-
if (["running", "recovering", "failed", "cancelled"].includes(nextState) && nextState !== previousState) return true;
|
|
194
207
|
return current;
|
|
195
208
|
}
|
|
196
209
|
|
|
@@ -217,6 +230,10 @@ export function createExecutionPresentationDefinition() {
|
|
|
217
230
|
if (event.kind === "delivery-list") {
|
|
218
231
|
return Object.freeze({ ...context.state, presentation: undefined });
|
|
219
232
|
}
|
|
233
|
+
if (event.kind === "terminal-result" && event.data.outcome === "SUCCEEDED"
|
|
234
|
+
&& !Object.hasOwn(event.data, "finalOutput") && !Object.hasOwn(event.data, "summary")) {
|
|
235
|
+
return Object.freeze({ ...context.state, presentation: undefined });
|
|
236
|
+
}
|
|
220
237
|
return Object.freeze({ ...context.state, presentation: projectExecutionPresentation(event) });
|
|
221
238
|
},
|
|
222
239
|
buildViewNode(context) {
|
|
@@ -1,29 +1,64 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createExecutionPresentationDefinition,
|
|
3
|
-
resolveDisclosureOpen,
|
|
4
|
-
} from "./model.js";
|
|
1
|
+
import { parseExecutionPresentation, projectExecutionPresentation, resolveDisclosureOpen } from "./model.js";
|
|
5
2
|
|
|
6
3
|
const DOT_STATE = Object.freeze({
|
|
7
4
|
running: "ongoing",
|
|
8
5
|
recovering: "ongoing",
|
|
6
|
+
uncertain: "warning",
|
|
7
|
+
unresolved: "warning",
|
|
9
8
|
completed: "done",
|
|
10
9
|
waiting: "warning",
|
|
11
10
|
failed: "error",
|
|
12
11
|
cancelled: "error",
|
|
13
12
|
});
|
|
14
13
|
|
|
14
|
+
const ACTIONS_STYLE_ID = "dsh-wsr-execution-final-actions";
|
|
15
|
+
const ACTIONS_CSS = ".wsr-answer-actions{align-items:center;gap:10px;height:28px;margin-top:16px;margin-left:-6px;display:flex}.wsr-answer-action{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:transparent;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.wsr-answer-action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}";
|
|
16
|
+
|
|
17
|
+
export function installActionPresentationStyle() {
|
|
18
|
+
if (typeof document === "undefined" || document.getElementById(ACTIONS_STYLE_ID) !== null) return;
|
|
19
|
+
const tag = document.createElement("style");
|
|
20
|
+
tag.id = ACTIONS_STYLE_ID;
|
|
21
|
+
tag.dataset.plugin = "dsh-wsr-execution";
|
|
22
|
+
tag.textContent = ACTIONS_CSS;
|
|
23
|
+
document.head.append(tag);
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
/**
|
|
16
27
|
* Build the WSR renderer from Harness-owned, public UI primitives. Dependency
|
|
17
28
|
* injection keeps the projection testable without copying any DSH component.
|
|
18
29
|
*/
|
|
19
|
-
export function createActionPresentationView({
|
|
30
|
+
export function createActionPresentationView({
|
|
31
|
+
React,
|
|
32
|
+
DisclosureRow,
|
|
33
|
+
MessageText,
|
|
34
|
+
StateDot,
|
|
35
|
+
JsonTree,
|
|
36
|
+
Tooltip,
|
|
37
|
+
IconCopyOutline16,
|
|
38
|
+
IconCheckOutline16,
|
|
39
|
+
writeClipboard,
|
|
40
|
+
observe = () => undefined,
|
|
41
|
+
}) {
|
|
20
42
|
if (typeof DisclosureRow !== "function") throw new TypeError("DSH_DISCLOSURE_ROW_REQUIRED");
|
|
43
|
+
installActionPresentationStyle();
|
|
21
44
|
|
|
22
|
-
return function WsrExecutionPresentationView({ node }) {
|
|
45
|
+
return function WsrExecutionPresentationView({ node, technicalDetails }) {
|
|
23
46
|
const presentation = node.data;
|
|
47
|
+
const presentationKind = typeof technicalDetails?.kind === "string"
|
|
48
|
+
? technicalDetails.kind
|
|
49
|
+
: presentation.layer === "final" ? "terminal-result"
|
|
50
|
+
: presentation.state === "waiting" ? "action-input-request"
|
|
51
|
+
: ["action", "tool"].includes(presentation.layer) ? "action-output"
|
|
52
|
+
: presentation.state === "failed" && presentation.title === "Workflow presentation" ? "error"
|
|
53
|
+
: presentation.layer === "progress" && presentation.state === "running" ? "command-accepted"
|
|
54
|
+
: "delivery-status";
|
|
24
55
|
const [open, setOpen] = React.useState(presentation.defaultOpen);
|
|
56
|
+
const [copyState, setCopyState] = React.useState("idle");
|
|
25
57
|
const bodyRef = React.useRef(null);
|
|
26
58
|
const previousState = React.useRef(presentation.state);
|
|
59
|
+
const copyPending = React.useRef(false);
|
|
60
|
+
const copyEpoch = React.useRef(0);
|
|
61
|
+
const copyTimer = React.useRef(null);
|
|
27
62
|
|
|
28
63
|
React.useEffect(() => {
|
|
29
64
|
setOpen((current) => resolveDisclosureOpen({
|
|
@@ -37,25 +72,67 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
|
|
|
37
72
|
previousState.current = presentation.state;
|
|
38
73
|
}, [presentation.state]);
|
|
39
74
|
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
copyEpoch.current += 1;
|
|
77
|
+
copyPending.current = false;
|
|
78
|
+
if (copyTimer.current !== null) clearTimeout(copyTimer.current);
|
|
79
|
+
copyTimer.current = null;
|
|
80
|
+
setCopyState("idle");
|
|
81
|
+
return () => {
|
|
82
|
+
copyEpoch.current += 1;
|
|
83
|
+
copyPending.current = false;
|
|
84
|
+
if (copyTimer.current !== null) clearTimeout(copyTimer.current);
|
|
85
|
+
};
|
|
86
|
+
}, [presentation.body, presentation.correlation]);
|
|
87
|
+
|
|
40
88
|
observe(presentation);
|
|
41
89
|
|
|
42
|
-
if (presentation.layer === "final") {
|
|
90
|
+
if (presentation.layer === "final" && presentation.state === "completed") {
|
|
91
|
+
const label = copyState === "copied" ? "Copied" : copyState === "failed" ? "Copy failed" : "Copy";
|
|
92
|
+
const onCopy = async () => {
|
|
93
|
+
if (copyState === "copied" || copyPending.current) return;
|
|
94
|
+
const epoch = copyEpoch.current;
|
|
95
|
+
copyPending.current = true;
|
|
96
|
+
let accepted = false;
|
|
97
|
+
try { accepted = await writeClipboard(presentation.body); }
|
|
98
|
+
catch { accepted = false; }
|
|
99
|
+
if (epoch !== copyEpoch.current) return;
|
|
100
|
+
copyPending.current = false;
|
|
101
|
+
setCopyState(accepted ? "copied" : "failed");
|
|
102
|
+
copyTimer.current = globalThis.setTimeout(() => {
|
|
103
|
+
copyTimer.current = null;
|
|
104
|
+
setCopyState("idle");
|
|
105
|
+
}, 1_000);
|
|
106
|
+
};
|
|
43
107
|
return React.createElement("article", {
|
|
44
108
|
"data-wsr-presentation": "true",
|
|
109
|
+
"data-wsr-kind": presentationKind,
|
|
110
|
+
"data-wsr-surface": "chat",
|
|
45
111
|
"data-wsr-layer": "final",
|
|
46
112
|
"data-wsr-state": presentation.state,
|
|
47
113
|
"data-wsr-correlation": presentation.correlation,
|
|
48
114
|
"data-wsr-chat-role": "assistant",
|
|
49
115
|
"data-wsr-compatibility": presentation.compatibility,
|
|
50
116
|
"aria-label": presentation.title,
|
|
51
|
-
},
|
|
117
|
+
},
|
|
118
|
+
React.createElement(MessageText, { text: presentation.body }),
|
|
119
|
+
React.createElement("div", {
|
|
120
|
+
className: "wsr-answer-actions",
|
|
121
|
+
"data-wsr-answer-actions": "true",
|
|
122
|
+
}, React.createElement(Tooltip, { label, side: "bottom" }, React.createElement("button", {
|
|
123
|
+
type: "button",
|
|
124
|
+
className: "wsr-answer-action",
|
|
125
|
+
"aria-label": label,
|
|
126
|
+
"data-copy-state": copyState,
|
|
127
|
+
onClick: onCopy,
|
|
128
|
+
}, React.createElement(copyState === "copied" ? IconCheckOutline16 : IconCopyOutline16, null)))));
|
|
52
129
|
}
|
|
53
130
|
|
|
54
131
|
const waiting = presentation.state === "waiting";
|
|
55
|
-
const expandable = presentation.body !== undefined && !waiting;
|
|
56
|
-
const body = presentation.body === undefined ? undefined : React.createElement("div", {
|
|
132
|
+
const expandable = (presentation.body !== undefined || technicalDetails !== undefined) && !waiting;
|
|
133
|
+
const body = presentation.body === undefined && technicalDetails === undefined ? undefined : React.createElement("div", {
|
|
57
134
|
ref: bodyRef,
|
|
58
|
-
"data-wsr-presentation": "true",
|
|
135
|
+
"data-wsr-presentation-body": "true",
|
|
59
136
|
"data-wsr-layer": presentation.layer,
|
|
60
137
|
"data-wsr-state": presentation.state,
|
|
61
138
|
"data-wsr-correlation": presentation.correlation,
|
|
@@ -64,9 +141,13 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
|
|
|
64
141
|
tabIndex: waiting ? 0 : undefined,
|
|
65
142
|
"aria-label": waiting ? presentation.summary : undefined,
|
|
66
143
|
"aria-live": waiting ? "polite" : undefined,
|
|
67
|
-
}, React.createElement("pre", {
|
|
144
|
+
}, presentation.body === undefined ? null : React.createElement("pre", {
|
|
68
145
|
style: { margin: 0, maxHeight: "20rem", overflow: "auto", whiteSpace: "pre-wrap", wordBreak: "break-word" },
|
|
69
|
-
}, presentation.body)
|
|
146
|
+
}, presentation.body), technicalDetails === undefined ? null : React.createElement("details", null,
|
|
147
|
+
React.createElement("summary", null, "Technical details"),
|
|
148
|
+
JsonTree === undefined
|
|
149
|
+
? React.createElement("pre", null, JSON.stringify(technicalDetails, null, 2))
|
|
150
|
+
: React.createElement(JsonTree, { data: technicalDetails, label: "WSR presentation", copyable: true, expandTopLevel: true })));
|
|
70
151
|
|
|
71
152
|
return React.createElement(DisclosureRow, {
|
|
72
153
|
icon: React.createElement(StateDot, { state: DOT_STATE[presentation.state], size: 10 }),
|
|
@@ -82,17 +163,94 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
|
|
|
82
163
|
keepContentWhenOpen: true,
|
|
83
164
|
collapsedContent: React.createElement("span", {
|
|
84
165
|
role: presentation.role,
|
|
85
|
-
"
|
|
166
|
+
"data-wsr-presentation": "true",
|
|
167
|
+
"data-wsr-kind": presentationKind,
|
|
168
|
+
"data-wsr-surface": "chat",
|
|
169
|
+
"data-wsr-chat-role": "assistant",
|
|
170
|
+
"data-wsr-correlation": presentation.correlation,
|
|
171
|
+
"data-wsr-state": presentation.state,
|
|
172
|
+
"aria-live": ["running", "recovering", "waiting"].includes(presentation.state) ? "polite" : undefined,
|
|
86
173
|
}, presentation.summary),
|
|
87
174
|
}, body);
|
|
88
175
|
};
|
|
89
176
|
}
|
|
90
177
|
|
|
91
|
-
|
|
178
|
+
const TERMINAL_PRESENTATION = Object.freeze({
|
|
179
|
+
SUCCEEDED: Object.freeze({ state: "completed", label: "Succeeded" }),
|
|
180
|
+
FAILED: Object.freeze({ state: "failed", label: "Failed" }),
|
|
181
|
+
CANCELLED: Object.freeze({ state: "cancelled", label: "Cancelled" }),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
function reconcileDeliveryPresentation(presentation, admitted, inventoryState) {
|
|
185
|
+
const deliveryId = admitted?.kind === "delivery-running" && typeof admitted.data.deliveryId === "string"
|
|
186
|
+
? admitted.data.deliveryId
|
|
187
|
+
: undefined;
|
|
188
|
+
const deliveries = ["ready", "reconnecting"].includes(inventoryState?.kind)
|
|
189
|
+
&& Array.isArray(inventoryState.snapshot?.deliveries)
|
|
190
|
+
? inventoryState.snapshot.deliveries
|
|
191
|
+
: [];
|
|
192
|
+
const matches = deliveryId === undefined ? [] : deliveries.filter((delivery) => delivery?.deliveryId === deliveryId);
|
|
193
|
+
const exact = matches.length === 1 ? matches[0] : undefined;
|
|
194
|
+
const terminal = exact?.lifecycle === "TERMINAL"
|
|
195
|
+
? TERMINAL_PRESENTATION[exact?.terminal?.outcome]
|
|
196
|
+
: undefined;
|
|
197
|
+
if (terminal !== undefined) return Object.freeze({
|
|
198
|
+
...presentation,
|
|
199
|
+
state: terminal.state,
|
|
200
|
+
summary: `${terminal.label} · ${deliveryId}`,
|
|
201
|
+
defaultOpen: false,
|
|
202
|
+
});
|
|
203
|
+
if (exact === undefined || typeof exact.lifecycle !== "string") return presentation;
|
|
204
|
+
const lifecycle = new Set(["BOUND", "START_UNCERTAIN", "RUNNING_CORRELATED", "START_FAILED", "RESULT_UNRESOLVED", "TERMINAL_HANDLING"]);
|
|
205
|
+
return lifecycle.has(exact.lifecycle)
|
|
206
|
+
? projectExecutionPresentation({
|
|
207
|
+
correlation: presentation.correlation,
|
|
208
|
+
kind: "delivery-status",
|
|
209
|
+
data: {
|
|
210
|
+
deliveryId,
|
|
211
|
+
state: exact.lifecycle,
|
|
212
|
+
...(admitted?.data?.diagnostic === undefined ? {} : { diagnostic: admitted.data.diagnostic }),
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
: presentation;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function commandPresentation(node, admitted, inventoryState) {
|
|
219
|
+
if (node.outcome === null) return Object.freeze({
|
|
220
|
+
correlation: String(node.commandId), layer: "progress", state: "running",
|
|
221
|
+
title: "Workflow delivery", summary: "Running", body: undefined,
|
|
222
|
+
defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
|
|
223
|
+
});
|
|
224
|
+
const event = admitted ?? parseExecutionPresentation(node.outcome?.text);
|
|
225
|
+
if (event.kind === "delivery-list") {
|
|
226
|
+
const count = Array.isArray(event.data.items) ? event.data.items.length : 0;
|
|
227
|
+
return Object.freeze({
|
|
228
|
+
correlation: event.correlation, layer: "progress", state: "completed",
|
|
229
|
+
title: "Delivery list", summary: `${count} ${count === 1 ? "delivery" : "deliveries"}`,
|
|
230
|
+
body: count === 0 ? "No deliveries." : JSON.stringify(event.data.items, null, 2),
|
|
231
|
+
defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return reconcileDeliveryPresentation(projectExecutionPresentation(event), event, inventoryState);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Replace the generic command card so one-line durable JSON remains inspectable. */
|
|
238
|
+
export function createWsrCommandView(options) {
|
|
239
|
+
const View = createActionPresentationView(options);
|
|
240
|
+
const { React, inventory } = options;
|
|
241
|
+
return function WsrCommandView({ node }) {
|
|
242
|
+
const admitted = node.outcome === null ? undefined : parseExecutionPresentation(node.outcome?.text);
|
|
243
|
+
const inventoryState = inventory === undefined
|
|
244
|
+
? undefined
|
|
245
|
+
: React.useSyncExternalStore(inventory.subscribe, inventory.getSnapshot, inventory.getSnapshot);
|
|
246
|
+
return View({ node: { data: commandPresentation(node, admitted, inventoryState) }, technicalDetails: admitted });
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Hide the earlier native command row and render the ordered presentation row. */
|
|
92
251
|
export function registerActionPresentation(ctx, View) {
|
|
93
|
-
ctx.
|
|
94
|
-
|
|
95
|
-
name: "conversation.chat.
|
|
96
|
-
|
|
97
|
-
}, View));
|
|
252
|
+
ctx.slots.inject("conversation.chat.commandview", () => {
|
|
253
|
+
ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr" }, () => null);
|
|
254
|
+
ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr-presentation" }, View);
|
|
255
|
+
});
|
|
98
256
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { DisclosureRow, MessageText, StateDot } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
2
|
+
import { Button, DisclosureRow, IconCheckOutline16, IconCopyOutline16, JsonTree, MessageText, Pill, StateDot, Tooltip, writeClipboard } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
3
3
|
import * as workspaceUi from "@deepseek-ai/dsh-client-ui-workspace";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { createWsrCommandView, registerActionPresentation } from "../action-presentation/view.js";
|
|
6
6
|
import { createDeliveryControlPlaneClient } from "./delivery/control-plane-port.js";
|
|
7
7
|
import { registerSessionDeliveryView } from "./delivery/session-delivery-view.js";
|
|
8
8
|
import { applyDeliverySidebar } from "./delivery-inventory/sidebar.js";
|
|
9
9
|
|
|
10
10
|
export const name = "wsr-execution-client";
|
|
11
11
|
export const inject = Object.freeze([
|
|
12
|
-
"connection", "
|
|
12
|
+
"connection", "sessions", "slots", "workspaces", "locale",
|
|
13
13
|
]);
|
|
14
14
|
|
|
15
15
|
export function apply(ctx) {
|
|
@@ -22,11 +22,30 @@ export function apply(ctx) {
|
|
|
22
22
|
applyDeliverySidebar(ctx, { React, workspaceUi, inventory: controlPlane.inventory });
|
|
23
23
|
registerSessionDeliveryView(ctx, {
|
|
24
24
|
React,
|
|
25
|
+
Button,
|
|
26
|
+
DisclosureRow,
|
|
27
|
+
IconCheckOutline16,
|
|
28
|
+
IconCopyOutline16,
|
|
29
|
+
Pill,
|
|
30
|
+
StateDot,
|
|
31
|
+
Tooltip,
|
|
32
|
+
writeClipboard,
|
|
25
33
|
bindProjection(sessionId) {
|
|
26
34
|
const source = controlPlane.bindSession(String(sessionId));
|
|
27
35
|
void source.refresh();
|
|
28
36
|
return source;
|
|
29
37
|
},
|
|
30
38
|
});
|
|
31
|
-
registerActionPresentation(ctx,
|
|
39
|
+
registerActionPresentation(ctx, createWsrCommandView({
|
|
40
|
+
React,
|
|
41
|
+
DisclosureRow,
|
|
42
|
+
IconCheckOutline16,
|
|
43
|
+
IconCopyOutline16,
|
|
44
|
+
JsonTree,
|
|
45
|
+
MessageText,
|
|
46
|
+
StateDot,
|
|
47
|
+
Tooltip,
|
|
48
|
+
writeClipboard,
|
|
49
|
+
inventory: controlPlane.inventory,
|
|
50
|
+
}));
|
|
32
51
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
const CHANNEL = "/wsr-execution";
|
|
2
|
+
const ERROR_CODES = new Set([
|
|
3
|
+
"DELIVERY_PROJECTION_CORRUPT",
|
|
4
|
+
"DELIVERY_PROJECTION_STALE_BINDING",
|
|
5
|
+
"DELIVERY_PROJECTION_RECOVERY_MISMATCH",
|
|
6
|
+
"DELIVERY_PROJECTION_UNAVAILABLE",
|
|
7
|
+
]);
|
|
2
8
|
|
|
3
9
|
function createStore(initial) {
|
|
4
10
|
let snapshot = initial;
|
|
@@ -23,7 +29,9 @@ export function createDeliveryControlPlaneClient(rpc) {
|
|
|
23
29
|
const sessions = new Map();
|
|
24
30
|
const read = async (endpoint, payload) => {
|
|
25
31
|
const result = await rpc.call(CHANNEL, endpoint, payload);
|
|
26
|
-
if (result?.ok !== true) throw new Error(message(result?.error))
|
|
32
|
+
if (result?.ok !== true) throw Object.assign(new Error(message(result?.error)), {
|
|
33
|
+
code: ERROR_CODES.has(result?.error?.code) ? result.error.code : "DELIVERY_PROJECTION_UNAVAILABLE",
|
|
34
|
+
});
|
|
27
35
|
return result.value;
|
|
28
36
|
};
|
|
29
37
|
const client = {
|
|
@@ -37,6 +45,7 @@ export function createDeliveryControlPlaneClient(rpc) {
|
|
|
37
45
|
const previous = inventory.getSnapshot();
|
|
38
46
|
inventory.publish({
|
|
39
47
|
kind: previous.kind === "ready" ? "reconnecting" : "error",
|
|
48
|
+
code: typeof error?.code === "string" ? error.code : "DELIVERY_PROJECTION_UNAVAILABLE",
|
|
40
49
|
message: message(error),
|
|
41
50
|
...(previous.kind === "ready" ? { snapshot: previous.snapshot } : {}),
|
|
42
51
|
});
|
|
@@ -53,7 +62,7 @@ export function createDeliveryControlPlaneClient(rpc) {
|
|
|
53
62
|
subscribe: store.subscribe,
|
|
54
63
|
async refresh() {
|
|
55
64
|
try { store.publish({ kind: "ready", view: await read("session/read", { sessionCorrelation }) }); }
|
|
56
|
-
catch (error) { store.publish({ kind: "error", code: "DELIVERY_PROJECTION_UNAVAILABLE", message: message(error) }); }
|
|
65
|
+
catch (error) { store.publish({ kind: "error", code: typeof error?.code === "string" ? error.code : "DELIVERY_PROJECTION_UNAVAILABLE", message: message(error) }); }
|
|
57
66
|
},
|
|
58
67
|
});
|
|
59
68
|
sessions.set(sessionCorrelation, source);
|