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.
Files changed (57) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/agent-loop.d.ts +13 -17
  3. package/dist/agent/agent-loop.js +118 -224
  4. package/dist/agent/conversation-state.d.ts +1 -1
  5. package/dist/agent/events.d.ts +218 -0
  6. package/dist/agent/events.js +1 -0
  7. package/dist/agent/host-types.d.ts +20 -0
  8. package/dist/agent/index.d.ts +5 -9
  9. package/dist/agent/index.js +269 -167
  10. package/dist/agent/llm-facade.d.ts +13 -0
  11. package/dist/{utils → agent}/llm-facade.js +1 -1
  12. package/dist/agent/nuclear-form.d.ts +1 -1
  13. package/dist/agent/providers/deepseek.js +2 -5
  14. package/dist/agent/providers/openai-compatible.js +2 -2
  15. package/dist/agent/providers/openai.js +2 -5
  16. package/dist/agent/providers/openrouter.js +5 -5
  17. package/dist/agent/subagent.d.ts +1 -1
  18. package/dist/agent/tool-protocol.d.ts +1 -1
  19. package/dist/agent/tool-registry.d.ts +1 -1
  20. package/dist/cli/args.d.ts +2 -0
  21. package/dist/cli/args.js +90 -0
  22. package/dist/cli/auth/cli.js +11 -6
  23. package/dist/cli/auth/discover.d.ts +5 -0
  24. package/dist/cli/auth/discover.js +25 -0
  25. package/dist/cli/auth/keys.d.ts +5 -2
  26. package/dist/cli/auth/keys.js +22 -2
  27. package/dist/cli/index.d.ts +16 -0
  28. package/dist/cli/index.js +15 -156
  29. package/dist/cli/shell-env.d.ts +2 -0
  30. package/dist/cli/shell-env.js +61 -0
  31. package/dist/core/event-bus.d.ts +28 -371
  32. package/dist/core/extension-loader.js +6 -6
  33. package/dist/core/index.d.ts +10 -29
  34. package/dist/core/index.js +31 -82
  35. package/dist/extensions/index.d.ts +2 -1
  36. package/dist/extensions/index.js +1 -1
  37. package/dist/extensions/slash-commands/events.d.ts +18 -0
  38. package/dist/extensions/slash-commands/events.js +1 -0
  39. package/dist/extensions/slash-commands/index.d.ts +15 -0
  40. package/dist/extensions/{slash-commands.js → slash-commands/index.js} +4 -3
  41. package/dist/shell/events.d.ts +85 -0
  42. package/dist/shell/events.js +1 -0
  43. package/dist/shell/index.d.ts +1 -0
  44. package/dist/shell/index.js +6 -0
  45. package/dist/shell/tui-renderer.js +0 -1
  46. package/examples/extensions/ash-acp-bridge/src/index.ts +2 -2
  47. package/examples/extensions/ashi/package.json +1 -1
  48. package/examples/extensions/ollama.ts +47 -42
  49. package/examples/extensions/opencode-bridge/README.md +4 -0
  50. package/examples/extensions/opencode-bridge/index.ts +3 -1
  51. package/examples/extensions/pi-bridge/index.ts +3 -4
  52. package/examples/extensions/zai-coding-plan.ts +2 -6
  53. package/package.json +2 -1
  54. package/dist/extensions/slash-commands.d.ts +0 -2
  55. package/dist/utils/llm-facade.d.ts +0 -11
  56. /package/dist/{utils → agent}/llm-client.d.ts +0 -0
  57. /package/dist/{utils → agent}/llm-client.js +0 -0
