agent-sh 0.12.27 → 0.13.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 +13 -2
- package/dist/agent/agent-loop.d.ts +3 -5
- package/dist/agent/agent-loop.js +42 -98
- package/dist/agent/conversation-state.d.ts +9 -0
- package/dist/agent/conversation-state.js +16 -0
- package/dist/agent/history-file.d.ts +6 -0
- package/dist/agent/history-file.js +1 -1
- package/dist/agent/host-types.d.ts +125 -0
- package/dist/agent/index.d.ts +12 -4
- package/dist/agent/index.js +357 -6
- package/dist/agent/nuclear-form.d.ts +7 -0
- package/dist/{extensions → agent}/providers/deepseek.d.ts +2 -2
- package/dist/{extensions → agent}/providers/deepseek.js +5 -4
- package/dist/{extensions → agent}/providers/openai-compatible.d.ts +2 -2
- package/dist/{extensions → agent}/providers/openai.d.ts +2 -2
- package/dist/{extensions → agent}/providers/openai.js +3 -2
- package/dist/{extensions → agent}/providers/openrouter.d.ts +2 -2
- package/dist/{extensions → agent}/providers/openrouter.js +4 -3
- package/dist/agent/skills.js +51 -7
- package/dist/agent/subagent.d.ts +1 -1
- package/dist/agent/system-prompt.js +14 -17
- package/dist/agent/tool-protocol.d.ts +1 -1
- package/dist/agent/tool-protocol.js +5 -3
- package/dist/agent/tool-registry.d.ts +9 -4
- package/dist/agent/tool-registry.js +27 -4
- package/dist/agent/tools/bash.d.ts +1 -1
- package/dist/agent/tools/bash.js +3 -2
- package/dist/agent/tools/edit-file.js +0 -1
- package/dist/agent/tools/glob.js +1 -1
- package/dist/agent/tools/grep.js +1 -1
- package/dist/agent/tools/pwsh.d.ts +1 -1
- package/dist/agent/tools/pwsh.js +1 -2
- package/dist/agent/tools/read-file.js +7 -4
- package/dist/agent/tools/write-file.js +0 -1
- package/dist/agent/types.d.ts +17 -2
- package/dist/cli/auth/cli.d.ts +1 -0
- package/dist/cli/auth/cli.js +216 -0
- package/dist/cli/auth/keys.d.ts +31 -0
- package/dist/cli/auth/keys.js +102 -0
- package/dist/{index.js → cli/index.js} +29 -32
- package/dist/{init.js → cli/init.js} +1 -1
- package/dist/{install.js → cli/install.js} +31 -2
- package/dist/cli/subcommands.d.ts +1 -0
- package/dist/cli/subcommands.js +17 -0
- package/dist/{event-bus.d.ts → core/event-bus.d.ts} +7 -13
- package/dist/{extension-loader.d.ts → core/extension-loader.d.ts} +1 -1
- package/dist/{extension-loader.js → core/extension-loader.js} +62 -70
- package/dist/{core.d.ts → core/index.d.ts} +18 -15
- package/dist/{core.js → core/index.js} +18 -92
- package/dist/{settings.d.ts → core/settings.d.ts} +7 -0
- package/dist/{settings.js → core/settings.js} +1 -0
- package/dist/core/types.d.ts +49 -0
- package/dist/core/types.js +1 -0
- package/dist/extensions/file-autocomplete.d.ts +1 -1
- package/dist/extensions/index.d.ts +7 -14
- package/dist/extensions/index.js +2 -19
- package/dist/extensions/slash-commands.d.ts +1 -1
- package/dist/extensions/slash-commands.js +7 -2
- package/dist/shell/host-types.d.ts +114 -0
- package/dist/shell/host-types.js +1 -0
- package/dist/shell/index.d.ts +8 -7
- package/dist/shell/index.js +58 -9
- package/dist/shell/input-handler.d.ts +7 -1
- package/dist/shell/input-handler.js +5 -2
- package/dist/shell/output-parser.d.ts +1 -1
- package/dist/{extensions → shell}/shell-context.d.ts +1 -1
- package/dist/{extensions → shell}/shell-context.js +18 -12
- package/dist/shell/shell.d.ts +6 -4
- package/dist/shell/shell.js +33 -109
- package/dist/shell/strategies/bash.d.ts +2 -0
- package/dist/shell/strategies/bash.js +68 -0
- package/dist/shell/strategies/fish.d.ts +2 -0
- package/dist/shell/strategies/fish.js +65 -0
- package/dist/shell/strategies/index.d.ts +13 -0
- package/dist/shell/strategies/index.js +17 -0
- package/dist/shell/strategies/types.d.ts +50 -0
- package/dist/shell/strategies/types.js +9 -0
- package/dist/shell/strategies/zsh.d.ts +2 -0
- package/dist/shell/strategies/zsh.js +72 -0
- package/dist/shell/tui-input-view.js +14 -3
- package/dist/{extensions → shell}/tui-renderer.d.ts +1 -1
- package/dist/{extensions → shell}/tui-renderer.js +27 -55
- package/dist/utils/box-frame.d.ts +4 -0
- package/dist/utils/box-frame.js +17 -6
- package/dist/utils/compositor.d.ts +1 -1
- package/dist/utils/compositor.js +2 -1
- package/dist/{executor.js → utils/executor.js} +1 -1
- package/dist/utils/floating-panel.d.ts +1 -1
- package/dist/utils/floating-panel.js +9 -4
- package/dist/utils/llm-facade.d.ts +7 -3
- package/dist/utils/stream-transform.d.ts +1 -1
- package/dist/utils/terminal-buffer.d.ts +1 -1
- package/dist/utils/tool-display.js +4 -0
- package/dist/utils/tool-interactive.d.ts +1 -1
- package/dist/utils/tty.d.ts +7 -0
- package/dist/utils/tty.js +15 -0
- package/examples/extensions/ash-acp-bridge/README.md +4 -1
- package/examples/extensions/ash-acp-bridge/src/index.ts +654 -0
- package/examples/extensions/ash-mcp-bridge/index.ts +1 -1
- package/examples/extensions/ashi/README.md +250 -0
- package/examples/extensions/ashi/package.json +60 -0
- package/examples/extensions/ashi/src/autocomplete.ts +91 -0
- package/examples/extensions/ashi/src/capture.ts +34 -0
- package/examples/extensions/ashi/src/cli.ts +126 -0
- package/examples/extensions/ashi/src/commands.ts +82 -0
- package/examples/extensions/ashi/src/compaction.ts +157 -0
- package/examples/extensions/ashi/src/components.ts +332 -0
- package/examples/extensions/ashi/src/default-renderers.ts +153 -0
- package/examples/extensions/ashi/src/display-config.ts +62 -0
- package/examples/extensions/ashi/src/frontend.ts +735 -0
- package/examples/extensions/ashi/src/hooks.ts +136 -0
- package/examples/extensions/ashi/src/multi-session-store.ts +146 -0
- package/examples/extensions/ashi/src/session-commands.ts +76 -0
- package/examples/extensions/ashi/src/session-store.ts +264 -0
- package/examples/extensions/ashi/src/status-footer.ts +66 -0
- package/examples/extensions/ashi/src/theme.ts +151 -0
- package/examples/extensions/ashi/tsconfig.json +14 -0
- package/examples/extensions/emacs-buffer.ts +1 -1
- package/examples/extensions/interactive-prompts.ts +114 -69
- package/examples/extensions/latex-images.ts +3 -3
- package/examples/extensions/opencode-bridge/index.ts +1 -1
- package/examples/extensions/overlay-agent.ts +7 -5
- package/examples/extensions/peer-mesh.ts +1 -1
- package/examples/extensions/pi-bridge/index.ts +0 -1
- package/examples/extensions/questionnaire.ts +2 -1
- package/examples/extensions/rtk-proxy.ts +3 -3
- package/examples/extensions/solarized-theme.ts +3 -3
- package/examples/extensions/subagents.ts +6 -6
- package/examples/extensions/terminal-buffer.ts +1 -1
- package/examples/extensions/tmux-pane.ts +6 -4
- package/examples/extensions/tunnel-vision.ts +5 -5
- package/examples/extensions/user-shell.ts +1 -1
- package/examples/extensions/web-access.ts +5 -5
- package/package.json +26 -22
- package/dist/extensions/agent-backend.d.ts +0 -14
- package/dist/extensions/agent-backend.js +0 -307
- package/dist/types.d.ts +0 -227
- /package/dist/{types.js → agent/host-types.js} +0 -0
- /package/dist/{extensions → agent}/providers/openai-compatible.js +0 -0
- /package/dist/{index.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{init.d.ts → cli/init.d.ts} +0 -0
- /package/dist/{install.d.ts → cli/install.d.ts} +0 -0
- /package/dist/{event-bus.js → core/event-bus.js} +0 -0
- /package/dist/{executor.d.ts → utils/executor.d.ts} +0 -0
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* subscribing to bus events. Shell-specific tracking lives in the
|
|
7
7
|
* shell-context built-in extension.
|
|
8
8
|
*
|
|
9
|
-
* Agent backends
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
12
|
*
|
|
13
13
|
* Usage:
|
|
14
14
|
* import { createCore } from "agent-sh";
|
|
@@ -18,22 +18,18 @@
|
|
|
18
18
|
* const response = await core.query("hello");
|
|
19
19
|
*/
|
|
20
20
|
import { EventBus } from "./event-bus.js";
|
|
21
|
-
import { createLlmFacade } from "./utils/llm-facade.js";
|
|
22
|
-
import { setPalette } from "./utils/palette.js";
|
|
23
|
-
import * as streamTransform from "./utils/stream-transform.js";
|
|
24
21
|
import * as settingsMod from "./settings.js";
|
|
25
|
-
import { HandlerRegistry } from "
|
|
22
|
+
import { HandlerRegistry } from "../utils/handler-registry.js";
|
|
26
23
|
import crypto from "node:crypto";
|
|
27
24
|
import * as fs from "node:fs";
|
|
28
25
|
import * as path from "node:path";
|
|
29
|
-
import { DefaultCompositor } from "./utils/compositor.js";
|
|
30
26
|
import { CONFIG_DIR } from "./settings.js";
|
|
31
|
-
// Re-export types that library consumers need
|
|
32
27
|
export { EventBus } from "./event-bus.js";
|
|
33
|
-
export { palette, setPalette, resetPalette } from "
|
|
34
|
-
export { runSubagent } from "
|
|
35
|
-
export { LlmClient } from "
|
|
36
|
-
export { HistoryFile, InMemoryHistory, NoopHistory } from "
|
|
28
|
+
export { palette, setPalette, resetPalette } from "../utils/palette.js";
|
|
29
|
+
export { runSubagent } from "../agent/subagent.js";
|
|
30
|
+
export { LlmClient } from "../utils/llm-client.js";
|
|
31
|
+
export { HistoryFile, InMemoryHistory, NoopHistory } from "../agent/history-file.js";
|
|
32
|
+
export { compileSearchRegex, matchEntry, formatNuclearLine } from "../agent/nuclear-form.js";
|
|
37
33
|
export function createCore(config) {
|
|
38
34
|
const bus = new EventBus();
|
|
39
35
|
const handlers = new HandlerRegistry();
|
|
@@ -43,9 +39,7 @@ export function createCore(config) {
|
|
|
43
39
|
const instanceId = crypto.randomBytes(3).toString("hex");
|
|
44
40
|
bus.setSource(instanceId);
|
|
45
41
|
const settings = settingsMod.getSettings();
|
|
46
|
-
|
|
47
|
-
// providers and create the LLM client.
|
|
48
|
-
handlers.define("config:get-shell-config", () => config);
|
|
42
|
+
handlers.define("config:get-app-config", () => config);
|
|
49
43
|
// Default; shell-context advises with the PTY-tracked cwd when loaded.
|
|
50
44
|
handlers.define("cwd", () => process.cwd());
|
|
51
45
|
// Empty defaults so registerContextProducer can advise regardless of
|
|
@@ -93,11 +87,6 @@ export function createCore(config) {
|
|
|
93
87
|
const names = [...backends.keys()];
|
|
94
88
|
return { names, active: activeBackendName };
|
|
95
89
|
});
|
|
96
|
-
// ── Compositor ──────────────────────────────────────────────
|
|
97
|
-
// Generic surface-routing primitive. No defaults here — the active
|
|
98
|
-
// frontend (src/shell/, a web bridge, headless test harness, etc.)
|
|
99
|
-
// sets its own surfaces during activation.
|
|
100
|
-
const compositor = new DefaultCompositor(bus);
|
|
101
90
|
return {
|
|
102
91
|
bus,
|
|
103
92
|
handlers,
|
|
@@ -153,14 +142,12 @@ export function createCore(config) {
|
|
|
153
142
|
const ctx = {
|
|
154
143
|
bus,
|
|
155
144
|
instanceId,
|
|
156
|
-
llm: createLlmFacade(handlers),
|
|
157
|
-
providers: {
|
|
158
|
-
configure: (id, opts) => bus.emit("provider:configure", { id, ...opts }),
|
|
159
|
-
},
|
|
160
145
|
quit: opts.quit,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
146
|
+
define: (name, fn) => handlers.define(name, fn),
|
|
147
|
+
advise: (name, wrapper) => handlers.advise(name, wrapper),
|
|
148
|
+
call: (name, ...args) => handlers.call(name, ...args),
|
|
149
|
+
list: () => handlers.list(),
|
|
150
|
+
onDispose: () => { },
|
|
164
151
|
getExtensionSettings: settingsMod.getExtensionSettings,
|
|
165
152
|
getStoragePath: (namespace) => {
|
|
166
153
|
const dir = path.join(CONFIG_DIR, namespace);
|
|
@@ -168,70 +155,9 @@ export function createCore(config) {
|
|
|
168
155
|
return dir;
|
|
169
156
|
},
|
|
170
157
|
registerCommand: (name, description, handler) => bus.emit("command:register", { name, description, handler }),
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
registerInstruction: (name, text) => bus.emit("agent:register-instruction", { name, text, extensionName: "" }),
|
|
175
|
-
removeInstruction: (name) => bus.emit("agent:remove-instruction", { name }),
|
|
176
|
-
registerSkill: (name, description, filePath) => bus.emit("agent:register-skill", { name, description, filePath, extensionName: "" }),
|
|
177
|
-
removeSkill: (name) => bus.emit("agent:remove-skill", { name }),
|
|
178
|
-
registerContextProducer: (_name, producer, opts) => {
|
|
179
|
-
const handlerName = opts?.mode === "per-query"
|
|
180
|
-
? "query-context:build"
|
|
181
|
-
: "dynamic-context:build";
|
|
182
|
-
return handlers.advise(handlerName, (next) => {
|
|
183
|
-
const base = next();
|
|
184
|
-
const part = producer();
|
|
185
|
-
if (!part)
|
|
186
|
-
return base;
|
|
187
|
-
const trimmed = part.trim();
|
|
188
|
-
if (!trimmed)
|
|
189
|
-
return base;
|
|
190
|
-
return base ? `${base}\n\n${trimmed}` : trimmed;
|
|
191
|
-
});
|
|
192
|
-
},
|
|
193
|
-
define: (name, fn) => handlers.define(name, fn),
|
|
194
|
-
advise: (name, wrapper) => handlers.advise(name, wrapper),
|
|
195
|
-
call: (name, ...args) => handlers.call(name, ...args),
|
|
196
|
-
list: () => handlers.list(),
|
|
197
|
-
compositor,
|
|
198
|
-
onDispose: () => { },
|
|
199
|
-
createRemoteSession: (opts) => {
|
|
200
|
-
const { surface } = opts;
|
|
201
|
-
const cleanups = [];
|
|
202
|
-
let active = true;
|
|
203
|
-
// Redirect all render streams
|
|
204
|
-
cleanups.push(compositor.redirect("agent", surface));
|
|
205
|
-
cleanups.push(compositor.redirect("query", surface));
|
|
206
|
-
cleanups.push(compositor.redirect("status", surface));
|
|
207
|
-
// Suppress the host shell's mute lifecycle and post-turn
|
|
208
|
-
// redraw nudge. on-processing-done is intentionally not advised
|
|
209
|
-
// — its scope cleanup must always run.
|
|
210
|
-
cleanups.push(handlers.advise("shell:on-processing-start", (next) => active ? undefined : next()));
|
|
211
|
-
cleanups.push(handlers.advise("shell:on-processing-redraw", (next) => active ? undefined : next()));
|
|
212
|
-
// Suppress chrome
|
|
213
|
-
if (opts.suppressBorders !== false) {
|
|
214
|
-
cleanups.push(handlers.advise("tui:response-border", (next, ...a) => active ? null : next(...a)));
|
|
215
|
-
}
|
|
216
|
-
if (opts.suppressQueryBox) {
|
|
217
|
-
cleanups.push(handlers.advise("tui:render-user-query", (next, ...a) => active ? [] : next(...a)));
|
|
218
|
-
}
|
|
219
|
-
if (opts.suppressUsage !== false) {
|
|
220
|
-
cleanups.push(handlers.advise("tui:render-usage", (next, ...a) => active ? "" : next(...a)));
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
submit(query) { bus.emit("agent:submit", { query }); },
|
|
224
|
-
get surface() { return surface; },
|
|
225
|
-
get active() { return active; },
|
|
226
|
-
close() {
|
|
227
|
-
if (!active)
|
|
228
|
-
return;
|
|
229
|
-
active = false;
|
|
230
|
-
for (const fn of cleanups.reverse())
|
|
231
|
-
fn();
|
|
232
|
-
cleanups.length = 0;
|
|
233
|
-
},
|
|
234
|
-
};
|
|
158
|
+
adviseCommand: (name, advisor) => {
|
|
159
|
+
const key = name.startsWith("/") ? name : `/${name}`;
|
|
160
|
+
return handlers.advise(`command:${key}`, advisor);
|
|
235
161
|
},
|
|
236
162
|
};
|
|
237
163
|
return ctx;
|
|
@@ -78,6 +78,13 @@ export interface Settings {
|
|
|
78
78
|
* "inline" — tools described as text.
|
|
79
79
|
*/
|
|
80
80
|
toolMode?: "api" | "deferred" | "deferred-lookup" | "inline";
|
|
81
|
+
/**
|
|
82
|
+
* Extra tool names treated as "core" in deferred / deferred-lookup mode —
|
|
83
|
+
* always sent with full schema instead of requiring an explicit load_tool
|
|
84
|
+
* call. Useful when an extension registers a substrate tool that should
|
|
85
|
+
* have first-class footing alongside the kernel built-ins.
|
|
86
|
+
*/
|
|
87
|
+
coreTools?: string[];
|
|
81
88
|
/** Additional directories to scan for skills (supports ~ expansion). */
|
|
82
89
|
skillPaths?: string[];
|
|
83
90
|
/**
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EventBus } from "./event-bus.js";
|
|
2
|
+
export type { ContentBlock } from "./event-bus.js";
|
|
3
|
+
/**
|
|
4
|
+
* The substrate context — what every backend and every host always
|
|
5
|
+
* provides. Bus, handler registry, lifecycle, and per-instance storage.
|
|
6
|
+
*
|
|
7
|
+
* Hosts (agent, shell, web bridge, …) extend this with their own
|
|
8
|
+
* surfaces — see src/agent/host-types.ts and src/shell/host-types.ts.
|
|
9
|
+
* Extensions that only need the substrate should type their ctx as
|
|
10
|
+
* `CoreContext`; those that need host facilities should type as
|
|
11
|
+
* `AgentContext` or `ShellContext` to make their host dependency
|
|
12
|
+
* explicit (and catch misuse under bridge backends at the type level).
|
|
13
|
+
*/
|
|
14
|
+
export interface CoreContext {
|
|
15
|
+
bus: EventBus;
|
|
16
|
+
/** Stable per-instance identifier (4-char hex). */
|
|
17
|
+
readonly instanceId: string;
|
|
18
|
+
quit: () => void;
|
|
19
|
+
/** Read extension-namespaced settings from ~/.agent-sh/settings.json. */
|
|
20
|
+
getExtensionSettings: <T extends Record<string, unknown>>(namespace: string, defaults: T) => T;
|
|
21
|
+
/**
|
|
22
|
+
* Get (and lazily create) a per-extension storage directory under
|
|
23
|
+
* ~/.agent-sh/<namespace>/. Returns the absolute path. Lets extensions
|
|
24
|
+
* persist state without each one re-deriving the location.
|
|
25
|
+
*/
|
|
26
|
+
getStoragePath: (namespace: string) => string;
|
|
27
|
+
/** Register a named handler. */
|
|
28
|
+
define: (name: string, fn: (...args: any[]) => any) => void;
|
|
29
|
+
/** Wrap a named handler. Receives `next` (original) + args. Returns an unadvise function. */
|
|
30
|
+
advise: (name: string, wrapper: (next: (...args: any[]) => any, ...args: any[]) => any) => () => void;
|
|
31
|
+
/** Call a named handler. */
|
|
32
|
+
call: (name: string, ...args: any[]) => any;
|
|
33
|
+
/** Names of all registered handlers — for diagnostic / introspection use. */
|
|
34
|
+
list: () => string[];
|
|
35
|
+
/** Teardown callback fired on /reload. For resources the scoped context
|
|
36
|
+
* can't track: process listeners, timers, watchers, sockets. */
|
|
37
|
+
onDispose: (fn: () => void) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The substrate config — kernel-level options. Hosts extend with their
|
|
41
|
+
* own surfaces (see AgentConfig in src/agent/host-types.ts and
|
|
42
|
+
* ShellConfig in src/shell/host-types.ts).
|
|
43
|
+
*/
|
|
44
|
+
export interface CoreConfig {
|
|
45
|
+
/** Extension specifiers (paths or package names) to load on startup. */
|
|
46
|
+
extensions?: string[];
|
|
47
|
+
/** Override settings.defaultBackend for this session only (does not persist). */
|
|
48
|
+
backend?: string;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ExtensionContext } from "../types.js";
|
|
1
|
+
import type { ExtensionContext } from "../shell/host-types.js";
|
|
2
2
|
export default function activate(ctx: ExtensionContext): void;
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* disabled via the `disabledBuiltins` setting in ~/.agent-sh/settings.json.
|
|
7
|
-
*
|
|
8
|
-
* For order-critical frontend bootstrap (the PTY shell), see `src/shell/`.
|
|
9
|
-
* That module exposes its own `activate(ctx, opts)` entry point, loaded
|
|
10
|
-
* specially from `src/index.ts` rather than through this manifest.
|
|
2
|
+
* Cross-cutting built-ins, toggleable via `disabledBuiltins`.
|
|
3
|
+
* Module-owned built-ins activate inline:
|
|
4
|
+
* shell-context, tui-renderer → registerShellHandlers (src/shell/)
|
|
5
|
+
* agent-backend, providers → activateAgent (src/agent/)
|
|
11
6
|
*/
|
|
12
|
-
import type { ExtensionContext } from "../types.js";
|
|
7
|
+
import type { ExtensionContext } from "../shell/host-types.js";
|
|
13
8
|
type ActivateFn = (ctx: ExtensionContext) => void;
|
|
14
9
|
export declare const BUILTIN_EXTENSIONS: Array<{
|
|
15
10
|
name: string;
|
|
16
|
-
when?: () => boolean;
|
|
17
11
|
load: () => Promise<ActivateFn>;
|
|
18
12
|
}>;
|
|
19
13
|
/**
|
|
20
|
-
* Load built-in extensions sequentially, skipping any in the disabled list
|
|
21
|
-
*
|
|
22
|
-
* that were loaded.
|
|
14
|
+
* Load built-in extensions sequentially, skipping any in the disabled list.
|
|
15
|
+
* Returns the names of extensions that were loaded.
|
|
23
16
|
*/
|
|
24
17
|
export declare function loadBuiltinExtensions(ctx: ExtensionContext, disabled?: string[]): Promise<string[]>;
|
|
25
18
|
export {};
|
package/dist/extensions/index.js
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
export const BUILTIN_EXTENSIONS = [
|
|
2
|
-
{ name: "shell-context", load: () => import("./shell-context.js").then(m => m.default) },
|
|
3
|
-
{ name: "agent-backend", load: () => import("./agent-backend.js").then(m => m.default) },
|
|
4
|
-
{ name: "openrouter",
|
|
5
|
-
when: () => !!process.env.OPENROUTER_API_KEY,
|
|
6
|
-
load: () => import("./providers/openrouter.js").then(m => m.default) },
|
|
7
|
-
{ name: "openai",
|
|
8
|
-
when: () => !!process.env.OPENAI_API_KEY && !process.env.OPENAI_BASE_URL,
|
|
9
|
-
load: () => import("./providers/openai.js").then(m => m.default) },
|
|
10
|
-
{ name: "openai-compatible",
|
|
11
|
-
when: () => !!process.env.OPENAI_BASE_URL,
|
|
12
|
-
load: () => import("./providers/openai-compatible.js").then(m => m.default) },
|
|
13
|
-
{ name: "deepseek",
|
|
14
|
-
load: () => import("./providers/deepseek.js").then(m => m.default) },
|
|
15
|
-
{ name: "tui-renderer", load: () => import("./tui-renderer.js").then(m => m.default) },
|
|
16
2
|
{ name: "slash-commands", load: () => import("./slash-commands.js").then(m => m.default) },
|
|
17
3
|
{ name: "file-autocomplete", load: () => import("./file-autocomplete.js").then(m => m.default) },
|
|
18
4
|
];
|
|
19
5
|
/**
|
|
20
|
-
* Load built-in extensions sequentially, skipping any in the disabled list
|
|
21
|
-
*
|
|
22
|
-
* that were loaded.
|
|
6
|
+
* Load built-in extensions sequentially, skipping any in the disabled list.
|
|
7
|
+
* Returns the names of extensions that were loaded.
|
|
23
8
|
*/
|
|
24
9
|
export async function loadBuiltinExtensions(ctx, disabled = []) {
|
|
25
10
|
const disabledSet = new Set(disabled);
|
|
@@ -27,8 +12,6 @@ export async function loadBuiltinExtensions(ctx, disabled = []) {
|
|
|
27
12
|
for (const ext of BUILTIN_EXTENSIONS) {
|
|
28
13
|
if (disabledSet.has(ext.name))
|
|
29
14
|
continue;
|
|
30
|
-
if (ext.when && !ext.when())
|
|
31
|
-
continue;
|
|
32
15
|
const activate = await ext.load();
|
|
33
16
|
activate(ctx);
|
|
34
17
|
loaded.push(ext.name);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ExtensionContext } from "../types.js";
|
|
1
|
+
import type { ExtensionContext } from "../shell/host-types.js";
|
|
2
2
|
export default function activate(ctx: ExtensionContext): void;
|
|
@@ -12,13 +12,17 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { palette as p } from "../utils/palette.js";
|
|
14
14
|
import { discoverSkills, loadSkillContent } from "../agent/skills.js";
|
|
15
|
-
import { reloadExtensions } from "../extension-loader.js";
|
|
15
|
+
import { reloadExtensions } from "../core/extension-loader.js";
|
|
16
16
|
export default function activate(ctx) {
|
|
17
17
|
const { bus } = ctx;
|
|
18
18
|
const commands = new Map();
|
|
19
19
|
const register = (cmd) => {
|
|
20
20
|
const name = cmd.name.startsWith("/") ? cmd.name : `/${cmd.name}`;
|
|
21
|
+
if (commands.has(name)) {
|
|
22
|
+
throw new Error(`Command "${name}" already registered. Use ctx.adviseCommand() to wrap it.`);
|
|
23
|
+
}
|
|
21
24
|
commands.set(name, { ...cmd, name });
|
|
25
|
+
ctx.define(`command:${name}`, cmd.handler);
|
|
22
26
|
};
|
|
23
27
|
// ── Built-in commands ─────────────────────────────────────────
|
|
24
28
|
register({
|
|
@@ -101,6 +105,7 @@ export default function activate(ctx) {
|
|
|
101
105
|
bus.on("command:unregister", ({ name }) => {
|
|
102
106
|
const key = name.startsWith("/") ? name : `/${name}`;
|
|
103
107
|
commands.delete(key);
|
|
108
|
+
// Handler entry retained so external advisors survive a reload of the owner.
|
|
104
109
|
});
|
|
105
110
|
// ── Skill commands (/skill:<name>) ────────────────────────────
|
|
106
111
|
const getSkills = () => {
|
|
@@ -214,7 +219,7 @@ export default function activate(ctx) {
|
|
|
214
219
|
}
|
|
215
220
|
const cmd = commands.get(e.name);
|
|
216
221
|
if (cmd) {
|
|
217
|
-
const result =
|
|
222
|
+
const result = ctx.call(`command:${e.name}`, e.args);
|
|
218
223
|
if (result instanceof Promise) {
|
|
219
224
|
result.catch((err) => {
|
|
220
225
|
bus.emit("ui:error", {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { EventBus } from "../core/event-bus.js";
|
|
2
|
+
import type { CoreConfig, CoreContext } from "../core/types.js";
|
|
3
|
+
import type { AgentSurface } from "../agent/host-types.js";
|
|
4
|
+
import type { ColorPalette } from "../utils/palette.js";
|
|
5
|
+
import type { Compositor, RenderSurface } from "../utils/compositor.js";
|
|
6
|
+
import type { BlockTransformOptions, FencedBlockTransformOptions } from "../utils/stream-transform.js";
|
|
7
|
+
export type { BlockTransformOptions, FencedBlockTransformOptions } from "../utils/stream-transform.js";
|
|
8
|
+
export type { RenderSurface } from "../utils/compositor.js";
|
|
9
|
+
export interface RemoteSessionOptions {
|
|
10
|
+
/** The surface to render agent output to. */
|
|
11
|
+
surface: RenderSurface;
|
|
12
|
+
/** Suppress response borders (default: true). */
|
|
13
|
+
suppressBorders?: boolean;
|
|
14
|
+
/** Suppress user query box (default: false).
|
|
15
|
+
* True for sessions with their own input (rsplit, overlay).
|
|
16
|
+
* False for sessions where input comes from the main shell (split). */
|
|
17
|
+
suppressQueryBox?: boolean;
|
|
18
|
+
/** Suppress usage stats line (default: true). */
|
|
19
|
+
suppressUsage?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface RemoteSession {
|
|
22
|
+
/** Submit a query to the agent from this session. */
|
|
23
|
+
submit(query: string): void;
|
|
24
|
+
/** The surface this session renders to. */
|
|
25
|
+
readonly surface: RenderSurface;
|
|
26
|
+
/** Whether this session is currently active. */
|
|
27
|
+
readonly active: boolean;
|
|
28
|
+
/** Tear down — restores all routing and advisors. */
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for a registered input mode.
|
|
33
|
+
* Extensions emit "input-mode:register" with this shape to add new modes.
|
|
34
|
+
*/
|
|
35
|
+
export interface InputModeConfig {
|
|
36
|
+
id: string;
|
|
37
|
+
trigger: string;
|
|
38
|
+
label: string;
|
|
39
|
+
promptIcon: string;
|
|
40
|
+
indicator: string;
|
|
41
|
+
onSubmit(query: string, bus: EventBus): void;
|
|
42
|
+
returnToSelf: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface TerminalSession {
|
|
45
|
+
id: string;
|
|
46
|
+
command: string;
|
|
47
|
+
output: string;
|
|
48
|
+
exitCode: number | null;
|
|
49
|
+
done: boolean;
|
|
50
|
+
resolve?: (value: void) => void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Capabilities the shell host adds to the extension context, exposed
|
|
54
|
+
* on the nested `ctx.shell` field. Available only when the TUI shell
|
|
55
|
+
* frontend is loaded; under headless backends these methods are silent
|
|
56
|
+
* no-ops (bus emits with no listeners).
|
|
57
|
+
*/
|
|
58
|
+
export interface ShellSurface {
|
|
59
|
+
/** Routes named render streams ("agent", "query", "status", or any
|
|
60
|
+
* extension-defined name) to terminal surfaces. Frontends register
|
|
61
|
+
* default surfaces during activation; extensions can `redirect()`
|
|
62
|
+
* to capture output. Shell-scoped because today only the TUI uses
|
|
63
|
+
* it — bus events are the wire for other frontends. */
|
|
64
|
+
compositor: Compositor;
|
|
65
|
+
/** Override color palette slots for theming. */
|
|
66
|
+
setPalette: (overrides: Partial<ColorPalette>) => void;
|
|
67
|
+
/** Register a delimiter-based content transform (e.g. $$...$$ → image). */
|
|
68
|
+
createBlockTransform: (opts: BlockTransformOptions) => void;
|
|
69
|
+
/** Register a fenced block transform (e.g. ```lang...``` → code-block). */
|
|
70
|
+
createFencedBlockTransform: (opts: FencedBlockTransformOptions) => void;
|
|
71
|
+
/** Wrap an input mode's `onSubmit`. Lets extensions transform queries
|
|
72
|
+
* on the way to the agent (logging, redaction, vetoing). The mode
|
|
73
|
+
* must already be registered via the `input-mode:register` bus event. */
|
|
74
|
+
adviseInputMode: (id: string, advisor: (next: (query: string, bus: EventBus) => void, query: string, bus: EventBus) => void) => () => void;
|
|
75
|
+
/** Create a remote session that routes agent output to a surface and
|
|
76
|
+
* optionally accepts queries. Handles compositor routing, shell
|
|
77
|
+
* lifecycle advisors, and chrome suppression. */
|
|
78
|
+
createRemoteSession: (opts: RemoteSessionOptions) => RemoteSession;
|
|
79
|
+
}
|
|
80
|
+
/** Substrate + shell surface. Use this when an extension only touches
|
|
81
|
+
* shell features (themes, palette, transforms) and doesn't need the
|
|
82
|
+
* agent surface. */
|
|
83
|
+
export type ShellContext = CoreContext & {
|
|
84
|
+
shell: ShellSurface;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* What extension `activate()` functions receive. Substrate (`CoreContext`)
|
|
88
|
+
* + slash-command registration + host surfaces, which are **optional**
|
|
89
|
+
* because hosts attach them on activation: under headless backends
|
|
90
|
+
* `ctx.shell` is undefined; under bridge backends `ctx.agent` may be
|
|
91
|
+
* undefined too. Extensions guard with `ctx.shell?.foo()` /
|
|
92
|
+
* `if (!ctx.agent) return;`, or type their parameter as the narrower
|
|
93
|
+
* `AgentContext` / `ShellContext` to declare host requirements (those
|
|
94
|
+
* variants make the surface non-optional). When both hosts are required,
|
|
95
|
+
* intersect them at the use site: `ctx: AgentContext & ShellContext`.
|
|
96
|
+
*/
|
|
97
|
+
export type ExtensionContext = CoreContext & {
|
|
98
|
+
registerCommand: (name: string, description: string, handler: (args: string) => Promise<void> | void) => void;
|
|
99
|
+
/** Wrap an already-registered command's handler. Name is normalized
|
|
100
|
+
* (leading `/` optional). */
|
|
101
|
+
adviseCommand: (name: string, advisor: (next: (args: string) => Promise<void> | void, args: string) => Promise<void> | void) => () => void;
|
|
102
|
+
agent?: AgentSurface;
|
|
103
|
+
shell?: ShellSurface;
|
|
104
|
+
};
|
|
105
|
+
export interface ShellConfigSurface {
|
|
106
|
+
/** Shell binary (e.g. /bin/zsh) launched by the PTY frontend. */
|
|
107
|
+
shell?: string;
|
|
108
|
+
}
|
|
109
|
+
export type ShellConfig = CoreConfig & ShellConfigSurface;
|
|
110
|
+
/** The full application config — substrate + agent + shell startup options.
|
|
111
|
+
* Prefer this in CLI/embedder code; layered names (`CoreConfig`,
|
|
112
|
+
* `AgentConfig`, `ShellConfig`) are for code that cares about which
|
|
113
|
+
* host owns which fields. */
|
|
114
|
+
export type AppConfig = CoreConfig & import("../agent/host-types.js").AgentConfigSurface & ShellConfigSurface;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Frontend bootstrap. Loaded directly from src/index.ts (not the
|
|
3
|
-
* extensions manifest) because PTY + stdin raw mode ownership is
|
|
4
|
-
* critical.
|
|
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
5
|
*/
|
|
6
|
-
import type { ExtensionContext } from "
|
|
6
|
+
import type { ExtensionContext } from "./host-types.js";
|
|
7
7
|
export interface ShellActivateOptions {
|
|
8
8
|
cols: number;
|
|
9
9
|
rows: number;
|
|
@@ -28,13 +28,14 @@ export interface ShellHandle {
|
|
|
28
28
|
resize(cols: number, rows: number): void;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Register shell-owned handlers extensions can `ctx.call
|
|
32
|
-
*
|
|
31
|
+
* Register shell-owned handlers extensions can `ctx.call`, and attach
|
|
32
|
+
* the shell surface to ctx. Must run before `loadExtensions` so user
|
|
33
|
+
* extensions see `ctx.shell` populated.
|
|
33
34
|
*/
|
|
34
35
|
export declare function registerShellHandlers(ctx: ExtensionContext): void;
|
|
35
36
|
/**
|
|
36
37
|
* Construct the Shell, wire resize forwarding, and register cleanup with the
|
|
37
38
|
* provided ExtensionContext. Returns a handle the caller (typically
|
|
38
|
-
* `src/index.ts`) uses to drive lifecycle from process-level events.
|
|
39
|
+
* `src/cli/index.ts`) uses to drive lifecycle from process-level events.
|
|
39
40
|
*/
|
|
40
41
|
export declare function activateShell(ctx: ExtensionContext, opts: ShellActivateOptions): ShellHandle;
|
package/dist/shell/index.js
CHANGED
|
@@ -1,11 +1,60 @@
|
|
|
1
1
|
import { Shell } from "./shell.js";
|
|
2
|
-
import { StdoutSurface } from "../utils/compositor.js";
|
|
2
|
+
import { DefaultCompositor, StdoutSurface } from "../utils/compositor.js";
|
|
3
3
|
import { TerminalBuffer } from "../utils/terminal-buffer.js";
|
|
4
|
+
import { setPalette } from "../utils/palette.js";
|
|
5
|
+
import * as streamTransform from "../utils/stream-transform.js";
|
|
6
|
+
import activateShellContext from "./shell-context.js";
|
|
7
|
+
import activateTuiRenderer from "./tui-renderer.js";
|
|
4
8
|
/**
|
|
5
|
-
* Register shell-owned handlers extensions can `ctx.call
|
|
6
|
-
*
|
|
9
|
+
* Register shell-owned handlers extensions can `ctx.call`, and attach
|
|
10
|
+
* the shell surface to ctx. Must run before `loadExtensions` so user
|
|
11
|
+
* extensions see `ctx.shell` populated.
|
|
7
12
|
*/
|
|
8
13
|
export function registerShellHandlers(ctx) {
|
|
14
|
+
const { bus } = ctx;
|
|
15
|
+
const compositor = new DefaultCompositor(bus);
|
|
16
|
+
const shellSurface = {
|
|
17
|
+
compositor,
|
|
18
|
+
setPalette,
|
|
19
|
+
createBlockTransform: (o) => streamTransform.createBlockTransform(bus, o),
|
|
20
|
+
createFencedBlockTransform: (o) => streamTransform.createFencedBlockTransform(bus, o),
|
|
21
|
+
adviseInputMode: (id, advisor) => ctx.advise(`input-mode:${id}:submit`, advisor),
|
|
22
|
+
createRemoteSession: (sessOpts) => {
|
|
23
|
+
const { surface } = sessOpts;
|
|
24
|
+
const cleanups = [];
|
|
25
|
+
let active = true;
|
|
26
|
+
cleanups.push(compositor.redirect("agent", surface));
|
|
27
|
+
cleanups.push(compositor.redirect("query", surface));
|
|
28
|
+
cleanups.push(compositor.redirect("status", surface));
|
|
29
|
+
// on-processing-done is intentionally not advised — its scope
|
|
30
|
+
// cleanup must always run.
|
|
31
|
+
cleanups.push(ctx.advise("shell:on-processing-start", (next) => active ? undefined : next()));
|
|
32
|
+
cleanups.push(ctx.advise("shell:on-processing-redraw", (next) => active ? undefined : next()));
|
|
33
|
+
if (sessOpts.suppressBorders !== false) {
|
|
34
|
+
cleanups.push(ctx.advise("tui:response-border", (next, ...a) => active ? null : next(...a)));
|
|
35
|
+
}
|
|
36
|
+
if (sessOpts.suppressQueryBox) {
|
|
37
|
+
cleanups.push(ctx.advise("tui:render-user-query", (next, ...a) => active ? [] : next(...a)));
|
|
38
|
+
}
|
|
39
|
+
if (sessOpts.suppressUsage !== false) {
|
|
40
|
+
cleanups.push(ctx.advise("tui:render-usage", (next, ...a) => active ? "" : next(...a)));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
submit(query) { bus.emit("agent:submit", { query }); },
|
|
44
|
+
get surface() { return surface; },
|
|
45
|
+
get active() { return active; },
|
|
46
|
+
close() {
|
|
47
|
+
if (!active)
|
|
48
|
+
return;
|
|
49
|
+
active = false;
|
|
50
|
+
for (const fn of cleanups.reverse())
|
|
51
|
+
fn();
|
|
52
|
+
cleanups.length = 0;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
ctx.shell = shellSurface;
|
|
9
58
|
let terminalBufferSingleton;
|
|
10
59
|
ctx.define("terminal-buffer", () => {
|
|
11
60
|
if (terminalBufferSingleton !== undefined)
|
|
@@ -13,19 +62,19 @@ export function registerShellHandlers(ctx) {
|
|
|
13
62
|
terminalBufferSingleton = TerminalBuffer.createWired(ctx.bus);
|
|
14
63
|
return terminalBufferSingleton;
|
|
15
64
|
});
|
|
65
|
+
activateShellContext(ctx);
|
|
66
|
+
activateTuiRenderer(ctx);
|
|
16
67
|
}
|
|
17
68
|
/**
|
|
18
69
|
* Construct the Shell, wire resize forwarding, and register cleanup with the
|
|
19
70
|
* provided ExtensionContext. Returns a handle the caller (typically
|
|
20
|
-
* `src/index.ts`) uses to drive lifecycle from process-level events.
|
|
71
|
+
* `src/cli/index.ts`) uses to drive lifecycle from process-level events.
|
|
21
72
|
*/
|
|
22
73
|
export function activateShell(ctx, opts) {
|
|
23
|
-
// Stdout-as-default is a frontend choice, not a kernel one — a hub or
|
|
24
|
-
// web bridge would point these at its own surfaces.
|
|
25
74
|
const stdoutSurface = new StdoutSurface();
|
|
26
|
-
ctx.compositor.setDefault("agent", stdoutSurface);
|
|
27
|
-
ctx.compositor.setDefault("query", stdoutSurface);
|
|
28
|
-
ctx.compositor.setDefault("status", stdoutSurface);
|
|
75
|
+
ctx.shell.compositor.setDefault("agent", stdoutSurface);
|
|
76
|
+
ctx.shell.compositor.setDefault("query", stdoutSurface);
|
|
77
|
+
ctx.shell.compositor.setDefault("status", stdoutSurface);
|
|
29
78
|
const shell = new Shell({
|
|
30
79
|
bus: ctx.bus,
|
|
31
80
|
handlers: { define: ctx.define, call: ctx.call },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EventBus } from "../event-bus.js";
|
|
1
|
+
import type { EventBus } from "../core/event-bus.js";
|
|
2
2
|
import { TuiInputView } from "./tui-input-view.js";
|
|
3
3
|
/** Narrow contract between InputHandler and its host (Shell). */
|
|
4
4
|
export interface InputContext {
|
|
@@ -10,6 +10,10 @@ export interface InputContext {
|
|
|
10
10
|
redrawPrompt(): void;
|
|
11
11
|
freshPrompt(): void;
|
|
12
12
|
}
|
|
13
|
+
export interface InputHandlers {
|
|
14
|
+
define: (name: string, fn: (...a: any[]) => any) => void;
|
|
15
|
+
call: (name: string, ...a: any[]) => any;
|
|
16
|
+
}
|
|
13
17
|
/** Line editor + shell-passthrough buffer. Delegates rendering to TuiInputView. */
|
|
14
18
|
export declare class InputHandler {
|
|
15
19
|
private ctx;
|
|
@@ -27,11 +31,13 @@ export declare class InputHandler {
|
|
|
27
31
|
private savedBuffer;
|
|
28
32
|
private escapeTimer;
|
|
29
33
|
private bus;
|
|
34
|
+
private handlers;
|
|
30
35
|
private onShowAgentInfo;
|
|
31
36
|
private view;
|
|
32
37
|
constructor(opts: {
|
|
33
38
|
ctx: InputContext;
|
|
34
39
|
bus: EventBus;
|
|
40
|
+
handlers: InputHandlers;
|
|
35
41
|
onShowAgentInfo: () => {
|
|
36
42
|
info: string;
|
|
37
43
|
model?: string;
|