@gaunt-sloth/core 2.0.0-alpha.24 → 2.0.0-alpha.26

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 (109) hide show
  1. package/README.md +1 -1
  2. package/dist/config/colour.d.ts +38 -0
  3. package/dist/config/colour.js +36 -0
  4. package/dist/config/colour.js.map +1 -0
  5. package/dist/config/defaults.d.ts +1 -0
  6. package/dist/config/defaults.js +1 -0
  7. package/dist/config/defaults.js.map +1 -1
  8. package/dist/config/loader.d.ts +13 -6
  9. package/dist/config/loader.js +171 -28
  10. package/dist/config/loader.js.map +1 -1
  11. package/dist/config/mouse.d.ts +50 -0
  12. package/dist/config/mouse.js +44 -0
  13. package/dist/config/mouse.js.map +1 -0
  14. package/dist/config/schema.d.ts +2372 -114
  15. package/dist/config/schema.js +845 -16
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/config/shell-policy.d.ts +466 -116
  18. package/dist/config/shell-policy.js +273 -62
  19. package/dist/config/shell-policy.js.map +1 -1
  20. package/dist/config/tool-descriptions.d.ts +165 -0
  21. package/dist/config/tool-descriptions.js +229 -0
  22. package/dist/config/tool-descriptions.js.map +1 -0
  23. package/dist/config/types.d.ts +36 -1
  24. package/dist/config/types.js.map +1 -1
  25. package/dist/config.d.ts +3 -0
  26. package/dist/config.js +3 -0
  27. package/dist/config.js.map +1 -1
  28. package/dist/core/GthAbstractAgent.d.ts +59 -1
  29. package/dist/core/GthAbstractAgent.js +72 -0
  30. package/dist/core/GthAbstractAgent.js.map +1 -1
  31. package/dist/core/GthAgentRunner.d.ts +341 -58
  32. package/dist/core/GthAgentRunner.js +817 -149
  33. package/dist/core/GthAgentRunner.js.map +1 -1
  34. package/dist/core/GthLangChainAgent.d.ts +16 -0
  35. package/dist/core/GthLangChainAgent.js +72 -2
  36. package/dist/core/GthLangChainAgent.js.map +1 -1
  37. package/dist/core/approvals/annotations.d.ts +122 -0
  38. package/dist/core/approvals/annotations.js +137 -0
  39. package/dist/core/approvals/annotations.js.map +1 -0
  40. package/dist/core/approvals/grants.d.ts +216 -0
  41. package/dist/core/approvals/grants.js +469 -0
  42. package/dist/core/approvals/grants.js.map +1 -0
  43. package/dist/core/approvals/matcher.d.ts +202 -0
  44. package/dist/core/approvals/matcher.js +267 -0
  45. package/dist/core/approvals/matcher.js.map +1 -0
  46. package/dist/core/approvals/mcpSubjects.d.ts +40 -0
  47. package/dist/core/approvals/mcpSubjects.js +99 -0
  48. package/dist/core/approvals/mcpSubjects.js.map +1 -0
  49. package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
  50. package/dist/core/approvals/toolAnnotationSources.js +277 -0
  51. package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
  52. package/dist/core/approvals/toolHost.d.ts +46 -0
  53. package/dist/core/approvals/toolHost.js +108 -0
  54. package/dist/core/approvals/toolHost.js.map +1 -0
  55. package/dist/core/launchBanner.d.ts +120 -0
  56. package/dist/core/launchBanner.js +399 -0
  57. package/dist/core/launchBanner.js.map +1 -0
  58. package/dist/core/plainToolIndication.js +13 -7
  59. package/dist/core/plainToolIndication.js.map +1 -1
  60. package/dist/core/shell/approvalStop.d.ts +75 -0
  61. package/dist/core/shell/approvalStop.js +108 -0
  62. package/dist/core/shell/approvalStop.js.map +1 -0
  63. package/dist/core/shell/arity.d.ts +6 -0
  64. package/dist/core/shell/arity.js +20 -6
  65. package/dist/core/shell/arity.js.map +1 -1
  66. package/dist/core/shell/denylist.d.ts +11 -0
  67. package/dist/core/shell/denylist.js +37 -0
  68. package/dist/core/shell/denylist.js.map +1 -0
  69. package/dist/core/shell/normalize.d.ts +41 -1
  70. package/dist/core/shell/normalize.js +58 -4
  71. package/dist/core/shell/normalize.js.map +1 -1
  72. package/dist/core/shell/openWorld.d.ts +138 -0
  73. package/dist/core/shell/openWorld.js +523 -0
  74. package/dist/core/shell/openWorld.js.map +1 -0
  75. package/dist/core/shell/rater.d.ts +560 -0
  76. package/dist/core/shell/rater.js +895 -0
  77. package/dist/core/shell/rater.js.map +1 -0
  78. package/dist/core/shell/raterModel.d.ts +41 -0
  79. package/dist/core/shell/raterModel.js +51 -0
  80. package/dist/core/shell/raterModel.js.map +1 -0
  81. package/dist/core/shell/rejection.d.ts +66 -0
  82. package/dist/core/shell/rejection.js +38 -0
  83. package/dist/core/shell/rejection.js.map +1 -0
  84. package/dist/core/types.d.ts +71 -9
  85. package/dist/core/types.js.map +1 -1
  86. package/dist/runtime/askStructured.d.ts +8 -7
  87. package/dist/runtime/askStructured.js +8 -7
  88. package/dist/runtime/askStructured.js.map +1 -1
  89. package/dist/runtime/conversation.js +89 -76
  90. package/dist/runtime/conversation.js.map +1 -1
  91. package/dist/runtime/singleShot.js +71 -60
  92. package/dist/runtime/singleShot.js.map +1 -1
  93. package/dist/utils/ProgressIndicator.d.ts +21 -0
  94. package/dist/utils/ProgressIndicator.js +30 -3
  95. package/dist/utils/ProgressIndicator.js.map +1 -1
  96. package/dist/utils/consoleUtils.d.ts +30 -0
  97. package/dist/utils/consoleUtils.js +27 -2
  98. package/dist/utils/consoleUtils.js.map +1 -1
  99. package/dist/utils/systemUtils.d.ts +17 -0
  100. package/dist/utils/systemUtils.js +17 -0
  101. package/dist/utils/systemUtils.js.map +1 -1
  102. package/package.json +5 -2
  103. package/schema/gsloth-config.schema.json +1216 -417
  104. package/dist/core/shell/allowlist.d.ts +0 -75
  105. package/dist/core/shell/allowlist.js +0 -187
  106. package/dist/core/shell/allowlist.js.map +0 -1
  107. package/dist/core/shell/judge.d.ts +0 -161
  108. package/dist/core/shell/judge.js +0 -261
  109. package/dist/core/shell/judge.js.map +0 -1
