@try-works/dsh-recursive-mode 0.1.7 → 0.1.8

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.
@@ -1,3 +1,22 @@
1
+ /**
2
+ * Slot registrations (SP2 R1 + run 08 R1/R3/R4): the sidebar launcher, the
3
+ * shell.overlay board, the conversation.input.dock status strip, and the
4
+ * settings.section page.
5
+ *
6
+ * Run 08: the launcher now OPENS the shared board store (onClick), and the
7
+ * shell.overlay entry renders the board gated on that store + the recursive
8
+ * preset, swapping to the drill-down inspector when a run is selected.
9
+ *
10
+ * Run 10 (LIVE BUG FIX): useSessions is a SnapshotSelectorHook (NOT a zero-arg
11
+ * thunk) — it is called WITH a selector, e.g. useSessions((s) => s) for the
12
+ * full SessionListStateLike. The old code called useSessions() with NO selector
13
+ * and wrapped the garbage in a fake {list:{getSnapshot}} — so the preset gate
14
+ * never saw the recursive preset and the board never mounted (launcher click
15
+ * did nothing). The board/strip now consume the SessionListStateLike directly.
16
+ *
17
+ * READ-ONLY (R9): the client only GETs the live host route; no mutation.
18
+ */
19
+ import { type ReactNode } from 'react';
1
20
  import type { ClientContext, SessionListStateLike, SnapshotSelectorHook } from './contract.ts';
2
21
  import { isRecursivePreset, currentSessionCwd } from './contract.ts';
3
22
  export type OverlayContent = 'hidden' | 'board' | 'inspector';
@@ -12,6 +31,16 @@ export declare function overlayContent(state: {
12
31
  * Exported so the spec can assert the selector is actually passed.
13
32
  */
14
33
  export declare function useRecursiveSessions(useSessions: SnapshotSelectorHook<SessionListStateLike>): SessionListStateLike;
34
+ /**
35
+ * Run 11 (UX gate): the ⧉ launcher renders ONLY in recursive sessions. The seat
36
+ * is root-scoped (the icon shows in every session otherwise), while the board it
37
+ * opens is recursive-preset-gated — in a code/other session the click was a dead
38
+ * no-op. Gating the launcher itself removes the trap: the icon appears exactly
39
+ * where clicking it opens the board.
40
+ */
41
+ export declare function RecursiveLauncherGate({ useSessions }: {
42
+ useSessions: SnapshotSelectorHook<SessionListStateLike>;
43
+ }): ReactNode;
15
44
  export declare function registerSlots(ctx: ClientContext): () => void;
16
45
  /** Export the gate helpers for tests. */
17
46
  export { isRecursivePreset, currentSessionCwd };
package/lib/client.js CHANGED
@@ -528,6 +528,21 @@ window.__ModuleLoader__.load({
528
528
  function useRecursiveSessions(useSessions) {
529
529
  return useSessions((s) => s);
530
530
  }
531
+ /**
532
+ * Run 11 (UX gate): the ⧉ launcher renders ONLY in recursive sessions. The seat
533
+ * is root-scoped (the icon shows in every session otherwise), while the board it
534
+ * opens is recursive-preset-gated — in a code/other session the click was a dead
535
+ * no-op. Gating the launcher itself removes the trap: the icon appears exactly
536
+ * where clicking it opens the board.
537
+ */
538
+ function RecursiveLauncherGate({ useSessions }) {
539
+ if (!isRecursivePreset(useRecursiveSessions(useSessions))) return null;
540
+ return (0, react.createElement)("button", {
541
+ className: "rec-launcher",
542
+ title: "Recursive runs",
543
+ onClick: () => boardState.openBoard()
544
+ }, "⧉");
545
+ }
531
546
  function registerSlots(ctx) {
532
547
  const disposers = [];
533
548
  disposers.push(ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
@@ -535,11 +550,11 @@ window.__ModuleLoader__.load({
535
550
  id: "recursive",
536
551
  order: 50,
537
552
  label: "Recursive runs"
538
- }, () => (0, react.createElement)("button", {
539
- className: "rec-launcher",
540
- title: "Recursive runs",
541
- onClick: () => boardState.openBoard()
542
- }, "⧉"))));
553
+ }, (props) => {
554
+ const useSessions = props?.useSessions;
555
+ if (useSessions === void 0) return null;
556
+ return (0, react.createElement)(RecursiveLauncherGate, { useSessions });
557
+ })));
543
558
  disposers.push(ctx.slots.inject("shell.overlay", () => ctx.slots.register({
544
559
  name: "shell.overlay",
545
560
  id: "recursive-board",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@try-works/dsh-recursive-mode",
3
3
  "description": "recursive-mode workflow as a DeepSeek Harness bundle: RecursiveRuntime service + recursive_status tool",
4
- "version": "0.1.7",
4
+ "version": "0.1.8",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -58,16 +58,36 @@ export function useRecursiveSessions(useSessions: SnapshotSelectorHook<SessionLi
58
58
  return useSessions((s) => s)
59
59
  }
60
60
 
61
+ /**
62
+ * Run 11 (UX gate): the ⧉ launcher renders ONLY in recursive sessions. The seat
63
+ * is root-scoped (the icon shows in every session otherwise), while the board it
64
+ * opens is recursive-preset-gated — in a code/other session the click was a dead
65
+ * no-op. Gating the launcher itself removes the trap: the icon appears exactly
66
+ * where clicking it opens the board.
67
+ */
68
+ export function RecursiveLauncherGate({ useSessions }: { useSessions: SnapshotSelectorHook<SessionListStateLike> }): ReactNode {
69
+ const list = useRecursiveSessions(useSessions)
70
+ if (!isRecursivePreset(list)) return null
71
+ return createElement('button', { className: 'rec-launcher', title: 'Recursive runs', onClick: () => boardState.openBoard() }, '⧉')
72
+ }
73
+
61
74
  export function registerSlots(ctx: ClientContext): () => void {
62
75
  const disposers: (() => void)[] = []
63
76
 
64
77
  // Board launcher in the sidebar footer action list — OPENS the shared board store (run 08 R1).
78
+ // Run 11 (UX gate): the seat is root-scoped (visible in every session), but the board it
79
+ // opens is recursive-preset-gated. In a code/other session the icon was a dead click — a
80
+ // trap. The launcher itself now renders ONLY in recursive sessions, where it works.
65
81
  disposers.push(ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
66
82
  name: 'sidebar.footer.action',
67
83
  id: 'recursive',
68
84
  order: 50,
69
85
  label: 'Recursive runs',
70
- }, () => createElement('button', { className: 'rec-launcher', title: 'Recursive runs', onClick: () => boardState.openBoard() }, '⧉'))))
86
+ }, (props: RootSlotProps) => {
87
+ const useSessions = props?.useSessions
88
+ if (useSessions === undefined) return null
89
+ return createElement(RecursiveLauncherGate, { useSessions })
90
+ })))
71
91
 
72
92
  // Board panel overlay (root scope, cross-session read) — gated on the recursive preset + shared store.
73
93
  disposers.push(ctx.slots.inject('shell.overlay', () => ctx.slots.register({