dsh-wsr-execution 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-wsr-execution",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
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",
@@ -61,9 +61,12 @@ function hasValidTypedData(value) {
61
61
  && (channel === "action" || channel === "tool");
62
62
  }
63
63
  if (value.kind === "terminal-result") {
64
- const outputValid = Object.hasOwn(value.data, "finalOutput")
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") {
@@ -145,7 +148,7 @@ export function projectExecutionPresentation(event) {
145
148
  return model({
146
149
  correlation, layer: "final", state, title: "Final result",
147
150
  summary: data.outcome[0] + data.outcome.slice(1).toLowerCase(), body,
148
- defaultOpen: true, focusPolicy: "none", role: "article",
151
+ defaultOpen: false, focusPolicy: "none", role: "article",
149
152
  compatibility: typeof data.finalOutput === "string" ? "current" : "legacy-summary",
150
153
  });
151
154
  }
@@ -163,7 +166,7 @@ export function projectExecutionPresentation(event) {
163
166
  correlation, layer: data.channel === "tool" ? "tool" : "action", state,
164
167
  title: typeof data.label === "string" ? data.label : "Workflow Action",
165
168
  summary: STATE_LABELS[state], body: text(data.content) ?? "WSR content unavailable",
166
- defaultOpen: state !== "completed", focusPolicy: "none", role: "status", compatibility: "current",
169
+ defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
167
170
  });
168
171
  }
169
172
  if (event.kind === "error") {
@@ -171,7 +174,7 @@ export function projectExecutionPresentation(event) {
171
174
  correlation, layer: "progress", state: "failed", title: "Workflow presentation",
172
175
  summary: typeof data.code === "string" ? data.code : "WSR_ERROR",
173
176
  body: typeof data.message === "string" ? data.message : "WSR presentation unavailable",
174
- defaultOpen: true, focusPolicy: "none", role: "alert", compatibility: "current",
177
+ defaultOpen: false, focusPolicy: "none", role: "alert", compatibility: "current",
175
178
  });
176
179
  }
177
180
 
@@ -182,7 +185,7 @@ export function projectExecutionPresentation(event) {
182
185
  return model({
183
186
  correlation, layer: "progress", state, title: "Workflow delivery",
184
187
  summary: `${STATE_LABELS[state]}${deliveryId === undefined ? "" : ` · ${deliveryId}`}`,
185
- body: undefined, defaultOpen: state !== "completed", focusPolicy: "none", role: "status", compatibility: "current",
188
+ body: undefined, defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
186
189
  });
187
190
  }
188
191
 
@@ -190,7 +193,6 @@ export function projectExecutionPresentation(event) {
190
193
  export function resolveDisclosureOpen({ current, previousState, nextState, containsFocus }) {
191
194
  if (nextState === "waiting") return true;
192
195
  if (nextState === "completed" && previousState !== "completed") return containsFocus ? true : false;
193
- if (["running", "recovering", "failed", "cancelled"].includes(nextState) && nextState !== previousState) return true;
194
196
  return current;
195
197
  }
196
198
 
@@ -217,6 +219,10 @@ export function createExecutionPresentationDefinition() {
217
219
  if (event.kind === "delivery-list") {
218
220
  return Object.freeze({ ...context.state, presentation: undefined });
219
221
  }
222
+ if (event.kind === "terminal-result" && event.data.outcome === "SUCCEEDED"
223
+ && !Object.hasOwn(event.data, "finalOutput") && !Object.hasOwn(event.data, "summary")) {
224
+ return Object.freeze({ ...context.state, presentation: undefined });
225
+ }
220
226
  return Object.freeze({ ...context.state, presentation: projectExecutionPresentation(event) });
221
227
  },
222
228
  buildViewNode(context) {
@@ -1,7 +1,4 @@
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",
@@ -12,18 +9,46 @@ const DOT_STATE = Object.freeze({
12
9
  cancelled: "error",
13
10
  });
14
11
 
12
+ const ACTIONS_STYLE_ID = "dsh-wsr-execution-final-actions";
13
+ 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)}";
14
+
15
+ export function installActionPresentationStyle() {
16
+ if (typeof document === "undefined" || document.getElementById(ACTIONS_STYLE_ID) !== null) return;
17
+ const tag = document.createElement("style");
18
+ tag.id = ACTIONS_STYLE_ID;
19
+ tag.dataset.plugin = "dsh-wsr-execution";
20
+ tag.textContent = ACTIONS_CSS;
21
+ document.head.append(tag);
22
+ }
23
+
15
24
  /**
16
25
  * Build the WSR renderer from Harness-owned, public UI primitives. Dependency
17
26
  * injection keeps the projection testable without copying any DSH component.
18
27
  */