@@ -0,0 +1,120 @@
1
+ /**
2
+ * TUI-C40 — the resting face, under the name the animation layer refers to it by.
3
+ *
4
+ * Every frame below is the same five lines and the same 16-column field, so swapping one in changes
5
+ * nothing about the layout: the right column still starts at {@link RIGHT_COLUMN} and the
6
+ * truncation budgets are untouched. That invariant is what makes an animation a data change rather
7
+ * than a geometry change, and it is asserted per frame in the specs.
8
+ */
9
+ export declare const RESTING_FACE: readonly [" ▄█▀▀▀▀▀▀▀▀█▄", "▄▀█▄█▀▀▀▀▀▀█▄█▀▄", "█ ▀█▄▀ ▀▄█▀ █", "▀▄▀▀ ██████ ▀▀▄▀", " ▀██████████▀"];
10
+ /** The animations a click can play. This list is the complete set. */
11
+ export declare const SLOTH_ANIMATIONS: readonly ['blink', 'nod', 'look-around', 'eyeroll'];
12
+ export type SlothAnimation = (typeof SLOTH_ANIMATIONS)[number];
13
+ /** One frame of an animation and how long it stays on screen. */
14
+ export interface SlothAnimationStep {
15
+ face: readonly string[];
16
+ holdMs: number;
17
+ }
18
+ /**
19
+ * The four sequences, as the frames shown AFTER the resting face.
20
+ *
21
+ * Playback always settles back on {@link RESTING_FACE} once the last step's hold elapses, so no
22
+ * sequence lists a trailing rest — but `nod` lists rest in the MIDDLE, because nodding twice means
23
+ * returning to level between the two dips. None of them loops, and a click plays exactly one.
24
+ */
25
+ export declare const SLOTH_ANIMATION_STEPS: Record<SlothAnimation, readonly SlothAnimationStep[]>;
26
+ /**
27
+ * Pick one animation at random. `random` is injectable so a test can pin the choice — the
28
+ * production call passes nothing and gets `Math.random`.
29
+ */
30
+ export declare function pickSlothAnimation(random?: () => number): SlothAnimation;
31
+ /**
32
+ * Blank columns prepended to every art row (TUI-C36). One column of left margin, so the block does
33
+ * not sit flush against the edge of the terminal.
34
+ */
35
+ export declare const LEFT_PAD = 1;
36
+ /**
37
+ * Column at which the right-hand column begins on EVERY row — the single number the whole layout
38
+ * and the colour split are expressed in terms of (margin + face + gutter = 22).
39
+ */
40
+ export declare const RIGHT_COLUMN: number;
41
+ /** Column at which the version label begins on row 2 (right column + wordmark + gap = 44). */
42
+ export declare const VERSION_COLUMN: number;
43
+ /**
44
+ * Narrowest terminal that still gets the right column. The wordmark itself ends at column 40, so
45
+ * anything narrower than this leaves no usable room for the fields beside it; below the threshold
46
+ * the face prints alone (see the module docs on why wrapping is not an option).
47
+ */
48
+ export declare const MIN_BANNER_COLUMNS: number;
49
+ /** The live values the banner reports, plus the terminal geometry it has to fit inside. */
50
+ export interface LaunchBannerInput {
51
+ /** Gaunt Sloth version, WITHOUT the `v` prefix (this module adds it). */
52
+ version?: string;
53
+ /** `config.modelDisplayName`. */
54
+ model?: string;
55
+ /** `config.modelProviderType`. */
56
+ provider?: string;
57
+ /** Working directory to report (`getProjectDir()`), before the home-prefix collapse. */
58
+ directory?: string;
59
+ /** The user's home dir; when `directory` sits under it the prefix collapses to `~`. */
60
+ homeDir?: string;
61
+ /** Live `stdout.columns`; `undefined` (non-TTY / tests) falls back to {@link DEFAULT_COLUMNS}. */
62
+ columns?: number;
63
+ /** Emit ANSI colour. Only {@link launchBannerText} reads this; Ink colours rows itself. */
64
+ colour?: boolean;
65
+ /**
66
+ * TUI-C40 — which face to draw. Defaults to {@link RESTING_FACE}, so every existing caller is
67
+ * unaffected and a plain launch is byte-identical to before. An animation frame differs only in
68
+ * these five lines: the geometry below reads the face's width from the constants, never from the
69
+ * strings, so a swapped frame cannot move the right column or change a truncation budget.
70
+ */
71
+ face?: readonly string[];
72
+ }
73
+ /**
74
+ * One rendered banner row, pre-split at {@link RIGHT_COLUMN} so each surface can colour the two
75
+ * halves independently without re-deriving the geometry.
76
+ */
77
+ export interface LaunchBannerRow {
78
+ /**
79
+ * Columns 0–21: the {@link LEFT_PAD} margin and the face, right-padded to {@link RIGHT_COLUMN}
80
+ * when this row has right-hand content (and left unpadded when it does not, so no row ends in
81
+ * trailing whitespace). Rendered in magenta — the margin is blank, so painting it is a no-op.
82
+ * Empty on the two blank padding rows, which carry nothing at all.
83
+ */
84
+ face: string;
85
+ /** Columns 21+: wordmark, version, model/provider or directory. Default foreground, no colour. */
86
+ right: string;
87
+ }
88
+ /**
89
+ * Assemble the banner as {@link LaunchBannerRow}s: a blank row, the five art rows each pre-split at
90
+ * {@link RIGHT_COLUMN}, and a blank row. Pure — every input arrives as an argument, so the geometry
91
+ * is testable without a terminal, a config or a process.
92
+ */
93
+ export declare function launchBannerRows(input: LaunchBannerInput): LaunchBannerRow[];
94
+ /**
95
+ * Render the banner as one multi-line string for the plain (readline) surface.
96
+ *
97
+ * Colour is the 16-colour `magenta` slot from {@link ANSI_COLORS} — the SAME slot the rest of the
98
+ * CLI uses — and never a 256-colour or 24-bit escape: the 16-colour slot picks up the violet from
99
+ * the user's own terminal theme, so the sloth looks native in both light and dark schemes. The
100
+ * right column carries no escape at all, not even dim.
101
+ *
102
+ * With `colour: false` the output contains ZERO escape sequences — not one dim wrapper, not one
103
+ * reset — so a monochrome session (`useColour: false`) stays genuinely clean. The caller decides;
104
+ * the plain surface passes `getUseColour()`, the TUI passes nothing and lets Ink's own
105
+ * colour-support detection make the same call.
106
+ */
107
+ export declare function launchBannerText(input: LaunchBannerInput): string;
108
+ /** The process-resolved half of {@link LaunchBannerInput} — everything but the geometry. */
109
+ export type LaunchBannerFields = Pick<LaunchBannerInput, 'version' | 'model' | 'provider' | 'directory' | 'homeDir'>;
110
+ /**
111
+ * Read the live fields off the process, so both surfaces report the same three values from the same
112
+ * sources instead of each picking its own. `model`/`provider` come from the caller because only it
113
+ * has the resolved config (`config.modelDisplayName` / `config.modelProviderType`); the rest are
114
+ * process-global.
115
+ *
116
+ * Fail-soft, as in `debugDump`: `getSlothVersion()` reads the installed `package.json` through the
117
+ * install dir that the CLI entry point sets, and THROWS when nothing set it (an embedded caller, a
118
+ * unit test). A missing version drops one label — it must never stop a session from starting.
119
+ */
120
+ export declare function launchBannerFields(model?: string, provider?: string): LaunchBannerFields;
@@ -0,0 +1,399 @@
1
+ /**
2
+ * @module launchBanner
3
+ * TUI-C33 — the welcoming ASCII-art launch banner shown at the top of an INTERACTIVE session
4
+ * (`gth chat` / `gth code`): a sloth face on the left, the `GAUNT SLOTH` wordmark plus three live
5
+ * fields (version, model/provider, working directory) on the right.
6
+ *
7
+ *
8
+ * ▄█▀▀▀▀▀▀▀▀█▄ ┏┓ ┏┓┓ ┓
9
+ * ▄▀█▄█▀▀▀▀▀▀█▄█▀▄ ┃┓┏┓┓┏┏┓╋ ┗┓┃┏┓╋┣┓ v2.0.0-alpha.25
10
+ * █ ▀█▄▀ ▀▄█▀ █ ┗┛┗┻┗┻┛┗┗ ┗┛┗┗┛┗┛┗
11
+ * ▀▄▀▀ ██████ ▀▀▄▀ gemini-3.1-pro (google-genai)
12
+ * ▀██████████▀ ~/dev/takahe
13
+ *
14
+ * It is deliberately NOT emitted by the one-shot verbs (`ask`, `review`, `pr`, `get`, `eval`,
15
+ * `batch`, `api`, `init`): their stdout is piped into files, diffs and CI, where a banner is
16
+ * corruption rather than welcome.
17
+ *
18
+ * ## Why this module is pure, and split in two halves
19
+ *
20
+ * The banner has to render IDENTICALLY on two surfaces that share no rendering code — the Ink TUI
21
+ * (`packages/app/src/tui/components/LaunchBanner.tsx`) and the plain `--no-tui` readline session
22
+ * (`packages/agent/src/modules/interactiveSessionModule.ts`). So all the geometry lives here as
23
+ * pure functions of `{ version, model, provider, directory, homeDir, columns, colour }`
24
+ * ({@link launchBannerRows} / {@link launchBannerText}), and the surfaces differ only in how they
25
+ * EMIT: one string through `displayLaunchBanner`, or one `<Text>` per row. That is the same split
26
+ * `Rule.tsx` uses (pure `ruleWidth` + a thin React component), including its
27
+ * `columns === undefined → 80` convention for a non-TTY / test stdout.
28
+ *
29
+ * The one impure function is {@link launchBannerFields} at the bottom, which reads the live
30
+ * version / project dir / home dir. It is shared for the same reason: both surfaces must read the
31
+ * same sources rather than each inventing its own.
32
+ *
33
+ * ## Why the right column is a slice at a fixed column, not a glyph classifier
34
+ *
35
+ * Colouring is "columns 0–20 are magenta, columns 21+ are default foreground" — a per-line split at
36
+ * {@link RIGHT_COLUMN}. It is emphatically NOT "is this character a block element": the wordmark is
37
+ * box-drawing, the face is block elements, and the moment a field value contained a `▀` (or the art
38
+ * gained a non-block glyph) a classifier would mis-colour it. A fixed column cannot.
39
+ *
40
+ * The face is 16 columns wide and the right column starts at {@link RIGHT_COLUMN}, leaving a
41
+ * 5-column gutter. The version label sits three columns after the widest wordmark line, i.e. at
42
+ * {@link VERSION_COLUMN}.
43
+ *
44
+ * ## Why the padding is part of the layout rather than of either surface
45
+ *
46
+ * TUI-C36 — the block is a splash, so it gets breathing room: one blank row above, one blank row
47
+ * below, and a {@link LEFT_PAD}-column left margin on every art row. That padding lives HERE, with
48
+ * the rest of the geometry, for the same reason the columns do — the Ink surface and the readline
49
+ * surface must show the same block, and a margin added by one renderer is a margin the other one
50
+ * lacks. The blank rows are genuinely empty (no pad, no escape): a padding row that carried
51
+ * trailing spaces would be visible the moment anything selected or copied it.
52
+ *
53
+ * The pad column shifts every column constant by exactly one, which is why they are all derived
54
+ * from {@link LEFT_PAD} rather than written as literals. The field budgets are expressed against
55
+ * those constants (`columns - RIGHT_COLUMN`), so they absorb the shift without changing.
56
+ *
57
+ * ## Why every dynamic field is bounded, and why the version is the one that is DROPPED
58
+ *
59
+ * A wrapped line SHATTERS the art: the continuation starts back at column 0 and collides with the
60
+ * face, so the sloth grows an extra row of model id. Wrapping is therefore not allowed to happen at
61
+ * all, and each field is bounded by what is left of the terminal on its own line — `columns - 21`
62
+ * for the model/provider and directory lines, `columns - 43` for the version.
63
+ *
64
+ * The model/provider and directory lines TRUNCATE to that budget: a clipped model id or path is
65
+ * still honest, it visibly ends in `…`. The directory is truncated from the LEFT
66
+ * (`…/dev/takahe`), because the leaf directory is the informative end; the model keeps its head and
67
+ * loses its tail.
68
+ *
69
+ * A VERSION that does not fit is dropped entirely instead ({@link fitOrDrop}) — never truncated.
70
+ * A clipped version number is not merely less informative, it is MISLEADING: `v2.0…` reads as a
71
+ * real, different version (2.0.1, say) rather than as a clipped `2.0.0-alpha.25`, and a wrong
72
+ * version in a bug report costs more than an absent one. The model/provider line already sets that
73
+ * drop-rather-than-mislead precedent by omitting itself when nothing resolves.
74
+ *
75
+ * Below {@link MIN_BANNER_COLUMNS} the right column cannot fit at all, so the face prints alone
76
+ * rather than wrapping the wordmark into rubble.
77
+ */
78
+ import { homedir } from 'node:os';
79
+ import { ELLIPSIS } from '#src/core/toolDisplay.js';
80
+ import { ANSI_COLORS } from '#src/utils/consoleUtils.js';
81
+ import { getProjectDir, getSlothVersion } from '#src/utils/systemUtils.js';
82
+ /**
83
+ * The sloth face, columns 0–15. Lines 1 and 5 are 14 columns, lines 2–4 are 16 — the face field is
84
+ * 16 wide and the short lines simply do not reach the edge. Do not "tidy" the leading spaces: they
85
+ * are part of the art.
86
+ */
87
+ const FACE_LINES = [
88
+ ' ▄█▀▀▀▀▀▀▀▀█▄',
89
+ '▄▀█▄█▀▀▀▀▀▀█▄█▀▄',
90
+ '█ ▀█▄▀ ▀▄█▀ █',
91
+ '▀▄▀▀ ██████ ▀▀▄▀',
92
+ ' ▀██████████▀',
93
+ ];
94
+ /**
95
+ * TUI-C40 — the resting face, under the name the animation layer refers to it by.
96
+ *
97
+ * Every frame below is the same five lines and the same 16-column field, so swapping one in changes
98
+ * nothing about the layout: the right column still starts at {@link RIGHT_COLUMN} and the
99
+ * truncation budgets are untouched. That invariant is what makes an animation a data change rather
100
+ * than a geometry change, and it is asserted per frame in the specs.
101
+ */
102
+ export const RESTING_FACE = FACE_LINES;
103
+ /**
104
+ * The extra faces, authored in takahē (`websites/ascii-art.md`) and transcribed here so the art has
105
+ * one home in this repo, exactly as the resting frame does. Names match the source file's blocks.
106
+ */
107
+ const BLINK_HALF = [
108
+ ' ▄█▀▀▀▀▀▀▀██▄',
109
+ '▄▀█▄█▀▀▀▀▀▀███▀▄',
110
+ '█ ▀█▄▀ ▀▄█▀ █',
111
+ '▀▄▀▀▀▐████▌▀▀▀▄▀',
112
+ ' ▀██████████▀',
113
+ ];
114
+ const BLINK_SHUT = [
115
+ ' ▄█▀▀▀▀▀▀███▄',
116
+ '▄▀█▄█▀▀▀▀▀▀███▀▄',
117
+ '█ ▀█▄▀ ▀▄█▀ █',
118
+ '▀▄▀▀▀▐████▌ ▄▀',
119
+ ' ▀██████████▀',
120
+ ];
121
+ const NOD_DOWN = [
122
+ ' ▄██████████▄',
123
+ '▄██▀▄▄▄▄▄▄▄▄▀██▄',
124
+ '█ ▀██ ▄ ▄ ██▀ █',
125
+ '█ ▄▄▀█▄▄▄▄█▀▄▄ █',
126
+ ' ▄▄▄██████▄▄▄',
127
+ ];
128
+ const LOOK_LEFT = [
129
+ ' ▄██████████▄',
130
+ '▄██▀▄▄▄▄▄▄▄████▄',
131
+ '█ ▀██ ▄ ▄ ██▀ █',
132
+ '█ ▄▀█▄▄▄▄█▀ ▄ █',
133
+ ' ▄▄▄██████▄▄▄',
134
+ ];
135
+ const LOOK_RIGHT = [
136
+ ' ▄██████████▄',
137
+ '▄████▄▄▄▄▄▄▄▀██▄',
138
+ '█ ▀██ ▄ ▄ ██▀ █',
139
+ '█ ▄ ▀█▄▄▄▄█▀▄ █',
140
+ ' ▄▄▄██████▄▄▄',
141
+ ];
142
+ const EYEROLL = [
143
+ ' ▄█▀▀▀▀▀▀▀▀█▄',
144
+ '▄▀█▄█▀▀▀▀▀▀█▄█▀▄',
145
+ '█ ▀██▄▀ ▀▄██▀ █',
146
+ '▀▌ ▄ ▐████▌ ▄ ▐▀',
147
+ ' ▀██████████▀',
148
+ ];
149
+ /** The animations a click can play. This list is the complete set. */
150
+ export const SLOTH_ANIMATIONS = ['blink', 'nod', 'look-around', 'eyeroll'];
151
+ /**
152
+ * The four sequences, as the frames shown AFTER the resting face.
153
+ *
154
+ * Playback always settles back on {@link RESTING_FACE} once the last step's hold elapses, so no
155
+ * sequence lists a trailing rest — but `nod` lists rest in the MIDDLE, because nodding twice means
156
+ * returning to level between the two dips. None of them loops, and a click plays exactly one.
157
+ */
158
+ export const SLOTH_ANIMATION_STEPS = {
159
+ // Quick, because a blink that lingers reads as a squint.
160
+ blink: [
161
+ { face: BLINK_HALF, holdMs: 90 },
162
+ { face: BLINK_SHUT, holdMs: 110 },
163
+ ],
164
+ nod: [
165
+ { face: NOD_DOWN, holdMs: 160 },
166
+ { face: RESTING_FACE, holdMs: 130 },
167
+ { face: NOD_DOWN, holdMs: 160 },
168
+ ],
169
+ 'look-around': [
170
+ { face: LOOK_LEFT, holdMs: 190 },
171
+ { face: LOOK_RIGHT, holdMs: 230 },
172
+ ],
173
+ // The longest hold: the joke only lands if you get to see it.
174
+ eyeroll: [{ face: EYEROLL, holdMs: 300 }],
175
+ };
176
+ /**
177
+ * Pick one animation at random. `random` is injectable so a test can pin the choice — the
178
+ * production call passes nothing and gets `Math.random`.
179
+ */
180
+ export function pickSlothAnimation(random = Math.random) {
181
+ const index = Math.floor(random() * SLOTH_ANIMATIONS.length);
182
+ // Guard the boundary: a `random()` of exactly 1 (or a stub returning it) would index past the end.
183
+ return SLOTH_ANIMATIONS[Math.min(index, SLOTH_ANIMATIONS.length - 1)];
184
+ }
185
+ /** The `GAUNT SLOTH` wordmark, occupying rows 1–3 of the right column. */
186
+ const WORDMARK_LINES = [
187
+ '┏┓ ┏┓┓ ┓',
188
+ '┃┓┏┓┓┏┏┓╋ ┗┓┃┏┓╋┣┓',
189
+ '┗┛┗┻┗┻┛┗┗ ┗┛┗┗┛┗┛┗',
190
+ ];
191
+ /** Widest wordmark line (19); rows 1 and 3 are padded to it so the version label lands square. */
192
+ const WORDMARK_WIDTH = Math.max(...WORDMARK_LINES.map((line) => [...line].length));
193
+ /** Blank columns between the wordmark and the version label. */
194
+ const VERSION_GAP = 3;
195
+ /**
196
+ * Blank columns prepended to every art row (TUI-C36). One column of left margin, so the block does
197
+ * not sit flush against the edge of the terminal.
198
+ */
199
+ export const LEFT_PAD = 1;
200
+ /** The prepended margin itself, applied to art rows only — never to the blank padding rows. */
201
+ const PAD = ' '.repeat(LEFT_PAD);
202
+ /** Columns the face field occupies, after the margin. */
203
+ const FACE_WIDTH = 16;
204
+ /** Blank columns between the face field and the right column. */
205
+ const FACE_GAP = 5;
206
+ /**
207
+ * Column at which the right-hand column begins on EVERY row — the single number the whole layout
208
+ * and the colour split are expressed in terms of (margin + face + gutter = 22).
209
+ */
210
+ export const RIGHT_COLUMN = LEFT_PAD + FACE_WIDTH + FACE_GAP;
211
+ /** Column at which the version label begins on row 2 (right column + wordmark + gap = 44). */
212
+ export const VERSION_COLUMN = RIGHT_COLUMN + WORDMARK_WIDTH + VERSION_GAP;
213
+ /**
214
+ * Field budget the narrowest banner still affords the right column — the widest wordmark line plus
215
+ * a few columns of field, which is what the original threshold of 45 amounted to at
216
+ * {@link RIGHT_COLUMN} 21.
217
+ */
218
+ const MIN_FIELD_BUDGET = 24;
219
+ /**
220
+ * Narrowest terminal that still gets the right column. The wordmark itself ends at column 40, so
221
+ * anything narrower than this leaves no usable room for the fields beside it; below the threshold
222
+ * the face prints alone (see the module docs on why wrapping is not an option).
223
+ */
224
+ export const MIN_BANNER_COLUMNS = RIGHT_COLUMN + MIN_FIELD_BUDGET;
225
+ /** Width assumed when the terminal width is unknown (non-TTY / tests) — as in `ruleWidth`. */
226
+ const DEFAULT_COLUMNS = 80;
227
+ /** Resolve the usable terminal width, mirroring `ruleWidth`'s unknown/non-finite handling. */
228
+ function resolveColumns(columns) {
229
+ const cols = typeof columns === 'number' && Number.isFinite(columns) ? columns : DEFAULT_COLUMNS;
230
+ return Math.floor(cols);
231
+ }
232
+ /**
233
+ * Truncate `text` to `budget` columns, losing the TAIL (`gemini-3.1-pro-experi…`). Returns
234
+ * `undefined` only when there is no budget at all; that guard keeps the function total (at
235
+ * `budget === 0` the slice below would otherwise drop a character and return a bare marker) and is
236
+ * not reachable from {@link launchBannerRows}, whose narrowest field budget is `45 - 21 = 24`.
237
+ *
238
+ * Slicing goes through `[...text]` rather than `String.slice` so a value containing an astral
239
+ * character (an emoji in a directory name) cannot be cut in half into a lone surrogate.
240
+ */
241
+ function truncateTail(text, budget) {
242
+ if (budget <= 0)
243
+ return undefined;
244
+ const chars = [...text];
245
+ if (chars.length <= budget)
246
+ return text;
247
+ return chars.slice(0, budget - 1).join('') + ELLIPSIS;
248
+ }
249
+ /**
250
+ * Truncate `text` to `budget` columns, losing the HEAD (`…/dev/takahe`). Used for the directory,
251
+ * where the leaf is what the user needs and the ancestors are noise.
252
+ */
253
+ function truncateHead(text, budget) {
254
+ if (budget <= 0)
255
+ return undefined;
256
+ const chars = [...text];
257
+ if (chars.length <= budget)
258
+ return text;
259
+ return ELLIPSIS + chars.slice(chars.length - (budget - 1)).join('');
260
+ }
261
+ /**
262
+ * All-or-nothing: `text` if it fits `budget` columns in full, otherwise `undefined`. Used for the
263
+ * VERSION label, which must never be truncated — see the module docs on why a clipped version
264
+ * number is misleading rather than merely terse.
265
+ */
266
+ function fitOrDrop(text, budget) {
267
+ return [...text].length <= budget ? text : undefined;
268
+ }
269
+ /**
270
+ * Collapse the home-directory prefix to `~`, the way a shell prompt does — a deep path spends its
271
+ * whole budget on `/Users/<name>` otherwise.
272
+ *
273
+ * Both separators are accepted deliberately: on Windows `homedir()` reads `%USERPROFILE%` and the
274
+ * path arrives back-slashed, and hard-coding POSIX `/` here is exactly the win32-only bug this repo
275
+ * keeps re-learning. The boundary check matters too — `/home/mar` must not collapse inside
276
+ * `/home/maria/x`.
277
+ */
278
+ function collapseHomePrefix(directory, homeDir) {
279
+ if (!homeDir)
280
+ return directory;
281
+ if (directory === homeDir)
282
+ return '~';
283
+ if (!directory.startsWith(homeDir))
284
+ return directory;
285
+ const boundary = directory.charAt(homeDir.length);
286
+ if (boundary !== '/' && boundary !== '\\')
287
+ return directory;
288
+ return '~' + directory.slice(homeDir.length);
289
+ }
290
+ /**
291
+ * Build the model/provider line: `model (provider)` when both resolve, the one that resolved when
292
+ * only one does, and nothing at all when neither does — an empty `()` would look like a bug.
293
+ */
294
+ function modelProviderLabel(model, provider) {
295
+ const m = model?.trim();
296
+ const p = provider?.trim();
297
+ if (m && p)
298
+ return `${m} (${p})`;
299
+ return m || p || undefined;
300
+ }
301
+ /** A padding row: no margin and no content, so it is an empty line on every surface. */
302
+ function blankRow() {
303
+ return { face: '', right: '' };
304
+ }
305
+ /** Wrap the five art rows in the blank row above and below that make the block a splash. */
306
+ function withPaddingRows(art) {
307
+ return [blankRow(), ...art, blankRow()];
308
+ }
309
+ /**
310
+ * Assemble the banner as {@link LaunchBannerRow}s: a blank row, the five art rows each pre-split at
311
+ * {@link RIGHT_COLUMN}, and a blank row. Pure — every input arrives as an argument, so the geometry
312
+ * is testable without a terminal, a config or a process.
313
+ */
314
+ export function launchBannerRows(input) {
315
+ const columns = resolveColumns(input.columns);
316
+ // TUI-C40 — the face is the one part a caller may swap; everything below is unchanged geometry.
317
+ const face = input.face ?? RESTING_FACE;
318
+ // Too narrow for the right column: the face alone, with no fields. It keeps its left margin and
319
+ // its blank rows — the padding is what makes it a splash, and it costs one column, not the
320
+ // gutter's five.
321
+ if (columns < MIN_BANNER_COLUMNS) {
322
+ return withPaddingRows(face.map((line) => ({ face: PAD + line, right: '' })));
323
+ }
324
+ // Every field is budgeted against ITS OWN start column, because the version starts further right
325
+ // than the other two. The version is the only fit-or-drop field; the other two truncate.
326
+ const fieldBudget = columns - RIGHT_COLUMN;
327
+ const version = input.version?.trim()
328
+ ? fitOrDrop(`v${input.version.trim()}`, columns - VERSION_COLUMN)
329
+ : undefined;
330
+ const model = modelProviderLabel(input.model, input.provider);
331
+ const modelLine = model ? truncateTail(model, fieldBudget) : undefined;
332
+ const directory = input.directory?.trim()
333
+ ? truncateHead(collapseHomePrefix(input.directory.trim(), input.homeDir), fieldBudget)
334
+ : undefined;
335
+ const rightLines = [
336
+ WORDMARK_LINES[0],
337
+ version
338
+ ? WORDMARK_LINES[1].padEnd(WORDMARK_WIDTH) + ' '.repeat(VERSION_GAP) + version
339
+ : WORDMARK_LINES[1],
340
+ WORDMARK_LINES[2],
341
+ modelLine ?? '',
342
+ directory ?? '',
343
+ ];
344
+ return withPaddingRows(face.map((line, index) => {
345
+ const right = rightLines[index] ?? '';
346
+ const padded = PAD + line;
347
+ // Pad the face out to the split column only when something follows it, so a row whose field
348
+ // was omitted (or truncated away) does not end in a run of spaces.
349
+ return { face: right ? padded.padEnd(RIGHT_COLUMN) : padded, right };
350
+ }));
351
+ }
352
+ /**
353
+ * Render the banner as one multi-line string for the plain (readline) surface.
354
+ *
355
+ * Colour is the 16-colour `magenta` slot from {@link ANSI_COLORS} — the SAME slot the rest of the
356
+ * CLI uses — and never a 256-colour or 24-bit escape: the 16-colour slot picks up the violet from
357
+ * the user's own terminal theme, so the sloth looks native in both light and dark schemes. The
358
+ * right column carries no escape at all, not even dim.
359
+ *
360
+ * With `colour: false` the output contains ZERO escape sequences — not one dim wrapper, not one
361
+ * reset — so a monochrome session (`useColour: false`) stays genuinely clean. The caller decides;
362
+ * the plain surface passes `getUseColour()`, the TUI passes nothing and lets Ink's own
363
+ * colour-support detection make the same call.
364
+ */
365
+ export function launchBannerText(input) {
366
+ return launchBannerRows(input)
367
+ .map(({ face, right }) => {
368
+ // A padding row is an empty line and nothing else: wrapping it in the colour pair would put
369
+ // two escapes on a row that has no glyph to paint, which the colour-off guarantee and the
370
+ // one-pair-per-row assertion both read as a bug.
371
+ if (!face && !right)
372
+ return '';
373
+ return input.colour
374
+ ? `${ANSI_COLORS.magenta}${face}${ANSI_COLORS.reset}${right}`
375
+ : face + right;
376
+ })
377
+ .join('\n');
378
+ }
379
+ /**
380
+ * Read the live fields off the process, so both surfaces report the same three values from the same
381
+ * sources instead of each picking its own. `model`/`provider` come from the caller because only it
382
+ * has the resolved config (`config.modelDisplayName` / `config.modelProviderType`); the rest are
383
+ * process-global.
384
+ *
385
+ * Fail-soft, as in `debugDump`: `getSlothVersion()` reads the installed `package.json` through the
386
+ * install dir that the CLI entry point sets, and THROWS when nothing set it (an embedded caller, a
387
+ * unit test). A missing version drops one label — it must never stop a session from starting.
388
+ */
389
+ export function launchBannerFields(model, provider) {
390
+ let version;
391
+ try {
392
+ version = getSlothVersion();
393
+ }
394
+ catch {
395
+ /* no install dir (embedded/test) — the banner simply shows no version */
396
+ }
397
+ return { version, model, provider, directory: getProjectDir(), homeDir: homedir() };
398
+ }
399
+ //# sourceMappingURL=launchBanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launchBanner.js","sourceRoot":"","sources":["../../src/core/launchBanner.ts"],"names":[],"mappings":"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"}
@@ -15,9 +15,12 @@
15
15
  * Stream discipline (matches how the plain surface prints tool activity today): the block is
