agent-sh 0.13.6 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agent/agent-loop.d.ts +13 -17
- package/dist/agent/agent-loop.js +118 -224
- package/dist/agent/conversation-state.d.ts +1 -1
- package/dist/agent/events.d.ts +218 -0
- package/dist/agent/events.js +1 -0
- package/dist/agent/host-types.d.ts +20 -0
- package/dist/agent/index.d.ts +5 -9
- package/dist/agent/index.js +269 -167
- package/dist/agent/llm-facade.d.ts +13 -0
- package/dist/{utils → agent}/llm-facade.js +1 -1
- package/dist/agent/nuclear-form.d.ts +1 -1
- package/dist/agent/providers/deepseek.js +2 -5
- package/dist/agent/providers/openai-compatible.js +2 -2
- package/dist/agent/providers/openai.js +2 -5
- package/dist/agent/providers/openrouter.js +5 -5
- package/dist/agent/subagent.d.ts +1 -1
- package/dist/agent/tool-protocol.d.ts +1 -1
- package/dist/agent/tool-registry.d.ts +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +90 -0
- package/dist/cli/auth/cli.js +11 -6
- package/dist/cli/auth/discover.d.ts +5 -0
- package/dist/cli/auth/discover.js +25 -0
- package/dist/cli/auth/keys.d.ts +5 -2
- package/dist/cli/auth/keys.js +22 -2
- package/dist/cli/index.d.ts +16 -0
- package/dist/cli/index.js +15 -156
- package/dist/cli/shell-env.d.ts +2 -0
- package/dist/cli/shell-env.js +61 -0
- package/dist/core/event-bus.d.ts +28 -371
- package/dist/core/extension-loader.js +6 -6
- package/dist/core/index.d.ts +10 -29
- package/dist/core/index.js +31 -82
- package/dist/extensions/index.d.ts +2 -1
- package/dist/extensions/index.js +1 -1
- package/dist/extensions/slash-commands/events.d.ts +18 -0
- package/dist/extensions/slash-commands/events.js +1 -0
- package/dist/extensions/slash-commands/index.d.ts +15 -0
- package/dist/extensions/{slash-commands.js → slash-commands/index.js} +4 -3
- package/dist/shell/events.d.ts +85 -0
- package/dist/shell/events.js +1 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.js +6 -0
- package/dist/shell/tui-renderer.js +0 -1
- package/examples/extensions/ash-acp-bridge/src/index.ts +2 -2
- package/examples/extensions/ashi/package.json +1 -1
- package/examples/extensions/ollama.ts +47 -42
- package/examples/extensions/opencode-bridge/README.md +4 -0
- package/examples/extensions/opencode-bridge/index.ts +3 -1
- package/examples/extensions/pi-bridge/index.ts +3 -4
- package/examples/extensions/zai-coding-plan.ts +2 -6
- package/package.json +2 -1
- package/dist/extensions/slash-commands.d.ts +0 -2
- package/dist/utils/llm-facade.d.ts +0 -11
- /package/dist/{utils → agent}/llm-client.d.ts +0 -0
- /package/dist/{utils → agent}/llm-client.js +0 -0
package/dist/core/index.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core kernel —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* subscribing to bus events. Shell-specific tracking lives in the
|
|
7
|
-
* shell-context built-in extension.
|
|
8
|
-
*
|
|
9
|
-
* Agent backends register themselves via the agent:register-backend bus
|
|
10
|
-
* event. The built-in "ash" backend lives in src/agent/ and is activated
|
|
11
|
-
* by hosts via activateAgent().
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* import { createCore } from "agent-sh";
|
|
15
|
-
* const core = createCore({ apiKey: "...", model: "gpt-4o" });
|
|
16
|
-
* core.bus.on("agent:response-chunk", ({ blocks }) => { ... });
|
|
17
|
-
* core.activateBackend();
|
|
18
|
-
* const response = await core.query("hello");
|
|
2
|
+
* Core kernel — EventBus + HandlerRegistry + backend registry. Knows
|
|
3
|
+
* nothing about LLMs, tools, or specific backends; backends (ash,
|
|
4
|
+
* claude-code-bridge, ...) register through `agent:register-backend`
|
|
5
|
+
* and core dispatches to whichever is configured as default.
|
|
19
6
|
*/
|
|
20
7
|
import { EventBus } from "./event-bus.js";
|
|
8
|
+
// Side-effect imports so downstream tsc sees module-augmented BusEvents.
|
|
9
|
+
import "../shell/events.js";
|
|
10
|
+
import "../agent/events.js";
|
|
11
|
+
import "../extensions/slash-commands/events.js";
|
|
21
12
|
import * as settingsMod from "./settings.js";
|
|
22
13
|
import { HandlerRegistry } from "../utils/handler-registry.js";
|
|
23
14
|
import crypto from "node:crypto";
|
|
@@ -27,117 +18,74 @@ import { CONFIG_DIR } from "./settings.js";
|
|
|
27
18
|
export { EventBus } from "./event-bus.js";
|
|
28
19
|
export { palette, setPalette, resetPalette } from "../utils/palette.js";
|
|
29
20
|
export { runSubagent } from "../agent/subagent.js";
|
|
30
|
-
export { LlmClient } from "../
|
|
21
|
+
export { LlmClient } from "../agent/llm-client.js";
|
|
31
22
|
export { HistoryFile, InMemoryHistory, NoopHistory } from "../agent/history-file.js";
|
|
32
23
|
export { compileSearchRegex, matchEntry, formatNuclearLine } from "../agent/nuclear-form.js";
|
|
33
24
|
export function createCore(config) {
|
|
34
25
|
const bus = new EventBus();
|
|
35
26
|
const handlers = new HandlerRegistry();
|
|
36
|
-
// 3 bytes = 6 hex chars
|
|
37
|
-
//
|
|
38
|
-
// parsers should accept ≥6 hex chars.
|
|
27
|
+
// 3 bytes = 6 hex chars; legacy content may have 16-char iids so parsers
|
|
28
|
+
// should accept ≥6 hex chars.
|
|
39
29
|
const instanceId = crypto.randomBytes(3).toString("hex");
|
|
40
30
|
bus.setSource(instanceId);
|
|
41
|
-
const settings = settingsMod.getSettings();
|
|
42
31
|
handlers.define("config:get-app-config", () => config);
|
|
43
|
-
// Default; shell-context advises with the PTY-tracked cwd when loaded.
|
|
44
32
|
handlers.define("cwd", () => process.cwd());
|
|
45
33
|
// Empty defaults so registerContextProducer can advise regardless of
|
|
46
|
-
// backend. Each backend chooses
|
|
47
|
-
// wraps them in <dynamic_context>/<query_context>; bridges may pull
|
|
48
|
-
// query-context:build and splice into the target SDK however they like.
|
|
34
|
+
// backend. Each backend chooses how to consume the strings.
|
|
49
35
|
handlers.define("dynamic-context:build", () => "");
|
|
50
36
|
handlers.define("query-context:build", () => "");
|
|
51
37
|
const backends = new Map();
|
|
52
38
|
let activeBackendName = null;
|
|
39
|
+
bus.on("agent:register-backend", (backend) => {
|
|
40
|
+
backends.set(backend.name, backend);
|
|
41
|
+
});
|
|
42
|
+
bus.onPipe("config:get-backends", () => ({
|
|
43
|
+
names: [...backends.keys()],
|
|
44
|
+
active: activeBackendName,
|
|
45
|
+
}));
|
|
53
46
|
const activateByName = async (name) => {
|
|
54
47
|
const backend = backends.get(name);
|
|
55
48
|
if (!backend) {
|
|
56
49
|
bus.emit("ui:error", { message: `Unknown backend: ${name}` });
|
|
57
50
|
return false;
|
|
58
51
|
}
|
|
59
|
-
if (activeBackendName) {
|
|
52
|
+
if (activeBackendName && activeBackendName !== name) {
|
|
60
53
|
backends.get(activeBackendName)?.kill();
|
|
61
54
|
}
|
|
62
|
-
await backend.start?.();
|
|
63
55
|
activeBackendName = name;
|
|
56
|
+
await backend.start?.();
|
|
64
57
|
return true;
|
|
65
58
|
};
|
|
66
|
-
bus.on("agent:register-backend", (backend) => {
|
|
67
|
-
backends.set(backend.name, backend);
|
|
68
|
-
});
|
|
69
59
|
bus.on("config:switch-backend", ({ name }) => {
|
|
70
60
|
activateByName(name).then((ok) => {
|
|
71
61
|
if (!ok)
|
|
72
62
|
return;
|
|
73
63
|
settingsMod.updateSettings({ defaultBackend: name });
|
|
74
|
-
// Single ui:info; config:changed (which triggers prompt redraw) follows it.
|
|
75
64
|
bus.emit("ui:info", { message: `Backend: ${name} (saved as default)` });
|
|
76
65
|
bus.emit("config:changed", {});
|
|
77
66
|
});
|
|
78
67
|
});
|
|
79
68
|
bus.on("config:list-backends", () => {
|
|
80
|
-
const
|
|
81
|
-
const list = names
|
|
69
|
+
const list = [...backends.keys()]
|
|
82
70
|
.map((n) => n === activeBackendName ? `${n} (active)` : n)
|
|
83
71
|
.join(", ");
|
|
84
|
-
bus.emit("ui:info", { message: `Backends: ${list}` });
|
|
85
|
-
});
|
|
86
|
-
bus.onPipe("config:get-backends", () => {
|
|
87
|
-
const names = [...backends.keys()];
|
|
88
|
-
return { names, active: activeBackendName };
|
|
72
|
+
bus.emit("ui:info", { message: `Backends: ${list || "(none registered)"}` });
|
|
89
73
|
});
|
|
90
74
|
return {
|
|
91
75
|
bus,
|
|
92
76
|
handlers,
|
|
93
77
|
instanceId,
|
|
94
78
|
async activateBackend(override) {
|
|
95
|
-
if (backends.size === 0)
|
|
79
|
+
if (backends.size === 0) {
|
|
80
|
+
bus.emit("ui:info", { message: "No agent backend registered." });
|
|
96
81
|
return;
|
|
97
|
-
|
|
98
|
-
const
|
|
82
|
+
}
|
|
83
|
+
const preferred = override ?? settingsMod.getSettings().defaultBackend;
|
|
84
|
+
const name = preferred && backends.has(preferred)
|
|
85
|
+
? preferred
|
|
86
|
+
: backends.keys().next().value;
|
|
99
87
|
await activateByName(name);
|
|
100
88
|
},
|
|
101
|
-
async query(text) {
|
|
102
|
-
return new Promise((resolve, reject) => {
|
|
103
|
-
let response = "";
|
|
104
|
-
let settled = false;
|
|
105
|
-
const onChunk = (e) => {
|
|
106
|
-
for (const b of e.blocks)
|
|
107
|
-
if (b.type === "text")
|
|
108
|
-
response += b.text;
|
|
109
|
-
};
|
|
110
|
-
const onDone = () => {
|
|
111
|
-
if (settled)
|
|
112
|
-
return;
|
|
113
|
-
settled = true;
|
|
114
|
-
cleanup();
|
|
115
|
-
resolve(response);
|
|
116
|
-
};
|
|
117
|
-
const onError = (e) => {
|
|
118
|
-
if (settled)
|
|
119
|
-
return;
|
|
120
|
-
settled = true;
|
|
121
|
-
cleanup();
|
|
122
|
-
reject(new Error(e.message));
|
|
123
|
-
};
|
|
124
|
-
const cleanup = () => {
|
|
125
|
-
bus.off("agent:response-chunk", onChunk);
|
|
126
|
-
bus.off("agent:processing-done", onDone);
|
|
127
|
-
bus.off("agent:error", onError);
|
|
128
|
-
};
|
|
129
|
-
bus.on("agent:response-chunk", onChunk);
|
|
130
|
-
bus.on("agent:processing-done", onDone);
|
|
131
|
-
bus.on("agent:error", onError);
|
|
132
|
-
bus.emit("agent:submit", { query: text });
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
cancel() {
|
|
136
|
-
bus.emit("agent:cancel-request", {});
|
|
137
|
-
},
|
|
138
|
-
appendUserMessage(text) {
|
|
139
|
-
bus.emit("agent:append-user-message", { text });
|
|
140
|
-
},
|
|
141
89
|
extensionContext(opts) {
|
|
142
90
|
const ctx = {
|
|
143
91
|
bus,
|
|
@@ -165,6 +113,7 @@ export function createCore(config) {
|
|
|
165
113
|
kill() {
|
|
166
114
|
if (activeBackendName) {
|
|
167
115
|
backends.get(activeBackendName)?.kill();
|
|
116
|
+
activeBackendName = null;
|
|
168
117
|
}
|
|
169
118
|
},
|
|
170
119
|
};
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Cross-cutting built-ins, toggleable via `disabledBuiltins`.
|
|
3
3
|
* Module-owned built-ins activate inline:
|
|
4
4
|
* shell-context, tui-renderer → registerShellHandlers (src/shell/)
|
|
5
|
-
*
|
|
5
|
+
* ash (a specific backend) → activateAgent (src/agent/)
|
|
6
|
+
* backend registry → createCore (src/core/)
|
|
6
7
|
*/
|
|
7
8
|
import type { ExtensionContext } from "../shell/host-types.js";
|
|
8
9
|
type ActivateFn = (ctx: ExtensionContext) => void;
|
package/dist/extensions/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BUILTIN_EXTENSIONS = [
|
|
2
|
-
{ name: "slash-commands", load: () => import("./slash-commands.js").then(m => m.default) },
|
|
2
|
+
{ name: "slash-commands", load: () => import("./slash-commands/index.js").then(m => m.default) },
|
|
3
3
|
{ name: "file-autocomplete", load: () => import("./file-autocomplete.js").then(m => m.default) },
|
|
4
4
|
];
|
|
5
5
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Events slash-commands owns. */
|
|
2
|
+
declare module "../../core/event-bus.js" {
|
|
3
|
+
interface BusEvents {
|
|
4
|
+
"command:register": {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
handler: (args: string) => Promise<void> | void;
|
|
8
|
+
};
|
|
9
|
+
"command:unregister": {
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
"command:execute": {
|
|
13
|
+
name: string;
|
|
14
|
+
args: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash commands extension.
|
|
3
|
+
*
|
|
4
|
+
* Registers built-in slash commands on the event bus:
|
|
5
|
+
* - Listens for "command:register" to accept commands from extensions
|
|
6
|
+
* - Responds to "autocomplete:request" pipe for /-prefixed completions
|
|
7
|
+
* - Handles "command:execute" events and dispatches to matching handler
|
|
8
|
+
* - Uses "ui:info"/"ui:error" for user feedback (no direct TUI dependency)
|
|
9
|
+
*
|
|
10
|
+
* Argument completion is composable: any extension can onPipe("autocomplete:request")
|
|
11
|
+
* and check payload.command / payload.commandArgs to add completions for any command.
|
|
12
|
+
*/
|
|
13
|
+
import "./events.js";
|
|
14
|
+
import type { ExtensionContext } from "../../shell/host-types.js";
|
|
15
|
+
export default function activate(ctx: ExtensionContext): void;
|
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
* Argument completion is composable: any extension can onPipe("autocomplete:request")
|
|
11
11
|
* and check payload.command / payload.commandArgs to add completions for any command.
|
|
12
12
|
*/
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import "./events.js";
|
|
14
|
+
import { palette as p } from "../../utils/palette.js";
|
|
15
|
+
import { discoverSkills, loadSkillContent } from "../../agent/skills.js";
|
|
16
|
+
import { reloadExtensions } from "../../core/extension-loader.js";
|
|
16
17
|
export default function activate(ctx) {
|
|
17
18
|
const { bus } = ctx;
|
|
18
19
|
const commands = new Map();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Events owned by the shell subsystem. */
|
|
2
|
+
declare module "../core/event-bus.js" {
|
|
3
|
+
interface BusEvents {
|
|
4
|
+
"shell:command-start": {
|
|
5
|
+
command: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
};
|
|
8
|
+
"shell:command-done": {
|
|
9
|
+
command: string;
|
|
10
|
+
output: string;
|
|
11
|
+
cwd: string;
|
|
12
|
+
exitCode: number | null;
|
|
13
|
+
};
|
|
14
|
+
"shell:cwd-change": {
|
|
15
|
+
cwd: string;
|
|
16
|
+
};
|
|
17
|
+
"shell:foreground-busy": {
|
|
18
|
+
busy: boolean;
|
|
19
|
+
};
|
|
20
|
+
"shell:agent-exec-start": Record<string, never>;
|
|
21
|
+
"shell:agent-exec-done": Record<string, never>;
|
|
22
|
+
"shell:pty-data": {
|
|
23
|
+
raw: string;
|
|
24
|
+
};
|
|
25
|
+
"shell:pty-write": {
|
|
26
|
+
data: string;
|
|
27
|
+
};
|
|
28
|
+
"shell:pty-resize": {
|
|
29
|
+
cols: number;
|
|
30
|
+
rows: number;
|
|
31
|
+
};
|
|
32
|
+
"shell:buffer-request": Record<string, never>;
|
|
33
|
+
"shell:buffer-snapshot": {
|
|
34
|
+
text: string;
|
|
35
|
+
altScreen: boolean;
|
|
36
|
+
cursor: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
"shell:stdout-hold": Record<string, never>;
|
|
42
|
+
"shell:stdout-release": Record<string, never>;
|
|
43
|
+
"shell:stdout-show": Record<string, never>;
|
|
44
|
+
"shell:stdout-hide": Record<string, never>;
|
|
45
|
+
/** Sync pipe: handled=true suppresses default redraw. */
|
|
46
|
+
"shell:redraw-prompt": {
|
|
47
|
+
cwd: string;
|
|
48
|
+
kind: "fresh" | "redraw";
|
|
49
|
+
handled: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** Async pipe: extension → user's PTY. */
|
|
52
|
+
"shell:exec-request": {
|
|
53
|
+
command: string;
|
|
54
|
+
output: string;
|
|
55
|
+
cwd: string;
|
|
56
|
+
exitCode: number | null;
|
|
57
|
+
done: boolean;
|
|
58
|
+
};
|
|
59
|
+
"input-mode:register": import("./host-types.js").InputModeConfig;
|
|
60
|
+
"input:keypress": {
|
|
61
|
+
key: string;
|
|
62
|
+
};
|
|
63
|
+
"input:intercept": {
|
|
64
|
+
data: string;
|
|
65
|
+
consumed: boolean;
|
|
66
|
+
};
|
|
67
|
+
"compositor:write": {
|
|
68
|
+
stream: string;
|
|
69
|
+
text: string;
|
|
70
|
+
};
|
|
71
|
+
/** Sync pipe: extensions append items. */
|
|
72
|
+
"autocomplete:request": {
|
|
73
|
+
buffer: string;
|
|
74
|
+
/** "/backend" or null if not a command. */
|
|
75
|
+
command: string | null;
|
|
76
|
+
/** Text after the command name, or null. */
|
|
77
|
+
commandArgs: string | null;
|
|
78
|
+
items: {
|
|
79
|
+
name: string;
|
|
80
|
+
description: string;
|
|
81
|
+
}[];
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/shell/index.d.ts
CHANGED
package/dist/shell/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontend bootstrap. Loaded directly from src/cli/index.ts (not the
|
|
3
|
+
* built-in extensions manifest) because PTY + stdin raw mode ownership is
|
|
4
|
+
* order-critical.
|
|
5
|
+
*/
|
|
6
|
+
import "./events.js"; // augments BusEvents with shell-owned events
|
|
1
7
|
import { Shell } from "./shell.js";
|
|
2
8
|
import { DefaultCompositor, StdoutSurface } from "../utils/compositor.js";
|
|
3
9
|
import { TerminalBuffer } from "../utils/terminal-buffer.js";
|
|
@@ -149,7 +149,6 @@ export default function activate(ctx) {
|
|
|
149
149
|
titleRight: modelLabel,
|
|
150
150
|
});
|
|
151
151
|
});
|
|
152
|
-
// Track backend/model info for display on response border
|
|
153
152
|
let backendInfo = null;
|
|
154
153
|
bus.on("agent:info", (info) => { backendInfo = info; });
|
|
155
154
|
// ── Register fenced block transform (code blocks → ContentBlock) ──
|
|
@@ -434,10 +434,10 @@ function waitForModelsToSettle(
|
|
|
434
434
|
timer = setTimeout(done, Math.max(0, Math.min(quietMs, remaining)));
|
|
435
435
|
};
|
|
436
436
|
const done = () => {
|
|
437
|
-
core.bus.off("
|
|
437
|
+
core.bus.off("agent:modes-changed", arm);
|
|
438
438
|
resolve();
|
|
439
439
|
};
|
|
440
|
-
core.bus.on("
|
|
440
|
+
core.bus.on("agent:modes-changed", arm);
|
|
441
441
|
arm();
|
|
442
442
|
});
|
|
443
443
|
}
|
|
@@ -1,60 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* agent-sh auth login ollama-cloud # preferred
|
|
6
|
-
* OLLAMA_API_KEY=... # env fallback
|
|
7
|
-
*
|
|
8
|
-
* Local host:
|
|
9
|
-
* OLLAMA_HOST (default http://localhost:11434)
|
|
10
|
-
*
|
|
11
|
-
* Catalog comes from /api/tags; per-model context length is fetched
|
|
12
|
-
* from /api/show (model_info["${arch}.context_length"]). Chat goes
|
|
13
|
-
* through the OpenAI-compatible /v1/chat/completions shim.
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* agent-sh -e ./examples/extensions/ollama.ts
|
|
17
|
-
*
|
|
18
|
-
* # Or add to settings.json:
|
|
19
|
-
* { "extensions": ["./examples/extensions/ollama.ts"] }
|
|
2
|
+
* Registers `ollama` (local, no auth) and `ollama-cloud` (login via
|
|
3
|
+
* `agent-sh auth login ollama-cloud` or OLLAMA_API_KEY). Local host
|
|
4
|
+
* overridable via OLLAMA_HOST.
|
|
20
5
|
*/
|
|
21
6
|
import { resolveApiKey } from "agent-sh/auth";
|
|
22
7
|
import type { AgentContext } from "agent-sh/types";
|
|
23
8
|
|
|
24
9
|
const ECHO_REASONING_PATTERNS: RegExp[] = [/deepseek/i];
|
|
25
10
|
|
|
11
|
+
function reasoningParams(level: string): Record<string, unknown> {
|
|
12
|
+
if (level === "off") return { reasoning_effort: "none" };
|
|
13
|
+
return { reasoning_effort: level === "xhigh" ? "high" : level };
|
|
14
|
+
}
|
|
15
|
+
|
|
26
16
|
export default function activate(ctx: AgentContext): void {
|
|
27
17
|
const cloudKey = resolveApiKey("ollama-cloud").key ?? process.env.OLLAMA_API_KEY;
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const headers: Record<string, string> = {};
|
|
37
|
-
if (cloudKey) headers.Authorization = `Bearer ${cloudKey}`;
|
|
38
|
-
|
|
39
|
-
ctx.agent.providers.configure(id, {
|
|
40
|
-
reasoningParams: (level) => {
|
|
41
|
-
if (level === "off") return { reasoning_effort: "none" };
|
|
42
|
-
return { reasoning_effort: level === "xhigh" ? "high" : level };
|
|
43
|
-
},
|
|
18
|
+
const cloudHost = "https://ollama.com";
|
|
19
|
+
const cloudBaseURL = `${cloudHost}/v1`;
|
|
20
|
+
ctx.agent.providers.configure("ollama-cloud", { reasoningParams });
|
|
21
|
+
ctx.agent.providers.register({
|
|
22
|
+
id: "ollama-cloud",
|
|
23
|
+
apiKey: cloudKey ?? undefined,
|
|
24
|
+
baseURL: cloudBaseURL,
|
|
25
|
+
models: [],
|
|
44
26
|
});
|
|
27
|
+
if (cloudKey) {
|
|
28
|
+
const headers = { Authorization: `Bearer ${cloudKey}` };
|
|
29
|
+
fetchCatalog(cloudHost, headers).then((models) => {
|
|
30
|
+
if (models.length === 0) return;
|
|
31
|
+
ctx.agent.providers.register({
|
|
32
|
+
id: "ollama-cloud",
|
|
33
|
+
apiKey: cloudKey,
|
|
34
|
+
baseURL: cloudBaseURL,
|
|
35
|
+
defaultModel: models[0]!.id,
|
|
36
|
+
models,
|
|
37
|
+
});
|
|
38
|
+
}).catch(() => {});
|
|
39
|
+
}
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
const localHost = (process.env.OLLAMA_HOST ?? "http://localhost:11434").replace(/\/$/, "");
|
|
42
|
+
const localBaseURL = `${localHost}/v1`;
|
|
43
|
+
ctx.agent.providers.configure("ollama", { reasoningParams });
|
|
44
|
+
// OpenAI SDK rejects an empty apiKey; the local daemon ignores it.
|
|
45
|
+
ctx.agent.providers.register({
|
|
46
|
+
id: "ollama",
|
|
47
|
+
apiKey: "no-key",
|
|
48
|
+
baseURL: localBaseURL,
|
|
49
|
+
models: [],
|
|
50
|
+
noAuth: true,
|
|
51
|
+
});
|
|
52
|
+
fetchCatalog(localHost, {}).then((models) => {
|
|
49
53
|
if (models.length === 0) return;
|
|
50
|
-
ctx.
|
|
51
|
-
id,
|
|
52
|
-
apiKey:
|
|
53
|
-
baseURL,
|
|
54
|
+
ctx.agent.providers.register({
|
|
55
|
+
id: "ollama",
|
|
56
|
+
apiKey: "no-key",
|
|
57
|
+
baseURL: localBaseURL,
|
|
54
58
|
defaultModel: models[0]!.id,
|
|
55
59
|
models,
|
|
60
|
+
noAuth: true,
|
|
56
61
|
});
|
|
57
|
-
}).catch(() => {
|
|
62
|
+
}).catch(() => {});
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
async function fetchCatalog(
|
|
@@ -40,6 +40,10 @@ opencode reads its own config from `~/.local/share/opencode/` (auth credentials)
|
|
|
40
40
|
- opencode authenticated locally — run `opencode auth login` once before using this bridge.
|
|
41
41
|
- Provider env vars (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) as required by opencode for the model you've selected.
|
|
42
42
|
|
|
43
|
+
## Environment
|
|
44
|
+
|
|
45
|
+
- `OPENCODE_SDK_PORT` — port for the in-process opencode HTTP server. Defaults to `0` (OS-assigned free port) so a stale standalone opencode on the SDK's default port 4096 can't collide with the bridge. Set explicitly only if you need a deterministic port.
|
|
46
|
+
|
|
43
47
|
## What works under opencode
|
|
44
48
|
|
|
45
49
|
agent-sh's per-query context producers (e.g. `<shell_events>` from `shell-context`) are inlined into opencode's prompt before each query, so opencode sees the user's recent shell activity even though the SDK doesn't subscribe to agent-sh's shell bus directly. The current cwd is part of that context, so opencode knows where the user is even when its tools are anchored elsewhere.
|
|
@@ -429,7 +429,9 @@ export default function activate(ctx: ExtensionContext): void {
|
|
|
429
429
|
start: async () => {
|
|
430
430
|
try {
|
|
431
431
|
serverAbort = new AbortController();
|
|
432
|
-
|
|
432
|
+
// port: 0 dodges collision with SDK default 4096 (override via OPENCODE_SDK_PORT).
|
|
433
|
+
const port = process.env.OPENCODE_SDK_PORT ? Number(process.env.OPENCODE_SDK_PORT) : 0;
|
|
434
|
+
runtime = await createOpencode({ signal: serverAbort.signal, port });
|
|
433
435
|
|
|
434
436
|
streamAbort = new AbortController();
|
|
435
437
|
// Subscribe before creating the session so we don't miss early events.
|
|
@@ -272,14 +272,13 @@ export default function activate(ctx: ExtensionContext): void {
|
|
|
272
272
|
}
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
booting = false;
|
|
276
|
+
const m = session.model;
|
|
276
277
|
bus.emit("agent:info", {
|
|
277
278
|
name: "pi",
|
|
278
279
|
version: "0.66",
|
|
279
|
-
model:
|
|
280
|
+
model: m ? `${m.provider}/${m.id}` : undefined,
|
|
280
281
|
});
|
|
281
|
-
|
|
282
|
-
booting = false;
|
|
283
282
|
} catch (err) {
|
|
284
283
|
booting = false;
|
|
285
284
|
bus.emit("ui:error", {
|
|
@@ -25,14 +25,10 @@ function buildReasoningParams(level: string, _model?: string): Record<string, un
|
|
|
25
25
|
|
|
26
26
|
export default function activate(ctx: AgentContext): void {
|
|
27
27
|
const { key } = resolveApiKey(ID);
|
|
28
|
-
const apiKey = key ?? process.env.ZAI_API_KEY ?? process.env.ZHIPU_API_KEY;
|
|
29
|
-
if (!apiKey) return;
|
|
30
|
-
|
|
31
28
|
ctx.agent.providers.configure(ID, { reasoningParams: buildReasoningParams });
|
|
32
|
-
|
|
33
|
-
ctx.bus.emit("provider:register", {
|
|
29
|
+
ctx.agent.providers.register({
|
|
34
30
|
id: ID,
|
|
35
|
-
apiKey:
|
|
31
|
+
apiKey: key ?? process.env.ZAI_API_KEY ?? process.env.ZHIPU_API_KEY,
|
|
36
32
|
baseURL: BASE_URL,
|
|
37
33
|
defaultModel: DEFAULT_MODELS[0].id,
|
|
38
34
|
models: DEFAULT_MODELS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "A shell-first terminal where AI is one keystroke away",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"dev": "tsx src/cli/index.ts",
|
|
122
122
|
"build": "tsc",
|
|
123
123
|
"start": "node dist/cli/index.js",
|
|
124
|
+
"test": "npm run build && node --import tsx --test $(find tests -name '*.test.ts' -type f)",
|
|
124
125
|
"prepare": "test -d dist || npm run build"
|
|
125
126
|
},
|
|
126
127
|
"keywords": [
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ctx.llm facade — delegates to an `llm:invoke` handler registered by the
|
|
3
|
-
* active backend. No handler → `available` is false and calls reject.
|
|
4
|
-
*/
|
|
5
|
-
import type { LlmInterface } from "../agent/host-types.js";
|
|
6
|
-
interface HandlerGate {
|
|
7
|
-
list: () => string[];
|
|
8
|
-
call: (name: string, ...args: unknown[]) => unknown;
|
|
9
|
-
}
|
|
10
|
-
export declare function createLlmFacade(handlers: HandlerGate): LlmInterface;
|
|
11
|
-
export {};
|
|
File without changes
|
|
File without changes
|