@sayknow-cli/coding-agent 0.6.5 → 0.6.7
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/CHANGELOG.md +29 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +79 -0
- package/dist/types/i18n/messages/en.d.ts +6 -29
- package/dist/types/modes/components/welcome.d.ts +34 -40
- package/dist/types/modes/interactive-mode.d.ts +7 -4
- package/dist/types/modes/types.d.ts +4 -0
- package/dist/types/sdk/broker/broker.d.ts +22 -0
- package/dist/types/sdk/broker/process-guard.d.ts +71 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/auth-storage-discovery.d.ts +15 -0
- package/dist/types/session/auto-fallback.d.ts +27 -0
- package/dist/types/session/fallback-chain-controller.d.ts +5 -0
- package/dist/types/session/response-language.d.ts +25 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +8 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +2 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/locate-core.d.ts +97 -0
- package/dist/types/tools/locate.d.ts +38 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/src/cli/setup-cli.ts +7 -4
- package/src/commands/sdk.ts +3 -0
- package/src/commands/setup.ts +4 -1
- package/src/config/keybindings.ts +7 -0
- package/src/config/settings-schema.ts +82 -0
- package/src/decisions/typesafe-backend.ts +38 -4
- package/src/i18n/messages/de.settings.ts +26 -0
- package/src/i18n/messages/de.ts +4 -27
- package/src/i18n/messages/en.ts +6 -29
- package/src/i18n/messages/es.settings.ts +26 -0
- package/src/i18n/messages/es.ts +4 -27
- package/src/i18n/messages/fr.settings.ts +26 -0
- package/src/i18n/messages/fr.ts +4 -27
- package/src/i18n/messages/ja.settings.ts +26 -0
- package/src/i18n/messages/ja.ts +4 -27
- package/src/i18n/messages/ko.settings.ts +25 -0
- package/src/i18n/messages/ko.ts +5 -28
- package/src/i18n/messages/zh.settings.ts +23 -0
- package/src/i18n/messages/zh.ts +4 -27
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/modes/action-registry.ts +1 -0
- package/src/modes/components/welcome.ts +385 -387
- package/src/modes/controllers/input-controller.ts +15 -0
- package/src/modes/controllers/selector-controller.ts +13 -0
- package/src/modes/interactive-mode.ts +160 -98
- package/src/modes/types.ts +4 -0
- package/src/prompts/system/system-prompt.md +7 -2
- package/src/prompts/tools/locate.md +12 -0
- package/src/sdk/broker/broker.ts +75 -1
- package/src/sdk/broker/process-guard.ts +160 -0
- package/src/sdk/broker/transport.ts +15 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +13 -1
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session.ts +4 -1
- package/src/session/agent-session.ts +118 -2
- package/src/session/auth-storage-discovery.ts +22 -7
- package/src/session/auto-fallback.ts +59 -0
- package/src/session/fallback-chain-controller.ts +5 -0
- package/src/session/response-language.ts +71 -0
- package/src/setup/model-onboarding-guidance.ts +29 -14
- package/src/setup/provider-onboarding.ts +5 -0
- package/src/slash-commands/builtin-registry.ts +112 -2
- package/src/tools/index.ts +3 -0
- package/src/tools/locate-core.ts +720 -0
- package/src/tools/locate.ts +197 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.6.7] - 2026-09-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Launch card sessions open from the card: press `↓` in the empty composer to highlight a recent session, `↑`/`↓` to move, `Enter` to open it (`Esc` returns to typing), or click it. Until the first prompt SKC captures the mouse so the click reaches the card, then hands it back to the terminal; `startup.welcomeMouse` (Settings → Interaction → Clickable Launch Card) turns that off.
|
|
10
|
+
- `locate` tool: find code by describing what it does. Jev (TypeSafe) judges relevance while walking the tree: large folders are split and only plausible subfolders are entered, then candidate files are ranked from their outlines. Only folder paths, file names and declaration lines are sent (no bodies, comments, imports or literal values; initialisers are cut). Offered only when a TypeSafe key is stored; `locate.enabled` turns it off. The six best files are then compared in one question to settle their order. A local keyword score over each file's full text (read on this machine, never sent) is blended in. On 154 held-out commit questions from 8 public repositories (Python, TypeScript, Go, Rust), an answer file ranked first 51% of the time, in the top five 78% and in the top ten 86%, against 27%, 58% and 69% for keyword grep; a search returns about 5 KB. Block comments, docstrings, multi-line strings, trailing comments and string-literal lines are kept out of what is sent. See `docs/tools/locate.md`.
|
|
11
|
+
- Automatic model fallback. When the default model is blocked — no credentials, quota, auth or server errors — the session now continues on a model from another provider you are logged in to instead of stopping. The chain is the configured default (a single model or a `modelRoles` array), then `fallback.models`, then one model per other logged-in provider (the one you used most recently there, else the provider's curated default; up to three). A missing login is skipped before any request; failures during a turn retry `fallback.maxAttempts` times, rotate to the provider's next logged-in account, then move on, and the status line shows each switch. Automatic picks are never added under an `enabledModels` allow-list, for keyless local providers, for an explicit `--model`, or for subagent chains, and they are not written into the session's configured chain. Turn it off with `fallback.auto` (Settings → Model → Automatic Model Fallback).
|
|
12
|
+
- `/fallback` shows the chain the next prompt walks — where each model came from (`default`, `chain`, `added`, `auto`) and which is in use — and edits it: `/fallback add <model>`, `remove <n|model>`, `clear`, `auto on|off`. Added models are kept when you pick another default in `/model`.
|
|
13
|
+
- Settings → Providers → **Multi-Account Order** (`auth.credentialRankingMode`: `balanced` or `earliest-reset`) chooses which logged-in account of a provider a new session uses; it applies immediately. The existing `SKC_CREDENTIAL_RANKING_MODE` env var still overrides it per machine. Settings → Model also shows **Fallback Attempts per Model** (`fallback.maxAttempts`).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Answers no longer drift into English after long tool work. The system prompt now always carries a reply-language rule (the language the user writes in, or the one they asked for, for the whole turn including the final report; English tool output, logs, skills and summaries never change it); `reasoningLanguage: english` only changes reasoning. When a message is clearly written in a non-Latin script (Korean, Japanese, Chinese, Russian, Arabic, Hebrew, Thai, Hindi, Greek), the turn starts with a one-line reminder naming that language. Code blocks, inline code, quotes and links are ignored when detecting it, so a pasted English log with one Korean word does not count. Compaction and handoff summaries now record the reply language, so it survives a context compaction.
|
|
18
|
+
- Typed decisions (Jev) kept failing with 401 in every session that was already open when the TypeSafe key was replaced, because a session reads stored credentials once at startup and kept sending the revoked key; routing then fell back to an uncalibrated model. On 401/403 the TypeSafe backend now reloads stored credentials and retries once if the key changed; an unchanged key is not retried.
|
|
19
|
+
- SDK background processes no longer live forever. A broker now stops after 30 minutes with no client connection, no request in flight and no live session host (the next client starts a fresh one; chat daemons now start one when the discovery file is missing). A session host stops once its ownership marker or worktree has been gone for three checks in a row. A broker or host run from TypeScript source kills itself as soon as its entry script disappears, so ejecting the drive that holds a checkout no longer leaves a process spinning at 100% CPU in Bun's fault handler on the unmapped native addon. Found after brokers and hosts left behind by SDK tests stayed up for two days and one pinned a core when the external drive disconnected.
|
|
20
|
+
- A missing-credential error no longer opens with "For MiniMax/GLM presets…" whatever provider was called. It names the provider that lacks credentials and its `/login <provider>` (or env var) first, then lists other ways to add a provider. Preset lists in these messages, `/provider`, and `skc setup provider` come from the bundled preset catalog instead of a hand-kept `minimax|minimax-cn|glm`, so `alibaba-token-plan` is no longer missing from them.
|
|
21
|
+
|
|
22
|
+
## [0.6.6] - 2026-09-28
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `ctrl+q` (`app.session.continue`) continues the most recent saved session in one keystroke, from the launch card or mid-session; it never resumes the live session into itself and refuses while a turn is running. The launch card marks that session with `›` and names the key next to `alt+r` for the full list. It is a Control chord on purpose: many macOS terminals turn Option into composed text, so Option shortcuts such as `alt+r` only work where the terminal sends Option as Meta.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Launch card redesign: the SAYKNOW CLI wordmark with a small version of the pet (8×4, drawn for the size) beside it, tagline and version under the wordmark, model and project/branch aligned in the same column (the place moves to its own row instead of being cut off). The release line and the recent-sessions heading are rule headers (`label ──── hints`) that divide the card, and the keys row uses accent keys with `·` separators.
|
|
31
|
+
- The composer's input rows sit on a background band (`userMessageBg`) with the rail, so the line being typed is distinct from the transcript.
|
|
32
|
+
- The launch screen is a compact card instead of the two-column ledger. The Sayknow pet — the same octopus sprite as the composer pet, drawn in half blocks so it needs no image protocol — sits beside four lines — version, tagline, model with reasoning level, and project path with branch and change counts — followed by one line after an update with the new version and its first note (pointing at `/changelog`), the three most recent sessions (with the key that opens the full list), and a single row of keys. Commits, preset, role models, MCP/skill/rule/LSP status, workflows, and the full keymap are no longer on it; they are one key or command away. The card stays within 76 columns on wide terminals and, when rows run short, drops the keys row first, then the release line, then the sessions. The pet is the one you keep beside the composer (`pet.mode`), or, with the pet off, blue on `blue-octopus` and red elsewhere. The intro has the pet sway its tentacles and lets color spread for about half a second; every fact is readable from the first frame, and `startup.skipLogoAnimation` still turns it off.
|
|
33
|
+
|
|
5
34
|
## [0.6.5] - 2026-09-26
|
|
6
35
|
|
|
7
36
|
### Added
|
|
@@ -30,6 +30,7 @@ interface AppKeybindings {
|
|
|
30
30
|
"app.session.tree": true;
|
|
31
31
|
"app.session.fork": true;
|
|
32
32
|
"app.session.resume": true;
|
|
33
|
+
"app.session.continue": true;
|
|
33
34
|
"app.session.observe": true;
|
|
34
35
|
"app.session.dashboard": true;
|
|
35
36
|
"app.jobs.open": true;
|
|
@@ -167,6 +168,10 @@ export declare const KEYBINDINGS: {
|
|
|
167
168
|
readonly defaultKeys: "alt+r";
|
|
168
169
|
readonly description: "Resume session";
|
|
169
170
|
};
|
|
171
|
+
readonly "app.session.continue": {
|
|
172
|
+
readonly defaultKeys: "ctrl+q";
|
|
173
|
+
readonly description: "Continue the most recent session";
|
|
174
|
+
};
|
|
170
175
|
readonly "app.session.observe": {
|
|
171
176
|
readonly defaultKeys: "ctrl+s";
|
|
172
177
|
readonly description: "Observe subagent sessions";
|
|
@@ -1456,6 +1456,43 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1456
1456
|
readonly type: "number";
|
|
1457
1457
|
readonly default: 3;
|
|
1458
1458
|
readonly validate: (value: number) => boolean;
|
|
1459
|
+
readonly ui: {
|
|
1460
|
+
readonly tab: "model";
|
|
1461
|
+
readonly label: "Fallback Attempts per Model";
|
|
1462
|
+
readonly description: "Tries on each model in the fallback chain (including the first) before moving to the next one. Missing credentials skip a model immediately.";
|
|
1463
|
+
readonly options: readonly [{
|
|
1464
|
+
readonly value: "1";
|
|
1465
|
+
readonly label: "1 try";
|
|
1466
|
+
}, {
|
|
1467
|
+
readonly value: "2";
|
|
1468
|
+
readonly label: "2 tries";
|
|
1469
|
+
}, {
|
|
1470
|
+
readonly value: "3";
|
|
1471
|
+
readonly label: "3 tries";
|
|
1472
|
+
}, {
|
|
1473
|
+
readonly value: "5";
|
|
1474
|
+
readonly label: "5 tries";
|
|
1475
|
+
}];
|
|
1476
|
+
};
|
|
1477
|
+
};
|
|
1478
|
+
/**
|
|
1479
|
+
* When the default model is blocked, keep going on another logged-in provider
|
|
1480
|
+
* after the configured chain and `fallback.models` run out. The extra entries
|
|
1481
|
+
* are computed per session and never written into the configured chain.
|
|
1482
|
+
*/
|
|
1483
|
+
readonly "fallback.auto": {
|
|
1484
|
+
readonly type: "boolean";
|
|
1485
|
+
readonly default: true;
|
|
1486
|
+
readonly ui: {
|
|
1487
|
+
readonly tab: "model";
|
|
1488
|
+
readonly label: "Automatic Model Fallback";
|
|
1489
|
+
readonly description: "When the default model is blocked (no login, quota, auth or server errors), continue on a model from another logged-in provider. /fallback shows the chain.";
|
|
1490
|
+
};
|
|
1491
|
+
};
|
|
1492
|
+
/** Models to try, in order, after the default chain. Edited with `/fallback`. */
|
|
1493
|
+
readonly "fallback.models": {
|
|
1494
|
+
readonly type: "array";
|
|
1495
|
+
readonly default: string[];
|
|
1459
1496
|
};
|
|
1460
1497
|
readonly "retry.enabled": {
|
|
1461
1498
|
readonly type: "boolean";
|
|
@@ -1691,6 +1728,15 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1691
1728
|
}];
|
|
1692
1729
|
};
|
|
1693
1730
|
};
|
|
1731
|
+
readonly "startup.welcomeMouse": {
|
|
1732
|
+
readonly type: "boolean";
|
|
1733
|
+
readonly default: true;
|
|
1734
|
+
readonly ui: {
|
|
1735
|
+
readonly tab: "interaction";
|
|
1736
|
+
readonly label: "Clickable Launch Card";
|
|
1737
|
+
readonly description: "Until the first prompt, capture the mouse so a recent session on the launch card opens with one click. Terminal text selection needs Shift held meanwhile. Off: open sessions with ↓ and Enter.";
|
|
1738
|
+
};
|
|
1739
|
+
};
|
|
1694
1740
|
readonly "startup.skipLogoAnimation": {
|
|
1695
1741
|
readonly type: "boolean";
|
|
1696
1742
|
readonly default: false;
|
|
@@ -3587,6 +3633,20 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
3587
3633
|
readonly description: "Hide non-essential built-in tools behind a search tool to save tokens.";
|
|
3588
3634
|
};
|
|
3589
3635
|
};
|
|
3636
|
+
/**
|
|
3637
|
+
* The `locate` tool: find code by describing what it does, judged by Jev. Offered only
|
|
3638
|
+
* when a TypeSafe key is stored. Sends folder paths, file names and declaration lines
|
|
3639
|
+
* (never bodies or comments) to TypeSafe.
|
|
3640
|
+
*/
|
|
3641
|
+
readonly "locate.enabled": {
|
|
3642
|
+
readonly type: "boolean";
|
|
3643
|
+
readonly default: true;
|
|
3644
|
+
readonly ui: {
|
|
3645
|
+
readonly tab: "tools";
|
|
3646
|
+
readonly label: "Locate (Jev code search)";
|
|
3647
|
+
readonly description: "Offer the locate tool when a TypeSafe key is stored. It sends folder paths, file names and declaration lines (no function bodies or comments) to TypeSafe to judge relevance.";
|
|
3648
|
+
};
|
|
3649
|
+
};
|
|
3590
3650
|
readonly "tools.essentialOverride": {
|
|
3591
3651
|
readonly type: "array";
|
|
3592
3652
|
readonly default: string[];
|
|
@@ -4002,6 +4062,25 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
4002
4062
|
readonly type: "boolean";
|
|
4003
4063
|
readonly default: false;
|
|
4004
4064
|
};
|
|
4065
|
+
readonly "auth.credentialRankingMode": {
|
|
4066
|
+
readonly type: "enum";
|
|
4067
|
+
readonly values: readonly ["balanced", "earliest-reset"];
|
|
4068
|
+
readonly default: "balanced";
|
|
4069
|
+
readonly ui: {
|
|
4070
|
+
readonly tab: "providers";
|
|
4071
|
+
readonly label: "Multi-Account Order";
|
|
4072
|
+
readonly description: "Which logged-in account of a provider a new session uses. A blocked or exhausted account is always skipped for the next one.";
|
|
4073
|
+
readonly options: readonly [{
|
|
4074
|
+
readonly value: "balanced";
|
|
4075
|
+
readonly label: "Balanced";
|
|
4076
|
+
readonly description: "Least-used account first; spreads load and keeps headroom on every account";
|
|
4077
|
+
}, {
|
|
4078
|
+
readonly value: "earliest-reset";
|
|
4079
|
+
readonly label: "Earliest reset";
|
|
4080
|
+
readonly description: "Account whose usage window resets soonest first, so quota is not lost at reset";
|
|
4081
|
+
}];
|
|
4082
|
+
};
|
|
4083
|
+
};
|
|
4005
4084
|
readonly "secrets.enabled": {
|
|
4006
4085
|
readonly type: "boolean";
|
|
4007
4086
|
readonly default: false;
|
|
@@ -8,41 +8,18 @@ export declare const en: {
|
|
|
8
8
|
readonly "lang.fr": "Français";
|
|
9
9
|
readonly "lang.de": "Deutsch";
|
|
10
10
|
readonly "welcome.tagline": "Coding should feel like thinking.";
|
|
11
|
-
readonly "welcome.workflows": "Workflows";
|
|
12
|
-
readonly "welcome.wf.deepInterview": "scope · interview → spec";
|
|
13
|
-
readonly "welcome.wf.ralplan": "consensus plan";
|
|
14
|
-
readonly "welcome.wf.ultragoal": "autonomous build";
|
|
15
|
-
readonly "welcome.wf.team": "parallel agents";
|
|
16
|
-
readonly "welcome.flowKeys": "Flow keys";
|
|
17
11
|
readonly "welcome.commands": "commands";
|
|
18
|
-
readonly "welcome.actions": "actions";
|
|
19
|
-
readonly "welcome.shell": "shell";
|
|
20
|
-
readonly "welcome.python": "python";
|
|
21
12
|
readonly "welcome.keymap": "keymap";
|
|
22
13
|
readonly "welcome.model": "model";
|
|
23
|
-
readonly "welcome.
|
|
24
|
-
readonly "welcome.
|
|
25
|
-
readonly "welcome.noLsp": "No LSP servers";
|
|
26
|
-
readonly "welcome.sessionTrail": "Session trail";
|
|
27
|
-
readonly "welcome.noSessions": "No saved trails";
|
|
14
|
+
readonly "welcome.sessionTrail": "Recent sessions";
|
|
15
|
+
readonly "welcome.noSessions": "No saved sessions";
|
|
28
16
|
readonly "welcome.allSessions": "{key} all sessions";
|
|
17
|
+
readonly "welcome.continue": "{key} continue";
|
|
18
|
+
readonly "welcome.pick": "{key} pick";
|
|
19
|
+
readonly "welcome.pickActive": "↑↓ move · ⏎ open · esc back";
|
|
20
|
+
readonly "welcome.sessions": "sessions";
|
|
29
21
|
readonly "welcome.chooseModel": "choose a model";
|
|
30
22
|
readonly "welcome.modelHint": "ctrl+l to pick · / for commands";
|
|
31
|
-
readonly "welcome.label.workspace": "workspace";
|
|
32
|
-
readonly "welcome.label.branch": "branch";
|
|
33
|
-
readonly "welcome.label.commits": "commits";
|
|
34
|
-
readonly "welcome.label.model": "model";
|
|
35
|
-
readonly "welcome.label.reasoning": "reasoning";
|
|
36
|
-
readonly "welcome.label.preset": "preset";
|
|
37
|
-
readonly "welcome.label.roles": "roles";
|
|
38
|
-
readonly "welcome.label.tools": "tools";
|
|
39
|
-
readonly "welcome.noGit": "not a git repository";
|
|
40
|
-
readonly "welcome.rolesInherit": "roles follow the default model";
|
|
41
|
-
readonly "welcome.skills": "skills";
|
|
42
|
-
readonly "welcome.rules": "rules";
|
|
43
|
-
readonly "welcome.clean": "clean";
|
|
44
|
-
readonly "welcome.whatsNew": "What's new";
|
|
45
|
-
readonly "welcome.readyPrompt": "Ready for your next prompt";
|
|
46
23
|
readonly "settings.tab.appearance": "Appearance";
|
|
47
24
|
readonly "settings.tab.model": "Model";
|
|
48
25
|
readonly "settings.tab.interaction": "Interaction";
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@sayknow-cli/agent-core";
|
|
2
|
-
import { type Component } from "@sayknow-cli/tui";
|
|
2
|
+
import { type Component, type PetSkinId } from "@sayknow-cli/tui";
|
|
3
3
|
import { type KeyDisplayContext } from "../../config/keybindings";
|
|
4
4
|
export interface RecentSession {
|
|
5
5
|
name: string;
|
|
6
6
|
timeAgo: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
name: string;
|
|
10
|
-
status: "idle" | "ready" | "error" | "connecting";
|
|
11
|
-
fileTypes: string[];
|
|
12
|
-
}
|
|
13
|
-
export interface WelcomeRoleBinding {
|
|
14
|
-
role: string;
|
|
15
|
-
model: string;
|
|
7
|
+
/** Session file; present when the row can be opened from the card. */
|
|
8
|
+
path?: string;
|
|
16
9
|
}
|
|
17
10
|
/**
|
|
18
|
-
* Workspace
|
|
19
|
-
*
|
|
20
|
-
* asynchronous probes (git status, context files, MCP) settle.
|
|
11
|
+
* Workspace facts on the launch card. Every field is optional: the card renders
|
|
12
|
+
* what is known and fills in git state once the asynchronous probe settles.
|
|
21
13
|
*/
|
|
22
14
|
export interface WelcomeSnapshot {
|
|
23
15
|
/** Display path of the project directory (already home-shortened). */
|
|
@@ -29,19 +21,7 @@ export interface WelcomeSnapshot {
|
|
|
29
21
|
unstaged: number;
|
|
30
22
|
untracked: number;
|
|
31
23
|
} | null;
|
|
32
|
-
/** Latest commits as `<short-sha> <subject>` onelines, newest first. */
|
|
33
|
-
recentCommits?: readonly string[];
|
|
34
24
|
thinkingLevel?: ThinkingLevel | string;
|
|
35
|
-
/** Display name of the active model preset. */
|
|
36
|
-
profile?: string;
|
|
37
|
-
roles?: readonly WelcomeRoleBinding[];
|
|
38
|
-
mcp?: {
|
|
39
|
-
connected: number;
|
|
40
|
-
total: number;
|
|
41
|
-
};
|
|
42
|
-
skills?: number;
|
|
43
|
-
/** Display names of the loaded project instruction files (AGENTS.md, …). */
|
|
44
|
-
contextFiles?: readonly string[];
|
|
45
25
|
}
|
|
46
26
|
export type WelcomeLogoMode = "unicode" | "square" | "ascii";
|
|
47
27
|
export interface WelcomeComponentOptions {
|
|
@@ -49,21 +29,26 @@ export interface WelcomeComponentOptions {
|
|
|
49
29
|
getReservedBottomRows?: (termWidth: number) => number;
|
|
50
30
|
changelogMarkdown?: string;
|
|
51
31
|
rightGutterWidth?: number;
|
|
32
|
+
/** Show only the version on the release line, without its first note. */
|
|
52
33
|
collapseChangelog?: boolean;
|
|
53
34
|
buildLabel?: string;
|
|
54
35
|
keyDisplayContext?: KeyDisplayContext;
|
|
55
36
|
skipLogoAnimation?: boolean;
|
|
56
37
|
snapshot?: WelcomeSnapshot;
|
|
57
|
-
/** Key bound to the resume picker (`app.session.resume`); the
|
|
38
|
+
/** Key bound to the resume picker (`app.session.resume`); the card names it. */
|
|
58
39
|
resumeKey?: string;
|
|
40
|
+
/** Key bound to `app.session.continue`, which resumes the first session on the card. */
|
|
41
|
+
continueKey?: string;
|
|
42
|
+
/** Which Sayknow pet stands on the card (default red). */
|
|
43
|
+
petSkin?: PetSkinId;
|
|
44
|
+
/** Called when a session row is opened by click or Enter. */
|
|
45
|
+
onOpenSession?: (session: RecentSession) => void;
|
|
59
46
|
}
|
|
60
47
|
/**
|
|
61
|
-
* Sayknow-CLI launch
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* and keys. No enclosing box and no hero wordmark: the octopus mark and the
|
|
66
|
-
* facts carry the identity.
|
|
48
|
+
* Sayknow-CLI launch card: the wordmark with the pet beside it, who and where you are
|
|
49
|
+
* (version, model and reasoning, project and branch), the release line after an update,
|
|
50
|
+
* the three most recent sessions — which can be opened from the card with a click or
|
|
51
|
+
* ↓ then Enter — and a single row of keys.
|
|
67
52
|
*/
|
|
68
53
|
export declare class WelcomeComponent implements Component {
|
|
69
54
|
#private;
|
|
@@ -71,24 +56,33 @@ export declare class WelcomeComponent implements Component {
|
|
|
71
56
|
private modelName;
|
|
72
57
|
private providerName;
|
|
73
58
|
private recentSessions;
|
|
74
|
-
private lspServers;
|
|
75
59
|
private readonly logoMode;
|
|
76
60
|
private readonly options;
|
|
77
|
-
constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[],
|
|
61
|
+
constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[], logoMode?: WelcomeLogoMode, options?: WelcomeComponentOptions);
|
|
78
62
|
invalidate(): void;
|
|
79
63
|
/**
|
|
80
|
-
* Play
|
|
81
|
-
*
|
|
82
|
-
* session, so a sub-second reveal is affordable; it never blocks input.
|
|
83
|
-
* Safe to call multiple times — subsequent calls reset and replay.
|
|
64
|
+
* Play the short launch intro. Content is fully readable from the first frame;
|
|
65
|
+
* only color and the tentacles move. Safe to call again — it restarts.
|
|
84
66
|
*/
|
|
85
67
|
playIntro(requestRender: () => void): void;
|
|
86
68
|
dispose(): void;
|
|
87
69
|
setModel(modelName: string, providerName: string): void;
|
|
88
70
|
setRecentSessions(sessions: RecentSession[]): void;
|
|
89
|
-
|
|
90
|
-
/** Merge newly probed workspace facts into the ledger. */
|
|
71
|
+
/** Merge newly probed workspace facts into the card. */
|
|
91
72
|
setSnapshot(patch: WelcomeSnapshot): void;
|
|
73
|
+
/** Rows that can be opened right now. Zero once the card stops taking input. */
|
|
74
|
+
get openableCount(): number;
|
|
75
|
+
get selectedIndex(): number | undefined;
|
|
76
|
+
/** Stop offering the session rows (after the first prompt or a session switch). */
|
|
77
|
+
endInteraction(): void;
|
|
78
|
+
get interactive(): boolean;
|
|
79
|
+
/** Highlight a row, or clear the highlight with undefined. Returns false when there is nothing to select. */
|
|
80
|
+
select(index: number | undefined): boolean;
|
|
81
|
+
/** Move the highlight; returns false when it would leave the list upward (the caller hands focus back). */
|
|
82
|
+
moveSelection(delta: number): boolean;
|
|
83
|
+
/** Open the highlighted row. */
|
|
84
|
+
openSelected(): boolean;
|
|
85
|
+
handleClick(line: number): boolean;
|
|
92
86
|
render(termWidth: number): string[];
|
|
93
87
|
}
|
|
94
88
|
/** Resolve the intro cadence without making tests mutate global process state. */
|
|
@@ -22,7 +22,7 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
|
|
|
22
22
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
23
23
|
import type { HookInputComponent } from "./components/hook-input";
|
|
24
24
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
25
|
-
import { type PetMode, SayknowPetWidget } from "./components/sayknow-pet-widget";
|
|
25
|
+
import { type PetMode, type PetSkinId, SayknowPetWidget } from "./components/sayknow-pet-widget";
|
|
26
26
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
27
27
|
import { StatusLineComponent } from "./components/tool-status-header";
|
|
28
28
|
import { type WelcomeLogoMode } from "./components/welcome";
|
|
@@ -42,10 +42,10 @@ export declare function getComposerPlaceholder(keybindings: Pick<KeybindingsMana
|
|
|
42
42
|
}): string;
|
|
43
43
|
export declare function getWelcomeTranscriptReservedRows(chatContainer: Container, width: number): number;
|
|
44
44
|
/**
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
* border color (session accent, thinking level, shell/python mode).
|
|
45
|
+
* The pet on the launch card: the one the user keeps beside the composer, or —
|
|
46
|
+
* with the pet off — the skin that matches the theme (blue for blue-octopus).
|
|
48
47
|
*/
|
|
48
|
+
export declare function resolveWelcomePetSkin(petMode: PetMode, themeName: string | undefined): PetSkinId;
|
|
49
49
|
export declare function configureDefaultComposerChrome(editor: CustomEditor): void;
|
|
50
50
|
export type WelcomeBannerSettingMode = "auto" | "unicode" | "square" | "ascii";
|
|
51
51
|
export declare function resolveWelcomeLogoMode(mode: WelcomeBannerSettingMode, env?: Record<string, string | undefined>, platform?: NodeJS.Platform): WelcomeLogoMode;
|
|
@@ -276,6 +276,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
|
|
|
276
276
|
showUserMessageSelector(): void;
|
|
277
277
|
showTreeSelector(): void;
|
|
278
278
|
showSessionSelector(): void;
|
|
279
|
+
/** The card stops taking input: first prompt sent, a session opened, or the UI rebuilt. */
|
|
280
|
+
endWelcomeInteraction(): void;
|
|
281
|
+
continueRecentSession(): Promise<void>;
|
|
279
282
|
handleResumeSession(sessionPath: string, options?: {
|
|
280
283
|
requireIdle?: boolean;
|
|
281
284
|
}): Promise<boolean>;
|
|
@@ -286,6 +286,10 @@ export interface InteractiveModeContext {
|
|
|
286
286
|
showUserMessageSelector(): void;
|
|
287
287
|
showTreeSelector(): void;
|
|
288
288
|
showSessionSelector(): void;
|
|
289
|
+
/** Resume the most recent saved session other than this one (the launch card's first row). */
|
|
290
|
+
continueRecentSession(): Promise<void>;
|
|
291
|
+
/** The launch card stops taking ↓/Enter/clicks and releases mouse capture (first prompt sent). */
|
|
292
|
+
endWelcomeInteraction?(): void;
|
|
289
293
|
showSessionsDashboard(): void;
|
|
290
294
|
handleResumeSession(sessionPath: string, options?: {
|
|
291
295
|
requireIdle?: boolean;
|
|
@@ -9,6 +9,18 @@ export interface BrokerSettings {
|
|
|
9
9
|
packageGeneration?: string;
|
|
10
10
|
port?: number;
|
|
11
11
|
heartbeatTtlMs?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Stop after this long with no connection, no request in flight and no live
|
|
14
|
+
* session. Clients call `ensureBroker` before use, so the next one starts a fresh
|
|
15
|
+
* broker. 0 disables. Default {@link BROKER_IDLE_SHUTDOWN_MS}.
|
|
16
|
+
*/
|
|
17
|
+
idleShutdownMs?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Runs at the start of every publication tick, before the tick calls into the
|
|
20
|
+
* native addon. The broker process uses it to stop at once when its source
|
|
21
|
+
* checkout has vanished (see `process-guard.ts`); return true to skip the tick.
|
|
22
|
+
*/
|
|
23
|
+
beforePublicationTick?: () => boolean;
|
|
12
24
|
/** Broker-owned migration policy. Client lifecycle frames cannot select it. */
|
|
13
25
|
resolveDirectoryMigration?: (_cwd: string) => Promise<DirectoryMigrationPolicy>;
|
|
14
26
|
}
|
|
@@ -17,6 +29,8 @@ type ResolvedBrokerSettings = {
|
|
|
17
29
|
packageGeneration: string;
|
|
18
30
|
port: number;
|
|
19
31
|
heartbeatTtlMs: number;
|
|
32
|
+
idleShutdownMs: number;
|
|
33
|
+
beforePublicationTick: () => boolean;
|
|
20
34
|
resolveDirectoryMigration: (_cwd: string) => Promise<DirectoryMigrationPolicy>;
|
|
21
35
|
};
|
|
22
36
|
export type BrokerErrorCode = "idempotency_conflict" | "terminal_uncertain" | "broker_restarting" | "unavailable" | "endpoint_stale" | "resource_gone" | "invalid_input" | "spawn_failed" | "readiness_timeout" | "close_refused" | "not_found" | "live_session" | "cleanup_pending" | (string & {});
|
|
@@ -102,6 +116,12 @@ export type BrokerResponse = {
|
|
|
102
116
|
durableEffects?: LifecycleDurableEffectsReceipt;
|
|
103
117
|
startupFailure?: LifecycleStartupFailureReceipt;
|
|
104
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* A broker with nothing to serve stops after this long. Without it a broker lived
|
|
121
|
+
* until killed: one started by a test (or a crashed client) whose caller never came
|
|
122
|
+
* back stayed up for days holding a port, memory and a mapped native addon.
|
|
123
|
+
*/
|
|
124
|
+
export declare const BROKER_IDLE_SHUTDOWN_MS: number;
|
|
105
125
|
export declare class Broker {
|
|
106
126
|
#private;
|
|
107
127
|
readonly settings: ResolvedBrokerSettings;
|
|
@@ -113,6 +133,8 @@ export declare class Broker {
|
|
|
113
133
|
get ownsDiscovery(): boolean;
|
|
114
134
|
get completion(): Promise<void>;
|
|
115
135
|
status(): RedactedBrokerDiscovery | null;
|
|
136
|
+
/** Record client activity; an active broker never idles out. */
|
|
137
|
+
noteActivity(): void;
|
|
116
138
|
heartbeat(): Promise<void>;
|
|
117
139
|
stop(): Promise<void>;
|
|
118
140
|
handleRequest(operation: string, input: Record<string, unknown>, idempotencyKey?: string): Promise<BrokerResponse>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards for the SDK's long-lived background processes (`sdk broker-internal`,
|
|
3
|
+
* `sdk session-host-internal`). They are spawned detached and outlive whoever
|
|
4
|
+
* started them, so each one has to notice on its own when it has no reason, or no
|
|
5
|
+
* way, to keep running.
|
|
6
|
+
*/
|
|
7
|
+
/** How often the guards run. Cheap: one `stat`, plus a small JSON read for hosts. */
|
|
8
|
+
export declare const PROCESS_GUARD_INTERVAL_MS = 5000;
|
|
9
|
+
/**
|
|
10
|
+
* The entry script of a process run from source (`bun …/src/cli.ts sdk …`), or
|
|
11
|
+
* undefined for a compiled binary, whose entry lives in Bun's virtual filesystem
|
|
12
|
+
* and cannot disappear from under it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sourceEntryPath(main?: string | undefined): string | undefined;
|
|
15
|
+
/** Whether the source entry is still reachable. A compiled binary (no entry) always is. */
|
|
16
|
+
export declare function sourceEntryAvailable(entry: string | undefined, stat?: (file: string) => unknown): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* End this process at once when the source it runs from has vanished — typically
|
|
19
|
+
* an external drive with the checkout was unplugged. The native addon is mapped
|
|
20
|
+
* from that checkout and paged in lazily, so the next call into it faults on a
|
|
21
|
+
* page that can no longer be read, and Bun's fault handler then spins on the same
|
|
22
|
+
* unreadable mapping at 100% CPU forever. No cleanup is safe at that point (it
|
|
23
|
+
* would call into the same addon), so the process kills itself; readers already
|
|
24
|
+
* treat its discovery and markers as stale once the pid is gone.
|
|
25
|
+
*
|
|
26
|
+
* Call this before any native call in a periodic task. Returns true if it fired.
|
|
27
|
+
*/
|
|
28
|
+
export declare function exitIfSourceGone(entry?: string | undefined, options?: {
|
|
29
|
+
stat?: (file: string) => unknown;
|
|
30
|
+
kill?: () => void;
|
|
31
|
+
}): boolean;
|
|
32
|
+
/** What a session host needs to recognise that its session is still its own. */
|
|
33
|
+
export interface SessionHostAuthority {
|
|
34
|
+
/** `<stateRoot>/sdk/<sessionId>.lifecycle.json`, written once by the broker when it spawned this host. */
|
|
35
|
+
markerPath: string;
|
|
36
|
+
pid: number;
|
|
37
|
+
effectMarker: string;
|
|
38
|
+
incarnation: string;
|
|
39
|
+
/** The host's worktree. */
|
|
40
|
+
cwd: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `held`: the marker still names this process and the worktree exists. `lost`: the
|
|
44
|
+
* marker or worktree is gone, or the marker names another process — the session was
|
|
45
|
+
* deleted, taken over, or its whole state root was removed (a finished test). `unknown`:
|
|
46
|
+
* something could not be read for another reason; that is not evidence either way.
|
|
47
|
+
*/
|
|
48
|
+
export type SessionHostAuthorityState = "held" | "lost" | "unknown";
|
|
49
|
+
export declare function checkSessionHostAuthority(authority: SessionHostAuthority, io?: {
|
|
50
|
+
readFile?: (file: string) => string;
|
|
51
|
+
stat?: (file: string) => unknown;
|
|
52
|
+
}): SessionHostAuthorityState;
|
|
53
|
+
/** Consecutive `lost` checks before a host gives up its session (≈15s at the default interval). */
|
|
54
|
+
export declare const SESSION_HOST_LOST_CHECKS = 3;
|
|
55
|
+
/**
|
|
56
|
+
* Watch a running session host. It stops (through `onLost`, the host's normal
|
|
57
|
+
* shutdown) once its ownership marker or worktree has been gone for
|
|
58
|
+
* {@link SESSION_HOST_LOST_CHECKS} checks in a row, and kills itself at once if the
|
|
59
|
+
* source checkout it runs from disappears. Before this, a host whose session state
|
|
60
|
+
* had been deleted kept running until someone killed it — for days, after tests.
|
|
61
|
+
* Returns a function that stops the watch.
|
|
62
|
+
*/
|
|
63
|
+
export declare function startSessionHostGuard(options: SessionHostAuthority & {
|
|
64
|
+
onLost: () => void;
|
|
65
|
+
intervalMs?: number;
|
|
66
|
+
lostChecks?: number;
|
|
67
|
+
check?: (authority: SessionHostAuthority) => SessionHostAuthorityState;
|
|
68
|
+
sourceGuard?: () => boolean;
|
|
69
|
+
setInterval?: typeof setInterval;
|
|
70
|
+
clearInterval?: typeof clearInterval;
|
|
71
|
+
}): () => void;
|
|
@@ -3,6 +3,8 @@ import type { Broker } from "./broker";
|
|
|
3
3
|
export declare class BrokerTransport {
|
|
4
4
|
#private;
|
|
5
5
|
constructor(broker: Broker, token: string, port?: number);
|
|
6
|
+
/** Client WebSockets currently open. The broker does not idle out while any is. */
|
|
7
|
+
get openConnections(): number;
|
|
6
8
|
get port(): number;
|
|
7
9
|
start(): Promise<number>;
|
|
8
10
|
stop(): Promise<void>;
|
|
@@ -46,6 +46,10 @@ export interface ChatDaemonRuntimeDeps {
|
|
|
46
46
|
url: string;
|
|
47
47
|
token: string;
|
|
48
48
|
}) => Promise<ChatDaemonSdkClient>;
|
|
49
|
+
/** Starts the agent broker when none is running (default: `ensureBroker`). */
|
|
50
|
+
ensureBroker?: (settings: {
|
|
51
|
+
agentDir: string;
|
|
52
|
+
}) => Promise<unknown>;
|
|
49
53
|
onReconciled?: () => void;
|
|
50
54
|
setInterval?: typeof setInterval;
|
|
51
55
|
clearInterval?: typeof clearInterval;
|
|
@@ -1221,6 +1221,15 @@ export declare class AgentSession {
|
|
|
1221
1221
|
setAutoCompactionEnabled(enabled: boolean): void;
|
|
1222
1222
|
/** Whether auto-compaction is enabled */
|
|
1223
1223
|
get autoCompactionEnabled(): boolean;
|
|
1224
|
+
/**
|
|
1225
|
+
* The default fallback chain the next prompt walks, automatic entries included,
|
|
1226
|
+
* and the position currently in use.
|
|
1227
|
+
*/
|
|
1228
|
+
getDefaultFallbackChain(): {
|
|
1229
|
+
entries: readonly string[];
|
|
1230
|
+
activeIndex: number;
|
|
1231
|
+
appendedFrom: number;
|
|
1232
|
+
};
|
|
1224
1233
|
/**
|
|
1225
1234
|
* Cancel in-progress retry.
|
|
1226
1235
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Settings } from "../config/settings";
|
|
1
2
|
import { AuthStorage } from "./auth-storage";
|
|
2
3
|
/**
|
|
3
4
|
* Create an AuthStorage instance.
|
|
@@ -11,3 +12,17 @@ import { AuthStorage } from "./auth-storage";
|
|
|
11
12
|
* override to re-mint access tokens when needed.
|
|
12
13
|
*/
|
|
13
14
|
export declare function discoverAuthStorage(agentDir?: string): Promise<AuthStorage>;
|
|
15
|
+
/**
|
|
16
|
+
* Per-machine multi-account ranking override from `SKC_CREDENTIAL_RANKING_MODE`.
|
|
17
|
+
* Unset/unknown → `undefined`, so the `auth.credentialRankingMode` setting (or
|
|
18
|
+
* {@link AuthStorage}'s `balanced` default) decides. `earliest-reset` switches to
|
|
19
|
+
* earliest-expiry-first selection so soon-to-reset tumbling-window quota is
|
|
20
|
+
* drained before it is lost.
|
|
21
|
+
*/
|
|
22
|
+
export declare function credentialRankingModeFromEnv(): "balanced" | "earliest-reset" | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Apply the `auth.credentialRankingMode` setting to a credential store. The env
|
|
25
|
+
* var still wins, so a machine that pins a mode keeps it whatever config says.
|
|
26
|
+
* Returns the mode now in effect.
|
|
27
|
+
*/
|
|
28
|
+
export declare function applyCredentialRankingModeSetting(storage: Pick<AuthStorage, "setCredentialRankingMode">, settings: Pick<Settings, "get">): "balanced" | "earliest-reset";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Api, Model } from "@sayknow-cli/ai";
|
|
2
|
+
/**
|
|
3
|
+
* How many other providers the automatic tail may add. Each entry can cost up to
|
|
4
|
+
* `fallback.maxAttempts` tries before the chain moves on, so the tail stays short.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AUTO_FALLBACK_PROVIDER_LIMIT = 3;
|
|
7
|
+
/** Provider of a `provider/model[:level]` selector, or undefined for bare aliases. */
|
|
8
|
+
export declare function selectorProvider(selector: string): string | undefined;
|
|
9
|
+
export interface AutoFallbackInput {
|
|
10
|
+
/** Models whose provider has auth configured (`ModelRegistry.getAvailable()`). */
|
|
11
|
+
available: readonly Model<Api>[];
|
|
12
|
+
/** True when the provider has a stored credential or API key (keyless local providers do not count). */
|
|
13
|
+
hasCredentials: (provider: string) => boolean;
|
|
14
|
+
/** Providers already in the chain; the tail adds a different provider or nothing. */
|
|
15
|
+
excludeProviders: ReadonlySet<string>;
|
|
16
|
+
/** Most-recently-used `provider/id` keys, newest first. */
|
|
17
|
+
usageOrder?: readonly string[];
|
|
18
|
+
limit?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One model per other logged-in provider, for the automatic tail of the default
|
|
22
|
+
* fallback chain. Within a provider the model the user used most recently wins,
|
|
23
|
+
* then the provider's curated default; a provider with neither is skipped rather
|
|
24
|
+
* than guessed, because catalog order would land on an old model. Providers used
|
|
25
|
+
* recently come first, then the rest in curated-default order.
|
|
26
|
+
*/
|
|
27
|
+
export declare function autoFallbackSelectors(input: AutoFallbackInput): string[];
|
|
@@ -6,6 +6,11 @@ export interface ConfiguredFallbackChain {
|
|
|
6
6
|
origin: string;
|
|
7
7
|
identity?: string;
|
|
8
8
|
explicitHead: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Index of the first entry appended at runtime after the configured intent
|
|
11
|
+
* (`fallback.models`, then automatic picks). Absent when nothing was appended.
|
|
12
|
+
*/
|
|
13
|
+
appendedFrom?: number;
|
|
9
14
|
}
|
|
10
15
|
export interface FallbackFailure {
|
|
11
16
|
selector: string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which language to answer in, detected from the script of the user's message.
|
|
3
|
+
*
|
|
4
|
+
* Long tool-heavy turns drift into English: tool output, logs, skill text and context
|
|
5
|
+
* summaries are English, and the final report follows them. The system prompt states
|
|
6
|
+
* the rule once; this adds a one-line reminder at the start of each turn, naming the
|
|
7
|
+
* language, when the message is clearly written in a non-Latin script. Latin-script
|
|
8
|
+
* languages (English, Spanish, French, German, …) cannot be told apart by script alone,
|
|
9
|
+
* so they get no reminder and rely on the system prompt rule.
|
|
10
|
+
*/
|
|
11
|
+
export interface DetectedLanguage {
|
|
12
|
+
/** BCP 47 code. */
|
|
13
|
+
code: string;
|
|
14
|
+
/** English name, as used in the reminder. */
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The language of `text` when its prose is clearly in a non-Latin script, else
|
|
19
|
+
* undefined. "Clearly": at least two characters of the script, and — since one CJK
|
|
20
|
+
* character carries about a word — at least a quarter as many as Latin letters, so a
|
|
21
|
+
* pasted English log with one Korean word does not count.
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectResponseLanguage(text: string): DetectedLanguage | undefined;
|
|
24
|
+
/** The per-turn reminder for a detected language. */
|
|
25
|
+
export declare function buildResponseLanguageReminder(language: DetectedLanguage): string;
|