16
16
  * emitted at INFO level through `displayToolIndication` — same stdout channel, same
17
17
  * `consoleLevel` gate and session-log treatment as the existing tool notices — so scripted
18
- * consumers that already silence INFO chatter silence this too. Colour is used only when
19
- * `useColour` is on AND stdout is a TTY; otherwise the block degrades to clean monochrome
20
- * (DL-7), with diff lines still readable via their `+`/`-` prefixes.
18
+ * consumers that already silence INFO chatter silence this too. Colour is used exactly when the
19
+ * resolved `useColour` (the CFG-30 ladder in `config/colour.ts`) says so TUI-C35 removed the
20
+ * local `&& stdout.isTTY` narrowing this module used to apply on top, which was redundant against
21
+ * the ladder's own rung-4 TTY auto-detection everywhere except `FORCE_COLOR` on a pipe, the one
22
+ * case that variable exists to serve. An ordinary piped run is therefore still clean monochrome
23
+ * (DL-7) — rung 4 decides that — with diff lines readable via their `+`/`-` prefixes.
21
24
  *
22
25
  * Live-output dedupe: shell-shaped results (`<COMMAND_OUTPUT>`) belong to tools whose child
23
26
  * output ALREADY streamed raw via the channel's default sink, so those render with
@@ -27,7 +30,7 @@
27
30
  import { AIMessage, AIMessageChunk, ToolMessage } from '@langchain/core/messages';
