@strayl/agent 0.1.15 → 0.1.17

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 (2) hide show
  1. package/dist/agent.js +40 -2
  2. package/package.json +1 -1
package/dist/agent.js CHANGED
@@ -133,7 +133,15 @@ Use sub-agents aggressively for parallel speedup:
133
133
  Create todos when: 3+ files involved, installing packages + writing code, sequential dependencies.
134
134
  Do NOT create todos for: single file edits, research only, single commands.
135
135
  Granularity: ONE completable action per todo.
136
- Lifecycle: create todos \u2192 set in_progress \u2192 completed \u2192 write_todos({ todos: [] }) when all done.`;
136
+ Lifecycle: create todos \u2192 set in_progress \u2192 completed \u2192 write_todos({ todos: [] }) when all done.
137
+
138
+ ## Task Completion
139
+ - Keep working until the user's request is FULLY implemented \u2014 do not stop after one step of a multi-step task
140
+ - After each action, verify the result and proceed to the next step
141
+ - A task is complete ONLY when: code is written, verified (build/test), and the feature works end-to-end
142
+ - If you output a text response without using tools, the system will ask you to continue \u2014 use your tools to make progress
143
+ - When truly done, include a clear completion statement (e.g., "The task is complete" or "All done")
144
+ - NEVER stop just because you explained what you did \u2014 actually verify it works`;
137
145
  PLAN_MODE_SYSTEM_PROMPT = `
138
146
 
139
147
  ## PLAN MODE \u2014 YOU CAN ONLY DO 3 THINGS
@@ -7225,6 +7233,12 @@ var LLMClient = class {
7225
7233
  }
7226
7234
  }
7227
7235
  }
7236
+ if (!emittedToolCalls && partialToolCalls.size > 0) {
7237
+ const hasIncomplete = [...partialToolCalls.values()].some((p) => !p.id || !p.name);
7238
+ if (hasIncomplete) {
7239
+ throw new Error("LLM stream truncated: incomplete tool calls received (provider may have timed out)");
7240
+ }
7241
+ }
7228
7242
  }
7229
7243
  };
7230
7244
 
@@ -13549,6 +13563,8 @@ async function runAgent(config) {
13549
13563
  const maxIterations = config.maxIterations ?? 200;
13550
13564
  let consecutiveLLMErrors = 0;
13551
13565
  const MAX_CONSECUTIVE_LLM_ERRORS = 5;
13566
+ let consecutiveTextOnly = 0;
13567
+ const MAX_CONSECUTIVE_TEXT_ONLY = 3;
13552
13568
  if (config.restoreCheckpoint) {
13553
13569
  const cp = config.restoreCheckpoint;
13554
13570
  context.restoreMessages(cp.messages);
@@ -13707,6 +13723,17 @@ ${IMPLEMENTATION_MODE_PROMPT2}`);
13707
13723
  emitter.emit({ type: "session-end", usage: context.totalUsage(), exit_reason: "cancelled" });
13708
13724
  return;
13709
13725
  }
13726
+ if (!assistantText.trim() && completedToolCalls.length === 0) {
13727
+ consecutiveLLMErrors++;
13728
+ if (consecutiveLLMErrors >= MAX_CONSECUTIVE_LLM_ERRORS) {
13729
+ emitter.emit({ type: "error", message: `LLM returned empty response ${consecutiveLLMErrors} times in a row`, recoverable: false });
13730
+ break;
13731
+ }
13732
+ const backoffMs = Math.min(2e3 * Math.pow(2, consecutiveLLMErrors - 1), 16e3);
13733
+ emitter.emit({ type: "error", message: `LLM returned empty response (${consecutiveLLMErrors}/${MAX_CONSECUTIVE_LLM_ERRORS}) \u2014 retrying in ${backoffMs / 1e3}s`, recoverable: true });
13734
+ await new Promise((r) => setTimeout(r, backoffMs));
13735
+ continue;
13736
+ }
13710
13737
  consecutiveLLMErrors = 0;
13711
13738
  context.addAssistant(assistantText, completedToolCalls.length > 0 ? completedToolCalls : void 0);
13712
13739
  {
@@ -13723,7 +13750,18 @@ ${IMPLEMENTATION_MODE_PROMPT2}`);
13723
13750
  context_left_percent: leftPercent
13724
13751
  });
13725
13752
  }
13726
- if (completedToolCalls.length === 0) break;
13753
+ if (completedToolCalls.length === 0) {
13754
+ consecutiveTextOnly++;
13755
+ const completionPattern = /\b(task\s+(is\s+)?complete|all\s+done|finished|ready\s+to\s+(use|go|test)|work\s+is\s+done|that'?s\s+it|nothing\s+(else|more)\s+to\s+do|i'?m\s+done)\b/i;
13756
+ const isExplicitlyDone = completionPattern.test(assistantText);
13757
+ if (isExplicitlyDone || consecutiveTextOnly >= MAX_CONSECUTIVE_TEXT_ONLY) {
13758
+ break;
13759
+ }
13760
+ context.addUser("[System: You responded with text but did not use any tools. If the task is complete, say so explicitly. Otherwise, continue working \u2014 use your tools to make progress on the user's request.]");
13761
+ emitter.emit({ type: "text-delta", text: "" });
13762
+ continue;
13763
+ }
13764
+ consecutiveTextOnly = 0;
13727
13765
  for (const tc of completedToolCalls) {
13728
13766
  if (stdin.isCancelled()) {
13729
13767
  context.addToolResult(tc.id, tc.function.name, JSON.stringify({ error: "Cancelled by user." }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strayl/agent",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"