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/lib/client.js CHANGED
@@ -2567,7 +2567,9 @@ function hasValidTypedData(value) {
2567
2567
  return typeof state === "string" && ACTION_STATES.has(state) && (channel === "action" || channel === "tool");
2568
2568
  }
2569
2569
  if (value.kind === "terminal-result") {
2570
- const outputValid = Object.hasOwn(value.data, "finalOutput") ? typeof value.data.finalOutput === "string" && value.data.finalOutput.length > 0 : typeof value.data.summary === "string" && value.data.summary.length > 0;
2570
+ const hasFinalOutput = Object.hasOwn(value.data, "finalOutput");
2571
+ const hasSummary = Object.hasOwn(value.data, "summary");
2572
+ const outputValid = hasFinalOutput ? typeof value.data.finalOutput === "string" && value.data.finalOutput.length > 0 : hasSummary ? typeof value.data.summary === "string" && value.data.summary.length > 0 : value.data.outcome === "SUCCEEDED";
2571
2573
  return typeof value.data.outcome === "string" && TERMINAL_OUTCOMES.has(value.data.outcome) && outputValid;
2572
2574
  }
2573
2575
  if (value.kind === "delivery-running" || value.kind === "delivery-status") {
@@ -2638,7 +2640,7 @@ function projectExecutionPresentation(event) {
2638
2640
  title: "Final result",
2639
2641
  summary: data.outcome[0] + data.outcome.slice(1).toLowerCase(),
2640
2642
  body,
2641
- defaultOpen: true,
2643
+ defaultOpen: false,
2642
2644
  focusPolicy: "none",
2643
2645
  role: "article",
2644
2646
  compatibility: typeof data.finalOutput === "string" ? "current" : "legacy-summary"
@@ -2667,7 +2669,7 @@ function projectExecutionPresentation(event) {
2667
2669
  title: typeof data.label === "string" ? data.label : "Workflow Action",
2668
2670
  summary: STATE_LABELS[state2],
2669
2671
  body: text(data.content) ?? "WSR content unavailable",
2670
- defaultOpen: state2 !== "completed",
2672
+ defaultOpen: false,
2671
2673
  focusPolicy: "none",
2672
2674
  role: "status",
2673
2675
  compatibility: "current"
@@ -2681,7 +2683,7 @@ function projectExecutionPresentation(event) {
2681
2683
  title: "Workflow presentation",
2682
2684
  summary: typeof data.code === "string" ? data.code : "WSR_ERROR",
2683
2685
  body: typeof data.message === "string" ? data.message : "WSR presentation unavailable",
2684
- defaultOpen: true,
2686
+ defaultOpen: false,
2685
2687
  focusPolicy: "none",
2686
2688
  role: "alert",
2687
2689
  compatibility: "current"
@@ -2696,7 +2698,7 @@ function projectExecutionPresentation(event) {
2696
2698
  title: "Workflow delivery",
2697
2699
  summary: `${STATE_LABELS[state]}${deliveryId === void 0 ? "" : ` \xB7 ${deliveryId}`}`,
2698
2700
  body: void 0,
2699
- defaultOpen: state !== "completed",
2701
+ defaultOpen: false,
2700
2702
  focusPolicy: "none",
2701
2703
  role: "status",
2702
2704
  compatibility: "current"
@@ -2705,44 +2707,8 @@ function projectExecutionPresentation(event) {
2705
2707
  function resolveDisclosureOpen({ current, previousState, nextState, containsFocus }) {
2706
2708
  if (nextState === "waiting") return true;
2707
2709
  if (nextState === "completed" && previousState !== "completed") return containsFocus ? true : false;
2708
- if (["running", "recovering", "failed", "cancelled"].includes(nextState) && nextState !== previousState) return true;
2709
2710
  return current;
2710
2711
  }
2711
- function createExecutionPresentationDefinition() {
2712
- return Object.freeze({
2713
- kind: "wsr-execution-presentation",
2714
- target: "chat",
2715
- match(event) {
2716
- if (event?.type === "command/run" && event.data?.name === "wsr" && event.data?.source?.kind === "plugin" && event.data?.source?.plugin === "workflow-execution" && typeof event.data?.commandId === "string") {
2717
- return { id: event.data.commandId, role: "start" };
2718
- }
2719
- return event?.type === "command/done" && typeof event.data?.commandId === "string" ? { id: event.data.commandId, role: "update" } : null;
2720
- },
2721
- start(_context, match) {
2722
- return Object.freeze({ seq: match.event.seq, presentation: void 0 });
2723
- },
2724
- update(context, match) {
2725
- const event = parseExecutionPresentation(match.event?.data?.text);
2726
- if (event.kind === "delivery-list") {
2727
- return Object.freeze({ ...context.state, presentation: void 0 });
2728
- }
2729
- return Object.freeze({ ...context.state, presentation: projectExecutionPresentation(event) });
2730
- },
2731
- buildViewNode(context) {
2732
- if (context.state?.presentation === void 0) return null;
2733
- return Object.freeze({
2734
- key: context.key,
2735
- kind: "wsr-execution-presentation",
2736
- id: context.id,
2737
- target: "chat",
2738
- anchorSeq: context.state.seq,
2739
- location: context.start?.location ?? { kind: "unresolved" },
2740
- visibility: "visible",
2741
- data: context.state.presentation
2742
- });
2743
- }
2744
- });
2745
- }
2746
2712
 
2747
2713
  // packages/execution/src/action-presentation/view.js
2748
2714
  var DOT_STATE = Object.freeze({
@@ -2753,13 +2719,39 @@ var DOT_STATE = Object.freeze({
2753
2719
  failed: "error",
2754
2720
  cancelled: "error"
2755
2721
  });
2756
- function createActionPresentationView({ React: React2, DisclosureRow: DisclosureRow2, MessageText: MessageText2, StateDot: StateDot2, observe = () => void 0 }) {
2722
+ var ACTIONS_STYLE_ID = "dsh-wsr-execution-final-actions";
2723
+ var 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)}";
2724
+ function installActionPresentationStyle() {
2725
+ if (typeof document === "undefined" || document.getElementById(ACTIONS_STYLE_ID) !== null) return;
2726
+ const tag = document.createElement("style");
2727
+ tag.id = ACTIONS_STYLE_ID;
2728
+ tag.dataset.plugin = "dsh-wsr-execution";
2729
+ tag.textContent = ACTIONS_CSS;
2730
+ document.head.append(tag);
2731
+ }
2732
+ function createActionPresentationView({
2733
+ React: React2,
2734
+ DisclosureRow: DisclosureRow2,
2735
+ MessageText: MessageText2,
2736
+ StateDot: StateDot2,
2737
+ JsonTree: JsonTree2,
2738
+ Tooltip: Tooltip2,
2739
+ IconCopyOutline16: IconCopyOutline162,
2740
+ IconCheckOutline16: IconCheckOutline162,
2741
+ writeClipboard: writeClipboard2,
2742
+ observe = () => void 0
2743
+ }) {
2757
2744
  if (typeof DisclosureRow2 !== "function") throw new TypeError("DSH_DISCLOSURE_ROW_REQUIRED");
2758
- return function WsrExecutionPresentationView({ node }) {
2745
+ installActionPresentationStyle();
2746
+ return function WsrExecutionPresentationView({ node, technicalDetails }) {
2759
2747
  const presentation = node.data;
2760
2748
  const [open, setOpen] = React2.useState(presentation.defaultOpen);
2749
+ const [copyState, setCopyState] = React2.useState("idle");
2761
2750
  const bodyRef = React2.useRef(null);
2762
2751
  const previousState = React2.useRef(presentation.state);
2752
+ const copyPending = React2.useRef(false);
2753
+ const copyEpoch = React2.useRef(0);
2754
+ const copyTimer = React2.useRef(null);
2763
2755
  React2.useEffect(() => {
2764
2756
  setOpen((current) => resolveDisclosureOpen({
2765
2757
  current,
@@ -2769,21 +2761,66 @@ function createActionPresentationView({ React: React2, DisclosureRow: Disclosure
2769
2761
  }));
2770
2762
  previousState.current = presentation.state;
2771
2763
  }, [presentation.state]);
2764
+ React2.useEffect(() => {
2765
+ copyEpoch.current += 1;
2766
+ copyPending.current = false;
2767
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
2768
+ copyTimer.current = null;
2769
+ setCopyState("idle");
2770
+ return () => {
2771
+ copyEpoch.current += 1;
2772
+ copyPending.current = false;
2773
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
2774
+ };
2775
+ }, [presentation.body, presentation.correlation]);
2772
2776
  observe(presentation);
2773
- if (presentation.layer === "final") {
2774
- return React2.createElement("article", {
2775
- "data-wsr-presentation": "true",
2776
- "data-wsr-layer": "final",
2777
- "data-wsr-state": presentation.state,
2778
- "data-wsr-correlation": presentation.correlation,
2779
- "data-wsr-chat-role": "assistant",
2780
- "data-wsr-compatibility": presentation.compatibility,
2781
- "aria-label": presentation.title
2782
- }, React2.createElement(MessageText2, { text: presentation.body }));
2777
+ if (presentation.layer === "final" && presentation.state === "completed") {
2778
+ const label = copyState === "copied" ? "Copied" : copyState === "failed" ? "Copy failed" : "Copy";
2779
+ const onCopy = async () => {
2780
+ if (copyState === "copied" || copyPending.current) return;
2781
+ const epoch = copyEpoch.current;
2782
+ copyPending.current = true;
2783
+ let accepted = false;
2784
+ try {
2785
+ accepted = await writeClipboard2(presentation.body);
2786
+ } catch {
2787
+ accepted = false;
2788
+ }
2789
+ if (epoch !== copyEpoch.current) return;
2790
+ copyPending.current = false;
2791
+ setCopyState(accepted ? "copied" : "failed");
2792
+ copyTimer.current = globalThis.setTimeout(() => {
2793
+ copyTimer.current = null;
2794
+ setCopyState("idle");
2795
+ }, 1e3);
2796
+ };
2797
+ return React2.createElement(
2798
+ "article",
2799
+ {
2800
+ "data-wsr-presentation": "true",
2801
+ "data-wsr-layer": "final",
2802
+ "data-wsr-state": presentation.state,
2803
+ "data-wsr-correlation": presentation.correlation,
2804
+ "data-wsr-chat-role": "assistant",
2805
+ "data-wsr-compatibility": presentation.compatibility,
2806
+ "aria-label": presentation.title
2807
+ },
2808
+ React2.createElement(MessageText2, { text: presentation.body }),
2809
+ React2.createElement("div", {
2810
+ className: "wsr-answer-actions",
2811
+ "data-wsr-answer-actions": "true"
2812
+ }, React2.createElement(Tooltip2, { label, side: "bottom" }, React2.createElement("button", {
2813
+ type: "button",
2814
+ className: "wsr-answer-action",
2815
+ "aria-label": label,
2816
+ "data-copy-state": copyState,
2817
+ onClick: onCopy
2818
+ }, React2.createElement(copyState === "copied" ? IconCheckOutline162 : IconCopyOutline162, null))))
2819
+ );
2783
2820
  }
2784
2821
  const waiting = presentation.state === "waiting";
2785
- const expandable = presentation.body !== void 0 && !waiting;
2786
- const body = presentation.body === void 0 ? void 0 : React2.createElement("div", {
2822
+ const expandable = (presentation.body !== void 0 || technicalDetails !== void 0) && !waiting;
2823
+ const body = presentation.body === void 0 && technicalDetails === void 0 ? void 0 : React2.createElement("div", {
2787
2824
  ref: bodyRef,
2788
2825
  "data-wsr-presentation": "true",
2789
2826
  "data-wsr-layer": presentation.layer,
@@ -2794,9 +2831,14 @@ function createActionPresentationView({ React: React2, DisclosureRow: Disclosure
2794
2831
  tabIndex: waiting ? 0 : void 0,
2795
2832
  "aria-label": waiting ? presentation.summary : void 0,
2796
2833
  "aria-live": waiting ? "polite" : void 0
2797
- }, React2.createElement("pre", {
2834
+ }, presentation.body === void 0 ? null : React2.createElement("pre", {
2798
2835
  style: { margin: 0, maxHeight: "20rem", overflow: "auto", whiteSpace: "pre-wrap", wordBreak: "break-word" }
2799
- }, presentation.body));
2836
+ }, presentation.body), technicalDetails === void 0 ? null : React2.createElement(
2837
+ "details",
2838
+ null,
2839
+ React2.createElement("summary", null, "Technical details"),
2840
+ JsonTree2 === void 0 ? React2.createElement("pre", null, JSON.stringify(technicalDetails, null, 2)) : React2.createElement(JsonTree2, { data: technicalDetails, label: "WSR presentation", copyable: true, expandTopLevel: true })
2841
+ ));
2800
2842
  return React2.createElement(DisclosureRow2, {
2801
2843
  icon: React2.createElement(StateDot2, { state: DOT_STATE[presentation.state], size: 10 }),
2802
2844
  title: presentation.title,
@@ -2816,16 +2858,79 @@ function createActionPresentationView({ React: React2, DisclosureRow: Disclosure
2816
2858
  }, body);
2817
2859
  };
2818
2860
  }
2861
+ var TERMINAL_PRESENTATION = Object.freeze({
2862
+ SUCCEEDED: Object.freeze({ state: "completed", label: "Succeeded" }),
2863
+ FAILED: Object.freeze({ state: "failed", label: "Failed" }),
2864
+ CANCELLED: Object.freeze({ state: "cancelled", label: "Cancelled" })
2865
+ });
2866
+ function reconcileDeliveryPresentation(presentation, admitted, inventoryState) {
2867
+ const deliveryId = admitted?.kind === "delivery-running" && typeof admitted.data.deliveryId === "string" ? admitted.data.deliveryId : void 0;
2868
+ const deliveries = ["ready", "reconnecting"].includes(inventoryState?.kind) && Array.isArray(inventoryState.snapshot?.deliveries) ? inventoryState.snapshot.deliveries : [];
2869
+ const matches = deliveryId === void 0 ? [] : deliveries.filter((delivery) => delivery?.deliveryId === deliveryId);
2870
+ const terminal = matches.length === 1 && matches[0]?.lifecycle === "TERMINAL" ? TERMINAL_PRESENTATION[matches[0]?.terminal?.outcome] : void 0;
2871
+ if (terminal === void 0) return presentation;
2872
+ return Object.freeze({
2873
+ ...presentation,
2874
+ state: terminal.state,
2875
+ summary: `${terminal.label} \xB7 ${deliveryId}`,
2876
+ defaultOpen: false
2877
+ });
2878
+ }
2879
+ function commandPresentation(node, admitted, inventoryState) {
2880
+ if (node.outcome === null) return Object.freeze({
2881
+ correlation: String(node.commandId),
2882
+ layer: "progress",
2883
+ state: "running",
2884
+ title: "Workflow delivery",
2885
+ summary: "Running",
2886
+ body: void 0,
2887
+ defaultOpen: false,
2888
+ focusPolicy: "none",
2889
+ role: "status",
2890
+ compatibility: "current"
2891
+ });
2892
+ const event = admitted ?? parseExecutionPresentation(node.outcome?.text);
2893
+ if (event.kind === "delivery-list") {
2894
+ const count = Array.isArray(event.data.items) ? event.data.items.length : 0;
2895
+ return Object.freeze({
2896
+ correlation: event.correlation,
2897
+ layer: "progress",
2898
+ state: "completed",
2899
+ title: "Delivery list",
2900
+ summary: `${count} ${count === 1 ? "delivery" : "deliveries"}`,
2901
+ body: count === 0 ? "No deliveries." : JSON.stringify(event.data.items, null, 2),
2902
+ defaultOpen: false,
2903
+ focusPolicy: "none",
2904
+ role: "status",
2905
+ compatibility: "current"
2906
+ });
2907
+ }
2908
+ return reconcileDeliveryPresentation(projectExecutionPresentation(event), event, inventoryState);
2909
+ }
2910
+ function createWsrCommandView(options) {
2911
+ const View = createActionPresentationView(options);
2912
+ const { React: React2, inventory } = options;
2913
+ return function WsrCommandView({ node }) {
2914
+ const admitted = node.outcome === null ? void 0 : parseExecutionPresentation(node.outcome?.text);
2915
+ const inventoryState = inventory === void 0 ? void 0 : React2.useSyncExternalStore(inventory.subscribe, inventory.getSnapshot, inventory.getSnapshot);
2916
+ return View({ node: { data: commandPresentation(node, admitted, inventoryState) }, technicalDetails: admitted });
2917
+ };
2918
+ }
2819
2919
  function registerActionPresentation(ctx, View) {
2820
- ctx.conversationEvents.register(createExecutionPresentationDefinition());
2821
- ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
2822
- name: "conversation.chat.node",
2823
- key: "wsr-execution-presentation"
2824
- }, View));
2920
+ ctx.slots.inject("conversation.chat.commandview", () => {
2921
+ ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr" }, () => null);
2922
+ ctx.slots.register({ name: "conversation.chat.commandview", key: "wsr-presentation" }, View);
2923
+ });
2825
2924
  }
2826
2925
 
2827
2926
  // packages/execution/src/client/delivery/control-plane-port.js
2828
2927
  var CHANNEL = "/wsr-execution";
2928
+ var ERROR_CODES = /* @__PURE__ */ new Set([
2929
+ "DELIVERY_PROJECTION_CORRUPT",
2930
+ "DELIVERY_PROJECTION_STALE_BINDING",
2931
+ "DELIVERY_PROJECTION_RECOVERY_MISMATCH",
2932
+ "DELIVERY_PROJECTION_UNAVAILABLE"
2933
+ ]);
2829
2934
  function createStore(initial) {
2830
2935
  let snapshot = initial;
2831
2936
  const listeners = /* @__PURE__ */ new Set();
@@ -2850,7 +2955,9 @@ function createDeliveryControlPlaneClient(rpc) {
2850
2955
  const sessions = /* @__PURE__ */ new Map();
2851
2956
  const read = async (endpoint, payload) => {
2852
2957
  const result = await rpc.call(CHANNEL, endpoint, payload);
2853
- if (result?.ok !== true) throw new Error(message(result?.error));
2958
+ if (result?.ok !== true) throw Object.assign(new Error(message(result?.error)), {
2959
+ code: ERROR_CODES.has(result?.error?.code) ? result.error.code : "DELIVERY_PROJECTION_UNAVAILABLE"
2960
+ });
2854
2961
  return result.value;
2855
2962
  };
2856
2963
  const client = {
@@ -2864,6 +2971,7 @@ function createDeliveryControlPlaneClient(rpc) {
2864
2971
  const previous = inventory.getSnapshot();
2865
2972
  inventory.publish({
2866
2973
  kind: previous.kind === "ready" ? "reconnecting" : "error",
2974
+ code: typeof error?.code === "string" ? error.code : "DELIVERY_PROJECTION_UNAVAILABLE",
2867
2975
  message: message(error),
2868
2976
  ...previous.kind === "ready" ? { snapshot: previous.snapshot } : {}
2869
2977
  });
@@ -2882,7 +2990,7 @@ function createDeliveryControlPlaneClient(rpc) {
2882
2990
  try {
2883
2991
  store.publish({ kind: "ready", view: await read("session/read", { sessionCorrelation }) });
2884
2992
  } catch (error) {
2885
- store.publish({ kind: "error", code: "DELIVERY_PROJECTION_UNAVAILABLE", message: message(error) });
2993
+ store.publish({ kind: "error", code: typeof error?.code === "string" ? error.code : "DELIVERY_PROJECTION_UNAVAILABLE", message: message(error) });
2886
2994
  }
2887
2995
  }
2888
2996
  });
@@ -2906,6 +3014,38 @@ var LIFECYCLES = /* @__PURE__ */ new Set([
2906
3014
  "TERMINAL_HANDLING",
2907
3015
  "TERMINAL"
2908
3016
  ]);
3017
+ var DELIVERY_STYLE_ID = "dsh-wsr-execution-delivery-view";
3018
+ var DELIVERY_CSS = `
3019
+ .wsr-delivery-view { box-sizing: border-box; width: 100%; max-width: 960px; margin: 0 auto; padding: 20px; color: var(--dsw-alias-label-primary); }
3020
+ .wsr-delivery-heading { margin: 0 0 16px; font-size: 20px; line-height: 28px; }
3021
+ .wsr-delivery-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: 8px; margin: 0 0 16px; }
3022
+ .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); }
3023
+ .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; }
3024
+ .wsr-delivery-summary-item dd, .wsr-delivery-identity dd { min-width: 0; margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
3025
+ .wsr-delivery-status { display: inline-flex; min-width: 0; align-items: center; gap: 6px; }
3026
+ .wsr-delivery-status > span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3027
+ .wsr-delivery-identities { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 8px 16px; margin: 8px 0 0; }
3028
+ .wsr-delivery-identity { min-width: 0; margin: 0; }
3029
+ .wsr-delivery-identity dd { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px; }
3030
+ .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; }
3031
+ .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; }
3032
+ .wsr-delivery-copy-feedback { min-height: 20px; margin: 8px 0 0; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 20px; }
3033
+ .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); }
3034
+ .wsr-delivery-condition h3 { margin: 0 0 4px; font-size: 13px; line-height: 20px; }
3035
+ .wsr-delivery-condition code, .wsr-delivery-state code { overflow-wrap: anywhere; }
3036
+ .wsr-delivery-state { display: grid; gap: 8px; }
3037
+ .wsr-delivery-state p { margin: 0; }
3038
+ @media (max-width: 720px) { .wsr-delivery-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
3039
+ @media (max-width: 420px) { .wsr-delivery-view { padding: 12px; } .wsr-delivery-summary, .wsr-delivery-identities { grid-template-columns: minmax(0, 1fr); } }
3040
+ @media (prefers-reduced-motion: reduce) { .wsr-delivery-view, .wsr-delivery-view * { scroll-behavior: auto !important; transition: none !important; } }
3041
+ `;
3042
+ function ensureDeliveryStyles() {
3043
+ if (typeof document === "undefined" || document.getElementById(DELIVERY_STYLE_ID) !== null) return;
3044
+ const tag = document.createElement("style");
3045
+ tag.id = DELIVERY_STYLE_ID;
3046
+ tag.textContent = DELIVERY_CSS;
3047
+ document.head.appendChild(tag);
3048
+ }
2909
3049
  function nonEmpty(value) {
2910
3050
  return typeof value === "string" && value.length > 0;
2911
3051
  }
@@ -2932,88 +3072,187 @@ function safeSubscribe(source, notify) {
2932
3072
  return () => void 0;
2933
3073
  }
2934
3074
  }
2935
- function pair(React2, label, value) {
2936
- return [
2937
- React2.createElement("dt", { key: `${label}-label` }, label),
2938
- React2.createElement("dd", { key: `${label}-value` }, value)
2939
- ];
3075
+ function summaryItem(React2, label, value, extra = {}) {
3076
+ return React2.createElement(
3077
+ "div",
3078
+ { className: "wsr-delivery-summary-item", ...extra },
3079
+ React2.createElement("dt", null, label),
3080
+ React2.createElement("dd", null, value)
3081
+ );
2940
3082
  }
2941
- function statePanel(React2, role, code, message2) {
3083
+ function statePanel(React2, StateDot2, role, code, message2) {
2942
3084
  return React2.createElement(
2943
3085
  "section",
2944
3086
  {
3087
+ className: "wsr-delivery-view wsr-delivery-state",
2945
3088
  "aria-labelledby": "wsr-delivery-view-title",
2946
3089
  "aria-live": role === "alert" ? "assertive" : "polite",
2947
3090
  "data-wsr-delivery-view": "true",
2948
3091
  role
2949
3092
  },
2950
- React2.createElement("h2", { id: "wsr-delivery-view-title" }, "Delivery"),
2951
- React2.createElement("p", null, message2),
3093
+ React2.createElement("h2", { className: "wsr-delivery-heading", id: "wsr-delivery-view-title" }, "Delivery"),
3094
+ React2.createElement(
3095
+ "p",
3096
+ null,
3097
+ React2.createElement(StateDot2, { state: role === "alert" ? "error" : "ongoing", size: 10 }),
3098
+ " ",
3099
+ message2
3100
+ ),
2952
3101
  code === void 0 ? null : React2.createElement("code", null, code)
2953
3102
  );
2954
3103
  }
2955
- function createSessionDeliveryView(React2) {
3104
+ function statusState(delivery, failed) {
3105
+ if (failed) return "error";
3106
+ if (delivery.terminal?.outcome === "SUCCEEDED") return "done";
3107
+ if (delivery.terminal !== null || ["START_UNCERTAIN", "RESULT_UNRESOLVED", "START_FAILED"].includes(delivery.lifecycle)) return "warning";
3108
+ return "ongoing";
3109
+ }
3110
+ function identityCard(React2, primitives, label, value, displayValue = value) {
3111
+ const { Button: Button2, IconCheckOutline16: IconCheckOutline162, IconCopyOutline16: IconCopyOutline162, Tooltip: Tooltip2, onCopy, copiedLabel } = primitives;
3112
+ const exact = React2.createElement("code", {
3113
+ "aria-label": `${label}: ${value}`,
3114
+ "data-wsr-delivery-identity": label,
3115
+ title: value
3116
+ }, displayValue);
3117
+ const copied = copiedLabel === label;
3118
+ const control = React2.createElement(
3119
+ Tooltip2,
3120
+ { label: copied ? `${label} copied` : `Copy ${label}`, side: "bottom" },
3121
+ React2.createElement(Button2, {
3122
+ "aria-label": `Copy ${label}`,
3123
+ icon: React2.createElement(copied ? IconCheckOutline162 : IconCopyOutline162, null),
3124
+ onClick: () => onCopy(label, value),
3125
+ size: "sm",
3126
+ type: "button",
3127
+ variant: "toolbar"
3128
+ }, copied ? "Copied" : "Copy")
3129
+ );
3130
+ return React2.createElement(
3131
+ "div",
3132
+ { className: "wsr-delivery-identity", key: label },
3133
+ React2.createElement("dt", null, label),
3134
+ React2.createElement("dd", null, exact, control)
3135
+ );
3136
+ }
3137
+ function createSessionDeliveryView(React2, primitives = {}) {
2956
3138
  if (typeof React2?.createElement !== "function" || typeof React2?.useSyncExternalStore !== "function") {
2957
3139
  throw new TypeError("DELIVERY_VIEW_REACT_INVALID");
2958
3140
  }
3141
+ const DisclosureRow2 = primitives.DisclosureRow ?? "div";
3142
+ const Button2 = primitives.Button ?? "button";
3143
+ const IconCheckOutline162 = primitives.IconCheckOutline16 ?? "span";
3144
+ const IconCopyOutline162 = primitives.IconCopyOutline16 ?? "span";
3145
+ const Pill2 = primitives.Pill ?? "span";
3146
+ const StateDot2 = primitives.StateDot ?? "span";
3147
+ const Tooltip2 = primitives.Tooltip ?? "span";
3148
+ const writeClipboard2 = primitives.writeClipboard ?? (async () => false);
3149
+ ensureDeliveryStyles();
2959
3150
  return function SessionDeliveryView({ sessionId, source }) {
3151
+ const [identitiesOpen, setIdentitiesOpen] = typeof React2.useState === "function" ? React2.useState(false) : [false, () => void 0];
3152
+ const [copiedLabel, setCopiedLabel] = typeof React2.useState === "function" ? React2.useState("") : ["", () => void 0];
2960
3153
  const state = React2.useSyncExternalStore(
2961
3154
  (notify) => safeSubscribe(source, notify),
2962
3155
  () => safeSnapshot(source),
2963
3156
  () => safeSnapshot(source)
2964
3157
  );
2965
- if (state.kind === "loading") return statePanel(React2, "status", void 0, "Loading Delivery\u2026");
2966
- if (state.kind === "error") return statePanel(React2, "alert", state.code ?? "DELIVERY_PROJECTION_UNAVAILABLE", state.message ?? "Execution projection unavailable");
3158
+ if (state.kind === "loading") return statePanel(React2, StateDot2, "status", void 0, "Loading Delivery\u2026");
3159
+ if (state.kind === "error") return statePanel(React2, StateDot2, "alert", state.code ?? "DELIVERY_PROJECTION_UNAVAILABLE", state.message ?? "Execution projection unavailable");
2967
3160
  const view = state.view;
2968
3161
  if (state.kind !== "ready" || view?.sessionCorrelation !== sessionId) {
2969
- return statePanel(React2, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
3162
+ return statePanel(React2, StateDot2, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
2970
3163
  }
2971
- if (view.kind === "UNBOUND") return statePanel(React2, "status", void 0, "No Delivery bound to this Session");
3164
+ if (view.kind === "UNBOUND") return statePanel(React2, StateDot2, "status", void 0, "No Delivery bound to this Session");
2972
3165
  if (view.kind !== "BOUND" || !validDelivery(view.delivery, sessionId)) {
2973
- return statePanel(React2, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
3166
+ return statePanel(React2, StateDot2, "alert", "DELIVERY_PROJECTION_CORRUPT", "Delivery projection invalid");
2974
3167
  }
2975
3168
  const delivery = view.delivery;
2976
3169
  const failed = delivery.terminal?.outcome === "FAILED" || delivery.error !== null;
2977
3170
  const identityRows = [
2978
- ...pair(React2, "Delivery", delivery.deliveryId),
2979
- ...pair(React2, "Task", delivery.task.displayName ?? delivery.task.identity),
2980
- ...pair(React2, "Workflow", delivery.workflow.identity),
2981
- ...pair(React2, "Package", `${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`),
2982
- ...pair(React2, "Package digest", delivery.workflow.packageDigest),
2983
- ...pair(React2, "Snapshot", delivery.workflow.snapshotIdentity),
2984
- ...pair(React2, "Snapshot digest", delivery.workflow.snapshotDigest),
2985
- ...pair(React2, "Binding", delivery.deliveryBindingIdentity)
3171
+ ["Delivery", delivery.deliveryId],
3172
+ ["Task", delivery.task.identity, delivery.task.displayName === null ? delivery.task.identity : `${delivery.task.displayName} \xB7 ${delivery.task.identity}`],
3173
+ ["Workflow", delivery.workflow.identity],
3174
+ ["Package", `${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`],
3175
+ ["Package digest", delivery.workflow.packageDigest],
3176
+ ["Snapshot", delivery.workflow.snapshotIdentity],
3177
+ ["Snapshot digest", delivery.workflow.snapshotDigest],
3178
+ ["Binding", delivery.deliveryBindingIdentity],
3179
+ ...nonEmpty(delivery.worktree) ? [["Worktree", delivery.worktree]] : []
2986
3180
  ];
2987
- const lifecycleRows = [
2988
- ...pair(React2, "Lifecycle", delivery.lifecycle),
2989
- ...pair(React2, "Recoverable", delivery.recoverable ? "yes" : "no"),
2990
- ...delivery.current === null ? [] : pair(React2, delivery.current.kind === "ACTION" ? "Current Action" : "Current Intervention", delivery.current.identity),
2991
- ...pair(React2, "Started", new Date(delivery.timing.startedAt).toISOString()),
2992
- ...pair(React2, "Elapsed", duration(delivery.timing.elapsedMs)),
2993
- ...delivery.terminal === null ? [] : [
2994
- ...pair(React2, "Ended", new Date(delivery.terminal.finishedAt).toISOString()),
2995
- ...pair(React2, "Outcome", delivery.terminal.outcome)
2996
- ],
2997
- ...delivery.error === null ? [] : pair(React2, "Error", delivery.error.code)
3181
+ const statusLabel = delivery.terminal?.outcome ?? delivery.lifecycle;
3182
+ const workflowLabel = `${delivery.workflow.identity} \xB7 ${delivery.workflow.packageName}@${delivery.workflow.exactPackageVersion}`;
3183
+ const summary = [
3184
+ summaryItem(React2, "Status", React2.createElement(
3185
+ "span",
3186
+ { className: "wsr-delivery-status" },
3187
+ React2.createElement(StateDot2, { state: statusState(delivery, failed), size: 10 }),
3188
+ React2.createElement(Pill2, { "aria-label": `Delivery status ${statusLabel}` }, statusLabel)
3189
+ )),
3190
+ summaryItem(React2, "Workflow", workflowLabel),
3191
+ ...delivery.current === null ? [] : [summaryItem(
3192
+ React2,
3193
+ delivery.current.kind === "ACTION" ? "Current Action" : "Current Intervention",
3194
+ delivery.current.identity,
3195
+ { "data-wsr-delivery-conditional": "current" }
3196
+ )],
3197
+ ...delivery.terminal === null ? [] : [summaryItem(React2, "Outcome", delivery.terminal.outcome, { "data-wsr-delivery-conditional": "terminal" })],
3198
+ summaryItem(React2, "Elapsed", duration(delivery.timing.elapsedMs)),
3199
+ summaryItem(React2, "Started", new Date(delivery.timing.startedAt).toISOString()),
3200
+ ...delivery.terminal === null ? [] : [summaryItem(React2, "Ended", new Date(delivery.terminal.finishedAt).toISOString())]
2998
3201
  ];
2999
3202
  return React2.createElement(
3000
3203
  "section",
3001
3204
  {
3205
+ className: "wsr-delivery-view",
3002
3206
  "aria-labelledby": "wsr-delivery-view-title",
3003
3207
  "aria-live": failed ? "assertive" : "polite",
3004
3208
  "data-wsr-delivery-id": delivery.deliveryId,
3005
3209
  "data-wsr-delivery-view": "true",
3006
3210
  role: failed ? "alert" : "region"
3007
3211
  },
3008
- React2.createElement("h2", { id: "wsr-delivery-view-title" }, "Delivery"),
3009
- React2.createElement("dl", { "aria-label": "Delivery identity" }, identityRows),
3010
- React2.createElement("dl", { "aria-label": "Delivery lifecycle" }, lifecycleRows)
3212
+ React2.createElement("h2", { className: "wsr-delivery-heading", id: "wsr-delivery-view-title" }, "Delivery"),
3213
+ React2.createElement("dl", { "aria-label": "Delivery summary", "data-wsr-delivery-summary": "true", className: "wsr-delivery-summary" }, summary),
3214
+ React2.createElement(
3215
+ DisclosureRow2,
3216
+ {
3217
+ title: "Identity details",
3218
+ icon: React2.createElement(StateDot2, { state: statusState(delivery, failed), size: 10 }),
3219
+ open: identitiesOpen,
3220
+ expandable: true,
3221
+ expandOnRowClick: true,
3222
+ onToggle: () => setIdentitiesOpen((open) => !open),
3223
+ collapsedContent: React2.createElement("code", { className: "wsr-delivery-preview" }, delivery.deliveryId)
3224
+ },
3225
+ React2.createElement(
3226
+ "dl",
3227
+ { "aria-label": "Delivery identity", className: "wsr-delivery-identities" },
3228
+ identityRows.map(([label, value, displayValue]) => identityCard(React2, {
3229
+ Button: Button2,
3230
+ IconCheckOutline16: IconCheckOutline162,
3231
+ IconCopyOutline16: IconCopyOutline162,
3232
+ Tooltip: Tooltip2,
3233
+ copiedLabel,
3234
+ async onCopy(copyLabel, value2) {
3235
+ setCopiedLabel(await writeClipboard2(value2) ? copyLabel : `${copyLabel} copy failed`);
3236
+ }
3237
+ }, label, value, displayValue))
3238
+ ),
3239
+ React2.createElement("p", {
3240
+ "aria-live": "polite",
3241
+ className: "wsr-delivery-copy-feedback",
3242
+ role: "status"
3243
+ }, copiedLabel === "" ? "" : copiedLabel.endsWith("copy failed") ? copiedLabel : `${copiedLabel} copied`)
3244
+ ),
3245
+ delivery.error === null ? null : React2.createElement("section", {
3246
+ className: "wsr-delivery-condition",
3247
+ "data-wsr-delivery-conditional": "error",
3248
+ role: "alert"
3249
+ }, React2.createElement("h3", null, "Failure diagnostic"), React2.createElement("code", null, delivery.error.code))
3011
3250
  );
3012
3251
  };
3013
3252
  }
3014
3253
  function registerSessionDeliveryView(ctx, options) {
3015
3254
  if (typeof ctx?.slots?.inject !== "function" || typeof ctx?.slots?.register !== "function" || typeof options?.bindProjection !== "function") throw new TypeError("DELIVERY_VIEW_REGISTRATION_INVALID");
3016
- const View = createSessionDeliveryView(options.React);
3255
+ const View = createSessionDeliveryView(options.React, options);
3017
3256
  ctx.slots.inject("conversation.view", () => ctx.slots.register({
3018
3257
  name: "conversation.view",
3019
3258
  id: DELIVERY_VIEW_ID,
@@ -3034,9 +3273,18 @@ var LIFECYCLES2 = /* @__PURE__ */ new Set([
3034
3273
  "TERMINAL_HANDLING",
3035
3274
  "TERMINAL"
3036
3275
  ]);
3276
+ var ERROR_CODES2 = /* @__PURE__ */ new Set([
3277
+ "DELIVERY_PROJECTION_CORRUPT",
3278
+ "DELIVERY_PROJECTION_STALE_BINDING",
3279
+ "DELIVERY_PROJECTION_RECOVERY_MISMATCH",
3280
+ "DELIVERY_PROJECTION_UNAVAILABLE"
3281
+ ]);
3037
3282
  function errorView(label = "Delivery inventory unavailable") {
3038
3283
  return Object.freeze({ kind: "error", role: "alert", label, rows: Object.freeze([]) });
3039
3284
  }
3285
+ function diagnostic(state, label) {
3286
+ return typeof state?.code === "string" && ERROR_CODES2.has(state.code) ? `${state.code}: ${label}` : label;
3287
+ }
3040
3288
  function validString(value) {
3041
3289
  return typeof value === "string" && value.length > 0 && value.length <= 512;
3042
3290
  }
@@ -3066,13 +3314,21 @@ function rowsFrom(deliveries, selectedSessionId) {
3066
3314
  }
3067
3315
  function projectDeliveryInventory(state, { selectedSessionId } = {}) {
3068
3316
  if (state?.kind === "loading") return Object.freeze({ kind: "loading", role: "status", label: "Loading Deliveries", rows: Object.freeze([]) });
3069
- if (state?.kind === "error") return errorView(validString(state.message) ? state.message : void 0);
3317
+ if (state?.kind === "error") {
3318
+ const label = validString(state.message) ? state.message : "Delivery inventory unavailable";
3319
+ return errorView(diagnostic(state, label));
3320
+ }
3070
3321
  if (!(/* @__PURE__ */ new Set(["ready", "reconnecting"])).has(state?.kind)) return errorView();
3071
3322
  const snapshot = state.snapshot;
3072
3323
  if (snapshot === null || typeof snapshot !== "object" || Array.isArray(snapshot) || snapshot.schemaVersion !== CONTROL_PLANE_SCHEMA || !Number.isSafeInteger(snapshot.generation) || snapshot.generation < 1) return errorView();
3073
3324
  const rows = rowsFrom(snapshot.deliveries, selectedSessionId);
3074
3325
  if (rows === void 0) return errorView();
3075
- if (state.kind === "reconnecting") return Object.freeze({ kind: "reconnecting", role: "status", label: "Reconnecting to Delivery inventory", rows });
3326
+ if (state.kind === "reconnecting") return Object.freeze({
3327
+ kind: "reconnecting",
3328
+ role: "status",
3329
+ label: diagnostic(state, "Reconnecting to Delivery inventory"),
3330
+ rows
3331
+ });
3076
3332
  if (rows.length === 0) return Object.freeze({ kind: "empty", role: "status", label: "No Deliveries", rows });
3077
3333
  return Object.freeze({ kind: "ready", role: "list", label: "Deliveries", rows });
3078
3334
  }
@@ -3180,7 +3436,6 @@ function applyDeliverySidebar(ctx, { React: React2, workspaceUi: workspaceUi2, i
3180
3436
  var name = "wsr-execution-client";
3181
3437
  var inject = Object.freeze([
3182
3438
  "connection",
3183
- "conversationEvents",
3184
3439
  "sessions",
3185
3440
  "slots",
3186
3441
  "workspaces",
@@ -3197,13 +3452,32 @@ function apply(ctx) {
3197
3452
  applyDeliverySidebar(ctx, { React: import_react.default, workspaceUi, inventory: controlPlane.inventory });
3198
3453
  registerSessionDeliveryView(ctx, {
3199
3454
  React: import_react.default,
3455
+ Button: import_dsh_client_ui_primitives.Button,
3456
+ DisclosureRow: import_dsh_client_ui_primitives.DisclosureRow,
3457
+ IconCheckOutline16: import_dsh_client_ui_primitives.IconCheckOutline16,
3458
+ IconCopyOutline16: import_dsh_client_ui_primitives.IconCopyOutline16,
3459
+ Pill: import_dsh_client_ui_primitives.Pill,
3460
+ StateDot: import_dsh_client_ui_primitives.StateDot,
3461
+ Tooltip: import_dsh_client_ui_primitives.Tooltip,
3462
+ writeClipboard: import_dsh_client_ui_primitives.writeClipboard,
3200
3463
  bindProjection(sessionId) {
3201
3464
  const source = controlPlane.bindSession(String(sessionId));
3202
3465
  void source.refresh();
3203
3466
  return source;
3204
3467
  }
3205
3468
  });
3206
- registerActionPresentation(ctx, createActionPresentationView({ React: import_react.default, DisclosureRow: import_dsh_client_ui_primitives.DisclosureRow, MessageText: import_dsh_client_ui_primitives.MessageText, StateDot: import_dsh_client_ui_primitives.StateDot }));
3469
+ registerActionPresentation(ctx, createWsrCommandView({
3470
+ React: import_react.default,
3471
+ DisclosureRow: import_dsh_client_ui_primitives.DisclosureRow,
3472
+ IconCheckOutline16: import_dsh_client_ui_primitives.IconCheckOutline16,
3473
+ IconCopyOutline16: import_dsh_client_ui_primitives.IconCopyOutline16,
3474
+ JsonTree: import_dsh_client_ui_primitives.JsonTree,
3475
+ MessageText: import_dsh_client_ui_primitives.MessageText,
3476
+ StateDot: import_dsh_client_ui_primitives.StateDot,
3477
+ Tooltip: import_dsh_client_ui_primitives.Tooltip,
3478
+ writeClipboard: import_dsh_client_ui_primitives.writeClipboard,
3479
+ inventory: controlPlane.inventory
3480
+ }));
3207
3481
  }
3208
3482
 
3209
3483
  return module.exports;