19
- export function createActionPresentationView({ React, DisclosureRow, MessageText, StateDot, observe = () => undefined }) {
28
+ export function createActionPresentationView({
29
+ React,
30
+ DisclosureRow,
31
+ MessageText,
32
+ StateDot,
33
+ JsonTree,
34
+ Tooltip,
35
+ IconCopyOutline16,
36
+ IconCheckOutline16,
37
+ writeClipboard,
38
+ observe = () => undefined,
39
+ }) {
20
40
  if (typeof DisclosureRow !== "function") throw new TypeError("DSH_DISCLOSURE_ROW_REQUIRED");
41
+ installActionPresentationStyle();
21
42
 
22
- return function WsrExecutionPresentationView({ node }) {
43
+ return function WsrExecutionPresentationView({ node, technicalDetails }) {
23
44
  const presentation = node.data;
24
45
  const [open, setOpen] = React.useState(presentation.defaultOpen);
46
+ const [copyState, setCopyState] = React.useState("idle");
25
47
  const bodyRef = React.useRef(null);
26
48
  const previousState = React.useRef(presentation.state);
49
+ const copyPending = React.useRef(false);
50
+ const copyEpoch = React.useRef(0);
51
+ const copyTimer = React.useRef(null);
27
52
 
28
53
  React.useEffect(() => {
29
54
  setOpen((current) => resolveDisclosureOpen({
@@ -37,9 +62,38 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
37
62
  previousState.current = presentation.state;
38
63
  }, [presentation.state]);
39
64
 
65
+ React.useEffect(() => {
66
+ copyEpoch.current += 1;
67
+ copyPending.current = false;
68
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
69
+ copyTimer.current = null;
70
+ setCopyState("idle");
71
+ return () => {
72
+ copyEpoch.current += 1;
73
+ copyPending.current = false;
74
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
75
+ };
76
+ }, [presentation.body, presentation.correlation]);
77
+
40
78
  observe(presentation);
41
79
 
42
- if (presentation.layer === "final") {
80
+ if (presentation.layer === "final" && presentation.state === "completed") {
81
+ const label = copyState === "copied" ? "Copied" : copyState === "failed" ? "Copy failed" : "Copy";
82
+ const onCopy = async () => {
83
+ if (copyState === "copied" || copyPending.current) return;
84
+ const epoch = copyEpoch.current;
85
+ copyPending.current = true;
86
+ let accepted = false;
87
+ try { accepted = await writeClipboard(presentation.body); }
88
+ catch { accepted = false; }
89
+ if (epoch !== copyEpoch.current) return;
90
+ copyPending.current = false;
91
+ setCopyState(accepted ? "copied" : "failed");
92
+ copyTimer.current = globalThis.setTimeout(() => {
93
+ copyTimer.current = null;
94
+ setCopyState("idle");
95
+ }, 1_000);
96
+ };
43
97
  return React.createElement("article", {
44
98
  "data-wsr-presentation": "true",
45
99
  "data-wsr-layer": "final",
@@ -48,12 +102,23 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
48
102
  "data-wsr-chat-role": "assistant",
49
103
  "data-wsr-compatibility": presentation.compatibility,
50
104
  "aria-label": presentation.title,
51
- }, React.createElement(MessageText, { text: presentation.body }));
105
+ },
106
+ React.createElement(MessageText, { text: presentation.body }),
107
+ React.createElement("div", {
108
+ className: "wsr-answer-actions",
109
+ "data-wsr-answer-actions": "true",
110
+ }, React.createElement(Tooltip, { label, side: "bottom" }, React.createElement("button", {
111
+ type: "button",
112
+ className: "wsr-answer-action",
113
+ "aria-label": label,
114
+ "data-copy-state": copyState,
115
+ onClick: onCopy,
116
+ }, React.createElement(copyState === "copied" ? IconCheckOutline16 : IconCopyOutline16, null)))));
52
117
  }
53
118
 
54
119
  const waiting = presentation.state === "waiting";
55
- const expandable = presentation.body !== undefined && !waiting;
56
- const body = presentation.body === undefined ? undefined : React.createElement("div", {
120
+ const expandable = (presentation.body !== undefined || technicalDetails !== undefined) && !waiting;
121
+ const body = presentation.body === undefined && technicalDetails === undefined ? undefined : React.createElement("div", {
57
122
  ref: bodyRef,
58
123
  "data-wsr-presentation": "true",
59
124
  "data-wsr-layer": presentation.layer,
@@ -64,9 +129,13 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
64
129
  tabIndex: waiting ? 0 : undefined,
65
130
  "aria-label": waiting ? presentation.summary : undefined,
66
131
  "aria-live": waiting ? "polite" : undefined,
67
- }, React.createElement("pre", {
132
+ }, presentation.body === undefined ? null : React.createElement("pre", {
68
133
  style: { margin: 0, maxHeight: "20rem", overflow: "auto", whiteSpace: "pre-wrap", wordBreak: "break-word" },
69
- }, presentation.body));
134
+ }, presentation.body), technicalDetails === undefined ? null : React.createElement("details", null,
135
+ React.createElement("summary", null, "Technical details"),
136
+ JsonTree === undefined
137
+ ? React.createElement("pre", null, JSON.stringify(technicalDetails, null, 2))
138
+ : React.createElement(JsonTree, { data: technicalDetails, label: "WSR presentation", copyable: true, expandTopLevel: true })));
70
139
 
71
140
  return React.createElement(DisclosureRow, {
72
141
  icon: React.createElement(StateDot, { state: DOT_STATE[presentation.state], size: 10 }),
@@ -88,11 +157,69 @@ export function createActionPresentationView({ React, DisclosureRow, MessageText
88
157
  };
89
158
  }
90
159
 
91
- /** Register the feature-owned event projection and its one keyed chat node. */
160
+ const TERMINAL_PRESENTATION = Object.freeze({
161
+ SUCCEEDED: Object.freeze({ state: "completed", label: "Succeeded" }),
162
+ FAILED: Object.freeze({ state: "failed", label: "Failed" }),
163
+ CANCELLED: Object.freeze({ state: "cancelled", label: "Cancelled" }),
164
+ });
165
+
166
+ function reconcileDeliveryPresentation(presentation, admitted, inventoryState) {
167
+ const deliveryId = admitted?.kind === "delivery-running" && typeof admitted.data.deliveryId === "string"
168
+ ? admitted.data.deliveryId
169
+ : undefined;
170
+ const deliveries = ["ready", "reconnecting"].includes(inventoryState?.kind)
171
+ && Array.isArray(inventoryState.snapshot?.deliveries)
172
+ ? inventoryState.snapshot.deliveries
173
+ : [];
174
+ const matches = deliveryId === undefined ? [] : deliveries.filter((delivery) => delivery?.deliveryId === deliveryId);
175
+ const terminal = matches.length === 1 && matches[0]?.lifecycle === "TERMINAL"
176
+ ? TERMINAL_PRESENTATION[matches[0]?.terminal?.outcome]
177
+ : undefined;
178
+ if (terminal === undefined) return presentation;
179
+ return Object.freeze({
180
+ ...presentation,
181
+ state: terminal.state,
182
+ summary: `${terminal.label} · ${deliveryId}`,
183
+ defaultOpen: false,
184
+ });
185
+ }
186
+
187
+ function commandPresentation(node, admitted, inventoryState) {
188
+ if (node.outcome === null) return Object.freeze({
189
+ correlation: String(node.commandId), layer: "progress", state: "running",
190
+ title: "Workflow delivery", summary: "Running", body: undefined,
191
+ defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
192
+ });
193
+ const event = admitted ?? parseExecutionPresentation(node.outcome?.text);
194
+ if (event.kind === "delivery-list") {
195
+ const count = Array.isArray(event.data.items) ? event.data.items.length : 0;
196
+ return Object.freeze({
197
+ correlation: event.correlation, layer: "progress", state: "completed",
198
+ title: "Delivery list", summary: `${count} ${count === 1 ? "delivery" : "deliveries"}`,
199
+ body: count === 0 ? "No deliveries." : JSON.stringify(event.data.items, null, 2),
200
+ defaultOpen: false, focusPolicy: "none", role: "status", compatibility: "current",
201
+ });
202
+ }
203
+ return reconcileDeliveryPresentation(projectExecutionPresentation(event), event, inventoryState);
204
+ }
205
+
206
+ /** Replace the generic command card so one-line durable JSON remains inspectable. */
207
+ export function createWsrCommandView(options) {
208
+ const View = createActionPresentationView(options);
209
+ const { React, inventory } = options;
210
+ return function WsrCommandView({ node }) {
211
+ const admitted = node.outcome === null ? undefined : parseExecutionPresentation(node.outcome?.text);
212
+ const inventoryState = inventory === undefined
213
+ ? undefined
214
+ : React.useSyncExternalStore(inventory.subscribe, inventory.getSnapshot, inventory.getSnapshot);
215
+ return View({ node: { data: commandPresentation(node, admitted, inventoryState) }, technicalDetails: admitted });
216
+ };
217
+ }
218
+
219
+ /** Hide the earlier native command row and render the ordered presentation row. */
92
220
  export function registerActionPresentation(ctx, View) {
93
- ctx.conversationEvents.register(createExecutionPresentationDefinition());
94
- ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
95
- name: "conversation.chat.node",
96
- key: "wsr-execution-presentation",
97
- }, View));
221
+ ctx.slots.inject("conversation.chat.commandview", () => {
222
+ ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr" }, () => null);
223
+ ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr-presentation" }, View);
224
+ });
98
225
  }
@@ -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 { createActionPresentationView, registerActionPresentation } from "../action-presentation/view.js";
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", "conversationEvents", "sessions", "slots", "workspaces", "locale",
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, createActionPresentationView({ React, DisclosureRow, MessageText, StateDot }));
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);
@@ -6,6 +6,39 @@ const LIFECYCLES = new Set([
6
6
  "BOUND", "START_UNCERTAIN", "RUNNING_CORRELATED", "START_FAILED",
7
7
  "RESULT_UNRESOLVED", "TERMINAL_HANDLING", "TERMINAL",
8
8
  ]);
9
+ const DELIVERY_STYLE_ID = "dsh-wsr-execution-delivery-view";
10
+ const DELIVERY_CSS = `
11
+ .wsr-delivery-view { box-sizing: border-box; width: 100%; max-width: 960px; margin: 0 auto; padding: 20px; color: var(--dsw-alias-label-primary); }
12
+ .wsr-delivery-heading { margin: 0 0 16px; font-size: 20px; line-height: 28px; }
13
+ .wsr-delivery-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: 8px; margin: 0 0 16px; }
14
+ .wsr-delivery-summary-item { min-width: 0; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; background: var(--dsw-alias-bg-layer-1); }
15
+ .wsr-delivery-summary-item dt, .wsr-delivery-identity dt { margin: 0 0 3px; color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 16px; }
16
+ .wsr-delivery-summary-item dd, .wsr-delivery-identity dd { min-width: 0; margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
17
+ .wsr-delivery-status { display: inline-flex; min-width: 0; align-items: center; gap: 6px; }
18
+ .wsr-delivery-status > span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
19
+ .wsr-delivery-identities { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 8px 16px; margin: 8px 0 0; }
20
+ .wsr-delivery-identity { min-width: 0; margin: 0; }
21
+ .wsr-delivery-identity dd { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px; }
22
+ .wsr-delivery-identity code { display: block; min-width: 0; max-width: 100%; color: inherit; font-family: var(--dsw-font-family-mono, ui-monospace, monospace); overflow-wrap: anywhere; white-space: normal; }
23
+ .wsr-delivery-preview { display: block; min-width: 0; max-width: 100%; margin-inline-start: 8px; overflow: hidden; color: var(--dsw-alias-label-tertiary); text-overflow: ellipsis; white-space: nowrap; }
24
+ .wsr-delivery-copy-feedback { min-height: 20px; margin: 8px 0 0; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 20px; }
25
+ .wsr-delivery-condition { margin-top: 12px; padding: 10px 12px; border-left: 3px solid var(--dsw-alias-state-warn-primary); border-radius: 4px; background: var(--dsw-alias-bg-layer-1); }
26
+ .wsr-delivery-condition h3 { margin: 0 0 4px; font-size: 13px; line-height: 20px; }
27
+ .wsr-delivery-condition code, .wsr-delivery-state code { overflow-wrap: anywhere; }
28
+ .wsr-delivery-state { display: grid; gap: 8px; }
29
+ .wsr-delivery-state p { margin: 0; }
30
+ @media (max-width: 720px) { .wsr-delivery-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
31
+ @media (max-width: 420px) { .wsr-delivery-view { padding: 12px; } .wsr-delivery-summary, .wsr-delivery-identities { grid-template-columns: minmax(0, 1fr); } }
32
+ @media (prefers-reduced-motion: reduce) { .wsr-delivery-view, .wsr-delivery-view * { scroll-behavior: auto !important; transition: none !important; } }
33
+ `;
34
+
35
+ function ensureDeliveryStyles() {
36
+ if (typeof document === "undefined" || document.getElementById(DELIVERY_STYLE_ID) !== null) return;
37
+ const tag = document.createElement("style");
38
+ tag.id = DELIVERY_STYLE_ID;
39
+ tag.textContent = DELIVERY_CSS;
40
+ document.head.appendChild(tag);
41
+ }
9
42
 
10
43
  function nonEmpty(value) {
11
44
  return typeof value === "string" && value.length > 0;
@@ -46,79 +79,157 @@ function safeSubscribe(source, notify) {
46
79
  catch { return () => undefined; }
47
80
  }
48
81
 
49
- function pair(React, label, value) {
50
- return [
51
- React.createElement("dt", { key: `${label}-label` }, label),
52
- React.createElement("dd", { key: `${label}-value` }, value),
53
- ];
82
+ function summaryItem(React, label, value, extra = {}) {
83
+ return React.createElement("div", { className: "wsr-delivery-summary-item", ...extra },
84
+ React.createElement("dt", null, label),
85
+ React.createElement("dd", null, value));
54
86
  }
55
87
 
56
- function statePanel(React, role, code, message) {
88
+ function statePanel(React, StateDot, role, code, message) {
57
89
  return React.createElement("section", {
90
+ className: "wsr-delivery-view wsr-delivery-state",
58
91
  "aria-labelledby": "wsr-delivery-view-title", "aria-live": role === "alert" ? "assertive" : "polite",
59
92
  "data-wsr-delivery-view": "true", role,
60
- }, React.createElement("h2", { id: "wsr-delivery-view-title" }, "Delivery"),
61
- React.createElement("p", null, message), code === undefined ? null : React.createElement("code", null, code));
93
+ }, React.createElement("h2", { className: "wsr-delivery-heading", id: "wsr-delivery-view-title" }, "Delivery"),
94
+ React.createElement("p", null,
95
+ React.createElement(StateDot, { state: role === "alert" ? "error" : "ongoing", size: 10 }), " ", message),
96
+ code === undefined ? null : React.createElement("code", null, code));
97
+ }
98
+
99
+ function statusState(delivery, failed) {
100
+ if (failed) return "error";
101
+ if (delivery.terminal?.outcome === "SUCCEEDED") return "done";
102
+ if (delivery.terminal !== null || ["START_UNCERTAIN", "RESULT_UNRESOLVED", "START_FAILED"].includes(delivery.lifecycle)) return "warning";
103
+ return "ongoing";
104
+ }
105
+
106
+ function identityCard(React, primitives, label, value, displayValue = value) {
107
+ const { Button, IconCheckOutline16, IconCopyOutline16, Tooltip, onCopy, copiedLabel } = primitives;
108
+ const exact = React.createElement("code", {
109
+ "aria-label": `${label}: ${value}`,
110
+ "data-wsr-delivery-identity": label,
111
+ title: value,
112
+ }, displayValue);
113
+ const copied = copiedLabel === label;
114
+ const control = React.createElement(Tooltip, { label: copied ? `${label} copied` : `Copy ${label}`, side: "bottom" },
115
+ React.createElement(Button, {
116
+ "aria-label": `Copy ${label}`,
117
+ icon: React.createElement(copied ? IconCheckOutline16 : IconCopyOutline16, null),
118
+ onClick: () => onCopy(label, value),
119
+ size: "sm",
120
+ type: "button",
121
+ variant: "toolbar",
122
+ }, copied ? "Copied" : "Copy"));
123
+ return React.createElement("div", { className: "wsr-delivery-identity", key: label },
124
+ React.createElement("dt", null, label),
125
+ React.createElement("dd", null, exact, control));
62
126
  }
63
127
 
64
128
  /** Render the exact owner `SessionDeliveryView` without a shadow projection. */
65
- export function createSessionDeliveryView(React) {
129
+ export function createSessionDeliveryView(React, primitives = {}) {
66
130
  if (typeof React?.createElement !== "function" || typeof React?.useSyncExternalStore !== "function") {
67
131
  throw new TypeError("DELIVERY_VIEW_REACT_INVALID");
68
132
  }
133
+ const DisclosureRow = primitives.DisclosureRow ?? "div";
134
+ const Button = primitives.Button ?? "button";
135
+ const IconCheckOutline16 = primitives.IconCheckOutline16 ?? "span";
136
+ const IconCopyOutline16 = primitives.IconCopyOutline16 ?? "span";
137
+ const Pill = primitives.Pill ?? "span";
138
+ const StateDot = primitives.StateDot ?? "span";
139
+ const Tooltip = primitives.Tooltip ?? "span";
140
+ const writeClipboard = primitives.writeClipboard ?? (async () => false);
141
+ ensureDeliveryStyles();
69
142
  return function SessionDeliveryView({ sessionId, source }) {
143
+ const [identitiesOpen, setIdentitiesOpen] = typeof React.useState === "function"
144
+ ? React.useState(false)
145
+ : [false, () => undefined];
146
+ const [copiedLabel, setCopiedLabel] = typeof React.useState === "function"
147
+ ? React.useState("")
148
+ : ["", () => undefined];
70
149
  const state = React.useSyncExternalStore(
71
150
  (notify) => safeSubscribe(source, notify),
72
151
  () => safeSnapshot(source),
73
152
  () => safeSnapshot(source),
74
153
  );
75
- if (state.kind === "loading") return statePanel(React, "status", undefined, "Loading Delivery…");
76
- if (state.kind === "error") return statePanel(React, "alert", state.code ?? "DELIVERY_PROJECTION_UNAVAILABLE", state.message ?? "Execution projection unavailable");
154
+ if (state.kind === "loading") return statePanel(React, StateDot, "status", undefined, "Loading Delivery…");
155
+ if (state.kind === "error") return statePanel(React, StateDot, "alert", state.code ?? "DELIVERY_PROJECTION_UNAVAILABLE", state.message ?? "Execution projection unavailable");
77
156
  const view = state.view;
78
157
  if (state.kind !== "ready" || view?.sessionCorrelation !== sessionId) {
79
- return statePanel(React, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
158
+ return statePanel(React, StateDot, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
80
159
  }
81
- if (view.kind === "UNBOUND") return statePanel(React, "status", undefined, "No Delivery bound to this Session");
160
+ if (view.kind === "UNBOUND") return statePanel(React, StateDot, "status", undefined, "No Delivery bound to this Session");
82
161
  if (view.kind !== "BOUND" || !validDelivery(view.delivery, sessionId)) {
83
- return statePanel(React, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
162
+ return statePanel(React, StateDot, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
84
163
  }
85
164
  const delivery = view.delivery;
86
165
  const failed = delivery.terminal?.outcome === "FAILED" || delivery.error !== null;
87
166
  const identityRows = [
88
- ...pair(React, "Delivery", delivery.deliveryId),
89
- ...pair(React, "Task", delivery.task.displayName ?? delivery.task.identity),
90
- ...pair(React, "Workflow", delivery.workflow.identity),
91
- ...pair(React, "Package", `${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`),
92
- ...pair(React, "Package digest", delivery.workflow.packageDigest),
93
- ...pair(React, "Snapshot", delivery.workflow.snapshotIdentity),
94
- ...pair(React, "Snapshot digest", delivery.workflow.snapshotDigest),
95
- ...pair(React, "Binding", delivery.deliveryBindingIdentity),
167
+ ["Delivery", delivery.deliveryId],
168
+ ["Task", delivery.task.identity, delivery.task.displayName === null ? delivery.task.identity : `${delivery.task.displayName} · ${delivery.task.identity}`],
169
+ ["Workflow", delivery.workflow.identity],
170
+ ["Package", `${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`],
171
+ ["Package digest", delivery.workflow.packageDigest],
172
+ ["Snapshot", delivery.workflow.snapshotIdentity],
173
+ ["Snapshot digest", delivery.workflow.snapshotDigest],
174
+ ["Binding", delivery.deliveryBindingIdentity],
175
+ ...(nonEmpty(delivery.worktree) ? [["Worktree", delivery.worktree]] : []),
96
176
  ];
97
- const lifecycleRows = [
98
- ...pair(React, "Lifecycle", delivery.lifecycle),
99
- ...pair(React, "Recoverable", delivery.recoverable ? "yes" : "no"),
100
- ...(delivery.current === null ? [] : pair(React, delivery.current.kind === "ACTION" ? "Current Action" : "Current Intervention", delivery.current.identity)),
101
- ...pair(React, "Started", new Date(delivery.timing.startedAt).toISOString()),
102
- ...pair(React, "Elapsed", duration(delivery.timing.elapsedMs)),
103
- ...(delivery.terminal === null ? [] : [
104
- ...pair(React, "Ended", new Date(delivery.terminal.finishedAt).toISOString()),
105
- ...pair(React, "Outcome", delivery.terminal.outcome),
106
- ]),
107
- ...(delivery.error === null ? [] : pair(React, "Error", delivery.error.code)),
177
+ const statusLabel = delivery.terminal?.outcome ?? delivery.lifecycle;
178
+ const workflowLabel = `${delivery.workflow.identity} · ${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`;
179
+ const summary = [
180
+ summaryItem(React, "Status", React.createElement("span", { className: "wsr-delivery-status" },
181
+ React.createElement(StateDot, { state: statusState(delivery, failed), size: 10 }),
182
+ React.createElement(Pill, { "aria-label": `Delivery status ${statusLabel}` }, statusLabel))),
183
+ summaryItem(React, "Workflow", workflowLabel),
184
+ ...(delivery.current === null ? [] : [summaryItem(
185
+ React,
186
+ delivery.current.kind === "ACTION" ? "Current Action" : "Current Intervention",
187
+ delivery.current.identity,
188
+ { "data-wsr-delivery-conditional": "current" },
189
+ )]),
190
+ ...(delivery.terminal === null ? [] : [summaryItem(React, "Outcome", delivery.terminal.outcome, { "data-wsr-delivery-conditional": "terminal" })]),
191
+ summaryItem(React, "Elapsed", duration(delivery.timing.elapsedMs)),
192
+ summaryItem(React, "Started", new Date(delivery.timing.startedAt).toISOString()),
193
+ ...(delivery.terminal === null ? [] : [summaryItem(React, "Ended", new Date(delivery.terminal.finishedAt).toISOString())]),
108
194
  ];
109
195
  return React.createElement("section", {
196
+ className: "wsr-delivery-view",
110
197
  "aria-labelledby": "wsr-delivery-view-title", "aria-live": failed ? "assertive" : "polite",
111
198
  "data-wsr-delivery-id": delivery.deliveryId, "data-wsr-delivery-view": "true", role: failed ? "alert" : "region",
112
- }, React.createElement("h2", { id: "wsr-delivery-view-title" }, "Delivery"),
113
- React.createElement("dl", { "aria-label": "Delivery identity" }, identityRows),
114
- React.createElement("dl", { "aria-label": "Delivery lifecycle" }, lifecycleRows));
199
+ }, React.createElement("h2", { className: "wsr-delivery-heading", id: "wsr-delivery-view-title" }, "Delivery"),
200
+ React.createElement("dl", { "aria-label": "Delivery summary", "data-wsr-delivery-summary": "true", className: "wsr-delivery-summary" }, summary),
201
+ React.createElement(DisclosureRow, {
202
+ title: "Identity details",
203
+ icon: React.createElement(StateDot, { state: statusState(delivery, failed), size: 10 }),
204
+ open: identitiesOpen,
205
+ expandable: true,
206
+ expandOnRowClick: true,
207
+ onToggle: () => setIdentitiesOpen((open) => !open),
208
+ collapsedContent: React.createElement("code", { className: "wsr-delivery-preview" }, delivery.deliveryId),
209
+ }, React.createElement("dl", { "aria-label": "Delivery identity", className: "wsr-delivery-identities" },
210
+ identityRows.map(([label, value, displayValue]) => identityCard(React, {
211
+ Button,
212
+ IconCheckOutline16,
213
+ IconCopyOutline16,
214
+ Tooltip,
215
+ copiedLabel,
216
+ async onCopy(copyLabel, value) {
217
+ setCopiedLabel(await writeClipboard(value) ? copyLabel : `${copyLabel} copy failed`);
218
+ },
219
+ }, label, value, displayValue))),
220
+ React.createElement("p", {
221
+ "aria-live": "polite", className: "wsr-delivery-copy-feedback", role: "status",
222
+ }, copiedLabel === "" ? "" : copiedLabel.endsWith("copy failed") ? copiedLabel : `${copiedLabel} copied`)),
223
+ delivery.error === null ? null : React.createElement("section", {
224
+ className: "wsr-delivery-condition", "data-wsr-delivery-conditional": "error", role: "alert",
225
+ }, React.createElement("h3", null, "Failure diagnostic"), React.createElement("code", null, delivery.error.code)));
115
226
  };
116
227
  }
117
228
 
118
229
  export function registerSessionDeliveryView(ctx, options) {
119
230
  if (typeof ctx?.slots?.inject !== "function" || typeof ctx?.slots?.register !== "function"
120
231
  || typeof options?.bindProjection !== "function") throw new TypeError("DELIVERY_VIEW_REGISTRATION_INVALID");
121
- const View = createSessionDeliveryView(options.React);
232
+ const View = createSessionDeliveryView(options.React, options);
122
233
  ctx.slots.inject("conversation.view", () => ctx.slots.register({
123
234
  name: "conversation.view", id: DELIVERY_VIEW_ID, order: DELIVERY_VIEW_ORDER, label: "Delivery",
124
235
  inject: (sessionId) => ({ source: options.bindProjection(sessionId) }),