agent-sh 0.13.2 → 0.13.3
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 +1 -1
- package/dist/utils/diff-renderer.d.ts +6 -0
- package/dist/utils/diff-renderer.js +2 -2
- package/examples/extensions/ashi/package.json +1 -1
- package/examples/extensions/ashi/src/cli.ts +1 -1
- package/examples/extensions/ashi/src/default-renderers.ts +3 -3
- package/examples/extensions/ashi/src/frontend.ts +6 -4
- package/examples/extensions/ollama-cloud.ts +78 -0
- package/examples/extensions/ollama.ts +4 -0
- package/examples/extensions/zai-coding-plan.ts +39 -0
- package/package.json +5 -1
package/dist/agent/agent-loop.js
CHANGED
|
@@ -1201,7 +1201,7 @@ export class AgentLoop {
|
|
|
1201
1201
|
signal });
|
|
1202
1202
|
// Truncate large outputs to avoid blowing context.
|
|
1203
1203
|
let content = result.content;
|
|
1204
|
-
const maxBytes = tool.maxResultBytes ??
|
|
1204
|
+
const maxBytes = tool.maxResultBytes ?? 100_000; // ~25k tokens
|
|
1205
1205
|
if (content.length > maxBytes) {
|
|
1206
1206
|
const headBytes = Math.floor(maxBytes * 0.6);
|
|
1207
1207
|
const tailBytes = maxBytes - headBytes;
|
|
@@ -14,6 +14,12 @@ export interface DiffRenderOptions {
|
|
|
14
14
|
/** Enable syntax highlighting on diff lines. Default true. */
|
|
15
15
|
syntaxHighlight?: boolean;
|
|
16
16
|
}
|
|
17
|
+
export declare function detectLanguage(filePath?: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Syntax-highlight a single line of code.
|
|
20
|
+
* Returns the original text if highlighting fails or no language detected.
|
|
21
|
+
*/
|
|
22
|
+
export declare function highlightLine(text: string, language?: string): string;
|
|
17
23
|
/** Select display mode based on available terminal width. */
|
|
18
24
|
export declare function selectMode(width: number): DiffDisplayMode;
|
|
19
25
|
/** Render a diff result as an array of ANSI-formatted terminal lines. */
|
|
@@ -25,7 +25,7 @@ const EXT_TO_LANG = {
|
|
|
25
25
|
".hs": "haskell", ".ml": "ocaml", ".clj": "clojure",
|
|
26
26
|
".vim": "vim", ".dockerfile": "dockerfile",
|
|
27
27
|
};
|
|
28
|
-
function detectLanguage(filePath) {
|
|
28
|
+
export function detectLanguage(filePath) {
|
|
29
29
|
if (!filePath)
|
|
30
30
|
return undefined;
|
|
31
31
|
const dot = filePath.lastIndexOf(".");
|
|
@@ -44,7 +44,7 @@ function detectLanguage(filePath) {
|
|
|
44
44
|
* Syntax-highlight a single line of code.
|
|
45
45
|
* Returns the original text if highlighting fails or no language detected.
|
|
46
46
|
*/
|
|
47
|
-
function highlightLine(text, language) {
|
|
47
|
+
export function highlightLine(text, language) {
|
|
48
48
|
if (!language || text.trim() === "")
|
|
49
49
|
return text;
|
|
50
50
|
try {
|
|
@@ -145,7 +145,7 @@ async function main(): Promise<void> {
|
|
|
145
145
|
registerDefaultToolRenderers(ctx);
|
|
146
146
|
|
|
147
147
|
ctx.advise("conversation:format-prior-history", () => null);
|
|
148
|
-
ctx.advise("system-prompt:build", (
|
|
148
|
+
ctx.advise("system-prompt:build", (next) => `${next()}\n\n<cwd>${process.cwd()}</cwd>`);
|
|
149
149
|
|
|
150
150
|
const handle = mountAshi(ctx, getStore, capture);
|
|
151
151
|
stopFrontend = handle.stop;
|
|
@@ -17,8 +17,8 @@ const TOOL_ICON: Record<string, string> = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
function iconPrefix(name: string): string {
|
|
20
|
-
const icon = TOOL_ICON[name];
|
|
21
|
-
return
|
|
20
|
+
const icon = TOOL_ICON[name] ?? "⚙";
|
|
21
|
+
return `${theme.fg("warning", icon)} `;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface StatusOpts { exitCode: number | null; elapsedMs: number; summary?: string }
|
|
@@ -145,7 +145,7 @@ function pathOnlyLabel(name: string, args: ToolCallArgs): string {
|
|
|
145
145
|
|
|
146
146
|
function genericLabel(args: ToolCallArgs): string {
|
|
147
147
|
const detail = args.displayDetail ? ` ${muted(args.displayDetail)}` : "";
|
|
148
|
-
return `${bold(args.title)}${detail}`;
|
|
148
|
+
return `${iconPrefix(args.name)}${bold(args.title)}${detail}`;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
export function registerDefaultToolRenderers(ctx: ExtensionContext): void {
|
|
@@ -40,7 +40,7 @@ import { resumeSession } from "./session-commands.js";
|
|
|
40
40
|
import { applyBranchMessages } from "./commands.js";
|
|
41
41
|
import type { Capture } from "./capture.js";
|
|
42
42
|
import { execSync } from "node:child_process";
|
|
43
|
-
import { renderDiff } from "agent-sh/utils/diff-renderer.js";
|
|
43
|
+
import { renderDiff, detectLanguage, highlightLine } from "agent-sh/utils/diff-renderer.js";
|
|
44
44
|
import { renderBoxFrame } from "agent-sh/utils/box-frame.js";
|
|
45
45
|
|
|
46
46
|
interface DiffStats { added: number; removed: number; isNewFile: boolean; isIdentical: boolean }
|
|
@@ -53,10 +53,10 @@ function buildDiffRenderer(
|
|
|
53
53
|
const boxW = Math.max(40, width);
|
|
54
54
|
const contentW = Math.max(20, boxW - 4);
|
|
55
55
|
const inner = diff.isNewFile
|
|
56
|
-
? renderNewFilePreview(diff, 30)
|
|
56
|
+
? renderNewFilePreview(diff, 30, filePath)
|
|
57
57
|
: ((): string[] => {
|
|
58
58
|
const lines = renderDiff(diff, {
|
|
59
|
-
width: contentW, filePath, trueColor: true, maxLines:
|
|
59
|
+
width: contentW, filePath, trueColor: true, maxLines: Number.MAX_SAFE_INTEGER, mode: "unified",
|
|
60
60
|
});
|
|
61
61
|
return lines.length > 1 ? ["", ...lines.slice(1), ""] : lines;
|
|
62
62
|
})();
|
|
@@ -71,14 +71,16 @@ function buildDiffRenderer(
|
|
|
71
71
|
function renderNewFilePreview(
|
|
72
72
|
diff: { hunks?: { lines: { type: string; text: string }[] }[] },
|
|
73
73
|
maxLines: number,
|
|
74
|
+
filePath: string,
|
|
74
75
|
): string[] {
|
|
75
76
|
const lines = diff.hunks?.[0]?.lines.filter((l) => l.type === "added") ?? [];
|
|
76
77
|
const shown = lines.slice(0, maxLines);
|
|
77
78
|
const overflow = lines.length - shown.length;
|
|
78
79
|
const noW = String(shown.length).length || 1;
|
|
80
|
+
const lang = detectLanguage(filePath);
|
|
79
81
|
const body = shown.map((l, i) => {
|
|
80
82
|
const no = String(i + 1).padStart(noW);
|
|
81
|
-
return `${theme.fg("muted", `${no} │`)} ${l.text}`;
|
|
83
|
+
return `${theme.fg("muted", `${no} │`)} ${highlightLine(l.text, lang)}`;
|
|
82
84
|
});
|
|
83
85
|
if (overflow > 0) body.push(theme.fg("muted", `… ${overflow} more lines`));
|
|
84
86
|
return ["", ...body, ""];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Cloud — hosted Ollama models (https://ollama.com).
|
|
3
|
+
*
|
|
4
|
+
* Auth: agent-sh auth login ollama-cloud
|
|
5
|
+
* Usage: agent-sh -e ./examples/extensions/ollama-cloud.ts
|
|
6
|
+
*/
|
|
7
|
+
import { resolveApiKey } from "agent-sh/auth";
|
|
8
|
+
import type { AgentContext } from "agent-sh/types";
|
|
9
|
+
|
|
10
|
+
const HOST = "https://ollama.com";
|
|
11
|
+
const BASE_URL = `${HOST}/v1`;
|
|
12
|
+
const ID = "ollama-cloud";
|
|
13
|
+
|
|
14
|
+
function buildReasoningParams(level: string, _model?: string): Record<string, unknown> {
|
|
15
|
+
return { reasoning_effort: level === "off" ? "none" : level };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function fetchModels(apiKey: string) {
|
|
19
|
+
const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` };
|
|
20
|
+
const tagsRes = await fetch(`${HOST}/api/tags`, { headers });
|
|
21
|
+
if (!tagsRes.ok) return [];
|
|
22
|
+
const tagsData = await tagsRes.json() as { models?: { name: string }[] };
|
|
23
|
+
const names = (tagsData.models ?? []).map((m) => m.name);
|
|
24
|
+
if (!names.length) return [];
|
|
25
|
+
|
|
26
|
+
const ctxs = await Promise.all(
|
|
27
|
+
names.map((name) =>
|
|
28
|
+
fetch(`${HOST}/api/show`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { ...headers, "Content-Type": "application/json" },
|
|
31
|
+
body: JSON.stringify({ name }),
|
|
32
|
+
})
|
|
33
|
+
.then((r) => r.ok ? r.json() as Promise<{ model_info?: Record<string, unknown> }> : null)
|
|
34
|
+
.then((d) => {
|
|
35
|
+
if (!d?.model_info) return undefined;
|
|
36
|
+
const info = d.model_info;
|
|
37
|
+
const arch = info["general.architecture"] as string | undefined;
|
|
38
|
+
if (arch) {
|
|
39
|
+
const ctx = info[`${arch}.context_length`];
|
|
40
|
+
if (typeof ctx === "number") return ctx;
|
|
41
|
+
}
|
|
42
|
+
for (const [k, v] of Object.entries(info)) {
|
|
43
|
+
if (k.endsWith(".context_length") && typeof v === "number") return v;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
})
|
|
47
|
+
.catch(() => undefined),
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return names.map((name, i) => ({
|
|
52
|
+
id: name,
|
|
53
|
+
contextWindow: ctxs[i],
|
|
54
|
+
echoReasoning: /deepseek/i.test(name),
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default function activate(ctx: AgentContext): void {
|
|
59
|
+
const { key } = resolveApiKey(ID);
|
|
60
|
+
const apiKey = key ?? process.env.OLLAMA_API_KEY;
|
|
61
|
+
if (!apiKey) return;
|
|
62
|
+
|
|
63
|
+
ctx.agent.providers.configure(ID, { reasoningParams: buildReasoningParams });
|
|
64
|
+
|
|
65
|
+
// Register placeholder while catalog loads
|
|
66
|
+
ctx.bus.emit("provider:register", { id: ID, apiKey, baseURL: BASE_URL, models: [] });
|
|
67
|
+
|
|
68
|
+
fetchModels(apiKey).then((models) => {
|
|
69
|
+
if (!models.length) return;
|
|
70
|
+
ctx.bus.emit("provider:register", {
|
|
71
|
+
id: ID,
|
|
72
|
+
apiKey,
|
|
73
|
+
baseURL: BASE_URL,
|
|
74
|
+
defaultModel: models[0]!.id,
|
|
75
|
+
models,
|
|
76
|
+
});
|
|
77
|
+
}).catch(() => { /* keep placeholder */ });
|
|
78
|
+
}
|
|
@@ -37,6 +37,10 @@ export default function activate(ctx: ExtensionContext): void {
|
|
|
37
37
|
const headers: Record<string, string> = {};
|
|
38
38
|
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
39
39
|
|
|
40
|
+
ctx.agent.providers.configure(id, {
|
|
41
|
+
reasoningParams: (level) => ({ reasoning_effort: level === "off" ? "none" : level }),
|
|
42
|
+
});
|
|
43
|
+
|
|
40
44
|
ctx.bus.emit("provider:register", { id, apiKey: sdkKey, baseURL, models: [] });
|
|
41
45
|
|
|
42
46
|
fetchCatalog(host, headers).then((models) => {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Z.AI Coding Plan — Zhipu AI's subscription GLM models for coding tools.
|
|
3
|
+
*
|
|
4
|
+
* Auth: agent-sh auth login zai-coding-plan
|
|
5
|
+
* Usage: agent-sh -e ./examples/extensions/zai-coding-plan.ts
|
|
6
|
+
*/
|
|
7
|
+
import { resolveApiKey } from "agent-sh/auth";
|
|
8
|
+
import type { AgentContext } from "agent-sh/types";
|
|
9
|
+
|
|
10
|
+
const BASE_URL = "https://api.z.ai/api/coding/paas/v4";
|
|
11
|
+
const ID = "zai-coding-plan";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_MODELS = [
|
|
14
|
+
{ id: "glm-5.1", reasoning: true, contextWindow: 200_000 },
|
|
15
|
+
{ id: "glm-5-turbo", reasoning: true, contextWindow: 200_000 },
|
|
16
|
+
{ id: "glm-4.7", reasoning: true, contextWindow: 204_800 },
|
|
17
|
+
{ id: "glm-4.5-air", reasoning: true, contextWindow: 131_072 },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function buildReasoningParams(level: string, _model?: string): Record<string, unknown> {
|
|
21
|
+
if (level === "off") return { thinking: { type: "disabled" } };
|
|
22
|
+
return { thinking: { type: "enabled" }, reasoning_effort: level };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function activate(ctx: AgentContext): void {
|
|
26
|
+
const { key } = resolveApiKey(ID);
|
|
27
|
+
const apiKey = key ?? process.env.ZAI_API_KEY ?? process.env.ZHIPU_API_KEY;
|
|
28
|
+
if (!apiKey) return;
|
|
29
|
+
|
|
30
|
+
ctx.agent.providers.configure(ID, { reasoningParams: buildReasoningParams });
|
|
31
|
+
|
|
32
|
+
ctx.bus.emit("provider:register", {
|
|
33
|
+
id: ID,
|
|
34
|
+
apiKey: apiKey,
|
|
35
|
+
baseURL: BASE_URL,
|
|
36
|
+
defaultModel: DEFAULT_MODELS[0].id,
|
|
37
|
+
models: DEFAULT_MODELS,
|
|
38
|
+
});
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sh",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.3",
|
|
4
4
|
"description": "A shell-first terminal where AI is one keystroke away",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -101,6 +101,10 @@
|
|
|
101
101
|
"./cli/auth": {
|
|
102
102
|
"types": "./dist/cli/auth/cli.d.ts",
|
|
103
103
|
"default": "./dist/cli/auth/cli.js"
|
|
104
|
+
},
|
|
105
|
+
"./auth": {
|
|
106
|
+
"types": "./dist/cli/auth/keys.d.ts",
|
|
107
|
+
"default": "./dist/cli/auth/keys.js"
|
|
104
108
|
}
|
|
105
109
|
},
|
|
106
110
|
"files": [
|