@runtypelabs/sdk 1.10.1 → 1.11.0
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/index.cjs +156 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.mjs +156 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -394,7 +394,7 @@ interface BuiltInTool {
|
|
|
394
394
|
id: string;
|
|
395
395
|
name: string;
|
|
396
396
|
description: string;
|
|
397
|
-
category: 'image_generation' | 'web_search' | 'code_execution' | 'file_operations' | 'data_analysis';
|
|
397
|
+
category: 'image_generation' | 'web_search' | 'web_scraping' | 'code_execution' | 'file_operations' | 'data_analysis' | 'knowledge_retrieval' | 'text_to_speech' | 'voice_processing' | 'third_party_api';
|
|
398
398
|
providers: string[];
|
|
399
399
|
parametersSchema: JSONSchema;
|
|
400
400
|
defaultConfig?: JsonObject;
|
|
@@ -3172,6 +3172,8 @@ interface RunTaskToolTraceSlice {
|
|
|
3172
3172
|
verificationPassed: boolean;
|
|
3173
3173
|
verificationBlocked: boolean;
|
|
3174
3174
|
localToolLoopGuardTriggered: boolean;
|
|
3175
|
+
/** Per-file write counts for detecting excessive same-file rewrites */
|
|
3176
|
+
writeCountByPath: Record<string, number>;
|
|
3175
3177
|
forcedTurnEndReason?: string;
|
|
3176
3178
|
bestCandidatePath?: string;
|
|
3177
3179
|
bestCandidateReason?: string;
|
|
@@ -4257,7 +4259,7 @@ interface ExecuteWithLocalToolsOptions {
|
|
|
4257
4259
|
/**
|
|
4258
4260
|
* Status of a long-task agent run
|
|
4259
4261
|
*/
|
|
4260
|
-
type RunTaskStatus = 'running' | 'complete' | 'paused' | 'error' | 'budget_exceeded' | 'max_sessions';
|
|
4262
|
+
type RunTaskStatus = 'running' | 'complete' | 'paused' | 'error' | 'budget_exceeded' | 'max_sessions' | 'stalled';
|
|
4261
4263
|
/**
|
|
4262
4264
|
* A continuation point within a long-task run (for marathon resume).
|
|
4263
4265
|
*/
|
|
@@ -4389,6 +4391,10 @@ interface RunTaskState {
|
|
|
4389
4391
|
workflowVariant?: string;
|
|
4390
4392
|
/** Number of consecutive sessions where verification was blocked */
|
|
4391
4393
|
consecutiveBlockedVerificationSessions?: number;
|
|
4394
|
+
/** Reason the last TASK_COMPLETE claim was rejected (set when detected but not accepted) */
|
|
4395
|
+
lastCompletionRejectionReason?: string;
|
|
4396
|
+
/** Number of consecutive sessions with no tool actions (for stall detection) */
|
|
4397
|
+
consecutiveEmptySessions?: number;
|
|
4392
4398
|
/** Arbitrary bag for workflow-specific data */
|
|
4393
4399
|
workflowState?: Record<string, unknown>;
|
|
4394
4400
|
}
|
|
@@ -4426,7 +4432,7 @@ interface RunTaskContextCompactionEvent {
|
|
|
4426
4432
|
}
|
|
4427
4433
|
type RunTaskOnContextCompaction = (event: RunTaskContextCompactionEvent) => void | Promise<void>;
|
|
4428
4434
|
interface RunTaskContextNoticeEvent {
|
|
4429
|
-
kind: 'provider_native_compaction' | 'tool_definitions_warning';
|
|
4435
|
+
kind: 'provider_native_compaction' | 'tool_definitions_warning' | 'server_network_retry';
|
|
4430
4436
|
sessionIndex: number;
|
|
4431
4437
|
model?: string;
|
|
4432
4438
|
message: string;
|
|
@@ -4690,6 +4696,7 @@ declare class AgentsEndpoint {
|
|
|
4690
4696
|
private hasSufficientResearchEvidence;
|
|
4691
4697
|
private buildEffectiveSessionOutput;
|
|
4692
4698
|
private canAcceptTaskCompletion;
|
|
4699
|
+
private computeCompletionRejectionReason;
|
|
4693
4700
|
private summarizeUnknownForTrace;
|
|
4694
4701
|
private summarizeTextBlockForTrace;
|
|
4695
4702
|
private parseVerificationResult;
|
|
@@ -4747,6 +4754,12 @@ declare class AgentsEndpoint {
|
|
|
4747
4754
|
* ```
|
|
4748
4755
|
*/
|
|
4749
4756
|
runTask(id: string, options: RunTaskOptions): Promise<RunTaskResult>;
|
|
4757
|
+
/** Error message patterns from server-side sessions that indicate a transient network failure
|
|
4758
|
+
* (e.g. AI provider connection dropped). These are retried automatically. */
|
|
4759
|
+
private static readonly RETRYABLE_SESSION_ERROR_PATTERNS;
|
|
4760
|
+
/** Returns true if a server-side session error message indicates a transient
|
|
4761
|
+
* network failure that is safe to retry. */
|
|
4762
|
+
static isRetryableSessionError(errorMessage?: string): boolean;
|
|
4750
4763
|
/** Stop phrases that indicate the agent considers its task complete. */
|
|
4751
4764
|
private static readonly STOP_PHRASES;
|
|
4752
4765
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -394,7 +394,7 @@ interface BuiltInTool {
|
|
|
394
394
|
id: string;
|
|
395
395
|
name: string;
|
|
396
396
|
description: string;
|
|
397
|
-
category: 'image_generation' | 'web_search' | 'code_execution' | 'file_operations' | 'data_analysis';
|
|
397
|
+
category: 'image_generation' | 'web_search' | 'web_scraping' | 'code_execution' | 'file_operations' | 'data_analysis' | 'knowledge_retrieval' | 'text_to_speech' | 'voice_processing' | 'third_party_api';
|
|
398
398
|
providers: string[];
|
|
399
399
|
parametersSchema: JSONSchema;
|
|
400
400
|
defaultConfig?: JsonObject;
|
|
@@ -3172,6 +3172,8 @@ interface RunTaskToolTraceSlice {
|
|
|
3172
3172
|
verificationPassed: boolean;
|
|
3173
3173
|
verificationBlocked: boolean;
|
|
3174
3174
|
localToolLoopGuardTriggered: boolean;
|
|
3175
|
+
/** Per-file write counts for detecting excessive same-file rewrites */
|
|
3176
|
+
writeCountByPath: Record<string, number>;
|
|
3175
3177
|
forcedTurnEndReason?: string;
|
|
3176
3178
|
bestCandidatePath?: string;
|
|
3177
3179
|
bestCandidateReason?: string;
|
|
@@ -4257,7 +4259,7 @@ interface ExecuteWithLocalToolsOptions {
|
|
|
4257
4259
|
/**
|
|
4258
4260
|
* Status of a long-task agent run
|
|
4259
4261
|
*/
|
|
4260
|
-
type RunTaskStatus = 'running' | 'complete' | 'paused' | 'error' | 'budget_exceeded' | 'max_sessions';
|
|
4262
|
+
type RunTaskStatus = 'running' | 'complete' | 'paused' | 'error' | 'budget_exceeded' | 'max_sessions' | 'stalled';
|
|
4261
4263
|
/**
|
|
4262
4264
|
* A continuation point within a long-task run (for marathon resume).
|
|
4263
4265
|
*/
|
|
@@ -4389,6 +4391,10 @@ interface RunTaskState {
|
|
|
4389
4391
|
workflowVariant?: string;
|
|
4390
4392
|
/** Number of consecutive sessions where verification was blocked */
|
|
4391
4393
|
consecutiveBlockedVerificationSessions?: number;
|
|
4394
|
+
/** Reason the last TASK_COMPLETE claim was rejected (set when detected but not accepted) */
|
|
4395
|
+
lastCompletionRejectionReason?: string;
|
|
4396
|
+
/** Number of consecutive sessions with no tool actions (for stall detection) */
|
|
4397
|
+
consecutiveEmptySessions?: number;
|
|
4392
4398
|
/** Arbitrary bag for workflow-specific data */
|
|
4393
4399
|
workflowState?: Record<string, unknown>;
|
|
4394
4400
|
}
|
|
@@ -4426,7 +4432,7 @@ interface RunTaskContextCompactionEvent {
|
|
|
4426
4432
|
}
|
|
4427
4433
|
type RunTaskOnContextCompaction = (event: RunTaskContextCompactionEvent) => void | Promise<void>;
|
|
4428
4434
|
interface RunTaskContextNoticeEvent {
|
|
4429
|
-
kind: 'provider_native_compaction' | 'tool_definitions_warning';
|
|
4435
|
+
kind: 'provider_native_compaction' | 'tool_definitions_warning' | 'server_network_retry';
|
|
4430
4436
|
sessionIndex: number;
|
|
4431
4437
|
model?: string;
|
|
4432
4438
|
message: string;
|
|
@@ -4690,6 +4696,7 @@ declare class AgentsEndpoint {
|
|
|
4690
4696
|
private hasSufficientResearchEvidence;
|
|
4691
4697
|
private buildEffectiveSessionOutput;
|
|
4692
4698
|
private canAcceptTaskCompletion;
|
|
4699
|
+
private computeCompletionRejectionReason;
|
|
4693
4700
|
private summarizeUnknownForTrace;
|
|
4694
4701
|
private summarizeTextBlockForTrace;
|
|
4695
4702
|
private parseVerificationResult;
|
|
@@ -4747,6 +4754,12 @@ declare class AgentsEndpoint {
|
|
|
4747
4754
|
* ```
|
|
4748
4755
|
*/
|
|
4749
4756
|
runTask(id: string, options: RunTaskOptions): Promise<RunTaskResult>;
|
|
4757
|
+
/** Error message patterns from server-side sessions that indicate a transient network failure
|
|
4758
|
+
* (e.g. AI provider connection dropped). These are retried automatically. */
|
|
4759
|
+
private static readonly RETRYABLE_SESSION_ERROR_PATTERNS;
|
|
4760
|
+
/** Returns true if a server-side session error message indicates a transient
|
|
4761
|
+
* network failure that is safe to retry. */
|
|
4762
|
+
static isRetryableSessionError(errorMessage?: string): boolean;
|
|
4750
4763
|
/** Stop phrases that indicate the agent considers its task complete. */
|
|
4751
4764
|
private static readonly STOP_PHRASES;
|
|
4752
4765
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -2240,7 +2240,10 @@ function hasSufficientResearchEvidence(state) {
|
|
|
2240
2240
|
return false;
|
|
2241
2241
|
}
|
|
2242
2242
|
if (state.isCreationTask) {
|
|
2243
|
-
|
|
2243
|
+
const hasReadFiles = (state.recentReadPaths?.length || 0) >= 1;
|
|
2244
|
+
const isDiscoveryKey = (key) => key.startsWith("tree_directory:") || key.startsWith("list_directory:") || key === "server:tree_directory" || key === "server:list_directory";
|
|
2245
|
+
const hasPerformedDiscovery = state.sessions.some((session) => session.actionKeys?.some(isDiscoveryKey)) || (state.recentActionKeys?.some(isDiscoveryKey) ?? false);
|
|
2246
|
+
return hasReadFiles || hasPerformedDiscovery;
|
|
2244
2247
|
}
|
|
2245
2248
|
if (!state.bestCandidatePath) return false;
|
|
2246
2249
|
const normalizedBestCandidatePath = normalizeCandidatePath(state.bestCandidatePath);
|
|
@@ -2455,6 +2458,21 @@ var researchPhase = {
|
|
|
2455
2458
|
].join("\n");
|
|
2456
2459
|
},
|
|
2457
2460
|
interceptToolCall(toolName, _args, ctx) {
|
|
2461
|
+
if (ctx.state.isCreationTask && !isExternalTask(ctx.state)) {
|
|
2462
|
+
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
2463
|
+
if (isWriteLikeTool) {
|
|
2464
|
+
const normalizedPathArg2 = typeof _args.path === "string" && _args.path.trim() ? ctx.normalizePath(String(_args.path)) : void 0;
|
|
2465
|
+
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
2466
|
+
if (normalizedPathArg2 && normalizedPlanPath && normalizedPathArg2 !== normalizedPlanPath) {
|
|
2467
|
+
return [
|
|
2468
|
+
`Blocked by marathon research guard: ${toolName} cannot create product files during the research phase.`,
|
|
2469
|
+
"Complete research first, then the system will advance you to planning.",
|
|
2470
|
+
`You may write the plan to "${normalizedPlanPath}" once research is complete.`
|
|
2471
|
+
].join(" ");
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
return void 0;
|
|
2475
|
+
}
|
|
2458
2476
|
if (!isExternalTask(ctx.state)) {
|
|
2459
2477
|
return void 0;
|
|
2460
2478
|
}
|
|
@@ -2557,7 +2575,7 @@ var researchPhase = {
|
|
|
2557
2575
|
},
|
|
2558
2576
|
canAcceptCompletion(state, trace) {
|
|
2559
2577
|
if (!isExternalTask(state)) {
|
|
2560
|
-
return
|
|
2578
|
+
return false;
|
|
2561
2579
|
}
|
|
2562
2580
|
return Boolean(state.planWritten || trace.planWritten);
|
|
2563
2581
|
}
|
|
@@ -2611,7 +2629,7 @@ var planningPhase = {
|
|
|
2611
2629
|
interceptToolCall(toolName, args, ctx) {
|
|
2612
2630
|
const normalizedPathArg = typeof args.path === "string" && args.path.trim() ? ctx.normalizePath(String(args.path)) : void 0;
|
|
2613
2631
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
2614
|
-
const isWriteLikeTool = toolName === "write_file" || toolName === "restore_file_checkpoint";
|
|
2632
|
+
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
2615
2633
|
if (isWriteLikeTool && normalizedPathArg && normalizedPlanPath && normalizedPathArg !== normalizedPlanPath) {
|
|
2616
2634
|
return [
|
|
2617
2635
|
`Blocked by marathon planning guard: ${toolName} must target the exact plan path during planning.`,
|
|
@@ -2685,9 +2703,11 @@ var executionPhase = {
|
|
|
2685
2703
|
"Do not write the plan file first in execution. Make a real repo-file edit before you update the plan with progress.",
|
|
2686
2704
|
"Do not create scratch or test files to probe the repo or tool behavior.",
|
|
2687
2705
|
"write_file automatically checkpoints original repo files before overwriting them. If an edit regresses behavior, use restore_file_checkpoint on that file.",
|
|
2688
|
-
"
|
|
2706
|
+
"Use edit_file for targeted changes instead of rewriting the entire file with write_file. edit_file takes old_string and new_string to surgically replace specific code.",
|
|
2707
|
+
"Read the target file and edit it with edit_file (preferred) or write_file. Update the plan file with progress after completing real edits.",
|
|
2689
2708
|
"Before large edits, read any already discovered supporting source/style files that power the target so you preserve existing behavior.",
|
|
2690
|
-
"Prefer
|
|
2709
|
+
"Prefer edit_file for small changes. Only use write_file when creating new files or when the changes are so extensive that a full rewrite is simpler.",
|
|
2710
|
+
"After writing a file 2+ times, you MUST read it back to verify correctness before writing again.",
|
|
2691
2711
|
'Use run_check for real verification before TASK_COMPLETE. Good examples: "pnpm lint", "pnpm exec tsc --noEmit", "pnpm test", or a focused vitest/pytest command.',
|
|
2692
2712
|
"Broad discovery is only allowed if a read of the current target file fails."
|
|
2693
2713
|
];
|
|
@@ -2699,7 +2719,7 @@ var executionPhase = {
|
|
|
2699
2719
|
const normalizedPathArg = typeof args.path === "string" && args.path.trim() ? ctx.normalizePath(String(args.path)) : void 0;
|
|
2700
2720
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
2701
2721
|
const normalizedBestCandidatePath = ctx.state.bestCandidatePath ? ctx.normalizePath(ctx.state.bestCandidatePath) : void 0;
|
|
2702
|
-
const isWriteLikeTool = toolName === "write_file" || toolName === "restore_file_checkpoint";
|
|
2722
|
+
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
2703
2723
|
if (normalizedBestCandidatePath && ctx.isDiscoveryTool(toolName) && !ctx.trace.bestCandidateReadFailed) {
|
|
2704
2724
|
return [
|
|
2705
2725
|
`Blocked by marathon execution guard: ${toolName} is disabled during execution.`,
|
|
@@ -2761,6 +2781,21 @@ var executionPhase = {
|
|
|
2761
2781
|
},
|
|
2762
2782
|
buildRecoveryMessage(state) {
|
|
2763
2783
|
const recent = state.sessions.slice(-2);
|
|
2784
|
+
if (recent.length >= 2 && recent.every(
|
|
2785
|
+
(session) => session.hadTextOutput === true && session.wroteFiles !== true
|
|
2786
|
+
)) {
|
|
2787
|
+
const noToolActions = recent.every(
|
|
2788
|
+
(session) => !session.wroteFiles && !session.verificationAttempted
|
|
2789
|
+
);
|
|
2790
|
+
if (noToolActions) {
|
|
2791
|
+
return [
|
|
2792
|
+
"Recovery instruction: You have been rejected from completing multiple times.",
|
|
2793
|
+
"The likely reason is that verification has not passed.",
|
|
2794
|
+
"Your next action must be run_check with a concrete command (e.g., syntax check, lint, or test).",
|
|
2795
|
+
"Do NOT output TASK_COMPLETE again until verification passes."
|
|
2796
|
+
].join("\n");
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2764
2799
|
if (recent.length >= 2 && recent.every(
|
|
2765
2800
|
(session) => session.verificationAttempted === true && session.wroteFiles !== true
|
|
2766
2801
|
)) {
|
|
@@ -2809,6 +2844,18 @@ var executionPhase = {
|
|
|
2809
2844
|
if (!ctx.trace.executionFileWritten && snapshot.consecutiveDiscoveryPauseCount >= 18) {
|
|
2810
2845
|
return "execution is looping on discovery instead of editing repo files and ending the turn";
|
|
2811
2846
|
}
|
|
2847
|
+
const writeKeys = snapshot.recentActionKeys.filter((k) => k.startsWith("write_file:"));
|
|
2848
|
+
if (writeKeys.length >= 4) {
|
|
2849
|
+
const uniqueWriteTargets = new Set(writeKeys.map((k) => k.split(":").slice(1).join(":")));
|
|
2850
|
+
if (uniqueWriteTargets.size === 1) {
|
|
2851
|
+
return `write_file called ${writeKeys.length} times on the same file \u2014 read the file and verify before continuing`;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
for (const [filePath, count] of Object.entries(ctx.trace.writeCountByPath)) {
|
|
2855
|
+
if (count >= 4) {
|
|
2856
|
+
return `same file rewritten ${count} times without verification (${filePath}) \u2014 read the file and verify before continuing`;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2812
2859
|
return void 0;
|
|
2813
2860
|
}
|
|
2814
2861
|
};
|
|
@@ -4745,7 +4792,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4745
4792
|
verificationAttempted: false,
|
|
4746
4793
|
verificationPassed: false,
|
|
4747
4794
|
verificationBlocked: false,
|
|
4748
|
-
localToolLoopGuardTriggered: false
|
|
4795
|
+
localToolLoopGuardTriggered: false,
|
|
4796
|
+
writeCountByPath: {}
|
|
4749
4797
|
};
|
|
4750
4798
|
}
|
|
4751
4799
|
isDiscoveryLocalTool(toolName) {
|
|
@@ -4948,8 +4996,26 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4948
4996
|
sessionTrace
|
|
4949
4997
|
);
|
|
4950
4998
|
}
|
|
4999
|
+
const phaseIndex = workflow.phases.findIndex((p) => p.name === state.workflowPhase);
|
|
5000
|
+
const executionPhaseIndex = workflow.phases.findIndex((p) => p.name === "execution");
|
|
5001
|
+
if (executionPhaseIndex >= 0 && phaseIndex < executionPhaseIndex) {
|
|
5002
|
+
return false;
|
|
5003
|
+
}
|
|
4951
5004
|
return true;
|
|
4952
5005
|
}
|
|
5006
|
+
computeCompletionRejectionReason(state, trace) {
|
|
5007
|
+
const reasons = [];
|
|
5008
|
+
if (!state.planWritten) {
|
|
5009
|
+
reasons.push("Plan file has not been written");
|
|
5010
|
+
}
|
|
5011
|
+
if (state.bestCandidatePath && !state.bestCandidateVerified && !trace.bestCandidateVerified) {
|
|
5012
|
+
reasons.push("Best candidate file has not been verified (read back after writing)");
|
|
5013
|
+
}
|
|
5014
|
+
if (state.verificationRequired && !state.lastVerificationPassed && !trace.verificationPassed) {
|
|
5015
|
+
reasons.push("Verification has not passed \u2014 run a verification command (run_check) before completing");
|
|
5016
|
+
}
|
|
5017
|
+
return reasons.length > 0 ? reasons.join("; ") : "Completion gates not satisfied for the current workflow phase";
|
|
5018
|
+
}
|
|
4953
5019
|
summarizeUnknownForTrace(value, maxLength = 180) {
|
|
4954
5020
|
const text = typeof value === "string" ? value : value === void 0 ? "" : JSON.stringify(value);
|
|
4955
5021
|
return text.replace(/\s+/g, " ").trim().slice(0, maxLength);
|
|
@@ -5263,7 +5329,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5263
5329
|
const pathArg = typeof args.path === "string" && args.path.trim() ? ` path=${String(args.path)}` : "";
|
|
5264
5330
|
const queryArg = typeof args.query === "string" && args.query.trim() ? ` query="${String(args.query)}"` : "";
|
|
5265
5331
|
const patternArg = typeof args.pattern === "string" && args.pattern.trim() ? ` pattern="${String(args.pattern)}"` : "";
|
|
5266
|
-
const isWriteLikeTool = toolName === "write_file" || toolName === "restore_file_checkpoint";
|
|
5332
|
+
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
5267
5333
|
const isVerificationTool = toolName === "run_check";
|
|
5268
5334
|
const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
|
|
5269
5335
|
if (currentPhase?.interceptToolCall) {
|
|
@@ -5318,8 +5384,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5318
5384
|
);
|
|
5319
5385
|
throw error;
|
|
5320
5386
|
}
|
|
5321
|
-
|
|
5387
|
+
const writeResultIndicatesError = isWriteLikeTool && typeof result === "string" && result.startsWith("Error:");
|
|
5388
|
+
if (isWriteLikeTool && normalizedPathArg && !writeResultIndicatesError) {
|
|
5322
5389
|
trace.wroteFiles = true;
|
|
5390
|
+
trace.writeCountByPath[normalizedPathArg] = (trace.writeCountByPath[normalizedPathArg] || 0) + 1;
|
|
5323
5391
|
if (normalizedPlanPath && normalizedPathArg === normalizedPlanPath) {
|
|
5324
5392
|
trace.planWritten = true;
|
|
5325
5393
|
} else if (state.workflowPhase === "execution") {
|
|
@@ -5627,6 +5695,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5627
5695
|
const maxCost = options.maxCost;
|
|
5628
5696
|
const useStream = options.stream ?? true;
|
|
5629
5697
|
const workflow = options.workflow ?? defaultWorkflow;
|
|
5698
|
+
const maxServerNetworkRetries = 3;
|
|
5699
|
+
let consecutiveServerNetworkErrors = 0;
|
|
5630
5700
|
const agent = await this.get(id);
|
|
5631
5701
|
const taskName = typeof options.trackProgress === "string" ? options.trackProgress : options.trackProgress ? `${agent.name} task` : "";
|
|
5632
5702
|
const resolvedTaskName = taskName || `${agent.name} task`;
|
|
@@ -5933,16 +6003,60 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5933
6003
|
if (state.sessions.length > 50) {
|
|
5934
6004
|
state.sessions = state.sessions.slice(-50);
|
|
5935
6005
|
}
|
|
6006
|
+
if (sessionResult.stopReason !== "error") {
|
|
6007
|
+
consecutiveServerNetworkErrors = 0;
|
|
6008
|
+
}
|
|
5936
6009
|
const detectedTaskCompletion = this.detectTaskCompletion(sessionResult.result);
|
|
5937
6010
|
const acceptedTaskCompletion = detectedTaskCompletion && this.canAcceptTaskCompletion(sessionResult.result, state, sessionTrace, workflow);
|
|
6011
|
+
if (detectedTaskCompletion && !acceptedTaskCompletion) {
|
|
6012
|
+
state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(state, sessionTrace);
|
|
6013
|
+
if (state.verificationRequired && !state.lastVerificationPassed && !sessionTrace.verificationPassed && !sessionTrace.verificationAttempted) {
|
|
6014
|
+
state.consecutiveBlockedVerificationSessions = (state.consecutiveBlockedVerificationSessions || 0) + 1;
|
|
6015
|
+
if ((state.consecutiveBlockedVerificationSessions || 0) >= 2) {
|
|
6016
|
+
state.verificationRequired = false;
|
|
6017
|
+
state.lastVerificationPassed = true;
|
|
6018
|
+
if (!state.planWritten) {
|
|
6019
|
+
state.planWritten = true;
|
|
6020
|
+
}
|
|
6021
|
+
if (!state.bestCandidateVerified) {
|
|
6022
|
+
state.bestCandidateVerified = true;
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
} else {
|
|
6027
|
+
state.lastCompletionRejectionReason = void 0;
|
|
6028
|
+
}
|
|
6029
|
+
const sessionHadActions = sessionTrace.wroteFiles || sessionTrace.readFiles || sessionTrace.discoveryPerformed || sessionTrace.verificationAttempted;
|
|
6030
|
+
if (sessionHadActions) {
|
|
6031
|
+
state.consecutiveEmptySessions = 0;
|
|
6032
|
+
} else {
|
|
6033
|
+
state.consecutiveEmptySessions = (state.consecutiveEmptySessions || 0) + 1;
|
|
6034
|
+
}
|
|
5938
6035
|
if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
|
|
5939
6036
|
state.status = "complete";
|
|
5940
6037
|
} else if (sessionResult.stopReason === "error") {
|
|
5941
|
-
|
|
6038
|
+
if (_AgentsEndpoint.isRetryableSessionError(sessionResult.error) && consecutiveServerNetworkErrors < maxServerNetworkRetries) {
|
|
6039
|
+
consecutiveServerNetworkErrors++;
|
|
6040
|
+
const delayMs = Math.min(
|
|
6041
|
+
5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1),
|
|
6042
|
+
3e4
|
|
6043
|
+
);
|
|
6044
|
+
const delaySec = Math.round(delayMs / 1e3);
|
|
6045
|
+
await this.emitContextNotice(options.onContextNotice, {
|
|
6046
|
+
kind: "server_network_retry",
|
|
6047
|
+
sessionIndex: session,
|
|
6048
|
+
message: `Server network error: ${sessionResult.error}. Retrying in ${delaySec}s (attempt ${consecutiveServerNetworkErrors}/${maxServerNetworkRetries})...`
|
|
6049
|
+
});
|
|
6050
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
6051
|
+
} else {
|
|
6052
|
+
state.status = "error";
|
|
6053
|
+
}
|
|
5942
6054
|
} else if (sessionResult.stopReason === "max_cost") {
|
|
5943
6055
|
state.status = "budget_exceeded";
|
|
5944
6056
|
} else if (acceptedTaskCompletion) {
|
|
5945
6057
|
state.status = "complete";
|
|
6058
|
+
} else if ((state.consecutiveEmptySessions || 0) >= 3) {
|
|
6059
|
+
state.status = "stalled";
|
|
5946
6060
|
} else if (maxCost && state.totalCost >= maxCost) {
|
|
5947
6061
|
state.status = "budget_exceeded";
|
|
5948
6062
|
} else if (session + 1 >= maxSessions) {
|
|
@@ -5971,6 +6085,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5971
6085
|
recordId
|
|
5972
6086
|
};
|
|
5973
6087
|
}
|
|
6088
|
+
/** Returns true if a server-side session error message indicates a transient
|
|
6089
|
+
* network failure that is safe to retry. */
|
|
6090
|
+
static isRetryableSessionError(errorMessage) {
|
|
6091
|
+
if (!errorMessage) return false;
|
|
6092
|
+
const lower = errorMessage.toLowerCase();
|
|
6093
|
+
return _AgentsEndpoint.RETRYABLE_SESSION_ERROR_PATTERNS.some(
|
|
6094
|
+
(pattern) => lower.includes(pattern)
|
|
6095
|
+
);
|
|
6096
|
+
}
|
|
5974
6097
|
/**
|
|
5975
6098
|
* Client-side fallback for detecting task completion in agent output.
|
|
5976
6099
|
* Mirrors the API's detectAutoComplete() for non-loop agents that return 'end_turn'.
|
|
@@ -6449,7 +6572,7 @@ Do NOT redo any of the above work.`
|
|
|
6449
6572
|
"Use write_file only if you want to save the final deliverable into the workspace."
|
|
6450
6573
|
] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
6451
6574
|
].join("\n") : "";
|
|
6452
|
-
const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^builtin:/, ""));
|
|
6575
|
+
const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^(builtin|platform):/, ""));
|
|
6453
6576
|
const builtinToolsBlock = builtinToolNames?.length ? [
|
|
6454
6577
|
"",
|
|
6455
6578
|
"--- Built-in Tools ---",
|
|
@@ -6575,6 +6698,7 @@ Do NOT redo any of the above work.`
|
|
|
6575
6698
|
).join("\n");
|
|
6576
6699
|
if (state.messages && state.messages.length > 0) {
|
|
6577
6700
|
const recoveryMessage2 = this.buildStuckTurnRecoveryMessage(state, wf);
|
|
6701
|
+
const rejectionNotice = state.lastCompletionRejectionReason ? `TASK_COMPLETE was rejected because: ${state.lastCompletionRejectionReason}. Address this before signaling completion again.` : void 0;
|
|
6578
6702
|
const continuationContent = [
|
|
6579
6703
|
"Continue the task.",
|
|
6580
6704
|
phaseBlock,
|
|
@@ -6586,6 +6710,7 @@ Do NOT redo any of the above work.`
|
|
|
6586
6710
|
`Previous sessions:`,
|
|
6587
6711
|
progressSummary,
|
|
6588
6712
|
"",
|
|
6713
|
+
...rejectionNotice ? [rejectionNotice, ""] : [],
|
|
6589
6714
|
...recoveryMessage2 ? [recoveryMessage2, ""] : [],
|
|
6590
6715
|
"Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
|
|
6591
6716
|
].join("\n");
|
|
@@ -6646,6 +6771,7 @@ Do NOT redo any of the above work.`
|
|
|
6646
6771
|
};
|
|
6647
6772
|
}
|
|
6648
6773
|
const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
|
|
6774
|
+
const fallbackRejectionNotice = state.lastCompletionRejectionReason ? `TASK_COMPLETE was rejected because: ${state.lastCompletionRejectionReason}. Address this before signaling completion again.` : void 0;
|
|
6649
6775
|
const content = [
|
|
6650
6776
|
originalMessage,
|
|
6651
6777
|
phaseBlock,
|
|
@@ -6657,6 +6783,7 @@ Do NOT redo any of the above work.`
|
|
|
6657
6783
|
`Previous sessions:`,
|
|
6658
6784
|
progressSummary,
|
|
6659
6785
|
"",
|
|
6786
|
+
...fallbackRejectionNotice ? [fallbackRejectionNotice, ""] : [],
|
|
6660
6787
|
...recoveryMessage ? [recoveryMessage, ""] : [],
|
|
6661
6788
|
`Last output (do NOT repeat this \u2014 build on it):`,
|
|
6662
6789
|
state.lastOutput.slice(0, 1e3),
|
|
@@ -6718,6 +6845,24 @@ Do NOT redo any of the above work.`
|
|
|
6718
6845
|
};
|
|
6719
6846
|
_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX = "You are continuing a long-running task. Here is a compact summary of prior work:";
|
|
6720
6847
|
_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX = "You are continuing a previously completed task. Here is a summary of prior work:";
|
|
6848
|
+
/** Error message patterns from server-side sessions that indicate a transient network failure
|
|
6849
|
+
* (e.g. AI provider connection dropped). These are retried automatically. */
|
|
6850
|
+
_AgentsEndpoint.RETRYABLE_SESSION_ERROR_PATTERNS = [
|
|
6851
|
+
"network connection lost",
|
|
6852
|
+
"network error",
|
|
6853
|
+
"fetch failed",
|
|
6854
|
+
"connection reset",
|
|
6855
|
+
"connection refused",
|
|
6856
|
+
"connection closed",
|
|
6857
|
+
"socket hang up",
|
|
6858
|
+
"econnreset",
|
|
6859
|
+
"econnrefused",
|
|
6860
|
+
"econnaborted",
|
|
6861
|
+
"etimedout",
|
|
6862
|
+
"enetunreach",
|
|
6863
|
+
"enotfound",
|
|
6864
|
+
"request timeout"
|
|
6865
|
+
];
|
|
6721
6866
|
/** Stop phrases that indicate the agent considers its task complete. */
|
|
6722
6867
|
_AgentsEndpoint.STOP_PHRASES = [
|
|
6723
6868
|
"DONE:",
|