agent-sh 0.14.4 → 0.14.6
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/agent/agent-loop.js
CHANGED
|
@@ -192,11 +192,15 @@ export class AgentLoop {
|
|
|
192
192
|
const modes = this.pullModes();
|
|
193
193
|
const prev = this.activeMode;
|
|
194
194
|
const fresh = modes.find((m) => m.model === prev.model && m.provider === prev.provider);
|
|
195
|
+
let identityChanged = false;
|
|
195
196
|
if (fresh) {
|
|
196
197
|
this.activeMode = fresh;
|
|
197
198
|
if (fresh.providerConfig && fresh.providerConfig !== prev.providerConfig) {
|
|
198
199
|
this.llmClient.reconfigure({ ...fresh.providerConfig, model: fresh.model });
|
|
199
200
|
}
|
|
201
|
+
identityChanged = fresh.model !== prev.model
|
|
202
|
+
|| fresh.provider !== prev.provider
|
|
203
|
+
|| fresh.contextWindow !== prev.contextWindow;
|
|
200
204
|
}
|
|
201
205
|
else if (prev.provider) {
|
|
202
206
|
// Ghost: keep prev active so mid-turn stream() doesn't switch models.
|
|
@@ -204,7 +208,8 @@ export class AgentLoop {
|
|
|
204
208
|
message: `${prev.provider}:${prev.model} is not in the refreshed catalog — keeping it active until you /model to another.`,
|
|
205
209
|
});
|
|
206
210
|
}
|
|
207
|
-
|
|
211
|
+
if (identityChanged)
|
|
212
|
+
this.emitIdentity();
|
|
208
213
|
this.bus.emit("config:changed", {});
|
|
209
214
|
});
|
|
210
215
|
onPipe("config:get-models", () => {
|
package/dist/agent/index.js
CHANGED
|
@@ -320,6 +320,8 @@ export default function agentBackend(ctx) {
|
|
|
320
320
|
bus.emit("agent:modes-changed", {});
|
|
321
321
|
if (!ashActive)
|
|
322
322
|
return;
|
|
323
|
+
if (buildModes().some((m) => m.model === llmClient.model))
|
|
324
|
+
return;
|
|
323
325
|
const pendingProvider = getSettings().defaultProvider;
|
|
324
326
|
if (!pendingProvider)
|
|
325
327
|
return;
|
package/dist/shell/shell.js
CHANGED
|
@@ -5,7 +5,8 @@ import { InputHandler } from "./input-handler.js";
|
|
|
5
5
|
import { OutputParser } from "./output-parser.js";
|
|
6
6
|
import { getSettings } from "../core/settings.js";
|
|
7
7
|
import { clearOpost } from "../utils/tty.js";
|
|
8
|
-
import { processTerminal } from "./terminal.js";
|
|
8
|
+
import { processTerminal, surfaceFromTerminal } from "./terminal.js";
|
|
9
|
+
import { TuiInputView } from "./tui-input-view.js";
|
|
9
10
|
import { pickStrategy, FALLBACK_STRATEGY, SUPPORTED_SHELL_NAMES, } from "./strategies/index.js";
|
|
10
11
|
export class Shell {
|
|
11
12
|
ptyProcess;
|
|
@@ -96,6 +97,7 @@ export class Shell {
|
|
|
96
97
|
bus: opts.bus,
|
|
97
98
|
handlers: opts.handlers,
|
|
98
99
|
onShowAgentInfo: opts.onShowAgentInfo ?? (() => ({ info: "" })),
|
|
100
|
+
view: new TuiInputView(surfaceFromTerminal(this.terminal)),
|
|
99
101
|
});
|
|
100
102
|
this.setupOutput();
|
|
101
103
|
this.setupInput();
|
package/dist/shell/terminal.d.ts
CHANGED
|
@@ -25,6 +25,32 @@ export interface Terminal {
|
|
|
25
25
|
}
|
|
26
26
|
/** Default Terminal: wraps process.stdin/stdout. */
|
|
27
27
|
export declare function processTerminal(): Terminal;
|
|
28
|
+
/**
|
|
29
|
+
* No-op terminal for non-rendering hosts (tests, agent-only embeds).
|
|
30
|
+
* Writes are discarded; input/resize never fire.
|
|
31
|
+
*/
|
|
32
|
+
export declare function headlessTerminal(cols?: number, rows?: number): Terminal;
|
|
33
|
+
/**
|
|
34
|
+
* Pipe-based terminal for embedders that own their own renderer (web hubs
|
|
35
|
+
* via xterm.js, electron windows, recording harnesses). Bytes from the
|
|
36
|
+
* Shell flow through `onWrite`; the host drives `pushInput`/`pushResize`
|
|
37
|
+
* to forward keystrokes and viewport changes back.
|
|
38
|
+
*/
|
|
39
|
+
export declare class BridgedTerminal implements Terminal {
|
|
40
|
+
private readonly onWrite;
|
|
41
|
+
private inputCbs;
|
|
42
|
+
private resizeCbs;
|
|
43
|
+
private _cols;
|
|
44
|
+
private _rows;
|
|
45
|
+
constructor(onWrite: (data: string) => void, cols?: number, rows?: number);
|
|
46
|
+
write(data: string): void;
|
|
47
|
+
onInput(cb: (d: string) => void): () => void;
|
|
48
|
+
onResize(cb: (c: number, r: number) => void): () => void;
|
|
49
|
+
cols(): number;
|
|
50
|
+
rows(): number;
|
|
51
|
+
pushInput(data: string): void;
|
|
52
|
+
pushResize(cols: number, rows: number): void;
|
|
53
|
+
}
|
|
28
54
|
/**
|
|
29
55
|
* Adapt a Terminal to a RenderSurface (the compositor's sink type). Adds
|
|
30
56
|
* the OPOST-cleared `\n` → `\r\n` translation that StdoutSurface applies,
|
package/dist/shell/terminal.js
CHANGED
|
@@ -45,6 +45,50 @@ export function processTerminal() {
|
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* No-op terminal for non-rendering hosts (tests, agent-only embeds).
|
|
50
|
+
* Writes are discarded; input/resize never fire.
|
|
51
|
+
*/
|
|
52
|
+
export function headlessTerminal(cols = 100, rows = 30) {
|
|
53
|
+
return {
|
|
54
|
+
write() { },
|
|
55
|
+
onInput: () => () => { },
|
|
56
|
+
onResize: () => () => { },
|
|
57
|
+
cols: () => cols,
|
|
58
|
+
rows: () => rows,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Pipe-based terminal for embedders that own their own renderer (web hubs
|
|
63
|
+
* via xterm.js, electron windows, recording harnesses). Bytes from the
|
|
64
|
+
* Shell flow through `onWrite`; the host drives `pushInput`/`pushResize`
|
|
65
|
+
* to forward keystrokes and viewport changes back.
|
|
66
|
+
*/
|
|
67
|
+
export class BridgedTerminal {
|
|
68
|
+
onWrite;
|
|
69
|
+
inputCbs = new Set();
|
|
70
|
+
resizeCbs = new Set();
|
|
71
|
+
_cols;
|
|
72
|
+
_rows;
|
|
73
|
+
constructor(onWrite, cols = 100, rows = 30) {
|
|
74
|
+
this.onWrite = onWrite;
|
|
75
|
+
this._cols = cols;
|
|
76
|
+
this._rows = rows;
|
|
77
|
+
}
|
|
78
|
+
write(data) { this.onWrite(data); }
|
|
79
|
+
onInput(cb) { this.inputCbs.add(cb); return () => { this.inputCbs.delete(cb); }; }
|
|
80
|
+
onResize(cb) { this.resizeCbs.add(cb); return () => { this.resizeCbs.delete(cb); }; }
|
|
81
|
+
cols() { return this._cols; }
|
|
82
|
+
rows() { return this._rows; }
|
|
83
|
+
pushInput(data) { for (const cb of this.inputCbs)
|
|
84
|
+
cb(data); }
|
|
85
|
+
pushResize(cols, rows) {
|
|
86
|
+
this._cols = cols;
|
|
87
|
+
this._rows = rows;
|
|
88
|
+
for (const cb of this.resizeCbs)
|
|
89
|
+
cb(cols, rows);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
48
92
|
/**
|
|
49
93
|
* Adapt a Terminal to a RenderSurface (the compositor's sink type). Adds
|
|
50
94
|
* the OPOST-cleared `\n` → `\r\n` translation that StdoutSurface applies,
|
|
@@ -43,16 +43,13 @@ export class StatusFooter extends Container {
|
|
|
43
43
|
const contentWidth = width > 0 ? Math.max(1, width - 2) : 0;
|
|
44
44
|
const right = this.buildRight();
|
|
45
45
|
const rightWidth = visibleWidth(right);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const fullFits = contentWidth === 0
|
|
54
|
-
|| visibleWidth(full) + (right ? rightWidth + 1 : 0) <= contentWidth;
|
|
55
|
-
this.text.setText(fullFits ? join(full) : join(this.buildLine("basename")));
|
|
46
|
+
const left = this.buildLine();
|
|
47
|
+
if (!right) {
|
|
48
|
+
this.text.setText(left);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const gap = Math.max(1, contentWidth - visibleWidth(left) - rightWidth);
|
|
52
|
+
this.text.setText(`${left}${" ".repeat(gap)}${right}`);
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
private buildRight(): string {
|
|
@@ -62,7 +59,7 @@ export class StatusFooter extends Container {
|
|
|
62
59
|
return "";
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
private buildLine(
|
|
62
|
+
private buildLine(): string {
|
|
66
63
|
const { model, provider, contextWindow, cwd, branch, leaf, tokens, compactions, thinking } = this.fields;
|
|
67
64
|
const sep = theme.fg("dim", " | ");
|
|
68
65
|
const parts: string[] = [];
|
|
@@ -73,7 +70,7 @@ export class StatusFooter extends Container {
|
|
|
73
70
|
} else if (provider) {
|
|
74
71
|
parts.push(theme.fg("muted", `@${provider}`));
|
|
75
72
|
}
|
|
76
|
-
if (cwd) parts.push(theme.fg("muted",
|
|
73
|
+
if (cwd) parts.push(theme.fg("muted", basename(cwd) || cwd));
|
|
77
74
|
if (branch) parts.push(theme.fg("muted", `⎇ ${branch}`));
|
|
78
75
|
if (leaf != null && leaf > 0) parts.push(theme.fg("muted", `#${leaf}`));
|
|
79
76
|
if (tokens != null) {
|
|
@@ -86,14 +83,6 @@ export class StatusFooter extends Container {
|
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
function formatCwd(cwd: string, mode: "full" | "basename"): string {
|
|
90
|
-
if (mode === "basename") return basename(cwd) || cwd;
|
|
91
|
-
const home = process.env.HOME;
|
|
92
|
-
if (home && cwd.startsWith(`${home}/`)) return `~/${cwd.slice(home.length + 1)}`;
|
|
93
|
-
if (home && cwd === home) return "~";
|
|
94
|
-
return cwd;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
86
|
function fmtTokens(n: number): string {
|
|
98
87
|
if (n < 1000) return String(n);
|
|
99
88
|
if (n < 100_000) return `${(n / 1000).toFixed(1)}k`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sh",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.6",
|
|
4
4
|
"description": "A shell-first terminal where AI is one keystroke away",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"types": "./dist/shell/shell.d.ts",
|
|
42
42
|
"default": "./dist/shell/shell.js"
|
|
43
43
|
},
|
|
44
|
+
"./shell/host": {
|
|
45
|
+
"types": "./dist/shell/index.d.ts",
|
|
46
|
+
"default": "./dist/shell/index.js"
|
|
47
|
+
},
|
|
44
48
|
"./shell/strategies": {
|
|
45
49
|
"types": "./dist/shell/strategies/index.d.ts",
|
|
46
50
|
"default": "./dist/shell/strategies/index.js"
|