@runtypelabs/sdk 1.8.1 → 1.9.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 +293 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +293 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2293,12 +2293,22 @@ function getDefaultPlanPath(taskName) {
|
|
|
2293
2293
|
const slug = sanitizeTaskSlug(taskName || "task");
|
|
2294
2294
|
return `.runtype/marathons/${slug}/plan.md`;
|
|
2295
2295
|
}
|
|
2296
|
+
function getDefaultExternalReportPath(taskName) {
|
|
2297
|
+
const slug = sanitizeTaskSlug(taskName || "task");
|
|
2298
|
+
return `${slug}.md`;
|
|
2299
|
+
}
|
|
2296
2300
|
function sanitizeTaskSlug(taskName) {
|
|
2297
2301
|
return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
2298
2302
|
}
|
|
2299
2303
|
|
|
2300
2304
|
// src/workflows/default-workflow.ts
|
|
2305
|
+
function isExternalTask(state) {
|
|
2306
|
+
return state.workflowVariant === "external";
|
|
2307
|
+
}
|
|
2301
2308
|
function hasSufficientResearchEvidence(state) {
|
|
2309
|
+
if (isExternalTask(state)) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2302
2312
|
if (state.isCreationTask) {
|
|
2303
2313
|
return (state.recentReadPaths?.length || 0) >= 1;
|
|
2304
2314
|
}
|
|
@@ -2442,6 +2452,17 @@ var researchPhase = {
|
|
|
2442
2452
|
description: "Inspect the repo and identify the correct target file",
|
|
2443
2453
|
buildInstructions(state) {
|
|
2444
2454
|
const planPath = state.planPath || getDefaultPlanPath(state.taskName);
|
|
2455
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2456
|
+
if (isExternalTask(state)) {
|
|
2457
|
+
return [
|
|
2458
|
+
"--- Workflow Phase: Research ---",
|
|
2459
|
+
"This is an external web research task, not a repository editing task.",
|
|
2460
|
+
"Research the requested website or external source directly using built-in web tools first.",
|
|
2461
|
+
"Do NOT inspect the local repo or search for a target file unless the user explicitly asks for local workspace changes.",
|
|
2462
|
+
`Write the final markdown report to exactly: ${reportPath}`,
|
|
2463
|
+
"Do NOT end with TASK_COMPLETE until that markdown file has been written."
|
|
2464
|
+
].join("\n");
|
|
2465
|
+
}
|
|
2445
2466
|
if (state.isCreationTask) {
|
|
2446
2467
|
return [
|
|
2447
2468
|
"--- Workflow Phase: Research ---",
|
|
@@ -2461,6 +2482,15 @@ var researchPhase = {
|
|
|
2461
2482
|
].join("\n");
|
|
2462
2483
|
},
|
|
2463
2484
|
buildToolGuidance(state) {
|
|
2485
|
+
if (isExternalTask(state)) {
|
|
2486
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2487
|
+
return [
|
|
2488
|
+
"This is a web/external research task.",
|
|
2489
|
+
"Start with built-in web tools such as firecrawl or web search, not local repo discovery tools.",
|
|
2490
|
+
"Do not call search_repo, glob_files, tree_directory, list_directory, or read_file unless the user explicitly asked you to inspect local files.",
|
|
2491
|
+
`Use write_file to save the final markdown report to exactly: ${reportPath}.`
|
|
2492
|
+
];
|
|
2493
|
+
}
|
|
2464
2494
|
if (state.isCreationTask) {
|
|
2465
2495
|
return [
|
|
2466
2496
|
"This is a creation task \u2014 you are building new files, not editing existing ones.",
|
|
@@ -2494,10 +2524,45 @@ var researchPhase = {
|
|
|
2494
2524
|
`Next step: write the plan markdown to ${state.planPath} before editing the product file.`
|
|
2495
2525
|
].join("\n");
|
|
2496
2526
|
},
|
|
2497
|
-
interceptToolCall() {
|
|
2527
|
+
interceptToolCall(toolName, _args, ctx) {
|
|
2528
|
+
if (!isExternalTask(ctx.state)) {
|
|
2529
|
+
return void 0;
|
|
2530
|
+
}
|
|
2531
|
+
const normalizedPathArg = typeof _args.path === "string" && _args.path.trim() ? ctx.normalizePath(String(_args.path)) : void 0;
|
|
2532
|
+
const normalizedReportPath = ctx.normalizePath(
|
|
2533
|
+
ctx.state.planPath || getDefaultExternalReportPath(ctx.state.taskName)
|
|
2534
|
+
);
|
|
2535
|
+
if (ctx.isDiscoveryTool(toolName) || toolName === "read_file" || toolName === "restore_file_checkpoint") {
|
|
2536
|
+
return [
|
|
2537
|
+
`Blocked by marathon external research guard: ${toolName} is disabled for web research tasks.`,
|
|
2538
|
+
"Research the requested external source with built-in web tools instead.",
|
|
2539
|
+
"Use local file tools only if the user explicitly asked for workspace inspection or if you are saving the final deliverable."
|
|
2540
|
+
].join(" ");
|
|
2541
|
+
}
|
|
2542
|
+
if (toolName === "write_file" && normalizedPathArg && normalizedPathArg !== normalizedReportPath) {
|
|
2543
|
+
return [
|
|
2544
|
+
`Blocked by marathon external research guard: write_file must target "${normalizedReportPath}".`,
|
|
2545
|
+
"Save the final markdown report to the required workspace path before TASK_COMPLETE."
|
|
2546
|
+
].join(" ");
|
|
2547
|
+
}
|
|
2498
2548
|
return void 0;
|
|
2499
2549
|
},
|
|
2500
2550
|
buildRecoveryMessage(state) {
|
|
2551
|
+
if (isExternalTask(state)) {
|
|
2552
|
+
const recent2 = state.sessions.slice(-2);
|
|
2553
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2554
|
+
if (recent2.length < 2 || !recent2.every((session) => session.wroteFiles === false)) {
|
|
2555
|
+
return void 0;
|
|
2556
|
+
}
|
|
2557
|
+
const repeatedSameActions2 = hasRepeatedSameActions(state.sessions);
|
|
2558
|
+
return [
|
|
2559
|
+
"Recovery instruction:",
|
|
2560
|
+
"This web research task is missing its required markdown artifact.",
|
|
2561
|
+
`Your next action must be write_file to "${reportPath}" with the final findings in markdown.`,
|
|
2562
|
+
"Do not end with TASK_COMPLETE until that file has been written.",
|
|
2563
|
+
...repeatedSameActions2 ? ["You are repeating the same actions; break the loop by writing the final report now."] : []
|
|
2564
|
+
].join("\n");
|
|
2565
|
+
}
|
|
2501
2566
|
const recent = state.sessions.slice(-2);
|
|
2502
2567
|
const normalizedPlanPath = typeof state.planPath === "string" && state.planPath.trim() ? normalizeCandidatePath(state.planPath) : void 0;
|
|
2503
2568
|
const recentPlanOnlyLoop = Boolean(normalizedPlanPath) && recent.length === 2 && recent.every((session) => {
|
|
@@ -2541,6 +2606,12 @@ var researchPhase = {
|
|
|
2541
2606
|
].join("\n");
|
|
2542
2607
|
},
|
|
2543
2608
|
shouldForceEndTurn(snapshot, ctx) {
|
|
2609
|
+
if (isExternalTask(ctx.state)) {
|
|
2610
|
+
if (ctx.isDiscoveryTool(snapshot.toolName) && snapshot.actionKeyCount >= 2) {
|
|
2611
|
+
return "this web research task is looping on local repo discovery instead of using external tools";
|
|
2612
|
+
}
|
|
2613
|
+
return void 0;
|
|
2614
|
+
}
|
|
2544
2615
|
const state = ctx.state;
|
|
2545
2616
|
const sufficientResearch = hasSufficientResearchEvidence(state);
|
|
2546
2617
|
if (sufficientResearch && snapshot.discoveryPauseCount >= 12) {
|
|
@@ -2553,6 +2624,12 @@ var researchPhase = {
|
|
|
2553
2624
|
return "this session exceeded the discovery-tool budget without ending the turn";
|
|
2554
2625
|
}
|
|
2555
2626
|
return void 0;
|
|
2627
|
+
},
|
|
2628
|
+
canAcceptCompletion(state, trace) {
|
|
2629
|
+
if (!isExternalTask(state)) {
|
|
2630
|
+
return true;
|
|
2631
|
+
}
|
|
2632
|
+
return Boolean(state.planWritten || trace.planWritten);
|
|
2556
2633
|
}
|
|
2557
2634
|
};
|
|
2558
2635
|
var planningPhase = {
|
|
@@ -2779,6 +2856,65 @@ var executionPhase = {
|
|
|
2779
2856
|
};
|
|
2780
2857
|
function classifyVariant(message) {
|
|
2781
2858
|
const lower = message.toLowerCase();
|
|
2859
|
+
const externalVerbs = [
|
|
2860
|
+
"fetch",
|
|
2861
|
+
"browse",
|
|
2862
|
+
"scrape",
|
|
2863
|
+
"crawl",
|
|
2864
|
+
"download",
|
|
2865
|
+
"visit",
|
|
2866
|
+
"navigate to",
|
|
2867
|
+
"go to",
|
|
2868
|
+
"open the",
|
|
2869
|
+
"look up",
|
|
2870
|
+
"search for",
|
|
2871
|
+
"find out",
|
|
2872
|
+
"check out",
|
|
2873
|
+
"pull up"
|
|
2874
|
+
];
|
|
2875
|
+
const externalTargets = [
|
|
2876
|
+
"http://",
|
|
2877
|
+
"https://",
|
|
2878
|
+
"www.",
|
|
2879
|
+
".com",
|
|
2880
|
+
".org",
|
|
2881
|
+
".io",
|
|
2882
|
+
".net",
|
|
2883
|
+
".dev",
|
|
2884
|
+
"website",
|
|
2885
|
+
"webpage",
|
|
2886
|
+
"web page",
|
|
2887
|
+
"home page",
|
|
2888
|
+
"homepage",
|
|
2889
|
+
"hacker news",
|
|
2890
|
+
"reddit",
|
|
2891
|
+
"twitter",
|
|
2892
|
+
"github.com",
|
|
2893
|
+
"firecrawl",
|
|
2894
|
+
"exa_search"
|
|
2895
|
+
];
|
|
2896
|
+
const hasExternalVerb = externalVerbs.some((v) => lower.includes(v));
|
|
2897
|
+
const hasExternalTarget = externalTargets.some((t) => lower.includes(t));
|
|
2898
|
+
const modificationVerbs = [
|
|
2899
|
+
"fix",
|
|
2900
|
+
"update",
|
|
2901
|
+
"change",
|
|
2902
|
+
"modify",
|
|
2903
|
+
"edit",
|
|
2904
|
+
"refactor",
|
|
2905
|
+
"improve",
|
|
2906
|
+
"add to",
|
|
2907
|
+
"remove",
|
|
2908
|
+
"delete",
|
|
2909
|
+
"rename",
|
|
2910
|
+
"migrate"
|
|
2911
|
+
];
|
|
2912
|
+
const hasModificationVerb = modificationVerbs.some(
|
|
2913
|
+
(v) => lower.startsWith(v) || new RegExp(`\\b${v}\\b`).test(lower)
|
|
2914
|
+
);
|
|
2915
|
+
if (hasExternalVerb && hasExternalTarget && !hasModificationVerb) {
|
|
2916
|
+
return "external";
|
|
2917
|
+
}
|
|
2782
2918
|
const creationPatterns = [
|
|
2783
2919
|
/^create\b/,
|
|
2784
2920
|
/^build\b/,
|
|
@@ -2799,30 +2935,13 @@ function classifyVariant(message) {
|
|
|
2799
2935
|
/^new\b/
|
|
2800
2936
|
];
|
|
2801
2937
|
const hasCreationStart = creationPatterns.some((p) => p.test(lower));
|
|
2802
|
-
const modificationVerbs = [
|
|
2803
|
-
"fix",
|
|
2804
|
-
"update",
|
|
2805
|
-
"change",
|
|
2806
|
-
"modify",
|
|
2807
|
-
"edit",
|
|
2808
|
-
"refactor",
|
|
2809
|
-
"improve",
|
|
2810
|
-
"add to",
|
|
2811
|
-
"remove",
|
|
2812
|
-
"delete",
|
|
2813
|
-
"rename",
|
|
2814
|
-
"migrate"
|
|
2815
|
-
];
|
|
2816
|
-
const hasModificationVerb = modificationVerbs.some(
|
|
2817
|
-
(v) => lower.startsWith(v) || new RegExp(`\\b${v}\\b`).test(lower)
|
|
2818
|
-
);
|
|
2819
2938
|
if (hasCreationStart && !hasModificationVerb) {
|
|
2820
2939
|
return "create";
|
|
2821
2940
|
}
|
|
2822
2941
|
return "modify";
|
|
2823
2942
|
}
|
|
2824
2943
|
async function generateBootstrapContext(message, localTools, variant) {
|
|
2825
|
-
if (variant === "create") return void 0;
|
|
2944
|
+
if (variant === "create" || variant === "external") return void 0;
|
|
2826
2945
|
if (!localTools) return void 0;
|
|
2827
2946
|
const searchTool = localTools.search_repo;
|
|
2828
2947
|
const globTool = localTools.glob_files;
|
|
@@ -2858,7 +2977,7 @@ async function generateBootstrapContext(message, localTools, variant) {
|
|
|
2858
2977
|
return ["Bootstrap repo hints:", ...lines].join("\n").slice(0, 1500);
|
|
2859
2978
|
}
|
|
2860
2979
|
function buildCandidateBlock(state) {
|
|
2861
|
-
if (!state.bestCandidatePath || state.isCreationTask) return "";
|
|
2980
|
+
if (!state.bestCandidatePath || state.isCreationTask || isExternalTask(state)) return "";
|
|
2862
2981
|
return [
|
|
2863
2982
|
"",
|
|
2864
2983
|
"--- Best Candidate ---",
|
|
@@ -4945,8 +5064,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4945
5064
|
return `[${toolName} output (${resultStr.length} chars) saved to ${filePath} \u2014 use read_file to retrieve if needed]`;
|
|
4946
5065
|
}
|
|
4947
5066
|
getDefaultPlanPath(taskName) {
|
|
4948
|
-
|
|
4949
|
-
|
|
5067
|
+
return getDefaultPlanPath(taskName);
|
|
5068
|
+
}
|
|
5069
|
+
getDefaultExternalReportPath(taskName) {
|
|
5070
|
+
return getDefaultExternalReportPath(taskName);
|
|
4950
5071
|
}
|
|
4951
5072
|
dirnameOfCandidatePath(candidatePath) {
|
|
4952
5073
|
const normalized = this.normalizeCandidatePath(candidatePath);
|
|
@@ -5052,7 +5173,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5052
5173
|
}
|
|
5053
5174
|
sanitizeResumeState(resumeState, taskName) {
|
|
5054
5175
|
if (!resumeState) return void 0;
|
|
5055
|
-
const
|
|
5176
|
+
const workflowVariant = resumeState.workflowVariant;
|
|
5177
|
+
const defaultPlanPath = workflowVariant === "external" ? this.getDefaultExternalReportPath(taskName) : this.getDefaultPlanPath(taskName);
|
|
5178
|
+
const planPath = typeof resumeState.planPath === "string" && resumeState.planPath.trim() ? this.normalizeCandidatePath(resumeState.planPath) : defaultPlanPath;
|
|
5179
|
+
const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
|
|
5056
5180
|
const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
|
|
5057
5181
|
const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
|
|
5058
5182
|
const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
|
|
@@ -5063,7 +5187,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5063
5187
|
return {
|
|
5064
5188
|
...resumeState,
|
|
5065
5189
|
workflowPhase,
|
|
5066
|
-
planPath,
|
|
5190
|
+
planPath: migratedPlanPath,
|
|
5067
5191
|
planWritten: Boolean(resumeState.planWritten),
|
|
5068
5192
|
bestCandidatePath,
|
|
5069
5193
|
bestCandidateReason: bestCandidatePath ? resumeState.bestCandidateReason : void 0,
|
|
@@ -5489,13 +5613,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5489
5613
|
const taskName = typeof options.trackProgress === "string" ? options.trackProgress : options.trackProgress ? `${agent.name} task` : "";
|
|
5490
5614
|
const resolvedTaskName = taskName || `${agent.name} task`;
|
|
5491
5615
|
const seededResumeState = this.sanitizeResumeState(options.resumeState, resolvedTaskName);
|
|
5616
|
+
const classifiedVariant = seededResumeState?.workflowVariant ?? workflow.classifyVariant?.(options.message);
|
|
5492
5617
|
const state = {
|
|
5493
5618
|
agentId: id,
|
|
5494
5619
|
agentName: agent.name,
|
|
5495
5620
|
taskName: resolvedTaskName,
|
|
5496
5621
|
status: "running",
|
|
5497
5622
|
workflowPhase: seededResumeState?.workflowPhase || workflow.phases[0]?.name || "research",
|
|
5498
|
-
planPath: seededResumeState?.planPath || this.getDefaultPlanPath(resolvedTaskName),
|
|
5623
|
+
planPath: seededResumeState?.planPath || (classifiedVariant === "external" ? this.getDefaultExternalReportPath(resolvedTaskName) : this.getDefaultPlanPath(resolvedTaskName)),
|
|
5499
5624
|
planWritten: seededResumeState?.planWritten || false,
|
|
5500
5625
|
bestCandidateNeedsVerification: seededResumeState?.bestCandidateNeedsVerification || false,
|
|
5501
5626
|
bestCandidateVerified: seededResumeState?.bestCandidateVerified || false,
|
|
@@ -5518,7 +5643,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5518
5643
|
...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
|
|
5519
5644
|
...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {}
|
|
5520
5645
|
};
|
|
5521
|
-
state.workflowVariant =
|
|
5646
|
+
state.workflowVariant = classifiedVariant;
|
|
5522
5647
|
state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
|
|
5523
5648
|
this.updateWorkflowPhase(state, this.createEmptyToolTrace(), workflow);
|
|
5524
5649
|
let recordId;
|
|
@@ -5536,7 +5661,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5536
5661
|
state.bootstrapContext = await this.generateBootstrapDiscoveryContext(
|
|
5537
5662
|
options.message,
|
|
5538
5663
|
options.localTools,
|
|
5539
|
-
state.isCreationTask
|
|
5664
|
+
state.isCreationTask || state.workflowVariant === "external"
|
|
5540
5665
|
);
|
|
5541
5666
|
}
|
|
5542
5667
|
const bootstrapCandidate = this.extractBestCandidateFromBootstrapContext(state.bootstrapContext);
|
|
@@ -5762,13 +5887,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5762
5887
|
if (state.sessions.length > 50) {
|
|
5763
5888
|
state.sessions = state.sessions.slice(-50);
|
|
5764
5889
|
}
|
|
5765
|
-
|
|
5890
|
+
const detectedTaskCompletion = this.detectTaskCompletion(sessionResult.result);
|
|
5891
|
+
const acceptedTaskCompletion = detectedTaskCompletion && this.canAcceptTaskCompletion(sessionResult.result, state, sessionTrace, workflow);
|
|
5892
|
+
if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
|
|
5766
5893
|
state.status = "complete";
|
|
5767
5894
|
} else if (sessionResult.stopReason === "error") {
|
|
5768
5895
|
state.status = "error";
|
|
5769
5896
|
} else if (sessionResult.stopReason === "max_cost") {
|
|
5770
5897
|
state.status = "budget_exceeded";
|
|
5771
|
-
} else if (
|
|
5898
|
+
} else if (acceptedTaskCompletion) {
|
|
5772
5899
|
state.status = "complete";
|
|
5773
5900
|
} else if (maxCost && state.totalCost >= maxCost) {
|
|
5774
5901
|
state.status = "budget_exceeded";
|
|
@@ -6091,7 +6218,7 @@ Do NOT redo any of the above work.`
|
|
|
6091
6218
|
"",
|
|
6092
6219
|
"Changed Files / Candidate Paths",
|
|
6093
6220
|
...candidatePaths.length > 0 ? candidatePaths.map((candidatePath) => `- ${candidatePath}`) : ["- No candidate paths recorded yet."],
|
|
6094
|
-
...state.planPath ? [`- Plan path: ${state.planPath}`] : [],
|
|
6221
|
+
...state.planPath ? [`- ${state.workflowVariant === "external" ? "Report path" : "Plan path"}: ${state.planPath}`] : [],
|
|
6095
6222
|
...state.planWritten ? ["- Planning artifact has been written."] : [],
|
|
6096
6223
|
...state.bestCandidateReason ? [`- Best candidate rationale: ${state.bestCandidateReason}`] : [],
|
|
6097
6224
|
"",
|
|
@@ -6112,6 +6239,75 @@ Do NOT redo any of the above work.`
|
|
|
6112
6239
|
(state.lastOutput || "").slice(0, 1800) || "- No final output recorded yet."
|
|
6113
6240
|
].join("\n");
|
|
6114
6241
|
}
|
|
6242
|
+
isAssistantToolCallMessage(message) {
|
|
6243
|
+
return Boolean(message?.role === "assistant" && message.toolCalls && message.toolCalls.length > 0);
|
|
6244
|
+
}
|
|
6245
|
+
isToolResultMessage(message) {
|
|
6246
|
+
return Boolean(message?.role === "tool" && message.toolResults && message.toolResults.length > 0);
|
|
6247
|
+
}
|
|
6248
|
+
/**
|
|
6249
|
+
* Replay only complete adjacent tool-call/result pairs so provider validation
|
|
6250
|
+
* never sees an orphaned tool result after history trimming or resume.
|
|
6251
|
+
*/
|
|
6252
|
+
sanitizeReplayHistoryMessages(messages) {
|
|
6253
|
+
const sanitized = [];
|
|
6254
|
+
for (let index = 0; index < messages.length; index++) {
|
|
6255
|
+
const message = messages[index];
|
|
6256
|
+
if (this.isAssistantToolCallMessage(message)) {
|
|
6257
|
+
const nextMessage = messages[index + 1];
|
|
6258
|
+
if (!this.isToolResultMessage(nextMessage)) {
|
|
6259
|
+
continue;
|
|
6260
|
+
}
|
|
6261
|
+
const matchedResultIds = new Set(
|
|
6262
|
+
nextMessage.toolResults.filter(
|
|
6263
|
+
(toolResult) => message.toolCalls.some((toolCall) => toolCall.toolCallId === toolResult.toolCallId)
|
|
6264
|
+
).map((toolResult) => toolResult.toolCallId)
|
|
6265
|
+
);
|
|
6266
|
+
if (matchedResultIds.size === 0) {
|
|
6267
|
+
continue;
|
|
6268
|
+
}
|
|
6269
|
+
const matchedToolCalls = message.toolCalls.filter(
|
|
6270
|
+
(toolCall) => matchedResultIds.has(toolCall.toolCallId)
|
|
6271
|
+
);
|
|
6272
|
+
const matchedToolResults = nextMessage.toolResults.filter(
|
|
6273
|
+
(toolResult) => matchedResultIds.has(toolResult.toolCallId)
|
|
6274
|
+
);
|
|
6275
|
+
sanitized.push(
|
|
6276
|
+
matchedToolCalls.length === message.toolCalls.length ? message : { ...message, toolCalls: matchedToolCalls }
|
|
6277
|
+
);
|
|
6278
|
+
sanitized.push(
|
|
6279
|
+
matchedToolResults.length === nextMessage.toolResults.length ? nextMessage : { ...nextMessage, toolResults: matchedToolResults }
|
|
6280
|
+
);
|
|
6281
|
+
index += 1;
|
|
6282
|
+
continue;
|
|
6283
|
+
}
|
|
6284
|
+
if (this.isToolResultMessage(message)) {
|
|
6285
|
+
continue;
|
|
6286
|
+
}
|
|
6287
|
+
sanitized.push(message);
|
|
6288
|
+
}
|
|
6289
|
+
return sanitized;
|
|
6290
|
+
}
|
|
6291
|
+
/**
|
|
6292
|
+
* Keep replay trimming on a pair boundary. If the trim cut would start on a
|
|
6293
|
+
* tool-result message, slide back to include the matching assistant tool call.
|
|
6294
|
+
*/
|
|
6295
|
+
trimReplayHistoryMessages(messages, maxMessages) {
|
|
6296
|
+
if (messages.length <= maxMessages) {
|
|
6297
|
+
return {
|
|
6298
|
+
historyMessages: messages,
|
|
6299
|
+
trimmedCount: 0
|
|
6300
|
+
};
|
|
6301
|
+
}
|
|
6302
|
+
let startIndex = messages.length - maxMessages;
|
|
6303
|
+
while (startIndex > 0 && messages[startIndex]?.role === "tool") {
|
|
6304
|
+
startIndex -= 1;
|
|
6305
|
+
}
|
|
6306
|
+
return {
|
|
6307
|
+
historyMessages: messages.slice(startIndex),
|
|
6308
|
+
trimmedCount: startIndex
|
|
6309
|
+
};
|
|
6310
|
+
}
|
|
6115
6311
|
/**
|
|
6116
6312
|
* Build messages for a session, injecting progress context for continuation sessions.
|
|
6117
6313
|
* Optionally accepts continuation context for marathon resume scenarios.
|
|
@@ -6182,29 +6378,41 @@ Do NOT redo any of the above work.`
|
|
|
6182
6378
|
const currentPhase = wf.phases.find((p) => p.name === state.workflowPhase);
|
|
6183
6379
|
const toolGuidanceLines = currentPhase?.buildToolGuidance(state) ?? [];
|
|
6184
6380
|
const isDeployWorkflow = wf.name === "deploy";
|
|
6381
|
+
const isExternalTask2 = state.workflowVariant === "external";
|
|
6382
|
+
const requiredArtifactPath = state.planPath;
|
|
6185
6383
|
const localToolsBlock = localToolNames?.length ? [
|
|
6186
6384
|
"",
|
|
6187
6385
|
"--- Local Tools ---",
|
|
6188
6386
|
`You have access to tools (${localToolNames.join(", ")}) that execute directly on the user's machine.`,
|
|
6189
|
-
...isDeployWorkflow ? ["Use deploy_sandbox to deploy code and get a live preview URL."] : [
|
|
6387
|
+
...isDeployWorkflow ? ["Use deploy_sandbox to deploy code and get a live preview URL."] : isExternalTask2 ? [
|
|
6388
|
+
"This is a web/external research task. Do not inspect or edit the local repository unless the user explicitly asked for workspace changes.",
|
|
6389
|
+
...requiredArtifactPath ? [`Write the final markdown findings to exactly: ${requiredArtifactPath}`] : []
|
|
6390
|
+
] : [
|
|
6190
6391
|
"Use these tools to inspect the existing repository and make real file edits \u2014 not just code in your response."
|
|
6191
6392
|
],
|
|
6192
6393
|
...toolGuidanceLines,
|
|
6193
|
-
...isDeployWorkflow ? [] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
6394
|
+
...isDeployWorkflow ? [] : isExternalTask2 ? ["Use write_file only if you want to save the final deliverable into the workspace."] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
6194
6395
|
].join("\n") : "";
|
|
6195
6396
|
const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^builtin:/, ""));
|
|
6196
6397
|
const builtinToolsBlock = builtinToolNames?.length ? [
|
|
6197
6398
|
"",
|
|
6198
6399
|
"--- Built-in Tools ---",
|
|
6199
6400
|
`You have access to built-in tools (${builtinToolNames.join(", ")}) for web search, web scraping, image generation, and other capabilities.`,
|
|
6200
|
-
"Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
|
|
6401
|
+
isExternalTask2 ? "This task targets an external site or source. Start with these built-in tools before considering any local file tools." : "Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
|
|
6201
6402
|
].join("\n") : "";
|
|
6202
6403
|
const toolsBlock = localToolsBlock + builtinToolsBlock;
|
|
6203
|
-
const bootstrapBlock = state.bootstrapContext ? [
|
|
6404
|
+
const bootstrapBlock = state.bootstrapContext ? [
|
|
6405
|
+
"",
|
|
6406
|
+
isExternalTask2 ? "--- Initial Research Context ---" : "--- Initial Repository Discovery ---",
|
|
6407
|
+
state.bootstrapContext
|
|
6408
|
+
].join("\n") : "";
|
|
6204
6409
|
const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
|
|
6205
6410
|
const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
|
|
6206
6411
|
const multiSessionInstruction = `This is a multi-session task (session ${sessionIndex + 1}/${maxSessions}). When you have fully completed the task, end your response with TASK_COMPLETE on its own line.`;
|
|
6207
6412
|
if (continuationContext && sessionIndex === 0) {
|
|
6413
|
+
const replayHistoryMessages = this.sanitizeReplayHistoryMessages(
|
|
6414
|
+
continuationContext.previousMessages
|
|
6415
|
+
);
|
|
6208
6416
|
const defaultContinueMessage = "Continue the task. Review your prior work above and proceed with any remaining work. If everything is already complete, respond with TASK_COMPLETE.";
|
|
6209
6417
|
const userMessage = continuationContext.newUserMessage || defaultContinueMessage;
|
|
6210
6418
|
const userContent = [
|
|
@@ -6217,7 +6425,7 @@ Do NOT redo any of the above work.`
|
|
|
6217
6425
|
multiSessionInstruction
|
|
6218
6426
|
].join("\n");
|
|
6219
6427
|
const fullHistoryMessages = [
|
|
6220
|
-
...
|
|
6428
|
+
...replayHistoryMessages,
|
|
6221
6429
|
{
|
|
6222
6430
|
role: "system",
|
|
6223
6431
|
content: "IMPORTANT: You are continuing a previously completed task. The conversation above shows your prior work. Do NOT redo any of it. Build on what was already accomplished. If there is nothing new to do, respond with TASK_COMPLETE."
|
|
@@ -6229,7 +6437,7 @@ Do NOT redo any of the above work.`
|
|
|
6229
6437
|
];
|
|
6230
6438
|
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6231
6439
|
const breakdown = this.buildContextBudgetBreakdown({
|
|
6232
|
-
historyMessages:
|
|
6440
|
+
historyMessages: replayHistoryMessages,
|
|
6233
6441
|
currentTurnContent: userContent,
|
|
6234
6442
|
localTools: compactionOptions?.localTools,
|
|
6235
6443
|
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
@@ -6319,16 +6527,19 @@ Do NOT redo any of the above work.`
|
|
|
6319
6527
|
"Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
|
|
6320
6528
|
].join("\n");
|
|
6321
6529
|
const MAX_HISTORY_MESSAGES = 60;
|
|
6322
|
-
let historyMessages = state.messages;
|
|
6530
|
+
let historyMessages = this.sanitizeReplayHistoryMessages(state.messages);
|
|
6323
6531
|
if (historyMessages.length > MAX_HISTORY_MESSAGES) {
|
|
6324
|
-
const
|
|
6325
|
-
historyMessages =
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6532
|
+
const trimmedHistory = this.trimReplayHistoryMessages(historyMessages, MAX_HISTORY_MESSAGES);
|
|
6533
|
+
historyMessages = trimmedHistory.historyMessages;
|
|
6534
|
+
if (trimmedHistory.trimmedCount > 0) {
|
|
6535
|
+
historyMessages = [
|
|
6536
|
+
{
|
|
6537
|
+
role: "system",
|
|
6538
|
+
content: `[${trimmedHistory.trimmedCount} earlier messages trimmed to stay within context limits. Original task: ${(state.originalMessage || originalMessage).slice(0, 500)}]`
|
|
6539
|
+
},
|
|
6540
|
+
...historyMessages
|
|
6541
|
+
];
|
|
6542
|
+
}
|
|
6332
6543
|
}
|
|
6333
6544
|
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6334
6545
|
const breakdown = this.buildContextBudgetBreakdown({
|
|
@@ -6590,6 +6801,42 @@ var FlowBuilder = class {
|
|
|
6590
6801
|
);
|
|
6591
6802
|
return this;
|
|
6592
6803
|
}
|
|
6804
|
+
/**
|
|
6805
|
+
* Add a crawl step
|
|
6806
|
+
*/
|
|
6807
|
+
crawl(config) {
|
|
6808
|
+
this.addStep(
|
|
6809
|
+
"crawl",
|
|
6810
|
+
config.name,
|
|
6811
|
+
{
|
|
6812
|
+
url: config.url,
|
|
6813
|
+
limit: config.limit,
|
|
6814
|
+
depth: config.depth,
|
|
6815
|
+
source: config.source,
|
|
6816
|
+
formats: config.formats,
|
|
6817
|
+
render: config.render,
|
|
6818
|
+
maxAge: config.maxAge,
|
|
6819
|
+
modifiedSince: config.modifiedSince,
|
|
6820
|
+
options: config.options,
|
|
6821
|
+
authenticate: config.authenticate,
|
|
6822
|
+
cookies: config.cookies,
|
|
6823
|
+
setExtraHTTPHeaders: config.setExtraHTTPHeaders,
|
|
6824
|
+
gotoOptions: config.gotoOptions,
|
|
6825
|
+
waitForSelector: config.waitForSelector,
|
|
6826
|
+
rejectResourceTypes: config.rejectResourceTypes,
|
|
6827
|
+
rejectRequestPattern: config.rejectRequestPattern,
|
|
6828
|
+
userAgent: config.userAgent,
|
|
6829
|
+
jsonOptions: config.jsonOptions,
|
|
6830
|
+
outputVariable: config.outputVariable,
|
|
6831
|
+
errorHandling: config.errorHandling,
|
|
6832
|
+
streamOutput: config.streamOutput,
|
|
6833
|
+
pollIntervalMs: config.pollIntervalMs,
|
|
6834
|
+
completionTimeoutMs: config.completionTimeoutMs
|
|
6835
|
+
},
|
|
6836
|
+
config.enabled
|
|
6837
|
+
);
|
|
6838
|
+
return this;
|
|
6839
|
+
}
|
|
6593
6840
|
/**
|
|
6594
6841
|
* Add a fetch URL step
|
|
6595
6842
|
*/
|
|
@@ -6932,7 +7179,7 @@ var ClientFlowBuilder = class extends FlowBuilder {
|
|
|
6932
7179
|
this.boundClient = client;
|
|
6933
7180
|
this.createFlow({ name });
|
|
6934
7181
|
}
|
|
6935
|
-
async run(arg1, arg2, arg3,
|
|
7182
|
+
async run(arg1, arg2, arg3, _arg4) {
|
|
6936
7183
|
const config = this.build();
|
|
6937
7184
|
let runOptions;
|
|
6938
7185
|
let runCallbacks;
|