@apolloyh/apollo-agent 0.1.10 → 0.2.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.
@@ -0,0 +1,214 @@
1
+ import { ProviderApiError, MalformedStreamError, MalformedToolInputError, } from "./anthropic.js";
2
+ export class OpenAIClient {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ async createMessage(input) {
8
+ const response = await fetch(openAIEndpoint(this.config.baseUrl), {
9
+ method: "POST",
10
+ headers: { "content-type": "application/json", authorization: `Bearer ${this.config.authToken}` },
11
+ body: JSON.stringify({
12
+ model: this.config.model,
13
+ messages: toOpenAIMessages(input.system, input.messages),
14
+ [this.config.openaiMaxTokensParam ?? "max_tokens"]: this.config.maxTokens,
15
+ ...(input.tools.length ? { tools: input.tools.map(toOpenAITool), tool_choice: "auto" } : {}),
16
+ stream: input.stream ?? false,
17
+ ...(input.stream ? { stream_options: { include_usage: true } } : {}),
18
+ }),
19
+ signal: input.signal
20
+ ? AbortSignal.any([input.signal, AbortSignal.timeout(normalizeTimeout(this.config.timeoutMs))])
21
+ : AbortSignal.timeout(normalizeTimeout(this.config.timeoutMs)),
22
+ });
23
+ if (!response.ok)
24
+ throw new ProviderApiError(response.status, await response.text(), response.headers);
25
+ return input.stream ? readOpenAIStream(response, input) : normalizeOpenAIResponse(await response.json());
26
+ }
27
+ }
28
+ function openAIEndpoint(baseUrl) {
29
+ const base = baseUrl.replace(/\/$/, "");
30
+ if (/\/chat\/completions$/i.test(base))
31
+ return base;
32
+ return `${base}${/\/v1$/i.test(base) ? "" : "/v1"}/chat/completions`;
33
+ }
34
+ function toOpenAITool(tool) {
35
+ return { type: "function", function: { name: tool.name, description: tool.description, parameters: tool.input_schema } };
36
+ }
37
+ function toOpenAIMessages(system, messages) {
38
+ const output = system ? [{ role: "system", content: system }] : [];
39
+ for (const message of messages) {
40
+ if (typeof message.content === "string") {
41
+ output.push({ role: message.role, content: message.content });
42
+ continue;
43
+ }
44
+ if (message.role === "assistant") {
45
+ const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
46
+ const toolCalls = message.content.filter((block) => block.type === "tool_use").map((block) => ({
47
+ id: block.id,
48
+ type: "function",
49
+ function: { name: block.name, arguments: JSON.stringify(block.input) },
50
+ }));
51
+ output.push({ role: "assistant", content: text || null, ...(toolCalls.length ? { tool_calls: toolCalls } : {}) });
52
+ continue;
53
+ }
54
+ for (const block of message.content) {
55
+ if (block.type === "tool_result")
56
+ output.push({ role: "tool", tool_call_id: block.tool_use_id, content: block.is_error ? `[error] ${block.content}` : block.content });
57
+ }
58
+ const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
59
+ if (text)
60
+ output.push({ role: "user", content: text });
61
+ }
62
+ return output;
63
+ }
64
+ function normalizeOpenAIResponse(value) {
65
+ const source = value;
66
+ const choice = source.choices?.[0];
67
+ if (!choice?.message)
68
+ throw new MalformedStreamError("OpenAI API returned no assistant message.");
69
+ return {
70
+ id: source.id ?? "",
71
+ type: "message",
72
+ role: "assistant",
73
+ content: normalizeOpenAIContent(choice.message),
74
+ stop_reason: normalizeStopReason(choice.finish_reason),
75
+ usage: normalizeUsage(source.usage),
76
+ };
77
+ }
78
+ async function readOpenAIStream(response, input) {
79
+ if (!response.body)
80
+ throw new MalformedStreamError("OpenAI API returned an empty stream body.");
81
+ const reader = response.body.getReader();
82
+ const decoder = new TextDecoder();
83
+ const tools = new Map();
84
+ let buffer = "", id = "", text = "", thinking = "", stopReason, usage, ended = false;
85
+ const apply = (data) => {
86
+ if (!data || data === "[DONE]")
87
+ return;
88
+ let event;
89
+ try {
90
+ event = JSON.parse(data);
91
+ }
92
+ catch (error) {
93
+ throw new MalformedStreamError("OpenAI API returned malformed JSON in the event stream.", error);
94
+ }
95
+ id ||= event.id ?? "";
96
+ usage = normalizeUsage(event.usage) ?? usage;
97
+ const choice = event.choices?.[0];
98
+ const delta = choice?.delta;
99
+ if (choice?.finish_reason)
100
+ stopReason = normalizeStopReason(choice.finish_reason);
101
+ if (!delta)
102
+ return;
103
+ const reasoning = delta.reasoning_content ?? delta.reasoning;
104
+ if (reasoning) {
105
+ thinking += reasoning;
106
+ input.onThinkingDelta?.({ text: reasoning });
107
+ }
108
+ if (delta.content) {
109
+ text += delta.content;
110
+ input.onTextDelta?.(delta.content);
111
+ }
112
+ for (const part of delta.tool_calls ?? []) {
113
+ const index = part.index ?? 0;
114
+ const tool = tools.get(index) ?? { id: "", name: "", arguments: "" };
115
+ const wasUnnamed = !tool.name;
116
+ tool.id ||= part.id ?? "";
117
+ tool.name += part.function?.name ?? "";
118
+ tool.arguments += part.function?.arguments ?? "";
119
+ tools.set(index, tool);
120
+ if (wasUnnamed && tool.name)
121
+ input.onToolInputDelta?.({ bytes: 0, tool: tool.name });
122
+ if (part.function?.arguments)
123
+ input.onToolInputDelta?.({ bytes: tool.arguments.length, tool: tool.name || undefined, detail: partialToolDetail(tool.arguments) });
124
+ }
125
+ };
126
+ try {
127
+ for (;;) {
128
+ const chunk = await reader.read();
129
+ if (chunk.done) {
130
+ ended = true;
131
+ break;
132
+ }
133
+ buffer += decoder.decode(chunk.value, { stream: true });
134
+ let separator = buffer.match(/\r?\n\r?\n/);
135
+ while (separator?.index !== undefined) {
136
+ const raw = buffer.slice(0, separator.index);
137
+ buffer = buffer.slice(separator.index + separator[0].length);
138
+ apply(parseSseData(raw));
139
+ separator = buffer.match(/\r?\n\r?\n/);
140
+ }
141
+ }
142
+ buffer += decoder.decode();
143
+ if (buffer.trim())
144
+ apply(parseSseData(buffer));
145
+ if (!text && !thinking && !tools.size)
146
+ throw new MalformedStreamError("OpenAI API returned an empty or incomplete event stream.");
147
+ const content = [];
148
+ if (thinking)
149
+ content.push({ type: "thinking", thinking });
150
+ if (text)
151
+ content.push({ type: "text", text });
152
+ for (const [, tool] of [...tools].sort(([a], [b]) => a - b))
153
+ content.push(toToolUse(tool));
154
+ return { id, type: "message", role: "assistant", content, stop_reason: stopReason, usage };
155
+ }
156
+ finally {
157
+ if (!ended)
158
+ await reader.cancel().catch(() => undefined);
159
+ reader.releaseLock();
160
+ }
161
+ }
162
+ function normalizeOpenAIContent(message) {
163
+ const content = [];
164
+ const reasoning = message.reasoning_content ?? message.reasoning;
165
+ if (reasoning)
166
+ content.push({ type: "thinking", thinking: reasoning });
167
+ if (message.content)
168
+ content.push({ type: "text", text: message.content });
169
+ for (const [index, call] of (message.tool_calls ?? []).entries())
170
+ content.push(toToolUse({ id: call.id ?? `call-${index}`, name: call.function?.name ?? "", arguments: call.function?.arguments ?? "" }));
171
+ return content;
172
+ }
173
+ function toToolUse(tool) {
174
+ try {
175
+ return { type: "tool_use", id: tool.id, name: tool.name, input: tool.arguments.trim() ? JSON.parse(tool.arguments) : {} };
176
+ }
177
+ catch (error) {
178
+ throw new MalformedToolInputError(tool.name, tool.arguments.length, error);
179
+ }
180
+ }
181
+ function normalizeUsage(value) {
182
+ if (!value || typeof value !== "object")
183
+ return undefined;
184
+ const usage = value;
185
+ if (typeof usage.prompt_tokens !== "number" && typeof usage.completion_tokens !== "number")
186
+ return undefined;
187
+ return { input_tokens: usage.prompt_tokens ?? 0, output_tokens: usage.completion_tokens ?? 0, cache_creation_input_tokens: 0, cache_read_input_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0 };
188
+ }
189
+ function normalizeStopReason(reason) {
190
+ if (reason === "tool_calls" || reason === "function_call")
191
+ return "tool_use";
192
+ if (reason === "length")
193
+ return "max_tokens";
194
+ if (reason === "stop")
195
+ return "end_turn";
196
+ return reason;
197
+ }
198
+ function parseSseData(raw) {
199
+ return raw.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n").trim();
200
+ }
201
+ function partialToolDetail(input) {
202
+ const match = input.slice(0, 8192).match(/"(?:path|file_path|command|query|url|description)"\s*:\s*"((?:\\.|[^"\\])*)"/);
203
+ if (!match?.[1])
204
+ return undefined;
205
+ try {
206
+ return JSON.parse(`"${match[1]}"`);
207
+ }
208
+ catch {
209
+ return undefined;
210
+ }
211
+ }
212
+ function normalizeTimeout(value) {
213
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 300000;
214
+ }
@@ -79,6 +79,14 @@ export declare class QueryEngine {
79
79
  conversationSize(): number;
80
80
  getUsageStats(): UsageStats;
81
81
  getCacheMode(): string;
82
+ getRuntimeInfo(): {
83
+ model: string;
84
+ baseUrl: string;
85
+ maxTokens: number;
86
+ openaiMaxTokensParam: "max_tokens" | "max_completion_tokens" | undefined;
87
+ contextMaxChars: number;
88
+ workspaceRoot: string;
89
+ };
82
90
  close(): Promise<void>;
83
91
  ensureSession(title?: string): Promise<SessionSnapshot>;
84
92
  saveCurrentSession(): Promise<string | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../../src/runtime/query-engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../../src/runtime/query-engine.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { AnthropicApiError, AnthropicClient, MalformedStreamError, } from "../llm/anthropic.js";
2
+ import { AnthropicClient, MalformedStreamError, ProviderApiError, } from "../llm/anthropic.js";
3
+ import { OpenAIClient } from "../llm/openai.js";
3
4
  import { ContextManager, isContextSummary } from "./context.js";
4
5
  import { buildCompactionTranscript, COMPACTION_SYSTEM_PROMPT, normalizeCompactionSummary, } from "./compaction.js";
5
6
  import { createCliApprovalProvider } from "./permissions.js";
@@ -68,7 +69,9 @@ export class QueryEngine {
68
69
  activeTurnAbort;
69
70
  constructor(options) {
70
71
  this.options = options;
71
- this.client = new AnthropicClient(options.llmConfig);
72
+ this.client = options.llmConfig.protocol === "openai"
73
+ ? new OpenAIClient(options.llmConfig)
74
+ : new AnthropicClient(options.llmConfig);
72
75
  this.contextManager = new ContextManager(options.agentConfig.context, options.emit);
73
76
  this.skillManager = new SkillManager(options.agentConfig);
74
77
  this.mcpManager = new McpManager(options.agentConfig);
@@ -133,6 +136,16 @@ export class QueryEngine {
133
136
  }
134
137
  return `Anthropic-compatible (${this.options.llmConfig.cacheTtl ?? "5m"} requested)`;
135
138
  }
139
+ getRuntimeInfo() {
140
+ return {
141
+ model: this.options.llmConfig.model,
142
+ baseUrl: this.options.llmConfig.baseUrl,
143
+ maxTokens: this.options.llmConfig.maxTokens,
144
+ openaiMaxTokensParam: this.options.llmConfig.openaiMaxTokensParam,
145
+ contextMaxChars: this.options.agentConfig.context.maxChars,
146
+ workspaceRoot: this.options.agentConfig.workspaceRoot,
147
+ };
148
+ }
136
149
  async close() {
137
150
  await this.mcpManager.closeAll();
138
151
  }
@@ -526,12 +539,18 @@ export class QueryEngine {
526
539
  streamedText += text;
527
540
  this.options.emit({ type: "assistant_delta", text });
528
541
  };
542
+ const onThinkingDelta = isSubagent
543
+ ? undefined
544
+ : (event) => {
545
+ this.options.emit({ type: "thinking_delta", text: event.text });
546
+ };
529
547
  const buildRequest = (useStream) => ({
530
548
  system: prompt,
531
549
  messages: this.messages,
532
550
  tools: toolDefinitions,
533
551
  stream: useStream,
534
552
  onTextDelta: useStream ? onTextDelta : undefined,
553
+ onThinkingDelta: useStream ? onThinkingDelta : undefined,
535
554
  onToolInputDelta: useStream && !isSubagent
536
555
  ? (event) => {
537
556
  const now = Date.now();
@@ -927,6 +946,7 @@ export class QueryEngine {
927
946
  }
928
947
  configFingerprint() {
929
948
  return fingerprint({
949
+ protocol: this.options.llmConfig.protocol ?? "anthropic",
930
950
  baseUrl: this.options.llmConfig.baseUrl.replace(/\/$/, ""),
931
951
  model: this.options.llmConfig.model,
932
952
  maxTokens: this.options.llmConfig.maxTokens,
@@ -1086,7 +1106,7 @@ function fingerprint(value) {
1086
1106
  return createHash("sha256").update(content).digest("hex").slice(0, 12);
1087
1107
  }
1088
1108
  function isTransientLlmError(error) {
1089
- if (error instanceof AnthropicApiError) {
1109
+ if (error instanceof ProviderApiError) {
1090
1110
  const shouldRetry = error.responseHeaders.get("x-should-retry");
1091
1111
  if (shouldRetry === "false")
1092
1112
  return false;
@@ -1107,7 +1127,7 @@ function isTransientLlmError(error) {
1107
1127
  return ["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT", "ENETUNREACH", "EAI_AGAIN"].includes(code ?? "");
1108
1128
  }
1109
1129
  function isContextOverflowError(error) {
1110
- if (!(error instanceof AnthropicApiError))
1130
+ if (!(error instanceof ProviderApiError))
1111
1131
  return false;
1112
1132
  if (error.status === 413)
1113
1133
  return true;
@@ -1117,7 +1137,7 @@ function isContextOverflowError(error) {
1117
1137
  .test(error.responseBody);
1118
1138
  }
1119
1139
  function retryDelayMs(error, attempt) {
1120
- if (error instanceof AnthropicApiError) {
1140
+ if (error instanceof ProviderApiError) {
1121
1141
  const retryAfter = error.responseHeaders.get("retry-after");
1122
1142
  if (retryAfter) {
1123
1143
  const seconds = Number(retryAfter);
@@ -1134,7 +1154,7 @@ function retryDelayMs(error, attempt) {
1134
1154
  return Math.round(base + Math.random() * base * 0.25);
1135
1155
  }
1136
1156
  function retryReason(error) {
1137
- if (error instanceof AnthropicApiError)
1157
+ if (error instanceof ProviderApiError)
1138
1158
  return `provider API ${error.status}`;
1139
1159
  if (error instanceof Error && error.name === "TimeoutError")
1140
1160
  return "provider request timed out";
package/dist/sdk.d.ts CHANGED
@@ -2,6 +2,8 @@ import { QueryEngine, type QueryEngineOptions } from "./runtime/query-engine.js"
2
2
  import type { AgentConfig, ApprovalProvider, AskUserFn, LlmConfig, TraceSink } from "./types.js";
3
3
  export type CreateQueryEngineOptions = {
4
4
  configPath?: string;
5
+ configMode?: "layered" | "isolated";
6
+ workspaceRoot?: string;
5
7
  envPath?: string;
6
8
  agentConfig?: AgentConfig;
7
9
  llmConfig?: LlmConfig;
package/dist/sdk.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7G,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACtC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC,CAkBpG;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAO5F;AAID,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAClF,YAAY,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,OAAO,EACP,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7G,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACtC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC,CAmBpG;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAO5F;AAID,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAClF,YAAY,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,OAAO,EACP,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/sdk.js CHANGED
@@ -3,7 +3,8 @@ import { QueryEngine } from "./runtime/query-engine.js";
3
3
  export async function createQueryEngine(options = {}) {
4
4
  if (options.envPath || !options.llmConfig)
5
5
  loadEnvFile(options.envPath);
6
- const agentConfig = options.agentConfig ?? (await loadAgentConfig(options.configPath));
6
+ const loadedConfig = options.agentConfig ?? (await loadAgentConfig(options.configPath, { isolated: options.configMode === "isolated" }));
7
+ const agentConfig = options.workspaceRoot ? { ...loadedConfig, workspaceRoot: options.workspaceRoot } : loadedConfig;
7
8
  const llmConfig = options.llmConfig ?? loadLlmConfig();
8
9
  const engine = new QueryEngine({
9
10
  agentConfig,
@@ -1,19 +1,10 @@
1
1
  /**
2
- * Terminal collapsible Thought blocks (like web ProcessTimeline).
3
- * Default: one line “◆ Thought for 0.5s ▸”
4
- * Expand: /thought or key `t` → show detail lines under it.
2
+ * Bounded terminal preview for model thinking tokens.
5
3
  */
6
4
  export type ThoughtBlock = {
7
- id: string;
8
5
  startedAt: number;
9
6
  endedAt?: number;
10
- /** Short lines shown when expanded */
11
- lines: string[];
12
- tools: string[];
13
- expanded: boolean;
14
- /** Printed to terminal already (collapsed header) */
15
- printed: boolean;
16
- /** true if turn failed */
7
+ thinking: string;
17
8
  failed?: boolean;
18
9
  };
19
10
  export type ThoughtFoldOptions = {
@@ -21,28 +12,22 @@ export type ThoughtFoldOptions = {
21
12
  out: (text: string) => void;
22
13
  };
23
14
  export declare class ThoughtFoldManager {
24
- private blocks;
25
15
  private current;
26
16
  private options;
27
17
  constructor(options: ThoughtFoldOptions);
28
18
  setColor(color: boolean): void;
29
19
  /** Start a new thought window for this user turn. */
30
20
  beginTurn(): void;
31
- note(line: string): void;
32
- noteTool(tool: string): void;
21
+ appendThinking(text: string): void;
22
+ printPreview(block: ThoughtBlock, maxChars?: number): void;
33
23
  /**
34
- * Finalize current thought and print collapsed header (once).
24
+ * Finalize current thought and optionally print a failure header.
35
25
  * Call before streaming the final answer, or at end of turn.
36
26
  */
37
- finalizeAndPrint(defaultDetail?: string, failed?: boolean, printHeader?: boolean): ThoughtBlock | null;
38
- /** Toggle expand/collapse of the last thought (or by id). */
39
- toggle(id?: string): void;
40
- expandLast(): void;
41
- lastSummary(): string | null;
27
+ finalizeAndPrint(_defaultDetail?: string, failed?: boolean, printHeader?: boolean): ThoughtBlock | null;
42
28
  clear(): void;
43
29
  private duration;
44
30
  private printHeader;
45
- private printExpanded;
46
31
  private dim;
47
32
  }
48
33
  //# sourceMappingURL=thought-fold.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"thought-fold.d.ts","sourceRoot":"","sources":["../src/thought-fold.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AAQF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAqB;gBAExB,OAAO,EAAE,kBAAkB;IAIvC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,qDAAqD;IACrD,SAAS,IAAI,IAAI;IAWjB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM5B;;;OAGG;IACH,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,UAAQ,EAAE,WAAW,UAAQ,GAAG,YAAY,GAAG,IAAI;IA0BlG,6DAA6D;IAC7D,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAgBzB,UAAU,IAAI,IAAI;IAclB,WAAW,IAAI,MAAM,GAAG,IAAI;IAO5B,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,GAAG;CAIZ"}
1
+ {"version":3,"file":"thought-fold.d.ts","sourceRoot":"","sources":["../src/thought-fold.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AAOF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAqB;gBAExB,OAAO,EAAE,kBAAkB;IAIvC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,qDAAqD;IACrD,SAAS,IAAI,IAAI;IAQjB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKlC,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,SAAM,GAAG,IAAI;IAevD;;;OAGG;IACH,gBAAgB,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,UAAQ,EAAE,WAAW,UAAQ,GAAG,YAAY,GAAG,IAAI;IAWnG,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,GAAG;CAIZ"}
@@ -1,15 +1,12 @@
1
1
  /**
2
- * Terminal collapsible Thought blocks (like web ProcessTimeline).
3
- * Default: one line “◆ Thought for 0.5s ▸”
4
- * Expand: /thought or key `t` → show detail lines under it.
2
+ * Bounded terminal preview for model thinking tokens.
5
3
  */
4
+ import { stripTerminalEscapes } from "./trace.js";
6
5
  const DIM = "\x1b[90m";
7
6
  const RESET = "\x1b[0m";
8
- const AMBER = "\x1b[38;2;251;191;36m";
9
7
  const GREEN = "\x1b[32m";
10
8
  const RED = "\x1b[31m";
11
9
  export class ThoughtFoldManager {
12
- blocks = [];
13
10
  current = null;
14
11
  options;
15
12
  constructor(options) {
@@ -20,103 +17,47 @@ export class ThoughtFoldManager {
20
17
  }
21
18
  /** Start a new thought window for this user turn. */
22
19
  beginTurn() {
20
+ if (this.current)
21
+ return;
23
22
  this.current = {
24
- id: `th-${Date.now()}`,
25
23
  startedAt: Date.now(),
26
- lines: [],
27
- tools: [],
28
- expanded: false,
29
- printed: false,
24
+ thinking: "",
30
25
  };
31
26
  }
32
- note(line) {
33
- if (!this.current)
34
- return;
35
- const t = line.trim();
36
- if (!t)
37
- return;
38
- // de-dupe consecutive
39
- if (this.current.lines[this.current.lines.length - 1] === t)
27
+ appendThinking(text) {
28
+ if (!this.current || !text)
40
29
  return;
41
- this.current.lines.push(t);
30
+ this.current.thinking = `${this.current.thinking}${text}`.slice(-6000);
42
31
  }
43
- noteTool(tool) {
44
- if (!this.current)
32
+ printPreview(block, maxChars = 300) {
33
+ const characters = Array.from(stripTerminalEscapes(block.thinking).replace(/\s+/g, " ").trim());
34
+ if (!characters.length)
45
35
  return;
46
- if (!this.current.tools.includes(tool))
47
- this.current.tools.push(tool);
48
- this.note(`used ${tool}`);
36
+ const visible = characters.slice(0, maxChars);
37
+ for (let index = 0; index < visible.length; index += 100) {
38
+ const line = visible.slice(index, index + 100).join("");
39
+ this.options.out(this.dim(`${index === 0 ? " └ " : " "}${line}\n`));
40
+ }
41
+ if (characters.length > maxChars) {
42
+ this.options.out(this.dim(" … more hidden\n"));
43
+ }
49
44
  }
50
45
  /**
51
- * Finalize current thought and print collapsed header (once).
46
+ * Finalize current thought and optionally print a failure header.
52
47
  * Call before streaming the final answer, or at end of turn.
53
48
  */
54
- finalizeAndPrint(defaultDetail, failed = false, printHeader = false) {
49
+ finalizeAndPrint(_defaultDetail, failed = false, printHeader = false) {
55
50
  if (!this.current)
56
51
  return null;
57
- if (this.current.printed)
58
- return this.current;
59
52
  this.current.endedAt = Date.now();
60
53
  this.current.failed = failed;
61
- if (this.current.lines.length === 0 && defaultDetail) {
62
- this.current.lines.push(defaultDetail);
63
- }
64
- if (this.current.lines.length === 0) {
65
- this.current.lines.push("Processed the request and prepared a reply.");
66
- }
67
- this.blocks.push(this.current);
68
- // Only the most recent block is ever toggled/expanded; cap history so a long-lived
69
- // interactive session doesn't grow this array (and its retained lines) without bound.
70
- if (this.blocks.length > 50)
71
- this.blocks.splice(0, this.blocks.length - 50);
72
- if (printHeader) {
54
+ if (printHeader)
73
55
  this.printHeader(this.current);
74
- this.current.printed = true;
75
- }
76
56
  const done = this.current;
77
57
  this.current = null;
78
58
  return done;
79
59
  }
80
- /** Toggle expand/collapse of the last thought (or by id). */
81
- toggle(id) {
82
- const block = id
83
- ? this.blocks.find((b) => b.id === id)
84
- : this.blocks[this.blocks.length - 1];
85
- if (!block) {
86
- this.options.out(this.dim("(no thought to expand yet)\n"));
87
- return;
88
- }
89
- block.expanded = !block.expanded;
90
- if (block.expanded) {
91
- this.printExpanded(block);
92
- }
93
- else {
94
- this.options.out(this.dim(" (thought collapsed — /thought to expand again)\n"));
95
- }
96
- }
97
- expandLast() {
98
- const block = this.blocks[this.blocks.length - 1];
99
- if (!block) {
100
- this.options.out(this.dim("(no thought to expand yet)\n"));
101
- return;
102
- }
103
- if (!block.expanded) {
104
- block.expanded = true;
105
- this.printExpanded(block);
106
- }
107
- else {
108
- this.printExpanded(block); // re-print
109
- }
110
- }
111
- lastSummary() {
112
- const b = this.blocks[this.blocks.length - 1];
113
- if (!b)
114
- return null;
115
- const sec = ((b.endedAt ?? Date.now()) - b.startedAt) / 1000;
116
- return `Thought for ${sec.toFixed(1)}s · ${b.tools.length} tools · ${b.expanded ? "expanded" : "collapsed"}`;
117
- }
118
60
  clear() {
119
- this.blocks = [];
120
61
  this.current = null;
121
62
  }
122
63
  duration(block) {
@@ -124,29 +65,13 @@ export class ThoughtFoldManager {
124
65
  return `${(ms / 1000).toFixed(1)}s`;
125
66
  }
126
67
  printHeader(block) {
127
- const arrow = block.expanded ? "▾" : "▸";
128
- // CC style: green ● success, red ● failure (not amber while done)
129
68
  const color = block.failed ? RED : GREEN;
130
69
  const dot = this.options.color ? `${color}●${RESET}` : "●";
131
70
  const label = block.failed
132
71
  ? `Failed · ${this.duration(block)}`
133
72
  : `Thought for ${this.duration(block)}`;
134
73
  const labelPainted = this.options.color ? `${color}${label}${RESET}` : label;
135
- const hint = this.dim(` ${arrow} /thought to expand`);
136
- this.options.out(`\n${dot} ${labelPainted}${hint}\n`);
137
- }
138
- printExpanded(block) {
139
- const color = block.failed ? RED : GREEN;
140
- const dot = this.options.color ? `${color}●${RESET}` : "●";
141
- const head = block.failed ? `Failed · ${this.duration(block)}` : `Thought for ${this.duration(block)}`;
142
- this.options.out(`\n${dot} ${head} ▾\n`);
143
- if (block.tools.length) {
144
- this.options.out(this.dim(` tools: ${block.tools.join(", ")}\n`));
145
- }
146
- for (const line of block.lines) {
147
- this.options.out(this.dim(` │ ${line}\n`));
148
- }
149
- this.options.out(this.dim(" └ /thought to collapse\n\n"));
74
+ this.options.out(`\n${dot} ${labelPainted}\n`);
150
75
  }
151
76
  dim(text) {
152
77
  if (!this.options.color)
@@ -1 +1 @@
1
- {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAiBxD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAgB;IAiBf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhBpC,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,WAAW,CAAoE;gBAE1D,OAAO,EAAE,eAAe;IAErD,IAAI,IAAI,SAAS;IAIjB,SAAS,IAAI,IAAI;IAajB,UAAU,IAAI,IAAI;IAelB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IA6H/B,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAsBtB,yEAAyE;IACzE,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,2BAA2B;IAgBnC,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,GAAG;CAKZ;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAOxD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAO9D;AA4ED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAY1D"}
1
+ {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAiBxD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAgB;IAiBf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhBpC,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,WAAW,CAAoE;gBAE1D,OAAO,EAAE,eAAe;IAErD,IAAI,IAAI,SAAS;IAIjB,SAAS,IAAI,IAAI;IAajB,UAAU,IAAI,IAAI;IAelB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IA6H/B,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAsBtB,yEAAyE;IACzE,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,GAAG;CAKZ;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAOxD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAO9D;AA4ED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAY1D"}