@sagmans/dsh-tui 0.1.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.
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/cordis.patch.yml +123 -0
- package/lib/agent/fork.d.ts +24 -0
- package/lib/agent/fork.d.ts.map +1 -0
- package/lib/agent/fork.js +25 -0
- package/lib/agent/fork.js.map +1 -0
- package/lib/agent/history.d.ts +78 -0
- package/lib/agent/history.d.ts.map +1 -0
- package/lib/agent/history.js +179 -0
- package/lib/agent/history.js.map +1 -0
- package/lib/agent/host.d.ts +58 -0
- package/lib/agent/host.d.ts.map +1 -0
- package/lib/agent/host.js +59 -0
- package/lib/agent/host.js.map +1 -0
- package/lib/agent/model.d.ts +61 -0
- package/lib/agent/model.d.ts.map +1 -0
- package/lib/agent/model.js +92 -0
- package/lib/agent/model.js.map +1 -0
- package/lib/agent/present.d.ts +12 -0
- package/lib/agent/present.d.ts.map +1 -0
- package/lib/agent/present.js +52 -0
- package/lib/agent/present.js.map +1 -0
- package/lib/agent/presets.d.ts +75 -0
- package/lib/agent/presets.d.ts.map +1 -0
- package/lib/agent/presets.js +117 -0
- package/lib/agent/presets.js.map +1 -0
- package/lib/agent/projections.d.ts +16 -0
- package/lib/agent/projections.d.ts.map +1 -0
- package/lib/agent/projections.js +31 -0
- package/lib/agent/projections.js.map +1 -0
- package/lib/agent/status.d.ts +46 -0
- package/lib/agent/status.d.ts.map +1 -0
- package/lib/agent/status.js +67 -0
- package/lib/agent/status.js.map +1 -0
- package/lib/cards.d.ts +88 -0
- package/lib/cards.d.ts.map +1 -0
- package/lib/cards.js +218 -0
- package/lib/cards.js.map +1 -0
- package/lib/compat/probe.d.ts +33 -0
- package/lib/compat/probe.d.ts.map +1 -0
- package/lib/compat/probe.js +74 -0
- package/lib/compat/probe.js.map +1 -0
- package/lib/config.d.ts +13 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +49 -0
- package/lib/config.js.map +1 -0
- package/lib/contracts.d.ts +32 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +2 -0
- package/lib/contracts.js.map +1 -0
- package/lib/export.d.ts +15 -0
- package/lib/export.d.ts.map +1 -0
- package/lib/export.js +52 -0
- package/lib/export.js.map +1 -0
- package/lib/gates.d.ts +85 -0
- package/lib/gates.d.ts.map +1 -0
- package/lib/gates.js +216 -0
- package/lib/gates.js.map +1 -0
- package/lib/identity.d.ts +33 -0
- package/lib/identity.d.ts.map +1 -0
- package/lib/identity.js +49 -0
- package/lib/identity.js.map +1 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +1196 -0
- package/lib/index.js.map +1 -0
- package/lib/input/completion.d.ts +22 -0
- package/lib/input/completion.d.ts.map +1 -0
- package/lib/input/completion.js +27 -0
- package/lib/input/completion.js.map +1 -0
- package/lib/input/submission.d.ts +62 -0
- package/lib/input/submission.d.ts.map +1 -0
- package/lib/input/submission.js +80 -0
- package/lib/input/submission.js.map +1 -0
- package/lib/jobs.d.ts +64 -0
- package/lib/jobs.d.ts.map +1 -0
- package/lib/jobs.js +123 -0
- package/lib/jobs.js.map +1 -0
- package/lib/startup.d.ts +17 -0
- package/lib/startup.d.ts.map +1 -0
- package/lib/startup.js +67 -0
- package/lib/startup.js.map +1 -0
- package/lib/subagents.d.ts +74 -0
- package/lib/subagents.d.ts.map +1 -0
- package/lib/subagents.js +141 -0
- package/lib/subagents.js.map +1 -0
- package/lib/terminal/bell.d.ts +18 -0
- package/lib/terminal/bell.d.ts.map +1 -0
- package/lib/terminal/bell.js +16 -0
- package/lib/terminal/bell.js.map +1 -0
- package/lib/terminal/clipboard.d.ts +11 -0
- package/lib/terminal/clipboard.d.ts.map +1 -0
- package/lib/terminal/clipboard.js +18 -0
- package/lib/terminal/clipboard.js.map +1 -0
- package/lib/terminal/restore.d.ts +17 -0
- package/lib/terminal/restore.d.ts.map +1 -0
- package/lib/terminal/restore.js +43 -0
- package/lib/terminal/restore.js.map +1 -0
- package/lib/terminal/title.d.ts +22 -0
- package/lib/terminal/title.d.ts.map +1 -0
- package/lib/terminal/title.js +34 -0
- package/lib/terminal/title.js.map +1 -0
- package/lib/text.d.ts +19 -0
- package/lib/text.d.ts.map +1 -0
- package/lib/text.js +61 -0
- package/lib/text.js.map +1 -0
- package/lib/theme.d.ts +47 -0
- package/lib/theme.d.ts.map +1 -0
- package/lib/theme.js +84 -0
- package/lib/theme.js.map +1 -0
- package/lib/transcript.d.ts +91 -0
- package/lib/transcript.d.ts.map +1 -0
- package/lib/transcript.js +315 -0
- package/lib/transcript.js.map +1 -0
- package/lib/ui/dock.d.ts +36 -0
- package/lib/ui/dock.d.ts.map +1 -0
- package/lib/ui/dock.js +110 -0
- package/lib/ui/dock.js.map +1 -0
- package/lib/ui/markdown.d.ts +17 -0
- package/lib/ui/markdown.d.ts.map +1 -0
- package/lib/ui/markdown.js +35 -0
- package/lib/ui/markdown.js.map +1 -0
- package/lib/ui/picker.d.ts +103 -0
- package/lib/ui/picker.d.ts.map +1 -0
- package/lib/ui/picker.js +165 -0
- package/lib/ui/picker.js.map +1 -0
- package/lib/ui/rows.d.ts +30 -0
- package/lib/ui/rows.d.ts.map +1 -0
- package/lib/ui/rows.js +49 -0
- package/lib/ui/rows.js.map +1 -0
- package/lib/ui/status.d.ts +49 -0
- package/lib/ui/status.d.ts.map +1 -0
- package/lib/ui/status.js +90 -0
- package/lib/ui/status.js.map +1 -0
- package/lib/ui/view.d.ts +56 -0
- package/lib/ui/view.d.ts.map +1 -0
- package/lib/ui/view.js +184 -0
- package/lib/ui/view.js.map +1 -0
- package/lib/work.d.ts +39 -0
- package/lib/work.d.ts.map +1 -0
- package/lib/work.js +97 -0
- package/lib/work.js.map +1 -0
- package/package.json +109 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sagmans
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# @sagmans/dsh-tui
|
|
2
|
+
|
|
3
|
+
Interactive terminal (TUI) surface for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): use `dsh` in a terminal instead of a browser.
|
|
4
|
+
|
|
5
|
+
Status: **v1 feature-complete, first release pending.** The surface owns the alternate screen, streams assistant text as markdown, renders every tool's own card, answers approvals and questions, restores and names stored conversations, switches model mid-session, runs any of the four shipped agent modes and switches between them before a session's first turn, reads a child agent's conversation in place, keeps the goal, plan mode, todo list, delegations, and background jobs above the editor with a status line below it, and hands the terminal back on every graceful exit. Publication is tag-driven with GitHub OIDC provenance and no stored npm token; see [RELEASE.md](RELEASE.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
A profile keeps this plugin as one bundle layer. Install it from a checkout of this repository, or from the registry once a release is published. Both paths need Node.js >= 22.19 and `pnpm` on `PATH`. Both need a real terminal: stdin and stdout must be TTYs.
|
|
10
|
+
|
|
11
|
+
### From a plugin checkout
|
|
12
|
+
|
|
13
|
+
A linked profile loads the package's built entry point. Build the checkout before you add it:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
cd "$PLUGIN_CHECKOUT"
|
|
17
|
+
CI=true pnpm install && pnpm run build
|
|
18
|
+
dsh plugin --profile tui add "$PWD"
|
|
19
|
+
dsh --profile tui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The `add` command creates the `tui` profile on first use and records a link to the directory. Keep the checkout in place. If you move or delete it, the link breaks, and a later `plugin install` removes the bundle from the layer list (see Troubleshooting).
|
|
23
|
+
|
|
24
|
+
### From the registry
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
dsh plugin --profile tui add @sagmans/dsh-tui@latest
|
|
28
|
+
dsh --profile tui
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This path needs a published release. No release is published yet, so use a checkout until then.
|
|
32
|
+
|
|
33
|
+
### Confirm the plugin mounted
|
|
34
|
+
|
|
35
|
+
The profile records its layers in `$DSH_HOME/profiles/tui/package.json` (`~/.dsh` by default). `@sagmans/dsh-tui` must appear in `dsh.profile.bundles`:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
node -p "require((process.env.DSH_HOME ?? require('node:os').homedir() + '/.dsh') + '/profiles/tui/package.json').dsh.profile.bundles.join('\n')"
|
|
39
|
+
# @deepseek-ai/dsh-base
|
|
40
|
+
# @sagmans/dsh-tui
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then check that the surface is mounted. A pipe is not a terminal, so this command must refuse before it takes the screen over:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
echo hi | dsh --profile tui
|
|
47
|
+
# dsh-tui: both stdin and stdout must be TTYs; run this profile from a terminal or SSH session
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Update
|
|
51
|
+
|
|
52
|
+
Rebuild a linked checkout, then start the next session. The link itself does not change:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
cd "$PLUGIN_CHECKOUT" && git pull && CI=true pnpm install && pnpm run build
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A registry install updates with `dsh plugin --profile tui update @sagmans/dsh-tui`.
|
|
59
|
+
|
|
60
|
+
### Remove
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
dsh plugin --profile tui remove @sagmans/dsh-tui
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The profile then keeps `@deepseek-ai/dsh-base` and no application, so `dsh --profile tui` waits with no output. Add the plugin again to use the profile.
|
|
67
|
+
|
|
68
|
+
### Launching from a harness checkout
|
|
69
|
+
|
|
70
|
+
`pnpm dsh --profile tui` is the sanctioned launcher, but pnpm verifies that dependencies are current before it runs any script, and a checkout whose `postinstall` refuses to take over a user-owned `core.hooksPath` fails that check — the process exits before the surface starts. Any of these reaches the surface:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
pnpm --config.verify-deps-before-run=false dsh --profile tui # from the checkout
|
|
74
|
+
CI=true pnpm dsh --profile tui # also suppresses the check
|
|
75
|
+
node "$CHECKOUT/apps/cli/lib/bin.js" --profile tui # needs neither pnpm nor the check
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Troubleshooting
|
|
79
|
+
|
|
80
|
+
Two facts explain most failures.
|
|
81
|
+
|
|
82
|
+
**A linked profile is a link, not a copy.** The profile points at a directory, so a checkout that moves or disappears breaks it.
|
|
83
|
+
|
|
84
|
+
**`dsh plugin install` removes a bundle it cannot resolve, and says nothing.** The command reconciles `dsh.profile.bundles` against the installed dependencies. A bundle whose path does not resolve leaves the list, and the command still exits 0. The next launch composes `@deepseek-ai/dsh-base` alone. No application plugin mounts, so nothing reads the command line: `dsh --profile tui` then prints nothing and never exits, and `--help` waits with it.
|
|
85
|
+
|
|
86
|
+
| Symptom | Cause | Fix |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `dsh: cannot resolve profile bundle "@sagmans/dsh-tui" ...` | the linked checkout moved or was deleted | `dsh plugin --profile tui add "$PLUGIN_CHECKOUT"` |
|
|
89
|
+
| `dsh --profile tui` prints nothing and never exits | the bundle left `dsh.profile.bundles`, usually after a broken link and a `plugin install` | confirm the layer list, then run the `add` command again |
|
|
90
|
+
| `dsh-tui: both stdin and stdout must be TTYs` | stdin or stdout is a pipe, a file, or a CI runner | run the command from a terminal |
|
|
91
|
+
| Changes under `src/` have no effect | a linked profile loads `lib/`, not `src/` | `pnpm run build` in the plugin checkout |
|
|
92
|
+
| `pnpm dsh --profile tui` exits before the surface appears | pnpm's dependency check fails on the harness checkout's own postinstall | see [Launching from a harness checkout](#launching-from-a-harness-checkout) |
|
|
93
|
+
| `--preset <id>` is refused, because the session's agent preset is fixed | a session keeps the mode that composed it, and this session already took a turn | `/preset <id>` before the first turn, or resume without `--preset` |
|
|
94
|
+
| `--resume <id>` starts a new session | the id is a bare UUID | pass the stored id, `tui-session-…` included; a bare `--resume` opens the picker |
|
|
95
|
+
| `dsh: profile "tui" does not exist` | the profile is not created yet | the `add` command creates it |
|
|
96
|
+
|
|
97
|
+
The full recovery from a broken link:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
cd "$PLUGIN_CHECKOUT" && CI=true pnpm install && pnpm run build
|
|
101
|
+
dsh plugin --profile tui add "$PWD"
|
|
102
|
+
node -p "require((process.env.DSH_HOME ?? require('node:os').homedir() + '/.dsh') + '/profiles/tui/package.json').dsh.profile.bundles.join('\n')"
|
|
103
|
+
# @deepseek-ai/dsh-base
|
|
104
|
+
# @sagmans/dsh-tui
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
dsh --profile tui # new session in the current directory
|
|
111
|
+
dsh --profile tui --resume # pick a stored session, titled by its first prompt
|
|
112
|
+
dsh --profile tui --resume <session-id>
|
|
113
|
+
dsh --profile tui --preset ptc # start in one of the shipped agent modes
|
|
114
|
+
dsh --profile tui --model deepseek-chat
|
|
115
|
+
dsh --profile tui --no-color
|
|
116
|
+
dsh --profile tui --no-bell # do not ring when a long turn finishes
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Key | Action |
|
|
120
|
+
|---|---|
|
|
121
|
+
| Enter | submit the prompt |
|
|
122
|
+
| Ctrl+C | interrupt the running turn, or leave when idle |
|
|
123
|
+
| Ctrl+O | show every line of the tool cards instead of their preview |
|
|
124
|
+
| Ctrl+T | show the reasoning behind an answer instead of its summary |
|
|
125
|
+
| `y` / `n` / Esc | allow once, reject, or cancel a pending approval |
|
|
126
|
+
| digits / space / ↑↓ / Enter / Esc | answer a question: pick or toggle, confirm, or skip one |
|
|
127
|
+
| `/` then Tab | complete commands, including every command this session registered |
|
|
128
|
+
| `@` or a path then Tab | complete workspace file references |
|
|
129
|
+
| `ctrl+shift+f` | search the transcript (`enter` next, `shift+enter` previous, `esc` close) |
|
|
130
|
+
| `home` / `end` | jump to the start or the end of the transcript |
|
|
131
|
+
| `ctrl+down` | jump to the next prompt |
|
|
132
|
+
| `ctrl+b` | leave a child's conversation and return to this session |
|
|
133
|
+
| mouse wheel, drag | scroll, and copy a selection through OSC 52 |
|
|
134
|
+
| `/help` | list registered and local commands |
|
|
135
|
+
| `/status` | show the session id, model, permissions, context, and directory |
|
|
136
|
+
| `/model` | show the route the next step will use, and the providers available |
|
|
137
|
+
| `/model <provider>` | list that provider's advertised models |
|
|
138
|
+
| `/model <provider>/<model>` | use that route from the next step on (session only, nothing is written to settings) |
|
|
139
|
+
| `/preset` | pick the agent mode for this session from the roster |
|
|
140
|
+
| `/preset <id>` | switch to that mode, while the session is still blank |
|
|
141
|
+
| `/jobs` | list background jobs with their state and duration |
|
|
142
|
+
| `/jobs read <id>` / `/jobs kill <id>` | show the tail of a job's output, or stop it |
|
|
143
|
+
| `/subagents` | list the delegations this session started, with their provider and age |
|
|
144
|
+
| `/subagents open <id\|last>` | read a child's own conversation in place; `ctrl+b` comes back |
|
|
145
|
+
| `/subagents kill <id>` | stop a live child agent |
|
|
146
|
+
| `/fork [title]` | branch this conversation after its last completed turn and continue in the branch |
|
|
147
|
+
| `/rename <title>` | title this session; the picker shows it instead of the session id |
|
|
148
|
+
| `/export [path]` | write the visible transcript as markdown (default `dsh-session-<id>.md`) |
|
|
149
|
+
| `/resume` | open another stored session without leaving the terminal |
|
|
150
|
+
| `/clear` | clear the visible transcript |
|
|
151
|
+
| `/quit` | leave and print the resume command |
|
|
152
|
+
|
|
153
|
+
Any other `/command` goes to the command registry, so `/plan`, `/compact`, `/goal`, and `/feedback` behave as they do on the other surfaces.
|
|
154
|
+
|
|
155
|
+
## Modes
|
|
156
|
+
|
|
157
|
+
A mode is an **agent preset**: the plugin composition an agent's own scope joins. It decides that agent's tools, prompt sections, skills, and planning rows, which is why a mode is fixed once a session has produced a turn — it is what composed the agent that answered.
|
|
158
|
+
|
|
159
|
+
Four ship, under the ids a session log records:
|
|
160
|
+
|
|
161
|
+
| `--preset` | Mode | What the agent gets |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| `standard` | standard | full agent: editing, shell, search, skills, planning, goals, subagents, workflows |
|
|
164
|
+
| `ptc` | PTC | the same agent, reaching its tools through one TypeScript program |
|
|
165
|
+
| `minimal` | minimal | one tool: a persistent shell |
|
|
166
|
+
| `cordis` | creator | harness authoring: runtime inspection and composition guidance |
|
|
167
|
+
|
|
168
|
+
A session takes its mode from the first of these that applies:
|
|
169
|
+
|
|
170
|
+
1. `--preset <id>`, refused before the terminal is taken over when the roster does not ship that id.
|
|
171
|
+
2. `/preset` while the session is still blank: a bare command opens the picker, `/preset <id>` switches directly, and the choice is written to the log.
|
|
172
|
+
3. The roster's default, `standard`, when nobody names one.
|
|
173
|
+
|
|
174
|
+
The mode is re-read rather than remembered: resuming mounts what that session's own log recorded, resuming with a `--preset` that disagrees with it is refused instead of silently ignored, and forking inherits the mode of the conversation being branched. The status line names the mode, and `/status` lists it with the rest.
|
|
175
|
+
|
|
176
|
+
## How it works
|
|
177
|
+
|
|
178
|
+
The package is a Cordis plugin bundle that stacks over `@deepseek-ai/dsh-base`:
|
|
179
|
+
|
|
180
|
+
- `@sagmans/dsh-tui/startup` parses this app's own flags and publishes the launch identity.
|
|
181
|
+
- `@deepseek-ai/dsh-agent-presets` is the roster of modes, holding the id a session starts in when nobody names one.
|
|
182
|
+
- `@deepseek-ai/dsh-code-runtime-worker-thread` and `@deepseek-ai/dsh-cordis-host-runner` are the host machinery PTC mode and creator mode need; only the Web bundle shipped them, so a terminal profile has to mount them to offer those modes at all.
|
|
183
|
+
- `@sagmans/dsh-tui` owns the terminal: it creates or resumes one agent through `ctx.agents`, folds `session/event` into transcript rows and work state, renders them with `@earendil-works/pi-tui`, and releases the terminal on exit, on a boot failure, and on a signal.
|
|
184
|
+
|
|
185
|
+
The fold is durable-only: the live stream decorates the row that is still being written, and everything else — cards, reasoning, work state, compaction markers — comes from the log, so a resumed session renders what the live one did. Subagent start and finish are the exception: they arrive as service events, and the transcript shows them as decoration because the durable record of a delegation is the tool call that asked for it.
|
|
186
|
+
|
|
187
|
+
The bundle also takes the base's global agent rows out of the composition, twenty-three of them. Every one is a row the shipped modes supply per session instead, so leaving it mounted registers the same tool names in two layers and doubles each prompt section it owns. What stays mounted is the host: sessions, storage, models, permissions, jobs, and the command registry.
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
pnpm install
|
|
193
|
+
pnpm run typecheck
|
|
194
|
+
pnpm test
|
|
195
|
+
pnpm run build
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**A linked profile loads the built entry point**, so edits under `src/` are invisible to `dsh --profile tui` until `pnpm run build` runs. Drive the real surface end to end — it rebuilds first, allocates a PTY, sends a prompt, and prints what the screen showed:
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
node tools/pty-drive.mjs --prompt 'Reply with exactly: pong'
|
|
202
|
+
node tools/pty-drive.mjs --prompt 'Run: echo hi' --approve 20 # answer the approval gate
|
|
203
|
+
node tools/pty-drive.mjs --home /tmp/scratch-home --seconds 20 # no credentials: proves failures are visible
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Keep verification off your real home: install the profile into a throwaway one and copy only the credentials it needs.
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
S=$(mktemp -d)
|
|
210
|
+
cp ~/.dsh/.credentials.yaml ~/.dsh/settings.yaml "$S/" && chmod 600 "$S"/*.yaml
|
|
211
|
+
DSH_HOME="$S" dsh plugin --profile tui add "$PWD"
|
|
212
|
+
node tools/pty-drive.mjs --home "$S" --prompt 'Reply with exactly: pong'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Test specs import plugin sources through the `@/` alias. Under this test runner the spec file is resolved with a root-relative id, so parent-relative imports (`../src/...`) do not resolve; the alias and its matching `tsconfig.test.json` path mapping avoid that.
|
|
216
|
+
|
|
217
|
+
## Manual acceptance
|
|
218
|
+
|
|
219
|
+
The automated checks drive a real PTY, but they run on this machine's terminal. These are the checks only a terminal on your desk can answer; each line is what to do and what it should look like.
|
|
220
|
+
|
|
221
|
+
| Check | Expected |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `DSH_HOME=$(mktemp -d) dsh plugin --profile tui add "$PWD"` from a built checkout | the profile is created, `dsh.profile.bundles` lists the plugin, and `dsh --profile tui` reaches a prompt |
|
|
224
|
+
| the same over SSH | the interface arrives intact; keys and mouse work on the host, with no local echo doubling |
|
|
225
|
+
| inside tmux or screen | wheel scroll and `ctrl+shift+f` search work; dragging selects text |
|
|
226
|
+
| a light terminal and a dark one | the interface follows the terminal's own palette; nothing becomes unreadable |
|
|
227
|
+
| `NO_COLOR=1 dsh --profile tui` | no styling anywhere, layout unchanged |
|
|
228
|
+
| `dsh --profile tui --no-bell` | a turn that runs for minutes still ends silently |
|
|
229
|
+
| `dsh --profile tui --preset ptc`, then a turn | the status line names `ptc`, and the agent reaches its tools through one TypeScript program rather than one shell call at a time |
|
|
230
|
+
| `/preset` on a fresh session | the picker lists four modes, marks the current one, and the switch survives a resume |
|
|
231
|
+
| `/preset minimal` after a turn | refused, naming the reason; the session keeps the mode it composed with |
|
|
232
|
+
| `--resume --preset <mode>` and then picking a session that runs another mode | the list stays open and says why that row cannot be taken; `esc` leaves the picker |
|
|
233
|
+
| `dsh --profile tui --preset nope` | exits non-zero naming the modes that do exist, before the alternate screen appears |
|
|
234
|
+
| arrow keys in a picker, or on a question's options, in a terminal that reports key events (Kitty, WezTerm, Ghostty, iTerm2) | one press moves one row, and holding a key still repeats; a terminal that sends only the legacy sequence behaves the same |
|
|
235
|
+
| resize the window mid-turn | the transcript rewraps; the dock, editor, and status row stay put |
|
|
236
|
+
| a 40-column terminal | rows end in `…` instead of wrapping into the next line |
|
|
237
|
+
| `echo hi \| dsh --profile tui` | refuses with a non-zero exit and a message naming the TTY requirement |
|
|
238
|
+
| `/quit`, Ctrl+C while idle, `kill -TERM <pid>` | the shell returns with cursor, echo, mouse, and title restored |
|
|
239
|
+
|
|
240
|
+
## Releasing
|
|
241
|
+
|
|
242
|
+
Published artefacts carry a provenance attestation, which only a CI provider can issue, so releases ship from the tag workflow rather than a laptop.
|
|
243
|
+
|
|
244
|
+
1. Bump `version` in `package.json` and commit it.
|
|
245
|
+
2. `git tag v<version> && git push origin v<version>`.
|
|
246
|
+
3. `.github/workflows/release.yml` re-runs typecheck, tests, and the package smoke, then publishes with `--provenance`.
|
|
247
|
+
|
|
248
|
+
The workflow needs an `NPM_TOKEN` repository secret with publish rights for the `@sagmans` scope. To publish by hand instead, run `pnpm publish --access public --provenance` from a clean checkout of the tag.
|
|
249
|
+
|
|
250
|
+
## Limitations
|
|
251
|
+
|
|
252
|
+
- Two different things are called a preset. The agent mode (`--preset`, `/preset`) is fixed once a session has produced a turn; the permission preset (`/permission <preset>`, named in the status line) can change at any time.
|
|
253
|
+
- `/model` changes the route for the running session only. Catalog membership is advisory — an adapter may accept an id it does not advertise.
|
|
254
|
+
- Scrolling is the mouse wheel, or the terminal's own scrollback keys where it offers them.
|
|
255
|
+
- A turn that ran longer than ten seconds rings the terminal bell when it ends, because the reader may have walked away; `--no-bell` turns that off.
|
|
256
|
+
- The dock shows the goal, plan mode, the todo list, and any background job still running; the transcript marks where older history was compacted away. `/plan` toggles plan mode; `/plan <message>` also steers that message, which is the base command's own behaviour.
|
|
257
|
+
- Background jobs and subagent runs are live process state, not durable events: they disappear when the run ends, and a resumed session starts with an empty board and roster.
|
|
258
|
+
- Reading a child's conversation does not move the terminal: commands, approvals, and the status line stay with the session you launched, and the transcript is the only thing that switches.
|
|
259
|
+
- Delete is unimplemented: the session store exposes no delete, and the surface does not reach around that seam into its files. `/fork` covers the case that needs it — it branches into a new session and leaves the original alone.
|
|
260
|
+
- Approvals and questions render inline and take the keyboard; a question batch is answered in order.
|
|
261
|
+
- Styling uses the standard 16 ANSI colors and terminal defaults, so light and dark terminals follow their own theme.
|
|
262
|
+
- Tool text, model text, and file content are escaped before rendering, so a hostile result cannot inject terminal control sequences; the cost is that a literal tab shows as \x09.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# dsh-tui — the terminal agent plane, over dsh-base.
|
|
2
|
+
#
|
|
3
|
+
# Every row this bundle INSERTS is owned by the plugin: no row names a package
|
|
4
|
+
# the plugin does not depend on. The rows it DISABLES are the base's global
|
|
5
|
+
# agent rows, because from here on an agent's tools, prompt sections, skills,
|
|
6
|
+
# and planning rows come from the agent preset its own session mounts — the
|
|
7
|
+
# composition the Web bundle composes for the same reason. Leaving them mounted
|
|
8
|
+
# would register the same tool names in two layers, double every prompt section
|
|
9
|
+
# and command they own, and provide the same services twice, which fails.
|
|
10
|
+
#
|
|
11
|
+
# The ids are exactly the base rows the shipped presets supply: standard, ptc,
|
|
12
|
+
# and cordis provide every one of them, while minimal supplies its own
|
|
13
|
+
# persistent-terminal set instead, which is the point of that mode.
|
|
14
|
+
- id: agent-instructions
|
|
15
|
+
disabled: true
|
|
16
|
+
|
|
17
|
+
- id: command-compact
|
|
18
|
+
disabled: true
|
|
19
|
+
|
|
20
|
+
- id: command-goal
|
|
21
|
+
disabled: true
|
|
22
|
+
|
|
23
|
+
- id: compaction-basic
|
|
24
|
+
disabled: true
|
|
25
|
+
|
|
26
|
+
- id: plan-mode
|
|
27
|
+
disabled: true
|
|
28
|
+
|
|
29
|
+
- id: skill-filesystem
|
|
30
|
+
disabled: true
|
|
31
|
+
|
|
32
|
+
- id: tool-bash
|
|
33
|
+
disabled: true
|
|
34
|
+
|
|
35
|
+
- id: tool-fs
|
|
36
|
+
disabled: true
|
|
37
|
+
|
|
38
|
+
- id: tool-fs-search
|
|
39
|
+
disabled: true
|
|
40
|
+
|
|
41
|
+
- id: tool-goal
|
|
42
|
+
disabled: true
|
|
43
|
+
|
|
44
|
+
- id: tool-jobs
|
|
45
|
+
disabled: true
|
|
46
|
+
|
|
47
|
+
- id: tool-pwsh
|
|
48
|
+
disabled: true
|
|
49
|
+
|
|
50
|
+
- id: tool-ralph
|
|
51
|
+
disabled: true
|
|
52
|
+
|
|
53
|
+
- id: tool-result-pruner
|
|
54
|
+
disabled: true
|
|
55
|
+
|
|
56
|
+
- id: tool-skill
|
|
57
|
+
disabled: true
|
|
58
|
+
|
|
59
|
+
- id: tool-subagent
|
|
60
|
+
disabled: true
|
|
61
|
+
|
|
62
|
+
- id: tool-subagent-control
|
|
63
|
+
disabled: true
|
|
64
|
+
|
|
65
|
+
- id: tool-subagent-fork
|
|
66
|
+
disabled: true
|
|
67
|
+
|
|
68
|
+
- id: tool-subagent-list-agents
|
|
69
|
+
disabled: true
|
|
70
|
+
|
|
71
|
+
- id: tool-todo
|
|
72
|
+
disabled: true
|
|
73
|
+
|
|
74
|
+
- id: tool-web
|
|
75
|
+
disabled: true
|
|
76
|
+
|
|
77
|
+
- id: tool-workflow
|
|
78
|
+
disabled: true
|
|
79
|
+
|
|
80
|
+
- id: workflow-worker-thread
|
|
81
|
+
disabled: true
|
|
82
|
+
|
|
83
|
+
- insert:
|
|
84
|
+
- id: tui-startup
|
|
85
|
+
name: '@sagmans/dsh-tui/startup'
|
|
86
|
+
|
|
87
|
+
- id: tui
|
|
88
|
+
name: '@sagmans/dsh-tui'
|
|
89
|
+
inject: [tuiStartup]
|
|
90
|
+
config:
|
|
91
|
+
sessionId: !!js ctx.tuiStartup.sessionId
|
|
92
|
+
resume: !!js ctx.tuiStartup.resume
|
|
93
|
+
resumePicker: !!js ctx.tuiStartup.resumePicker
|
|
94
|
+
model: !!js ctx.tuiStartup.model
|
|
95
|
+
provider: !!js ctx.tuiStartup.provider
|
|
96
|
+
preset: !!js ctx.tuiStartup.preset
|
|
97
|
+
color: !!js ctx.tuiStartup.color
|
|
98
|
+
bell: !!js ctx.tuiStartup.bell
|
|
99
|
+
|
|
100
|
+
# The roster of agent compositions, and the mode a session starts in when
|
|
101
|
+
# nobody names one. A profile patch may override this row's config.
|
|
102
|
+
- id: agent-presets
|
|
103
|
+
name: '@deepseek-ai/dsh-agent-presets'
|
|
104
|
+
config:
|
|
105
|
+
default: standard
|
|
106
|
+
|
|
107
|
+
# A preset's subagent tool samples its delegation model from this host-owned
|
|
108
|
+
# settings row. It is host machinery rather than agent plane, which is why
|
|
109
|
+
# the Web bundle inserts it too instead of leaving it to the preset.
|
|
110
|
+
- id: subagent-model-selection-settings
|
|
111
|
+
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
|
|
112
|
+
|
|
113
|
+
# Creator mode inspects and re-composes the running harness, which needs the
|
|
114
|
+
# host half of the dynamic-plugin runner. Only the Web bundle ships this
|
|
115
|
+
# row, so a terminal profile has to mount it to offer that mode at all.
|
|
116
|
+
- id: cordis-host-runner
|
|
117
|
+
name: '@deepseek-ai/dsh-cordis-host-runner'
|
|
118
|
+
|
|
119
|
+
# PTC mode runs its programs against a code runtime. The Web bundle mounts
|
|
120
|
+
# this at host level for the same reason: the runtime is machinery, while
|
|
121
|
+
# the preset decides whether an agent uses it.
|
|
122
|
+
- id: code-runtime
|
|
123
|
+
name: '@deepseek-ai/dsh-code-runtime-worker-thread'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** One durable event, as much of it as a fork decision needs. */
|
|
2
|
+
export interface ForkEvent {
|
|
3
|
+
readonly type: string;
|
|
4
|
+
readonly data?: unknown;
|
|
5
|
+
readonly seq?: number;
|
|
6
|
+
}
|
|
7
|
+
/** Where a fork may branch, and how much of the log the child inherits. */
|
|
8
|
+
export interface ForkPoint {
|
|
9
|
+
/** Events the child inherits, counted from the start of the log. */
|
|
10
|
+
readonly inheritedEvents: number;
|
|
11
|
+
/** Seq of the turn/end the cut is anchored to. */
|
|
12
|
+
readonly boundarySeq: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where a session may be branched.
|
|
16
|
+
*
|
|
17
|
+
* A turn is the unit a conversation can be cut at: an open turn, an open step,
|
|
18
|
+
* or a tool call without its result would leave the child holding half an
|
|
19
|
+
* exchange. The cut is therefore anchored to the last completed turn and
|
|
20
|
+
* extended to the next `turn/start`, which keeps the trailing bookkeeping of
|
|
21
|
+
* that turn — this is the same boundary the host's own fork uses.
|
|
22
|
+
*/
|
|
23
|
+
export declare function forkPoint(events: readonly ForkEvent[]): ForkPoint | undefined;
|
|
24
|
+
//# sourceMappingURL=fork.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../../src/agent/fork.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,2EAA2E;AAC3E,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,GAAG,SAAS,CAY7E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a session may be branched.
|
|
3
|
+
*
|
|
4
|
+
* A turn is the unit a conversation can be cut at: an open turn, an open step,
|
|
5
|
+
* or a tool call without its result would leave the child holding half an
|
|
6
|
+
* exchange. The cut is therefore anchored to the last completed turn and
|
|
7
|
+
* extended to the next `turn/start`, which keeps the trailing bookkeeping of
|
|
8
|
+
* that turn — this is the same boundary the host's own fork uses.
|
|
9
|
+
*/
|
|
10
|
+
export function forkPoint(events) {
|
|
11
|
+
let boundary = -1;
|
|
12
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
13
|
+
if (events[index]?.type === 'turn/end') {
|
|
14
|
+
boundary = index;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (boundary < 0)
|
|
19
|
+
return undefined;
|
|
20
|
+
let cut = boundary + 1;
|
|
21
|
+
while (cut < events.length && events[cut]?.type !== 'turn/start')
|
|
22
|
+
cut += 1;
|
|
23
|
+
return { inheritedEvents: cut, boundarySeq: events[boundary]?.seq ?? boundary };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.js","sourceRoot":"","sources":["../../src/agent/fork.ts"],"names":[],"mappings":"AAeA;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,MAA4B;IACpD,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAA;IACjB,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3D,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;YACvC,QAAQ,GAAG,KAAK,CAAA;YAChB,MAAK;QACP,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAClC,IAAI,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAA;IACtB,OAAO,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,KAAK,YAAY;QAAE,GAAG,IAAI,CAAC,CAAA;IAC1E,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAA;AACjF,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
/** One stored session as the picker shows it. */
|
|
3
|
+
export interface StoredSession {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly cwd: string | undefined;
|
|
6
|
+
readonly createdAt: number;
|
|
7
|
+
readonly eventCount: number | undefined;
|
|
8
|
+
}
|
|
9
|
+
/** The persisted header fields this surface reads besides the list columns. */
|
|
10
|
+
export interface StoredHeader {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
/** Preset the session STARTED with; a later selection is a log event. */
|
|
13
|
+
readonly agentPreset: string | undefined;
|
|
14
|
+
readonly eventCount: number | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** One stored event, reduced to what the transcript fold reads. */
|
|
17
|
+
export interface StoredEvent {
|
|
18
|
+
readonly type: string;
|
|
19
|
+
readonly data?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** The part of the persistence seam this surface uses, described structurally. */
|
|
22
|
+
export interface SessionHistory {
|
|
23
|
+
/** Newest sessions first, at most `limit` of them. */
|
|
24
|
+
list(limit: number): Promise<readonly StoredSession[]>;
|
|
25
|
+
/** Read a slice of a session's log; `limit` bounds a title read. */
|
|
26
|
+
read(id: string, options?: {
|
|
27
|
+
readonly offset?: number;
|
|
28
|
+
readonly limit?: number;
|
|
29
|
+
}): Promise<readonly StoredEvent[]>;
|
|
30
|
+
/** One session's stored header, or undefined when nothing is stored under that id. */
|
|
31
|
+
header(id: string): Promise<StoredHeader | undefined>;
|
|
32
|
+
}
|
|
33
|
+
/** Sessions the picker offers before a menu stops being a menu. */
|
|
34
|
+
export declare const PICKER_LIMIT = 30;
|
|
35
|
+
/** Events read to title one session: enough to reach its title or first prompt. */
|
|
36
|
+
export declare const TITLE_EVENT_LIMIT = 40;
|
|
37
|
+
/** Longest session title the picker shows, before the row can no longer hold it. */
|
|
38
|
+
export declare const TITLE_CHAR_LIMIT = 72;
|
|
39
|
+
/** Events read to resolve a session's preset: a selection sits in its blank prefix. */
|
|
40
|
+
export declare const PRESET_EVENT_LIMIT = 40;
|
|
41
|
+
/**
|
|
42
|
+
* Name a session, preferring the title the harness derived or the reader set.
|
|
43
|
+
*
|
|
44
|
+
* The durable `session/title` wins because it is what every other surface
|
|
45
|
+
* shows; a session that has none yet falls back to its first human prompt.
|
|
46
|
+
* Injected context and plugin notices also arrive as user-role messages, so
|
|
47
|
+
* only a direct prompt may name a session.
|
|
48
|
+
*/
|
|
49
|
+
export declare function sessionTitle(events: readonly StoredEvent[], limit?: number): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Title one stored session.
|
|
52
|
+
*
|
|
53
|
+
* The tail is read first because a title is latest-wins and usually written
|
|
54
|
+
* after the first turn; the head is the fallback for a session that has a
|
|
55
|
+
* prompt but no title yet.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readSessionTitle(history: SessionHistory, session: StoredSession): Promise<string | undefined>;
|
|
58
|
+
/**
|
|
59
|
+
* The agent preset a stored session runs.
|
|
60
|
+
*
|
|
61
|
+
* The creation header names the preset the session STARTED with, and a
|
|
62
|
+
* selection recorded while it was still blank replaced it. The projection's own
|
|
63
|
+
* rule is that the last selection wins over the header, so the tail is read
|
|
64
|
+
* first and the head second: the harness refuses a switch once a turn has run,
|
|
65
|
+
* which puts any selection in the blank prefix. Reading both keeps this correct
|
|
66
|
+
* if that rule ever loosens, and the event count bounds the work either way.
|
|
67
|
+
*/
|
|
68
|
+
export declare function presetOfStoredSession(history: SessionHistory, id: string): Promise<string | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Wrap the persistence service in the little of it a terminal needs.
|
|
71
|
+
*
|
|
72
|
+
* The seam itself is optional here: a composition without durable storage can
|
|
73
|
+
* still run the surface, it just cannot list or replay stored sessions. A read
|
|
74
|
+
* handle is always closed, because an unclosed one keeps backend resources
|
|
75
|
+
* alive for the rest of the process.
|
|
76
|
+
*/
|
|
77
|
+
export declare function createSessionHistory(ctx: Context): SessionHistory | undefined;
|
|
78
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/agent/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAED,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAED,mEAAmE;AACnE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAA;IACtD,oEAAoE;IACpE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAAA;IAClH,sFAAsF;IACtF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAA;CACtD;AAED,mEAAmE;AACnE,eAAO,MAAM,YAAY,KAAK,CAAA;AAE9B,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,KAAK,CAAA;AA0BpC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,SAAmB,GAAG,MAAM,GAAG,SAAS,CAwBzG;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQnH;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS5G;AAmBD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsD7E"}
|