@inhuman.tools/harness-tui 0.0.0-stage → 0.2.0

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.
Files changed (167) hide show
  1. package/LICENSE.md +7 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +2 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/interpreter/ComputedExecBox.d.ts +12 -0
  7. package/dist/interpreter/ComputedExecBox.d.ts.map +1 -0
  8. package/dist/interpreter/ComputedExecBox.js +88 -0
  9. package/dist/interpreter/ComputedExecBox.js.map +1 -0
  10. package/dist/interpreter/imageCapability.d.ts +9 -0
  11. package/dist/interpreter/imageCapability.d.ts.map +1 -0
  12. package/dist/interpreter/imageCapability.js +49 -0
  13. package/dist/interpreter/imageCapability.js.map +1 -0
  14. package/dist/interpreter/rpcCapability.d.ts +11 -0
  15. package/dist/interpreter/rpcCapability.d.ts.map +1 -0
  16. package/dist/interpreter/rpcCapability.js +91 -0
  17. package/dist/interpreter/rpcCapability.js.map +1 -0
  18. package/dist/interpreter/uiCapability.d.ts +34 -0
  19. package/dist/interpreter/uiCapability.d.ts.map +1 -0
  20. package/dist/interpreter/uiCapability.js +207 -0
  21. package/dist/interpreter/uiCapability.js.map +1 -0
  22. package/dist/schemas/index.d.ts +2 -0
  23. package/dist/schemas/index.d.ts.map +1 -0
  24. package/dist/schemas/index.js +2 -0
  25. package/dist/schemas/index.js.map +1 -0
  26. package/dist/schemas/worker-boot.d.ts +14 -0
  27. package/dist/schemas/worker-boot.d.ts.map +1 -0
  28. package/dist/schemas/worker-boot.js +15 -0
  29. package/dist/schemas/worker-boot.js.map +1 -0
  30. package/dist/tui/ack-prompt.d.ts +53 -0
  31. package/dist/tui/ack-prompt.d.ts.map +1 -0
  32. package/dist/tui/ack-prompt.js +61 -0
  33. package/dist/tui/ack-prompt.js.map +1 -0
  34. package/dist/tui/agents-drawer.d.ts +47 -0
  35. package/dist/tui/agents-drawer.d.ts.map +1 -0
  36. package/dist/tui/agents-drawer.js +314 -0
  37. package/dist/tui/agents-drawer.js.map +1 -0
  38. package/dist/tui/ask-shortcuts.d.ts +59 -0
  39. package/dist/tui/ask-shortcuts.d.ts.map +1 -0
  40. package/dist/tui/ask-shortcuts.js +96 -0
  41. package/dist/tui/ask-shortcuts.js.map +1 -0
  42. package/dist/tui/channel.d.ts +59 -0
  43. package/dist/tui/channel.d.ts.map +1 -0
  44. package/dist/tui/channel.js +27 -0
  45. package/dist/tui/channel.js.map +1 -0
  46. package/dist/tui/host-verbs.d.ts +7 -0
  47. package/dist/tui/host-verbs.d.ts.map +1 -0
  48. package/dist/tui/host-verbs.js +19 -0
  49. package/dist/tui/host-verbs.js.map +1 -0
  50. package/dist/tui/host.d.ts +25 -0
  51. package/dist/tui/host.d.ts.map +1 -0
  52. package/dist/tui/host.js +380 -0
  53. package/dist/tui/host.js.map +1 -0
  54. package/dist/tui/omnibox-seat.d.ts +132 -0
  55. package/dist/tui/omnibox-seat.d.ts.map +1 -0
  56. package/dist/tui/omnibox-seat.js +182 -0
  57. package/dist/tui/omnibox-seat.js.map +1 -0
  58. package/dist/tui/paint-controller.d.ts +170 -0
  59. package/dist/tui/paint-controller.d.ts.map +1 -0
  60. package/dist/tui/paint-controller.js +965 -0
  61. package/dist/tui/paint-controller.js.map +1 -0
  62. package/dist/tui/paint-dom.d.ts +336 -0
  63. package/dist/tui/paint-dom.d.ts.map +1 -0
  64. package/dist/tui/paint-dom.js +709 -0
  65. package/dist/tui/paint-dom.js.map +1 -0
  66. package/dist/tui/paint-source.d.ts +57 -0
  67. package/dist/tui/paint-source.d.ts.map +1 -0
  68. package/dist/tui/paint-source.js +570 -0
  69. package/dist/tui/paint-source.js.map +1 -0
  70. package/dist/tui/paint-transport.d.ts +42 -0
  71. package/dist/tui/paint-transport.d.ts.map +1 -0
  72. package/dist/tui/paint-transport.js +207 -0
  73. package/dist/tui/paint-transport.js.map +1 -0
  74. package/dist/tui/paint-types.d.ts +15 -0
  75. package/dist/tui/paint-types.d.ts.map +1 -0
  76. package/dist/tui/paint-types.js +6 -0
  77. package/dist/tui/paint-types.js.map +1 -0
  78. package/dist/tui/paint-widgets.d.ts +94 -0
  79. package/dist/tui/paint-widgets.d.ts.map +1 -0
  80. package/dist/tui/paint-widgets.js +877 -0
  81. package/dist/tui/paint-widgets.js.map +1 -0
  82. package/dist/tui/parse-args.d.ts +3 -0
  83. package/dist/tui/parse-args.d.ts.map +1 -0
  84. package/dist/tui/parse-args.js +13 -0
  85. package/dist/tui/parse-args.js.map +1 -0
  86. package/dist/tui/permissions-view.d.ts +93 -0
  87. package/dist/tui/permissions-view.d.ts.map +1 -0
  88. package/dist/tui/permissions-view.js +174 -0
  89. package/dist/tui/permissions-view.js.map +1 -0
  90. package/dist/tui/present-visual/approve.d.ts +10 -0
  91. package/dist/tui/present-visual/approve.d.ts.map +1 -0
  92. package/dist/tui/present-visual/approve.js +156 -0
  93. package/dist/tui/present-visual/approve.js.map +1 -0
  94. package/dist/tui/present-visual/dump.d.ts +47 -0
  95. package/dist/tui/present-visual/dump.d.ts.map +1 -0
  96. package/dist/tui/present-visual/dump.js +144 -0
  97. package/dist/tui/present-visual/dump.js.map +1 -0
  98. package/dist/tui/present-visual/journey-teardown.d.ts +3 -0
  99. package/dist/tui/present-visual/journey-teardown.d.ts.map +1 -0
  100. package/dist/tui/present-visual/journey-teardown.js +188 -0
  101. package/dist/tui/present-visual/journey-teardown.js.map +1 -0
  102. package/dist/tui/present-visual/journey.d.ts +15 -0
  103. package/dist/tui/present-visual/journey.d.ts.map +1 -0
  104. package/dist/tui/present-visual/journey.js +88 -0
  105. package/dist/tui/present-visual/journey.js.map +1 -0
  106. package/dist/tui/present-visual/raster.d.ts +3 -0
  107. package/dist/tui/present-visual/raster.d.ts.map +1 -0
  108. package/dist/tui/present-visual/raster.js +75 -0
  109. package/dist/tui/present-visual/raster.js.map +1 -0
  110. package/dist/tui/queue-shortcuts.d.ts +13 -0
  111. package/dist/tui/queue-shortcuts.d.ts.map +1 -0
  112. package/dist/tui/queue-shortcuts.js +23 -0
  113. package/dist/tui/queue-shortcuts.js.map +1 -0
  114. package/dist/tui/read-widen.d.ts +14 -0
  115. package/dist/tui/read-widen.d.ts.map +1 -0
  116. package/dist/tui/read-widen.js +45 -0
  117. package/dist/tui/read-widen.js.map +1 -0
  118. package/dist/tui/rewind-chrome.d.ts +29 -0
  119. package/dist/tui/rewind-chrome.d.ts.map +1 -0
  120. package/dist/tui/rewind-chrome.js +77 -0
  121. package/dist/tui/rewind-chrome.js.map +1 -0
  122. package/dist/tui/rust-present.d.ts +37 -0
  123. package/dist/tui/rust-present.d.ts.map +1 -0
  124. package/dist/tui/rust-present.js +205 -0
  125. package/dist/tui/rust-present.js.map +1 -0
  126. package/dist/tui/spinner-phase.d.ts +24 -0
  127. package/dist/tui/spinner-phase.d.ts.map +1 -0
  128. package/dist/tui/spinner-phase.js +25 -0
  129. package/dist/tui/spinner-phase.js.map +1 -0
  130. package/dist/tui/statusline-path.d.ts +10 -0
  131. package/dist/tui/statusline-path.d.ts.map +1 -0
  132. package/dist/tui/statusline-path.js +42 -0
  133. package/dist/tui/statusline-path.js.map +1 -0
  134. package/dist/tui/sugarcoat-paint.d.ts +14 -0
  135. package/dist/tui/sugarcoat-paint.d.ts.map +1 -0
  136. package/dist/tui/sugarcoat-paint.js +146 -0
  137. package/dist/tui/sugarcoat-paint.js.map +1 -0
  138. package/dist/tui/tape-spans.d.ts +64 -0
  139. package/dist/tui/tape-spans.d.ts.map +1 -0
  140. package/dist/tui/tape-spans.js +93 -0
  141. package/dist/tui/tape-spans.js.map +1 -0
  142. package/dist/tui/tea.d.ts +75 -0
  143. package/dist/tui/tea.d.ts.map +1 -0
  144. package/dist/tui/tea.js +2952 -0
  145. package/dist/tui/tea.js.map +1 -0
  146. package/dist/tui/vt-paint.d.ts +12 -0
  147. package/dist/tui/vt-paint.d.ts.map +1 -0
  148. package/dist/tui/vt-paint.js +123 -0
  149. package/dist/tui/vt-paint.js.map +1 -0
  150. package/dist/tui/wait-phrase.d.ts +31 -0
  151. package/dist/tui/wait-phrase.d.ts.map +1 -0
  152. package/dist/tui/wait-phrase.js +26 -0
  153. package/dist/tui/wait-phrase.js.map +1 -0
  154. package/dist/tui/widget-host.d.ts +34 -0
  155. package/dist/tui/widget-host.d.ts.map +1 -0
  156. package/dist/tui/widget-host.js +80 -0
  157. package/dist/tui/widget-host.js.map +1 -0
  158. package/dist/tui/work-cells.d.ts +47 -0
  159. package/dist/tui/work-cells.d.ts.map +1 -0
  160. package/dist/tui/work-cells.js +606 -0
  161. package/dist/tui/work-cells.js.map +1 -0
  162. package/dist/tui/worker-entry.d.ts +2 -0
  163. package/dist/tui/worker-entry.d.ts.map +1 -0
  164. package/dist/tui/worker-entry.js +98 -0
  165. package/dist/tui/worker-entry.js.map +1 -0
  166. package/package.json +84 -3
  167. package/README.md +0 -3
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Tool / scheme / loop layout. Agent knowledge stays here — not in Quickdraw.
3
+ * Cover chrome is Tea; this file paints the in-tape ask gutter and ask pin.
4
+ * `approvalComment` rides identity status. Policy and judge votes do not —
5
+ * waiting is the `?` gutter, not an `ask` / `allow` word. The pin follows
6
+ * `askPaintHead` — a mutate
7
+ * Diff stays the pin until its result is a tape row, not merely until `approved`.
8
+ * Work voice is `foreground` at a prominence step — same hue as speech, quieter
9
+ * than body. Live flux is the spinner, not a `running` hue. Fail is the gutter
10
+ * (`error`), not a red row.
11
+ */
12
+ import { type BlobPeek, type KernelRoot, ProgrammaticJob, ToolCallJob } from "@inhuman.tools/harness-model";
13
+ import { type ReceiptFace, type WorkCellFace } from "@inhuman.tools/harness-sdk/text";
14
+ import { type LayoutNode } from "@inhuman.tools/quickdraw/layout";
15
+ import type { WorkReceiptKind } from "./paint-types.js";
16
+ export { receiptPlainText, workCellFace, type CallStatus, type ReceiptFace, type WorkCellFace, type WorkCellState, } from "@inhuman.tools/harness-sdk/text";
17
+ export declare const SIGIL_TOOL = "\u00B7";
18
+ export declare const SIGIL_SHELL = "$";
19
+ export declare const SIGIL_SCHEME_REPL = "\u24AA";
20
+ export declare const SIGIL_SCHEME_WATCH = "\u24B2";
21
+ export declare const SIGIL_ASK = "?";
22
+ /** Same seat as `SIGIL_TOOL`, but the classifier is evaluating, not executing. */
23
+ export declare const SIGIL_JUDGE = "?";
24
+ export declare function receiptFromReplica(kind: WorkReceiptKind, text: string, diff: {
25
+ readonly old: string;
26
+ readonly new: string;
27
+ readonly path: string;
28
+ readonly lineStart?: number;
29
+ }): ReceiptFace;
30
+ /**
31
+ * Compact omits floor-allow leaves only when `hideFloorTools` is on.
32
+ * Default off: the call stays, including the question identity while it
33
+ * is still open. Comm-first (agent) stays omitted. The scheme bag never
34
+ * fuses. Inners of a bag never fuse — the bag row is their host parent.
35
+ */
36
+ export declare function workCellFuse(work: ToolCallJob | ProgrammaticJob, nested?: boolean, hideFloorTools?: boolean): boolean;
37
+ /** Pin exists to hold a Diff, or an identity the rim cannot say. MCP options and shell commands ride the composer stack. */
38
+ export declare function askPinWorthPainting(face: WorkCellFace): boolean;
39
+ /**
40
+ * Sticky pin: typed receipt. Always open. Not a disclosure.
41
+ * No ask gutter — the receipt and the cover already mark the wait.
42
+ * Rim-named work omits the identity line. No receipt and no identity → do not mount.
43
+ */
44
+ export declare function askPinNode(face: WorkCellFace, width: number): LayoutNode;
45
+ export declare function askPinFace(work: ToolCallJob | ProgrammaticJob, kernel?: KernelRoot | null, blobs?: BlobPeek): WorkCellFace;
46
+ export declare function workDisclosure(face: WorkCellFace, width: number): LayoutNode;
47
+ //# sourceMappingURL=work-cells.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-cells.d.ts","sourceRoot":"","sources":["../../src/tui/work-cells.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE5G,OAAO,EAUL,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMxD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,iCAAiC,CAAC;AAEzC,eAAO,MAAM,UAAU,WAAM,CAAC;AAC9B,eAAO,MAAM,WAAW,MAAM,CAAC;AAC/B,eAAO,MAAM,iBAAiB,WAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,WAAM,CAAC;AACtC,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,kFAAkF;AAClF,eAAO,MAAM,WAAW,MAAM,CAAC;AAS/B,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACvG,WAAW,CAmCb;AAgWD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,MAAM,UAAQ,EAAE,cAAc,UAAQ,GAAG,OAAO,CAGjH;AAMD,4HAA4H;AAC5H,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAI/D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAoBxE;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,GAAG,eAAe,EACnC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,EAC1B,KAAK,CAAC,EAAE,QAAQ,GACf,YAAY,CAMd;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAiH5E"}
@@ -0,0 +1,606 @@
1
+ /**
2
+ * Tool / scheme / loop layout. Agent knowledge stays here — not in Quickdraw.
3
+ * Cover chrome is Tea; this file paints the in-tape ask gutter and ask pin.
4
+ * `approvalComment` rides identity status. Policy and judge votes do not —
5
+ * waiting is the `?` gutter, not an `ask` / `allow` word. The pin follows
6
+ * `askPaintHead` — a mutate
7
+ * Diff stays the pin until its result is a tape row, not merely until `approved`.
8
+ * Work voice is `foreground` at a prominence step — same hue as speech, quieter
9
+ * than body. Live flux is the spinner, not a `running` hue. Fail is the gutter
10
+ * (`error`), not a red row.
11
+ */
12
+ import { ProgrammaticJob, ToolCallJob } from "@inhuman.tools/harness-model";
13
+ import { stripVt } from "@inhuman.tools/harness-model/vt-text";
14
+ import { formatIdentity, formatMutateIdentity, isMutateName, isSchemeBagName, SCHEME_REPL_NAME, SCHEME_WATCH_NAME, compactOmitsTool, mutateSuggestionReceipt, workCellFace, } from "@inhuman.tools/harness-sdk/text";
15
+ import { INDICATION_CONTENT_INSET, INDICATION_EXPAND_GLYPH, INDICATION_VOID_GLYPH, } from "@inhuman.tools/quickdraw/layout";
16
+ import { FgP } from "@inhuman.tools/quickdraw/lexicon";
17
+ import { Diff, Indication, Markdown, SyntaxBlock, SyntaxLine, Text } from "@inhuman.tools/quickdraw/widgets";
18
+ import { ShadowStream } from "@inhuman.tools/quickdraw/widgets/shadow-stream";
19
+ import { highlightSchemePart, highlightSugarcoat } from "./sugarcoat-paint.js";
20
+ import { VtText } from "./vt-paint.js";
21
+ // The face layer lives in `work-text.ts` of `@inhuman.tools/harness-sdk/text` (no quickdraw).
22
+ // One door for paint importers and tests.
23
+ export { receiptPlainText, workCellFace, } from "@inhuman.tools/harness-sdk/text";
24
+ export const SIGIL_TOOL = "·";
25
+ export const SIGIL_SHELL = "$";
26
+ export const SIGIL_SCHEME_REPL = "⒪";
27
+ export const SIGIL_SCHEME_WATCH = "⒲";
28
+ export const SIGIL_ASK = "?";
29
+ /** Same seat as `SIGIL_TOOL`, but the classifier is evaluating, not executing. */
30
+ export const SIGIL_JUDGE = "?";
31
+ /** Same hue as speech. Ask is in hand; sealed/live recede; meta recedes further. */
32
+ const ASK_VOICE = { role: "foreground", prominence: FgP.primary };
33
+ const WORK_VOICE = { role: "foreground", prominence: FgP.rising };
34
+ const WORK_META = { role: "foreground", prominence: FgP.ghost };
35
+ export function receiptFromReplica(kind, text, diff) {
36
+ if (kind === "diff") {
37
+ return {
38
+ kind: "diff",
39
+ old: diff.old,
40
+ new: diff.new,
41
+ path: diff.path,
42
+ ...(diff.lineStart === undefined || diff.lineStart <= 1 ? {} : { lineStart: diff.lineStart }),
43
+ };
44
+ }
45
+ if (kind === "none")
46
+ return { kind: "none" };
47
+ if (kind === "parts") {
48
+ let parts = [];
49
+ try {
50
+ const parsed = JSON.parse(text);
51
+ if (Array.isArray(parsed)) {
52
+ parts = parsed.filter((value) => {
53
+ if (value === null || typeof value !== "object")
54
+ return false;
55
+ const rec = value;
56
+ return ((rec.kind === "text" ||
57
+ rec.kind === "markdown" ||
58
+ rec.kind === "number" ||
59
+ rec.kind === "boolean" ||
60
+ rec.kind === "syntax") &&
61
+ typeof rec.text === "string");
62
+ });
63
+ }
64
+ }
65
+ catch {
66
+ parts = [];
67
+ }
68
+ return { kind: "parts", parts };
69
+ }
70
+ return { kind, text };
71
+ }
72
+ function shellOutputBox(text, key, width) {
73
+ return squareBox(VtText(text, {
74
+ key: `${key}:out`,
75
+ width: Math.max(1, width - 2),
76
+ style: WORK_META,
77
+ }), `${key}:out-box`, width);
78
+ }
79
+ function pushShellReceipt(kids, face, width) {
80
+ const receipt = face.receipt;
81
+ if (receipt.kind === "text") {
82
+ kids.push(shellOutputBox(receipt.text, face.key, width));
83
+ return;
84
+ }
85
+ if (receipt.kind === "parts") {
86
+ for (const [i, part] of receipt.parts.entries()) {
87
+ if (part.kind !== "text") {
88
+ kids.push(receiptBody(part, `${face.key}:${i}`, width));
89
+ continue;
90
+ }
91
+ kids.push(VtText(part.text, { key: `${face.key}:policy`, width, style: WORK_META }));
92
+ }
93
+ return;
94
+ }
95
+ const extra = receiptContent(receipt, face.key, width);
96
+ if (extra !== undefined)
97
+ kids.push(extra);
98
+ }
99
+ function squareBox(child, key, width) {
100
+ return {
101
+ type: "box",
102
+ key,
103
+ style: { border: true, width },
104
+ children: [child],
105
+ };
106
+ }
107
+ function voidRow(key, child) {
108
+ return Indication({
109
+ key,
110
+ gutter: { kind: "top", glyph: INDICATION_VOID_GLYPH },
111
+ children: [child],
112
+ });
113
+ }
114
+ function receiptContent(face, key, width) {
115
+ if (face.kind === "none" || face.kind === "diff")
116
+ return undefined;
117
+ if (face.kind === "stream") {
118
+ return ShadowStream({
119
+ primitive: "",
120
+ lines: stripVt(face.text),
121
+ state: "peek",
122
+ height: 5,
123
+ width,
124
+ role: "foreground",
125
+ key: `${key}:tail`,
126
+ });
127
+ }
128
+ if (face.kind === "markdown") {
129
+ return Markdown(face.text, { key: `${key}:md`, width, style: WORK_META });
130
+ }
131
+ if (face.kind === "parts") {
132
+ return {
133
+ type: "flex",
134
+ key: `${key}:parts`,
135
+ style: { direction: "column", width },
136
+ children: face.parts.map((part, i) => receiptBody(part, `${key}:${i}`, width)),
137
+ };
138
+ }
139
+ return receiptBody(face, key, width);
140
+ }
141
+ function receiptNode(face, key, width, opts) {
142
+ if (face.kind === "none")
143
+ return undefined;
144
+ if (face.kind === "diff") {
145
+ return Diff({
146
+ old: face.old,
147
+ new: face.new,
148
+ path: face.path,
149
+ width,
150
+ hideHeader: true,
151
+ // Sides stay whole for syntax and line numbers. Diff elides at paint.
152
+ stickyBottomRows: opts?.stickyBottomRows,
153
+ lineStart: face.lineStart,
154
+ key: `${key}:diff`,
155
+ });
156
+ }
157
+ const contentW = Math.max(8, width - INDICATION_CONTENT_INSET);
158
+ const inner = receiptContent(face, key, contentW);
159
+ if (inner === undefined)
160
+ return undefined;
161
+ if (face.kind === "stream")
162
+ return inner;
163
+ return voidRow(face.kind === "markdown" ? `${key}:md-row` : `${key}:body-row`, inner);
164
+ }
165
+ function receiptBody(part, key, width) {
166
+ if (part.kind === "markdown") {
167
+ return Markdown(part.text, { key: `${key}:md`, width, style: WORK_META });
168
+ }
169
+ if (part.kind === "text") {
170
+ return VtText(part.text, {
171
+ key: `${key}:body`,
172
+ width,
173
+ style: WORK_META,
174
+ });
175
+ }
176
+ return SyntaxBlock(part.text, {
177
+ key: `${key}:body`,
178
+ width,
179
+ lines: highlightSchemePart(part),
180
+ });
181
+ }
182
+ function identityStyle(state) {
183
+ return state === "ask" ? ASK_VOICE : WORK_VOICE;
184
+ }
185
+ const MUTATE_CAPTION_GAP = 2;
186
+ function cols(text) {
187
+ return [...text].length;
188
+ }
189
+ function mutateIdentity(face, width, voice) {
190
+ const pathText = formatMutateIdentity(face, width);
191
+ if (face.caption === "") {
192
+ return Text(pathText, {
193
+ key: `${face.key}:text`,
194
+ whiteSpace: "nowrap",
195
+ ellipsis: true,
196
+ lineClamp: 1,
197
+ style: voice,
198
+ });
199
+ }
200
+ const pathBare = face.status === "" ? face.object : `${face.object} ${face.status}`;
201
+ const fits = cols(pathBare) + MUTATE_CAPTION_GAP + cols(face.caption) <= width;
202
+ const path = Text(fits ? pathBare : pathText, {
203
+ key: `${face.key}:path`,
204
+ whiteSpace: "nowrap",
205
+ ellipsis: true,
206
+ lineClamp: 1,
207
+ style: voice,
208
+ });
209
+ const caption = Text(face.caption, {
210
+ key: `${face.key}:caption`,
211
+ whiteSpace: fits ? "nowrap" : "normal",
212
+ style: WORK_META,
213
+ });
214
+ return {
215
+ type: "box",
216
+ key: `${face.key}:id-wrap`,
217
+ style: { width },
218
+ children: [
219
+ {
220
+ type: "flex",
221
+ key: `${face.key}:id-line`,
222
+ style: fits
223
+ ? {
224
+ direction: "row",
225
+ width,
226
+ justifyContent: "space_between",
227
+ gap: MUTATE_CAPTION_GAP,
228
+ alignItems: "flex_start",
229
+ }
230
+ : { direction: "column", width },
231
+ children: [path, caption],
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ function scriptLine(face, width, key, wrap = false) {
237
+ if (face.name === "shell") {
238
+ if (wrap || face.object.includes("\n")) {
239
+ return SyntaxBlock(face.object, { lang: "bash", key, width });
240
+ }
241
+ return SyntaxLine(face.object, { lang: "bash", key, width });
242
+ }
243
+ if (face.object.includes("\n")) {
244
+ return SyntaxBlock(face.object, {
245
+ key,
246
+ width,
247
+ lines: highlightSugarcoat(face.object),
248
+ });
249
+ }
250
+ return SyntaxLine(face.object, {
251
+ key,
252
+ width,
253
+ runs: highlightSugarcoat(face.object)[0],
254
+ });
255
+ }
256
+ function shellCwdLine(face) {
257
+ return Text(face.status, {
258
+ key: `${face.key}:status`,
259
+ whiteSpace: "nowrap",
260
+ ellipsis: true,
261
+ lineClamp: 1,
262
+ style: WORK_META,
263
+ });
264
+ }
265
+ function identityContent(face, width, voice, wrapScript = false) {
266
+ if (face.agent) {
267
+ const name = Text(face.object === "" ? face.name : `${face.name} `, {
268
+ key: `${face.key}:name`,
269
+ whiteSpace: "nowrap",
270
+ style: voice,
271
+ });
272
+ if (face.object === "")
273
+ return name;
274
+ return {
275
+ type: "flex",
276
+ key: `${face.key}:id-line`,
277
+ style: { direction: "row", width },
278
+ children: [
279
+ name,
280
+ Text(face.object, {
281
+ key: `${face.key}:title`,
282
+ whiteSpace: "nowrap",
283
+ ellipsis: true,
284
+ lineClamp: 1,
285
+ style: WORK_META,
286
+ }),
287
+ ],
288
+ };
289
+ }
290
+ if (isSchemeBagName(face.name)) {
291
+ if (face.caption === "") {
292
+ return scriptLine(face, width, `${face.key}:cmd`, wrapScript);
293
+ }
294
+ return Text(face.caption, {
295
+ key: `${face.key}:caption`,
296
+ whiteSpace: "nowrap",
297
+ ellipsis: true,
298
+ lineClamp: 1,
299
+ style: voice,
300
+ });
301
+ }
302
+ if (face.name === "shell") {
303
+ const body = face.caption === ""
304
+ ? scriptLine(face, width, `${face.key}:cmd`, wrapScript)
305
+ : Text(face.caption, {
306
+ key: `${face.key}:caption`,
307
+ whiteSpace: "nowrap",
308
+ ellipsis: true,
309
+ lineClamp: 1,
310
+ style: voice,
311
+ });
312
+ // Open caption: the expand script is the call, and cwd follows that script.
313
+ if (face.status === "" || (wrapScript && face.caption !== ""))
314
+ return body;
315
+ return {
316
+ type: "flex",
317
+ key: `${face.key}:id-stack`,
318
+ style: { direction: "column", width },
319
+ children: [body, shellCwdLine(face)],
320
+ };
321
+ }
322
+ if (face.pretty) {
323
+ const headline = face.status === "" ? face.object : `${face.object} ${face.status}`;
324
+ const line = Markdown(headline, {
325
+ key: `${face.key}:text`,
326
+ width,
327
+ style: voice,
328
+ });
329
+ if (face.caption === "")
330
+ return line;
331
+ return {
332
+ type: "flex",
333
+ key: `${face.key}:id-stack`,
334
+ style: { direction: "column", width },
335
+ children: [
336
+ line,
337
+ Text(face.caption, {
338
+ key: `${face.key}:subtitle`,
339
+ whiteSpace: "pre-wrap",
340
+ style: WORK_META,
341
+ }),
342
+ ],
343
+ };
344
+ }
345
+ if (isMutateName(face.name)) {
346
+ return mutateIdentity(face, width, voice);
347
+ }
348
+ if (!face.split) {
349
+ return Text(formatIdentity({ name: face.name, object: face.object, status: face.status }, width, face.pathShaped), {
350
+ key: `${face.key}:text`,
351
+ whiteSpace: "nowrap",
352
+ ellipsis: true,
353
+ lineClamp: 1,
354
+ style: voice,
355
+ });
356
+ }
357
+ const children = [
358
+ Text(`${face.name} `, {
359
+ key: `${face.key}:server`,
360
+ whiteSpace: "nowrap",
361
+ style: WORK_META,
362
+ }),
363
+ Text(face.object, {
364
+ key: `${face.key}:action`,
365
+ whiteSpace: "nowrap",
366
+ ellipsis: true,
367
+ lineClamp: 1,
368
+ style: voice,
369
+ }),
370
+ ];
371
+ if (face.status !== "") {
372
+ children.push(Text(` ${face.status}`, {
373
+ key: `${face.key}:status`,
374
+ whiteSpace: "nowrap",
375
+ style: WORK_META,
376
+ }));
377
+ }
378
+ return {
379
+ type: "flex",
380
+ key: `${face.key}:id-line`,
381
+ style: { direction: "row", width },
382
+ children,
383
+ };
384
+ }
385
+ function sealedGlyph(name) {
386
+ if (name === "shell")
387
+ return SIGIL_SHELL;
388
+ if (name === SCHEME_REPL_NAME)
389
+ return SIGIL_SCHEME_REPL;
390
+ if (name === SCHEME_WATCH_NAME)
391
+ return SIGIL_SCHEME_WATCH;
392
+ return SIGIL_TOOL;
393
+ }
394
+ function cellGutter(state, name) {
395
+ // Waiting for a human wins the seat over the classifier mark, the live
396
+ // spinner, and the sealed glyph (`·`, `$`, `⒪`, `⒲`).
397
+ if (state === "ask")
398
+ return { kind: "top", glyph: SIGIL_ASK };
399
+ if (state === "judging")
400
+ return { kind: "top", glyph: SIGIL_JUDGE };
401
+ if (state === "live")
402
+ return { kind: "spinner", profile: "tool", rows: "top" };
403
+ return { kind: "top", glyph: sealedGlyph(name) };
404
+ }
405
+ /**
406
+ * Compact omits floor-allow leaves only when `hideFloorTools` is on.
407
+ * Default off: the call stays, including the question identity while it
408
+ * is still open. Comm-first (agent) stays omitted. The scheme bag never
409
+ * fuses. Inners of a bag never fuse — the bag row is their host parent.
410
+ */
411
+ export function workCellFuse(work, nested = false, hideFloorTools = false) {
412
+ if (work instanceof ProgrammaticJob)
413
+ return false;
414
+ return compactOmitsTool(work, hideFloorTools, nested);
415
+ }
416
+ function pinOmitsIdentity(face) {
417
+ return face.rimNamed;
418
+ }
419
+ /** Pin exists to hold a Diff, or an identity the rim cannot say. MCP options and shell commands ride the composer stack. */
420
+ export function askPinWorthPainting(face) {
421
+ if (face.split)
422
+ return false;
423
+ if (face.receipt.kind !== "none")
424
+ return true;
425
+ return !pinOmitsIdentity(face);
426
+ }
427
+ /**
428
+ * Sticky pin: typed receipt. Always open. Not a disclosure.
429
+ * No ask gutter — the receipt and the cover already mark the wait.
430
+ * Rim-named work omits the identity line. No receipt and no identity → do not mount.
431
+ */
432
+ export function askPinNode(face, width) {
433
+ const body = receiptNode(face.receipt, face.key, width, {
434
+ stickyBottomRows: 4,
435
+ });
436
+ if (pinOmitsIdentity(face)) {
437
+ return {
438
+ type: "box",
439
+ key: face.key,
440
+ style: { width, padding: { t: 1 } },
441
+ children: body === undefined ? [] : [body],
442
+ };
443
+ }
444
+ const contentW = Math.max(8, width - INDICATION_CONTENT_INSET);
445
+ const identity = voidRow(`${face.key}:id`, identityContent(face, contentW, identityStyle(face.state)));
446
+ return {
447
+ type: "box",
448
+ key: face.key,
449
+ style: { width, padding: { t: 1 } },
450
+ children: body === undefined ? [identity] : [identity, body],
451
+ };
452
+ }
453
+ export function askPinFace(work, kernel, blobs) {
454
+ const face = workCellFace(work, `ask:${work.toolCallId}`, false, false, "", kernel, blobs);
455
+ if (!(work instanceof ToolCallJob))
456
+ return face;
457
+ const suggestion = mutateSuggestionReceipt(work, blobs);
458
+ if (suggestion.kind === "none")
459
+ return face;
460
+ return { ...face, receipt: suggestion };
461
+ }
462
+ export function workDisclosure(face, width) {
463
+ const allocW = face.indent ? Math.max(8, width - 2) : width;
464
+ const textW = Math.max(8, allocW - INDICATION_CONTENT_INSET);
465
+ const voice = identityStyle(face.state);
466
+ const gutterStyle = face.state === "fail" ? { role: "error" } : voice;
467
+ const identity = Indication({
468
+ key: `${face.key}:id`,
469
+ gutter: cellGutter(face.state, face.name),
470
+ style: gutterStyle,
471
+ children: [identityContent(face, textW, voice)],
472
+ });
473
+ const identityOpen = Indication({
474
+ key: `${face.key}:id`,
475
+ gutter: cellGutter(face.state, face.name),
476
+ style: gutterStyle,
477
+ children: [identityContent(face, textW, voice, true)],
478
+ });
479
+ // File reads stay one line: `Read <path>`. No click, no body.
480
+ if (face.name === "read_file") {
481
+ const row = {
482
+ type: "box",
483
+ key: face.key,
484
+ style: { width: allocW },
485
+ children: [identity],
486
+ };
487
+ if (!face.indent)
488
+ return row;
489
+ return {
490
+ type: "box",
491
+ key: `${face.key}:indent`,
492
+ style: { padding: { l: 2 }, width },
493
+ children: [row],
494
+ };
495
+ }
496
+ const scriptInner = face.caption !== "" && (face.name === "shell" || isSchemeBagName(face.name))
497
+ ? scriptLine(face, textW, `${face.key}:script`, face.name === "shell")
498
+ : undefined;
499
+ const prettyInner = face.prettyBlock !== "" && face.receipt.kind !== "diff"
500
+ ? Markdown(face.prettyBlock, {
501
+ key: face.agent ? `${face.key}:spawn` : `${face.key}:pretty-md`,
502
+ width: textW,
503
+ style: WORK_META,
504
+ })
505
+ : undefined;
506
+ const livePeek = face.state === "live" && face.receipt.kind === "stream";
507
+ // Mutations are the review surface: filename, then Diff. Click-collapse would hide the change.
508
+ if (isMutateName(face.name)) {
509
+ const extras = [
510
+ ...(scriptInner === undefined ? [] : [voidRow(`${face.key}:script`, scriptInner)]),
511
+ ...(prettyInner === undefined ? [] : [voidRow(`${face.key}:pretty-block`, prettyInner)]),
512
+ ];
513
+ const body = livePeek ? undefined : receiptNode(face.receipt, face.key, allocW);
514
+ const peek = livePeek ? receiptNode(face.receipt, face.key, allocW) : undefined;
515
+ const open = [identity, ...extras];
516
+ if (peek !== undefined)
517
+ open.push(peek);
518
+ if (body !== undefined)
519
+ open.push(body);
520
+ const block = {
521
+ type: "box",
522
+ key: face.key,
523
+ style: { width: allocW },
524
+ children: open,
525
+ };
526
+ if (!face.indent)
527
+ return block;
528
+ return {
529
+ type: "box",
530
+ key: `${face.key}:indent`,
531
+ style: { padding: { l: 2 }, width },
532
+ children: [block],
533
+ };
534
+ }
535
+ const extraKids = [];
536
+ if (scriptInner !== undefined)
537
+ extraKids.push(scriptInner);
538
+ if (face.name === "shell" && face.caption !== "" && face.status !== "")
539
+ extraKids.push(shellCwdLine(face));
540
+ if (prettyInner !== undefined)
541
+ extraKids.push(prettyInner);
542
+ if (face.agent && face.response !== "")
543
+ extraKids.push(agentResponseBlock(face, textW));
544
+ if (face.name === "shell") {
545
+ pushShellReceipt(extraKids, face, textW);
546
+ }
547
+ else {
548
+ const extraReceipt = receiptContent(face.receipt, face.key, textW);
549
+ if (extraReceipt !== undefined)
550
+ extraKids.push(extraReceipt);
551
+ }
552
+ const extras = extraKids.length === 0
553
+ ? []
554
+ : [
555
+ Indication({
556
+ key: `${face.key}:open`,
557
+ gutter: { kind: "rule", glyph: INDICATION_EXPAND_GLYPH },
558
+ style: face.state === "fail"
559
+ ? { role: "error", prominence: FgP.ghost }
560
+ : { role: "foreground", prominence: FgP.ghost },
561
+ children: extraKids,
562
+ }),
563
+ ];
564
+ const disclosure = {
565
+ type: "disclosure",
566
+ key: face.key,
567
+ collapsed: livePeek ? false : face.collapsed,
568
+ intent: "activate",
569
+ onClick: { type: "toggle_collapsed", blockId: face.key },
570
+ userSelect: "none",
571
+ summary: identity,
572
+ children: [identityOpen, ...extras],
573
+ };
574
+ if (!face.indent)
575
+ return disclosure;
576
+ return {
577
+ type: "box",
578
+ key: `${face.key}:indent`,
579
+ style: { padding: { l: 2 }, width },
580
+ children: [disclosure],
581
+ };
582
+ }
583
+ /**
584
+ * Bottom of an expanded agent call: a quiet "Response" rule, then the reply
585
+ * at speech voice — brighter than the prompt stacked above it.
586
+ */
587
+ function agentResponseBlock(face, width) {
588
+ return {
589
+ type: "flex",
590
+ key: `${face.key}:response`,
591
+ style: { direction: "column", width, padding: { t: 1 } },
592
+ children: [
593
+ Text("Response", {
594
+ key: `${face.key}:response-label`,
595
+ whiteSpace: "nowrap",
596
+ style: WORK_META,
597
+ }),
598
+ Markdown(face.response, {
599
+ key: `${face.key}:response-body`,
600
+ width,
601
+ style: { role: "foreground", prominence: FgP.secondary },
602
+ }),
603
+ ],
604
+ };
605
+ }
606
+ //# sourceMappingURL=work-cells.js.map