28
31
  import { buildToolPreviewLines, getToolGlyph, isShellShapedResult, renderToolLineAnsi, summariseToolCall, } from '#src/core/toolDisplay.js';
29
32
  import { displayToolIndication } from '#src/utils/consoleUtils.js';
30
- import { getUseColour, stdout } from '#src/utils/systemUtils.js';
33
+ import { getUseColour } from '#src/utils/systemUtils.js';
31
34
  const INDENT = ' ';
32
35
  /**
33
36
  * Create the per-stream observer. State is scoped to one stream (one `agent.stream()` call);
@@ -59,9 +62,12 @@ export function createPlainToolIndication(emit = displayToolIndication) {
59
62
  const name = tracked?.name || (typeof message.name === 'string' ? message.name : '') || '';
60
63
  const result = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
61
64
  const isError = message.status === 'error';
62
- // Colour only when configured AND stdout is a real terminal piped output stays clean
63
- // monochrome (unlike consoleUtils' blanket dim, this block is asserted non-TTY-clean).
64
- const colour = getUseColour() && !!stdout.isTTY;
65
+ // TUI-C35 colour is exactly what the resolved ladder says, with no local narrowing.
66
+ // This used to AND in `stdout.isTTY`, which was redundant in every case but one: rung 4 of
67
+ // `config/colour.ts` already auto-detects from stdout's TTY status, so an unconfigured piped
68
+ // run is monochrome either way. The one case it changed was `FORCE_COLOR` into a pipe — which
69
+ // it suppressed, defeating the only thing that variable is for.
70
+ const colour = getUseColour();
65
71
  const statusGlyph = isError
66
72
  ? colour
67
73
  ? '\x1b[31m✗\x1b[0m'
@@ -1 +1 @@
1
- {"version":3,"file":"plainToolIndication.js","sourceRoot":"","sources":["../../src/core/plainToolIndication.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plainToolIndication.js","sourceRoot":"","sources":["../../src/core/plainToolIndication.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAElF,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,MAAM,MAAM,GAAG,MAAM,CAAC;AActB;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAAI,GAA2B,qBAAqB;IAEpD,wFAAwF;IACxF,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IACrD,yEAAyE;IACzE,MAAM,IAAI,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEhD,MAAM,sBAAsB,GAAG,GAAS,EAAE;QACxC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,EAAE;gBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,SAAS,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,CAAC,OAAoB,EAAQ,EAAE;QACvD,MAAM,EAAE,GAAG,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9C,IAAI,EAAE;YAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,CAAC,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC3F,MAAM,MAAM,GACV,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1F,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC;QAC3C,sFAAsF;QACtF,2FAA2F;QAC3F,6FAA6F;QAC7F,8FAA8F;QAC9F,gEAAgE;QAChE,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAE9B,MAAM,WAAW,GAAG,OAAO;YACzB,CAAC,CAAC,MAAM;gBACN,CAAC,CAAC,kBAAkB;gBACpB,CAAC,CAAC,GAAG;YACP,CAAC,CAAC,MAAM;gBACN,CAAC,CAAC,kBAAkB;gBACpB,CAAC,CAAC,GAAG,CAAC;QACV,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,OAAO,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,MAAM,IAAI,GAAG,GAAG,WAAW,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;QAEnE,MAAM,OAAO,GAAG,qBAAqB,CAAC;YACpC,IAAI;YACJ,QAAQ,EAAE,OAAO,EAAE,QAAQ;YAC3B,MAAM;YACN,OAAO;YACP,oFAAoF;YACpF,qFAAqF;YACrF,yFAAyF;YACzF,8FAA8F;YAC9F,sBAAsB,EAAE,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9E,wFAAwF;QACxF,6BAA6B;QAC7B,IAAI,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO;QACL,OAAO,CAAC,KAAc;YACpB,iFAAiF;YACjF,gCAAgC;YAChC,IAAI,cAAc,CAAC,UAAU,CAAC,KAAoB,CAAC,EAAE,CAAC;gBACpD,0FAA0F;gBAC1F,4FAA4F;gBAC5F,wFAAwF;gBACxF,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,KAAuB,CAAC;oBAClC,MAAM,MAAM,GAAG,CAAC,CAAC,gBAAgB,IAAI,EAAE,CAAC;oBACxC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACtB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;4BAC3B,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;4BAChE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;4BACjE,IAAI,KAAK,CAAC,EAAE;gCAAE,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;4BAClC,IAAI,KAAK,CAAC,IAAI;gCAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;4BACtD,IAAI,KAAK,CAAC,IAAI;gCAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC;4BAC7C,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;wBAC9B,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,2EAA2E;wBAC3E,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;4BACpC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;gCACV,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;oCACd,EAAE,EAAE,EAAE,CAAC,EAAE;oCACT,IAAI,EAAE,EAAE,CAAC,IAAI;oCACb,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;iCACxC,CAAC,CAAC;4BACL,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,qEAAqE;gBACvE,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,SAAS,CAAC,UAAU,CAAC,KAAoB,CAAC,EAAE,CAAC;gBAC/C,qFAAqF;gBACrF,4EAA4E;gBAC5E,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,KAAkB,CAAC;oBAC7B,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;wBACpC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;4BACV,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;gCACd,EAAE,EAAE,EAAE,CAAC,EAAE;gCACT,IAAI,EAAE,EAAE,CAAC,IAAI;gCACb,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;6BACxC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,qEAAqE;gBACvE,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;gBACjC,oFAAoF;gBACpF,sFAAsF;gBACtF,WAAW;gBACX,IAAI,CAAC;oBACH,sBAAsB,EAAE,CAAC;oBACzB,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;gBAAC,MAAM,CAAC;oBACP,qEAAqE;gBACvE,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}