@sheepbun/yips 0.1.1 → 0.1.46

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/bin/yips.js +15 -0
  4. package/package.json +21 -128
  5. package/postinstall.js +50 -0
  6. package/dist/agent/commands/command-catalog.js +0 -243
  7. package/dist/agent/commands/commands.js +0 -418
  8. package/dist/agent/conductor.js +0 -118
  9. package/dist/agent/context/code-context.js +0 -68
  10. package/dist/agent/context/memory-store.js +0 -159
  11. package/dist/agent/context/session-store.js +0 -211
  12. package/dist/agent/protocol/tool-protocol.js +0 -160
  13. package/dist/agent/skills/skills.js +0 -327
  14. package/dist/agent/tools/tool-executor.js +0 -415
  15. package/dist/agent/tools/tool-safety.js +0 -52
  16. package/dist/app/index.js +0 -35
  17. package/dist/app/repl.js +0 -105
  18. package/dist/app/update-check.js +0 -132
  19. package/dist/app/version.js +0 -51
  20. package/dist/code-context.js +0 -68
  21. package/dist/colors.js +0 -204
  22. package/dist/command-catalog.js +0 -242
  23. package/dist/commands.js +0 -350
  24. package/dist/conductor.js +0 -94
  25. package/dist/config/config.js +0 -335
  26. package/dist/config/hooks.js +0 -187
  27. package/dist/config.js +0 -335
  28. package/dist/downloader-state.js +0 -302
  29. package/dist/downloader-ui.js +0 -289
  30. package/dist/gateway/adapters/discord.js +0 -108
  31. package/dist/gateway/adapters/formatting.js +0 -96
  32. package/dist/gateway/adapters/telegram.js +0 -106
  33. package/dist/gateway/adapters/types.js +0 -2
  34. package/dist/gateway/adapters/whatsapp.js +0 -124
  35. package/dist/gateway/auth-policy.js +0 -66
  36. package/dist/gateway/core.js +0 -87
  37. package/dist/gateway/headless-conductor.js +0 -328
  38. package/dist/gateway/message-router.js +0 -23
  39. package/dist/gateway/rate-limiter.js +0 -48
  40. package/dist/gateway/runtime/backend-policy.js +0 -18
  41. package/dist/gateway/runtime/discord-bot.js +0 -104
  42. package/dist/gateway/runtime/discord-main.js +0 -69
  43. package/dist/gateway/session-manager.js +0 -77
  44. package/dist/gateway/types.js +0 -2
  45. package/dist/hardware.js +0 -92
  46. package/dist/hooks.js +0 -187
  47. package/dist/index.js +0 -34
  48. package/dist/input-engine.js +0 -250
  49. package/dist/llama-client.js +0 -227
  50. package/dist/llama-server.js +0 -620
  51. package/dist/llm/llama-client.js +0 -227
  52. package/dist/llm/llama-server.js +0 -620
  53. package/dist/llm/token-counter.js +0 -47
  54. package/dist/memory-store.js +0 -159
  55. package/dist/messages.js +0 -59
  56. package/dist/model-downloader.js +0 -382
  57. package/dist/model-manager-state.js +0 -118
  58. package/dist/model-manager-ui.js +0 -194
  59. package/dist/model-manager.js +0 -190
  60. package/dist/models/hardware.js +0 -92
  61. package/dist/models/model-downloader.js +0 -382
  62. package/dist/models/model-manager.js +0 -190
  63. package/dist/prompt-box.js +0 -78
  64. package/dist/prompt-composer.js +0 -498
  65. package/dist/repl.js +0 -105
  66. package/dist/session-store.js +0 -211
  67. package/dist/spinner.js +0 -76
  68. package/dist/title-box.js +0 -388
  69. package/dist/token-counter.js +0 -47
  70. package/dist/tool-executor.js +0 -415
  71. package/dist/tool-protocol.js +0 -121
  72. package/dist/tool-safety.js +0 -52
  73. package/dist/tui/app.js +0 -2553
  74. package/dist/tui/startup.js +0 -56
  75. package/dist/tui-input-routing.js +0 -53
  76. package/dist/tui.js +0 -51
  77. package/dist/types/app-types.js +0 -2
  78. package/dist/types.js +0 -2
  79. package/dist/ui/colors.js +0 -204
  80. package/dist/ui/downloader/downloader-state.js +0 -302
  81. package/dist/ui/downloader/downloader-ui.js +0 -289
  82. package/dist/ui/input/input-engine.js +0 -250
  83. package/dist/ui/input/tui-input-routing.js +0 -53
  84. package/dist/ui/input/vt-session.js +0 -168
  85. package/dist/ui/messages.js +0 -59
  86. package/dist/ui/model-manager/model-manager-state.js +0 -118
  87. package/dist/ui/model-manager/model-manager-ui.js +0 -194
  88. package/dist/ui/prompt/prompt-box.js +0 -78
  89. package/dist/ui/prompt/prompt-composer.js +0 -498
  90. package/dist/ui/spinner.js +0 -76
  91. package/dist/ui/title-box.js +0 -388
  92. package/dist/ui/tui/app.js +0 -6
  93. package/dist/ui/tui/autocomplete.js +0 -85
  94. package/dist/ui/tui/constants.js +0 -18
  95. package/dist/ui/tui/history.js +0 -29
  96. package/dist/ui/tui/layout.js +0 -341
  97. package/dist/ui/tui/runtime-core.js +0 -2584
  98. package/dist/ui/tui/runtime-utils.js +0 -53
  99. package/dist/ui/tui/start-tui.js +0 -54
  100. package/dist/ui/tui/startup.js +0 -56
  101. package/dist/version.js +0 -51
  102. package/dist/vt-session.js +0 -168
  103. package/install.sh +0 -457
