agent-sh 0.14.8 → 0.14.10

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 (58) hide show
  1. package/dist/agent/agent-loop.d.ts +0 -4
  2. package/dist/agent/agent-loop.js +8 -166
  3. package/dist/agent/entry-format.d.ts +5 -0
  4. package/dist/agent/entry-format.js +9 -0
  5. package/dist/agent/extensions/rolling-history/constants.d.ts +1 -0
  6. package/dist/agent/extensions/rolling-history/constants.js +1 -0
  7. package/dist/agent/extensions/rolling-history/index.d.ts +4 -0
  8. package/dist/agent/extensions/rolling-history/index.js +203 -0
  9. package/dist/agent/extensions/rolling-history/recall.d.ts +4 -0
  10. package/dist/agent/extensions/rolling-history/recall.js +122 -0
  11. package/dist/agent/extensions/rolling-history/strategy.d.ts +70 -0
  12. package/dist/agent/extensions/rolling-history/strategy.js +336 -0
  13. package/dist/agent/host-types.d.ts +0 -3
  14. package/dist/agent/index.js +50 -5
  15. package/dist/agent/live-view.d.ts +57 -0
  16. package/dist/agent/live-view.js +238 -0
  17. package/dist/agent/llm-client.d.ts +1 -0
  18. package/dist/agent/llm-client.js +1 -1
  19. package/dist/agent/providers/ollama.d.ts +11 -0
  20. package/dist/agent/providers/ollama.js +72 -0
  21. package/dist/agent/providers/opencode.d.ts +10 -0
  22. package/dist/agent/providers/opencode.js +112 -0
  23. package/dist/agent/providers/zai-coding-plan.d.ts +5 -0
  24. package/dist/agent/providers/zai-coding-plan.js +26 -0
  25. package/dist/agent/session-store.d.ts +90 -0
  26. package/dist/agent/session-store.js +288 -0
  27. package/dist/agent/store.d.ts +74 -0
  28. package/dist/agent/store.js +284 -0
  29. package/dist/agent/subagent.js +2 -2
  30. package/dist/agent/tool-protocol.d.ts +11 -11
  31. package/dist/cli/args.js +2 -2
  32. package/dist/cli/index.js +4 -2
  33. package/dist/core/index.d.ts +0 -1
  34. package/dist/core/index.js +0 -1
  35. package/dist/core/settings.d.ts +5 -1
  36. package/dist/core/settings.js +62 -1
  37. package/dist/extensions/index.d.ts +1 -0
  38. package/dist/shell/events.d.ts +1 -0
  39. package/dist/shell/input-handler.js +4 -0
  40. package/dist/shell/tui-renderer.js +5 -2
  41. package/dist/utils/diff-renderer.js +9 -7
  42. package/examples/extensions/ads/index.ts +695 -0
  43. package/examples/extensions/ash-acp-bridge/src/index.ts +1 -2
  44. package/examples/extensions/ash-scheme/index.ts +77 -3
  45. package/examples/extensions/ashi/package.json +2 -2
  46. package/examples/extensions/ashi/src/capture.ts +1 -1
  47. package/examples/extensions/ashi/src/cli.ts +5 -6
  48. package/examples/extensions/ashi/src/compaction.ts +6 -2
  49. package/examples/extensions/ashi/src/frontend.ts +13 -13
  50. package/examples/extensions/ashi/src/multi-session-store.ts +35 -12
  51. package/examples/extensions/ashi/src/session-commands.ts +1 -1
  52. package/examples/extensions/ashi/src/user-shell-intents.ts +17 -0
  53. package/package.json +13 -1
  54. package/dist/agent/conversation-state.d.ts +0 -142
  55. package/dist/agent/conversation-state.js +0 -788
  56. package/dist/agent/history-file.d.ts +0 -81
  57. package/dist/agent/history-file.js +0 -271
  58. package/examples/extensions/ashi/src/session-store.ts +0 -363