@@ -1,181 +1,17 @@
1
- import type { AgentMode } from "../agent/host-types.js";
2
- import type { ToolResultDisplay } from "../agent/types.js";
3
- /**
4
- * Typed event map — every event has a known payload shape.
5
- */
6
- export interface ShellEvents {
7
- "shell:command-start": {
8
- command: string;
9
- cwd: string;
10
- };
11
- "shell:command-done": {
12
- command: string;
13
- output: string;
14
- cwd: string;
15
- exitCode: number | null;
16
- };
17
- "shell:cwd-change": {
18
- cwd: string;
19
- };
20
- "shell:foreground-busy": {
21
- busy: boolean;
22
- };
23
- "shell:agent-exec-start": Record<string, never>;
24
- "shell:agent-exec-done": Record<string, never>;
25
- "shell:pty-data": {
26
- raw: string;
27
- };
28
- "shell:pty-write": {
29
- data: string;
30
- };
31
- "shell:pty-resize": {
32
- cols: number;
33
- rows: number;
34
- };
35
- "shell:buffer-request": Record<string, never>;
36
- "shell:buffer-snapshot": {
37
- text: string;
38
- altScreen: boolean;
39
- cursor: {
40
- x: number;
41
- y: number;
42
- };
43
- };
44
- "agent:submit": {
45
- query: string;
46
- };
47
- "agent:cancel-request": {
48
- silent?: boolean;
49
- };
50
- "agent:append-user-message": {
51
- text: string;
52
- };
53
- "input-mode:register": import("../shell/host-types.js").InputModeConfig;
54
- "agent:query": {
55
- query: string;
56
- };
57
- "agent:thinking-chunk": {
58
- text: string;
59
- };
60
- "agent:response-chunk": {
61
- blocks: ContentBlock[];
62
- };
63
- "agent:response-done": {
64
- response: string;
65
- };
66
- "agent:usage": {
67
- prompt_tokens: number;
68
- completion_tokens: number;
69
- total_tokens: number;
70
- };
71
- "llm:request": {
72
- messages: unknown[];
73
- tools?: unknown;
74
- model?: string;
75
- max_tokens?: number;
76
- reasoning_effort?: string;
77
- };
78
- "llm:chunk": {
79
- chunk: unknown;
80
- };
81
- "agent:processing-start": Record<string, never>;
82
- "agent:processing-done": Record<string, never>;
83
- "agent:cancelled": Record<string, never>;
84
- "agent:error": {
85
- message: string;
86
- };
87
- "agent:tool-call": {
88
- tool: string;
89
- args: Record<string, unknown>;
90
- };
91
- "agent:tool-output": {
92
- tool: string;
93
- output: string;
94
- exitCode: number | null;
95
- };
96
- "agent:tool-batch": {
97
- groups: Array<{
98
- kind: string;
99
- tools: Array<{
100
- name: string;
101
- displayDetail?: string;
102
- }>;
103
- }>;
104
- };
105
- "agent:tool-batch-complete": {
106
- results: Array<{
107
- name: string;
108
- isError: boolean;
109
- errorSummary?: string;
110
- }>;
111
- };
112
- "conversation:message-appended": {
113
- role: "user" | "assistant" | "tool" | "system";
114
- content: string;
115
- /** For role="tool": name of the tool whose result this is. */
116
- toolName?: string;
117
- /** For role="tool": parsed arguments passed to the tool. */
118
- toolArgs?: Record<string, unknown>;
119
- /** For role="tool": whether the tool errored. */
120
- isError?: boolean;
121
- };
122
- "conversation:after-compact": {
123
- beforeTokens: number;
124
- afterTokens: number;
125
- evictedCount: number;
126
- };
127
- "agent:tool-started": {
128
- title: string;
129
- toolCallId?: string;
130
- kind?: string;
131
- icon?: string;
132
- locations?: {
133
- path: string;
134
- line?: number | null;
135
- }[];
136
- rawInput?: unknown;
137
- /** Pre-formatted display detail from tool's formatCall(). */
138
- displayDetail?: string;
139
- /** highlight.js-style identifier for syntax-highlighting `rawInput.source`. */
140
- sourceLanguage?: string;
141
- batchIndex?: number;
142
- batchTotal?: number;
143
- };
144
- "agent:tool-completed": {
145
- toolCallId?: string;
146
- exitCode: number | null;
147
- rawOutput?: unknown;
148
- kind?: string;
149
- /** Structured result display — set by formatResult or defaults, overridable via onPipe. */
150
- resultDisplay?: ToolResultDisplay;
151
- };
152
- "agent:tool-output-chunk": {
153
- chunk: string;
154
- };
155
- "agent:subagent-started": {
156
- taskId: string;
157
- task: string;
158
- };
159
- "agent:subagent-completed": {
160
- taskId: string;
161
- task: string;
162
- result: string;
163
- isError: boolean;
164
- };
165
- "tool:interactive-start": Record<string, never>;
166
- "tool:interactive-end": Record<string, never>;
167
- "command:register": {
168
- name: string;
169
- description: string;
170
- handler: (args: string) => Promise<void> | void;
171
- };
172
- "command:unregister": {
173
- name: string;
174
- };
175
- "command:execute": {
176
- name: string;
177
- args: string;
1
+ export interface BackendRegistration {
2
+ name: string;
3
+ kill: () => void;
4
+ start?: () => Promise<void>;
5
+ }
6
+ /** Typed event map — every event has a known payload shape. */
7
+ export interface BusEvents {
8
+ "core:extensions-loaded": {
9
+ names: string[];
178
10
  };
11
+ /** Cross-cutting "config might have changed, repaint" signal. */
12
+ "config:changed": Record<string, never>;
13
+ /** Universal UI feedback channel (any frontend may render; silently
14
+ * ignored without one). */
179
15
  "ui:info": {
180
16
  message: string;
181
17
  };
@@ -185,196 +21,17 @@ export interface ShellEvents {
185
21
  "ui:suggestion": {
186
22
  text: string;
187
23
  };
188
- "compositor:write": {
189
- stream: string;
190
- text: string;
191
- };
192
- "input:keypress": {
193
- key: string;
194
- };
195
- "input:intercept": {
196
- data: string;
197
- consumed: boolean;
198
- };
199
- "shell:stdout-hold": Record<string, never>;
200
- "shell:stdout-release": Record<string, never>;
201
- "shell:stdout-show": Record<string, never>;
202
- "shell:stdout-hide": Record<string, never>;
203
- "agent:terminal-intercept": {
204
- command: string;
205
- cwd: string;
206
- intercepted: boolean;
207
- output: string;
208
- };
209
- "shell:redraw-prompt": {
210
- cwd: string;
211
- kind: "fresh" | "redraw";
212
- handled: boolean;
213
- };
214
- "shell:exec-request": {
215
- command: string;
216
- output: string;
217
- cwd: string;
218
- exitCode: number | null;
219
- done: boolean;
220
- };
221
- "agent:info": {
222
- name: string;
223
- version: string;
224
- model?: string;
225
- provider?: string;
226
- contextWindow?: number;
227
- };
228
- "agent:reset-session": Record<string, never>;
229
- "agent:compact-request": Record<string, never>;
230
- "context:get-stats": {
231
- activeTokens: number;
232
- totalTokens: number;
233
- budgetTokens: number;
234
- };
235
- "context:snapshot": {
236
- messages: unknown[];
237
- contextWindow: number;
238
- activeTokens: number;
239
- };
240
- "context:compact": {
241
- strategy?: {
242
- kind: "two-tier-pin";
243
- target: number;
244
- keepRecent?: number;
245
- force?: boolean;
246
- } | {
247
- kind: "rewind";
248
- toIndex: number;
249
- } | {
250
- kind: "replace";
251
- messages: unknown[];
252
- };
253
- stats?: {
254
- before: number;
255
- after: number;
256
- evictedCount: number;
257
- };
258
- };
259
- "agent:register-backend": {
260
- name: string;
261
- kill: () => void;
262
- start?: () => Promise<void>;
263
- };
264
- "config:switch-backend": {
265
- name: string;
266
- };
267
- "config:list-backends": Record<string, never>;
24
+ /** Backend registry — core owns these; every backend (ash, bridges)
25
+ * emits register, switch/list flow through here too. */
26
+ "agent:register-backend": BackendRegistration;
268
27
  "config:get-backends": {
269
28
  names: string[];
270
29
  active: string | null;
271
30
  };
272
- "config:changed": Record<string, never>;
273
- "config:switch-model": {
274
- model: string;
275
- };
276
- "config:get-models": {
277
- models: {
278
- model: string;
279
- provider: string;
280
- }[];
281
- active: {
282
- model: string;
283
- provider: string;
284
- } | null;
285
- };
286
- "config:set-thinking": {
287
- level: string;
288
- };
289
- "config:get-thinking": {
290
- level: string;
291
- levels: string[];
292
- supported: boolean;
293
- };
294
- "config:switch-provider": {
295
- provider: string;
296
- };
297
- "config:get-initial-modes": {
298
- modes: AgentMode[];
299
- initialModeIndex: number;
300
- };
301
- "config:set-modes": {
302
- modes: AgentMode[];
303
- activeIndex?: number;
304
- };
305
- "config:add-modes": {
306
- modes: AgentMode[];
307
- };
308
- "core:extensions-loaded": {
309
- names: string[];
310
- };
311
- "provider:register": {
312
- id: string;
313
- apiKey?: string;
314
- baseURL?: string;
315
- /** Optional — providers for custom endpoints may not know the catalog
316
- * at registration time. Falls back to models[0] when absent. */
317
- defaultModel?: string;
318
- models?: (string | {
319
- id: string;
320
- reasoning?: boolean;
321
- contextWindow?: number;
322
- maxTokens?: number;
323
- echoReasoning?: boolean;
324
- })[];
325
- /** Provider supports the reasoning_effort parameter. Default: true. */
326
- supportsReasoningEffort?: boolean;
327
- };
328
- "provider:configure": {
329
- id: string;
330
- reasoningParams?: (level: string, model?: string) => Record<string, unknown>;
331
- };
332
- "agent:register-tool": {
333
- tool: import("../agent/types.js").ToolDefinition;
334
- extensionName?: string;
335
- };
336
- "agent:unregister-tool": {
337
- name: string;
338
- };
339
- "agent:get-tools": {
340
- tools: import("../agent/types.js").ToolDefinition[];
341
- };
342
- "agent:register-instruction": {
343
- name: string;
344
- text: string;
345
- extensionName: string;
346
- };
347
- "agent:remove-instruction": {
348
- name: string;
349
- };
350
- "agent:register-skill": {
351
- name: string;
352
- description: string;
353
- filePath: string;
354
- extensionName: string;
355
- };
356
- "agent:remove-skill": {
31
+ "config:switch-backend": {
357
32
  name: string;
358
33
  };
359
- "banner:collect": {
360
- sections: Array<{
361
- label: string;
362
- items: string[];
363
- }>;
364
- /** Name of the backend being launched. Extensions should gate per-backend sections on this rather than settings.defaultBackend. */
365
- activeBackend?: string;
366
- };
367
- "autocomplete:request": {
368
- buffer: string;
369
- /** Parsed slash command name (e.g. "/backend"), or null if not a command. */
370
- command: string | null;
371
- /** Text after the command name (e.g. "clau" for "/backend clau"), or null. */
372
- commandArgs: string | null;
373
- items: {
374
- name: string;
375
- description: string;
376
- }[];
377
- };
34
+ "config:list-backends": Record<string, never>;
378
35
  }
379
36
  export type ContentBlock = {
380
37
  type: "text";
@@ -421,11 +78,11 @@ export declare class EventBus {
421
78
  /** Stamp + dispatch — used by every emit path. */
422
79
  private dispatch;
423
80
  /** Subscribe to a fire-and-forget event. */
424
- on<K extends keyof ShellEvents>(event: K, fn: Listener<ShellEvents[K]>): void;
81
+ on<K extends keyof BusEvents>(event: K, fn: Listener<BusEvents[K]>): void;
425
82
  /** Unsubscribe from a fire-and-forget event. */
426
- off<K extends keyof ShellEvents>(event: K, fn: Listener<ShellEvents[K]>): void;
83
+ off<K extends keyof BusEvents>(event: K, fn: Listener<BusEvents[K]>): void;
427
84
  /** Emit a fire-and-forget event. */
428
- emit<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): void;
85
+ emit<K extends keyof BusEvents>(event: K, payload: BusEvents[K]): void;
429
86
  /** Re-dispatch an event with externally-supplied meta. Used by bridges
430
87
  * and replay tools to preserve the original source/ts/id of remote or
431
88
  * recorded events instead of restamping them as locally originated. */
@@ -436,21 +93,21 @@ export declare class EventBus {
436
93
  * listeners (renderers). This enables content pipelines where extensions
437
94
  * modify data (e.g. render LaTeX → terminal image) before renderers see it.
438
95
  */
439
- emitTransform<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): void;
96
+ emitTransform<K extends keyof BusEvents>(event: K, payload: BusEvents[K]): void;
440
97
  /** Register a transform listener for a pipeline event. */
441
- onPipe<K extends keyof ShellEvents>(event: K, fn: PipeListener<ShellEvents[K]>): void;
98
+ onPipe<K extends keyof BusEvents>(event: K, fn: PipeListener<BusEvents[K]>): void;
442
99
  /** Remove a transform listener from a pipeline event. */
443
- offPipe<K extends keyof ShellEvents>(event: K, fn: PipeListener<ShellEvents[K]>): void;
100
+ offPipe<K extends keyof BusEvents>(event: K, fn: PipeListener<BusEvents[K]>): void;
444
101
  /**
445
102
  * Emit a pipeline event — each registered pipe listener receives the
446
103
  * output of the previous one. Returns the final transformed payload.
447
104
  * If no listeners are registered, returns the original payload unchanged.
448
105
  */
449
- emitPipe<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): ShellEvents[K];
106
+ emitPipe<K extends keyof BusEvents>(event: K, payload: BusEvents[K]): BusEvents[K];
450
107
  /** Remove an async transform listener from a pipeline event. */
451
- offPipeAsync<K extends keyof ShellEvents>(event: K, fn: AsyncPipeListener<ShellEvents[K]>): void;
108
+ offPipeAsync<K extends keyof BusEvents>(event: K, fn: AsyncPipeListener<BusEvents[K]>): void;
452
109
  /** Register an async transform listener for a pipeline event. */
453
- onPipeAsync<K extends keyof ShellEvents>(event: K, fn: AsyncPipeListener<ShellEvents[K]>): void;
110
+ onPipeAsync<K extends keyof BusEvents>(event: K, fn: AsyncPipeListener<BusEvents[K]>): void;
454
111
  /**
455
112
  * Emit an async pipeline event. Two phases:
456
113
  * 1. Notify — fire regular `on` listeners synchronously (e.g., TUI flushes state)
@@ -460,6 +117,6 @@ export declare class EventBus {
460
117
  * Returns the final transformed payload. If no pipe listeners are registered,
461
118
  * returns the original payload unchanged (with safe defaults).
462
119
  */
463
- emitPipeAsync<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): Promise<ShellEvents[K]>;
120
+ emitPipeAsync<K extends keyof BusEvents>(event: K, payload: BusEvents[K]): Promise<BusEvents[K]>;
464
121
  }
465
122
  export {};
@@ -77,19 +77,19 @@ function createScopedContext(ctx, extensionName) {
77
77
  scopedAgent = {
78
78
  ...agent,
79
79
  registerTool: (tool) => {
80
- bus.emit("agent:register-tool", { tool, extensionName });
81
- cleanups.push(() => bus.emit("agent:unregister-tool", { name: tool.name }));
80
+ agent.registerTool(tool);
81
+ cleanups.push(() => agent.unregisterTool(tool.name));
82
82
  },
83
83
  adviseTool: trackUnsub(agent.adviseTool),
84
84
  adviseToolSchema: trackUnsub(agent.adviseToolSchema),
85
85
  registerInstruction: (name, text) => {
86
- bus.emit("agent:register-instruction", { name, text, extensionName });
87
- cleanups.push(() => bus.emit("agent:remove-instruction", { name }));
86
+ agent.registerInstruction(name, text);
87
+ cleanups.push(() => agent.removeInstruction(name));
88
88
  },
89
89
  adviseInstruction: trackUnsub(agent.adviseInstruction),
90
90
  registerSkill: (name, description, filePath) => {
91
- bus.emit("agent:register-skill", { name, description, filePath, extensionName });
92
- cleanups.push(() => bus.emit("agent:remove-skill", { name }));
91
+ agent.registerSkill(name, description, filePath);
92
+ cleanups.push(() => agent.removeSkill(name));
93
93
  },
94
94
  adviseSkill: trackUnsub(agent.adviseSkill),
95
95
  registerContextProducer: (name, producer, opts) => {
@@ -1,27 +1,17 @@
1
1
  /**
2
- * Core kernel — the minimum viable agent-sh.
3
- *
4
- * Wires up EventBus + HandlerRegistry without any frontend or agent backend.
5
- * Consumers attach their own I/O (Shell, WebSocket, REST, tests) by
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
+ import "../shell/events.js";
9
+ import "../agent/events.js";
10
+ import "../extensions/slash-commands/events.js";
21
11
  import type { AppConfig, ExtensionContext } from "../shell/host-types.js";
22
12
  import { HandlerRegistry } from "../utils/handler-registry.js";
23
13
  export { EventBus } from "./event-bus.js";
24
- export type { ShellEvents, ContentBlock } from "./event-bus.js";
14
+ export type { BusEvents, ContentBlock, BackendRegistration } from "./event-bus.js";
25
15
  export type { CoreContext, CoreConfig } from "./types.js";
26
16
  export type { AgentContext, AgentConfig, AgentSurface, AgentConfigSurface, AgentMode, LlmInterface, LlmMessage, LlmSession } from "../agent/host-types.js";
27
17
  export type { ShellContext, ShellConfig, ShellSurface, ShellConfigSurface, ExtensionContext, RemoteSession, RemoteSessionOptions, RenderSurface, InputModeConfig, TerminalSession, BlockTransformOptions, FencedBlockTransformOptions, AppConfig } from "../shell/host-types.js";
@@ -29,29 +19,20 @@ export { palette, setPalette, resetPalette } from "../utils/palette.js";
29
19
  export type { ColorPalette } from "../utils/palette.js";
30
20
  export type { AgentBackend, ToolDefinition } from "../agent/types.js";
31
21
  export { runSubagent, type SubagentOptions } from "../agent/subagent.js";
32
- export { LlmClient } from "../utils/llm-client.js";
22
+ export { LlmClient } from "../agent/llm-client.js";
33
23
  export { HistoryFile, InMemoryHistory, NoopHistory, type HistoryAdapter } from "../agent/history-file.js";
34
24
  export type { NuclearEntry } from "../agent/nuclear-form.js";
35
25
  export { compileSearchRegex, matchEntry, formatNuclearLine } from "../agent/nuclear-form.js";
36
26
  export interface AgentShellCore {
37
27
  bus: EventBus;
38
- /** Handler registry for define/advise/call. */
39
28
  handlers: HandlerRegistry;
40
29
  /** Unique id for this agent process; used for shell-marker tagging and lineage tracking. */
41
30
  instanceId: string;
42
- /** Activate the agent backend (call after extensions load). */
31
+ /** Activates a registered backend by name (or persisted default / first registered). */
43
32
  activateBackend(override?: string): Promise<void>;
44
- /** Convenience: emit agent:submit and await the response. */
45
- query(text: string): Promise<string>;
46
- /** Convenience: emit agent:cancel-request. */
47
- cancel(): void;
48
- /** Convenience: emit agent:append-user-message. */
49
- appendUserMessage(text: string): void;
50
- /** Build an ExtensionContext for loading extensions against this core. */
51
33
  extensionContext(opts: {
52
34
  quit: () => void;
53
35
  }): ExtensionContext;
54
- /** Tear down the agent and clean up. */
55
36
  kill(): void;
56
37
  }
57
38
  export declare function createCore(config: AppConfig): AgentShellCore;