@@ -1,418 +0,0 @@
1
- "use strict";
2
- /** Slash command registry and dispatch system. */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.CommandRegistry = void 0;
5
- exports.parseCommand = parseCommand;
6
- exports.createDefaultRegistry = createDefaultRegistry;
7
- const command_catalog_1 = require("#agent/commands/command-catalog");
8
- const update_check_1 = require("#app/update-check");
9
- const config_1 = require("#config/config");
10
- const memory_store_1 = require("#agent/context/memory-store");
11
- const model_manager_1 = require("#models/model-manager");
12
- const model_downloader_1 = require("#models/model-downloader");
13
- const skills_1 = require("#agent/skills/skills");
14
- function isGenericDescription(description) {
15
- const trimmed = description.trim();
16
- return trimmed.length === 0 || trimmed === "Command";
17
- }
18
- class CommandRegistry {
19
- commands = new Map();
20
- descriptors = new Map();
21
- constructor(initialDescriptors = []) {
22
- for (const descriptor of initialDescriptors) {
23
- const name = descriptor.name.toLowerCase();
24
- this.descriptors.set(name, { ...descriptor, name });
25
- }
26
- }
27
- register(name, handler, description, kind = "builtin") {
28
- const normalizedName = name.toLowerCase();
29
- const existing = this.descriptors.get(normalizedName);
30
- const mergedDescription = existing && !isGenericDescription(existing.description) ? existing.description : description;
31
- this.commands.set(normalizedName, {
32
- name: normalizedName,
33
- description: mergedDescription,
34
- handler
35
- });
36
- this.descriptors.set(normalizedName, {
37
- name: normalizedName,
38
- description: mergedDescription,
39
- kind: existing?.kind ?? kind,
40
- implemented: true
41
- });
42
- }
43
- async dispatch(name, args, context) {
44
- const command = this.commands.get(name.toLowerCase());
45
- if (command) {
46
- return await command.handler(args, context);
47
- }
48
- if (this.descriptors.has(name.toLowerCase())) {
49
- return {
50
- output: `Command /${name} is recognized but not implemented in this TypeScript rewrite yet. ` +
51
- "Type /help to see implemented commands.",
52
- action: "continue"
53
- };
54
- }
55
- return {
56
- output: `Unknown command: /${name}. Type /help for help.`,
57
- action: "continue"
58
- };
59
- }
60
- getHelp() {
61
- const commands = this.listCommands();
62
- const implemented = commands.filter((command) => command.implemented);
63
- const planned = commands.filter((command) => !command.implemented);
64
- const lines = ["Available commands:"];
65
- lines.push("Implemented:");
66
- for (const cmd of implemented) {
67
- lines.push(` /${cmd.name} - ${cmd.description}`);
68
- }
69
- if (planned.length > 0) {
70
- lines.push("");
71
- lines.push("Recognized (not implemented in this rewrite yet):");
72
- for (const cmd of planned) {
73
- lines.push(` /${cmd.name} - ${cmd.description}`);
74
- }
75
- }
76
- return lines.join("\n");
77
- }
78
- has(name) {
79
- return this.descriptors.has(name.toLowerCase());
80
- }
81
- getNames() {
82
- return this.listCommands().map((command) => command.name);
83
- }
84
- listCommands() {
85
- return [...this.descriptors.values()].sort((left, right) => left.name.localeCompare(right.name));
86
- }
87
- getAutocompleteCommands() {
88
- return this.getNames().map((name) => `/${name}`);
89
- }
90
- }
91
- exports.CommandRegistry = CommandRegistry;
92
- function splitCommandArgs(input) {
93
- const matches = input.match(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+/gu);
94
- if (!matches) {
95
- return [];
96
- }
97
- return matches.map((token) => {
98
- if ((token.startsWith('"') && token.endsWith('"')) ||
99
- (token.startsWith("'") && token.endsWith("'"))) {
100
- return token.slice(1, -1);
101
- }
102
- return token;
103
- });
104
- }
105
- function parseTokenCountArg(input) {
106
- const trimmed = input.trim().toLowerCase();
107
- if (trimmed.length === 0) {
108
- return null;
109
- }
110
- const match = trimmed.match(/^(\d+)(k)?$/u);
111
- if (!match) {
112
- return null;
113
- }
114
- const raw = Number(match[1]);
115
- if (!Number.isInteger(raw) || raw <= 0) {
116
- return null;
117
- }
118
- const multiplier = match[2] === "k" ? 1000 : 1;
119
- return raw * multiplier;
120
- }
121
- function parseCommand(input) {
122
- const trimmed = input.trim();
123
- if (!trimmed.startsWith("/"))
124
- return null;
125
- const spaceIndex = trimmed.indexOf(" ");
126
- if (spaceIndex === -1) {
127
- return { command: trimmed.slice(1).toLowerCase(), args: "" };
128
- }
129
- return {
130
- command: trimmed.slice(1, spaceIndex).toLowerCase(),
131
- args: trimmed.slice(spaceIndex + 1).trim()
132
- };
133
- }
134
- function createDefaultRegistry() {
135
- const registry = new CommandRegistry((0, command_catalog_1.loadCommandCatalog)());
136
- const downloadUsage = [
137
- "Model downloader:",
138
- " /download Open interactive downloader",
139
- " /download <hf_url> Download directly from hf.co/huggingface URL",
140
- " /dl ... Alias for /download"
141
- ].join("\n");
142
- const handleDownload = async (args) => {
143
- const trimmed = args.trim();
144
- const tokens = trimmed.length > 0 ? trimmed.split(/\s+/u) : [];
145
- try {
146
- if (tokens.length === 0) {
147
- return {
148
- action: "continue",
149
- uiAction: { type: "open-downloader" }
150
- };
151
- }
152
- const inputArg = tokens.join(" ");
153
- if (inputArg.toLowerCase() === "help") {
154
- return { output: downloadUsage, action: "continue" };
155
- }
156
- if (!(0, model_downloader_1.isHfDownloadUrl)(inputArg)) {
157
- return {
158
- output: `Invalid /download argument. Only direct Hugging Face URLs are supported.\n\n${downloadUsage}`,
159
- action: "continue"
160
- };
161
- }
162
- const parsed = (0, model_downloader_1.parseHfDownloadUrl)(inputArg);
163
- const result = await (0, model_downloader_1.downloadModelFile)(parsed);
164
- const modelsDir = (0, model_downloader_1.resolveDefaultModelsDir)();
165
- return {
166
- output: `Downloaded ${parsed.filename} from ${parsed.repoId}.\n` +
167
- `Saved to: ${result.localPath}\n` +
168
- `Models dir: ${modelsDir}\n` +
169
- `Use with: /model ${parsed.repoId}/${parsed.filename}`,
170
- action: "continue"
171
- };
172
- }
173
- catch (error) {
174
- const message = error instanceof Error ? error.message : String(error);
175
- return {
176
- output: `Download command failed: ${message}`,
177
- action: "continue"
178
- };
179
- }
180
- };
181
- const memorizeUsage = [
182
- "Memory commands:",
183
- " /memorize <fact> Save a memory",
184
- " /memorize list [limit] List recent memories (default 10)",
185
- " /memorize read <memory_id> Read a saved memory",
186
- " /memorize help Show this help"
187
- ].join("\n");
188
- registry.register("help", () => ({
189
- output: registry.getHelp(),
190
- action: "continue"
191
- }), "Show this help");
192
- registry.register("exit", () => ({ output: "Goodbye.", action: "exit" }), "Exit Yips");
193
- registry.register("quit", () => ({ output: "Goodbye.", action: "exit" }), "Exit Yips");
194
- registry.register("restart", () => ({ output: "Restarting Yips.", action: "restart" }), "Restart Yips");
195
- registry.register("clear", () => ({ action: "clear" }), "Clear the screen");
196
- registry.register("new", () => ({ action: "clear" }), "Start a new conversation");
197
- registry.register("model", async (args, context) => {
198
- try {
199
- const trimmed = args.trim();
200
- if (trimmed.length === 0) {
201
- return { action: "continue", uiAction: { type: "open-model-manager" } };
202
- }
203
- let selectedModel = trimmed;
204
- const localModels = await (0, model_manager_1.listLocalModels)({ nicknames: context.config.nicknames });
205
- const matched = (0, model_manager_1.findMatchingModel)(localModels, trimmed);
206
- if (matched) {
207
- selectedModel = matched.id;
208
- }
209
- context.config.backend = "llamacpp";
210
- context.config.model = selectedModel;
211
- await (0, config_1.saveConfig)(context.config);
212
- const matchSuffix = matched ? ` (matched from '${trimmed}')` : " (free-form fallback)";
213
- return { output: `Model set to: ${selectedModel}${matchSuffix}`, action: "continue" };
214
- }
215
- catch (error) {
216
- const message = error instanceof Error ? error.message : String(error);
217
- return {
218
- output: `Model command failed: ${message}`,
219
- action: "continue"
220
- };
221
- }
222
- }, "View or set the current model");
223
- registry.register("sessions", async () => {
224
- return { action: "continue", uiAction: { type: "open-sessions" } };
225
- }, "Interactively select and load a session");
226
- registry.register("vt", async () => ({ action: "continue", uiAction: { type: "open-vt" } }), "Open Virtual Terminal");
227
- registry.register("stream", (_args, context) => {
228
- context.config.streaming = !context.config.streaming;
229
- const state = context.config.streaming ? "enabled" : "disabled";
230
- return { output: `Streaming ${state}.`, action: "continue" };
231
- }, "Toggle streaming mode");
232
- registry.register("verbose", (_args, context) => {
233
- context.config.verbose = !context.config.verbose;
234
- const state = context.config.verbose ? "enabled" : "disabled";
235
- return { output: `Verbose mode ${state}.`, action: "continue" };
236
- }, "Toggle verbose mode");
237
- registry.register("update", async () => {
238
- const currentVersion = await (0, update_check_1.getInstalledPackageVersion)();
239
- const result = await (0, update_check_1.checkForUpdates)(currentVersion);
240
- const guidance = [
241
- "Update options:",
242
- " npm global (canonical): npm install -g @sheepbun/yips@latest",
243
- " npm global (legacy/unscoped): npm install -g yips@latest (may be unavailable)",
244
- " local source: git pull --ff-only && ./install.sh",
245
- " docs: https://yips.dev"
246
- ];
247
- if (result.status === "update-available" && result.latestVersion) {
248
- return {
249
- output: [
250
- `Update available: ${result.currentVersion} -> ${result.latestVersion}`,
251
- ...guidance
252
- ].join("\n"),
253
- action: "continue"
254
- };
255
- }
256
- if (result.status === "up-to-date" && result.latestVersion) {
257
- return {
258
- output: [
259
- `You are up to date (${result.currentVersion}). Latest: ${result.latestVersion}.`,
260
- ...guidance
261
- ].join("\n"),
262
- action: "continue"
263
- };
264
- }
265
- const unknownReason = result.error ? ` (${result.error})` : "";
266
- return {
267
- output: [
268
- `Could not verify latest version${unknownReason}`,
269
- `Current version: ${result.currentVersion}`,
270
- ...guidance
271
- ].join("\n"),
272
- action: "continue"
273
- };
274
- }, "Check for updates and show upgrade commands");
275
- registry.register("tokens", async (args, context) => {
276
- const trimmed = args.trim();
277
- if (trimmed.length === 0) {
278
- if (context.config.tokensMode === "auto") {
279
- return { output: "Tokens mode: auto (dynamic).", action: "continue" };
280
- }
281
- return {
282
- output: `Tokens mode: manual (${context.config.tokensManualMax}).`,
283
- action: "continue"
284
- };
285
- }
286
- if (trimmed.toLowerCase() === "auto") {
287
- context.config.tokensMode = "auto";
288
- await (0, config_1.saveConfig)(context.config);
289
- return {
290
- output: "Token limit mode set to auto.",
291
- action: "continue"
292
- };
293
- }
294
- const parsed = parseTokenCountArg(trimmed);
295
- if (parsed === null) {
296
- return {
297
- output: "Usage: /tokens auto | /tokens <positive_number|numberk>",
298
- action: "continue"
299
- };
300
- }
301
- context.config.tokensMode = "manual";
302
- context.config.tokensManualMax = parsed;
303
- await (0, config_1.saveConfig)(context.config);
304
- return {
305
- output: `Token limit set to ${parsed} (manual).`,
306
- action: "continue"
307
- };
308
- }, "Show or set token counter mode and max");
309
- registry.register("download", (args) => handleDownload(args), "Open the model downloader");
310
- registry.register("dl", (args) => handleDownload(args), "Alias for /download");
311
- registry.register("search", async (args) => {
312
- const query = args.trim();
313
- if (query.length === 0) {
314
- return { action: "continue", output: "Usage: /search <query>" };
315
- }
316
- try {
317
- const output = await (0, skills_1.executeSearchSkill)({ query });
318
- return { action: "continue", output };
319
- }
320
- catch (error) {
321
- const message = error instanceof Error ? error.message : String(error);
322
- return { action: "continue", output: `Search command failed: ${message}` };
323
- }
324
- }, "Search the web (DuckDuckGo)");
325
- registry.register("fetch", async (args) => {
326
- const url = args.trim();
327
- if (url.length === 0) {
328
- return { action: "continue", output: "Usage: /fetch <url>" };
329
- }
330
- try {
331
- const output = await (0, skills_1.executeFetchSkill)({ url });
332
- return { action: "continue", output };
333
- }
334
- catch (error) {
335
- const message = error instanceof Error ? error.message : String(error);
336
- return { action: "continue", output: `Fetch command failed: ${message}` };
337
- }
338
- }, "Retrieve and display content from a URL");
339
- registry.register("memorize", async (args) => {
340
- const trimmed = args.trim();
341
- if (trimmed.length === 0 || trimmed.toLowerCase() === "help") {
342
- return { output: memorizeUsage, action: "continue" };
343
- }
344
- const tokens = trimmed.split(/\s+/u);
345
- const subcommand = (tokens[0] ?? "").toLowerCase();
346
- try {
347
- if (subcommand === "list") {
348
- const limitRaw = tokens[1] ?? "10";
349
- const limit = Number.parseInt(limitRaw, 10);
350
- if (!Number.isInteger(limit) || limit <= 0) {
351
- return { output: "Usage: /memorize list [positive_limit]", action: "continue" };
352
- }
353
- const memories = await (0, memory_store_1.listMemories)(limit);
354
- if (memories.length === 0) {
355
- return { output: "No saved memories yet.", action: "continue" };
356
- }
357
- const lines = ["Saved memories:"];
358
- for (const memory of memories) {
359
- lines.push(`- ${memory.id}: ${memory.preview}`);
360
- }
361
- return { output: lines.join("\n"), action: "continue" };
362
- }
363
- if (subcommand === "read") {
364
- const memoryId = tokens.slice(1).join(" ").trim();
365
- if (memoryId.length === 0) {
366
- return { output: "Usage: /memorize read <memory_id>", action: "continue" };
367
- }
368
- const memory = await (0, memory_store_1.readMemory)(memoryId);
369
- return {
370
- output: [`Memory ${memory.id}:`, memory.content].join("\n\n"),
371
- action: "continue"
372
- };
373
- }
374
- const saved = await (0, memory_store_1.saveMemory)(trimmed);
375
- return {
376
- output: `Saved memory: ${saved.id}`,
377
- action: "continue"
378
- };
379
- }
380
- catch (error) {
381
- const message = error instanceof Error ? error.message : String(error);
382
- return { output: `Memorize command failed: ${message}`, action: "continue" };
383
- }
384
- }, "Save, list, and read long-term memories");
385
- registry.register("nick", async (args, context) => {
386
- try {
387
- const tokens = splitCommandArgs(args.trim());
388
- if (tokens.length < 2) {
389
- return {
390
- output: "Usage: /nick <model_name_or_filename> <nickname>",
391
- action: "continue"
392
- };
393
- }
394
- const [target, ...nicknameTokens] = tokens;
395
- const nickname = nicknameTokens.join(" ").trim();
396
- if (!target || nickname.length === 0) {
397
- return {
398
- output: "Usage: /nick <model_name_or_filename> <nickname>",
399
- action: "continue"
400
- };
401
- }
402
- context.config.nicknames[target] = nickname;
403
- await (0, config_1.saveConfig)(context.config);
404
- return {
405
- output: `Nickname set: ${target} -> ${nickname}`,
406
- action: "continue"
407
- };
408
- }
409
- catch (error) {
410
- const message = error instanceof Error ? error.message : String(error);
411
- return {
412
- output: `Nick command failed: ${message}`,
413
- action: "continue"
414
- };
415
- }
416
- }, "Set a custom nickname for a model");
417
- return registry;
418
- }
@@ -1,118 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runConductorTurn = runConductorTurn;
4
- const tool_protocol_1 = require("#agent/protocol/tool-protocol");
5
- const TOOL_FAILURE_STATUSES = new Set(["error", "denied", "timeout"]);
6
- const CONSECUTIVE_TOOL_FAILURE_PIVOT_THRESHOLD = 2;
7
- function shouldCountAsFailureRound(toolResults) {
8
- if (toolResults.length === 0) {
9
- return false;
10
- }
11
- return toolResults.every((result) => TOOL_FAILURE_STATUSES.has(result.status));
12
- }
13
- async function runConductorTurn(dependencies) {
14
- const maxRounds = dependencies.maxRounds ?? 6;
15
- let rounds = 0;
16
- let finished = false;
17
- let latestOutputTokensPerSecond = null;
18
- let usedTokensExact = null;
19
- let consecutiveFailureRounds = 0;
20
- while (!finished && rounds < maxRounds) {
21
- rounds += 1;
22
- const reply = await dependencies.requestAssistant();
23
- const parsed = (0, tool_protocol_1.parseToolProtocol)(reply.text);
24
- const assistantText = parsed.assistantText.trim();
25
- if (assistantText.length > 0) {
26
- dependencies.onAssistantText(assistantText, reply.rendered);
27
- dependencies.history.push({ role: "assistant", content: assistantText });
28
- }
29
- const completionTokens = typeof reply.completionTokens === "number" && reply.completionTokens > 0
30
- ? reply.completionTokens
31
- : dependencies.estimateCompletionTokens(reply.text);
32
- latestOutputTokensPerSecond = dependencies.computeTokensPerSecond(completionTokens, reply.generationDurationMs ?? 0);
33
- usedTokensExact =
34
- typeof reply.totalTokens === "number" && reply.totalTokens >= 0
35
- ? reply.totalTokens
36
- : dependencies.estimateHistoryTokens(dependencies.history);
37
- if (parsed.toolCalls.length === 0 &&
38
- parsed.subagentCalls.length === 0 &&
39
- parsed.skillCalls.length === 0) {
40
- finished = true;
41
- break;
42
- }
43
- if (parsed.toolCalls.length > 0) {
44
- const toolResults = await dependencies.executeToolCalls(parsed.toolCalls);
45
- dependencies.history.push({
46
- role: "system",
47
- content: `Tool results: ${JSON.stringify(toolResults)}`
48
- });
49
- if (shouldCountAsFailureRound(toolResults)) {
50
- consecutiveFailureRounds += 1;
51
- if (consecutiveFailureRounds >= CONSECUTIVE_TOOL_FAILURE_PIVOT_THRESHOLD) {
52
- dependencies.history.push({
53
- role: "system",
54
- content: "Automatic pivot: consecutive tool failures detected. Try a different approach, use different tools, or ask the user for clarification."
55
- });
56
- dependencies.onWarning("Consecutive tool failures detected. Attempting an alternative approach.");
57
- consecutiveFailureRounds = 0;
58
- }
59
- }
60
- else {
61
- consecutiveFailureRounds = 0;
62
- }
63
- }
64
- if (parsed.skillCalls.length > 0) {
65
- if (!dependencies.executeSkillCalls) {
66
- dependencies.onWarning("Skill invocation requested, but no skill runner is configured.");
67
- const fallbackResults = parsed.skillCalls.map((call) => ({
68
- callId: call.id,
69
- skill: call.name,
70
- status: "error",
71
- output: "Skill invocation is unavailable in this runtime."
72
- }));
73
- dependencies.history.push({
74
- role: "system",
75
- content: `Skill results: ${JSON.stringify(fallbackResults)}`
76
- });
77
- }
78
- else {
79
- const skillResults = await dependencies.executeSkillCalls(parsed.skillCalls);
80
- dependencies.history.push({
81
- role: "system",
82
- content: `Skill results: ${JSON.stringify(skillResults)}`
83
- });
84
- }
85
- }
86
- if (parsed.subagentCalls.length > 0) {
87
- if (!dependencies.executeSubagentCalls) {
88
- dependencies.onWarning("Subagent delegation requested, but no subagent runner is configured.");
89
- const fallbackResults = parsed.subagentCalls.map((call) => ({
90
- callId: call.id,
91
- status: "error",
92
- output: "Subagent delegation is unavailable in this runtime."
93
- }));
94
- dependencies.history.push({
95
- role: "system",
96
- content: `Subagent results: ${JSON.stringify(fallbackResults)}`
97
- });
98
- }
99
- else {
100
- const subagentResults = await dependencies.executeSubagentCalls(parsed.subagentCalls);
101
- dependencies.history.push({
102
- role: "system",
103
- content: `Subagent results: ${JSON.stringify(subagentResults)}`
104
- });
105
- }
106
- }
107
- dependencies.onRoundComplete?.();
108
- }
109
- if (!finished) {
110
- dependencies.onWarning("Stopped tool chaining after max depth (6 rounds).");
111
- }
112
- return {
113
- finished,
114
- rounds,
115
- latestOutputTokensPerSecond,
116
- usedTokensExact
117
- };
118
- }
@@ -1,68 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findCodeMdCandidates = findCodeMdCandidates;
4
- exports.loadCodeContext = loadCodeContext;
5
- exports.toCodeContextSystemMessage = toCodeContextSystemMessage;
6
- const promises_1 = require("node:fs/promises");
7
- const node_path_1 = require("node:path");
8
- const CODE_MD_FILENAME = "CODE.md";
9
- const MAX_CODE_MD_CHARS = 24_000;
10
- function truncateContent(content, maxChars) {
11
- if (content.length <= maxChars) {
12
- return { content, truncated: false };
13
- }
14
- return {
15
- content: `${content.slice(0, maxChars)}\n\n[CODE.md truncated to ${maxChars} characters]`,
16
- truncated: true
17
- };
18
- }
19
- async function isReadable(path) {
20
- try {
21
- await (0, promises_1.access)(path);
22
- return true;
23
- }
24
- catch {
25
- return false;
26
- }
27
- }
28
- function findCodeMdCandidates(startDir) {
29
- const candidates = [];
30
- let current = (0, node_path_1.resolve)(startDir);
31
- for (;;) {
32
- candidates.push((0, node_path_1.join)(current, CODE_MD_FILENAME));
33
- const parent = (0, node_path_1.dirname)(current);
34
- if (parent === current) {
35
- break;
36
- }
37
- current = parent;
38
- }
39
- return candidates;
40
- }
41
- async function loadCodeContext(startDir = process.cwd()) {
42
- const candidates = findCodeMdCandidates(startDir);
43
- for (const candidate of candidates) {
44
- if (!(await isReadable(candidate))) {
45
- continue;
46
- }
47
- try {
48
- const raw = await (0, promises_1.readFile)(candidate, "utf8");
49
- const trimmed = raw.trim();
50
- if (trimmed.length === 0) {
51
- continue;
52
- }
53
- const truncated = truncateContent(trimmed, MAX_CODE_MD_CHARS);
54
- return {
55
- path: candidate,
56
- content: truncated.content,
57
- truncated: truncated.truncated
58
- };
59
- }
60
- catch {
61
- // continue to parent candidate
62
- }
63
- }
64
- return null;
65
- }
66
- function toCodeContextSystemMessage(context) {
67
- return [`Project context from CODE.md (${context.path}):`, "", context.content].join("\n");
68
- }