@sagmans/dsh-tui 0.6.0 → 0.7.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/.agents/skills/dsh-tui-dogfood/SKILL.md +83 -0
- package/.agents/skills/dsh-tui-dogfood/references/home-state.md +78 -0
- package/.agents/skills/dsh-tui-dogfood/scripts/clone-links.mjs +133 -0
- package/.agents/skills/dsh-tui-dogfood/scripts/run-plugin-from-worktree.sh +417 -0
- package/.agents/skills/dsh-tui-update-models/SKILL.md +121 -0
- package/.agents/skills/dsh-tui-update-models/references/model-wiring.md +162 -0
- package/.agents/skills/dsh-tui-update-models/scripts/dump-model-catalog.mjs +178 -0
- package/README.md +99 -5
- package/lib/agent/present.d.ts.map +1 -1
- package/lib/agent/present.js +2 -1
- package/lib/agent/present.js.map +1 -1
- package/lib/cards/composition.d.ts +68 -0
- package/lib/cards/composition.d.ts.map +1 -0
- package/lib/cards/composition.js +117 -0
- package/lib/cards/composition.js.map +1 -0
- package/lib/cards/presenter.d.ts +23 -0
- package/lib/cards/presenter.d.ts.map +1 -0
- package/lib/cards/presenter.js +394 -0
- package/lib/cards/presenter.js.map +1 -0
- package/lib/cards/preview.d.ts +42 -0
- package/lib/cards/preview.d.ts.map +1 -0
- package/lib/cards/preview.js +51 -0
- package/lib/cards/preview.js.map +1 -0
- package/lib/cards.d.ts +12 -120
- package/lib/cards.d.ts.map +1 -1
- package/lib/cards.js +4 -556
- package/lib/cards.js.map +1 -1
- package/lib/compat/section.d.ts +29 -0
- package/lib/compat/section.d.ts.map +1 -0
- package/lib/compat/section.js +82 -0
- package/lib/compat/section.js.map +1 -0
- package/lib/config.d.ts +27 -2
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +61 -1
- package/lib/config.js.map +1 -1
- package/lib/contracts.d.ts +28 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/fold-cursor.d.ts +8 -0
- package/lib/fold-cursor.d.ts.map +1 -1
- package/lib/fold-cursor.js +18 -0
- package/lib/fold-cursor.js.map +1 -1
- package/lib/gates/question-card.d.ts +77 -0
- package/lib/gates/question-card.d.ts.map +1 -0
- package/lib/gates/question-card.js +113 -0
- package/lib/gates/question-card.js.map +1 -0
- package/lib/gates/questions.d.ts +127 -0
- package/lib/gates/questions.d.ts.map +1 -0
- package/lib/gates/questions.js +418 -0
- package/lib/gates/questions.js.map +1 -0
- package/lib/gates.d.ts +1 -113
- package/lib/gates.d.ts.map +1 -1
- package/lib/gates.js +1 -484
- package/lib/gates.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +343 -2602
- package/lib/index.js.map +1 -1
- package/lib/input/action-catalog.d.ts +74 -0
- package/lib/input/action-catalog.d.ts.map +1 -0
- package/lib/input/action-catalog.js +137 -0
- package/lib/input/action-catalog.js.map +1 -0
- package/lib/input/actions.d.ts +5 -81
- package/lib/input/actions.d.ts.map +1 -1
- package/lib/input/actions.js +4 -625
- package/lib/input/actions.js.map +1 -1
- package/lib/input/completion.d.ts +1 -1
- package/lib/input/completion.d.ts.map +1 -1
- package/lib/input/completion.js +2 -1
- package/lib/input/completion.js.map +1 -1
- package/lib/input/file-index.d.ts +40 -0
- package/lib/input/file-index.d.ts.map +1 -0
- package/lib/input/file-index.js +141 -0
- package/lib/input/file-index.js.map +1 -0
- package/lib/input/file-search.d.ts +1 -38
- package/lib/input/file-search.d.ts.map +1 -1
- package/lib/input/file-search.js +1 -487
- package/lib/input/file-search.js.map +1 -1
- package/lib/input/key-press.d.ts +57 -0
- package/lib/input/key-press.d.ts.map +1 -0
- package/lib/input/key-press.js +218 -0
- package/lib/input/key-press.js.map +1 -0
- package/lib/input/keymap-conflicts.d.ts +59 -0
- package/lib/input/keymap-conflicts.d.ts.map +1 -0
- package/lib/input/keymap-conflicts.js +292 -0
- package/lib/input/keymap-conflicts.js.map +1 -0
- package/lib/input/keymap-settings.js +1 -1
- package/lib/input/keymap-settings.js.map +1 -1
- package/lib/input/keymap.d.ts.map +1 -1
- package/lib/input/keymap.js +3 -1
- package/lib/input/keymap.js.map +1 -1
- package/lib/input/workspace-files.d.ts +10 -0
- package/lib/input/workspace-files.d.ts.map +1 -0
- package/lib/input/workspace-files.js +359 -0
- package/lib/input/workspace-files.js.map +1 -0
- package/lib/install-skills.d.ts +20 -0
- package/lib/install-skills.d.ts.map +1 -0
- package/lib/install-skills.js +198 -0
- package/lib/install-skills.js.map +1 -0
- package/lib/keys-command.d.ts +2 -1
- package/lib/keys-command.d.ts.map +1 -1
- package/lib/keys-command.js +2 -1
- package/lib/keys-command.js.map +1 -1
- package/lib/model-list.d.ts +37 -0
- package/lib/model-list.d.ts.map +1 -0
- package/lib/model-list.js +60 -0
- package/lib/model-list.js.map +1 -0
- package/lib/startup.d.ts +8 -0
- package/lib/startup.d.ts.map +1 -1
- package/lib/startup.js +120 -24
- package/lib/startup.js.map +1 -1
- package/lib/subagents.d.ts +7 -3
- package/lib/subagents.d.ts.map +1 -1
- package/lib/subagents.js +28 -4
- package/lib/subagents.js.map +1 -1
- package/lib/surface/appearance-preferences.d.ts +26 -0
- package/lib/surface/appearance-preferences.d.ts.map +1 -0
- package/lib/surface/appearance-preferences.js +139 -0
- package/lib/surface/appearance-preferences.js.map +1 -0
- package/lib/surface/appearance.d.ts +87 -0
- package/lib/surface/appearance.d.ts.map +1 -0
- package/lib/surface/appearance.js +353 -0
- package/lib/surface/appearance.js.map +1 -0
- package/lib/surface/background-work.d.ts +49 -0
- package/lib/surface/background-work.d.ts.map +1 -0
- package/lib/surface/background-work.js +146 -0
- package/lib/surface/background-work.js.map +1 -0
- package/lib/surface/commands.d.ts +80 -0
- package/lib/surface/commands.d.ts.map +1 -0
- package/lib/surface/commands.js +364 -0
- package/lib/surface/commands.js.map +1 -0
- package/lib/surface/modal-input.d.ts +58 -0
- package/lib/surface/modal-input.d.ts.map +1 -0
- package/lib/surface/modal-input.js +237 -0
- package/lib/surface/modal-input.js.map +1 -0
- package/lib/surface/model-choice.d.ts +54 -0
- package/lib/surface/model-choice.d.ts.map +1 -0
- package/lib/surface/model-choice.js +325 -0
- package/lib/surface/model-choice.js.map +1 -0
- package/lib/surface/preset-choice.d.ts +78 -0
- package/lib/surface/preset-choice.d.ts.map +1 -0
- package/lib/surface/preset-choice.js +163 -0
- package/lib/surface/preset-choice.js.map +1 -0
- package/lib/surface/prompt-input.d.ts +97 -0
- package/lib/surface/prompt-input.d.ts.map +1 -0
- package/lib/surface/prompt-input.js +215 -0
- package/lib/surface/prompt-input.js.map +1 -0
- package/lib/surface/prompt-memory.d.ts +76 -0
- package/lib/surface/prompt-memory.d.ts.map +1 -0
- package/lib/surface/prompt-memory.js +167 -0
- package/lib/surface/prompt-memory.js.map +1 -0
- package/lib/surface/session-lifecycle.d.ts +120 -0
- package/lib/surface/session-lifecycle.d.ts.map +1 -0
- package/lib/surface/session-lifecycle.js +321 -0
- package/lib/surface/session-lifecycle.js.map +1 -0
- package/lib/surface/session-picker.d.ts +39 -0
- package/lib/surface/session-picker.d.ts.map +1 -0
- package/lib/surface/session-picker.js +107 -0
- package/lib/surface/session-picker.js.map +1 -0
- package/lib/surface/session-view.d.ts +86 -0
- package/lib/surface/session-view.d.ts.map +1 -0
- package/lib/surface/session-view.js +215 -0
- package/lib/surface/session-view.js.map +1 -0
- package/lib/surface/staged-turns.d.ts +81 -0
- package/lib/surface/staged-turns.d.ts.map +1 -0
- package/lib/surface/staged-turns.js +206 -0
- package/lib/surface/staged-turns.js.map +1 -0
- package/lib/surface/terminal-lifecycle.d.ts +76 -0
- package/lib/surface/terminal-lifecycle.d.ts.map +1 -0
- package/lib/surface/terminal-lifecycle.js +222 -0
- package/lib/surface/terminal-lifecycle.js.map +1 -0
- package/lib/terminal-text/scan.d.ts +44 -0
- package/lib/terminal-text/scan.d.ts.map +1 -0
- package/lib/terminal-text/scan.js +195 -0
- package/lib/terminal-text/scan.js.map +1 -0
- package/lib/terminal-text/sgr.d.ts +32 -0
- package/lib/terminal-text/sgr.d.ts.map +1 -0
- package/lib/terminal-text/sgr.js +203 -0
- package/lib/terminal-text/sgr.js.map +1 -0
- package/lib/terminal-text.d.ts +1 -1
- package/lib/terminal-text.d.ts.map +1 -1
- package/lib/terminal-text.js +2 -379
- package/lib/terminal-text.js.map +1 -1
- package/lib/theme-command.js +2 -1
- package/lib/theme-command.js.map +1 -1
- package/lib/theme-defaults.d.ts +64 -0
- package/lib/theme-defaults.d.ts.map +1 -0
- package/lib/theme-defaults.js +248 -0
- package/lib/theme-defaults.js.map +1 -0
- package/lib/theme-resolver.d.ts +36 -0
- package/lib/theme-resolver.d.ts.map +1 -0
- package/lib/theme-resolver.js +112 -0
- package/lib/theme-resolver.js.map +1 -0
- package/lib/theme-schema.d.ts.map +1 -1
- package/lib/theme-schema.js +3 -2
- package/lib/theme-schema.js.map +1 -1
- package/lib/theme-settings.d.ts +5 -7
- package/lib/theme-settings.d.ts.map +1 -1
- package/lib/theme-settings.js +27 -19
- package/lib/theme-settings.js.map +1 -1
- package/lib/theme-tokens.d.ts +0 -89
- package/lib/theme-tokens.d.ts.map +1 -1
- package/lib/theme-tokens.js +0 -356
- package/lib/theme-tokens.js.map +1 -1
- package/lib/theme.d.ts +1 -1
- package/lib/theme.d.ts.map +1 -1
- package/lib/theme.js +2 -1
- package/lib/theme.js.map +1 -1
- package/lib/tool-display.d.ts.map +1 -1
- package/lib/tool-display.js +2 -1
- package/lib/tool-display.js.map +1 -1
- package/lib/transcript/message-content.d.ts +21 -0
- package/lib/transcript/message-content.d.ts.map +1 -0
- package/lib/transcript/message-content.js +61 -0
- package/lib/transcript/message-content.js.map +1 -0
- package/lib/transcript/tool-calls.d.ts +106 -0
- package/lib/transcript/tool-calls.d.ts.map +1 -0
- package/lib/transcript/tool-calls.js +254 -0
- package/lib/transcript/tool-calls.js.map +1 -0
- package/lib/transcript.d.ts +14 -55
- package/lib/transcript.d.ts.map +1 -1
- package/lib/transcript.js +31 -253
- package/lib/transcript.js.map +1 -1
- package/lib/ui/dock.d.ts +14 -2
- package/lib/ui/dock.d.ts.map +1 -1
- package/lib/ui/dock.js +52 -10
- package/lib/ui/dock.js.map +1 -1
- package/lib/ui/editor.d.ts.map +1 -1
- package/lib/ui/editor.js +2 -1
- package/lib/ui/editor.js.map +1 -1
- package/lib/ui/keymap-picker.d.ts +2 -1
- package/lib/ui/keymap-picker.d.ts.map +1 -1
- package/lib/ui/keymap-picker.js +1 -0
- package/lib/ui/keymap-picker.js.map +1 -1
- package/lib/ui/view/gate-card.d.ts +21 -0
- package/lib/ui/view/gate-card.d.ts.map +1 -0
- package/lib/ui/view/gate-card.js +104 -0
- package/lib/ui/view/gate-card.js.map +1 -0
- package/lib/ui/view/tool-card.d.ts +138 -0
- package/lib/ui/view/tool-card.d.ts.map +1 -0
- package/lib/ui/view/tool-card.js +413 -0
- package/lib/ui/view/tool-card.js.map +1 -0
- package/lib/ui/view/transcript-message.d.ts +62 -0
- package/lib/ui/view/transcript-message.d.ts.map +1 -0
- package/lib/ui/view/transcript-message.js +158 -0
- package/lib/ui/view/transcript-message.js.map +1 -0
- package/lib/ui/view.d.ts +80 -220
- package/lib/ui/view.d.ts.map +1 -1
- package/lib/ui/view.js +81 -675
- package/lib/ui/view.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Model wiring reference
|
|
2
|
+
|
|
3
|
+
Companion to [SKILL.md](../SKILL.md): exact fields, files, and checks.
|
|
4
|
+
|
|
5
|
+
## Resolution order
|
|
6
|
+
|
|
7
|
+
1. Bundle layers in `cordis.yml`, in order.
|
|
8
|
+
2. The profile patch `~/.dsh/profiles/<profile>/cordis.patch.yml`, entry by entry:
|
|
9
|
+
`id` targeted config, `disabled`, `insert`.
|
|
10
|
+
3. A patch `config` **replaces** that row's base config; fields it omits fall back
|
|
11
|
+
to the schema default, not to the bundle's value.
|
|
12
|
+
4. The model directory a profile sees is owned by whichever row is enabled:
|
|
13
|
+
`llm-pi-ai` (additive provider config), `dsh-provider-extra` (owned catalog),
|
|
14
|
+
or both, if the profile is not using exclusive ownership.
|
|
15
|
+
|
|
16
|
+
Exclusive ownership is a set of disable rows plus the catalog itself:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
- { "id": "agent-default-model", "disabled": true }
|
|
20
|
+
- { "id": "llm-pi-ai", "disabled": true }
|
|
21
|
+
- { "id": "llm-deepseek", "disabled": true }
|
|
22
|
+
- id: dsh-provider-extra
|
|
23
|
+
config: { catalog: { version: 1, ... } }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## `llm-pi-ai` provider config
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
- id: llm-pi-ai
|
|
30
|
+
config:
|
|
31
|
+
providers:
|
|
32
|
+
qwen-token-plan:
|
|
33
|
+
apiKeyEnv: QWEN_TOKEN_PLAN_API_KEY # env var holding the key
|
|
34
|
+
baseURL: https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1
|
|
35
|
+
api: openai-completions # optional wire override
|
|
36
|
+
models:
|
|
37
|
+
- id: qwen3.8-max # served as-is
|
|
38
|
+
- id: deepseek-v4.1-flash
|
|
39
|
+
name: DeepSeek V4.1 Flash
|
|
40
|
+
contextWindow: 1000000
|
|
41
|
+
maxTokens: 384000
|
|
42
|
+
input: [text, image]
|
|
43
|
+
reasoningEfforts: { low: low, high: high, max: max }
|
|
44
|
+
compat:
|
|
45
|
+
supportsStore: false
|
|
46
|
+
supportsDeveloperRole: false
|
|
47
|
+
supportsReasoningEffort: true
|
|
48
|
+
supportsStrictMode: true
|
|
49
|
+
thinkingFormat: qwen
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
An id that the installed catalog already describes needs no restatement. An id it
|
|
53
|
+
does not describe needs the metadata (or a `template` sibling to clone).
|
|
54
|
+
|
|
55
|
+
## `dsh-provider-extra`
|
|
56
|
+
|
|
57
|
+
Two config generations exist; a profile normally uses one:
|
|
58
|
+
|
|
59
|
+
| Config | Use |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Additive: `routeId`, `displayName`, `apiKeyEnv`, `fallbackSessionId`, `extraModels`, `models`, `codexExtraModels`, `codexModels`, `codexTransport` | add a gateway or subscription route beside whatever else the profile serves |
|
|
62
|
+
| `catalog` v1 | own the entire directory: ids, aliases, metadata, and the default selection |
|
|
63
|
+
|
|
64
|
+
Catalog fields:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
catalog:
|
|
68
|
+
version: 1 # exactly 1
|
|
69
|
+
default: # null only when the catalog is empty
|
|
70
|
+
{ provider: opencode-go-session, model: deepseek-flash, reasoningEffort: max }
|
|
71
|
+
providers:
|
|
72
|
+
- id: opencode-go-session # the id every selection and alias check uses
|
|
73
|
+
name: OpenCode Go (session)
|
|
74
|
+
source: opencode-go # pi-ai provider id: its installed data is the base
|
|
75
|
+
baseURL: https://api.x.ai/v1 # optional
|
|
76
|
+
transport: sse # optional
|
|
77
|
+
fallbackSessionId: dsh-provider-extra # optional
|
|
78
|
+
auth: { apiKeyRef: OPENCODE_GO_API_KEY } # or { credentialProvider: openai-codex }
|
|
79
|
+
models:
|
|
80
|
+
- id: deepseek-flash
|
|
81
|
+
name: DeepSeek V4.1 Flash
|
|
82
|
+
aliases: [deepseek-v4.1-flash] # extra ids that resolve to this model
|
|
83
|
+
template: deepseek-v4-flash # clone wire behavior when the source lacks the id
|
|
84
|
+
defaultMaxTokens: 384000 # request ceiling; metadata.maxTokens overrides
|
|
85
|
+
metadata:
|
|
86
|
+
api: openai-completions
|
|
87
|
+
reasoning: true
|
|
88
|
+
input: [text, image]
|
|
89
|
+
cost: { input: 0.22, output: 0.66, cacheRead: 0.007, cacheWrite: 0 }
|
|
90
|
+
contextWindow: 1000000
|
|
91
|
+
maxTokens: 384000
|
|
92
|
+
thinkingLevelMap: { off: null, minimal: null, low: low, medium: null, high: high, xhigh: null, max: max }
|
|
93
|
+
compat: { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: true, supportsStrictMode: true, thinkingFormat: qwen }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Behaviour:
|
|
97
|
+
|
|
98
|
+
| Case | Result |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `template` names a sibling the source ships | clone its wire behavior and metadata |
|
|
101
|
+
| `template` names nothing | that model lands in the route's diagnostics; the rest of the route still serves |
|
|
102
|
+
| a `models` whitelist names an id nothing provides | the whole route is refused - a typo there is a broken deployment |
|
|
103
|
+
| two providers share an id | composition error |
|
|
104
|
+
| `default` names a model outside the catalog | composition error |
|
|
105
|
+
| `reasoningEffort` unsupported by the model | composition error |
|
|
106
|
+
|
|
107
|
+
## Where the base data comes from
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
# the harness install owns the pi-ai catalog the profile resolves against
|
|
111
|
+
DSH=$(readlink -f "$(command -v dsh)")
|
|
112
|
+
PI_AI="${DSH%/lib/bin.js}/node_modules/@earendil-works/pi-ai"
|
|
113
|
+
ls "$PI_AI/dist/providers/data" # one JSON per provider source, keyed by api
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A JSON entry is the whole truth for a `source`: `id`, `name`, `cost`,
|
|
117
|
+
`contextWindow`, `maxTokens`, `input`, `reasoning`, `thinkingLevelMap`, `compat`.
|
|
118
|
+
A model declared without `template` and without `metadata` is served exactly as
|
|
119
|
+
this file describes it, so an id the vendor renamed or repriced shows stale
|
|
120
|
+
numbers until the catalog declares them. An entry that carries its own complete
|
|
121
|
+
`metadata` needs no installed entry at all, which is how a route the harness does
|
|
122
|
+
not describe is wired.
|
|
123
|
+
|
|
124
|
+
## Credentials
|
|
125
|
+
|
|
126
|
+
| Field | Meaning |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `apiKeyEnv` | read the key from this environment variable |
|
|
129
|
+
| `apiKeyRef` | read it from the profiles' stored credentials under this reference |
|
|
130
|
+
| `credentialProvider` | use a login flow's stored credentials (codex subscriptions) |
|
|
131
|
+
|
|
132
|
+
Keys never belong in a patch, a settings file, or a repository. Put launch-time
|
|
133
|
+
values in `$DSH_HOME/.env` or the environment. A provider row whose credential is
|
|
134
|
+
missing fails its route only; other routes still serve.
|
|
135
|
+
|
|
136
|
+
## Verify
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
dsh --profile tui list-models # provider/model<TAB>display name, one per reachable route
|
|
140
|
+
~/.agents/skills/dsh-tui-update-models/scripts/dump-model-catalog.mjs --home "$DSH_HOME"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`list-models` exits zero only after printing a line, so a deployment that
|
|
144
|
+
advertises nothing cannot pass for one that was enumerated.
|
|
145
|
+
|
|
146
|
+
For every model you touched, confirm against the vendor's own page:
|
|
147
|
+
|
|
148
|
+
1. price per 1M tokens for input, output, cached input, cache write;
|
|
149
|
+
2. context window and maximum output tokens (a "272K" beside "above 272K input
|
|
150
|
+
is priced 2x" is a billing threshold, not the window);
|
|
151
|
+
3. the accepted reasoning effort values, and how the vendor spells "no reasoning"
|
|
152
|
+
(OpenAI: `none`; codex: omit the parameter);
|
|
153
|
+
4. whether the route bills per token at all - a credit or token plan wants cost
|
|
154
|
+
`0`, a metered API wants its published rates;
|
|
155
|
+
5. that an `xhigh` or `max` mapping exists when the vendor documents one.
|
|
156
|
+
|
|
157
|
+
A model with no vendor page (stealth releases, previews) is documented only by the
|
|
158
|
+
routing catalog: its pi.dev model page and `models.dev/api.json`.
|
|
159
|
+
|
|
160
|
+
Then run the profile against a cloned home before the real one - see the
|
|
161
|
+
`dsh-tui-dogfood` skill - and `pnpm run build` any linked bundle first: a linked
|
|
162
|
+
profile loads `lib/`, not `src/`.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Print the model directory a dsh profile resolves, with the provenance of
|
|
4
|
+
* every field.
|
|
5
|
+
*
|
|
6
|
+
* The surface reads prices, context windows, and reasoning levels from the
|
|
7
|
+
* composition, not from the vendor's page, so an inherited or stale value is
|
|
8
|
+
* invisible in the UI. This reads the same two inputs the resolver does - the
|
|
9
|
+
* owned catalog in the profile patch, and the installed pi-ai provider data -
|
|
10
|
+
* and names where each number came from: "installed" for the source's own
|
|
11
|
+
* entry, "template:<id>" for a clone, "metadata" for what the catalog declares.
|
|
12
|
+
* An entry the catalog spells out completely needs no installed entry at all,
|
|
13
|
+
* so a missing harness install must not hide it.
|
|
14
|
+
*
|
|
15
|
+
* Usage: dump-model-catalog.mjs [--home <dsh home>] [--profile <name>] [--json]
|
|
16
|
+
*/
|
|
17
|
+
import { execFileSync } from 'node:child_process'
|
|
18
|
+
import { readFileSync, realpathSync } from 'node:fs'
|
|
19
|
+
import { homedir } from 'node:os'
|
|
20
|
+
import { join } from 'node:path'
|
|
21
|
+
import { fileURLToPath } from 'node:url'
|
|
22
|
+
|
|
23
|
+
const LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']
|
|
24
|
+
/** Fields a catalog entry may inherit, and the layer each value came from. */
|
|
25
|
+
const INHERITED_FIELDS = ['contextWindow', 'maxTokens', 'cost', 'input', 'reasoning', 'thinkingLevelMap', 'api']
|
|
26
|
+
const INSTALLED = 'installed'
|
|
27
|
+
const METADATA = 'metadata'
|
|
28
|
+
const ABSENT = 'none'
|
|
29
|
+
|
|
30
|
+
/** The levels pi-ai offers: a null mapping removes one, and xhigh/max need one. */
|
|
31
|
+
export function supportedLevels(levelMap) {
|
|
32
|
+
return LEVELS.filter((level) => {
|
|
33
|
+
const mapped = levelMap?.[level]
|
|
34
|
+
if (mapped === null) return false
|
|
35
|
+
if (level === 'xhigh' || level === 'max') return mapped !== undefined
|
|
36
|
+
return true
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseArgs(argv) {
|
|
41
|
+
const options = { home: process.env.DSH_HOME ?? join(homedir(), '.dsh'), profile: 'tui', json: false }
|
|
42
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
43
|
+
const flag = argv[index]
|
|
44
|
+
if (flag === '--json') options.json = true
|
|
45
|
+
else if (flag === '--home') options.home = argv[++index]
|
|
46
|
+
else if (flag === '--profile') options.profile = argv[++index]
|
|
47
|
+
else throw new Error('unknown argument ' + flag)
|
|
48
|
+
}
|
|
49
|
+
return options
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The pi-ai data directory of the harness install the launcher belongs to. */
|
|
53
|
+
function dataDirectory() {
|
|
54
|
+
try {
|
|
55
|
+
const bin = execFileSync('sh', ['-c', 'command -v dsh'], { encoding: 'utf8' }).trim()
|
|
56
|
+
const root = join(realpathSync(bin), '..', '..')
|
|
57
|
+
return join(root, 'node_modules', '@earendil-works', 'pi-ai', 'dist', 'providers', 'data')
|
|
58
|
+
} catch {
|
|
59
|
+
// No harness install is a supported reading: declared entries still resolve.
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Every JSON patch entry that configures the owned catalog, last one winning. */
|
|
65
|
+
export function catalogConfig(patch) {
|
|
66
|
+
const lines = readFileSync(patch, 'utf8').split('\n')
|
|
67
|
+
let catalog
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
if (!line.startsWith('- {') || !line.includes('"catalog"')) continue
|
|
70
|
+
const entry = JSON.parse(line.slice(2))
|
|
71
|
+
if (entry.config?.catalog !== undefined) catalog = entry.config.catalog
|
|
72
|
+
}
|
|
73
|
+
return catalog
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function providerData(directory, source) {
|
|
77
|
+
if (directory === undefined) return undefined
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(readFileSync(join(directory, source + '.json'), 'utf8'))
|
|
80
|
+
} catch {
|
|
81
|
+
return undefined
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Look one id up across a provider's api maps; an api in the spec disambiguates. */
|
|
86
|
+
function lookup(data, id, api) {
|
|
87
|
+
if (data === undefined) return undefined
|
|
88
|
+
for (const [key, models] of Object.entries(data)) {
|
|
89
|
+
if ((api === undefined || key === api) && models[id] !== undefined) return { ...models[id], api: models[id].api ?? key }
|
|
90
|
+
}
|
|
91
|
+
return undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Resolve one catalog entry against the installed data it may inherit from. */
|
|
95
|
+
export function resolve(spec, data) {
|
|
96
|
+
const metadata = spec.metadata ?? {}
|
|
97
|
+
const declared = Object.keys(metadata).length > 0
|
|
98
|
+
const cloneOf = spec.template ?? spec.id
|
|
99
|
+
const base = lookup(data, cloneOf, metadata.api)
|
|
100
|
+
if (base === undefined && !declared) {
|
|
101
|
+
return { error: 'no source entry for ' + cloneOf + '; declare a template or metadata' }
|
|
102
|
+
}
|
|
103
|
+
const merged = { ...base, ...metadata, id: spec.id, name: spec.name ?? base?.name ?? spec.id }
|
|
104
|
+
merged.maxTokens = metadata.maxTokens ?? spec.defaultMaxTokens ?? base?.maxTokens
|
|
105
|
+
const origins = {}
|
|
106
|
+
for (const field of INHERITED_FIELDS) {
|
|
107
|
+
const declaredValue = field === 'maxTokens' ? metadata.maxTokens ?? spec.defaultMaxTokens : metadata[field]
|
|
108
|
+
if (declaredValue !== undefined) origins[field] = METADATA
|
|
109
|
+
else if (base === undefined) origins[field] = ABSENT
|
|
110
|
+
else origins[field] = spec.template === undefined ? INSTALLED : 'template:' + spec.template
|
|
111
|
+
}
|
|
112
|
+
return { model: merged, origins, levels: supportedLevels(merged.thinkingLevelMap) }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function rowsOf(catalog, directory) {
|
|
116
|
+
const rows = []
|
|
117
|
+
for (const provider of catalog.providers ?? []) {
|
|
118
|
+
const data = providerData(directory, provider.source)
|
|
119
|
+
for (const spec of provider.models ?? []) {
|
|
120
|
+
const result = resolve(spec, data)
|
|
121
|
+
const route = provider.id + '/' + spec.id
|
|
122
|
+
if (result.error !== undefined) {
|
|
123
|
+
rows.push({ route, error: result.error })
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
const cost = result.model.cost ?? {}
|
|
127
|
+
rows.push({
|
|
128
|
+
route,
|
|
129
|
+
name: result.model.name,
|
|
130
|
+
api: result.model.api,
|
|
131
|
+
contextWindow: result.model.contextWindow,
|
|
132
|
+
maxTokens: result.model.maxTokens,
|
|
133
|
+
cost: '$' + [cost.input, cost.output, cost.cacheRead, cost.cacheWrite].join('/') + ' per 1M',
|
|
134
|
+
levels: result.levels.join(','),
|
|
135
|
+
origins: INHERITED_FIELDS
|
|
136
|
+
.map(field => [field, result.origins[field]])
|
|
137
|
+
.filter(([, origin]) => origin !== INSTALLED)
|
|
138
|
+
.map(([field, origin]) => field + '=' + origin)
|
|
139
|
+
.join(' ') || 'all installed',
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return rows
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function main() {
|
|
147
|
+
const options = parseArgs(process.argv.slice(2))
|
|
148
|
+
const patch = join(options.home, 'profiles', options.profile, 'cordis.patch.yml')
|
|
149
|
+
const catalog = catalogConfig(patch)
|
|
150
|
+
if (catalog === undefined) {
|
|
151
|
+
process.stderr.write('no owned catalog in ' + patch + '\n'
|
|
152
|
+
+ 'this profile wires models another way; see references/model-wiring.md\n')
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
const directory = dataDirectory()
|
|
156
|
+
if (directory === undefined) {
|
|
157
|
+
process.stderr.write('no harness install found; entries that inherit from it cannot be checked\n')
|
|
158
|
+
}
|
|
159
|
+
const rows = rowsOf(catalog, directory)
|
|
160
|
+
const unresolved = rows.filter(row => row.error !== undefined).length
|
|
161
|
+
if (options.json) {
|
|
162
|
+
process.stdout.write(JSON.stringify({ default: catalog.default ?? null, rows }, null, 2) + '\n')
|
|
163
|
+
} else {
|
|
164
|
+
for (const row of rows) {
|
|
165
|
+
if (row.error !== undefined) {
|
|
166
|
+
process.stdout.write(row.route + '\n UNRESOLVED: ' + row.error + '\n')
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
process.stdout.write(row.route + '\n ' + row.name + ' | ' + row.api + ' | ctx ' + row.contextWindow
|
|
170
|
+
+ ' | max ' + row.maxTokens + ' | ' + row.cost + ' | efforts ' + row.levels + '\n inherited: ' + row.origins + '\n')
|
|
171
|
+
}
|
|
172
|
+
process.stdout.write('default: ' + JSON.stringify(catalog.default ?? null) + '\n')
|
|
173
|
+
}
|
|
174
|
+
process.exit(unresolved === 0 ? 0 : 1)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Importing the resolver must not run the CLI: the spec reads it that way.
|
|
178
|
+
if (process.argv[1] !== undefined && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) main()
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Status: **v1 feature-complete; published on npm as `@sagmans/dsh-tui`.** The sur
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
A profile keeps this plugin as one bundle layer. Install it from a checkout of this repository, or from the registry (`0.1.0` or later). Both paths need Node.js >= 22.19 and `pnpm` on `PATH`.
|
|
9
|
+
A profile keeps this plugin as one bundle layer. Install it from a checkout of this repository, or from the registry (`0.1.0` or later). Both paths need Node.js >= 22.19 and `pnpm` on `PATH`. Interactive sessions need a real terminal: stdin and stdout must be TTYs.
|
|
10
10
|
|
|
11
11
|
### From a plugin checkout
|
|
12
12
|
|
|
@@ -30,6 +30,33 @@ dsh --profile tui
|
|
|
30
30
|
|
|
31
31
|
Releases are published, so this path works today. A checkout stays the path for unreleased work.
|
|
32
32
|
|
|
33
|
+
### Install the dogfood skill (optional)
|
|
34
|
+
|
|
35
|
+
Run this explicit command after you add the plugin:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
dsh --profile tui install-skills
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The command copies `dsh-tui-dogfood` to
|
|
42
|
+
`~/.agents/skills/dsh-tui-dogfood/`. Agents can then load it from any dsh
|
|
43
|
+
plugin repository. The skill uses a cloned dsh home, so tests do not change
|
|
44
|
+
your real profile. A first install needs no TTY. `npm install` does not copy
|
|
45
|
+
skills into your home.
|
|
46
|
+
|
|
47
|
+
If the skill exists, the command asks `Update existing skill? [y/N]` in a
|
|
48
|
+
terminal. Only `y` or `yes` replaces it. Enter or `n` keeps the existing
|
|
49
|
+
copy. To update without a prompt, including in non-interactive runs, use:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
dsh --profile tui install-skills --update
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Updating removes any local changes inside the previous skill directory.
|
|
56
|
+
The command stages the new copy before replacement and restores the old
|
|
57
|
+
copy if replacement fails. If cleanup fails after replacement, the new copy
|
|
58
|
+
stays installed and the command reports the old backup path.
|
|
59
|
+
|
|
33
60
|
### Confirm the plugin mounted
|
|
34
61
|
|
|
35
62
|
The profile records its layers in `$DSH_HOME/profiles/tui/package.json` (`~/.dsh` by default). `@sagmans/dsh-tui` must appear in `dsh.profile.bundles`:
|
|
@@ -117,8 +144,18 @@ dsh --profile tui --preset minimal # start in a shipped mode other than
|
|
|
117
144
|
dsh --profile tui --model deepseek-chat
|
|
118
145
|
dsh --profile tui --no-color
|
|
119
146
|
dsh --profile tui --no-bell # do not ring when a long turn finishes
|
|
147
|
+
dsh --profile tui list-models # print every provider/model the picker can reach
|
|
120
148
|
```
|
|
121
149
|
|
|
150
|
+
`list-models` prints one line per route the `/model` picker can reach —
|
|
151
|
+
`provider/model`, a tab, then the model's display name — in picker order:
|
|
152
|
+
providers as the llm service registered them, then each provider's own model
|
|
153
|
+
order. It writes plain stdout, so it works piped or redirected, never opens the
|
|
154
|
+
alternate screen, and exits 0 after printing at least one route. When the
|
|
155
|
+
profile has no llm service, when the provider listing cannot be read, or when
|
|
156
|
+
nothing is configured to advertise a model, it names the reason on stderr and
|
|
157
|
+
exits 1.
|
|
158
|
+
|
|
122
159
|
Every key below is a shipped default. `/keys` opens every action the surface
|
|
123
160
|
and its library can perform as a list you filter as you type, with the keys in
|
|
124
161
|
force, and the `keys:` section moves any of them — see [Keys](#keys).
|
|
@@ -223,7 +260,7 @@ While a turn runs, a prompt submitted into the editor waits in the agent's own i
|
|
|
223
260
|
|
|
224
261
|
Copying is read back through what the surface drew rather than through the screen, so a selection is the words alone: dragging across a message takes its box with it on screen, and the surface takes its own frame back out before the text reaches the clipboard — the sides, the padding beside them, and the rules above and below. A selection that covers a whole message, two of them, or a part of one is read the same way, and a row the transcript did not draw — the editor's own bar, a picker's card — is copied exactly as it read, and a selection that was nothing but frame is handed back as the reader made it, because a copy is never emptied. The frame still comes back in a copy taken with the terminal's own selection — Shift held while dragging, or a terminal that keeps selection to itself — which is the one path no program can filter.
|
|
225
262
|
|
|
226
|
-
|
|
263
|
+
When prompt history is enabled, each submitted line is kept in a global history at
|
|
227
264
|
`$DSH_HOME/prompt-history.json`. Typing the start of a prompt that was sent
|
|
228
265
|
before draws the rest of the newest match after the cursor in a faint shade.
|
|
229
266
|
`Ctrl+E` takes the whole suggestion and the word-movement key takes the next
|
|
@@ -328,8 +365,8 @@ must not also be a way to lose the work.
|
|
|
328
365
|
|
|
329
366
|
Every styled element is a named token with a shipped default, and every key is
|
|
330
367
|
an action with one, so the surface can be restyled and rebound without touching
|
|
331
|
-
code.
|
|
332
|
-
|
|
368
|
+
code. Released hosts with section APIs store preferences in
|
|
369
|
+
`$DSH_HOME/settings.yaml`, under a `dsh-tui:` section:
|
|
333
370
|
|
|
334
371
|
```yaml
|
|
335
372
|
dsh-tui:
|
|
@@ -364,6 +401,61 @@ dsh-tui:
|
|
|
364
401
|
hidden: true # the element renders nothing at all
|
|
365
402
|
```
|
|
366
403
|
|
|
404
|
+
Config-backed source hosts store the same preference fields in the TUI entry's
|
|
405
|
+
`config` in the active profile patch. Use the actual entry ID, normally `tui`,
|
|
406
|
+
not the legacy `dsh-tui` namespace. For example:
|
|
407
|
+
|
|
408
|
+
```yaml
|
|
409
|
+
- id: tui
|
|
410
|
+
config:
|
|
411
|
+
theme: violet-orbit
|
|
412
|
+
history:
|
|
413
|
+
enabled: false
|
|
414
|
+
ghost: false
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
The plugin exports a Config schema for all preferences shown above, including
|
|
418
|
+
`prefix`, `prefixWindow`, and `keys`. Launch fields such as `sessionId`,
|
|
419
|
+
`model`, and `provider` remain separate from live preference edits.
|
|
420
|
+
The settings service owns persistence. TUI does not create another preference store.
|
|
421
|
+
It uses released `installSection` or `register` APIs, or Config-backed
|
|
422
|
+
`describe` and revision-checked `update` APIs. Unsupported or read-only writes
|
|
423
|
+
show a notice instead of reporting success. A rejected theme selection restores
|
|
424
|
+
the applied theme, and `/theme tokens` reports that same appearance.
|
|
425
|
+
|
|
426
|
+
On Config-backed hosts, absent `history.enabled` and `history.ghost` stay off.
|
|
427
|
+
Set each switch to `true` explicitly to enable it. This prevents recording while
|
|
428
|
+
the host's asynchronous legacy import is pending or has failed. Released section
|
|
429
|
+
hosts retain their existing defaults. If the host reports unreadable preferences,
|
|
430
|
+
history stays off. Readable `false` switches survive errors in other fields.
|
|
431
|
+
Malformed updates retain the last valid appearance and do not overwrite their source.
|
|
432
|
+
|
|
433
|
+
When public descriptors expose raw user layers, history checks those layers at use
|
|
434
|
+
time. A readable opt-out survives rejected siblings even without a change event.
|
|
435
|
+
Another rejected opt-in cannot clear this protection; a valid committed update can.
|
|
436
|
+
A released-provider limit remains: after an absent user section, rejected scalar
|
|
437
|
+
sections can produce identical public descriptors. TUI cannot detect that transition
|
|
438
|
+
without host validity metadata, so normal released defaults remain active.
|
|
439
|
+
|
|
440
|
+
The inspected source host has no legacy import alias from `dsh-tui` to `tui`.
|
|
441
|
+
Exporting Config does not resolve that namespace mismatch. The host can rename
|
|
442
|
+
`settings.yaml` to `settings.yaml.imported` before import completes.
|
|
443
|
+
TUI does not retry or restore that file automatically. Preserve current privacy
|
|
444
|
+
opt-outs and copy only the intended legacy fields into the actual TUI entry's
|
|
445
|
+
`config`. Keep the original file for rollback.
|
|
446
|
+
|
|
447
|
+
Live Config edits require the loaded schema runtime to create native volatile
|
|
448
|
+
references. The released schemastery 3.18.2 implementation used by this checkout's
|
|
449
|
+
unit tests lacks that API. A source host can resolve a different implementation
|
|
450
|
+
with the same version label. TUI checks the loaded implementation and actual
|
|
451
|
+
references, not the package version or CLI identity.
|
|
452
|
+
|
|
453
|
+
If the loaded schema runtime lacks native support, TUI exports ordinary fields
|
|
454
|
+
rather than unsupported live metadata. TUI then rejects Config-backed writes,
|
|
455
|
+
and the host cannot import preferences through its volatile-field settings API.
|
|
456
|
+
Reading row preferences still works, and ordinary profile changes use the host's
|
|
457
|
+
reload lifecycle. Native schema-backed hosts can apply live preference updates.
|
|
458
|
+
|
|
367
459
|
Every field is optional, so a section that changes one shade is enough. The
|
|
368
460
|
document is hot-reloaded: an edit restyles a running session and re-arms the
|
|
369
461
|
keymap on the next press, and `/theme` shows each element's effective value and
|
|
@@ -778,9 +870,11 @@ The workflow stores no npm token: the registry trusts `release.yml` on the `npm-
|
|
|
778
870
|
|
|
779
871
|
- 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.
|
|
780
872
|
- `/model` changes the route and reasoning effort for the running session only. Catalog membership is advisory — an adapter may accept an id it does not advertise, while an explicit effort is checked against the route's own levels before it is applied. The picker offers the routes this deployment configured, not the ones it can prove credentialed: a provider whose key or sign-in is still missing appears like any other, and its first request names the missing credential.
|
|
873
|
+
- Model discovery failures show a safe notice. Healthy provider catalogs remain selectable. Check provider configuration and credentials before retrying `/model`.
|
|
781
874
|
- Scrolling is the mouse wheel, or the terminal's own scrollback keys where it offers them.
|
|
782
875
|
- 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.
|
|
783
876
|
- The dock shows the goal, plan mode, the todo items still to do, and any background job or delegation still running; a settled item leaves rather than turns into a completed row, and the list clears when the next turn opens so a fresh task never inherits the previous one's checklist. 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.
|
|
877
|
+
- The dock shows three running subagents by default. When more than three run, click the heading, overflow row, or blank cells in that section to show all. Click again to show three. Click a child’s text to view its transcript. Press `ctrl+b` to return.
|
|
784
878
|
- 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.
|
|
785
879
|
- A card reads its tool's own render intent through the agent whose session is on screen, so a stored session with no live agent — one this process is not running, or a child that has already finished — folds to the generic card instead of the tool's own.
|
|
786
880
|
- 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. The status line carries the way back, read from the map in force, so a remap shows up without reopening the view.
|
|
@@ -804,7 +898,7 @@ discipline as this one.
|
|
|
804
898
|
| Plugin | What it adds |
|
|
805
899
|
|---|---|
|
|
806
900
|
| [`@sagmans/dsh-auto-compact`](https://github.com/sagmans/dsh-auto-compact) | An absolute token trigger for automatic compaction: the conversation condenses at `min(thresholdTokens, contextWindow × thresholdRatio)` instead of the window ratio alone, so a large-window model pays a fixed price, with per-route overrides. Its patch swaps the shipped `compaction-basic` backend — the same row this bundle already takes out of the global composition — so the profile still keeps exactly one compaction service, and this surface needs no setting for it. |
|
|
807
|
-
| [`@sagmans/dsh-provider-extra`](https://github.com/sagmans/dsh-provider-extra) | Extra provider routes: OpenCode Go, which sends the live conversation id in `x-opencode-session` for routing and prompt caching, and OpenAI Codex over a ChatGPT subscription's OAuth flow with the harness credential store. Its routes join the `/model` picker like every configured provider. |
|
|
901
|
+
| [`@sagmans/dsh-provider-extra`](https://github.com/sagmans/dsh-provider-extra) | Extra provider routes: OpenCode Go, which sends the live conversation id in `x-opencode-session` for routing and prompt caching, and OpenAI Codex over a ChatGPT subscription's OAuth flow with the harness credential store. Its routes join the `/model` picker like every configured provider. TUI also works without this package, using the core `llm` and `agentDefaultModel` services. |
|
|
808
902
|
|
|
809
903
|
```sh
|
|
810
904
|
dsh plugin --profile tui add @sagmans/dsh-auto-compact
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"present.d.ts","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"present.d.ts","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAEnD,OAAO,EAAgB,KAAK,aAAa,EAAwB,MAAM,aAAa,CAAA;AA4BpF;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,GAAG,SAAS,GAAG,aAAa,CAqChG"}
|
package/lib/agent/present.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { cardOfCall, cardOfResult
|
|
1
|
+
import { cardOfCall, cardOfResult } from "../cards/presenter.js";
|
|
2
|
+
import { contentLines } from "../cards.js";
|
|
2
3
|
function parseArguments(argumentsJson) {
|
|
3
4
|
try {
|
|
4
5
|
return JSON.parse(argumentsJson);
|
package/lib/agent/present.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,YAAY,EAA4C,MAAM,aAAa,CAAA;AAEpF,SAAS,cAAc,CAAC,aAAqB;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAY,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,OAAgB;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,SAAQ;QACzD,MAAM,MAAM,GAAG,KAAgC,CAAA;QAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAA;IAC3F,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY,EAAE,KAA+B;IAC/E,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,aAAa;YACtB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,cAAc,CAAC,aAAa,CAAU,CAAC,CAAA;gBACpF,wEAAwE;gBACxE,wEAAwE;gBACxE,sEAAsE;gBACtE,yCAAyC;gBACzC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,KAAsB;YACjC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,CAC5C,cAAc,CAAC,KAAK,CAAC,aAAa,CAAU,EAC5C,EAAE,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAW,CACjG,CAAA;gBACD,OAAO,YAAY,CAAC,IAAI,EAAE;oBACxB,IAAI;oBACJ,MAAM,EAAE,KAAK,CAAC,OAAO;oBACrB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC;iBAC1C,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ToolSubCall, type ToolSubCallRows, type ToolCard } from '../cards.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Nested calls retained on one PTC card.
|
|
4
|
+
*
|
|
5
|
+
* A program can dispatch in a loop, and one line each is small but not free;
|
|
6
|
+
* the cap keeps a card bounded while {@link ToolCard.subCallsTotal} still
|
|
7
|
+
* reports everything the program ran.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SUBCALL_MAX = 100;
|
|
10
|
+
/**
|
|
11
|
+
* What the fold learned about one dispatched call, in the order it learned it.
|
|
12
|
+
*
|
|
13
|
+
* A call is logged twice — once when the program asks for it, once with what it
|
|
14
|
+
* answered — and each moment supplies part of the row: the call view names it,
|
|
15
|
+
* the result view says how it ended and what it produced.
|
|
16
|
+
*/
|
|
17
|
+
export interface SubCallFacts {
|
|
18
|
+
/** The card the call declared, or nothing when no presenter answered for it. */
|
|
19
|
+
readonly view: ToolCard | undefined;
|
|
20
|
+
/** The rows the row opens to, once the call reported them. */
|
|
21
|
+
readonly output: ToolSubCallRows | undefined;
|
|
22
|
+
/** How the call finished, as the tool that ran it reported. */
|
|
23
|
+
readonly status: string | undefined;
|
|
24
|
+
/** Whether the call is logged and still unanswered. */
|
|
25
|
+
readonly running: boolean;
|
|
26
|
+
/** Whether the call is one the log marked as an error. */
|
|
27
|
+
readonly failed: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The one-line row for one nested call, as its start or its settle leaves it.
|
|
31
|
+
*
|
|
32
|
+
* A tool that declares a call view is drawn exactly as its own header would be;
|
|
33
|
+
* only a tool with no view at all falls back to its registry name and raw call,
|
|
34
|
+
* because the surface cannot name a salient argument for a schema it never saw.
|
|
35
|
+
* A settle logs the same call again, and the presenter that answered the first
|
|
36
|
+
* time is asked again for the view it would draw — so the row's identity comes
|
|
37
|
+
* from the row already on screen wherever there is one, and only a call that was
|
|
38
|
+
* dropped earlier is named from the log. The row also keeps how the call ended,
|
|
39
|
+
* because a program's calls are told apart by their outcomes rather than by
|
|
40
|
+
* anything the program itself says about them.
|
|
41
|
+
*/
|
|
42
|
+
export declare function subCallRow(id: string, name: string, argumentsJson: string, facts: SubCallFacts, existing?: ToolSubCall): ToolSubCall;
|
|
43
|
+
/**
|
|
44
|
+
* The rows one dispatched call keeps, from the card that drew them.
|
|
45
|
+
*
|
|
46
|
+
* The total is clamped to what was kept: a card built from a call or result
|
|
47
|
+
* view reports no total of its own, because it is not a stream retention had to
|
|
48
|
+
* cut, and a count below the rows on screen would hint at rows already shown.
|
|
49
|
+
*/
|
|
50
|
+
export declare function subCallRows(card: ToolCard | undefined): ToolSubCallRows | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* The header fields a rebuilt card must keep.
|
|
53
|
+
*
|
|
54
|
+
* Replacing a card's rows says nothing about what the call was made with, what
|
|
55
|
+
* it measured, how it ended, or which calls it dispatched, so those fields
|
|
56
|
+
* travel with the rebuild: dropping the argument is how a shell card loses its
|
|
57
|
+
* command the moment a presenter declines the result.
|
|
58
|
+
*/
|
|
59
|
+
export declare function carriedFields(card: ToolCard): Pick<ToolCard, 'argument' | 'stats' | 'status' | 'subCalls' | 'subCallsTotal'>;
|
|
60
|
+
/**
|
|
61
|
+
* Collapse one call and its result into the single row the reader sees.
|
|
62
|
+
*
|
|
63
|
+
* The pending row already told the reader what the tool is doing, so the
|
|
64
|
+
* settled row keeps that header and swaps in the outcome; a result that
|
|
65
|
+
* presents nothing keeps the call's detail rather than blanking the row.
|
|
66
|
+
*/
|
|
67
|
+
export declare function mergeCards(call: ToolCard | undefined, result: ToolCard | undefined): ToolCard;
|
|
68
|
+
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../src/cards/composition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAY,MAAM,aAAa,CAAA;AAG7F;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,MAAM,CAAA;AAE9B;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAA;IACnC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAA;IAC5C,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,WAAW,CAuBpI;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAGnF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC,CAQ5H;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CA8B7F"}
|