pulseed 0.4.13 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/seedy.png +0 -0
- package/dist/adapters/types/mcp.d.ts +6 -6
- package/dist/base/config/global-config.d.ts.map +1 -1
- package/dist/base/config/global-config.js +2 -2
- package/dist/base/config/global-config.js.map +1 -1
- package/dist/base/llm/codex-llm-client.d.ts +1 -1
- package/dist/base/llm/codex-llm-client.d.ts.map +1 -1
- package/dist/base/llm/codex-llm-client.js +1 -1
- package/dist/base/llm/codex-llm-client.js.map +1 -1
- package/dist/base/llm/llm-client.d.ts +3 -2
- package/dist/base/llm/llm-client.d.ts.map +1 -1
- package/dist/base/llm/llm-client.js.map +1 -1
- package/dist/base/llm/provider-config.d.ts +6 -1
- package/dist/base/llm/provider-config.d.ts.map +1 -1
- package/dist/base/llm/provider-config.js +148 -94
- package/dist/base/llm/provider-config.js.map +1 -1
- package/dist/base/utils/json-io.d.ts +6 -1
- package/dist/base/utils/json-io.d.ts.map +1 -1
- package/dist/base/utils/json-io.js +16 -3
- package/dist/base/utils/json-io.js.map +1 -1
- package/dist/interface/chat/chat-history.d.ts +6 -1
- package/dist/interface/chat/chat-history.d.ts.map +1 -1
- package/dist/interface/chat/chat-history.js +16 -2
- package/dist/interface/chat/chat-history.js.map +1 -1
- package/dist/interface/chat/chat-runner.d.ts +27 -1
- package/dist/interface/chat/chat-runner.d.ts.map +1 -1
- package/dist/interface/chat/chat-runner.js +441 -13
- package/dist/interface/chat/chat-runner.js.map +1 -1
- package/dist/interface/chat/grounding.d.ts.map +1 -1
- package/dist/interface/chat/grounding.js +2 -1
- package/dist/interface/chat/grounding.js.map +1 -1
- package/dist/interface/chat/mutation-tool-defs.d.ts +0 -21
- package/dist/interface/chat/mutation-tool-defs.d.ts.map +1 -1
- package/dist/interface/chat/mutation-tool-defs.js +1 -164
- package/dist/interface/chat/mutation-tool-defs.js.map +1 -1
- package/dist/interface/cli/commands/daemon.d.ts.map +1 -1
- package/dist/interface/cli/commands/daemon.js +4 -14
- package/dist/interface/cli/commands/daemon.js.map +1 -1
- package/dist/interface/cli/commands/setup/import/apply.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/import/apply.js +4 -0
- package/dist/interface/cli/commands/setup/import/apply.js.map +1 -1
- package/dist/interface/cli/commands/setup/import/discovery.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/import/discovery.js +10 -2
- package/dist/interface/cli/commands/setup/import/discovery.js.map +1 -1
- package/dist/interface/cli/commands/setup/import/flow.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/import/flow.js +11 -1
- package/dist/interface/cli/commands/setup/import/flow.js.map +1 -1
- package/dist/interface/cli/commands/setup/import/types.d.ts +3 -0
- package/dist/interface/cli/commands/setup/import/types.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/steps-identity.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/steps-identity.js +1 -2
- package/dist/interface/cli/commands/setup/steps-identity.js.map +1 -1
- package/dist/interface/cli/commands/setup/steps-provider.d.ts +4 -0
- package/dist/interface/cli/commands/setup/steps-provider.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup/steps-provider.js +26 -0
- package/dist/interface/cli/commands/setup/steps-provider.js.map +1 -1
- package/dist/interface/cli/commands/setup-wizard.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup-wizard.js +65 -17
- package/dist/interface/cli/commands/setup-wizard.js.map +1 -1
- package/dist/interface/cli/commands/setup.d.ts.map +1 -1
- package/dist/interface/cli/commands/setup.js +12 -2
- package/dist/interface/cli/commands/setup.js.map +1 -1
- package/dist/interface/cli/setup.d.ts.map +1 -1
- package/dist/interface/cli/setup.js +6 -5
- package/dist/interface/cli/setup.js.map +1 -1
- package/dist/interface/mcp-server/index.d.ts.map +1 -1
- package/dist/interface/mcp-server/index.js +2 -1
- package/dist/interface/mcp-server/index.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-budget.d.ts +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-budget.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client-factory.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client-factory.js +3 -0
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client-factory.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client.js +15 -6
- package/dist/orchestrator/execution/agent-loop/agent-loop-model-client.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model.d.ts +1 -0
- package/dist/orchestrator/execution/agent-loop/agent-loop-model.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/agent-loop-model.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/bounded-agent-loop-runner.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/bounded-agent-loop-runner.js +0 -3
- package/dist/orchestrator/execution/agent-loop/bounded-agent-loop-runner.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/index.d.ts +1 -0
- package/dist/orchestrator/execution/agent-loop/index.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/index.js +1 -0
- package/dist/orchestrator/execution/agent-loop/index.js.map +1 -1
- package/dist/orchestrator/execution/agent-loop/prompted-tool-protocol.d.ts +16 -0
- package/dist/orchestrator/execution/agent-loop/prompted-tool-protocol.d.ts.map +1 -0
- package/dist/orchestrator/execution/agent-loop/prompted-tool-protocol.js +74 -0
- package/dist/orchestrator/execution/agent-loop/prompted-tool-protocol.js.map +1 -0
- package/dist/orchestrator/execution/agent-loop/task-agent-loop-factory.d.ts +1 -1
- package/dist/orchestrator/execution/agent-loop/task-agent-loop-factory.d.ts.map +1 -1
- package/dist/orchestrator/execution/agent-loop/task-agent-loop-factory.js +3 -2
- package/dist/orchestrator/execution/agent-loop/task-agent-loop-factory.js.map +1 -1
- package/dist/orchestrator/execution/task/task-lifecycle.js +1 -1
- package/dist/orchestrator/execution/task/task-lifecycle.js.map +1 -1
- package/dist/platform/observation/observation-tools.d.ts.map +1 -1
- package/dist/platform/observation/observation-tools.js +4 -1
- package/dist/platform/observation/observation-tools.js.map +1 -1
- package/dist/platform/soil/config.d.ts.map +1 -1
- package/dist/platform/soil/config.js +1 -2
- package/dist/platform/soil/config.js.map +1 -1
- package/dist/platform/soil/display/index.d.ts +4 -0
- package/dist/platform/soil/display/index.d.ts.map +1 -0
- package/dist/platform/soil/display/index.js +4 -0
- package/dist/platform/soil/display/index.js.map +1 -0
- package/dist/platform/soil/display/materialize.d.ts +3 -0
- package/dist/platform/soil/display/materialize.d.ts.map +1 -0
- package/dist/platform/soil/display/materialize.js +381 -0
- package/dist/platform/soil/display/materialize.js.map +1 -0
- package/dist/platform/soil/display/registry.d.ts +4 -0
- package/dist/platform/soil/display/registry.d.ts.map +1 -0
- package/dist/platform/soil/display/registry.js +19 -0
- package/dist/platform/soil/display/registry.js.map +1 -0
- package/dist/platform/soil/display/types.d.ts +28 -0
- package/dist/platform/soil/display/types.d.ts.map +1 -0
- package/dist/platform/soil/display/types.js +2 -0
- package/dist/platform/soil/display/types.js.map +1 -0
- package/dist/platform/soil/index.d.ts +1 -0
- package/dist/platform/soil/index.d.ts.map +1 -1
- package/dist/platform/soil/index.js +1 -0
- package/dist/platform/soil/index.js.map +1 -1
- package/dist/platform/soil/open.d.ts.map +1 -1
- package/dist/platform/soil/open.js +2 -0
- package/dist/platform/soil/open.js.map +1 -1
- package/dist/platform/soil/publish/publisher.d.ts.map +1 -1
- package/dist/platform/soil/publish/publisher.js +2 -0
- package/dist/platform/soil/publish/publisher.js.map +1 -1
- package/dist/reflection/evening-catchup.d.ts.map +1 -1
- package/dist/reflection/evening-catchup.js +6 -41
- package/dist/reflection/evening-catchup.js.map +1 -1
- package/dist/reflection/morning-planning.d.ts.map +1 -1
- package/dist/reflection/morning-planning.js +6 -46
- package/dist/reflection/morning-planning.js.map +1 -1
- package/dist/reflection/reflection-utils.d.ts +16 -0
- package/dist/reflection/reflection-utils.d.ts.map +1 -0
- package/dist/reflection/reflection-utils.js +56 -0
- package/dist/reflection/reflection-utils.js.map +1 -0
- package/dist/reflection/weekly-review.d.ts.map +1 -1
- package/dist/reflection/weekly-review.js +3 -12
- package/dist/reflection/weekly-review.js.map +1 -1
- package/dist/reporting/report-formatters.d.ts +7 -0
- package/dist/reporting/report-formatters.d.ts.map +1 -1
- package/dist/reporting/report-formatters.js +22 -33
- package/dist/reporting/report-formatters.js.map +1 -1
- package/dist/reporting/reporting-engine.d.ts.map +1 -1
- package/dist/reporting/reporting-engine.js +34 -49
- package/dist/reporting/reporting-engine.js.map +1 -1
- package/dist/runtime/builtin-integrations.d.ts +4 -0
- package/dist/runtime/builtin-integrations.d.ts.map +1 -0
- package/dist/runtime/builtin-integrations.js +45 -0
- package/dist/runtime/builtin-integrations.js.map +1 -0
- package/dist/runtime/event/server.d.ts +1 -1
- package/dist/runtime/event/server.d.ts.map +1 -1
- package/dist/runtime/event/server.js +5 -27
- package/dist/runtime/event/server.js.map +1 -1
- package/dist/runtime/executor/loop-supervisor.d.ts.map +1 -1
- package/dist/runtime/executor/loop-supervisor.js +2 -2
- package/dist/runtime/executor/loop-supervisor.js.map +1 -1
- package/dist/runtime/foreign-plugins/compatibility.d.ts +7 -0
- package/dist/runtime/foreign-plugins/compatibility.d.ts.map +1 -0
- package/dist/runtime/foreign-plugins/compatibility.js +175 -0
- package/dist/runtime/foreign-plugins/compatibility.js.map +1 -0
- package/dist/runtime/foreign-plugins/index.d.ts +3 -0
- package/dist/runtime/foreign-plugins/index.d.ts.map +1 -0
- package/dist/runtime/foreign-plugins/index.js +2 -0
- package/dist/runtime/foreign-plugins/index.js.map +1 -0
- package/dist/runtime/foreign-plugins/types.d.ts +25 -0
- package/dist/runtime/foreign-plugins/types.d.ts.map +1 -0
- package/dist/runtime/foreign-plugins/types.js +2 -0
- package/dist/runtime/foreign-plugins/types.js.map +1 -0
- package/dist/runtime/schedule/engine.d.ts +12 -0
- package/dist/runtime/schedule/engine.d.ts.map +1 -1
- package/dist/runtime/schedule/engine.js +402 -243
- package/dist/runtime/schedule/engine.js.map +1 -1
- package/dist/runtime/types/builtin-integration.d.ts +13 -0
- package/dist/runtime/types/builtin-integration.d.ts.map +1 -0
- package/dist/runtime/types/builtin-integration.js +2 -0
- package/dist/runtime/types/builtin-integration.js.map +1 -0
- package/dist/tools/executor.js +2 -2
- package/dist/tools/executor.js.map +1 -1
- package/dist/tools/fs/ReadPulseedFileTool/ReadPulseedFileTool.d.ts.map +1 -1
- package/dist/tools/fs/ReadPulseedFileTool/ReadPulseedFileTool.js +21 -5
- package/dist/tools/fs/ReadPulseedFileTool/ReadPulseedFileTool.js.map +1 -1
- package/dist/tools/fs/WritePulseedFileTool/WritePulseedFileTool.d.ts.map +1 -1
- package/dist/tools/fs/WritePulseedFileTool/WritePulseedFileTool.js +61 -6
- package/dist/tools/fs/WritePulseedFileTool/WritePulseedFileTool.js.map +1 -1
- package/dist/tools/interaction/plan-utils.js +2 -2
- package/dist/tools/interaction/plan-utils.js.map +1 -1
- package/dist/tools/network/McpStdioTool/McpStdioTool.d.ts +10 -10
- package/dist/tools/query/ConfigTool/ConfigTool.d.ts.map +1 -1
- package/dist/tools/query/ConfigTool/ConfigTool.js +4 -3
- package/dist/tools/query/ConfigTool/ConfigTool.js.map +1 -1
- package/dist/tools/query/PluginStateTool/PluginStateTool.d.ts.map +1 -1
- package/dist/tools/query/PluginStateTool/PluginStateTool.js +13 -2
- package/dist/tools/query/PluginStateTool/PluginStateTool.js.map +1 -1
- package/dist/tools/system/ProcessSessionTool/ProcessSessionTool.d.ts +4 -4
- package/package.json +2 -1
- package/dist/interface/chat/self-knowledge-mutation-tools.d.ts +0 -12
- package/dist/interface/chat/self-knowledge-mutation-tools.d.ts.map +0 -1
- package/dist/interface/chat/self-knowledge-mutation-tools.js +0 -261
- package/dist/interface/chat/self-knowledge-mutation-tools.js.map +0 -1
- package/dist/interface/chat/self-knowledge-tools.d.ts +0 -30
- package/dist/interface/chat/self-knowledge-tools.d.ts.map +0 -1
- package/dist/interface/chat/self-knowledge-tools.js +0 -248
- package/dist/interface/chat/self-knowledge-tools.js.map +0 -1
- package/dist/prompt/index.d.ts +0 -12
- package/dist/prompt/index.d.ts.map +0 -1
- package/dist/prompt/index.js +0 -9
- package/dist/prompt/index.js.map +0 -1
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// Central coordinator for 1-shot chat execution (Tier 1).
|
|
4
4
|
// Bypasses TaskLifecycle — calls adapter.execute() directly.
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
|
+
import * as fsp from "node:fs/promises";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { getPulseedDirPath } from "../../base/utils/paths.js";
|
|
9
|
+
import { loadProviderConfig } from "../../base/llm/provider-config.js";
|
|
10
|
+
import { TaskSchema } from "../../base/types/task.js";
|
|
6
11
|
import { ChatHistory } from "./chat-history.js";
|
|
7
12
|
import { ChatSessionCatalog, ChatSessionSelectorError, } from "./chat-session-store.js";
|
|
8
13
|
import { buildChatContext, resolveGitRoot } from "../../platform/observation/context-provider.js";
|
|
@@ -11,6 +16,7 @@ import { verifyChatAction } from "./chat-verifier.js";
|
|
|
11
16
|
import { toToolDefinitionsFiltered } from "../../tools/tool-definition-adapter.js";
|
|
12
17
|
import { TendCommand } from "./tend-command.js";
|
|
13
18
|
import { EventSubscriber } from "./event-subscriber.js";
|
|
19
|
+
import { buildPromptedToolProtocolSystemPrompt, extractPromptedToolCalls, } from "../../orchestrator/execution/agent-loop/prompted-tool-protocol.js";
|
|
14
20
|
import { recognizeRuntimeControlIntent } from "../../runtime/control/index.js";
|
|
15
21
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
16
22
|
const MAX_VERIFY_RETRIES = 2;
|
|
@@ -19,6 +25,7 @@ const ACTIVITY_PREVIEW_CHARS = 40;
|
|
|
19
25
|
const DIRECT_ANSWER_MAX_TOKENS = 256;
|
|
20
26
|
// ─── Command help text ───
|
|
21
27
|
const COMMAND_HELP = `Available commands:
|
|
28
|
+
Session
|
|
22
29
|
/help Show this help message
|
|
23
30
|
/clear Clear conversation history
|
|
24
31
|
/sessions List prior chat sessions
|
|
@@ -26,9 +33,25 @@ const COMMAND_HELP = `Available commands:
|
|
|
26
33
|
/title <title> Rename the current session
|
|
27
34
|
/resume [id|title] Resume native agentloop state for the current or selected session
|
|
28
35
|
/cleanup [--dry-run] Clean up stale chat sessions
|
|
36
|
+
/compact Summarize older chat turns and keep the latest turns
|
|
29
37
|
/exit Exit chat mode
|
|
38
|
+
|
|
39
|
+
Goals and tasks
|
|
40
|
+
/status [goal-id] Show active goal status, or one goal when an id is provided
|
|
41
|
+
/goals List goals
|
|
42
|
+
/tasks [goal-id] List tasks for a goal; uses the only active goal when unambiguous
|
|
43
|
+
/task <task-id> [goal-id]
|
|
44
|
+
Show one task; searches goals when no goal id is provided
|
|
30
45
|
/track Promote session to Tier 2 goal pursuit (not yet implemented)
|
|
31
|
-
/tend Generate a goal from chat history and start autonomous daemon execution
|
|
46
|
+
/tend Generate a goal from chat history and start autonomous daemon execution
|
|
47
|
+
|
|
48
|
+
Configuration
|
|
49
|
+
/config Show provider configuration with secrets masked
|
|
50
|
+
/model Show the active provider/model/adapter
|
|
51
|
+
/plugins List installed plugins when plugin metadata is available
|
|
52
|
+
|
|
53
|
+
Deferred
|
|
54
|
+
/retry, /undo, and /usage are intentionally not supported yet.`;
|
|
32
55
|
// ─── Helpers ───
|
|
33
56
|
function checkGitChanges(cwd) {
|
|
34
57
|
return new Promise((resolve) => {
|
|
@@ -164,6 +187,352 @@ export class ChatRunner {
|
|
|
164
187
|
});
|
|
165
188
|
return `Session ${session.id}${title} (${session.cwd})\n${lines.join("\n")}`;
|
|
166
189
|
}
|
|
190
|
+
async loadGoals() {
|
|
191
|
+
const goalIds = await this.deps.stateManager.listGoalIds();
|
|
192
|
+
const goals = await Promise.all(goalIds.map((id) => this.deps.stateManager.loadGoal(id)));
|
|
193
|
+
return goals.filter((goal) => goal !== null);
|
|
194
|
+
}
|
|
195
|
+
async listAllGoalIds() {
|
|
196
|
+
const activeIds = await this.deps.stateManager.listGoalIds();
|
|
197
|
+
const archivedIds = await this.deps.stateManager.listArchivedGoals();
|
|
198
|
+
const recoverableArchivedIds = await this.listRecoverableArchivedGoalIds();
|
|
199
|
+
return [...new Set([...activeIds, ...archivedIds, ...recoverableArchivedIds])];
|
|
200
|
+
}
|
|
201
|
+
resolveStatePath(baseDir, ...segments) {
|
|
202
|
+
const base = path.resolve(baseDir);
|
|
203
|
+
const resolved = path.resolve(base, ...segments);
|
|
204
|
+
if (!resolved.startsWith(base + path.sep))
|
|
205
|
+
return null;
|
|
206
|
+
return resolved;
|
|
207
|
+
}
|
|
208
|
+
async listRecoverableArchivedGoalIds() {
|
|
209
|
+
const stateManager = this.deps.stateManager;
|
|
210
|
+
if (typeof stateManager.getBaseDir !== "function")
|
|
211
|
+
return [];
|
|
212
|
+
const archiveDir = this.resolveStatePath(stateManager.getBaseDir(), "archive");
|
|
213
|
+
if (archiveDir === null)
|
|
214
|
+
return [];
|
|
215
|
+
let entries = [];
|
|
216
|
+
try {
|
|
217
|
+
entries = await fsp.readdir(archiveDir, { withFileTypes: true });
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
const goalIds = [];
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
if (!entry.isDirectory() || entry.name === ".staging")
|
|
225
|
+
continue;
|
|
226
|
+
try {
|
|
227
|
+
await fsp.access(path.join(archiveDir, entry.name, "goal", "goal.json"));
|
|
228
|
+
goalIds.push(entry.name);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return goalIds;
|
|
235
|
+
}
|
|
236
|
+
activeGoals(goals) {
|
|
237
|
+
return goals.filter((goal) => goal.status === "active" || goal.status === "waiting" || goal.loop_status === "running");
|
|
238
|
+
}
|
|
239
|
+
formatGoalLine(goal) {
|
|
240
|
+
const dimensions = goal.dimensions.length === 0
|
|
241
|
+
? "no dimensions"
|
|
242
|
+
: goal.dimensions
|
|
243
|
+
.slice(0, 3)
|
|
244
|
+
.map((dimension) => `${dimension.name}: ${String(dimension.current_value)} target ${JSON.stringify(dimension.threshold)}`)
|
|
245
|
+
.join("; ");
|
|
246
|
+
return `${goal.id} - ${goal.title} [${goal.status}, loop ${goal.loop_status}] ${dimensions}`;
|
|
247
|
+
}
|
|
248
|
+
async handleStatus(args, start) {
|
|
249
|
+
if (args) {
|
|
250
|
+
const goal = await this.deps.stateManager.loadGoal(args);
|
|
251
|
+
if (!goal) {
|
|
252
|
+
return { success: false, output: `Goal not found: ${args}`, elapsed_ms: Date.now() - start };
|
|
253
|
+
}
|
|
254
|
+
const lines = [
|
|
255
|
+
`Goal status: ${goal.title}`,
|
|
256
|
+
`ID: ${goal.id}`,
|
|
257
|
+
`Status: ${goal.status}`,
|
|
258
|
+
`Loop: ${goal.loop_status}`,
|
|
259
|
+
`Updated: ${goal.updated_at}`,
|
|
260
|
+
`Children: ${goal.children_ids.length}`,
|
|
261
|
+
`Dimensions:`,
|
|
262
|
+
...goal.dimensions.map((dimension) => `- ${dimension.name}: current=${String(dimension.current_value)}, threshold=${JSON.stringify(dimension.threshold)}, confidence=${dimension.confidence}`),
|
|
263
|
+
];
|
|
264
|
+
return { success: true, output: lines.join("\n"), elapsed_ms: Date.now() - start };
|
|
265
|
+
}
|
|
266
|
+
const goals = await this.loadGoals();
|
|
267
|
+
const active = this.activeGoals(goals);
|
|
268
|
+
if (active.length === 0) {
|
|
269
|
+
return { success: true, output: "No active goals found.", elapsed_ms: Date.now() - start };
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
success: true,
|
|
273
|
+
output: `Active goals:\n${active.map((goal) => this.formatGoalLine(goal)).join("\n")}`,
|
|
274
|
+
elapsed_ms: Date.now() - start,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
async handleGoals(start) {
|
|
278
|
+
const goals = await this.loadGoals();
|
|
279
|
+
if (goals.length === 0) {
|
|
280
|
+
return { success: true, output: "No goals found.", elapsed_ms: Date.now() - start };
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
success: true,
|
|
284
|
+
output: `Goals:\n${goals.map((goal) => this.formatGoalLine(goal)).join("\n")}`,
|
|
285
|
+
elapsed_ms: Date.now() - start,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async readTasksFromDir(tasksDir) {
|
|
289
|
+
let entries = [];
|
|
290
|
+
try {
|
|
291
|
+
entries = await fsp.readdir(tasksDir);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
const tasks = [];
|
|
297
|
+
for (const entry of entries) {
|
|
298
|
+
if (!entry.endsWith(".json") || entry === "task-history.json" || entry === "last-failure-context.json")
|
|
299
|
+
continue;
|
|
300
|
+
let raw;
|
|
301
|
+
try {
|
|
302
|
+
raw = JSON.parse(await fsp.readFile(path.join(tasksDir, entry), "utf-8"));
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const parsed = TaskSchema.safeParse(raw);
|
|
308
|
+
if (parsed.success)
|
|
309
|
+
tasks.push(parsed.data);
|
|
310
|
+
}
|
|
311
|
+
return tasks.sort((a, b) => (a.created_at < b.created_at ? 1 : -1));
|
|
312
|
+
}
|
|
313
|
+
async readTasksForGoal(goalId) {
|
|
314
|
+
const stateManager = this.deps.stateManager;
|
|
315
|
+
if (typeof stateManager.getBaseDir !== "function")
|
|
316
|
+
return [];
|
|
317
|
+
const baseDir = stateManager.getBaseDir();
|
|
318
|
+
const activeTasksDir = this.resolveStatePath(baseDir, "tasks", goalId);
|
|
319
|
+
const archiveTasksDir = this.resolveStatePath(baseDir, "archive", goalId, "tasks");
|
|
320
|
+
if (activeTasksDir === null || archiveTasksDir === null)
|
|
321
|
+
return [];
|
|
322
|
+
const activeTasks = await this.readTasksFromDir(activeTasksDir);
|
|
323
|
+
if (activeTasks.length > 0)
|
|
324
|
+
return activeTasks;
|
|
325
|
+
return this.readTasksFromDir(archiveTasksDir);
|
|
326
|
+
}
|
|
327
|
+
async resolveGoalForTasks(selector) {
|
|
328
|
+
if (selector)
|
|
329
|
+
return { goalId: selector };
|
|
330
|
+
const active = this.activeGoals(await this.loadGoals());
|
|
331
|
+
if (active.length === 1)
|
|
332
|
+
return { goalId: active[0].id };
|
|
333
|
+
if (active.length === 0)
|
|
334
|
+
return { error: "No active goals found. Use /tasks <goal-id>." };
|
|
335
|
+
return { error: "Multiple active goals found. Use /tasks <goal-id>." };
|
|
336
|
+
}
|
|
337
|
+
formatTaskLine(task) {
|
|
338
|
+
const verdict = task.verification_verdict ? `, verdict ${task.verification_verdict}` : "";
|
|
339
|
+
return `${task.id} - ${task.status}${verdict}: ${task.work_description}`;
|
|
340
|
+
}
|
|
341
|
+
async handleTasks(args, start) {
|
|
342
|
+
const resolved = await this.resolveGoalForTasks(args);
|
|
343
|
+
if (resolved.error || !resolved.goalId) {
|
|
344
|
+
return { success: false, output: resolved.error ?? "Usage: /tasks <goal-id>", elapsed_ms: Date.now() - start };
|
|
345
|
+
}
|
|
346
|
+
const tasks = await this.readTasksForGoal(resolved.goalId);
|
|
347
|
+
if (tasks.length === 0) {
|
|
348
|
+
return { success: true, output: `No tasks found for goal "${resolved.goalId}".`, elapsed_ms: Date.now() - start };
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
success: true,
|
|
352
|
+
output: `Tasks for goal ${resolved.goalId}:\n${tasks.map((task) => this.formatTaskLine(task)).join("\n")}`,
|
|
353
|
+
elapsed_ms: Date.now() - start,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
parseTaskArgs(args) {
|
|
357
|
+
const parts = args.split(/\s+/).filter(Boolean);
|
|
358
|
+
const goalFlagIndex = parts.indexOf("--goal");
|
|
359
|
+
if (goalFlagIndex >= 0) {
|
|
360
|
+
const goalId = parts[goalFlagIndex + 1];
|
|
361
|
+
parts.splice(goalFlagIndex, goalId ? 2 : 1);
|
|
362
|
+
return { taskId: parts[0], goalId };
|
|
363
|
+
}
|
|
364
|
+
return { taskId: parts[0], goalId: parts[1] };
|
|
365
|
+
}
|
|
366
|
+
async findTask(taskId, goalId) {
|
|
367
|
+
const goalIds = goalId ? [goalId] : await this.listAllGoalIds();
|
|
368
|
+
const matches = [];
|
|
369
|
+
for (const candidateGoalId of goalIds) {
|
|
370
|
+
let raw = null;
|
|
371
|
+
try {
|
|
372
|
+
raw = await this.deps.stateManager.readRaw(`tasks/${candidateGoalId}/${taskId}.json`);
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
raw = null;
|
|
376
|
+
}
|
|
377
|
+
if (!raw) {
|
|
378
|
+
const tasks = await this.readTasksForGoal(candidateGoalId);
|
|
379
|
+
const matched = tasks.find((task) => task.id === taskId || task.id.startsWith(taskId));
|
|
380
|
+
if (matched)
|
|
381
|
+
matches.push({ goalId: candidateGoalId, task: matched });
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const parsed = TaskSchema.safeParse(raw);
|
|
385
|
+
if (parsed.success)
|
|
386
|
+
matches.push({ goalId: candidateGoalId, task: parsed.data });
|
|
387
|
+
}
|
|
388
|
+
return { task: matches.length === 1 ? matches[0].task : undefined, matches };
|
|
389
|
+
}
|
|
390
|
+
formatTask(task) {
|
|
391
|
+
const lines = [
|
|
392
|
+
`Task: ${task.id}`,
|
|
393
|
+
`Goal: ${task.goal_id}`,
|
|
394
|
+
`Status: ${task.status}`,
|
|
395
|
+
`Category: ${task.task_category}`,
|
|
396
|
+
`Created: ${task.created_at}`,
|
|
397
|
+
`Work: ${task.work_description}`,
|
|
398
|
+
`Approach: ${task.approach}`,
|
|
399
|
+
];
|
|
400
|
+
if (task.started_at)
|
|
401
|
+
lines.push(`Started: ${task.started_at}`);
|
|
402
|
+
if (task.completed_at)
|
|
403
|
+
lines.push(`Completed: ${task.completed_at}`);
|
|
404
|
+
if (task.verification_verdict)
|
|
405
|
+
lines.push(`Verification: ${task.verification_verdict}`);
|
|
406
|
+
if (task.verification_evidence?.length)
|
|
407
|
+
lines.push(`Evidence: ${task.verification_evidence.join("; ")}`);
|
|
408
|
+
if (task.success_criteria.length > 0) {
|
|
409
|
+
lines.push("Success criteria:");
|
|
410
|
+
lines.push(...task.success_criteria.map((criterion) => `- ${criterion.description}`));
|
|
411
|
+
}
|
|
412
|
+
return lines.join("\n");
|
|
413
|
+
}
|
|
414
|
+
async handleTask(args, start) {
|
|
415
|
+
const { taskId, goalId } = this.parseTaskArgs(args);
|
|
416
|
+
if (!taskId) {
|
|
417
|
+
return { success: false, output: "Usage: /task <task-id> [goal-id]", elapsed_ms: Date.now() - start };
|
|
418
|
+
}
|
|
419
|
+
const found = await this.findTask(taskId, goalId);
|
|
420
|
+
if (found.matches.length > 1) {
|
|
421
|
+
return {
|
|
422
|
+
success: false,
|
|
423
|
+
output: `Task selector "${taskId}" matched multiple goals. Use /task ${taskId} <goal-id>.\n${found.matches.map((match) => `- ${match.goalId}`).join("\n")}`,
|
|
424
|
+
elapsed_ms: Date.now() - start,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
if (!found.task) {
|
|
428
|
+
const suffix = goalId ? ` for goal "${goalId}"` : "";
|
|
429
|
+
return { success: false, output: `Task not found: ${taskId}${suffix}`, elapsed_ms: Date.now() - start };
|
|
430
|
+
}
|
|
431
|
+
return { success: true, output: this.formatTask(found.task), elapsed_ms: Date.now() - start };
|
|
432
|
+
}
|
|
433
|
+
providerConfigBaseDir() {
|
|
434
|
+
const stateManager = this.deps.stateManager;
|
|
435
|
+
return typeof stateManager.getBaseDir === "function" ? stateManager.getBaseDir() : getPulseedDirPath();
|
|
436
|
+
}
|
|
437
|
+
async readProviderConfigSummary() {
|
|
438
|
+
const config = await loadProviderConfig({
|
|
439
|
+
baseDir: this.providerConfigBaseDir(),
|
|
440
|
+
saveMigration: false,
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
provider: config.provider,
|
|
444
|
+
model: config.model,
|
|
445
|
+
adapter: config.adapter,
|
|
446
|
+
light_model: config.light_model,
|
|
447
|
+
base_url: config.base_url,
|
|
448
|
+
codex_cli_path: config.codex_cli_path,
|
|
449
|
+
has_api_key: Boolean(config.api_key),
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
formatConfig(config) {
|
|
453
|
+
return Object.entries(config)
|
|
454
|
+
.filter(([, value]) => value !== undefined)
|
|
455
|
+
.map(([key, value]) => `${key}: ${typeof value === "string" && /key|token|secret/i.test(key) ? "[masked]" : String(value)}`)
|
|
456
|
+
.join("\n");
|
|
457
|
+
}
|
|
458
|
+
async handleConfig(start) {
|
|
459
|
+
const config = await this.readProviderConfigSummary();
|
|
460
|
+
return { success: true, output: `Provider configuration:\n${this.formatConfig(config)}`, elapsed_ms: Date.now() - start };
|
|
461
|
+
}
|
|
462
|
+
async handleModel(start) {
|
|
463
|
+
const config = await this.readProviderConfigSummary();
|
|
464
|
+
return {
|
|
465
|
+
success: true,
|
|
466
|
+
output: `Model: ${config.model}\nProvider: ${config.provider}\nAdapter: ${config.adapter}`,
|
|
467
|
+
elapsed_ms: Date.now() - start,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
async handlePlugins(start) {
|
|
471
|
+
if (!this.deps.pluginLoader) {
|
|
472
|
+
return { success: true, output: "Plugin information is not available in this chat session.", elapsed_ms: Date.now() - start };
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
const plugins = await this.deps.pluginLoader.loadAll();
|
|
476
|
+
if (plugins.length === 0) {
|
|
477
|
+
return { success: true, output: "No plugins found.", elapsed_ms: Date.now() - start };
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
success: true,
|
|
481
|
+
output: `Plugins:\n${plugins.map((plugin) => `${plugin.name} - ${plugin.type ?? "unknown"} - ${plugin.enabled === false ? "disabled" : "enabled"}`).join("\n")}`,
|
|
482
|
+
elapsed_ms: Date.now() - start,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
487
|
+
return { success: true, output: `Plugin information is unavailable: ${message}`, elapsed_ms: Date.now() - start };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
deterministicChatSummary(messages) {
|
|
491
|
+
const lines = messages.map((message) => `${message.role}: ${message.content.replace(/\s+/g, " ").trim()}`);
|
|
492
|
+
return lines.join("\n").slice(0, 4_000);
|
|
493
|
+
}
|
|
494
|
+
async summarizeChatForCompaction(messages, existingSummary) {
|
|
495
|
+
const content = [
|
|
496
|
+
existingSummary ? `Previous summary:\n${existingSummary}` : "",
|
|
497
|
+
`Messages to summarize:\n${messages.map((message) => `${message.role}: ${message.content}`).join("\n")}`,
|
|
498
|
+
].filter(Boolean).join("\n\n");
|
|
499
|
+
if (this.deps.llmClient) {
|
|
500
|
+
try {
|
|
501
|
+
const response = await this.deps.llmClient.sendMessage([
|
|
502
|
+
{ role: "user", content: `Summarize this chat history for later continuation. Preserve decisions, open tasks, constraints, and user preferences. Keep it concise.\n\n${content}` },
|
|
503
|
+
], { max_tokens: 700, model_tier: "light" });
|
|
504
|
+
if (response.content.trim())
|
|
505
|
+
return { summary: response.content.trim(), usedLlm: true };
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// Fall back to deterministic summary below.
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const fallback = [
|
|
512
|
+
existingSummary ? `Previous summary:\n${existingSummary}` : "",
|
|
513
|
+
"Extractive summary:",
|
|
514
|
+
this.deterministicChatSummary(messages),
|
|
515
|
+
].filter(Boolean).join("\n\n");
|
|
516
|
+
return { summary: fallback, usedLlm: false };
|
|
517
|
+
}
|
|
518
|
+
async handleCompact(start) {
|
|
519
|
+
if (!this.history) {
|
|
520
|
+
return { success: false, output: "No active chat session to compact.", elapsed_ms: Date.now() - start };
|
|
521
|
+
}
|
|
522
|
+
const session = this.history.getSessionData();
|
|
523
|
+
if (session.messages.length <= 4) {
|
|
524
|
+
return { success: true, output: "Chat history is already compact. No messages were removed.", elapsed_ms: Date.now() - start };
|
|
525
|
+
}
|
|
526
|
+
const olderMessages = session.messages.slice(0, -4);
|
|
527
|
+
const { summary, usedLlm } = await this.summarizeChatForCompaction(olderMessages, session.compactionSummary);
|
|
528
|
+
const { before, after } = await this.history.compact(summary, 4);
|
|
529
|
+
const method = usedLlm ? "LLM summary" : "deterministic summary";
|
|
530
|
+
return {
|
|
531
|
+
success: true,
|
|
532
|
+
output: `Compacted chat history with ${method}. Persisted ${before} message(s) down to ${after}; the latest user/assistant turns were kept.`,
|
|
533
|
+
elapsed_ms: Date.now() - start,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
167
536
|
async handleCommand(input) {
|
|
168
537
|
const trimmed = input.trim();
|
|
169
538
|
if (!trimmed.startsWith("/"))
|
|
@@ -174,7 +543,7 @@ export class ChatRunner {
|
|
|
174
543
|
return { success: true, output: COMMAND_HELP, elapsed_ms: Date.now() - start };
|
|
175
544
|
}
|
|
176
545
|
if (cmd === "/clear") {
|
|
177
|
-
this.history?.clear();
|
|
546
|
+
await this.history?.clear();
|
|
178
547
|
return { success: true, output: "Conversation history cleared.", elapsed_ms: Date.now() - start };
|
|
179
548
|
}
|
|
180
549
|
if (cmd === "/sessions") {
|
|
@@ -223,6 +592,30 @@ export class ChatRunner {
|
|
|
223
592
|
elapsed_ms: Date.now() - start,
|
|
224
593
|
};
|
|
225
594
|
}
|
|
595
|
+
if (cmd === "/compact") {
|
|
596
|
+
return this.handleCompact(start);
|
|
597
|
+
}
|
|
598
|
+
if (cmd === "/status") {
|
|
599
|
+
return this.handleStatus(trimmed.slice("/status".length).trim(), start);
|
|
600
|
+
}
|
|
601
|
+
if (cmd === "/goals") {
|
|
602
|
+
return this.handleGoals(start);
|
|
603
|
+
}
|
|
604
|
+
if (cmd === "/tasks") {
|
|
605
|
+
return this.handleTasks(trimmed.slice("/tasks".length).trim(), start);
|
|
606
|
+
}
|
|
607
|
+
if (cmd === "/task") {
|
|
608
|
+
return this.handleTask(trimmed.slice("/task".length).trim(), start);
|
|
609
|
+
}
|
|
610
|
+
if (cmd === "/config") {
|
|
611
|
+
return this.handleConfig(start);
|
|
612
|
+
}
|
|
613
|
+
if (cmd === "/model") {
|
|
614
|
+
return this.handleModel(start);
|
|
615
|
+
}
|
|
616
|
+
if (cmd === "/plugins") {
|
|
617
|
+
return this.handlePlugins(start);
|
|
618
|
+
}
|
|
226
619
|
if (cmd === "/exit") {
|
|
227
620
|
return { success: true, output: "Exiting chat mode.", elapsed_ms: Date.now() - start };
|
|
228
621
|
}
|
|
@@ -495,15 +888,23 @@ export class ChatRunner {
|
|
|
495
888
|
this.cachedStaticSystemPrompt = "";
|
|
496
889
|
}
|
|
497
890
|
}
|
|
498
|
-
// Build conversation history from prior turns (last 10)
|
|
891
|
+
// Build conversation history from prior turns (last 10), including any manual compaction summary.
|
|
499
892
|
const messages = history.getMessages();
|
|
893
|
+
const compactionSummary = history.getSessionData().compactionSummary;
|
|
500
894
|
const priorTurns = resumeOnly ? messages.slice(-10) : messages.slice(0, -1).slice(-10);
|
|
501
895
|
let historyBlock = "";
|
|
896
|
+
const historySections = [];
|
|
897
|
+
if (compactionSummary) {
|
|
898
|
+
historySections.push(`Compacted previous conversation summary:\n${compactionSummary}`);
|
|
899
|
+
}
|
|
502
900
|
if (priorTurns.length > 0) {
|
|
503
901
|
const lines = priorTurns.map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n");
|
|
504
|
-
|
|
902
|
+
historySections.push(`Previous conversation:\n${lines}`);
|
|
505
903
|
}
|
|
506
|
-
|
|
904
|
+
if (historySections.length > 0) {
|
|
905
|
+
historyBlock = `${historySections.join("\n\n")}\n\nCurrent message:\n`;
|
|
906
|
+
}
|
|
907
|
+
const directAnswerRoute = !resumeOnly && !this.deps.chatAgentLoopRunner && this.deps.llmClient !== undefined && shouldUseDirectAnswerRoute(input);
|
|
507
908
|
const directPrompt = historyBlock ? `${historyBlock}${input}` : input;
|
|
508
909
|
const start = Date.now();
|
|
509
910
|
const assistantBuffer = { text: "" };
|
|
@@ -569,6 +970,13 @@ export class ChatRunner {
|
|
|
569
970
|
.filter((section) => section && section.trim().length > 0)
|
|
570
971
|
.join("\n\n")
|
|
571
972
|
.trim();
|
|
973
|
+
const agentLoopSystemPrompt = [
|
|
974
|
+
systemPrompt,
|
|
975
|
+
compactionSummary ? `## Compacted Chat Summary\n${compactionSummary}` : "",
|
|
976
|
+
]
|
|
977
|
+
.filter((section) => section && section.trim().length > 0)
|
|
978
|
+
.join("\n\n")
|
|
979
|
+
.trim();
|
|
572
980
|
const context = resumeOnly ? "" : await buildChatContext(input, gitRoot);
|
|
573
981
|
const basePrompt = resumeOnly ? "" : (context ? `${context}\n\n${input}` : input);
|
|
574
982
|
const prompt = historyBlock ? `${historyBlock}${basePrompt}` : basePrompt;
|
|
@@ -626,7 +1034,7 @@ export class ChatRunner {
|
|
|
626
1034
|
...(this.nativeAgentLoopStatePath ? { resumeStatePath: this.nativeAgentLoopStatePath } : {}),
|
|
627
1035
|
...(resumeState ? { resumeState } : {}),
|
|
628
1036
|
...(resumeOnly ? { resumeOnly: true } : {}),
|
|
629
|
-
...(
|
|
1037
|
+
...(agentLoopSystemPrompt ? { systemPrompt: agentLoopSystemPrompt } : {}),
|
|
630
1038
|
});
|
|
631
1039
|
const elapsed_ms = Date.now() - start;
|
|
632
1040
|
if (result.output) {
|
|
@@ -666,9 +1074,8 @@ export class ChatRunner {
|
|
|
666
1074
|
};
|
|
667
1075
|
}
|
|
668
1076
|
}
|
|
669
|
-
//
|
|
670
|
-
|
|
671
|
-
if (this.deps.llmClient && this.deps.llmClient.supportsToolCalling?.() !== false) {
|
|
1077
|
+
// Prefer the local LLM/tool loop over the external adapter fallback whenever a client is available.
|
|
1078
|
+
if (this.deps.llmClient) {
|
|
672
1079
|
try {
|
|
673
1080
|
const toolResult = await this.executeWithTools(prompt, eventContext, assistantBuffer, systemPrompt || undefined);
|
|
674
1081
|
const elapsed_ms = Date.now() - start;
|
|
@@ -812,12 +1219,14 @@ export class ChatRunner {
|
|
|
812
1219
|
const tools = this.deps.registry
|
|
813
1220
|
? toToolDefinitionsFiltered(this.deps.registry.listAll(), { activatedTools: this.activatedTools })
|
|
814
1221
|
: [];
|
|
1222
|
+
const supportsNativeToolCalling = llmClient.supportsToolCalling?.() !== false;
|
|
815
1223
|
let response;
|
|
816
1224
|
try {
|
|
817
1225
|
this.emitActivity("lifecycle", "Calling model...", eventContext, "lifecycle:model");
|
|
818
1226
|
response = await this.sendLLMMessage(llmClient, messages, {
|
|
819
|
-
|
|
820
|
-
|
|
1227
|
+
...(supportsNativeToolCalling
|
|
1228
|
+
? { tools, ...(systemPrompt ? { system: systemPrompt } : {}) }
|
|
1229
|
+
: { system: buildPromptedToolProtocolSystemPrompt({ systemPrompt, tools }) }),
|
|
821
1230
|
}, assistantBuffer, eventContext);
|
|
822
1231
|
}
|
|
823
1232
|
catch (err) {
|
|
@@ -825,13 +1234,32 @@ export class ChatRunner {
|
|
|
825
1234
|
const hint = err instanceof Error ? `: ${err.message}` : "";
|
|
826
1235
|
throw new Error(`Sorry, I encountered an error processing your request${hint}.`);
|
|
827
1236
|
}
|
|
1237
|
+
const toolCalls = response.tool_calls?.length
|
|
1238
|
+
? response.tool_calls
|
|
1239
|
+
: supportsNativeToolCalling
|
|
1240
|
+
? []
|
|
1241
|
+
: extractPromptedToolCalls({
|
|
1242
|
+
content: response.content,
|
|
1243
|
+
tools,
|
|
1244
|
+
createId: () => `prompted-${loop}-${crypto.randomUUID()}`,
|
|
1245
|
+
}).map((call) => ({
|
|
1246
|
+
id: call.id,
|
|
1247
|
+
type: "function",
|
|
1248
|
+
function: {
|
|
1249
|
+
name: call.name,
|
|
1250
|
+
arguments: JSON.stringify(call.input ?? {}),
|
|
1251
|
+
},
|
|
1252
|
+
}));
|
|
1253
|
+
if (!supportsNativeToolCalling && toolCalls.length > 0) {
|
|
1254
|
+
assistantBuffer.text = "";
|
|
1255
|
+
}
|
|
828
1256
|
// No tool calls — return the text content
|
|
829
|
-
if (
|
|
1257
|
+
if (toolCalls.length === 0) {
|
|
830
1258
|
return assistantBuffer.text || response.content || "(no response)";
|
|
831
1259
|
}
|
|
832
1260
|
// Append assistant message, then process tool calls
|
|
833
1261
|
messages.push({ role: "assistant", content: response.content || "" });
|
|
834
|
-
for (const tc of
|
|
1262
|
+
for (const tc of toolCalls) {
|
|
835
1263
|
let args = {};
|
|
836
1264
|
try {
|
|
837
1265
|
args = JSON.parse(tc.function.arguments || "{}");
|