@@ -0,0 +1,238 @@
1
+ import { stripMeta } from "./llm-client.js";
2
+ export class LiveView {
3
+ messages = [];
4
+ messagesDirty = true;
5
+ cachedMessagesJson = null;
6
+ instanceId;
7
+ handlers;
8
+ lastApiTokenCount = null;
9
+ lastApiMessageCount = 0;
10
+ // Mid-tool-pair user/system messages are buffered and flushed after
11
+ // the trailing tool_result — splicing into the gap breaks
12
+ // reasoning_content pairing on strict providers.
13
+ pendingMessages = [];
14
+ constructor(handlers, instanceId = "0000") {
15
+ this.handlers = handlers ?? null;
16
+ this.instanceId = instanceId;
17
+ }
18
+ getMessagesJson() {
19
+ if (this.messagesDirty || this.cachedMessagesJson === null) {
20
+ this.cachedMessagesJson = JSON.stringify(this.messages);
21
+ this.messagesDirty = false;
22
+ }
23
+ return this.cachedMessagesJson;
24
+ }
25
+ invalidateMessagesCache() {
26
+ this.messagesDirty = true;
27
+ this.cachedMessagesJson = null;
28
+ }
29
+ addUserMessage(text) {
30
+ this.messages.push({ role: "user", content: text });
31
+ this.invalidateMessagesCache();
32
+ }
33
+ addAssistantMessage(content, toolCalls, extras) {
34
+ const hasToolCalls = !!toolCalls?.length;
35
+ // Promote reasoning into content on reasoning-only turns; strict
36
+ // providers (DeepSeek native) reject content="" with no tool_calls.
37
+ if (!content && !hasToolCalls) {
38
+ const r = (extras?.reasoning_content ?? extras?.reasoning);
39
+ if (typeof r === "string" && r)
40
+ content = r;
41
+ }
42
+ if (!content && !hasToolCalls)
43
+ return;
44
+ const base = {
45
+ role: "assistant",
46
+ content: hasToolCalls ? (content ?? null) : content,
47
+ };
48
+ if (hasToolCalls) {
49
+ base.tool_calls = toolCalls.map((tc) => ({
50
+ id: tc.id,
51
+ type: "function",
52
+ function: tc.function,
53
+ }));
54
+ }
55
+ if (extras)
56
+ Object.assign(base, extras);
57
+ this.messages.push(base);
58
+ this.invalidateMessagesCache();
59
+ }
60
+ addToolResult(toolCallId, content, isError = false) {
61
+ if (typeof content === "string") {
62
+ this.messages.push({ role: "tool", tool_call_id: toolCallId, content });
63
+ }
64
+ else {
65
+ const parts = [];
66
+ for (const img of content) {
67
+ parts.push({ type: "image_url", image_url: { url: `data:${img.mimeType};base64,${img.data}` } });
68
+ }
69
+ const label = isError ? `Error: [${content.length} image(s)]` : `[${content.length} image(s)]`;
70
+ parts.unshift({ type: "text", text: label });
71
+ this.messages.push({ role: "tool", tool_call_id: toolCallId, content: parts });
72
+ }
73
+ this.invalidateMessagesCache();
74
+ this.flushPendingMessages();
75
+ }
76
+ addToolResultInline(content) {
77
+ this.messages.push({ role: "user", content });
78
+ this.invalidateMessagesCache();
79
+ this.flushPendingMessages();
80
+ }
81
+ /** Safe from any context: queues if mid-tool-pair, appends otherwise. */
82
+ addSystemNote(text) {
83
+ if (this.hasOpenToolCalls()) {
84
+ this.pendingMessages.push({ kind: "system", text });
85
+ return;
86
+ }
87
+ this.messages.push({ role: "user", content: text });
88
+ this.invalidateMessagesCache();
89
+ }
90
+ appendUserMessage(text) {
91
+ if (this.hasOpenToolCalls()) {
92
+ this.pendingMessages.push({ kind: "user", text });
93
+ return;
94
+ }
95
+ this.addUserMessage(text);
96
+ }
97
+ hasOpenToolCalls() {
98
+ for (let i = this.messages.length - 1; i >= 0; i--) {
99
+ const msg = this.messages[i];
100
+ if (msg.role === "tool")
101
+ continue;
102
+ if (msg.role !== "assistant")
103
+ return false;
104
+ if (!("tool_calls" in msg) || !msg.tool_calls)
105
+ return false;
106
+ const answered = new Set();
107
+ for (let j = i + 1; j < this.messages.length; j++) {
108
+ const m = this.messages[j];
109
+ if (m.role !== "tool")
110
+ break;
111
+ answered.add(m.tool_call_id);
112
+ }
113
+ return msg.tool_calls.some((tc) => !answered.has(tc.id));
114
+ }
115
+ return false;
116
+ }
117
+ flushPendingMessages() {
118
+ if (this.pendingMessages.length === 0)
119
+ return;
120
+ if (this.hasOpenToolCalls())
121
+ return;
122
+ const pending = this.pendingMessages;
123
+ this.pendingMessages = [];
124
+ for (const m of pending) {
125
+ if (m.kind === "user") {
126
+ this.addUserMessage(m.text);
127
+ }
128
+ else {
129
+ this.messages.push({ role: "user", content: m.text });
130
+ }
131
+ }
132
+ this.invalidateMessagesCache();
133
+ }
134
+ getMessages() {
135
+ return this.normalizeReasoningConsistency(this.stubDanglingToolCalls(this.dropOrphanToolMessages(this.messages)));
136
+ }
137
+ get() {
138
+ return this.messages;
139
+ }
140
+ forLLM() {
141
+ return this.getMessages().map(stripMeta);
142
+ }
143
+ replace(msgs) {
144
+ this.replaceMessages(msgs);
145
+ }
146
+ link(index, entryId) {
147
+ const m = this.messages[index];
148
+ if (!m)
149
+ throw new Error(`LiveView.link: no message at index ${index}`);
150
+ const am = m;
151
+ am.meta = { ...am.meta, entryId };
152
+ }
153
+ /** DeepSeek 400s on tool messages without a matching tool_call;
154
+ * compaction can leave such orphans. */
155
+ dropOrphanToolMessages(messages) {
156
+ const knownIds = new Set();
157
+ const result = [];
158
+ for (const msg of messages) {
159
+ if (msg.role === "assistant" && "tool_calls" in msg && msg.tool_calls) {
160
+ for (const tc of msg.tool_calls)
161
+ knownIds.add(tc.id);
162
+ }
163
+ if (msg.role === "tool" && !knownIds.has(msg.tool_call_id)) {
164
+ continue;
165
+ }
166
+ result.push(msg);
167
+ }
168
+ return result;
169
+ }
170
+ /** Stub missing tool results after a mid-execution interrupt so
171
+ * DeepSeek doesn't 400 on dangling tool_calls. */
172
+ stubDanglingToolCalls(messages) {
173
+ const result = [];
174
+ let i = 0;
175
+ while (i < messages.length) {
176
+ const msg = messages[i];
177
+ result.push(msg);
178
+ i++;
179
+ if (msg.role !== "assistant" || !("tool_calls" in msg) || !msg.tool_calls)
180
+ continue;
181
+ const seen = new Set();
182
+ while (i < messages.length && messages[i].role === "tool") {
183
+ const t = messages[i];
184
+ seen.add(t.tool_call_id);
185
+ result.push(t);
186
+ i++;
187
+ }
188
+ for (const tc of msg.tool_calls) {
189
+ if (!seen.has(tc.id)) {
190
+ result.push({ role: "tool", tool_call_id: tc.id, content: "[cancelled]" });
191
+ }
192
+ }
193
+ }
194
+ return result;
195
+ }
196
+ /** DeepSeek 400s if any assistant in a thinking-mode conversation
197
+ * is missing `reasoning_content`. Cross-alias `reasoning` (from
198
+ * OpenRouter) and stub gaps with "". */
199
+ normalizeReasoningConsistency(messages) {
200
+ const needsNormalize = messages.some((m) => m.role === "assistant" && (m.reasoning !== undefined ||
201
+ m.reasoning_content !== undefined ||
202
+ m.reasoning_details !== undefined));
203
+ if (!needsNormalize)
204
+ return messages;
205
+ return messages.map((m) => {
206
+ if (m.role !== "assistant")
207
+ return m;
208
+ const a = m;
209
+ if (a.reasoning_content !== undefined)
210
+ return m;
211
+ return { ...m, reasoning_content: a.reasoning ?? "" };
212
+ });
213
+ }
214
+ /** Invalidates the API token baseline since the new array's count is unknown. */
215
+ replaceMessages(messages) {
216
+ this.messages = messages;
217
+ this.invalidateMessagesCache();
218
+ this.lastApiTokenCount = null;
219
+ this.lastApiMessageCount = 0;
220
+ this.flushPendingMessages();
221
+ }
222
+ updateApiTokenCount(promptTokens) {
223
+ this.lastApiTokenCount = promptTokens;
224
+ this.lastApiMessageCount = this.messages.length;
225
+ }
226
+ estimatePromptTokens() {
227
+ if (this.lastApiTokenCount === null)
228
+ return this.estimateTokens();
229
+ const trailing = this.messages.length - this.lastApiMessageCount;
230
+ if (trailing <= 0)
231
+ return this.lastApiTokenCount;
232
+ const trailingMessages = this.messages.slice(this.lastApiMessageCount);
233
+ return this.lastApiTokenCount + Math.ceil(JSON.stringify(trailingMessages).length / 4);
234
+ }
235
+ estimateTokens() {
236
+ return Math.ceil(this.getMessagesJson().length / 4);
237
+ }
238
+ }
@@ -11,6 +11,7 @@ export type { ChatCompletionMessageParam, ChatCompletionTool };
11
11
  export type AgentShMessage = ChatCompletionMessageParam & {
12
12
  meta?: Record<string, unknown>;
13
13
  };
14
+ export declare function stripMeta(m: ChatCompletionMessageParam): ChatCompletionMessageParam;
14
15
  export interface LlmClientConfig {
15
16
  apiKey: string;
16
17
  baseURL?: string;
@@ -6,7 +6,7 @@
6
6
  * (command suggestions, completions).
7
7
  */
8
8
  import OpenAI from "openai";
9
- function stripMeta(m) {
9
+ export function stripMeta(m) {
10
10
  if (!("meta" in m))
11
11
  return m;
12
12
  const { meta: _meta, ...rest } = m;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Ollama provider — local daemon or Ollama Cloud.
3
+ *
4
+ * Cloud auth: agent-sh auth login ollama-cloud
5
+ * Local host: OLLAMA_HOST (default http://localhost:11434)
6
+ *
7
+ * Catalog comes from /api/tags; per-model context length is fetched
8
+ * from /api/show. Chat goes through the OpenAI-compatible /v1 shim.
9
+ */
10
+ import type { AgentContext } from "../host-types.js";
11
+ export default function activate(ctx: AgentContext): void;
@@ -0,0 +1,72 @@
1
+ import { resolveApiKey } from "../../cli/auth/keys.js";
2
+ const ECHO_REASONING_PATTERNS = [/deepseek/i];
3
+ export default function activate(ctx) {
4
+ const cloudKey = resolveApiKey("ollama-cloud").key ?? process.env.OLLAMA_API_KEY;
5
+ const host = cloudKey
6
+ ? "https://ollama.com"
7
+ : (process.env.OLLAMA_HOST ?? "http://localhost:11434").replace(/\/$/, "");
8
+ const id = cloudKey ? "ollama-cloud" : "ollama";
9
+ const sdkKey = cloudKey || "no-key";
10
+ const noAuth = !cloudKey;
11
+ const baseURL = `${host}/v1`;
12
+ const headers = {};
13
+ if (cloudKey)
14
+ headers.Authorization = `Bearer ${cloudKey}`;
15
+ ctx.agent.providers.configure(id, {
16
+ reasoningParams: (level) => {
17
+ if (level === "off")
18
+ return { reasoning_effort: "none" };
19
+ return { reasoning_effort: level === "xhigh" ? "high" : level };
20
+ },
21
+ });
22
+ ctx.agent.providers.register({ id, apiKey: sdkKey, baseURL, models: [], noAuth });
23
+ fetchCatalog(host, headers).then((models) => {
24
+ if (models.length === 0)
25
+ return;
26
+ ctx.agent.providers.register({
27
+ id,
28
+ apiKey: sdkKey,
29
+ baseURL,
30
+ defaultModel: models[0].id,
31
+ models,
32
+ noAuth,
33
+ });
34
+ }).catch(() => { });
35
+ }
36
+ async function fetchCatalog(host, headers) {
37
+ const tagsRes = await fetch(`${host}/api/tags`, { headers });
38
+ if (!tagsRes.ok)
39
+ return [];
40
+ const tagsData = await tagsRes.json();
41
+ const names = (tagsData.models ?? []).map((m) => m.name);
42
+ if (names.length === 0)
43
+ return [];
44
+ const ctxs = await Promise.all(names.map((name) => fetchContextLength(host, headers, name).catch(() => undefined)));
45
+ return names.map((name, i) => ({
46
+ id: name,
47
+ contextWindow: ctxs[i],
48
+ echoReasoning: ECHO_REASONING_PATTERNS.some((re) => re.test(name)),
49
+ }));
50
+ }
51
+ async function fetchContextLength(host, headers, name) {
52
+ const res = await fetch(`${host}/api/show`, {
53
+ method: "POST",
54
+ headers: { ...headers, "Content-Type": "application/json" },
55
+ body: JSON.stringify({ name }),
56
+ });
57
+ if (!res.ok)
58
+ return undefined;
59
+ const data = await res.json();
60
+ const info = data.model_info ?? {};
61
+ const arch = info["general.architecture"];
62
+ if (arch) {
63
+ const ctx = info[`${arch}.context_length`];
64
+ if (typeof ctx === "number")
65
+ return ctx;
66
+ }
67
+ for (const [k, v] of Object.entries(info)) {
68
+ if (k.endsWith(".context_length") && typeof v === "number")
69
+ return v;
70
+ }
71
+ return undefined;
72
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OpenCode Zen & Go providers — runtime model discovery via /models +
3
+ * models.dev metadata enrichment.
4
+ *
5
+ * Registers two providers:
6
+ * - opencode — Zen tier (https://opencode.ai/zen/v1)
7
+ * - opencode-go — Go tier (https://opencode.ai/zen/go/v1)
8
+ */
9
+ import type { AgentContext } from "../host-types.js";
10
+ export default function activate(ctx: AgentContext): void;
@@ -0,0 +1,112 @@
1
+ import { resolveApiKey } from "../../cli/auth/keys.js";
2
+ const ZEN_BASE_URL = "https://opencode.ai/zen/v1";
3
+ const GO_BASE_URL = "https://opencode.ai/zen/go/v1";
4
+ const MODELS_DEV_ENDPOINT = "https://models.dev/api.json";
5
+ const DEFAULT_CTX = 128_000;
6
+ const DEFAULT_MAX_TOKENS = 16_384;
7
+ const ZEN_FALLBACK = ["claude-sonnet-4-6"];
8
+ const GO_FALLBACK = ["gpt-5.2"];
9
+ // ── Helpers ──────────────────────────────────────────────────────
10
+ async function fetchJson(url) {
11
+ const res = await fetch(url, {
12
+ headers: { Accept: "application/json" },
13
+ signal: AbortSignal.timeout(15_000),
14
+ });
15
+ if (!res.ok)
16
+ throw new Error(`HTTP ${res.status}`);
17
+ return res.json();
18
+ }
19
+ function findEntry(provider, id) {
20
+ const direct = provider?.models?.[id];
21
+ if (direct)
22
+ return direct;
23
+ if (!provider?.models)
24
+ return undefined;
25
+ return Object.values(provider.models).find((m) => m.id === id);
26
+ }
27
+ function resolveModel(id, meta) {
28
+ const raw = meta?.modalities?.input;
29
+ const modalities = Array.isArray(raw)
30
+ ? raw.filter((v) => v === "text" || v === "image")
31
+ : ["text"];
32
+ return {
33
+ id,
34
+ reasoning: meta?.reasoning ?? false,
35
+ contextWindow: (typeof meta?.limit?.context === "number" && meta.limit.context > 0)
36
+ ? meta.limit.context : DEFAULT_CTX,
37
+ maxTokens: (typeof meta?.limit?.output === "number" && meta.limit.output > 0)
38
+ ? meta.limit.output : DEFAULT_MAX_TOKENS,
39
+ modalities,
40
+ };
41
+ }
42
+ function reasoningParams(level) {
43
+ if (level === "off")
44
+ return { reasoning_effort: "none" };
45
+ return { reasoning_effort: level === "xhigh" ? "high" : level };
46
+ }
47
+ // ── Activation ───────────────────────────────────────────────────
48
+ export default function activate(ctx) {
49
+ const apiKey = process.env.OPENCODE_API_KEY ??
50
+ resolveApiKey("opencode").key ?? undefined;
51
+ ctx.agent.providers.configure("opencode", { reasoningParams });
52
+ ctx.agent.providers.register({
53
+ id: "opencode", apiKey, baseURL: ZEN_BASE_URL,
54
+ defaultModel: ZEN_FALLBACK[0], models: ZEN_FALLBACK,
55
+ supportsReasoningEffort: true,
56
+ });
57
+ ctx.agent.providers.configure("opencode-go", { reasoningParams });
58
+ ctx.agent.providers.register({
59
+ id: "opencode-go", apiKey, baseURL: GO_BASE_URL,
60
+ defaultModel: GO_FALLBACK[0], models: GO_FALLBACK,
61
+ supportsReasoningEffort: true,
62
+ });
63
+ if (!apiKey)
64
+ return;
65
+ fetchModelsDev()
66
+ .then(async (md) => {
67
+ const zenIds = await fetchModelIds(ZEN_BASE_URL);
68
+ const goIds = await fetchModelIds(GO_BASE_URL);
69
+ const resolve = (ids, prov, fb) => (ids.length > 0 ? ids : fb).map((id) => resolveModel(id, findEntry(prov, id)));
70
+ const zen = resolve(zenIds, md?.opencode, ZEN_FALLBACK);
71
+ const go = resolve(goIds, md?.["opencode-go"], GO_FALLBACK);
72
+ ctx.agent.providers.register({
73
+ id: "opencode", apiKey, baseURL: ZEN_BASE_URL,
74
+ defaultModel: zen[0]?.id ?? ZEN_FALLBACK[0], models: zen,
75
+ supportsReasoningEffort: true,
76
+ });
77
+ ctx.agent.providers.register({
78
+ id: "opencode-go", apiKey, baseURL: GO_BASE_URL,
79
+ defaultModel: go[0]?.id ?? GO_FALLBACK[0], models: go,
80
+ supportsReasoningEffort: true,
81
+ });
82
+ })
83
+ .catch(() => { });
84
+ }
85
+ async function fetchModelsDev() {
86
+ try {
87
+ const payload = await fetchJson(MODELS_DEV_ENDPOINT);
88
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
89
+ return undefined;
90
+ return payload;
91
+ }
92
+ catch {
93
+ return undefined;
94
+ }
95
+ }
96
+ async function fetchModelIds(baseURL) {
97
+ try {
98
+ const res = await fetch(`${baseURL}/models`, {
99
+ headers: { Accept: "application/json" },
100
+ signal: AbortSignal.timeout(10_000),
101
+ });
102
+ if (!res.ok)
103
+ return [];
104
+ const payload = await res.json();
105
+ if (!Array.isArray(payload.data))
106
+ return [];
107
+ return [...new Set(payload.data.map((e) => e.id).filter(Boolean))];
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Z.AI Coding Plan — Zhipu AI's subscription GLM models for coding.
3
+ */
4
+ import type { AgentContext } from "../host-types.js";
5
+ export default function activate(ctx: AgentContext): void;
@@ -0,0 +1,26 @@
1
+ import { resolveApiKey } from "../../cli/auth/keys.js";
2
+ const BASE_URL = "https://api.z.ai/api/coding/paas/v4";
3
+ const ID = "zai-coding-plan";
4
+ const DEFAULT_MODELS = [
5
+ { id: "glm-5.1", reasoning: true, contextWindow: 200_000 },
6
+ { id: "glm-5-turbo", reasoning: true, contextWindow: 200_000 },
7
+ { id: "glm-4.7", reasoning: true, contextWindow: 204_800 },
8
+ { id: "glm-4.5-air", reasoning: true, contextWindow: 131_072 },
9
+ ];
10
+ function buildReasoningParams(level, _model) {
11
+ if (level === "off")
12
+ return { thinking: { type: "disabled" } };
13
+ const effort = level === "xhigh" ? "high" : level;
14
+ return { thinking: { type: "enabled" }, reasoning_effort: effort };
15
+ }
16
+ export default function activate(ctx) {
17
+ const { key } = resolveApiKey(ID);
18
+ ctx.agent.providers.configure(ID, { reasoningParams: buildReasoningParams });
19
+ ctx.agent.providers.register({
20
+ id: ID,
21
+ apiKey: key ?? process.env.ZAI_API_KEY ?? process.env.ZHIPU_API_KEY,
22
+ baseURL: BASE_URL,
23
+ defaultModel: DEFAULT_MODELS[0].id,
24
+ models: DEFAULT_MODELS,
25
+ });
26
+ }
@@ -0,0 +1,90 @@
1
+ import type { AgentShMessage } from "./llm-client.js";
2
+ export type { AgentShMessage } from "./llm-client.js";
3
+ export interface SessionHeaderEntry {
4
+ type: "session";
5
+ id: string;
6
+ parentId: null;
7
+ timestamp: number;
8
+ cwd: string;
9
+ version: 1;
10
+ }
11
+ export interface MessageEntry {
12
+ type: "message";
13
+ id: string;
14
+ parentId: string;
15
+ timestamp: number;
16
+ message: AgentShMessage;
17
+ }
18
+ export interface CompactionEntry {
19
+ type: "compaction";
20
+ id: string;
21
+ parentId: string;
22
+ timestamp: number;
23
+ firstKeptId: string;
24
+ tokensBefore: number;
25
+ summary?: string;
26
+ }
27
+ /** Omitted from buildMessages — the agent already saw it via <shell_events>. */
28
+ export interface ShellExchangeEntry {
29
+ type: "shell-exchange";
30
+ id: string;
31
+ parentId: string;
32
+ timestamp: number;
33
+ command: string;
34
+ output: string;
35
+ exitCode: number | null;
36
+ cwd?: string;
37
+ private?: boolean;
38
+ }
39
+ export type SessionEntry = SessionHeaderEntry | MessageEntry | CompactionEntry | ShellExchangeEntry;
40
+ export declare function newEntryId(): string;
41
+ export declare function summarizeMessage(m: AgentShMessage): string;
42
+ /** For displayed user text. Loops because both wrappers can stack at the head. */
43
+ export declare function stripContextWrappers(content: string): string;
44
+ export declare function renderEvictedSummary(evicted: AgentShMessage[]): string;
45
+ export declare class SessionStore {
46
+ private entriesPath;
47
+ private leafPath;
48
+ private entries;
49
+ private rootId;
50
+ private activeLeaf;
51
+ private pendingHeader;
52
+ readonly id: string;
53
+ constructor(filePath: string, opts?: {
54
+ create?: {
55
+ cwd: string;
56
+ sessionId: string;
57
+ };
58
+ });
59
+ /** Deferred so an opened-but-unused session leaves no files on disk. */
60
+ private flushHeader;
61
+ getActiveLeaf(): string;
62
+ setActiveLeaf(id: string): void;
63
+ getRootId(): string;
64
+ getEntry(id: string): SessionEntry | undefined;
65
+ getAllEntries(): SessionEntry[];
66
+ appendMessages(messages: AgentShMessage[]): Promise<string[]>;
67
+ appendShellExchange(e: {
68
+ command: string;
69
+ output: string;
70
+ exitCode: number | null;
71
+ cwd?: string;
72
+ private?: boolean;
73
+ }): Promise<string>;
74
+ appendCompaction(firstKeptId: string, tokensBefore: number, summary?: string): Promise<string>;
75
+ /** Returns oldest-first. */
76
+ getBranch(leafId?: string): SessionEntry[];
77
+ /** Latest compaction on the branch replaces the evicted prefix with
78
+ * its stored summary, or a rendered one if no summary was stored. */
79
+ buildMessages(leafId?: string): AgentShMessage[];
80
+ /** Parallel entryIds array — null for the synthetic compaction-summary
81
+ * slot — so callers can map message indices back to on-disk ids. */
82
+ buildBranchWithIds(leafId?: string): {
83
+ messages: AgentShMessage[];
84
+ entryIds: (string | null)[];
85
+ };
86
+ getPreview(): string;
87
+ private load;
88
+ private lastEntryId;
89
+ private persistLeaf;
90
+ }