@strayl/agent 0.1.10 → 0.1.12

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/dist/agent.js CHANGED
@@ -7201,13 +7201,11 @@ var LLMClient = class {
7201
7201
  }
7202
7202
  }
7203
7203
  }
7204
- if (choice.finish_reason === "tool_calls" || choice.finish_reason === "stop") {
7205
- if (!emittedToolCalls) {
7206
- emittedToolCalls = true;
7207
- for (const [, partial] of partialToolCalls) {
7208
- if (partial.id && partial.name) {
7209
- yield { type: "tool_call_complete", id: partial.id, name: partial.name, arguments: partial.arguments };
7210
- }
7204
+ if (choice.finish_reason && !emittedToolCalls) {
7205
+ emittedToolCalls = true;
7206
+ for (const [, partial] of partialToolCalls) {
7207
+ if (partial.id && partial.name) {
7208
+ yield { type: "tool_call_complete", id: partial.id, name: partial.name, arguments: partial.arguments };
7211
7209
  }
7212
7210
  }
7213
7211
  }
@@ -7222,7 +7220,7 @@ var LLMClient = class {
7222
7220
  }
7223
7221
  if (!emittedToolCalls) {
7224
7222
  for (const [, partial] of partialToolCalls) {
7225
- if (partial.id && partial.name && partial.arguments) {
7223
+ if (partial.id && partial.name) {
7226
7224
  yield { type: "tool_call_complete", id: partial.id, name: partial.name, arguments: partial.arguments };
7227
7225
  }
7228
7226
  }
@@ -7656,7 +7654,11 @@ var HITLManager = class {
7656
7654
  this.idMap.set(id, safe);
7657
7655
  return safe;
7658
7656
  }
7659
- async waitForResponse(id) {
7657
+ /**
7658
+ * Wait for HITL response, polling both file system and optional stdin drain callback.
7659
+ * @param onPoll - Called each poll iteration so the caller can drain stdin and write pending responses.
7660
+ */
7661
+ async waitForResponse(id, onPoll) {
7660
7662
  const safe = this.safeId(id);
7661
7663
  const filePath = path2.join(this.dir, `${safe}.json`);
7662
7664
  const maxWait = 30 * 60 * 1e3;
@@ -7665,6 +7667,7 @@ var HITLManager = class {
7665
7667
  if (await this.isCancelled()) {
7666
7668
  return { decision: "reject" };
7667
7669
  }
7670
+ if (onPoll) await onPoll();
7668
7671
  try {
7669
7672
  const content = await fs.readFile(filePath, "utf-8");
7670
7673
  const response = JSON.parse(content);
@@ -13401,15 +13404,16 @@ function createPlanModeMiddleware(getMode) {
13401
13404
  name: "planModeFilter",
13402
13405
  filterTools: (tools) => {
13403
13406
  const mode2 = getMode();
13407
+ const getName2 = (t) => t.type === "function" ? t.function.name : "";
13404
13408
  if (mode2 === "plan") {
13405
- return tools.filter((t) => !PLAN_MODE_BLOCKED.has(t.function.name));
13409
+ return tools.filter((t) => !PLAN_MODE_BLOCKED.has(getName2(t)));
13406
13410
  }
13407
13411
  if (mode2 === "implement") {
13408
13412
  return tools.filter(
13409
- (t) => !IMPL_MODE_BLOCKED.has(t.function.name) && t.function.name !== "enterPlanMode"
13413
+ (t) => !IMPL_MODE_BLOCKED.has(getName2(t)) && getName2(t) !== "enterPlanMode"
13410
13414
  );
13411
13415
  }
13412
- return tools.filter((t) => !IMPL_MODE_BLOCKED.has(t.function.name));
13416
+ return tools.filter((t) => !IMPL_MODE_BLOCKED.has(getName2(t)));
13413
13417
  },
13414
13418
  wrapToolCall: async (call, next) => {
13415
13419
  const mode2 = getMode();
@@ -13545,6 +13549,8 @@ async function runAgent(config) {
13545
13549
  const maxIterations = config.maxIterations ?? 200;
13546
13550
  let consecutiveLLMErrors = 0;
13547
13551
  const MAX_CONSECUTIVE_LLM_ERRORS = 5;
13552
+ let consecutiveNoToolCalls = 0;
13553
+ const MAX_NO_TOOL_RETRIES = 3;
13548
13554
  if (config.restoreCheckpoint) {
13549
13555
  const cp = config.restoreCheckpoint;
13550
13556
  context.restoreMessages(cp.messages);
@@ -13717,7 +13723,15 @@ ${IMPLEMENTATION_MODE_PROMPT2}`);
13717
13723
  context_left_percent: leftPercent
13718
13724
  });
13719
13725
  }
13720
- if (completedToolCalls.length === 0) break;
13726
+ if (completedToolCalls.length === 0) {
13727
+ consecutiveNoToolCalls++;
13728
+ if (consecutiveNoToolCalls >= MAX_NO_TOOL_RETRIES) break;
13729
+ context.addUser(
13730
+ "[System] You output text without calling any tools. Do not think out loud \u2014 use your tools to take action. If you need to communicate with the user, call the askUser tool. If you need to create a plan, call the writePlan tool. Continue working on the task."
13731
+ );
13732
+ continue;
13733
+ }
13734
+ consecutiveNoToolCalls = 0;
13721
13735
  for (const tc of completedToolCalls) {
13722
13736
  if (stdin.isCancelled()) {
13723
13737
  context.addToolResult(tc.id, tc.function.name, JSON.stringify({ error: "Cancelled by user." }));
@@ -13738,7 +13752,14 @@ ${IMPLEMENTATION_MODE_PROMPT2}`);
13738
13752
  const toolDef = registry.get(tc.function.name);
13739
13753
  if (toolDef?.hitl) {
13740
13754
  emitter.emit({ type: "hitl-request", id: tc.id, safe_id: hitl.safeId(tc.id), tool: tc.function.name, args: parsedArgs });
13741
- const response = await hitl.waitForResponse(tc.id);
13755
+ const response = await hitl.waitForResponse(tc.id, async () => {
13756
+ for (const cmd of stdin.drain()) {
13757
+ if (cmd.type === "hitl-response") {
13758
+ await hitl.writeResponse(cmd.id, { decision: cmd.decision, data: cmd.data });
13759
+ } else if (cmd.type === "cancel") {
13760
+ }
13761
+ }
13762
+ });
13742
13763
  if (response.decision === "reject") {
13743
13764
  const rejectResult = JSON.stringify({ error: "User rejected this action." });
13744
13765
  emitter.emit({ type: "tool-result", id: tc.id, name: tc.function.name, output: rejectResult });
package/package.json CHANGED
@@ -1,27 +1,27 @@
1
- {
2
- "name": "@strayl/agent",
3
- "version": "0.1.10",
4
- "type": "module",
5
- "publishConfig": {
6
- "access": "public"
7
- },
8
- "main": "dist/index.js",
9
- "files": [
10
- "dist",
11
- "skills"
12
- ],
13
- "scripts": {
14
- "build": "esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/agent.js --external:fsevents",
15
- "dev": "tsx watch src/index.ts"
16
- },
17
- "dependencies": {
18
- "openai": "^5.8.0",
19
- "zod": "^3.25.67"
20
- },
21
- "devDependencies": {
22
- "esbuild": "^0.25.5",
23
- "tsx": "^4.19.4",
24
- "typescript": "^5.8.3",
25
- "@types/node": "^22.15.31"
26
- }
27
- }
1
+ {
2
+ "name": "@strayl/agent",
3
+ "version": "0.1.12",
4
+ "type": "module",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "dist/index.js",
9
+ "files": [
10
+ "dist",
11
+ "skills"
12
+ ],
13
+ "scripts": {
14
+ "build": "esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/agent.js --external:fsevents",
15
+ "dev": "tsx watch src/index.ts"
16
+ },
17
+ "dependencies": {
18
+ "openai": "^5.8.0",
19
+ "zod": "^3.25.67"
20
+ },
21
+ "devDependencies": {
22
+ "esbuild": "^0.25.5",
23
+ "tsx": "^4.19.4",
24
+ "typescript": "^5.8.3",
25
+ "@types/node": "^22.15.31"
26
+ }
27
+ }