@vincemakes/kiso-code 0.38.0 → 0.39.1
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/dist/chat.js +8 -0
- package/dist/dispatch.js +52 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -4
- package/dist/window-title.d.ts +76 -0
- package/dist/window-title.js +114 -0
- package/package.json +15 -15
package/dist/chat.js
CHANGED
|
@@ -12,6 +12,7 @@ import { echoText } from "@vincemakes/kiso-tui-cells/render";
|
|
|
12
12
|
import { canonicalizeUsage } from "@vincemakes/kiso-runtime";
|
|
13
13
|
import { canonicalizeUsageForModel, requestBudget } from "@vincemakes/kiso-runtime/internal";
|
|
14
14
|
import { dispatch, abortBangCommand } from "./dispatch.js";
|
|
15
|
+
import { paintWindowTitle } from "./window-title.js";
|
|
15
16
|
import { agentBaseUrl, agentModel, body, bodyLog, configuredWindow, dock } from "./state.js";
|
|
16
17
|
import { attachImages } from "./attachments.js";
|
|
17
18
|
import { lookupModelMetadata } from "@vincemakes/kiso-runtime/internal";
|
|
@@ -781,6 +782,13 @@ submitTurn) {
|
|
|
781
782
|
// construction (ADR-0040).
|
|
782
783
|
switch (ev.type) {
|
|
783
784
|
case "user_input":
|
|
785
|
+
// The window title is re-derived here because THIS is when a
|
|
786
|
+
// session stops being nameless: the tab opened as `kiso —
|
|
787
|
+
// <workspace>` and the first substantive prompt is what gives
|
|
788
|
+
// it a name. Re-derived rather than set, so the opener rule is
|
|
789
|
+
// `sessionTitle`'s and a greeting-first session upgrades when
|
|
790
|
+
// the real prompt lands instead of keeping "hi" forever.
|
|
791
|
+
paintWindowTitle(session.log.all);
|
|
784
792
|
// TV-1B: a system-sourced input is PRODUCT MACHINERY — visible
|
|
785
793
|
// (every durable input renders) but never painted as the
|
|
786
794
|
// user's words. Provenance is honest on screen, not only in
|
package/dist/dispatch.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* turns. The bodies moved verbatim from chat()'s closure; chat provides
|
|
4
4
|
* the context (the chain, the run state, the prompt arming).
|
|
5
5
|
*/
|
|
6
|
-
import { contextRows, contextUnavailableRows, displayVerb, escapeTerminal, helpRows, kUnit, modePickView, modelPickView, palette } from "@vincemakes/kiso-tui";
|
|
6
|
+
import { contextRows, contextUnavailableRows, displayVerb, escapeTerminal, helpRows, kUnit, modePickView, modelPickView, palette, renderEvent, settledLabel, slashCommandNames } from "@vincemakes/kiso-tui";
|
|
7
7
|
import { newSessionId } from "./session-id.js";
|
|
8
8
|
import { buildAdapter, lookupModelMetadata, resolveContinuationScope, resolveReasoning } from "@vincemakes/kiso-runtime/internal";
|
|
9
9
|
import { MODES, MODE_NOTE, getMode, setMode } from "./mode.js";
|
|
@@ -761,10 +761,28 @@ export function dispatch(line, ctx) {
|
|
|
761
761
|
// (the estimate BEFORE vs AFTER — the same chars/4
|
|
762
762
|
// proxy the status bar shows, marked ~), and the time.
|
|
763
763
|
const ctxAfter = Math.round(ctx.estimateCtx() * 100);
|
|
764
|
-
const elapsed = compactStart > 0 ? ((Date.now() - compactStart) / 1000)
|
|
764
|
+
const elapsed = compactStart > 0 ? settledLabel((Date.now() - compactStart) / 1000) : "?s";
|
|
765
765
|
// a non-null result implies onStart ran — the "?" is
|
|
766
766
|
// reachable only at the type level
|
|
767
|
-
body.notice(`[/compact] ✦ compacted · ${compactInfo?.rounds ?? "?"} rounds → 1 summary · saved ~${kUnit(result.savedTokens)} · ctx ${ctxBefore ?? "?"}% → ${ctxAfter}% · ${elapsed}
|
|
767
|
+
body.notice(`[/compact] ✦ compacted · ${compactInfo?.rounds ?? "?"} rounds → 1 summary · saved ~${kUnit(result.savedTokens)} · ctx ${ctxBefore ?? "?"}% → ${ctxAfter}% · ${elapsed}`);
|
|
768
|
+
// 0.39.1 — the boundary row, under the recap.
|
|
769
|
+
//
|
|
770
|
+
// `summarized` is appended OFF-LOOP: session.summarize()
|
|
771
|
+
// writes it to the log and persists it, and no hook
|
|
772
|
+
// carries it to a renderer. So the row that marks where
|
|
773
|
+
// the transcript was cut had exactly one producer — a
|
|
774
|
+
// `case "summarized"` in lines.ts whose own comment says
|
|
775
|
+
// it is there "for the switch's completeness only" — and
|
|
776
|
+
// zero callers. The durable log said a compaction
|
|
777
|
+
// happened at seq N; no screen ever did.
|
|
778
|
+
//
|
|
779
|
+
// Rendered THROUGH that case rather than written again
|
|
780
|
+
// here: the line a resumed or replayed view would draw is
|
|
781
|
+
// the line drawn now, by construction, and the row cannot
|
|
782
|
+
// drift into two spellings of one fact.
|
|
783
|
+
const boundary = renderEvent({ type: "summarized", coversToSeq: result.coversToSeq });
|
|
784
|
+
if (boundary.text !== "")
|
|
785
|
+
body.raw(boundary.text.replace(/\n$/, "").split("\n"));
|
|
768
786
|
}
|
|
769
787
|
}
|
|
770
788
|
catch (err) {
|
|
@@ -774,7 +792,22 @@ export function dispatch(line, ctx) {
|
|
|
774
792
|
body.notice("[/compact] cancelled — nothing was persisted");
|
|
775
793
|
}
|
|
776
794
|
else {
|
|
777
|
-
|
|
795
|
+
// 0.39.1 — two repairs on one line. A failure the ADAPTER
|
|
796
|
+
// classified is a plain object, not an Error: `String(err)`
|
|
797
|
+
// rendered it `[object Object]`, which became reachable the
|
|
798
|
+
// moment the adapters started THROWING the
|
|
799
|
+
// transport-failure class instead of stopping with it. And
|
|
800
|
+
// a transport failure is worth saying so about: summarize
|
|
801
|
+
// already retried it once (it has no kernel behind it to do
|
|
802
|
+
// that), so a second failure means the gateway is still
|
|
803
|
+
// dropping the stream — the run is fine and the gesture is
|
|
804
|
+
// worth repeating. A permanent failure is not, and must not
|
|
805
|
+
// be dressed up as one that is.
|
|
806
|
+
const e = err;
|
|
807
|
+
const classified = typeof e.code === "string" && typeof e.message === "string";
|
|
808
|
+
const detail = classified ? String(e.message) : err instanceof Error ? err.message : String(err);
|
|
809
|
+
const again = classified && e.retryable === true ? " — the connection dropped again; /compact is worth another try" : "";
|
|
810
|
+
body.notice(`[/compact] failed: ${escapeTerminal(detail)}${again}`);
|
|
778
811
|
}
|
|
779
812
|
}
|
|
780
813
|
finally {
|
|
@@ -902,7 +935,21 @@ export function dispatch(line, ctx) {
|
|
|
902
935
|
// still submits.
|
|
903
936
|
if (trimmed.startsWith("/") && !trimmed.includes("\n")) {
|
|
904
937
|
ctx.chainRef.current = ctx.chainRef.current.then(async () => {
|
|
905
|
-
|
|
938
|
+
// The word, not the line: a command that EXISTS but was handed an
|
|
939
|
+
// argument it does not take reaches here too, and calling it
|
|
940
|
+
// unknown is a lie the user cannot get past — `/help` lists it,
|
|
941
|
+
// they typed what `/help` said, and kiso answers that there is no
|
|
942
|
+
// such command. (Reported on `/reload extensions`, 0.39.1.) The
|
|
943
|
+
// commands that DO take an argument matched on their own prefix
|
|
944
|
+
// far above, so a known word arriving here means exactly one
|
|
945
|
+
// thing: it takes none. `slashCommandNames` is the help sheet's
|
|
946
|
+
// own table, so this line cannot drift from the list the user was
|
|
947
|
+
// reading when they typed it.
|
|
948
|
+
const word = trimmed.split(/\s+/)[0] ?? trimmed;
|
|
949
|
+
const known = slashCommandNames().includes(word);
|
|
950
|
+
bodyLog(known
|
|
951
|
+
? `${escapeTerminal(word)} takes no arguments — /help says what it does`
|
|
952
|
+
: `unknown command: ${escapeTerminal(word)} — /help lists the commands`);
|
|
906
953
|
ctx.input.prompt();
|
|
907
954
|
});
|
|
908
955
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export { applyProjectMerges } from "./trust-ui.js";
|
|
|
31
31
|
*/
|
|
32
32
|
/** The built-in prompt. Exported for scripts/request-surface.mjs — the
|
|
33
33
|
* model-side token-rent counter measures the REAL bytes, never a copy. */
|
|
34
|
-
export declare const SYSTEM_PROMPT = "You are kiso, a coding agent. You work in a workspace\ndirectory and change code with tools. Be concise: answer in a few lines\nunless the task genuinely needs more. Never claim a file was changed\nunless a tool confirmed it.\n\nWhat you can reach:\n- The workspace: read_file, list_dir, search_text, write_file, edit_file.\n- This machine and the network: shell \u2014 builds, tests, git, package\n managers, curl for HTTP, system queries. A request one command can\n answer is answered by running it.\n- The human: ask_user, for a decision that is theirs.\n\nTool discipline:\n- READ BEFORE YOU EDIT. For any file you are about to change, read it\n first \u2014 never guess its content.\n- Use edit_file for targeted changes and write_file for full rewrites.\n Prefer many small edits over one large write.\n-
|
|
34
|
+
export declare const SYSTEM_PROMPT = "You are kiso, a coding agent. You work in a workspace\ndirectory and change code with tools. Be concise: answer in a few lines\nunless the task genuinely needs more. Never claim a file was changed\nunless a tool confirmed it.\n\nWhat you can reach:\n- The workspace: read_file, list_dir, search_text, write_file, edit_file.\n- This machine and the network: shell \u2014 builds, tests, git, package\n managers, curl for HTTP, system queries. A request one command can\n answer is answered by running it.\n- The human: ask_user, for a decision that is theirs.\n\nTool discipline:\n- READ BEFORE YOU EDIT. For any file you are about to change, read it\n first \u2014 never guess its content.\n- Use edit_file for targeted changes and write_file for full rewrites.\n Prefer many small edits over one large write.\n- Be careful \u2014 shell has side effects and may take time.\n- search_text and list_dir are cheap \u2014 locate first, then read ranges\n with read_file offset/limit; never read a whole large file in one call.\n- Do not re-read a file you already read unchanged, or one you changed\n yourself through a confirmed edit \u2014 rely on the earlier result and\n on the change you just made.\n- When a tool fails, read the error and adjust; do not repeat the same\n call blindly.\n\nAn authorization covers what it NAMES: when the scope is not named \u2014\nwhich files, which branches, whether to push \u2014 ask before acting.\nDelivering, sharing or handing over changes means committing locally and\nstopping; pushing, publishing or sending happens only when the human\nnames it.\n\nWorkflow: understand the request, find the relevant code, make the\nsmallest change that works, then verify with a command (tests/build).\nReport what you did in one or two lines per change.";
|
|
35
35
|
/** Hard cap for injected instructions — truncate and say so. */
|
|
36
36
|
/**
|
|
37
37
|
* A area: read the FIRST present instruction file (AGENTS.md preferred) and
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import { loadProjectConfig, loadUserConfig, mergeConfigs, resolveAutoCompact, re
|
|
|
47
47
|
import { checkForUpdate, knownUpdate, updateCardLines } from "./update-check.js";
|
|
48
48
|
import { tmuxMouseHint } from "./tmux-hint.js";
|
|
49
49
|
import { resume } from "./resume.js";
|
|
50
|
+
import { paintWindowTitle } from "./window-title.js";
|
|
50
51
|
import { resumeTail } from "./resume-tail.js";
|
|
51
52
|
import { armByteTrace } from "./byte-trace.js";
|
|
52
53
|
import { tmpdir, homedir } from "node:os";
|
|
@@ -592,10 +593,7 @@ Tool discipline:
|
|
|
592
593
|
first — never guess its content.
|
|
593
594
|
- Use edit_file for targeted changes and write_file for full rewrites.
|
|
594
595
|
Prefer many small edits over one large write.
|
|
595
|
-
-
|
|
596
|
-
side effects and may take time. Run one command at a time and inspect
|
|
597
|
-
the output before continuing.
|
|
598
|
-
- Batch independent tool calls into one reply — they run in parallel.
|
|
596
|
+
- Be careful — shell has side effects and may take time.
|
|
599
597
|
- search_text and list_dir are cheap — locate first, then read ranges
|
|
600
598
|
with read_file offset/limit; never read a whole large file in one call.
|
|
601
599
|
- Do not re-read a file you already read unchanged, or one you changed
|
|
@@ -1128,6 +1126,11 @@ async function chatLoop(first, firstId, input, autoCompact) {
|
|
|
1128
1126
|
bindRestoredSession(session);
|
|
1129
1127
|
setCurrentModelName(session.model);
|
|
1130
1128
|
paintBootStatus(session);
|
|
1129
|
+
// The terminal's window title, HERE for the same reason the three
|
|
1130
|
+
// lines above are here: this is the one step all three entry points
|
|
1131
|
+
// share (first start, `/resume <id>`, a switch), so a tab can never
|
|
1132
|
+
// be left naming the session the user just left.
|
|
1133
|
+
paintWindowTitle(session.log.all);
|
|
1131
1134
|
const nav = {
|
|
1132
1135
|
sessions: () => agent.sessions().map((m) => m.id),
|
|
1133
1136
|
...(process.stdin.isTTY ? { pick: () => pickSession(agent, input) } : {}),
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal's window title (0.39.1, the owner's call).
|
|
3
|
+
*
|
|
4
|
+
* kiso set none, so a tab read whatever the shell left there — the
|
|
5
|
+
* directory the shell happened to be in and the absolute path of a node
|
|
6
|
+
* binary, identically for every kiso in every tab. The title is the one
|
|
7
|
+
* place a terminal shows which session a tab holds without the user
|
|
8
|
+
* switching to it, and it was the only screen kiso never wrote to.
|
|
9
|
+
*
|
|
10
|
+
* `kiso — <workspace>` at the start, `kiso — <session title> —
|
|
11
|
+
* <workspace>` once the session has one. The session's title is
|
|
12
|
+
* `sessionTitle`'s — the SAME projection the resume picker and `kiso
|
|
13
|
+
* sessions` print, so a tab and the picker can never disagree about what
|
|
14
|
+
* a session is called. That is also why an opener does not become the
|
|
15
|
+
* title: the rule for "substantive" lives in one place and this is a
|
|
16
|
+
* consumer of it, not a second opinion.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IT WRITES. OSC 0, BEL-terminated — icon name and window title in
|
|
19
|
+
* one sequence, the form every terminal that supports either accepts. It
|
|
20
|
+
* goes STRAIGHT to stdout, deliberately outside the compositor: a title
|
|
21
|
+
* occupies no cell and no column, so it is not in the width accounting
|
|
22
|
+
* and must not be a cell that the repaint could move, cut, or scroll.
|
|
23
|
+
*
|
|
24
|
+
* WHAT IT NEVER WRITES. Anything at all when stdout is not a TTY. An OSC
|
|
25
|
+
* into a pipe is bytes in someone's output, and the pipe-identity gates
|
|
26
|
+
* exist because that has been a real defect before.
|
|
27
|
+
*
|
|
28
|
+
* NO RESTORE ON EXIT. Putting back what was there first requires reading
|
|
29
|
+
* it, which no terminal reliably answers; the shell repaints its own
|
|
30
|
+
* title on the next prompt anyway. A guess written on the way out would
|
|
31
|
+
* be a title kiso invented, which is worse than one it left.
|
|
32
|
+
*
|
|
33
|
+
* AND NO `process.title`. The reference implementation sets one; kiso
|
|
34
|
+
* does not, because two gates in this repo said no and the benefit was
|
|
35
|
+
* that `ps` would print `kiso` instead of a node path.
|
|
36
|
+
*
|
|
37
|
+
* - It overwrites the process's ARGV memory, so a renamed process shows
|
|
38
|
+
* as `kiso` with its arguments simply gone. The subagent's
|
|
39
|
+
* concurrency probe counts live children by matching `chat
|
|
40
|
+
* sub-parent-` in the command column; six renamed children were
|
|
41
|
+
* indistinguishable from each other and from nothing.
|
|
42
|
+
* - Setting it is not free on this platform: 14 ms measured for the
|
|
43
|
+
* first assignment on the machine this was written on, 7 ms for the
|
|
44
|
+
* second. A synchronous stall of that size on the main thread while a
|
|
45
|
+
* 3,000-line paste is arriving wedges the PTY in both directions —
|
|
46
|
+
* the UD-1 byte-exact paste gate deadlocked reproducibly and passed
|
|
47
|
+
* the moment the assignment was removed.
|
|
48
|
+
*
|
|
49
|
+
* The window title is what was asked for; the process name was a detail
|
|
50
|
+
* that came with it, and it costs more than it is worth.
|
|
51
|
+
*/
|
|
52
|
+
import type { Event } from "@vincemakes/kiso-core";
|
|
53
|
+
/** OSC 0 — "set icon name and window title". */
|
|
54
|
+
export declare const OSC_TITLE_PREFIX = "\u001B]0;";
|
|
55
|
+
/** BEL, the terminator the reference form uses (ST works too; BEL is the
|
|
56
|
+
* one older terminals accept without argument). */
|
|
57
|
+
export declare const OSC_TITLE_SUFFIX = "\u0007";
|
|
58
|
+
/**
|
|
59
|
+
* The text of the title, with no IO — the whole rule, testable.
|
|
60
|
+
*
|
|
61
|
+
* Every part is passed through `escapeTerminal` before it can reach the
|
|
62
|
+
* sequence. A title is whatever a human typed or PASTED: a BEL in it
|
|
63
|
+
* would end the sequence early and an ESC would begin another one, and
|
|
64
|
+
* the title would stop being a title and start being commands to the
|
|
65
|
+
* terminal. The same stripper the rendered surfaces use — one definition
|
|
66
|
+
* of "what is safe to put on a terminal", not a second.
|
|
67
|
+
*/
|
|
68
|
+
export declare function windowTitleText(events: readonly Event[], workspace: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Write the title for this session's state. Called where the session is
|
|
71
|
+
* BOUND (which is one place for all three entry points — first start,
|
|
72
|
+
* `/resume <id>`, and a switch) and again as the session's own title
|
|
73
|
+
* becomes knowable, so a tab named before the first prompt is renamed by
|
|
74
|
+
* it rather than keeping a name that was only ever a placeholder.
|
|
75
|
+
*/
|
|
76
|
+
export declare function paintWindowTitle(events: readonly Event[], cwd?: string): void;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal's window title (0.39.1, the owner's call).
|
|
3
|
+
*
|
|
4
|
+
* kiso set none, so a tab read whatever the shell left there — the
|
|
5
|
+
* directory the shell happened to be in and the absolute path of a node
|
|
6
|
+
* binary, identically for every kiso in every tab. The title is the one
|
|
7
|
+
* place a terminal shows which session a tab holds without the user
|
|
8
|
+
* switching to it, and it was the only screen kiso never wrote to.
|
|
9
|
+
*
|
|
10
|
+
* `kiso — <workspace>` at the start, `kiso — <session title> —
|
|
11
|
+
* <workspace>` once the session has one. The session's title is
|
|
12
|
+
* `sessionTitle`'s — the SAME projection the resume picker and `kiso
|
|
13
|
+
* sessions` print, so a tab and the picker can never disagree about what
|
|
14
|
+
* a session is called. That is also why an opener does not become the
|
|
15
|
+
* title: the rule for "substantive" lives in one place and this is a
|
|
16
|
+
* consumer of it, not a second opinion.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IT WRITES. OSC 0, BEL-terminated — icon name and window title in
|
|
19
|
+
* one sequence, the form every terminal that supports either accepts. It
|
|
20
|
+
* goes STRAIGHT to stdout, deliberately outside the compositor: a title
|
|
21
|
+
* occupies no cell and no column, so it is not in the width accounting
|
|
22
|
+
* and must not be a cell that the repaint could move, cut, or scroll.
|
|
23
|
+
*
|
|
24
|
+
* WHAT IT NEVER WRITES. Anything at all when stdout is not a TTY. An OSC
|
|
25
|
+
* into a pipe is bytes in someone's output, and the pipe-identity gates
|
|
26
|
+
* exist because that has been a real defect before.
|
|
27
|
+
*
|
|
28
|
+
* NO RESTORE ON EXIT. Putting back what was there first requires reading
|
|
29
|
+
* it, which no terminal reliably answers; the shell repaints its own
|
|
30
|
+
* title on the next prompt anyway. A guess written on the way out would
|
|
31
|
+
* be a title kiso invented, which is worse than one it left.
|
|
32
|
+
*
|
|
33
|
+
* AND NO `process.title`. The reference implementation sets one; kiso
|
|
34
|
+
* does not, because two gates in this repo said no and the benefit was
|
|
35
|
+
* that `ps` would print `kiso` instead of a node path.
|
|
36
|
+
*
|
|
37
|
+
* - It overwrites the process's ARGV memory, so a renamed process shows
|
|
38
|
+
* as `kiso` with its arguments simply gone. The subagent's
|
|
39
|
+
* concurrency probe counts live children by matching `chat
|
|
40
|
+
* sub-parent-` in the command column; six renamed children were
|
|
41
|
+
* indistinguishable from each other and from nothing.
|
|
42
|
+
* - Setting it is not free on this platform: 14 ms measured for the
|
|
43
|
+
* first assignment on the machine this was written on, 7 ms for the
|
|
44
|
+
* second. A synchronous stall of that size on the main thread while a
|
|
45
|
+
* 3,000-line paste is arriving wedges the PTY in both directions —
|
|
46
|
+
* the UD-1 byte-exact paste gate deadlocked reproducibly and passed
|
|
47
|
+
* the moment the assignment was removed.
|
|
48
|
+
*
|
|
49
|
+
* The window title is what was asked for; the process name was a detail
|
|
50
|
+
* that came with it, and it costs more than it is worth.
|
|
51
|
+
*/
|
|
52
|
+
import { basename } from "node:path";
|
|
53
|
+
import { sessionTitle } from "@vincemakes/kiso-runtime/internal";
|
|
54
|
+
import { displayWidth, widthCut } from "@vincemakes/kiso-tui-cells/width";
|
|
55
|
+
import { escapeTerminal } from "@vincemakes/kiso-tui-cells/render";
|
|
56
|
+
/** OSC 0 — "set icon name and window title". */
|
|
57
|
+
export const OSC_TITLE_PREFIX = "\u001b]0;";
|
|
58
|
+
/** BEL, the terminator the reference form uses (ST works too; BEL is the
|
|
59
|
+
* one older terminals accept without argument). */
|
|
60
|
+
export const OSC_TITLE_SUFFIX = "\u0007";
|
|
61
|
+
/** The session title's share of the tab, in CELLS. A tab strip gives a
|
|
62
|
+
* title far less than a row, and the workspace — which says WHERE, the
|
|
63
|
+
* thing a human scanning tabs is usually after — must survive the cut. */
|
|
64
|
+
const TITLE_CELLS = 40;
|
|
65
|
+
/** The title a session with no title yet has. `sessionTitle` returns this
|
|
66
|
+
* when nothing has been asked; it is not a label, so it is not shown. */
|
|
67
|
+
const NO_TITLE = "(no prompt)";
|
|
68
|
+
/**
|
|
69
|
+
* The text of the title, with no IO — the whole rule, testable.
|
|
70
|
+
*
|
|
71
|
+
* Every part is passed through `escapeTerminal` before it can reach the
|
|
72
|
+
* sequence. A title is whatever a human typed or PASTED: a BEL in it
|
|
73
|
+
* would end the sequence early and an ESC would begin another one, and
|
|
74
|
+
* the title would stop being a title and start being commands to the
|
|
75
|
+
* terminal. The same stripper the rendered surfaces use — one definition
|
|
76
|
+
* of "what is safe to put on a terminal", not a second.
|
|
77
|
+
*/
|
|
78
|
+
export function windowTitleText(events, workspace) {
|
|
79
|
+
const place = escapeTerminal(workspace);
|
|
80
|
+
// Filtered BEFORE the wrap, so the cost is one object per user turn
|
|
81
|
+
// rather than one per event — this is re-derived on every turn of a
|
|
82
|
+
// session whose log may hold thousands. `sessionTitle` filters again;
|
|
83
|
+
// it is the authority on what counts and this is not a second opinion.
|
|
84
|
+
const inputs = events.filter((e) => e.type === "user_input");
|
|
85
|
+
const raw = sessionTitle(inputs.map((event) => ({ runId: "", ts: 0, event })));
|
|
86
|
+
if (raw === NO_TITLE)
|
|
87
|
+
return `kiso — ${place}`;
|
|
88
|
+
const safe = escapeTerminal(raw);
|
|
89
|
+
// Cut by CELLS, not code points: a title of wide characters counts
|
|
90
|
+
// two columns each, and a count of characters would overflow the tab
|
|
91
|
+
// by as much again. The mark is inside the budget, never added to it.
|
|
92
|
+
const shown = displayWidth(safe) > TITLE_CELLS ? `${widthCut(safe, TITLE_CELLS - 1)}…` : safe;
|
|
93
|
+
return `kiso — ${shown} — ${place}`;
|
|
94
|
+
}
|
|
95
|
+
/** What was last written, so a repaint of the same title writes nothing.
|
|
96
|
+
* The title is re-derived on every bind and every user turn; most of
|
|
97
|
+
* those derive what is already on screen. */
|
|
98
|
+
let shown = null;
|
|
99
|
+
/**
|
|
100
|
+
* Write the title for this session's state. Called where the session is
|
|
101
|
+
* BOUND (which is one place for all three entry points — first start,
|
|
102
|
+
* `/resume <id>`, and a switch) and again as the session's own title
|
|
103
|
+
* becomes knowable, so a tab named before the first prompt is renamed by
|
|
104
|
+
* it rather than keeping a name that was only ever a placeholder.
|
|
105
|
+
*/
|
|
106
|
+
export function paintWindowTitle(events, cwd = process.cwd()) {
|
|
107
|
+
if (process.stdout.isTTY !== true)
|
|
108
|
+
return;
|
|
109
|
+
const text = windowTitleText(events, basename(cwd) || cwd);
|
|
110
|
+
if (text === shown)
|
|
111
|
+
return;
|
|
112
|
+
shown = text;
|
|
113
|
+
process.stdout.write(`${OSC_TITLE_PREFIX}${text}${OSC_TITLE_SUFFIX}`);
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"description": "kiso-code \u2014 the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-ask-ext": "0.
|
|
22
|
-
"@vincemakes/kiso-core": "0.
|
|
23
|
-
"@vincemakes/kiso-evals": "0.
|
|
24
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
25
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
26
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
27
|
-
"@vincemakes/kiso-provider-openai-responses": "0.
|
|
28
|
-
"@vincemakes/kiso-runtime": "0.
|
|
29
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
30
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
31
|
-
"@vincemakes/kiso-task-ext": "0.
|
|
32
|
-
"@vincemakes/kiso-tools-node": "0.
|
|
33
|
-
"@vincemakes/kiso-tui": "0.
|
|
34
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
21
|
+
"@vincemakes/kiso-ask-ext": "0.39.1",
|
|
22
|
+
"@vincemakes/kiso-core": "0.39.1",
|
|
23
|
+
"@vincemakes/kiso-evals": "0.39.1",
|
|
24
|
+
"@vincemakes/kiso-mcp-ext": "0.39.1",
|
|
25
|
+
"@vincemakes/kiso-provider-anthropic": "0.39.1",
|
|
26
|
+
"@vincemakes/kiso-provider-openai": "0.39.1",
|
|
27
|
+
"@vincemakes/kiso-provider-openai-responses": "0.39.1",
|
|
28
|
+
"@vincemakes/kiso-runtime": "0.39.1",
|
|
29
|
+
"@vincemakes/kiso-skills-ext": "0.39.1",
|
|
30
|
+
"@vincemakes/kiso-subagent-ext": "0.39.1",
|
|
31
|
+
"@vincemakes/kiso-task-ext": "0.39.1",
|
|
32
|
+
"@vincemakes/kiso-tools-node": "0.39.1",
|
|
33
|
+
"@vincemakes/kiso-tui": "0.39.1",
|
|
34
|
+
"@vincemakes/kiso-tui-cells": "0.39.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^26.1.2",
|