agent-sh 0.12.26 → 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.
Files changed (144) hide show
  1. package/README.md +13 -2
  2. package/dist/agent/agent-loop.d.ts +3 -5
  3. package/dist/agent/agent-loop.js +44 -100
  4. package/dist/agent/conversation-state.d.ts +9 -0
  5. package/dist/agent/conversation-state.js +38 -1
  6. package/dist/agent/history-file.d.ts +6 -0
  7. package/dist/agent/history-file.js +1 -1
  8. package/dist/agent/host-types.d.ts +125 -0
  9. package/dist/agent/index.d.ts +12 -4
  10. package/dist/agent/index.js +357 -6
  11. package/dist/agent/nuclear-form.d.ts +7 -0
  12. package/dist/{extensions → agent}/providers/deepseek.d.ts +2 -2
  13. package/dist/{extensions → agent}/providers/deepseek.js +5 -4
  14. package/dist/{extensions → agent}/providers/openai-compatible.d.ts +2 -2
  15. package/dist/{extensions → agent}/providers/openai.d.ts +2 -2
  16. package/dist/{extensions → agent}/providers/openai.js +3 -2
  17. package/dist/{extensions → agent}/providers/openrouter.d.ts +2 -2
  18. package/dist/{extensions → agent}/providers/openrouter.js +4 -3
  19. package/dist/agent/skills.js +51 -7
  20. package/dist/agent/subagent.d.ts +1 -1
  21. package/dist/agent/system-prompt.js +14 -17
  22. package/dist/agent/tool-protocol.d.ts +1 -1
  23. package/dist/agent/tool-protocol.js +5 -3
  24. package/dist/agent/tool-registry.d.ts +9 -4
  25. package/dist/agent/tool-registry.js +27 -4
  26. package/dist/agent/tools/bash.d.ts +1 -1
  27. package/dist/agent/tools/bash.js +3 -2
  28. package/dist/agent/tools/edit-file.js +0 -1
  29. package/dist/agent/tools/glob.js +1 -1
  30. package/dist/agent/tools/grep.js +1 -1
  31. package/dist/agent/tools/pwsh.d.ts +1 -1
  32. package/dist/agent/tools/pwsh.js +1 -2
  33. package/dist/agent/tools/read-file.js +7 -4
  34. package/dist/agent/tools/write-file.js +0 -1
  35. package/dist/agent/types.d.ts +17 -2
  36. package/dist/cli/auth/cli.d.ts +1 -0
  37. package/dist/cli/auth/cli.js +216 -0
  38. package/dist/cli/auth/keys.d.ts +31 -0
  39. package/dist/cli/auth/keys.js +102 -0
  40. package/dist/{index.js → cli/index.js} +29 -32
  41. package/dist/{init.js → cli/init.js} +1 -1
  42. package/dist/{install.js → cli/install.js} +114 -5
  43. package/dist/cli/subcommands.d.ts +1 -0
  44. package/dist/cli/subcommands.js +17 -0
  45. package/dist/{event-bus.d.ts → core/event-bus.d.ts} +7 -13
  46. package/dist/{extension-loader.d.ts → core/extension-loader.d.ts} +1 -1
  47. package/dist/{extension-loader.js → core/extension-loader.js} +62 -70
  48. package/dist/{core.d.ts → core/index.d.ts} +18 -15
  49. package/dist/{core.js → core/index.js} +18 -92
  50. package/dist/{settings.d.ts → core/settings.d.ts} +7 -0
  51. package/dist/{settings.js → core/settings.js} +1 -0
  52. package/dist/core/types.d.ts +49 -0
  53. package/dist/core/types.js +1 -0
  54. package/dist/extensions/file-autocomplete.d.ts +1 -1
  55. package/dist/extensions/index.d.ts +7 -14
  56. package/dist/extensions/index.js +2 -19
  57. package/dist/extensions/slash-commands.d.ts +1 -1
  58. package/dist/extensions/slash-commands.js +7 -2
  59. package/dist/shell/host-types.d.ts +114 -0
  60. package/dist/shell/host-types.js +1 -0
  61. package/dist/shell/index.d.ts +8 -7
  62. package/dist/shell/index.js +58 -9
  63. package/dist/shell/input-handler.d.ts +7 -1
  64. package/dist/shell/input-handler.js +5 -2
  65. package/dist/shell/output-parser.d.ts +1 -1
  66. package/dist/{extensions → shell}/shell-context.d.ts +1 -1
  67. package/dist/{extensions → shell}/shell-context.js +18 -12
  68. package/dist/shell/shell.d.ts +6 -4
  69. package/dist/shell/shell.js +33 -109
  70. package/dist/shell/strategies/bash.d.ts +2 -0
  71. package/dist/shell/strategies/bash.js +68 -0
  72. package/dist/shell/strategies/fish.d.ts +2 -0
  73. package/dist/shell/strategies/fish.js +65 -0
  74. package/dist/shell/strategies/index.d.ts +13 -0
  75. package/dist/shell/strategies/index.js +17 -0
  76. package/dist/shell/strategies/types.d.ts +50 -0
  77. package/dist/shell/strategies/types.js +9 -0
  78. package/dist/shell/strategies/zsh.d.ts +2 -0
  79. package/dist/shell/strategies/zsh.js +72 -0
  80. package/dist/shell/tui-input-view.js +14 -3
  81. package/dist/{extensions → shell}/tui-renderer.d.ts +1 -1
  82. package/dist/{extensions → shell}/tui-renderer.js +27 -55
  83. package/dist/utils/box-frame.d.ts +4 -0
  84. package/dist/utils/box-frame.js +17 -6
  85. package/dist/utils/compositor.d.ts +1 -1
  86. package/dist/utils/compositor.js +2 -1
  87. package/dist/{executor.js → utils/executor.js} +1 -1
  88. package/dist/utils/floating-panel.d.ts +17 -5
  89. package/dist/utils/floating-panel.js +218 -70
  90. package/dist/utils/llm-facade.d.ts +7 -3
  91. package/dist/utils/stream-transform.d.ts +1 -1
  92. package/dist/utils/terminal-buffer.d.ts +1 -1
  93. package/dist/utils/tool-display.js +4 -0
  94. package/dist/utils/tool-interactive.d.ts +1 -1
  95. package/dist/utils/tty.d.ts +7 -0
  96. package/dist/utils/tty.js +15 -0
  97. package/examples/extensions/ash-acp-bridge/README.md +4 -1
  98. package/examples/extensions/ash-acp-bridge/src/index.ts +654 -0
  99. package/examples/extensions/ash-mcp-bridge/index.ts +1 -1
  100. package/examples/extensions/ashi/README.md +250 -0
  101. package/examples/extensions/ashi/package.json +60 -0
  102. package/examples/extensions/ashi/src/autocomplete.ts +91 -0
  103. package/examples/extensions/ashi/src/capture.ts +34 -0
  104. package/examples/extensions/ashi/src/cli.ts +126 -0
  105. package/examples/extensions/ashi/src/commands.ts +82 -0
  106. package/examples/extensions/ashi/src/compaction.ts +157 -0
  107. package/examples/extensions/ashi/src/components.ts +332 -0
  108. package/examples/extensions/ashi/src/default-renderers.ts +153 -0
  109. package/examples/extensions/ashi/src/display-config.ts +62 -0
  110. package/examples/extensions/ashi/src/frontend.ts +735 -0
  111. package/examples/extensions/ashi/src/hooks.ts +136 -0
  112. package/examples/extensions/ashi/src/multi-session-store.ts +146 -0
  113. package/examples/extensions/ashi/src/session-commands.ts +76 -0
  114. package/examples/extensions/ashi/src/session-store.ts +264 -0
  115. package/examples/extensions/ashi/src/status-footer.ts +66 -0
  116. package/examples/extensions/ashi/src/theme.ts +151 -0
  117. package/examples/extensions/ashi/tsconfig.json +14 -0
  118. package/examples/extensions/emacs-buffer.ts +364 -0
  119. package/examples/extensions/interactive-prompts.ts +114 -69
  120. package/examples/extensions/latex-images.ts +3 -3
  121. package/examples/extensions/opencode-bridge/index.ts +1 -1
  122. package/examples/extensions/overlay-agent.ts +35 -10
  123. package/examples/extensions/peer-mesh.ts +1 -1
  124. package/examples/extensions/pi-bridge/index.ts +0 -1
  125. package/examples/extensions/questionnaire.ts +2 -1
  126. package/examples/extensions/rtk-proxy.ts +3 -3
  127. package/examples/extensions/solarized-theme.ts +3 -3
  128. package/examples/extensions/subagents.ts +6 -6
  129. package/examples/extensions/terminal-buffer.ts +174 -33
  130. package/examples/extensions/tmux-pane.ts +6 -4
  131. package/examples/extensions/tunnel-vision.ts +405 -0
  132. package/examples/extensions/user-shell.ts +1 -1
  133. package/examples/extensions/web-access.ts +8 -113
  134. package/package.json +26 -22
  135. package/dist/extensions/agent-backend.d.ts +0 -14
  136. package/dist/extensions/agent-backend.js +0 -307
  137. package/dist/types.d.ts +0 -227
  138. /package/dist/{types.js → agent/host-types.js} +0 -0
  139. /package/dist/{extensions → agent}/providers/openai-compatible.js +0 -0
  140. /package/dist/{index.d.ts → cli/index.d.ts} +0 -0
  141. /package/dist/{init.d.ts → cli/init.d.ts} +0 -0
  142. /package/dist/{install.d.ts → cli/install.d.ts} +0 -0
  143. /package/dist/{event-bus.js → core/event-bus.js} +0 -0
  144. /package/dist/{executor.d.ts → utils/executor.d.ts} +0 -0
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
1
  {
2
2
  "name": "agent-sh",
3
- "version": "0.12.26",
3
+ "version": "0.13.0",
4
4
  "description": "A shell-first terminal where AI is one keystroke away",
5
5
  "type": "module",
6
- "main": "dist/core.js",
7
- "types": "dist/core.d.ts",
6
+ "main": "dist/core/index.js",
7
+ "types": "dist/core/index.d.ts",
8
8
  "bin": {
9
- "agent-sh": "dist/index.js"
9
+ "agent-sh": "dist/cli/index.js"
10
10
  },
11
11
  "exports": {
12
12
  ".": {
13
- "types": "./dist/core.d.ts",
14
- "default": "./dist/core.js"
13
+ "types": "./dist/core/index.d.ts",
14
+ "default": "./dist/core/index.js"
15
15
  },
16
16
  "./core": {
17
- "types": "./dist/core.d.ts",
18
- "default": "./dist/core.js"
17
+ "types": "./dist/core/index.d.ts",
18
+ "default": "./dist/core/index.js"
19
19
  },
20
20
  "./utils/*": "./dist/utils/*",
21
21
  "./types": {
22
- "types": "./dist/types.d.ts",
23
- "default": "./dist/types.js"
22
+ "types": "./dist/core/index.d.ts",
23
+ "default": "./dist/core/index.js"
24
24
  },
25
25
  "./settings": {
26
- "types": "./dist/settings.d.ts",
27
- "default": "./dist/settings.js"
26
+ "types": "./dist/core/settings.d.ts",
27
+ "default": "./dist/core/settings.js"
28
28
  },
29
29
  "./extension-loader": {
30
- "types": "./dist/extension-loader.d.ts",
31
- "default": "./dist/extension-loader.js"
30
+ "types": "./dist/core/extension-loader.d.ts",
31
+ "default": "./dist/core/extension-loader.js"
32
32
  },
33
33
  "./extensions": {
34
34
  "types": "./dist/extensions/index.d.ts",
@@ -46,6 +46,10 @@
46
46
  "types": "./dist/utils/terminal-buffer.d.ts",
47
47
  "default": "./dist/utils/terminal-buffer.js"
48
48
  },
49
+ "./agent": {
50
+ "types": "./dist/agent/index.d.ts",
51
+ "default": "./dist/agent/index.js"
52
+ },
49
53
  "./agent/types": {
50
54
  "types": "./dist/agent/types.d.ts",
51
55
  "default": "./dist/agent/types.js"
@@ -59,8 +63,8 @@
59
63
  "default": "./dist/agent/agent-loop.js"
60
64
  },
61
65
  "./event-bus": {
62
- "types": "./dist/event-bus.d.ts",
63
- "default": "./dist/event-bus.js"
66
+ "types": "./dist/core/event-bus.d.ts",
67
+ "default": "./dist/core/event-bus.js"
64
68
  },
65
69
  "./utils/compositor": {
66
70
  "types": "./dist/utils/compositor.d.ts",
@@ -83,8 +87,8 @@
83
87
  "default": "./dist/agent/nuclear-form.js"
84
88
  },
85
89
  "./executor": {
86
- "types": "./dist/executor.d.ts",
87
- "default": "./dist/executor.js"
90
+ "types": "./dist/utils/executor.d.ts",
91
+ "default": "./dist/utils/executor.js"
88
92
  }
89
93
  },
90
94
  "files": [
@@ -93,15 +97,15 @@
93
97
  "examples/extensions/*/package.json",
94
98
  "examples/extensions/*/tsconfig.json",
95
99
  "examples/extensions/*/README.md",
96
- "examples/extensions/*/src",
100
+ "examples/extensions/*/src/**",
97
101
  "examples/extensions/*/index.ts",
98
102
  "examples/extensions/*/index.js"
99
103
  ],
100
104
  "scripts": {
101
- "dev": "tsx src/index.ts",
105
+ "dev": "tsx src/cli/index.ts",
102
106
  "build": "tsc",
103
- "start": "node dist/index.js",
104
- "prepare": "npm run build"
107
+ "start": "node dist/cli/index.js",
108
+ "prepare": "test -d dist || npm run build"
105
109
  },
106
110
  "keywords": [
107
111
  "terminal",
@@ -1,14 +0,0 @@
1
- /**
2
- * Built-in agent backend extension.
3
- *
4
- * Constructs the AgentLoop synchronously with a placeholder LlmClient,
5
- * so core handlers (history:append, system-prompt:build, conversation:*)
6
- * are defined before user extensions activate. Mode resolution is
7
- * deferred to `core:extensions-loaded`, giving runtime-registered
8
- * providers (e.g. openrouter) a chance to register before we look up
9
- * settings.defaultProvider. Without this deferral, a persisted
10
- * `defaultProvider: "openrouter"` loses to a cold-start race and the
11
- * backend bails silently.
12
- */
13
- import type { ExtensionContext } from "../types.js";
14
- export default function agentBackend(ctx: ExtensionContext): void;
@@ -1,307 +0,0 @@
1
- import { AgentLoop } from "../agent/agent-loop.js";
2
- import { LlmClient } from "../utils/llm-client.js";
3
- import { resolveProvider, getProviderNames, getSettings } from "../settings.js";
4
- import { PACKAGE_VERSION } from "../utils/package-version.js";
5
- import { discoverSkills } from "../agent/skills.js";
6
- /** Read the user's persisted defaultModel for a provider, if any. */
7
- function persistedModelFor(providerName) {
8
- if (!providerName)
9
- return undefined;
10
- return getSettings().providers?.[providerName]?.defaultModel;
11
- }
12
- function defaultReasoningBuilder(level) {
13
- return level === "off" ? {} : { reasoning_effort: level };
14
- }
15
- function mergeCaps(settingsCaps, payloadCaps, modelIds) {
16
- if (!settingsCaps)
17
- return payloadCaps.size > 0 ? payloadCaps : undefined;
18
- const out = new Map();
19
- for (const id of modelIds) {
20
- const s = settingsCaps.get(id);
21
- const p = payloadCaps.get(id);
22
- if (!s && !p)
23
- continue;
24
- out.set(id, {
25
- reasoning: s?.reasoning ?? p?.reasoning,
26
- contextWindow: s?.contextWindow ?? p?.contextWindow,
27
- maxTokens: s?.maxTokens ?? p?.maxTokens,
28
- echoReasoning: s?.echoReasoning ?? p?.echoReasoning,
29
- });
30
- }
31
- return out.size > 0 ? out : undefined;
32
- }
33
- export default function agentBackend(ctx) {
34
- const { bus } = ctx;
35
- const config = ctx.call("config:get-shell-config") ?? {};
36
- // Immutable settings snapshot; provider:register payloads merge against it.
37
- const providerRegistry = new Map();
38
- const settingsProviders = new Map();
39
- for (const name of getProviderNames()) {
40
- const p = resolveProvider(name);
41
- if (p) {
42
- providerRegistry.set(name, p);
43
- settingsProviders.set(name, p);
44
- }
45
- }
46
- const providerHooks = new Map();
47
- // Bakes model id into the hook so AgentMode.buildReasoningParams keeps
48
- // its (level) signature while the hook can branch on model.
49
- const bindReasoning = (shapeId, model) => {
50
- const hook = providerHooks.get(shapeId)?.reasoningParams;
51
- return hook ? (level) => hook(level, model) : defaultReasoningBuilder;
52
- };
53
- const buildModes = () => {
54
- const allModes = [];
55
- for (const [id, p] of providerRegistry) {
56
- if (!p.apiKey)
57
- continue;
58
- const shapeId = p.reasoningShape ?? id;
59
- for (const model of p.models) {
60
- const mc = p.modelCapabilities?.get(model);
61
- allModes.push({
62
- model,
63
- provider: id,
64
- providerConfig: { apiKey: p.apiKey, baseURL: p.baseURL },
65
- contextWindow: mc?.contextWindow ?? p.contextWindow,
66
- maxTokens: mc?.maxTokens ?? (mc?.contextWindow ? Math.min(Math.floor(mc.contextWindow * 0.4), 65536) : undefined),
67
- reasoning: mc?.reasoning,
68
- supportsReasoningEffort: p.supportsReasoningEffort,
69
- echoReasoning: mc?.echoReasoning,
70
- buildReasoningParams: bindReasoning(shapeId, model),
71
- });
72
- }
73
- }
74
- return allModes;
75
- };
76
- // Placeholder client — reconfigured at core:extensions-loaded. Any
77
- // stream() call before then fails from the OpenAI SDK; start() won't
78
- // wire the loop until we've resolved, so users never hit that path.
79
- const llmClient = new LlmClient({ apiKey: "not-configured", model: "not-configured" });
80
- ctx.define("llm:get-client", () => llmClient);
81
- ctx.define("llm:invoke", (messages, opts) => {
82
- return llmClient.complete({
83
- messages: messages,
84
- max_tokens: opts?.maxTokens,
85
- model: opts?.model,
86
- reasoning_effort: opts?.reasoningEffort,
87
- });
88
- });
89
- let modes = [];
90
- let initialModeIndex = 0;
91
- let resolved = false;
92
- // Gates late-registration reconcile so its config:switch-model emit doesn't misroute under a non-ash backend.
93
- let ashActive = false;
94
- bus.onPipe("config:get-initial-modes", () => ({ modes, initialModeIndex }));
95
- // AgentLoop must be constructed *before* user extensions activate,
96
- // because its ctor defines handlers (history:append, etc.) that
97
- // extensions like superash call synchronously during their own
98
- // activate. Advise-before-define works for advisers, but plain calls
99
- // would hit a no-op stub.
100
- const agentLoop = new AgentLoop({
101
- bus,
102
- llmClient,
103
- handlers: { define: ctx.define, advise: ctx.advise, call: ctx.call, list: ctx.list },
104
- modes,
105
- initialModeIndex,
106
- compositor: ctx.compositor,
107
- instanceId: ctx.instanceId,
108
- history: config.history,
109
- });
110
- let loadedExtensionNames = [];
111
- bus.on("core:extensions-loaded", ({ names }) => {
112
- loadedExtensionNames = names;
113
- const settings = getSettings();
114
- // If the user didn't pick a default, fall back to the first registered
115
- // provider (built-in load order biases to openrouter → openai).
116
- const providerName = config.provider ?? settings.defaultProvider
117
- ?? (providerRegistry.size > 0 ? providerRegistry.keys().next().value : undefined);
118
- const activeProvider = providerName ? providerRegistry.get(providerName) ?? null : null;
119
- // User's persisted defaultModel wins over the provider's declared
120
- // default. Dynamic providers (openrouter) re-register with their
121
- // hardcoded DEFAULT_MODELS[0] each startup, which would otherwise
122
- // clobber the user's /model selection.
123
- const effectiveApiKey = config.apiKey ?? activeProvider?.apiKey;
124
- const effectiveBaseURL = config.baseURL ?? activeProvider?.baseURL;
125
- const effectiveModel = config.model ?? persistedModelFor(providerName) ?? activeProvider?.defaultModel;
126
- // No provider → don't register ash at all, so another backend (e.g.
127
- // claude-code-bridge) can own activation. index.ts hard-fails only
128
- // when no backend ended up registered.
129
- if (!effectiveApiKey || !effectiveModel)
130
- return;
131
- modes = buildModes();
132
- if (modes.length === 0)
133
- modes = [{ model: effectiveModel }];
134
- let foundIdx = modes.findIndex((m) => m.model === effectiveModel && (!activeProvider || m.provider === activeProvider.id));
135
- // Persisted default may not be in the provider's curated list yet (e.g.
136
- // openrouter's async catalog fetch hasn't returned). Prepend a stub so
137
- // the initial config:set-modes activeIndex points at the real model —
138
- // otherwise AgentLoop reconfigures llmClient back to modes[0].
139
- if (foundIdx === -1 && activeProvider) {
140
- modes = [
141
- {
142
- model: effectiveModel,
143
- provider: activeProvider.id,
144
- providerConfig: { apiKey: effectiveApiKey, baseURL: effectiveBaseURL },
145
- supportsReasoningEffort: activeProvider.supportsReasoningEffort,
146
- },
147
- ...modes,
148
- ];
149
- foundIdx = 0;
150
- }
151
- initialModeIndex = Math.max(0, foundIdx);
152
- llmClient.reconfigure({ apiKey: effectiveApiKey, baseURL: effectiveBaseURL, model: effectiveModel });
153
- bus.emit("config:set-modes", { modes, activeIndex: initialModeIndex });
154
- resolved = true;
155
- bus.emit("agent:register-backend", {
156
- name: "ash",
157
- kill: () => {
158
- ashActive = false;
159
- bus.emit("command:unregister", { name: "/compact" });
160
- bus.emit("command:unregister", { name: "/context" });
161
- agentLoop.kill();
162
- },
163
- start: async () => {
164
- agentLoop.wire();
165
- ashActive = true;
166
- bus.emit("command:register", {
167
- name: "/compact",
168
- description: "Compact conversation via the active compaction strategy",
169
- handler: () => bus.emit("agent:compact-request", {}),
170
- });
171
- bus.emit("command:register", {
172
- name: "/context",
173
- description: "Show context budget usage",
174
- handler: () => {
175
- const stats = bus.emitPipe("context:get-stats", {
176
- activeTokens: 0,
177
- totalTokens: 0,
178
- budgetTokens: 0,
179
- });
180
- const pct = stats.budgetTokens > 0
181
- ? Math.round((stats.activeTokens / stats.budgetTokens) * 100)
182
- : 0;
183
- bus.emit("ui:info", {
184
- message: `Active context: ~${stats.activeTokens.toLocaleString()} tokens / ${stats.budgetTokens.toLocaleString()} budget (${pct}%)`,
185
- });
186
- },
187
- });
188
- bus.emit("agent:info", {
189
- name: "ash",
190
- version: PACKAGE_VERSION,
191
- model: llmClient.model,
192
- provider: modes[initialModeIndex]?.provider,
193
- contextWindow: modes[initialModeIndex]?.contextWindow,
194
- });
195
- },
196
- });
197
- });
198
- bus.on("provider:configure", ({ id, reasoningParams }) => {
199
- const prev = providerHooks.get(id) ?? {};
200
- if (reasoningParams !== undefined)
201
- prev.reasoningParams = reasoningParams;
202
- providerHooks.set(id, prev);
203
- });
204
- bus.on("provider:register", (p) => {
205
- const rawModels = p.models ?? (p.defaultModel ? [p.defaultModel] : []);
206
- const payloadModelIds = [];
207
- const payloadCaps = new Map();
208
- for (const m of rawModels) {
209
- if (typeof m === "string") {
210
- payloadModelIds.push(m);
211
- }
212
- else {
213
- payloadModelIds.push(m.id);
214
- payloadCaps.set(m.id, { reasoning: m.reasoning, contextWindow: m.contextWindow, maxTokens: m.maxTokens, echoReasoning: m.echoReasoning });
215
- }
216
- }
217
- const settings = settingsProviders.get(p.id);
218
- const modelIds = settings?.modelsExplicit && settings.models.length > 0 ? settings.models : payloadModelIds;
219
- const mergedCaps = mergeCaps(settings?.modelCapabilities, payloadCaps, modelIds);
220
- const merged = {
221
- id: p.id,
222
- apiKey: settings?.apiKey ?? p.apiKey,
223
- baseURL: settings?.baseURL ?? p.baseURL,
224
- defaultModel: settings?.defaultModel ?? p.defaultModel,
225
- models: modelIds,
226
- modelsExplicit: settings?.modelsExplicit ?? false,
227
- contextWindow: settings?.contextWindow,
228
- supportsReasoningEffort: settings?.supportsReasoningEffort ?? p.supportsReasoningEffort,
229
- modelCapabilities: mergedCaps,
230
- reasoningShape: settings?.reasoningShape,
231
- };
232
- providerRegistry.set(p.id, merged);
233
- const addModes = modelIds.map((m) => {
234
- const mc = mergedCaps?.get(m);
235
- return {
236
- model: m,
237
- provider: p.id,
238
- providerConfig: { apiKey: merged.apiKey ?? "", baseURL: merged.baseURL },
239
- contextWindow: mc?.contextWindow,
240
- maxTokens: mc?.maxTokens,
241
- reasoning: mc?.reasoning,
242
- supportsReasoningEffort: merged.supportsReasoningEffort,
243
- echoReasoning: mc?.echoReasoning,
244
- buildReasoningParams: bindReasoning(p.id, m),
245
- };
246
- });
247
- bus.emit("config:add-modes", { modes: addModes });
248
- // Late-registration reconcile: if this completes the user's persisted
249
- // default (openrouter's async fetch delivers the full catalog after
250
- // we've already fallen back to mode 0), quietly switch to it.
251
- if (!resolved || !ashActive)
252
- return;
253
- const pendingProvider = getSettings().defaultProvider;
254
- if (pendingProvider !== p.id)
255
- return;
256
- const pendingModel = persistedModelFor(pendingProvider);
257
- if (pendingModel && modelIds.includes(pendingModel) && llmClient.model !== pendingModel) {
258
- bus.emit("config:switch-model", { model: pendingModel });
259
- }
260
- });
261
- bus.on("config:switch-provider", ({ provider: name }) => {
262
- const p = providerRegistry.get(name);
263
- if (!p) {
264
- bus.emit("ui:error", { message: `Unknown provider: ${name}` });
265
- return;
266
- }
267
- if (!p.apiKey) {
268
- bus.emit("ui:error", { message: `Provider "${name}" has no API key configured` });
269
- return;
270
- }
271
- const switchModel = p.defaultModel ?? p.models[0];
272
- if (!switchModel) {
273
- bus.emit("ui:error", { message: `Provider "${name}" has no models configured` });
274
- return;
275
- }
276
- llmClient.reconfigure({ apiKey: p.apiKey, baseURL: p.baseURL, model: switchModel });
277
- const newModes = p.models.map((m) => {
278
- const mc = p.modelCapabilities?.get(m);
279
- return {
280
- model: m,
281
- provider: name,
282
- providerConfig: { apiKey: p.apiKey, baseURL: p.baseURL },
283
- contextWindow: mc?.contextWindow ?? p.contextWindow,
284
- maxTokens: mc?.maxTokens ?? (mc?.contextWindow ? Math.min(Math.floor(mc.contextWindow * 0.4), 65536) : undefined),
285
- reasoning: mc?.reasoning,
286
- supportsReasoningEffort: p.supportsReasoningEffort,
287
- echoReasoning: mc?.echoReasoning,
288
- };
289
- });
290
- bus.emit("config:set-modes", { modes: newModes });
291
- bus.emit("agent:info", { name: "ash", version: PACKAGE_VERSION, model: switchModel, provider: name, contextWindow: p.contextWindow });
292
- bus.emit("ui:info", { message: `Switched to ${name} (${switchModel})` });
293
- bus.emit("config:changed", {});
294
- });
295
- bus.onPipe("banner:collect", (e) => {
296
- if (e.activeBackend && e.activeBackend !== "ash")
297
- return e;
298
- if (loadedExtensionNames.length > 0) {
299
- e.sections.push({ label: "Extensions", items: [...loadedExtensionNames] });
300
- }
301
- const skills = discoverSkills(ctx.call("cwd") ?? process.cwd());
302
- if (skills.length > 0) {
303
- e.sections.push({ label: "Skills", items: skills.map((s) => s.name) });
304
- }
305
- return e;
306
- });
307
- }
package/dist/types.d.ts DELETED
@@ -1,227 +0,0 @@
1
- import type { EventBus } from "./event-bus.js";
2
- import type { ColorPalette } from "./utils/palette.js";
3
- import type { BlockTransformOptions, FencedBlockTransformOptions } from "./utils/stream-transform.js";
4
- import type { ToolDefinition } from "./agent/types.js";
5
- import type { Compositor } from "./utils/compositor.js";
6
- import type { HistoryAdapter } from "./agent/history-file.js";
7
- export type { ContentBlock } from "./event-bus.js";
8
- export type { BlockTransformOptions, FencedBlockTransformOptions } from "./utils/stream-transform.js";
9
- export type { RenderSurface } from "./utils/compositor.js";
10
- export interface RemoteSessionOptions {
11
- /** The surface to render agent output to. */
12
- surface: import("./utils/compositor.js").RenderSurface;
13
- /** Suppress response borders (default: true). */
14
- suppressBorders?: boolean;
15
- /** Suppress user query box (default: false).
16
- * True for sessions with their own input (rsplit, overlay).
17
- * False for sessions where input comes from the main shell (split). */
18
- suppressQueryBox?: boolean;
19
- /** Suppress usage stats line (default: true). */
20
- suppressUsage?: boolean;
21
- }
22
- export interface RemoteSession {
23
- /** Submit a query to the agent from this session. */
24
- submit(query: string): void;
25
- /** The surface this session renders to. */
26
- readonly surface: import("./utils/compositor.js").RenderSurface;
27
- /** Whether this session is currently active. */
28
- readonly active: boolean;
29
- /** Tear down — restores all routing and advisors. */
30
- close(): void;
31
- }
32
- /** A model entry in the cycling list, optionally tied to a provider. */
33
- export interface AgentMode {
34
- model: string;
35
- /** Provider id — when cycling changes provider, LlmClient is reconfigured. */
36
- provider?: string;
37
- /** Provider-specific config for reconfiguring LlmClient on switch. */
38
- providerConfig?: {
39
- apiKey: string;
40
- baseURL?: string;
41
- };
42
- /** Context window size in tokens (for usage display). */
43
- contextWindow?: number;
44
- /** Max output tokens for this mode. */
45
- maxTokens?: number;
46
- /** Model supports reasoning/thinking tokens. */
47
- reasoning?: boolean;
48
- /** Provider supports the reasoning_effort parameter. */
49
- supportsReasoningEffort?: boolean;
50
- /** Echo reasoning_content back on assistant turns. Required by DeepSeek;
51
- * default off (leaky shims may forward it to the model as OOD input). */
52
- echoReasoning?: boolean;
53
- buildReasoningParams?: (level: string) => Record<string, unknown>;
54
- }
55
- /**
56
- * Backend-agnostic LLM interface exposed via `ctx.llm`. Backends fulfill it
57
- * by defining an `llm:invoke` handler; those without an LLM leave
58
- * `available` false and calls reject.
59
- */
60
- export interface LlmMessage {
61
- role: "system" | "user" | "assistant";
62
- content: string;
63
- }
64
- export interface LlmSession {
65
- send(message: string): Promise<string>;
66
- history(): ReadonlyArray<LlmMessage>;
67
- }
68
- export interface LlmInterface {
69
- readonly available: boolean;
70
- /** `model` overrides the globally-configured model for this call only.
71
- * Provider-specific identifier (e.g. "claude-haiku-4-5"). When omitted,
72
- * the active provider's configured default is used.
73
- *
74
- * `reasoningEffort` controls thinking-model token allocation between
75
- * reasoning and final content (e.g. "low", "medium", "high", or
76
- * provider-specific). For non-reasoning models it is ignored. Set to
77
- * "low" for cheap structured-output calls so reasoning doesn't exhaust
78
- * the max-tokens budget and leave content empty. */
79
- ask(opts: {
80
- query: string;
81
- system?: string;
82
- maxTokens?: number;
83
- model?: string;
84
- reasoningEffort?: string;
85
- }): Promise<string>;
86
- session(opts?: {
87
- system?: string;
88
- maxTokens?: number;
89
- model?: string;
90
- reasoningEffort?: string;
91
- }): LlmSession;
92
- }
93
- export interface AgentShellConfig {
94
- shell?: string;
95
- model?: string;
96
- extensions?: string[];
97
- /** API key for OpenAI-compatible provider. */
98
- apiKey?: string;
99
- /** Base URL for OpenAI-compatible API. */
100
- baseURL?: string;
101
- /** Named provider to use from settings.json. */
102
- provider?: string;
103
- /** Override settings.defaultBackend for this session only (does not persist). */
104
- backend?: string;
105
- /** Conversation history backend. Defaults to the on-disk HistoryFile. */
106
- history?: HistoryAdapter;
107
- }
108
- /**
109
- * Context passed to user/third-party extensions.
110
- * Extensions interact with the system through the event bus — no direct
111
- * frontend (Shell/TUI) dependencies. This enables headless, web, or
112
- * alternative frontends without changing extensions.
113
- */
114
- export interface ExtensionContext {
115
- bus: EventBus;
116
- /** Stable per-instance identifier (4-char hex). */
117
- readonly instanceId: string;
118
- quit: () => void;
119
- /** Override color palette slots for theming. */
120
- setPalette: (overrides: Partial<ColorPalette>) => void;
121
- /** Register a delimiter-based content transform (e.g. $$...$$ → image). */
122
- createBlockTransform: (opts: BlockTransformOptions) => void;
123
- /** Register a fenced block transform (e.g. ```lang...``` → code-block). */
124
- createFencedBlockTransform: (opts: FencedBlockTransformOptions) => void;
125
- /** Read extension-namespaced settings from ~/.agent-sh/settings.json. */
126
- getExtensionSettings: <T extends Record<string, unknown>>(namespace: string, defaults: T) => T;
127
- /**
128
- * Get (and lazily create) a per-extension storage directory under
129
- * ~/.agent-sh/<namespace>/. Returns the absolute path. Lets extensions
130
- * persist state without each one re-deriving the location.
131
- */
132
- getStoragePath: (namespace: string) => string;
133
- /** Register a slash command available in any input mode. */
134
- registerCommand: (name: string, description: string, handler: (args: string) => Promise<void> | void) => void;
135
- /** Register a tool for the built-in agent. No-op when using bridge backends. */
136
- registerTool: (tool: ToolDefinition) => void;
137
- /** Unregister a tool by name. */
138
- unregisterTool: (name: string) => void;
139
- /** Get all registered tools (for subagent tool subsets). Returns [] when using bridge backends. */
140
- getTools: () => ToolDefinition[];
141
- /** Register a named instruction block for the agent's system prompt. */
142
- registerInstruction: (name: string, text: string) => void;
143
- /** Remove a named instruction block from the system prompt. */
144
- removeInstruction: (name: string) => void;
145
- /** Register a skill (on-demand reference material) for the agent. */
146
- registerSkill: (name: string, description: string, filePath: string) => void;
147
- /** Remove a registered skill by name. */
148
- removeSkill: (name: string) => void;
149
- /**
150
- * Register a context producer — a function that contributes a string
151
- * (or `null` to skip) into one of two lifecycles:
152
- *
153
- * - `mode: "per-request"` (default) — fires on **every LLM request**,
154
- * including each tool-loop iteration. Output is ephemerally wrapped
155
- * in `<dynamic_context>` onto the trailing message at request time;
156
- * never persisted. Use for "current state" signals (in-flight work,
157
- * active mode, threshold warnings).
158
- *
159
- * - `mode: "per-query"` — fires **once at user-query start** in
160
- * handleQuery. Output is wrapped in `<query_context>` and frozen into
161
- * the user message; persists in conversation history. Use for
162
- * "what happened between turns" signals (shell events, accumulated
163
- * notifications, calendar/inbox deltas).
164
- *
165
- * In both modes producers run in registration order, non-null outputs
166
- * joined with blank lines. When nothing contributes, no envelope tag
167
- * is emitted.
168
- *
169
- * Returns a dispose fn that unregisters the producer.
170
- */
171
- registerContextProducer: (name: string, producer: () => string | null, opts?: {
172
- mode?: "per-request" | "per-query";
173
- }) => () => void;
174
- providers: {
175
- configure: (id: string, opts: {
176
- reasoningParams?: (level: string, model?: string) => Record<string, unknown>;
177
- }) => void;
178
- };
179
- llm: LlmInterface;
180
- /** Register a named handler. */
181
- define: (name: string, fn: (...args: any[]) => any) => void;
182
- /** Wrap a named handler. Receives `next` (original) + args. Returns an unadvise function. */
183
- advise: (name: string, wrapper: (next: (...args: any[]) => any, ...args: any[]) => any) => () => void;
184
- /** Call a named handler. */
185
- call: (name: string, ...args: any[]) => any;
186
- /** Names of all registered handlers — for diagnostic / introspection use. */
187
- list: () => string[];
188
- /**
189
- * Routes named render streams ("agent", "query", "status") to surfaces.
190
- * Extensions use `compositor.redirect()` to capture output (e.g. overlay panels).
191
- */
192
- compositor: Compositor;
193
- /** Teardown callback fired on /reload. For resources the scoped context
194
- * can't track: process listeners, timers, watchers, sockets. */
195
- onDispose: (fn: () => void) => void;
196
- /**
197
- * Create a remote session that routes agent output to a surface and
198
- * optionally accepts queries. Handles all compositor routing, shell
199
- * lifecycle advisors, and chrome suppression.
200
- *
201
- * const session = ctx.createRemoteSession({ surface });
202
- * session.submit("what's on screen?");
203
- * session.close(); // restores everything
204
- */
205
- createRemoteSession: (opts: RemoteSessionOptions) => RemoteSession;
206
- }
207
- /**
208
- * Configuration for a registered input mode.
209
- * Extensions emit "input-mode:register" with this shape to add new modes.
210
- */
211
- export interface InputModeConfig {
212
- id: string;
213
- trigger: string;
214
- label: string;
215
- promptIcon: string;
216
- indicator: string;
217
- onSubmit(query: string, bus: EventBus): void;
218
- returnToSelf: boolean;
219
- }
220
- export interface TerminalSession {
221
- id: string;
222
- command: string;
223
- output: string;
224
- exitCode: number | null;
225
- done: boolean;
226
- resolve?: (value: void) => void;
227
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes