@gaunt-sloth/core 2.0.0-alpha.9 → 2.0.0-beta.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/.gsloth.review.md +2 -0
- package/README.md +71 -20
- package/dist/config/colour.d.ts +38 -0
- package/dist/config/colour.js +36 -0
- package/dist/config/colour.js.map +1 -0
- package/dist/config/configDiscovery.d.ts +79 -0
- package/dist/config/configDiscovery.js +80 -0
- package/dist/config/configDiscovery.js.map +1 -0
- package/dist/config/defaults.d.ts +20 -20
- package/dist/config/defaults.js +10 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/filesystem-tools.d.ts +41 -0
- package/dist/config/filesystem-tools.js +56 -0
- package/dist/config/filesystem-tools.js.map +1 -0
- package/dist/config/loader.d.ts +171 -19
- package/dist/config/loader.js +1054 -144
- package/dist/config/loader.js.map +1 -1
- package/dist/config/mouse.d.ts +50 -0
- package/dist/config/mouse.js +44 -0
- package/dist/config/mouse.js.map +1 -0
- package/dist/config/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providerKeys.d.ts +69 -0
- package/dist/config/providerKeys.js +69 -0
- package/dist/config/providerKeys.js.map +1 -0
- package/dist/config/schema.d.ts +2671 -138
- package/dist/config/schema.js +1361 -85
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +899 -111
- package/dist/config/shell-policy.js +800 -70
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +211 -0
- package/dist/config/tool-descriptions.js +272 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +352 -41
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +35 -1
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +28 -1
- package/dist/constants.js +28 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +166 -11
- package/dist/core/GthAbstractAgent.js +484 -45
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +543 -57
- package/dist/core/GthAgentRunner.js +1494 -140
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +117 -2
- package/dist/core/GthLangChainAgent.js +602 -29
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/annotations.d.ts +122 -0
- package/dist/core/approvals/annotations.js +137 -0
- package/dist/core/approvals/annotations.js.map +1 -0
- package/dist/core/approvals/grants.d.ts +216 -0
- package/dist/core/approvals/grants.js +469 -0
- package/dist/core/approvals/grants.js.map +1 -0
- package/dist/core/approvals/matcher.d.ts +202 -0
- package/dist/core/approvals/matcher.js +267 -0
- package/dist/core/approvals/matcher.js.map +1 -0
- package/dist/core/approvals/mcpSubjects.d.ts +40 -0
- package/dist/core/approvals/mcpSubjects.js +99 -0
- package/dist/core/approvals/mcpSubjects.js.map +1 -0
- package/dist/core/approvals/promptHeader.d.ts +28 -0
- package/dist/core/approvals/promptHeader.js +62 -0
- package/dist/core/approvals/promptHeader.js.map +1 -0
- package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
- package/dist/core/approvals/toolAnnotationSources.js +277 -0
- package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
- package/dist/core/approvals/toolHost.d.ts +46 -0
- package/dist/core/approvals/toolHost.js +108 -0
- package/dist/core/approvals/toolHost.js.map +1 -0
- package/dist/core/debugCapture.d.ts +74 -0
- package/dist/core/debugCapture.js +100 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +4 -4
- package/dist/core/gthLeanAgentFactory.js +4 -4
- package/dist/core/launchBanner.d.ts +127 -0
- package/dist/core/launchBanner.js +414 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/modelLabel.d.ts +19 -0
- package/dist/core/modelLabel.js +26 -0
- package/dist/core/modelLabel.js.map +1 -0
- package/dist/core/plainToolIndication.d.ts +15 -0
- package/dist/core/plainToolIndication.js +174 -0
- package/dist/core/plainToolIndication.js.map +1 -0
- package/dist/core/reasoningBlocks.d.ts +65 -0
- package/dist/core/reasoningBlocks.js +103 -0
- package/dist/core/reasoningBlocks.js.map +1 -0
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +133 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/runHeader.d.ts +38 -0
- package/dist/core/runHeader.js +42 -0
- package/dist/core/runHeader.js.map +1 -0
- package/dist/core/runStats.d.ts +14 -3
- package/dist/core/runStats.js +48 -3
- package/dist/core/runStats.js.map +1 -1
- package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
- package/dist/core/shell/ShellCommandFailedError.js +3 -4
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
- package/dist/core/shell/abstention.d.ts +88 -0
- package/dist/core/shell/abstention.js +184 -0
- package/dist/core/shell/abstention.js.map +1 -0
- package/dist/core/shell/approvalCapture.d.ts +271 -0
- package/dist/core/shell/approvalCapture.js +108 -0
- package/dist/core/shell/approvalCapture.js.map +1 -0
- package/dist/core/shell/approvalStop.d.ts +123 -0
- package/dist/core/shell/approvalStop.js +269 -0
- package/dist/core/shell/approvalStop.js.map +1 -0
- package/dist/core/shell/arity.d.ts +6 -0
- package/dist/core/shell/arity.js +20 -6
- package/dist/core/shell/arity.js.map +1 -1
- package/dist/core/shell/denylist.d.ts +11 -0
- package/dist/core/shell/denylist.js +37 -0
- package/dist/core/shell/denylist.js.map +1 -0
- package/dist/core/shell/escalationSeverity.d.ts +141 -0
- package/dist/core/shell/escalationSeverity.js +89 -0
- package/dist/core/shell/escalationSeverity.js.map +1 -0
- package/dist/core/shell/framing.d.ts +190 -0
- package/dist/core/shell/framing.js +633 -0
- package/dist/core/shell/framing.js.map +1 -0
- package/dist/core/shell/hardline.d.ts +103 -0
- package/dist/core/shell/hardline.js +780 -0
- package/dist/core/shell/hardline.js.map +1 -0
- package/dist/core/shell/negotiation.d.ts +328 -0
- package/dist/core/shell/negotiation.js +488 -0
- package/dist/core/shell/negotiation.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +44 -4
- package/dist/core/shell/normalize.js +61 -7
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +263 -0
- package/dist/core/shell/openWorld.js +1188 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +873 -0
- package/dist/core/shell/rater.js +1454 -0
- package/dist/core/shell/rater.js.map +1 -0
- package/dist/core/shell/raterModel.d.ts +41 -0
- package/dist/core/shell/raterModel.js +51 -0
- package/dist/core/shell/raterModel.js.map +1 -0
- package/dist/core/shell/raterVocabulary.d.ts +121 -0
- package/dist/core/shell/raterVocabulary.js +116 -0
- package/dist/core/shell/raterVocabulary.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +69 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +123 -0
- package/dist/core/toolDisplay.js +451 -0
- package/dist/core/toolDisplay.js.map +1 -0
- package/dist/core/toolOutputChannel.d.ts +95 -0
- package/dist/core/toolOutputChannel.js +165 -0
- package/dist/core/toolOutputChannel.js.map +1 -0
- package/dist/core/types.d.ts +349 -16
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +12 -3
- package/dist/history/historyFormat.js +50 -8
- package/dist/history/historyFormat.js.map +1 -1
- package/dist/history/historyStore.d.ts +77 -0
- package/dist/history/historyStore.js +173 -6
- package/dist/history/historyStore.js.map +1 -1
- package/dist/history/recordSession.d.ts +10 -1
- package/dist/history/recordSession.js +27 -0
- package/dist/history/recordSession.js.map +1 -1
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/configurationPassthrough.d.ts +107 -0
- package/dist/providers/configurationPassthrough.js +148 -0
- package/dist/providers/configurationPassthrough.js.map +1 -0
- package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
- package/dist/providers/geminiSchemaSanitizer.js +347 -0
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
- package/dist/providers/geminiThinking.d.ts +60 -0
- package/dist/providers/geminiThinking.js +92 -0
- package/dist/providers/geminiThinking.js.map +1 -0
- package/dist/providers/google-genai.js +18 -1
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.js +12 -0
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/huggingface.d.ts +25 -0
- package/dist/providers/huggingface.js +69 -0
- package/dist/providers/huggingface.js.map +1 -0
- package/dist/providers/modelCatalog.d.ts +109 -0
- package/dist/providers/modelCatalog.js +245 -0
- package/dist/providers/modelCatalog.js.map +1 -0
- package/dist/providers/modelDiscovery.d.ts +99 -5
- package/dist/providers/modelDiscovery.js +191 -35
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +18 -4
- package/dist/providers/ollama.js +67 -37
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +26 -4
- package/dist/providers/openrouter.js +83 -26
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.js +19 -1
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.js +20 -0
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +105 -0
- package/dist/runtime/askStructured.js +120 -0
- package/dist/runtime/askStructured.js.map +1 -0
- package/dist/runtime/conversation.d.ts +64 -0
- package/dist/runtime/conversation.js +171 -0
- package/dist/runtime/conversation.js.map +1 -0
- package/dist/runtime/singleShot.d.ts +37 -6
- package/dist/runtime/singleShot.js +113 -67
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/runtime/structuredOutput.d.ts +104 -0
- package/dist/runtime/structuredOutput.js +393 -0
- package/dist/runtime/structuredOutput.js.map +1 -0
- package/dist/utils/ProgressIndicator.d.ts +21 -0
- package/dist/utils/ProgressIndicator.js +30 -3
- package/dist/utils/ProgressIndicator.js.map +1 -1
- package/dist/utils/aiignoreUtils.js.map +1 -1
- package/dist/utils/binaryOutputUtils.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +95 -0
- package/dist/utils/consoleUtils.js +112 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +134 -0
- package/dist/utils/debugDump.js +381 -0
- package/dist/utils/debugDump.js.map +1 -0
- package/dist/utils/debugUtils.d.ts +13 -4
- package/dist/utils/debugUtils.js +36 -13
- package/dist/utils/debugUtils.js.map +1 -1
- package/dist/utils/displayWidth.d.ts +53 -0
- package/dist/utils/displayWidth.js +195 -0
- package/dist/utils/displayWidth.js.map +1 -0
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/globalConfigUtils.d.ts +14 -2
- package/dist/utils/globalConfigUtils.js +22 -4
- package/dist/utils/globalConfigUtils.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +39 -8
- package/dist/utils/llmUtils.js +76 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.d.ts +63 -0
- package/dist/utils/redactSecrets.js +286 -0
- package/dist/utils/redactSecrets.js.map +1 -0
- package/dist/utils/systemPromptNotes.d.ts +222 -0
- package/dist/utils/systemPromptNotes.js +338 -0
- package/dist/utils/systemPromptNotes.js.map +1 -0
- package/dist/utils/systemUtils.d.ts +18 -1
- package/dist/utils/systemUtils.js +38 -3
- package/dist/utils/systemUtils.js.map +1 -1
- package/dist/utils/toolMatching.d.ts +30 -0
- package/dist/utils/toolMatching.js +44 -0
- package/dist/utils/toolMatching.js.map +1 -0
- package/dist/utils/untrustedText.d.ts +86 -0
- package/dist/utils/untrustedText.js +101 -0
- package/dist/utils/untrustedText.js.map +1 -0
- package/package.json +21 -7
- package/schema/gsloth-config.schema.json +1921 -326
- package/dist/core/shell/allowlist.d.ts +0 -75
- package/dist/core/shell/allowlist.js +0 -187
- package/dist/core/shell/allowlist.js.map +0 -1
- package/dist/core/shell/judge.d.ts +0 -161
- package/dist/core/shell/judge.js +0 -261
- package/dist/core/shell/judge.js.map +0 -1
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module launchBanner
|
|
3
|
+
* TUI-C33 — the welcoming ASCII-art launch banner shown at the top of an INTERACTIVE session
|
|
4
|
+
* (`gth chat` / `gth code`): a sloth face on the left, the `GAUNT SLOTH` wordmark plus three live
|
|
5
|
+
* fields (version, model/provider, working directory) on the right.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* ▄█▀▀▀▀▀▀▀▀█▄ ┏┓ ┏┓┓ ┓
|
|
9
|
+
* ▄▀█▄█▀▀▀▀▀▀█▄█▀▄ ┃┓┏┓┓┏┏┓╋ ┗┓┃┏┓╋┣┓ v2.0.0-alpha.25
|
|
10
|
+
* █ ▀█▄▀ ▀▄█▀ █ ┗┛┗┻┗┻┛┗┗ ┗┛┗┗┛┗┛┗
|
|
11
|
+
* ▀▄▀▀ ██████ ▀▀▄▀ gemini-3.1-pro (google-genai)
|
|
12
|
+
* ▀██████████▀ ~/dev/takahe
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately NOT emitted by the one-shot verbs (`ask`, `review`, `pr`, `get`, `eval`,
|
|
15
|
+
* `batch`, `api`, `init`): their stdout is piped into files, diffs and CI, where a banner is
|
|
16
|
+
* corruption rather than welcome.
|
|
17
|
+
*
|
|
18
|
+
* ## Why this module is pure, and split in two halves
|
|
19
|
+
*
|
|
20
|
+
* The banner has to render IDENTICALLY on two surfaces that share no rendering code — the Ink TUI
|
|
21
|
+
* (`packages/app/src/tui/components/LaunchBanner.tsx`) and the plain `--no-tui` readline session
|
|
22
|
+
* (`packages/agent/src/modules/interactiveSessionModule.ts`). So all the geometry lives here as
|
|
23
|
+
* pure functions of `{ version, model, provider, directory, homeDir, columns, colour }`
|
|
24
|
+
* ({@link launchBannerRows} / {@link launchBannerText}), and the surfaces differ only in how they
|
|
25
|
+
* EMIT: one string through `displayLaunchBanner`, or one `<Text>` per row. That is the same split
|
|
26
|
+
* `Rule.tsx` uses (pure `ruleWidth` + a thin React component), including its
|
|
27
|
+
* `columns === undefined → 80` convention for a non-TTY / test stdout.
|
|
28
|
+
*
|
|
29
|
+
* The one impure function is {@link launchBannerFields} at the bottom, which reads the live
|
|
30
|
+
* version / project dir / home dir. It is shared for the same reason: both surfaces must read the
|
|
31
|
+
* same sources rather than each inventing its own.
|
|
32
|
+
*
|
|
33
|
+
* ## Why the right column is a slice at a fixed column, not a glyph classifier
|
|
34
|
+
*
|
|
35
|
+
* Colouring is "columns 0–20 are magenta, columns 21+ are default foreground" — a per-line split at
|
|
36
|
+
* {@link RIGHT_COLUMN}. It is emphatically NOT "is this character a block element": the wordmark is
|
|
37
|
+
* box-drawing, the face is block elements, and the moment a field value contained a `▀` (or the art
|
|
38
|
+
* gained a non-block glyph) a classifier would mis-colour it. A fixed column cannot.
|
|
39
|
+
*
|
|
40
|
+
* The face is 16 columns wide and the right column starts at {@link RIGHT_COLUMN}, leaving a
|
|
41
|
+
* 5-column gutter. The version label sits three columns after the widest wordmark line, i.e. at
|
|
42
|
+
* {@link VERSION_COLUMN}.
|
|
43
|
+
*
|
|
44
|
+
* ## Why the padding is part of the layout rather than of either surface
|
|
45
|
+
*
|
|
46
|
+
* TUI-C36 — the block is a splash, so it gets breathing room: one blank row above, one blank row
|
|
47
|
+
* below, and a {@link LEFT_PAD}-column left margin on every art row. That padding lives HERE, with
|
|
48
|
+
* the rest of the geometry, for the same reason the columns do — the Ink surface and the readline
|
|
49
|
+
* surface must show the same block, and a margin added by one renderer is a margin the other one
|
|
50
|
+
* lacks. The blank rows are genuinely empty (no pad, no escape): a padding row that carried
|
|
51
|
+
* trailing spaces would be visible the moment anything selected or copied it.
|
|
52
|
+
*
|
|
53
|
+
* The pad column shifts every column constant by exactly one, which is why they are all derived
|
|
54
|
+
* from {@link LEFT_PAD} rather than written as literals. The field budgets are expressed against
|
|
55
|
+
* those constants (`columns - RIGHT_COLUMN`), so they absorb the shift without changing.
|
|
56
|
+
*
|
|
57
|
+
* ## Why every dynamic field is bounded, and why the version is the one that is DROPPED
|
|
58
|
+
*
|
|
59
|
+
* A wrapped line SHATTERS the art: the continuation starts back at column 0 and collides with the
|
|
60
|
+
* face, so the sloth grows an extra row of model id. Wrapping is therefore not allowed to happen at
|
|
61
|
+
* all, and each field is bounded by what is left of the terminal on its own line — `columns - 21`
|
|
62
|
+
* for the model/provider and directory lines, `columns - 43` for the version.
|
|
63
|
+
*
|
|
64
|
+
* Those budgets are TERMINAL COLUMNS, and every measurement and cut in this module goes through
|
|
65
|
+
* `#src/utils/displayWidth.js` for that reason. A CJK ideograph or an emoji is one code point in
|
|
66
|
+
* two columns, so a `.length`-style count would let a path or model id containing either measure
|
|
67
|
+
* short, slip past its budget and wrap — the very failure above.
|
|
68
|
+
*
|
|
69
|
+
* The model/provider and directory lines TRUNCATE to that budget: a clipped model id or path is
|
|
70
|
+
* still honest, it visibly ends in `…`. The directory is truncated from the LEFT
|
|
71
|
+
* (`…/dev/takahe`), because the leaf directory is the informative end; the model keeps its head and
|
|
72
|
+
* loses its tail.
|
|
73
|
+
*
|
|
74
|
+
* A VERSION that does not fit is dropped entirely instead ({@link fitOrDrop}) — never truncated.
|
|
75
|
+
* A clipped version number is not merely less informative, it is MISLEADING: `v2.0…` reads as a
|
|
76
|
+
* real, different version (2.0.1, say) rather than as a clipped `2.0.0-alpha.25`, and a wrong
|
|
77
|
+
* version in a bug report costs more than an absent one. The model/provider line already sets that
|
|
78
|
+
* drop-rather-than-mislead precedent by omitting itself when nothing resolves.
|
|
79
|
+
*
|
|
80
|
+
* Below {@link MIN_BANNER_COLUMNS} the right column cannot fit at all, so the face prints alone
|
|
81
|
+
* rather than wrapping the wordmark into rubble.
|
|
82
|
+
*/
|
|
83
|
+
import { homedir } from 'node:os';
|
|
84
|
+
import { ELLIPSIS, ELLIPSIS_WIDTH } from '#src/core/toolDisplay.js';
|
|
85
|
+
import { modelProviderLabel } from '#src/core/modelLabel.js';
|
|
86
|
+
import { ANSI_COLORS } from '#src/utils/consoleUtils.js';
|
|
87
|
+
import { displayWidth, sliceEndToWidth, sliceToWidth } from '#src/utils/displayWidth.js';
|
|
88
|
+
import { getProjectDir, getSlothVersion } from '#src/utils/systemUtils.js';
|
|
89
|
+
/**
|
|
90
|
+
* The sloth face, columns 0–15. Lines 1 and 5 are 14 columns, lines 2–4 are 16 — the face field is
|
|
91
|
+
* 16 wide and the short lines simply do not reach the edge. Do not "tidy" the leading spaces: they
|
|
92
|
+
* are part of the art.
|
|
93
|
+
*/
|
|
94
|
+
const FACE_LINES = [
|
|
95
|
+
' ▄█▀▀▀▀▀▀▀▀█▄',
|
|
96
|
+
'▄▀█▄█▀▀▀▀▀▀█▄█▀▄',
|
|
97
|
+
'█ ▀█▄▀ ▀▄█▀ █',
|
|
98
|
+
'▀▄▀▀ ██████ ▀▀▄▀',
|
|
99
|
+
' ▀██████████▀',
|
|
100
|
+
];
|
|
101
|
+
/**
|
|
102
|
+
* TUI-C40 — the resting face, under the name the animation layer refers to it by.
|
|
103
|
+
*
|
|
104
|
+
* Every frame below is the same five lines and the same 16-column field, so swapping one in changes
|
|
105
|
+
* nothing about the layout: the right column still starts at {@link RIGHT_COLUMN} and the
|
|
106
|
+
* truncation budgets are untouched. That invariant is what makes an animation a data change rather
|
|
107
|
+
* than a geometry change, and it is asserted per frame in the specs.
|
|
108
|
+
*/
|
|
109
|
+
export const RESTING_FACE = FACE_LINES;
|
|
110
|
+
/**
|
|
111
|
+
* The extra faces, authored in takahē (`websites/ascii-art.md`) and transcribed here so the art has
|
|
112
|
+
* one home in this repo, exactly as the resting frame does. Names match the source file's blocks.
|
|
113
|
+
*/
|
|
114
|
+
const BLINK_HALF = [
|
|
115
|
+
' ▄█▀▀▀▀▀▀▀██▄',
|
|
116
|
+
'▄▀█▄█▀▀▀▀▀▀███▀▄',
|
|
117
|
+
'█ ▀█▄▀ ▀▄█▀ █',
|
|
118
|
+
'▀▄▀▀▀▐████▌▀▀▀▄▀',
|
|
119
|
+
' ▀██████████▀',
|
|
120
|
+
];
|
|
121
|
+
const BLINK_SHUT = [
|
|
122
|
+
' ▄█▀▀▀▀▀▀███▄',
|
|
123
|
+
'▄▀█▄█▀▀▀▀▀▀███▀▄',
|
|
124
|
+
'█ ▀█▄▀ ▀▄█▀ █',
|
|
125
|
+
'▀▄▀▀▀▐████▌ ▄▀',
|
|
126
|
+
' ▀██████████▀',
|
|
127
|
+
];
|
|
128
|
+
const NOD_DOWN = [
|
|
129
|
+
' ▄██████████▄',
|
|
130
|
+
'▄██▀▄▄▄▄▄▄▄▄▀██▄',
|
|
131
|
+
'█ ▀██ ▄ ▄ ██▀ █',
|
|
132
|
+
'█ ▄▄▀█▄▄▄▄█▀▄▄ █',
|
|
133
|
+
' ▄▄▄██████▄▄▄',
|
|
134
|
+
];
|
|
135
|
+
const LOOK_LEFT = [
|
|
136
|
+
' ▄██████████▄',
|
|
137
|
+
'▄██▀▄▄▄▄▄▄▄████▄',
|
|
138
|
+
'█ ▀██ ▄ ▄ ██▀ █',
|
|
139
|
+
'█ ▄▀█▄▄▄▄█▀ ▄ █',
|
|
140
|
+
' ▄▄▄██████▄▄▄',
|
|
141
|
+
];
|
|
142
|
+
const LOOK_RIGHT = [
|
|
143
|
+
' ▄██████████▄',
|
|
144
|
+
'▄████▄▄▄▄▄▄▄▀██▄',
|
|
145
|
+
'█ ▀██ ▄ ▄ ██▀ █',
|
|
146
|
+
'█ ▄ ▀█▄▄▄▄█▀▄ █',
|
|
147
|
+
' ▄▄▄██████▄▄▄',
|
|
148
|
+
];
|
|
149
|
+
const EYEROLL = [
|
|
150
|
+
' ▄█▀▀▀▀▀▀▀▀█▄',
|
|
151
|
+
'▄▀█▄█▀▀▀▀▀▀█▄█▀▄',
|
|
152
|
+
'█ ▀██▄▀ ▀▄██▀ █',
|
|
153
|
+
'▀▌ ▄ ▐████▌ ▄ ▐▀',
|
|
154
|
+
' ▀██████████▀',
|
|
155
|
+
];
|
|
156
|
+
/** The animations a click can play. This list is the complete set. */
|
|
157
|
+
export const SLOTH_ANIMATIONS = ['blink', 'nod', 'look-around', 'eyeroll'];
|
|
158
|
+
/**
|
|
159
|
+
* The four sequences, as the frames shown AFTER the resting face.
|
|
160
|
+
*
|
|
161
|
+
* Playback always settles back on {@link RESTING_FACE} once the last step's hold elapses, so no
|
|
162
|
+
* sequence lists a trailing rest — but `nod` lists rest in the MIDDLE, because nodding twice means
|
|
163
|
+
* returning to level between the two dips. None of them loops, and a click plays exactly one.
|
|
164
|
+
*/
|
|
165
|
+
export const SLOTH_ANIMATION_STEPS = {
|
|
166
|
+
// Quick, because a blink that lingers reads as a squint.
|
|
167
|
+
blink: [
|
|
168
|
+
{ face: BLINK_HALF, holdMs: 90 },
|
|
169
|
+
{ face: BLINK_SHUT, holdMs: 110 },
|
|
170
|
+
],
|
|
171
|
+
nod: [
|
|
172
|
+
{ face: NOD_DOWN, holdMs: 160 },
|
|
173
|
+
{ face: RESTING_FACE, holdMs: 130 },
|
|
174
|
+
{ face: NOD_DOWN, holdMs: 160 },
|
|
175
|
+
],
|
|
176
|
+
'look-around': [
|
|
177
|
+
{ face: LOOK_LEFT, holdMs: 190 },
|
|
178
|
+
{ face: LOOK_RIGHT, holdMs: 230 },
|
|
179
|
+
],
|
|
180
|
+
// The longest hold: the joke only lands if you get to see it.
|
|
181
|
+
eyeroll: [{ face: EYEROLL, holdMs: 300 }],
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Pick one animation at random. `random` is injectable so a test can pin the choice — the
|
|
185
|
+
* production call passes nothing and gets `Math.random`.
|
|
186
|
+
*/
|
|
187
|
+
export function pickSlothAnimation(random = Math.random) {
|
|
188
|
+
const index = Math.floor(random() * SLOTH_ANIMATIONS.length);
|
|
189
|
+
// Guard the boundary: a `random()` of exactly 1 (or a stub returning it) would index past the end.
|
|
190
|
+
return SLOTH_ANIMATIONS[Math.min(index, SLOTH_ANIMATIONS.length - 1)];
|
|
191
|
+
}
|
|
192
|
+
/** The `GAUNT SLOTH` wordmark, occupying rows 1–3 of the right column. */
|
|
193
|
+
const WORDMARK_LINES = [
|
|
194
|
+
'┏┓ ┏┓┓ ┓',
|
|
195
|
+
'┃┓┏┓┓┏┏┓╋ ┗┓┃┏┓╋┣┓',
|
|
196
|
+
'┗┛┗┻┗┻┛┗┗ ┗┛┗┗┛┗┛┗',
|
|
197
|
+
];
|
|
198
|
+
/** Widest wordmark line (19); rows 1 and 3 are padded to it so the version label lands square. */
|
|
199
|
+
const WORDMARK_WIDTH = Math.max(...WORDMARK_LINES.map((line) => displayWidth(line)));
|
|
200
|
+
/** Blank columns between the wordmark and the version label. */
|
|
201
|
+
const VERSION_GAP = 3;
|
|
202
|
+
/**
|
|
203
|
+
* Blank columns prepended to every art row (TUI-C36). One column of left margin, so the block does
|
|
204
|
+
* not sit flush against the edge of the terminal.
|
|
205
|
+
*/
|
|
206
|
+
export const LEFT_PAD = 1;
|
|
207
|
+
/** The prepended margin itself, applied to art rows only — never to the blank padding rows. */
|
|
208
|
+
const PAD = ' '.repeat(LEFT_PAD);
|
|
209
|
+
/** Columns the face field occupies, after the margin. */
|
|
210
|
+
const FACE_WIDTH = 16;
|
|
211
|
+
/** Blank columns between the face field and the right column. */
|
|
212
|
+
const FACE_GAP = 5;
|
|
213
|
+
/**
|
|
214
|
+
* Column at which the right-hand column begins on EVERY row — the single number the whole layout
|
|
215
|
+
* and the colour split are expressed in terms of (margin + face + gutter = 22).
|
|
216
|
+
*/
|
|
217
|
+
export const RIGHT_COLUMN = LEFT_PAD + FACE_WIDTH + FACE_GAP;
|
|
218
|
+
/** Column at which the version label begins on row 2 (right column + wordmark + gap = 44). */
|
|
219
|
+
export const VERSION_COLUMN = RIGHT_COLUMN + WORDMARK_WIDTH + VERSION_GAP;
|
|
220
|
+
/**
|
|
221
|
+
* Field budget the narrowest banner still affords the right column — the widest wordmark line plus
|
|
222
|
+
* a few columns of field, which is what the original threshold of 45 amounted to at
|
|
223
|
+
* {@link RIGHT_COLUMN} 21.
|
|
224
|
+
*/
|
|
225
|
+
const MIN_FIELD_BUDGET = 24;
|
|
226
|
+
/**
|
|
227
|
+
* Narrowest terminal that still gets the right column. The wordmark itself ends at column 40, so
|
|
228
|
+
* anything narrower than this leaves no usable room for the fields beside it; below the threshold
|
|
229
|
+
* the face prints alone (see the module docs on why wrapping is not an option).
|
|
230
|
+
*/
|
|
231
|
+
export const MIN_BANNER_COLUMNS = RIGHT_COLUMN + MIN_FIELD_BUDGET;
|
|
232
|
+
/** Width assumed when the terminal width is unknown (non-TTY / tests) — as in `ruleWidth`. */
|
|
233
|
+
const DEFAULT_COLUMNS = 80;
|
|
234
|
+
/**
|
|
235
|
+
* Right-pad `text` to `width` TERMINAL COLUMNS. `String.prototype.padEnd` counts UTF-16 units, so
|
|
236
|
+
* a field holding a wide glyph would be filled one column too far for each of them — and since the
|
|
237
|
+
* split column is the single number this whole layout is expressed in terms of, that shifts the
|
|
238
|
+
* right column and pushes the row past the terminal. Text already at or past the width is returned
|
|
239
|
+
* untouched, exactly as `padEnd` leaves it.
|
|
240
|
+
*/
|
|
241
|
+
function padToWidth(text, width) {
|
|
242
|
+
return text + ' '.repeat(Math.max(0, width - displayWidth(text)));
|
|
243
|
+
}
|
|
244
|
+
/** Resolve the usable terminal width, mirroring `ruleWidth`'s unknown/non-finite handling. */
|
|
245
|
+
function resolveColumns(columns) {
|
|
246
|
+
const cols = typeof columns === 'number' && Number.isFinite(columns) ? columns : DEFAULT_COLUMNS;
|
|
247
|
+
return Math.floor(cols);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Truncate `text` to `budget` columns, losing the TAIL (`gemini-3.1-pro-experi…`). Returns
|
|
251
|
+
* `undefined` only when there is no budget at all; that guard keeps the function total (at
|
|
252
|
+
* `budget === 0` the slice below would otherwise return a bare marker) and is not reachable from
|
|
253
|
+
* {@link launchBannerRows}, whose narrowest field budget is `46 - 22 = 24`.
|
|
254
|
+
*
|
|
255
|
+
* `budget` is TERMINAL COLUMNS, and so is every measurement here: measuring and slicing both go
|
|
256
|
+
* through the shared width primitive, which counts a CJK ideograph or an emoji as the two columns
|
|
257
|
+
* it draws and cuts only between whole grapheme clusters. A code-point count would let such a
|
|
258
|
+
* value measure short, escape its budget and wrap into the face; a code-point slice would cut a
|
|
259
|
+
* cluster in half.
|
|
260
|
+
*/
|
|
261
|
+
function truncateTail(text, budget) {
|
|
262
|
+
if (budget <= 0)
|
|
263
|
+
return undefined;
|
|
264
|
+
if (displayWidth(text) <= budget)
|
|
265
|
+
return text;
|
|
266
|
+
return sliceToWidth(text, budget - ELLIPSIS_WIDTH) + ELLIPSIS;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Truncate `text` to `budget` columns, losing the HEAD (`…/dev/takahe`). Used for the directory,
|
|
270
|
+
* where the leaf is what the user needs and the ancestors are noise. Columns, not code points —
|
|
271
|
+
* see {@link truncateTail}.
|
|
272
|
+
*/
|
|
273
|
+
function truncateHead(text, budget) {
|
|
274
|
+
if (budget <= 0)
|
|
275
|
+
return undefined;
|
|
276
|
+
if (displayWidth(text) <= budget)
|
|
277
|
+
return text;
|
|
278
|
+
return ELLIPSIS + sliceEndToWidth(text, budget - ELLIPSIS_WIDTH);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* All-or-nothing: `text` if it fits `budget` columns in full, otherwise `undefined`. Used for the
|
|
282
|
+
* VERSION label, which must never be truncated — see the module docs on why a clipped version
|
|
283
|
+
* number is misleading rather than merely terse. "Fits" is measured in columns, so a wide glyph
|
|
284
|
+
* cannot buy its way in on a short code-point count and push the row past the terminal.
|
|
285
|
+
*/
|
|
286
|
+
function fitOrDrop(text, budget) {
|
|
287
|
+
return displayWidth(text) <= budget ? text : undefined;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Collapse the home-directory prefix to `~`, the way a shell prompt does — a deep path spends its
|
|
291
|
+
* whole budget on `/Users/<name>` otherwise.
|
|
292
|
+
*
|
|
293
|
+
* Both separators are accepted deliberately: on Windows `homedir()` reads `%USERPROFILE%` and the
|
|
294
|
+
* path arrives back-slashed, and hard-coding POSIX `/` here is exactly the win32-only bug this repo
|
|
295
|
+
* keeps re-learning. The boundary check matters too — `/home/mar` must not collapse inside
|
|
296
|
+
* `/home/maria/x`.
|
|
297
|
+
*/
|
|
298
|
+
function collapseHomePrefix(directory, homeDir) {
|
|
299
|
+
if (!homeDir)
|
|
300
|
+
return directory;
|
|
301
|
+
if (directory === homeDir)
|
|
302
|
+
return '~';
|
|
303
|
+
if (!directory.startsWith(homeDir))
|
|
304
|
+
return directory;
|
|
305
|
+
const boundary = directory.charAt(homeDir.length);
|
|
306
|
+
if (boundary !== '/' && boundary !== '\\')
|
|
307
|
+
return directory;
|
|
308
|
+
return '~' + directory.slice(homeDir.length);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* The shared `model (provider)` spelling, re-exported so this module stays the documented home of
|
|
312
|
+
* the banner's fields. It lives in `modelLabel.ts` because the agent renders it too and must not
|
|
313
|
+
* import this module's console/filesystem graph to do so.
|
|
314
|
+
*/
|
|
315
|
+
export { modelProviderLabel };
|
|
316
|
+
/** A padding row: no margin and no content, so it is an empty line on every surface. */
|
|
317
|
+
function blankRow() {
|
|
318
|
+
return { face: '', right: '' };
|
|
319
|
+
}
|
|
320
|
+
/** Wrap the five art rows in the blank row above and below that make the block a splash. */
|
|
321
|
+
function withPaddingRows(art) {
|
|
322
|
+
return [blankRow(), ...art, blankRow()];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Assemble the banner as {@link LaunchBannerRow}s: a blank row, the five art rows each pre-split at
|
|
326
|
+
* {@link RIGHT_COLUMN}, and a blank row. Pure — every input arrives as an argument, so the geometry
|
|
327
|
+
* is testable without a terminal, a config or a process.
|
|
328
|
+
*/
|
|
329
|
+
export function launchBannerRows(input) {
|
|
330
|
+
const columns = resolveColumns(input.columns);
|
|
331
|
+
// TUI-C40 — the face is the one part a caller may swap; everything below is unchanged geometry.
|
|
332
|
+
const face = input.face ?? RESTING_FACE;
|
|
333
|
+
// Too narrow for the right column: the face alone, with no fields. It keeps its left margin and
|
|
334
|
+
// its blank rows — the padding is what makes it a splash, and it costs one column, not the
|
|
335
|
+
// gutter's five.
|
|
336
|
+
if (columns < MIN_BANNER_COLUMNS) {
|
|
337
|
+
return withPaddingRows(face.map((line) => ({ face: PAD + line, right: '' })));
|
|
338
|
+
}
|
|
339
|
+
// Every field is budgeted against ITS OWN start column, because the version starts further right
|
|
340
|
+
// than the other two. The version is the only fit-or-drop field; the other two truncate.
|
|
341
|
+
const fieldBudget = columns - RIGHT_COLUMN;
|
|
342
|
+
const version = input.version?.trim()
|
|
343
|
+
? fitOrDrop(`v${input.version.trim()}`, columns - VERSION_COLUMN)
|
|
344
|
+
: undefined;
|
|
345
|
+
const model = modelProviderLabel(input.model, input.provider);
|
|
346
|
+
const modelLine = model ? truncateTail(model, fieldBudget) : undefined;
|
|
347
|
+
const directory = input.directory?.trim()
|
|
348
|
+
? truncateHead(collapseHomePrefix(input.directory.trim(), input.homeDir), fieldBudget)
|
|
349
|
+
: undefined;
|
|
350
|
+
const rightLines = [
|
|
351
|
+
WORDMARK_LINES[0],
|
|
352
|
+
version
|
|
353
|
+
? padToWidth(WORDMARK_LINES[1], WORDMARK_WIDTH) + ' '.repeat(VERSION_GAP) + version
|
|
354
|
+
: WORDMARK_LINES[1],
|
|
355
|
+
WORDMARK_LINES[2],
|
|
356
|
+
modelLine ?? '',
|
|
357
|
+
directory ?? '',
|
|
358
|
+
];
|
|
359
|
+
return withPaddingRows(face.map((line, index) => {
|
|
360
|
+
const right = rightLines[index] ?? '';
|
|
361
|
+
const padded = PAD + line;
|
|
362
|
+
// Pad the face out to the split column only when something follows it, so a row whose field
|
|
363
|
+
// was omitted (or truncated away) does not end in a run of spaces.
|
|
364
|
+
return { face: right ? padToWidth(padded, RIGHT_COLUMN) : padded, right };
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Render the banner as one multi-line string for the plain (readline) surface.
|
|
369
|
+
*
|
|
370
|
+
* Colour is the 16-colour `magenta` slot from {@link ANSI_COLORS} — the SAME slot the rest of the
|
|
371
|
+
* CLI uses — and never a 256-colour or 24-bit escape: the 16-colour slot picks up the violet from
|
|
372
|
+
* the user's own terminal theme, so the sloth looks native in both light and dark schemes. The
|
|
373
|
+
* right column carries no escape at all, not even dim.
|
|
374
|
+
*
|
|
375
|
+
* With `colour: false` the output contains ZERO escape sequences — not one dim wrapper, not one
|
|
376
|
+
* reset — so a monochrome session (`useColour: false`) stays genuinely clean. The caller decides;
|
|
377
|
+
* the plain surface passes `getUseColour()`, the TUI passes nothing and lets Ink's own
|
|
378
|
+
* colour-support detection make the same call.
|
|
379
|
+
*/
|
|
380
|
+
export function launchBannerText(input) {
|
|
381
|
+
return launchBannerRows(input)
|
|
382
|
+
.map(({ face, right }) => {
|
|
383
|
+
// A padding row is an empty line and nothing else: wrapping it in the colour pair would put
|
|
384
|
+
// two escapes on a row that has no glyph to paint, which the colour-off guarantee and the
|
|
385
|
+
// one-pair-per-row assertion both read as a bug.
|
|
386
|
+
if (!face && !right)
|
|
387
|
+
return '';
|
|
388
|
+
return input.colour
|
|
389
|
+
? `${ANSI_COLORS.magenta}${face}${ANSI_COLORS.reset}${right}`
|
|
390
|
+
: face + right;
|
|
391
|
+
})
|
|
392
|
+
.join('\n');
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Read the live fields off the process, so both surfaces report the same three values from the same
|
|
396
|
+
* sources instead of each picking its own. `model`/`provider` come from the caller because only it
|
|
397
|
+
* has the resolved config (`config.modelDisplayName` / `config.modelProviderType`); the rest are
|
|
398
|
+
* process-global.
|
|
399
|
+
*
|
|
400
|
+
* Fail-soft, as in `debugDump`: `getSlothVersion()` reads the installed `package.json` through the
|
|
401
|
+
* install dir that the CLI entry point sets, and THROWS when nothing set it (an embedded caller, a
|
|
402
|
+
* unit test). A missing version drops one label — it must never stop a session from starting.
|
|
403
|
+
*/
|
|
404
|
+
export function launchBannerFields(model, provider) {
|
|
405
|
+
let version;
|
|
406
|
+
try {
|
|
407
|
+
version = getSlothVersion();
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
/* no install dir (embedded/test) — the banner simply shows no version */
|
|
411
|
+
}
|
|
412
|
+
return { version, model, provider, directory: getProjectDir(), homeDir: homedir() };
|
|
413
|
+
}
|
|
414
|
+
//# sourceMappingURL=launchBanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchBanner.js","sourceRoot":"","sources":["../../src/core/launchBanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module modelLabel
|
|
3
|
+
* The ONE spelling of "which model served this run", on its own so every surface can reach it.
|
|
4
|
+
*
|
|
5
|
+
* It is a leaf with **no imports at all**, and that is the point: the agent emits the compact run
|
|
6
|
+
* header from `GthAbstractAgent`, and pulling in the launch banner — which reaches console and
|
|
7
|
+
* filesystem helpers — just to format two strings would drag that graph into the agent's module
|
|
8
|
+
* load. A second local `${model} (${provider})` would avoid the import and is exactly what must not
|
|
9
|
+
* happen (DL-6): that is how one surface ends up saying `google-genai:gemini` where its neighbour
|
|
10
|
+
* says `gemini (google-genai)`.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Build the model/provider label: `model (provider)` when both resolve, the one that resolved when
|
|
14
|
+
* only one does, and nothing at all when neither does — an empty `()` would look like a bug.
|
|
15
|
+
*
|
|
16
|
+
* Its three renderings today are the launch banner, the review document's attribution line, and the
|
|
17
|
+
* `compact` run header.
|
|
18
|
+
*/
|
|
19
|
+
export declare function modelProviderLabel(model?: string, provider?: string): string | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module modelLabel
|
|
3
|
+
* The ONE spelling of "which model served this run", on its own so every surface can reach it.
|
|
4
|
+
*
|
|
5
|
+
* It is a leaf with **no imports at all**, and that is the point: the agent emits the compact run
|
|
6
|
+
* header from `GthAbstractAgent`, and pulling in the launch banner — which reaches console and
|
|
7
|
+
* filesystem helpers — just to format two strings would drag that graph into the agent's module
|
|
8
|
+
* load. A second local `${model} (${provider})` would avoid the import and is exactly what must not
|
|
9
|
+
* happen (DL-6): that is how one surface ends up saying `google-genai:gemini` where its neighbour
|
|
10
|
+
* says `gemini (google-genai)`.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Build the model/provider label: `model (provider)` when both resolve, the one that resolved when
|
|
14
|
+
* only one does, and nothing at all when neither does — an empty `()` would look like a bug.
|
|
15
|
+
*
|
|
16
|
+
* Its three renderings today are the launch banner, the review document's attribution line, and the
|
|
17
|
+
* `compact` run header.
|
|
18
|
+
*/
|
|
19
|
+
export function modelProviderLabel(model, provider) {
|
|
20
|
+
const m = model?.trim();
|
|
21
|
+
const p = provider?.trim();
|
|
22
|
+
if (m && p)
|
|
23
|
+
return `${m} (${p})`;
|
|
24
|
+
return m || p || undefined;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=modelLabel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelLabel.js","sourceRoot":"","sources":["../../src/core/modelLabel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAAE,QAAiB;IAClE,MAAM,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACxB,MAAM,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PlainToolIndicationObserver {
|
|
2
|
+
/** Feed every streamed message/chunk (the string path's existing loop) through this. */
|
|
3
|
+
observe(chunk: unknown): void;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Create the per-stream observer. State is scoped to one stream (one `agent.stream()` call);
|
|
7
|
+
* tool_call deltas are accumulated from `tool_call_chunks` (keyed by the provider's chunk
|
|
8
|
+
* `index`, which restarts per LLM round — the map is flushed into the by-id map whenever a
|
|
9
|
+
* `ToolMessage` arrives, mirroring `processEventStream`'s reset-per-round). Deliberately does
|
|
10
|
+
* NOT `concat()` whole `AIMessageChunk`s: only the tool-call slices are needed, which also
|
|
11
|
+
* sidesteps the TUI-C29 `__raw_response` aggregation-growth trap entirely.
|
|
12
|
+
*
|
|
13
|
+
* `emit` is injectable for tests; production uses the INFO-level `displayToolIndication`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPlainToolIndication(emit?: (text: string) => void): PlainToolIndicationObserver;
|