@agi-cli/server 0.1.95 → 0.1.96

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agi-cli/server",
3
- "version": "0.1.95",
3
+ "version": "0.1.96",
4
4
  "description": "HTTP API server for AGI CLI",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -29,8 +29,8 @@
29
29
  "typecheck": "tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "@agi-cli/sdk": "0.1.95",
33
- "@agi-cli/database": "0.1.95",
32
+ "@agi-cli/sdk": "0.1.96",
33
+ "@agi-cli/database": "0.1.96",
34
34
  "drizzle-orm": "^0.44.5",
35
35
  "hono": "^4.9.9",
36
36
  "zod": "^4.1.8"
@@ -6,6 +6,7 @@ import { publish } from '../events/bus.ts';
6
6
  export type StepExecutionState = {
7
7
  chain: Promise<void>;
8
8
  failed: boolean;
9
+ failedToolName?: string;
9
10
  };
10
11
 
11
12
  export type ToolAdapterContext = {
@@ -10,6 +10,13 @@ import type {
10
10
  } from '../runtime/tool-context.ts';
11
11
  import { isToolError } from '@agi-cli/sdk/tools/error';
12
12
 
13
+ function isSkippedToolCallError(error: unknown): boolean {
14
+ if (!isToolError(error)) return false;
15
+ const details = (error as { details?: unknown }).details;
16
+ if (!details || typeof details !== 'object') return false;
17
+ return 'skippedTool' in (details as Record<string, unknown>);
18
+ }
19
+
13
20
  export type { ToolAdapterContext } from '../runtime/tool-context.ts';
14
21
 
15
22
  type ToolExecuteSignature = Tool['execute'] extends (
@@ -50,6 +57,10 @@ export function adaptTools(
50
57
  ) {
51
58
  const out: Record<string, Tool> = {};
52
59
  const pendingCalls = new Map<string, PendingCallMeta[]>();
60
+ const failureState: { active: boolean; toolName?: string } = {
61
+ active: false,
62
+ toolName: undefined,
63
+ };
53
64
  let firstToolCallReported = false;
54
65
 
55
66
  if (!ctx.stepExecution) {
@@ -289,19 +300,32 @@ export function adaptTools(
289
300
  : 0;
290
301
  let stepState = stepStates.get(stepKey);
291
302
  if (!stepState) {
292
- stepState = { chain: Promise.resolve(), failed: false };
303
+ stepState = {
304
+ chain: Promise.resolve(),
305
+ failed: false,
306
+ failedToolName: undefined,
307
+ };
293
308
  stepStates.set(stepKey, stepState);
294
309
  }
295
310
 
296
311
  const executeWithGuards = async (): Promise<ToolExecuteReturn> => {
297
312
  try {
298
- if (stepState.failed) {
299
- const skipError = {
300
- ok: false,
301
- error: `Cannot execute "${name}" because a previous tool call in this step failed. Retry the failing tool before continuing with "${name}".`,
302
- details: { skippedTool: name },
303
- };
304
- throw skipError;
313
+ if (failureState.active) {
314
+ const expectedTool = failureState.toolName;
315
+ if (!expectedTool || expectedTool !== name) {
316
+ const skipError = {
317
+ ok: false,
318
+ error: expectedTool
319
+ ? `Cannot execute "${name}" because "${expectedTool}" failed earlier in this step. Retry "${expectedTool}" before using other tools.`
320
+ : `Cannot execute "${name}" because a previous tool call in this session failed. Retry that tool before continuing with "${name}".`,
321
+ details: {
322
+ skippedTool: name,
323
+ reason: 'previous_tool_failed',
324
+ expectedTool,
325
+ },
326
+ };
327
+ throw skipError;
328
+ }
305
329
  }
306
330
  // Handle session-relative paths and cwd tools
307
331
  let res: ToolExecuteReturn | { cwd: string } | null | undefined;
@@ -415,6 +439,12 @@ export function adaptTools(
415
439
 
416
440
  // Special-case: keep progress_update result lightweight; publish first, persist best-effort
417
441
  if (name === 'progress_update') {
442
+ stepState.failed = false;
443
+ stepState.failedToolName = undefined;
444
+ if (failureState.active && failureState.toolName === name) {
445
+ failureState.active = false;
446
+ failureState.toolName = undefined;
447
+ }
418
448
  publish({
419
449
  type: 'tool.result',
420
450
  sessionId: ctx.sessionId,
@@ -444,6 +474,13 @@ export function adaptTools(
444
474
  return result as ToolExecuteReturn;
445
475
  }
446
476
 
477
+ stepState.failed = false;
478
+ stepState.failedToolName = undefined;
479
+ if (failureState.active && failureState.toolName === name) {
480
+ failureState.active = false;
481
+ failureState.toolName = undefined;
482
+ }
483
+
447
484
  await ctx.db.insert(messageParts).values({
448
485
  id: resultPartId,
449
486
  messageId: ctx.messageId,
@@ -510,7 +547,14 @@ export function adaptTools(
510
547
  }
511
548
  return result as ToolExecuteReturn;
512
549
  } catch (error) {
550
+ if (isSkippedToolCallError(error)) {
551
+ throw error;
552
+ }
553
+
513
554
  stepState.failed = true;
555
+ stepState.failedToolName = name;
556
+ failureState.active = true;
557
+ failureState.toolName = name;
514
558
 
515
559
  // Tool execution failed
516
560
  if (