@probelabs/probe 0.6.0-rc203 → 0.6.0-rc205
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/bin/binaries/probe-v0.6.0-rc205-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc205-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.d.ts +2 -0
- package/build/agent/ProbeAgent.js +233 -40
- package/build/agent/index.js +1566 -84
- package/build/agent/simpleTelemetry.js +12 -0
- package/build/agent/tasks/TaskManager.js +604 -0
- package/build/agent/tasks/index.js +15 -0
- package/build/agent/tasks/taskTool.js +476 -0
- package/build/agent/tools.js +11 -0
- package/build/delegate.js +7 -2
- package/build/index.js +14 -1
- package/build/search.js +19 -5
- package/build/tools/common.js +67 -0
- package/build/tools/vercel.js +28 -12
- package/build/utils/error-types.js +303 -0
- package/build/utils/path-validation.js +21 -3
- package/cjs/agent/ProbeAgent.cjs +8940 -6393
- package/cjs/agent/simpleTelemetry.cjs +10 -0
- package/cjs/index.cjs +8960 -6393
- package/package.json +2 -2
- package/src/agent/ProbeAgent.d.ts +2 -0
- package/src/agent/ProbeAgent.js +233 -40
- package/src/agent/index.js +14 -2
- package/src/agent/simpleTelemetry.js +12 -0
- package/src/agent/tasks/TaskManager.js +604 -0
- package/src/agent/tasks/index.js +15 -0
- package/src/agent/tasks/taskTool.js +476 -0
- package/src/agent/tools.js +11 -0
- package/src/delegate.js +7 -2
- package/src/index.js +14 -1
- package/src/search.js +19 -5
- package/src/tools/common.js +67 -0
- package/src/tools/vercel.js +28 -12
- package/src/utils/error-types.js +303 -0
- package/src/utils/path-validation.js +21 -3
- package/bin/binaries/probe-v0.6.0-rc203-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc203-x86_64-unknown-linux-musl.tar.gz +0 -0
package/build/agent/index.js
CHANGED
|
@@ -3068,6 +3068,222 @@ var init_utils = __esm({
|
|
|
3068
3068
|
}
|
|
3069
3069
|
});
|
|
3070
3070
|
|
|
3071
|
+
// src/utils/error-types.js
|
|
3072
|
+
function escapeXml(str) {
|
|
3073
|
+
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3074
|
+
}
|
|
3075
|
+
function categorizeError(error) {
|
|
3076
|
+
if (error instanceof ProbeError) {
|
|
3077
|
+
return error;
|
|
3078
|
+
}
|
|
3079
|
+
const message = error?.message || String(error);
|
|
3080
|
+
const lowerMessage = message.toLowerCase();
|
|
3081
|
+
const errorCode = error?.code != null ? String(error.code).toLowerCase() : "";
|
|
3082
|
+
if (lowerMessage.includes("path does not exist") || lowerMessage.includes("no such file or directory") || errorCode === "enoent") {
|
|
3083
|
+
return new PathError(message, {
|
|
3084
|
+
originalError: error,
|
|
3085
|
+
suggestion: "The specified path does not exist. Please verify the path or use a different directory."
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
if (lowerMessage.includes("not a directory") || errorCode === "enotdir") {
|
|
3089
|
+
return new PathError(message, {
|
|
3090
|
+
originalError: error,
|
|
3091
|
+
suggestion: "The path is not a directory. Please provide a valid directory path."
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
if (lowerMessage.includes("permission denied") || errorCode === "eacces") {
|
|
3095
|
+
return new PathError(message, {
|
|
3096
|
+
originalError: error,
|
|
3097
|
+
recoverable: false,
|
|
3098
|
+
suggestion: "Permission denied. This is a system-level restriction that cannot be resolved by changing the query."
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
if (lowerMessage.includes("timed out") || lowerMessage.includes("timeout") || errorCode === "etimedout" || error?.killed === true) {
|
|
3102
|
+
return new TimeoutError(message, {
|
|
3103
|
+
originalError: error,
|
|
3104
|
+
suggestion: "The operation timed out. Try a more specific query, reduce the search scope, or increase the timeout."
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
const rateLimitPatterns = ["rate_limit", "rate limit", "429", "too many requests", "overloaded"];
|
|
3108
|
+
if (rateLimitPatterns.some((p) => lowerMessage.includes(p))) {
|
|
3109
|
+
return new ApiError(message, {
|
|
3110
|
+
originalError: error,
|
|
3111
|
+
recoverable: true,
|
|
3112
|
+
suggestion: "API rate limit or overload encountered. The system will retry automatically with backoff."
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
const serverErrorPatterns = ["500", "502", "503", "504", "internal server error", "bad gateway", "service unavailable"];
|
|
3116
|
+
if (serverErrorPatterns.some((p) => lowerMessage.includes(p))) {
|
|
3117
|
+
return new ApiError(message, {
|
|
3118
|
+
originalError: error,
|
|
3119
|
+
recoverable: true,
|
|
3120
|
+
suggestion: "API server error encountered. The system will retry automatically."
|
|
3121
|
+
});
|
|
3122
|
+
}
|
|
3123
|
+
if ((lowerMessage.includes("context") || lowerMessage.includes("token")) && (lowerMessage.includes("limit") || lowerMessage.includes("exceed"))) {
|
|
3124
|
+
return new ApiError(message, {
|
|
3125
|
+
originalError: error,
|
|
3126
|
+
recoverable: true,
|
|
3127
|
+
suggestion: "Context or token limit exceeded. The conversation may be automatically compacted."
|
|
3128
|
+
});
|
|
3129
|
+
}
|
|
3130
|
+
if (lowerMessage.includes("invalid parameter") || lowerMessage.includes("missing parameter") || lowerMessage.includes("required") || lowerMessage.includes("must be")) {
|
|
3131
|
+
return new ParameterError(message, {
|
|
3132
|
+
originalError: error
|
|
3133
|
+
});
|
|
3134
|
+
}
|
|
3135
|
+
if (lowerMessage.includes("invalid") && (lowerMessage.includes("api") || lowerMessage.includes("key") || lowerMessage.includes("auth"))) {
|
|
3136
|
+
return new ApiError(message, {
|
|
3137
|
+
originalError: error,
|
|
3138
|
+
recoverable: false,
|
|
3139
|
+
suggestion: "API authentication error. Please check the API key configuration."
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
if (lowerMessage.includes("delegation failed") || lowerMessage.includes("delegate agent") || lowerMessage.includes("subagent")) {
|
|
3143
|
+
return new DelegationError(message, {
|
|
3144
|
+
originalError: error
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
if (errorCode === "econnreset" || errorCode === "econnrefused" || errorCode === "enotfound" || lowerMessage.includes("network") || lowerMessage.includes("connection")) {
|
|
3148
|
+
return new ApiError(message, {
|
|
3149
|
+
originalError: error,
|
|
3150
|
+
recoverable: true,
|
|
3151
|
+
suggestion: "Network error encountered. The system will retry automatically."
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
return new ProbeError(message, {
|
|
3155
|
+
category: ErrorCategory.INTERNAL_ERROR,
|
|
3156
|
+
recoverable: false,
|
|
3157
|
+
originalError: error,
|
|
3158
|
+
suggestion: "An unexpected error occurred. Please check the error message for details."
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
function formatErrorForAI(error) {
|
|
3162
|
+
const structuredError = categorizeError(error);
|
|
3163
|
+
return structuredError.toXml();
|
|
3164
|
+
}
|
|
3165
|
+
var ErrorCategory, ProbeError, PathError, ParameterError, TimeoutError, ApiError, DelegationError;
|
|
3166
|
+
var init_error_types = __esm({
|
|
3167
|
+
"src/utils/error-types.js"() {
|
|
3168
|
+
"use strict";
|
|
3169
|
+
ErrorCategory = {
|
|
3170
|
+
PATH_ERROR: "path_error",
|
|
3171
|
+
// Path doesn't exist, not a directory, permission denied
|
|
3172
|
+
PARAMETER_ERROR: "parameter_error",
|
|
3173
|
+
// Invalid parameters provided
|
|
3174
|
+
TIMEOUT_ERROR: "timeout_error",
|
|
3175
|
+
// Operation timed out
|
|
3176
|
+
API_ERROR: "api_error",
|
|
3177
|
+
// AI provider errors (rate limit, token limit)
|
|
3178
|
+
DELEGATION_ERROR: "delegation_error",
|
|
3179
|
+
// Subagent failures
|
|
3180
|
+
INTERNAL_ERROR: "internal_error"
|
|
3181
|
+
// Unexpected system errors
|
|
3182
|
+
};
|
|
3183
|
+
ProbeError = class extends Error {
|
|
3184
|
+
/**
|
|
3185
|
+
* Create a ProbeError
|
|
3186
|
+
* @param {string} message - Error message
|
|
3187
|
+
* @param {Object} options - Options
|
|
3188
|
+
* @param {string} [options.category] - Error category from ErrorCategory
|
|
3189
|
+
* @param {boolean} [options.recoverable] - Whether the error is recoverable by AI action
|
|
3190
|
+
* @param {string} [options.suggestion] - Suggested action for recovery
|
|
3191
|
+
* @param {Object} [options.details] - Additional error details
|
|
3192
|
+
* @param {Error} [options.originalError] - Original error that was wrapped
|
|
3193
|
+
*/
|
|
3194
|
+
constructor(message, options = {}) {
|
|
3195
|
+
super(message);
|
|
3196
|
+
this.name = "ProbeError";
|
|
3197
|
+
this.category = options.category || ErrorCategory.INTERNAL_ERROR;
|
|
3198
|
+
this.recoverable = options.recoverable ?? false;
|
|
3199
|
+
this.suggestion = options.suggestion || null;
|
|
3200
|
+
this.details = options.details || {};
|
|
3201
|
+
this.originalError = options.originalError || null;
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Format error as XML for AI consumption
|
|
3205
|
+
* @returns {string} - XML-formatted error
|
|
3206
|
+
*/
|
|
3207
|
+
toXml() {
|
|
3208
|
+
const parts = [
|
|
3209
|
+
`<error type="${this.category}" recoverable="${this.recoverable}">`,
|
|
3210
|
+
`<message>${escapeXml(this.message)}</message>`
|
|
3211
|
+
];
|
|
3212
|
+
if (this.suggestion) {
|
|
3213
|
+
parts.push(`<suggestion>${escapeXml(this.suggestion)}</suggestion>`);
|
|
3214
|
+
}
|
|
3215
|
+
if (Object.keys(this.details).length > 0) {
|
|
3216
|
+
parts.push(`<details>${escapeXml(JSON.stringify(this.details))}</details>`);
|
|
3217
|
+
}
|
|
3218
|
+
parts.push("</error>");
|
|
3219
|
+
return parts.join("\n");
|
|
3220
|
+
}
|
|
3221
|
+
/**
|
|
3222
|
+
* Format error for plain text (backward compatible)
|
|
3223
|
+
* @returns {string} - Plain text error
|
|
3224
|
+
*/
|
|
3225
|
+
toString() {
|
|
3226
|
+
return `Error: ${this.message}`;
|
|
3227
|
+
}
|
|
3228
|
+
};
|
|
3229
|
+
PathError = class extends ProbeError {
|
|
3230
|
+
constructor(message, options = {}) {
|
|
3231
|
+
super(message, {
|
|
3232
|
+
...options,
|
|
3233
|
+
category: ErrorCategory.PATH_ERROR,
|
|
3234
|
+
recoverable: options.recoverable ?? true,
|
|
3235
|
+
suggestion: options.suggestion || "Please verify the path exists or try searching in a different directory."
|
|
3236
|
+
});
|
|
3237
|
+
this.name = "PathError";
|
|
3238
|
+
}
|
|
3239
|
+
};
|
|
3240
|
+
ParameterError = class extends ProbeError {
|
|
3241
|
+
constructor(message, options = {}) {
|
|
3242
|
+
super(message, {
|
|
3243
|
+
...options,
|
|
3244
|
+
category: ErrorCategory.PARAMETER_ERROR,
|
|
3245
|
+
recoverable: options.recoverable ?? true,
|
|
3246
|
+
suggestion: options.suggestion || "Please check and correct the parameter values."
|
|
3247
|
+
});
|
|
3248
|
+
this.name = "ParameterError";
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
TimeoutError = class extends ProbeError {
|
|
3252
|
+
constructor(message, options = {}) {
|
|
3253
|
+
super(message, {
|
|
3254
|
+
...options,
|
|
3255
|
+
category: ErrorCategory.TIMEOUT_ERROR,
|
|
3256
|
+
recoverable: options.recoverable ?? true,
|
|
3257
|
+
suggestion: options.suggestion || "The operation timed out. Try a more specific query or increase the timeout."
|
|
3258
|
+
});
|
|
3259
|
+
this.name = "TimeoutError";
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
ApiError = class extends ProbeError {
|
|
3263
|
+
constructor(message, options = {}) {
|
|
3264
|
+
super(message, {
|
|
3265
|
+
...options,
|
|
3266
|
+
category: ErrorCategory.API_ERROR,
|
|
3267
|
+
recoverable: options.recoverable ?? false,
|
|
3268
|
+
suggestion: options.suggestion || "This is an API provider error. The system will retry automatically if possible."
|
|
3269
|
+
});
|
|
3270
|
+
this.name = "ApiError";
|
|
3271
|
+
}
|
|
3272
|
+
};
|
|
3273
|
+
DelegationError = class extends ProbeError {
|
|
3274
|
+
constructor(message, options = {}) {
|
|
3275
|
+
super(message, {
|
|
3276
|
+
...options,
|
|
3277
|
+
category: ErrorCategory.DELEGATION_ERROR,
|
|
3278
|
+
recoverable: options.recoverable ?? true,
|
|
3279
|
+
suggestion: options.suggestion || "The delegated task failed. Consider breaking down the task further or trying a different approach."
|
|
3280
|
+
});
|
|
3281
|
+
this.name = "DelegationError";
|
|
3282
|
+
}
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
});
|
|
3286
|
+
|
|
3071
3287
|
// src/utils/path-validation.js
|
|
3072
3288
|
import path4 from "path";
|
|
3073
3289
|
import { promises as fs5 } from "fs";
|
|
@@ -3077,11 +3293,27 @@ async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
3077
3293
|
try {
|
|
3078
3294
|
const stats = await fs5.stat(normalizedPath);
|
|
3079
3295
|
if (!stats.isDirectory()) {
|
|
3080
|
-
throw new
|
|
3296
|
+
throw new PathError(`Path is not a directory: ${normalizedPath}`, {
|
|
3297
|
+
suggestion: "The specified path is a file, not a directory. Please provide a directory path for searching.",
|
|
3298
|
+
details: { path: normalizedPath, type: "file" }
|
|
3299
|
+
});
|
|
3081
3300
|
}
|
|
3082
3301
|
} catch (error) {
|
|
3302
|
+
if (error instanceof PathError) {
|
|
3303
|
+
throw error;
|
|
3304
|
+
}
|
|
3083
3305
|
if (error.code === "ENOENT") {
|
|
3084
|
-
throw new
|
|
3306
|
+
throw new PathError(`Path does not exist: ${normalizedPath}`, {
|
|
3307
|
+
suggestion: "The specified path does not exist. Please verify the path is correct or use a different directory.",
|
|
3308
|
+
details: { path: normalizedPath }
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
if (error.code === "EACCES") {
|
|
3312
|
+
throw new PathError(`Permission denied: ${normalizedPath}`, {
|
|
3313
|
+
recoverable: false,
|
|
3314
|
+
suggestion: "Permission denied accessing this path. This is a system-level restriction.",
|
|
3315
|
+
details: { path: normalizedPath }
|
|
3316
|
+
});
|
|
3085
3317
|
}
|
|
3086
3318
|
throw error;
|
|
3087
3319
|
}
|
|
@@ -3090,6 +3322,7 @@ async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
3090
3322
|
var init_path_validation = __esm({
|
|
3091
3323
|
"src/utils/path-validation.js"() {
|
|
3092
3324
|
"use strict";
|
|
3325
|
+
init_error_types();
|
|
3093
3326
|
}
|
|
3094
3327
|
});
|
|
3095
3328
|
|
|
@@ -3210,18 +3443,25 @@ Search results: ${resultCount} matches, ${tokenCount} tokens`;
|
|
|
3210
3443
|
return stdout;
|
|
3211
3444
|
} catch (error) {
|
|
3212
3445
|
if (error.code === "ETIMEDOUT" || error.killed) {
|
|
3213
|
-
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds
|
|
3214
|
-
Binary: ${binaryPath}
|
|
3215
|
-
Args: ${args.join(" ")}
|
|
3216
|
-
Cwd: ${cwd}`;
|
|
3446
|
+
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds`;
|
|
3217
3447
|
console.error(timeoutMessage);
|
|
3218
|
-
throw new
|
|
3448
|
+
throw new TimeoutError(timeoutMessage, {
|
|
3449
|
+
suggestion: "The search operation timed out. Try a more specific query, reduce the search scope, or increase the timeout.",
|
|
3450
|
+
details: {
|
|
3451
|
+
timeout: options.timeout,
|
|
3452
|
+
query: options.query,
|
|
3453
|
+
path: options.path
|
|
3454
|
+
}
|
|
3455
|
+
});
|
|
3219
3456
|
}
|
|
3220
|
-
const
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3457
|
+
const structuredError = categorizeError(error);
|
|
3458
|
+
structuredError.details = {
|
|
3459
|
+
...structuredError.details,
|
|
3460
|
+
binary: binaryPath,
|
|
3461
|
+
args: args.join(" "),
|
|
3462
|
+
cwd
|
|
3463
|
+
};
|
|
3464
|
+
throw structuredError;
|
|
3225
3465
|
}
|
|
3226
3466
|
}
|
|
3227
3467
|
var execFileAsync, SEARCH_FLAG_MAP;
|
|
@@ -3230,6 +3470,7 @@ var init_search = __esm({
|
|
|
3230
3470
|
"use strict";
|
|
3231
3471
|
init_utils();
|
|
3232
3472
|
init_path_validation();
|
|
3473
|
+
init_error_types();
|
|
3233
3474
|
execFileAsync = promisify2(execFile);
|
|
3234
3475
|
SEARCH_FLAG_MAP = {
|
|
3235
3476
|
filesOnly: "--files-only",
|
|
@@ -3522,7 +3763,8 @@ async function delegate({
|
|
|
3522
3763
|
allowedTools = null,
|
|
3523
3764
|
disableTools = false,
|
|
3524
3765
|
searchDelegate = void 0,
|
|
3525
|
-
schema = null
|
|
3766
|
+
schema = null,
|
|
3767
|
+
enableTasks = false
|
|
3526
3768
|
}) {
|
|
3527
3769
|
if (!task || typeof task !== "string") {
|
|
3528
3770
|
throw new Error("Task parameter is required and must be a string");
|
|
@@ -3577,7 +3819,9 @@ async function delegate({
|
|
|
3577
3819
|
architectureFileName,
|
|
3578
3820
|
allowedTools,
|
|
3579
3821
|
disableTools,
|
|
3580
|
-
searchDelegate
|
|
3822
|
+
searchDelegate,
|
|
3823
|
+
enableTasks
|
|
3824
|
+
// Inherit from parent (subagent gets isolated TaskManager)
|
|
3581
3825
|
});
|
|
3582
3826
|
if (debug) {
|
|
3583
3827
|
console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
|
|
@@ -8202,6 +8446,439 @@ This is a new project.</content>
|
|
|
8202
8446
|
}
|
|
8203
8447
|
});
|
|
8204
8448
|
|
|
8449
|
+
// src/agent/tasks/taskTool.js
|
|
8450
|
+
function createTaskCompletionBlockedMessage(taskSummary) {
|
|
8451
|
+
return `<task_completion_blocked>
|
|
8452
|
+
You cannot complete yet. The following tasks are still unresolved:
|
|
8453
|
+
|
|
8454
|
+
${taskSummary}
|
|
8455
|
+
|
|
8456
|
+
Required action:
|
|
8457
|
+
1. For each "pending" or "in_progress" task, either:
|
|
8458
|
+
- Complete the work and mark it: <task><action>complete</action><id>task-X</id></task>
|
|
8459
|
+
- Or cancel if no longer needed: <task><action>update</action><id>task-X</id><status>cancelled</status></task>
|
|
8460
|
+
|
|
8461
|
+
2. After ALL tasks are resolved (completed or cancelled), call attempt_completion again.
|
|
8462
|
+
|
|
8463
|
+
Use <task><action>list</action></task> to review current status.
|
|
8464
|
+
</task_completion_blocked>`;
|
|
8465
|
+
}
|
|
8466
|
+
function createTaskTool(options = {}) {
|
|
8467
|
+
const { taskManager, tracer, debug = false } = options;
|
|
8468
|
+
if (!taskManager) {
|
|
8469
|
+
throw new Error("TaskManager instance is required");
|
|
8470
|
+
}
|
|
8471
|
+
const recordTaskEvent = (eventType, data = {}) => {
|
|
8472
|
+
if (tracer && typeof tracer.recordTaskEvent === "function") {
|
|
8473
|
+
tracer.recordTaskEvent(eventType, data);
|
|
8474
|
+
}
|
|
8475
|
+
};
|
|
8476
|
+
return {
|
|
8477
|
+
name: "task",
|
|
8478
|
+
description: "Manage tasks for tracking progress during code exploration",
|
|
8479
|
+
parameters: taskSchema,
|
|
8480
|
+
/**
|
|
8481
|
+
* Execute task action
|
|
8482
|
+
* @param {Object} params - Tool parameters
|
|
8483
|
+
* @returns {string} Result message
|
|
8484
|
+
*/
|
|
8485
|
+
execute: async (params) => {
|
|
8486
|
+
try {
|
|
8487
|
+
const validation = taskSchema.safeParse(params);
|
|
8488
|
+
if (!validation.success) {
|
|
8489
|
+
recordTaskEvent("validation_error", {
|
|
8490
|
+
"task.error": validation.error.message
|
|
8491
|
+
});
|
|
8492
|
+
return `Error: Invalid task parameters - ${validation.error.message}`;
|
|
8493
|
+
}
|
|
8494
|
+
const { action, tasks, id, title, description, status, priority, dependencies, after } = validation.data;
|
|
8495
|
+
switch (action) {
|
|
8496
|
+
case "create": {
|
|
8497
|
+
if (tasks && Array.isArray(tasks)) {
|
|
8498
|
+
const created = taskManager.createTasks(tasks);
|
|
8499
|
+
const ids = created.map((t) => t.id).join(", ");
|
|
8500
|
+
recordTaskEvent("batch_created", {
|
|
8501
|
+
"task.action": "create",
|
|
8502
|
+
"task.count": created.length,
|
|
8503
|
+
"task.ids": ids,
|
|
8504
|
+
"task.total_count": taskManager.listTasks().length
|
|
8505
|
+
});
|
|
8506
|
+
return `Created ${created.length} tasks: ${ids}
|
|
8507
|
+
|
|
8508
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8509
|
+
} else if (title) {
|
|
8510
|
+
const task = taskManager.createTask({ title, description, priority, dependencies, after });
|
|
8511
|
+
recordTaskEvent("created", {
|
|
8512
|
+
"task.action": "create",
|
|
8513
|
+
"task.id": task.id,
|
|
8514
|
+
"task.title": title,
|
|
8515
|
+
"task.priority": priority || "none",
|
|
8516
|
+
"task.has_dependencies": dependencies && dependencies.length > 0,
|
|
8517
|
+
"task.after": after || "none",
|
|
8518
|
+
"task.total_count": taskManager.listTasks().length
|
|
8519
|
+
});
|
|
8520
|
+
return `Created task ${task.id}: ${task.title}
|
|
8521
|
+
|
|
8522
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8523
|
+
} else {
|
|
8524
|
+
return 'Error: Create action requires either "tasks" array or "title" parameter';
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
case "update": {
|
|
8528
|
+
if (tasks && Array.isArray(tasks)) {
|
|
8529
|
+
const updated = taskManager.updateTasks(tasks);
|
|
8530
|
+
const ids = updated.map((t) => t.id).join(", ");
|
|
8531
|
+
recordTaskEvent("batch_updated", {
|
|
8532
|
+
"task.action": "update",
|
|
8533
|
+
"task.count": updated.length,
|
|
8534
|
+
"task.ids": ids
|
|
8535
|
+
});
|
|
8536
|
+
return `Updated ${updated.length} tasks: ${ids}
|
|
8537
|
+
|
|
8538
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8539
|
+
} else if (id) {
|
|
8540
|
+
const updates = {};
|
|
8541
|
+
if (status) updates.status = status;
|
|
8542
|
+
if (title) updates.title = title;
|
|
8543
|
+
if (description) updates.description = description;
|
|
8544
|
+
if (priority) updates.priority = priority;
|
|
8545
|
+
if (dependencies) updates.dependencies = dependencies;
|
|
8546
|
+
const task = taskManager.updateTask(id, updates);
|
|
8547
|
+
recordTaskEvent("updated", {
|
|
8548
|
+
"task.action": "update",
|
|
8549
|
+
"task.id": id,
|
|
8550
|
+
"task.new_status": status || "unchanged",
|
|
8551
|
+
"task.fields_updated": Object.keys(updates).join(", ")
|
|
8552
|
+
});
|
|
8553
|
+
return `Updated task ${task.id}
|
|
8554
|
+
|
|
8555
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8556
|
+
} else {
|
|
8557
|
+
return 'Error: Update action requires either "tasks" array or "id" parameter';
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8560
|
+
case "complete": {
|
|
8561
|
+
if (tasks && Array.isArray(tasks)) {
|
|
8562
|
+
const ids = tasks.map((t, index) => {
|
|
8563
|
+
if (typeof t === "string") return t;
|
|
8564
|
+
if (t && typeof t.id === "string") return t.id;
|
|
8565
|
+
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
8566
|
+
});
|
|
8567
|
+
const completed = taskManager.completeTasks(ids);
|
|
8568
|
+
recordTaskEvent("batch_completed", {
|
|
8569
|
+
"task.action": "complete",
|
|
8570
|
+
"task.count": completed.length,
|
|
8571
|
+
"task.ids": ids.join(", "),
|
|
8572
|
+
"task.incomplete_remaining": taskManager.getIncompleteTasks().length
|
|
8573
|
+
});
|
|
8574
|
+
return `Completed ${completed.length} tasks
|
|
8575
|
+
|
|
8576
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8577
|
+
} else if (id) {
|
|
8578
|
+
const task = taskManager.completeTask(id);
|
|
8579
|
+
recordTaskEvent("completed", {
|
|
8580
|
+
"task.action": "complete",
|
|
8581
|
+
"task.id": id,
|
|
8582
|
+
"task.title": task.title,
|
|
8583
|
+
"task.incomplete_remaining": taskManager.getIncompleteTasks().length
|
|
8584
|
+
});
|
|
8585
|
+
return `Completed task ${task.id}: ${task.title}
|
|
8586
|
+
|
|
8587
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8588
|
+
} else {
|
|
8589
|
+
return 'Error: Complete action requires either "tasks" array or "id" parameter';
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
case "delete": {
|
|
8593
|
+
if (tasks && Array.isArray(tasks)) {
|
|
8594
|
+
const ids = tasks.map((t, index) => {
|
|
8595
|
+
if (typeof t === "string") return t;
|
|
8596
|
+
if (t && typeof t.id === "string") return t.id;
|
|
8597
|
+
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
8598
|
+
});
|
|
8599
|
+
const deleted = taskManager.deleteTasks(ids);
|
|
8600
|
+
recordTaskEvent("batch_deleted", {
|
|
8601
|
+
"task.action": "delete",
|
|
8602
|
+
"task.count": deleted.length,
|
|
8603
|
+
"task.ids": deleted.join(", "),
|
|
8604
|
+
"task.total_count": taskManager.listTasks().length
|
|
8605
|
+
});
|
|
8606
|
+
return `Deleted ${deleted.length} tasks: ${deleted.join(", ")}
|
|
8607
|
+
|
|
8608
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8609
|
+
} else if (id) {
|
|
8610
|
+
taskManager.deleteTask(id);
|
|
8611
|
+
recordTaskEvent("deleted", {
|
|
8612
|
+
"task.action": "delete",
|
|
8613
|
+
"task.id": id,
|
|
8614
|
+
"task.total_count": taskManager.listTasks().length
|
|
8615
|
+
});
|
|
8616
|
+
return `Deleted task ${id}
|
|
8617
|
+
|
|
8618
|
+
${taskManager.formatTasksForPrompt()}`;
|
|
8619
|
+
} else {
|
|
8620
|
+
return 'Error: Delete action requires either "tasks" array or "id" parameter';
|
|
8621
|
+
}
|
|
8622
|
+
}
|
|
8623
|
+
case "list": {
|
|
8624
|
+
const allTasks = taskManager.listTasks();
|
|
8625
|
+
const incomplete = taskManager.getIncompleteTasks();
|
|
8626
|
+
recordTaskEvent("listed", {
|
|
8627
|
+
"task.action": "list",
|
|
8628
|
+
"task.total_count": allTasks.length,
|
|
8629
|
+
"task.incomplete_count": incomplete.length,
|
|
8630
|
+
"task.completed_count": allTasks.length - incomplete.length
|
|
8631
|
+
});
|
|
8632
|
+
return taskManager.formatTasksForPrompt();
|
|
8633
|
+
}
|
|
8634
|
+
default:
|
|
8635
|
+
recordTaskEvent("unknown_action", {
|
|
8636
|
+
"task.action": action
|
|
8637
|
+
});
|
|
8638
|
+
return `Error: Unknown action "${action}". Valid actions: create, update, complete, delete, list`;
|
|
8639
|
+
}
|
|
8640
|
+
} catch (error) {
|
|
8641
|
+
recordTaskEvent("error", {
|
|
8642
|
+
"task.error": error.message,
|
|
8643
|
+
"task.action": params?.action || "unknown"
|
|
8644
|
+
});
|
|
8645
|
+
if (debug) {
|
|
8646
|
+
console.error("[TaskTool] Error:", error);
|
|
8647
|
+
}
|
|
8648
|
+
return `Error: ${error.message}`;
|
|
8649
|
+
}
|
|
8650
|
+
}
|
|
8651
|
+
};
|
|
8652
|
+
}
|
|
8653
|
+
var taskItemSchema, taskSchema, taskToolDefinition, taskSystemPrompt, taskGuidancePrompt;
|
|
8654
|
+
var init_taskTool = __esm({
|
|
8655
|
+
"src/agent/tasks/taskTool.js"() {
|
|
8656
|
+
"use strict";
|
|
8657
|
+
init_zod();
|
|
8658
|
+
taskItemSchema = external_exports.object({
|
|
8659
|
+
id: external_exports.string().optional(),
|
|
8660
|
+
title: external_exports.string().optional(),
|
|
8661
|
+
description: external_exports.string().optional(),
|
|
8662
|
+
status: external_exports.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
8663
|
+
priority: external_exports.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8664
|
+
dependencies: external_exports.array(external_exports.string()).optional(),
|
|
8665
|
+
after: external_exports.string().optional()
|
|
8666
|
+
});
|
|
8667
|
+
taskSchema = external_exports.object({
|
|
8668
|
+
action: external_exports.enum(["create", "update", "complete", "delete", "list"]),
|
|
8669
|
+
tasks: external_exports.array(external_exports.union([external_exports.string(), taskItemSchema])).optional(),
|
|
8670
|
+
id: external_exports.string().optional(),
|
|
8671
|
+
title: external_exports.string().optional(),
|
|
8672
|
+
description: external_exports.string().optional(),
|
|
8673
|
+
status: external_exports.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
8674
|
+
priority: external_exports.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8675
|
+
dependencies: external_exports.array(external_exports.string()).optional(),
|
|
8676
|
+
after: external_exports.string().optional()
|
|
8677
|
+
});
|
|
8678
|
+
taskToolDefinition = `## task
|
|
8679
|
+
Manage tasks for tracking progress during code exploration and problem-solving. Create tasks to break down complex problems, track dependencies, and ensure all work is completed.
|
|
8680
|
+
|
|
8681
|
+
Parameters:
|
|
8682
|
+
- action: (required) The action to perform: create, update, complete, delete, list
|
|
8683
|
+
- tasks: (optional) JSON array for batch operations - alternative to single-task params
|
|
8684
|
+
- id: (optional) Task ID for single operations (e.g., "task-1")
|
|
8685
|
+
- title: (optional) Task title for create/update
|
|
8686
|
+
- description: (optional) Task description for create/update
|
|
8687
|
+
- status: (optional) Task status for update: pending, in_progress, completed, cancelled
|
|
8688
|
+
- priority: (optional) Task priority: low, medium, high, critical
|
|
8689
|
+
- dependencies: (optional) JSON array of task IDs that must be completed first
|
|
8690
|
+
- after: (optional) Task ID to insert the new task after (for ordering). By default, new tasks are appended to the end
|
|
8691
|
+
|
|
8692
|
+
Usage Examples:
|
|
8693
|
+
|
|
8694
|
+
Creating a single task:
|
|
8695
|
+
<task>
|
|
8696
|
+
<action>create</action>
|
|
8697
|
+
<title>Analyze authentication module</title>
|
|
8698
|
+
<description>Search and understand how authentication works</description>
|
|
8699
|
+
<priority>high</priority>
|
|
8700
|
+
</task>
|
|
8701
|
+
|
|
8702
|
+
Creating multiple tasks with dependencies:
|
|
8703
|
+
<task>
|
|
8704
|
+
<action>create</action>
|
|
8705
|
+
<tasks>[
|
|
8706
|
+
{"title": "Search for user model", "priority": "high"},
|
|
8707
|
+
{"title": "Analyze authentication flow", "dependencies": ["task-1"]},
|
|
8708
|
+
{"title": "Review session management", "dependencies": ["task-2"]}
|
|
8709
|
+
]</tasks>
|
|
8710
|
+
</task>
|
|
8711
|
+
|
|
8712
|
+
Inserting a task after a specific task (instead of appending to end):
|
|
8713
|
+
<task>
|
|
8714
|
+
<action>create</action>
|
|
8715
|
+
<title>Investigate error handling</title>
|
|
8716
|
+
<after>task-2</after>
|
|
8717
|
+
</task>
|
|
8718
|
+
|
|
8719
|
+
Updating a task status:
|
|
8720
|
+
<task>
|
|
8721
|
+
<action>update</action>
|
|
8722
|
+
<id>task-1</id>
|
|
8723
|
+
<status>in_progress</status>
|
|
8724
|
+
</task>
|
|
8725
|
+
|
|
8726
|
+
Batch updating multiple tasks:
|
|
8727
|
+
<task>
|
|
8728
|
+
<action>update</action>
|
|
8729
|
+
<tasks>[
|
|
8730
|
+
{"id": "task-1", "status": "completed"},
|
|
8731
|
+
{"id": "task-2", "status": "in_progress"}
|
|
8732
|
+
]</tasks>
|
|
8733
|
+
</task>
|
|
8734
|
+
|
|
8735
|
+
Completing a task:
|
|
8736
|
+
<task>
|
|
8737
|
+
<action>complete</action>
|
|
8738
|
+
<id>task-1</id>
|
|
8739
|
+
</task>
|
|
8740
|
+
|
|
8741
|
+
Cancelling a task:
|
|
8742
|
+
<task>
|
|
8743
|
+
<action>update</action>
|
|
8744
|
+
<id>task-1</id>
|
|
8745
|
+
<status>cancelled</status>
|
|
8746
|
+
</task>
|
|
8747
|
+
|
|
8748
|
+
Deleting a task:
|
|
8749
|
+
<task>
|
|
8750
|
+
<action>delete</action>
|
|
8751
|
+
<id>task-1</id>
|
|
8752
|
+
</task>
|
|
8753
|
+
|
|
8754
|
+
Listing all tasks:
|
|
8755
|
+
<task>
|
|
8756
|
+
<action>list</action>
|
|
8757
|
+
</task>
|
|
8758
|
+
`;
|
|
8759
|
+
taskSystemPrompt = `[Task Management System]
|
|
8760
|
+
|
|
8761
|
+
You have access to a task tracking tool to organize your work on complex requests.
|
|
8762
|
+
|
|
8763
|
+
## When to Create Tasks
|
|
8764
|
+
|
|
8765
|
+
CREATE TASKS when the request has **multiple distinct deliverables or goals**:
|
|
8766
|
+
- "Fix bug A AND add feature B" \u2192 Two separate tasks
|
|
8767
|
+
- "Investigate auth, payments, AND notifications" \u2192 Three independent areas
|
|
8768
|
+
- "Implement X, then add tests, then update docs" \u2192 Sequential phases with different outputs
|
|
8769
|
+
- User explicitly asks for a plan or task breakdown
|
|
8770
|
+
|
|
8771
|
+
SKIP TASKS for single-goal requests, even if they require multiple searches:
|
|
8772
|
+
- "How does ranking work?" \u2192 Just investigate and answer (one goal)
|
|
8773
|
+
- "What does function X do?" \u2192 Just look it up (one goal)
|
|
8774
|
+
- "Explain the authentication flow" \u2192 Just trace and explain (one goal)
|
|
8775
|
+
- "Find where errors are logged" \u2192 Just search and report (one goal)
|
|
8776
|
+
|
|
8777
|
+
**Key insight**: Multiple *internal steps* (search, read, analyze) are NOT the same as multiple *goals*.
|
|
8778
|
+
A single investigation with many steps is still ONE task, not many.
|
|
8779
|
+
|
|
8780
|
+
MODIFY TASKS when (during execution):
|
|
8781
|
+
- You discover the problem is more complex than expected \u2192 Add new tasks
|
|
8782
|
+
- A single task covers too much scope \u2192 Split into smaller tasks
|
|
8783
|
+
- You find related work that needs attention \u2192 Add dependent tasks
|
|
8784
|
+
- A task becomes irrelevant based on findings \u2192 Cancel it
|
|
8785
|
+
- Task priorities change based on discoveries \u2192 Update priority
|
|
8786
|
+
- You learn new context \u2192 Update task description
|
|
8787
|
+
|
|
8788
|
+
## Task Workflow
|
|
8789
|
+
|
|
8790
|
+
**STEP 1 - Plan (at start):**
|
|
8791
|
+
Analyze the request and create tasks for each logical step:
|
|
8792
|
+
|
|
8793
|
+
<task>
|
|
8794
|
+
<action>create</action>
|
|
8795
|
+
<tasks>[
|
|
8796
|
+
{"title": "Search for authentication module", "priority": "high"},
|
|
8797
|
+
{"title": "Analyze login flow implementation", "dependencies": ["task-1"]},
|
|
8798
|
+
{"title": "Find session management code", "dependencies": ["task-1"]},
|
|
8799
|
+
{"title": "Summarize authentication architecture", "dependencies": ["task-2", "task-3"]}
|
|
8800
|
+
]</tasks>
|
|
8801
|
+
</task>
|
|
8802
|
+
|
|
8803
|
+
**STEP 2 - Execute (during work):**
|
|
8804
|
+
Update task status as you work:
|
|
8805
|
+
|
|
8806
|
+
<task>
|
|
8807
|
+
<action>update</action>
|
|
8808
|
+
<id>task-1</id>
|
|
8809
|
+
<status>in_progress</status>
|
|
8810
|
+
</task>
|
|
8811
|
+
|
|
8812
|
+
... do the work (search, extract, etc.) ...
|
|
8813
|
+
|
|
8814
|
+
<task>
|
|
8815
|
+
<action>complete</action>
|
|
8816
|
+
<id>task-1</id>
|
|
8817
|
+
</task>
|
|
8818
|
+
|
|
8819
|
+
**STEP 2b - Adapt (when you discover new work):**
|
|
8820
|
+
As you work, you may discover that:
|
|
8821
|
+
- A task is more complex than expected \u2192 Split it into subtasks
|
|
8822
|
+
- New areas need investigation \u2192 Add new tasks
|
|
8823
|
+
- Some tasks are no longer needed \u2192 Cancel them
|
|
8824
|
+
- Task order should change \u2192 Update dependencies
|
|
8825
|
+
|
|
8826
|
+
*Adding a new task when you discover more work:*
|
|
8827
|
+
<task>
|
|
8828
|
+
<action>create</action>
|
|
8829
|
+
<title>Investigate caching layer</title>
|
|
8830
|
+
<description>Found references to Redis caching in auth module</description>
|
|
8831
|
+
</task>
|
|
8832
|
+
|
|
8833
|
+
*Inserting a task after a specific task (to maintain logical order):*
|
|
8834
|
+
<task>
|
|
8835
|
+
<action>create</action>
|
|
8836
|
+
<title>Check rate limiting</title>
|
|
8837
|
+
<after>task-2</after>
|
|
8838
|
+
</task>
|
|
8839
|
+
|
|
8840
|
+
*Cancelling and splitting a complex task:*
|
|
8841
|
+
<task>
|
|
8842
|
+
<action>update</action>
|
|
8843
|
+
<id>task-3</id>
|
|
8844
|
+
<status>cancelled</status>
|
|
8845
|
+
</task>
|
|
8846
|
+
<task>
|
|
8847
|
+
<action>create</action>
|
|
8848
|
+
<tasks>[
|
|
8849
|
+
{"title": "Review JWT token generation", "priority": "high"},
|
|
8850
|
+
{"title": "Review token refresh logic"}
|
|
8851
|
+
]</tasks>
|
|
8852
|
+
</task>
|
|
8853
|
+
|
|
8854
|
+
**STEP 3 - Finish (before completion):**
|
|
8855
|
+
Before calling attempt_completion, ensure ALL tasks are either:
|
|
8856
|
+
- \`completed\` - you finished the work
|
|
8857
|
+
- \`cancelled\` - no longer needed
|
|
8858
|
+
|
|
8859
|
+
If you created tasks, you MUST resolve them all before completing.
|
|
8860
|
+
|
|
8861
|
+
## Key Rules
|
|
8862
|
+
|
|
8863
|
+
1. **Dependencies are enforced**: A task cannot start until its dependencies are completed
|
|
8864
|
+
2. **Circular dependencies are rejected**: task-1 \u2192 task-2 \u2192 task-1 is invalid
|
|
8865
|
+
3. **Completion is blocked**: attempt_completion will fail if tasks remain unresolved
|
|
8866
|
+
4. **List to review**: Use <task><action>list</action></task> to see current task status
|
|
8867
|
+
5. **Tasks are living documents**: Add, split, or cancel tasks as you learn more about the problem
|
|
8868
|
+
`;
|
|
8869
|
+
taskGuidancePrompt = `<task_guidance>
|
|
8870
|
+
Does this request have MULTIPLE DISTINCT GOALS?
|
|
8871
|
+
- "Do A AND B AND C" (multiple goals) \u2192 Create tasks for each goal
|
|
8872
|
+
- "Investigate/explain/find X" (single goal) \u2192 Skip tasks, just answer directly
|
|
8873
|
+
|
|
8874
|
+
Multiple internal steps (search, read, analyze) for ONE goal = NO tasks needed.
|
|
8875
|
+
Only create tasks when there are separate deliverables the user is asking for.
|
|
8876
|
+
|
|
8877
|
+
If creating tasks, use the task tool with action="create" first.
|
|
8878
|
+
</task_guidance>`;
|
|
8879
|
+
}
|
|
8880
|
+
});
|
|
8881
|
+
|
|
8205
8882
|
// src/tools/common.js
|
|
8206
8883
|
import { resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
8207
8884
|
function buildToolTagPattern(tools2 = DEFAULT_VALID_TOOLS) {
|
|
@@ -8221,6 +8898,7 @@ function getValidParamsForTool(toolName) {
|
|
|
8221
8898
|
listSkills: listSkillsSchema,
|
|
8222
8899
|
useSkill: useSkillSchema,
|
|
8223
8900
|
bash: bashSchema,
|
|
8901
|
+
task: taskSchema,
|
|
8224
8902
|
attempt_completion: attemptCompletionSchema,
|
|
8225
8903
|
edit: editSchema,
|
|
8226
8904
|
create: createSchema
|
|
@@ -8327,6 +9005,62 @@ function createMessagePreview(message, charsPerSide = 200) {
|
|
|
8327
9005
|
const end = message.substring(message.length - charsPerSide);
|
|
8328
9006
|
return `${start}...${end}`;
|
|
8329
9007
|
}
|
|
9008
|
+
function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
9009
|
+
if (!xmlString || typeof xmlString !== "string") {
|
|
9010
|
+
return null;
|
|
9011
|
+
}
|
|
9012
|
+
const knownToolNames = [
|
|
9013
|
+
"search",
|
|
9014
|
+
"query",
|
|
9015
|
+
"extract",
|
|
9016
|
+
"listFiles",
|
|
9017
|
+
"searchFiles",
|
|
9018
|
+
"listSkills",
|
|
9019
|
+
"useSkill",
|
|
9020
|
+
"readImage",
|
|
9021
|
+
"implement",
|
|
9022
|
+
"edit",
|
|
9023
|
+
"create",
|
|
9024
|
+
"delegate",
|
|
9025
|
+
"bash",
|
|
9026
|
+
"task",
|
|
9027
|
+
"attempt_completion",
|
|
9028
|
+
"attempt_complete",
|
|
9029
|
+
"read_file",
|
|
9030
|
+
"write_file",
|
|
9031
|
+
"run_command",
|
|
9032
|
+
"grep",
|
|
9033
|
+
"find",
|
|
9034
|
+
"cat",
|
|
9035
|
+
"list_directory"
|
|
9036
|
+
];
|
|
9037
|
+
for (const toolName of knownToolNames) {
|
|
9038
|
+
if (validTools.includes(toolName)) {
|
|
9039
|
+
continue;
|
|
9040
|
+
}
|
|
9041
|
+
const openTag = `<${toolName}>`;
|
|
9042
|
+
const closeTag = `</${toolName}>`;
|
|
9043
|
+
const openIndex = xmlString.indexOf(openTag);
|
|
9044
|
+
if (openIndex === -1) {
|
|
9045
|
+
continue;
|
|
9046
|
+
}
|
|
9047
|
+
let isNested = false;
|
|
9048
|
+
for (const validTool of validTools) {
|
|
9049
|
+
const validOpenTag = `<${validTool}>`;
|
|
9050
|
+
const validCloseTag = `</${validTool}>`;
|
|
9051
|
+
const validOpenIndex = xmlString.indexOf(validOpenTag);
|
|
9052
|
+
const validCloseIndex = xmlString.indexOf(validCloseTag);
|
|
9053
|
+
if (validOpenIndex !== -1 && validCloseIndex !== -1 && validOpenIndex < openIndex && openIndex < validCloseIndex) {
|
|
9054
|
+
isNested = true;
|
|
9055
|
+
break;
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
if (!isNested) {
|
|
9059
|
+
return toolName;
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
return null;
|
|
9063
|
+
}
|
|
8330
9064
|
function parseTargets(targets) {
|
|
8331
9065
|
if (!targets || typeof targets !== "string") {
|
|
8332
9066
|
return [];
|
|
@@ -8369,6 +9103,7 @@ var init_common = __esm({
|
|
|
8369
9103
|
"use strict";
|
|
8370
9104
|
init_zod();
|
|
8371
9105
|
init_edit();
|
|
9106
|
+
init_taskTool();
|
|
8372
9107
|
searchSchema = external_exports.object({
|
|
8373
9108
|
query: external_exports.string().describe("Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
|
|
8374
9109
|
path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.')
|
|
@@ -8679,6 +9414,7 @@ User: Check system info
|
|
|
8679
9414
|
"searchFiles",
|
|
8680
9415
|
"implement",
|
|
8681
9416
|
"bash",
|
|
9417
|
+
"task",
|
|
8682
9418
|
"attempt_completion"
|
|
8683
9419
|
];
|
|
8684
9420
|
}
|
|
@@ -8764,15 +9500,29 @@ function parseDelegatedTargets(rawResponse) {
|
|
|
8764
9500
|
}
|
|
8765
9501
|
function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, allowTests }) {
|
|
8766
9502
|
return [
|
|
8767
|
-
"You are a code-search subagent. Your
|
|
8768
|
-
"
|
|
8769
|
-
|
|
8770
|
-
"
|
|
8771
|
-
|
|
9503
|
+
"You are a code-search subagent. Your job is to find ALL relevant code locations for the given query.",
|
|
9504
|
+
"",
|
|
9505
|
+
"The query may be complex - it could be a natural language question, a multi-part request, or a simple keyword.",
|
|
9506
|
+
"Break down complex queries into multiple searches to cover all aspects.",
|
|
9507
|
+
"",
|
|
9508
|
+
"Available tools:",
|
|
9509
|
+
"- search: Find code matching keywords or patterns. Run multiple searches for different aspects of complex queries.",
|
|
9510
|
+
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
9511
|
+
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
9512
|
+
"",
|
|
9513
|
+
"Strategy for complex queries:",
|
|
9514
|
+
"1. Analyze the query - identify key concepts, entities, and relationships",
|
|
9515
|
+
'2. Run focused searches for each concept (e.g., "error handling" + "authentication" separately)',
|
|
9516
|
+
"3. Use extract to verify relevance of promising results",
|
|
9517
|
+
"4. Combine all relevant targets in your final response",
|
|
9518
|
+
"",
|
|
9519
|
+
`Query: ${searchQuery}`,
|
|
8772
9520
|
`Search path(s): ${searchPath}`,
|
|
8773
9521
|
`Options: exact=${exact ? "true" : "false"}, language=${language || "auto"}, allow_tests=${allowTests ? "true" : "false"}.`,
|
|
8774
|
-
"
|
|
8775
|
-
"
|
|
9522
|
+
"",
|
|
9523
|
+
'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
|
|
9524
|
+
"Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
|
|
9525
|
+
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets."
|
|
8776
9526
|
].join("\n");
|
|
8777
9527
|
}
|
|
8778
9528
|
var CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool;
|
|
@@ -8784,6 +9534,7 @@ var init_vercel = __esm({
|
|
|
8784
9534
|
init_extract();
|
|
8785
9535
|
init_delegate();
|
|
8786
9536
|
init_common();
|
|
9537
|
+
init_error_types();
|
|
8787
9538
|
CODE_SEARCH_SCHEMA = {
|
|
8788
9539
|
type: "object",
|
|
8789
9540
|
properties: {
|
|
@@ -8846,7 +9597,7 @@ var init_vercel = __esm({
|
|
|
8846
9597
|
return await runRawSearch();
|
|
8847
9598
|
} catch (error) {
|
|
8848
9599
|
console.error("Error executing search command:", error);
|
|
8849
|
-
return
|
|
9600
|
+
return formatErrorForAI(error);
|
|
8850
9601
|
}
|
|
8851
9602
|
}
|
|
8852
9603
|
try {
|
|
@@ -8873,7 +9624,7 @@ var init_vercel = __esm({
|
|
|
8873
9624
|
bashConfig: null,
|
|
8874
9625
|
architectureFileName: options.architectureFileName || null,
|
|
8875
9626
|
promptType: "code-searcher",
|
|
8876
|
-
allowedTools: ["search", "attempt_completion"],
|
|
9627
|
+
allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
|
|
8877
9628
|
searchDelegate: false,
|
|
8878
9629
|
schema: CODE_SEARCH_SCHEMA
|
|
8879
9630
|
});
|
|
@@ -8905,7 +9656,7 @@ var init_vercel = __esm({
|
|
|
8905
9656
|
return await runRawSearch();
|
|
8906
9657
|
} catch (fallbackError) {
|
|
8907
9658
|
console.error("Error executing search command:", fallbackError);
|
|
8908
|
-
return
|
|
9659
|
+
return formatErrorForAI(fallbackError);
|
|
8909
9660
|
}
|
|
8910
9661
|
}
|
|
8911
9662
|
}
|
|
@@ -8942,7 +9693,7 @@ var init_vercel = __esm({
|
|
|
8942
9693
|
return results;
|
|
8943
9694
|
} catch (error) {
|
|
8944
9695
|
console.error("Error executing query command:", error);
|
|
8945
|
-
return
|
|
9696
|
+
return formatErrorForAI(error);
|
|
8946
9697
|
}
|
|
8947
9698
|
}
|
|
8948
9699
|
});
|
|
@@ -9018,7 +9769,7 @@ var init_vercel = __esm({
|
|
|
9018
9769
|
return results;
|
|
9019
9770
|
} catch (error) {
|
|
9020
9771
|
console.error("Error executing extract command:", error);
|
|
9021
|
-
return
|
|
9772
|
+
return formatErrorForAI(error);
|
|
9022
9773
|
}
|
|
9023
9774
|
}
|
|
9024
9775
|
});
|
|
@@ -10904,6 +11655,16 @@ var init_simpleTelemetry = __esm({
|
|
|
10904
11655
|
...data
|
|
10905
11656
|
});
|
|
10906
11657
|
}
|
|
11658
|
+
/**
|
|
11659
|
+
* Record task management events
|
|
11660
|
+
*/
|
|
11661
|
+
recordTaskEvent(eventType, data = {}) {
|
|
11662
|
+
if (!this.isEnabled()) return;
|
|
11663
|
+
this.addEvent(`task.${eventType}`, {
|
|
11664
|
+
"session.id": this.sessionId,
|
|
11665
|
+
...data
|
|
11666
|
+
});
|
|
11667
|
+
}
|
|
10907
11668
|
setAttributes(attributes) {
|
|
10908
11669
|
if (this.telemetry && this.telemetry.enableConsole) {
|
|
10909
11670
|
console.log("[Attributes]", attributes);
|
|
@@ -17932,6 +18693,507 @@ var init_hooks = __esm({
|
|
|
17932
18693
|
}
|
|
17933
18694
|
});
|
|
17934
18695
|
|
|
18696
|
+
// src/agent/tasks/TaskManager.js
|
|
18697
|
+
var TaskManager;
|
|
18698
|
+
var init_TaskManager = __esm({
|
|
18699
|
+
"src/agent/tasks/TaskManager.js"() {
|
|
18700
|
+
"use strict";
|
|
18701
|
+
TaskManager = class _TaskManager {
|
|
18702
|
+
/**
|
|
18703
|
+
* Create a new TaskManager instance
|
|
18704
|
+
* @param {Object} [options] - Configuration options
|
|
18705
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
18706
|
+
*/
|
|
18707
|
+
constructor(options = {}) {
|
|
18708
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
18709
|
+
this.taskCounter = 0;
|
|
18710
|
+
this.debug = options.debug || false;
|
|
18711
|
+
}
|
|
18712
|
+
/**
|
|
18713
|
+
* Generate the next task ID
|
|
18714
|
+
* @returns {string} New task ID
|
|
18715
|
+
* @private
|
|
18716
|
+
*/
|
|
18717
|
+
_generateId() {
|
|
18718
|
+
this.taskCounter++;
|
|
18719
|
+
return `task-${this.taskCounter}`;
|
|
18720
|
+
}
|
|
18721
|
+
/**
|
|
18722
|
+
* Get current timestamp in ISO format
|
|
18723
|
+
* @returns {string} ISO timestamp
|
|
18724
|
+
* @private
|
|
18725
|
+
*/
|
|
18726
|
+
_now() {
|
|
18727
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
18728
|
+
}
|
|
18729
|
+
/**
|
|
18730
|
+
* Create a single task
|
|
18731
|
+
* @param {Object} taskData - Task data
|
|
18732
|
+
* @param {string} taskData.title - Task title
|
|
18733
|
+
* @param {string} [taskData.description] - Task description
|
|
18734
|
+
* @param {'low'|'medium'|'high'|'critical'} [taskData.priority] - Task priority
|
|
18735
|
+
* @param {string[]} [taskData.dependencies] - Task IDs this task depends on
|
|
18736
|
+
* @param {string} [taskData.after] - Task ID to insert this task after (for ordering)
|
|
18737
|
+
* @returns {Task} Created task
|
|
18738
|
+
* @throws {Error} If dependencies are invalid or create a cycle
|
|
18739
|
+
*/
|
|
18740
|
+
createTask(taskData) {
|
|
18741
|
+
const id = this._generateId();
|
|
18742
|
+
const now = this._now();
|
|
18743
|
+
const dependencies = taskData.dependencies || [];
|
|
18744
|
+
for (const depId of dependencies) {
|
|
18745
|
+
if (!this.tasks.has(depId)) {
|
|
18746
|
+
throw new Error(`Dependency "${depId}" does not exist. Available tasks: ${this._getAvailableTaskIds()}`);
|
|
18747
|
+
}
|
|
18748
|
+
}
|
|
18749
|
+
const afterTaskId = taskData.after;
|
|
18750
|
+
if (afterTaskId && !this.tasks.has(afterTaskId)) {
|
|
18751
|
+
throw new Error(`Task "${afterTaskId}" does not exist. Cannot insert after non-existent task. Available tasks: ${this._getAvailableTaskIds()}`);
|
|
18752
|
+
}
|
|
18753
|
+
if (dependencies.length > 0 && !this._validateNoCycle(id, dependencies)) {
|
|
18754
|
+
throw new Error(`Adding dependencies [${dependencies.join(", ")}] to "${id}" would create a circular dependency`);
|
|
18755
|
+
}
|
|
18756
|
+
const task = {
|
|
18757
|
+
id,
|
|
18758
|
+
title: taskData.title,
|
|
18759
|
+
description: taskData.description || null,
|
|
18760
|
+
status: "pending",
|
|
18761
|
+
priority: taskData.priority || null,
|
|
18762
|
+
dependencies,
|
|
18763
|
+
createdAt: now,
|
|
18764
|
+
updatedAt: now,
|
|
18765
|
+
completedAt: null
|
|
18766
|
+
};
|
|
18767
|
+
if (afterTaskId) {
|
|
18768
|
+
this._insertAfter(afterTaskId, id, task);
|
|
18769
|
+
} else {
|
|
18770
|
+
this.tasks.set(id, task);
|
|
18771
|
+
}
|
|
18772
|
+
if (this.debug) {
|
|
18773
|
+
console.log(`[TaskManager] Created task: ${id} - ${task.title}${afterTaskId ? ` (after ${afterTaskId})` : ""}`);
|
|
18774
|
+
}
|
|
18775
|
+
return task;
|
|
18776
|
+
}
|
|
18777
|
+
/**
|
|
18778
|
+
* Insert a task after a specific task in the Map order
|
|
18779
|
+
* @param {string} afterId - Task ID to insert after
|
|
18780
|
+
* @param {string} newId - New task ID
|
|
18781
|
+
* @param {Task} newTask - New task object
|
|
18782
|
+
* @private
|
|
18783
|
+
*/
|
|
18784
|
+
_insertAfter(afterId, newId, newTask) {
|
|
18785
|
+
const newTasks = /* @__PURE__ */ new Map();
|
|
18786
|
+
for (const [id, task] of this.tasks) {
|
|
18787
|
+
newTasks.set(id, task);
|
|
18788
|
+
if (id === afterId) {
|
|
18789
|
+
newTasks.set(newId, newTask);
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
this.tasks = newTasks;
|
|
18793
|
+
}
|
|
18794
|
+
/**
|
|
18795
|
+
* Create multiple tasks in batch
|
|
18796
|
+
* @param {Object[]} tasksData - Array of task data objects
|
|
18797
|
+
* @returns {Task[]} Created tasks
|
|
18798
|
+
*/
|
|
18799
|
+
createTasks(tasksData) {
|
|
18800
|
+
const createdTasks = [];
|
|
18801
|
+
for (const taskData of tasksData) {
|
|
18802
|
+
const task = this.createTask(taskData);
|
|
18803
|
+
createdTasks.push(task);
|
|
18804
|
+
}
|
|
18805
|
+
return createdTasks;
|
|
18806
|
+
}
|
|
18807
|
+
/**
|
|
18808
|
+
* Get a task by ID
|
|
18809
|
+
* @param {string} id - Task ID
|
|
18810
|
+
* @returns {Task|null} Task or null if not found
|
|
18811
|
+
*/
|
|
18812
|
+
getTask(id) {
|
|
18813
|
+
return this.tasks.get(id) || null;
|
|
18814
|
+
}
|
|
18815
|
+
/**
|
|
18816
|
+
* Valid status values for tasks
|
|
18817
|
+
* @type {string[]}
|
|
18818
|
+
* @private
|
|
18819
|
+
*/
|
|
18820
|
+
static VALID_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
|
|
18821
|
+
/**
|
|
18822
|
+
* Valid priority values for tasks
|
|
18823
|
+
* @type {string[]}
|
|
18824
|
+
* @private
|
|
18825
|
+
*/
|
|
18826
|
+
static VALID_PRIORITIES = ["low", "medium", "high", "critical"];
|
|
18827
|
+
/**
|
|
18828
|
+
* Update a task
|
|
18829
|
+
* @param {string} id - Task ID
|
|
18830
|
+
* @param {Object} updates - Fields to update
|
|
18831
|
+
* @returns {Task} Updated task
|
|
18832
|
+
* @throws {Error} If task not found or update is invalid
|
|
18833
|
+
*/
|
|
18834
|
+
updateTask(id, updates) {
|
|
18835
|
+
const task = this.tasks.get(id);
|
|
18836
|
+
if (!task) {
|
|
18837
|
+
throw new Error(`Task "${id}" not found. Available tasks: ${this._getAvailableTaskIds()}`);
|
|
18838
|
+
}
|
|
18839
|
+
if (updates.status !== void 0) {
|
|
18840
|
+
if (!_TaskManager.VALID_STATUSES.includes(updates.status)) {
|
|
18841
|
+
throw new Error(`Invalid status "${updates.status}". Valid statuses: ${_TaskManager.VALID_STATUSES.join(", ")}`);
|
|
18842
|
+
}
|
|
18843
|
+
if (updates.status === "completed" && !task.completedAt) {
|
|
18844
|
+
updates.completedAt = this._now();
|
|
18845
|
+
}
|
|
18846
|
+
}
|
|
18847
|
+
if (updates.priority !== void 0 && updates.priority !== null) {
|
|
18848
|
+
if (!_TaskManager.VALID_PRIORITIES.includes(updates.priority)) {
|
|
18849
|
+
throw new Error(`Invalid priority "${updates.priority}". Valid priorities: ${_TaskManager.VALID_PRIORITIES.join(", ")}`);
|
|
18850
|
+
}
|
|
18851
|
+
}
|
|
18852
|
+
if (updates.dependencies !== void 0) {
|
|
18853
|
+
if (!Array.isArray(updates.dependencies)) {
|
|
18854
|
+
throw new Error("Dependencies must be an array");
|
|
18855
|
+
}
|
|
18856
|
+
for (const depId of updates.dependencies) {
|
|
18857
|
+
if (!this.tasks.has(depId)) {
|
|
18858
|
+
throw new Error(`Dependency "${depId}" does not exist. Available tasks: ${this._getAvailableTaskIds()}`);
|
|
18859
|
+
}
|
|
18860
|
+
}
|
|
18861
|
+
if (!this._validateNoCycle(id, updates.dependencies)) {
|
|
18862
|
+
throw new Error(`Adding dependencies [${updates.dependencies.join(", ")}] to "${id}" would create a circular dependency`);
|
|
18863
|
+
}
|
|
18864
|
+
}
|
|
18865
|
+
const updatedTask = {
|
|
18866
|
+
...task,
|
|
18867
|
+
...updates,
|
|
18868
|
+
id,
|
|
18869
|
+
// Ensure ID cannot be changed
|
|
18870
|
+
createdAt: task.createdAt,
|
|
18871
|
+
// Ensure createdAt cannot be changed
|
|
18872
|
+
updatedAt: this._now()
|
|
18873
|
+
};
|
|
18874
|
+
this.tasks.set(id, updatedTask);
|
|
18875
|
+
if (this.debug) {
|
|
18876
|
+
console.log(`[TaskManager] Updated task: ${id}`, updates);
|
|
18877
|
+
}
|
|
18878
|
+
return updatedTask;
|
|
18879
|
+
}
|
|
18880
|
+
/**
|
|
18881
|
+
* Update multiple tasks in batch
|
|
18882
|
+
* @param {Object[]} updates - Array of {id, ...updates} objects
|
|
18883
|
+
* @returns {Task[]} Updated tasks
|
|
18884
|
+
*/
|
|
18885
|
+
updateTasks(updates) {
|
|
18886
|
+
const updatedTasks = [];
|
|
18887
|
+
for (const update of updates) {
|
|
18888
|
+
const { id, ...taskUpdates } = update;
|
|
18889
|
+
const task = this.updateTask(id, taskUpdates);
|
|
18890
|
+
updatedTasks.push(task);
|
|
18891
|
+
}
|
|
18892
|
+
return updatedTasks;
|
|
18893
|
+
}
|
|
18894
|
+
/**
|
|
18895
|
+
* Delete a task
|
|
18896
|
+
* @param {string} id - Task ID
|
|
18897
|
+
* @returns {boolean} True if deleted
|
|
18898
|
+
* @throws {Error} If task has dependents
|
|
18899
|
+
*/
|
|
18900
|
+
deleteTask(id) {
|
|
18901
|
+
const task = this.tasks.get(id);
|
|
18902
|
+
if (!task) {
|
|
18903
|
+
throw new Error(`Task "${id}" not found. Available tasks: ${this._getAvailableTaskIds()}`);
|
|
18904
|
+
}
|
|
18905
|
+
const dependents = this._getDependents(id);
|
|
18906
|
+
if (dependents.length > 0) {
|
|
18907
|
+
throw new Error(`Cannot delete "${id}" - other tasks depend on it: ${dependents.join(", ")}`);
|
|
18908
|
+
}
|
|
18909
|
+
this.tasks.delete(id);
|
|
18910
|
+
if (this.debug) {
|
|
18911
|
+
console.log(`[TaskManager] Deleted task: ${id}`);
|
|
18912
|
+
}
|
|
18913
|
+
return true;
|
|
18914
|
+
}
|
|
18915
|
+
/**
|
|
18916
|
+
* Delete multiple tasks in batch
|
|
18917
|
+
* @param {string[]} ids - Task IDs to delete
|
|
18918
|
+
* @returns {string[]} Deleted task IDs
|
|
18919
|
+
*/
|
|
18920
|
+
deleteTasks(ids) {
|
|
18921
|
+
const deletedIds = [];
|
|
18922
|
+
for (const id of ids) {
|
|
18923
|
+
this.deleteTask(id);
|
|
18924
|
+
deletedIds.push(id);
|
|
18925
|
+
}
|
|
18926
|
+
return deletedIds;
|
|
18927
|
+
}
|
|
18928
|
+
/**
|
|
18929
|
+
* Mark a task as completed
|
|
18930
|
+
* @param {string} id - Task ID
|
|
18931
|
+
* @returns {Task} Updated task
|
|
18932
|
+
*/
|
|
18933
|
+
completeTask(id) {
|
|
18934
|
+
return this.updateTask(id, { status: "completed" });
|
|
18935
|
+
}
|
|
18936
|
+
/**
|
|
18937
|
+
* Mark multiple tasks as completed
|
|
18938
|
+
* @param {string[]} ids - Task IDs
|
|
18939
|
+
* @returns {Task[]} Updated tasks
|
|
18940
|
+
*/
|
|
18941
|
+
completeTasks(ids) {
|
|
18942
|
+
return ids.map((id) => this.completeTask(id));
|
|
18943
|
+
}
|
|
18944
|
+
/**
|
|
18945
|
+
* List all tasks
|
|
18946
|
+
* @param {Object} [filter] - Optional filter
|
|
18947
|
+
* @param {'pending'|'in_progress'|'completed'|'cancelled'} [filter.status] - Filter by status
|
|
18948
|
+
* @returns {Task[]} Array of tasks
|
|
18949
|
+
*/
|
|
18950
|
+
listTasks(filter3 = {}) {
|
|
18951
|
+
let tasks = Array.from(this.tasks.values());
|
|
18952
|
+
if (filter3.status) {
|
|
18953
|
+
tasks = tasks.filter((t) => t.status === filter3.status);
|
|
18954
|
+
}
|
|
18955
|
+
return tasks;
|
|
18956
|
+
}
|
|
18957
|
+
/**
|
|
18958
|
+
* Check if there are any incomplete tasks (pending or in_progress)
|
|
18959
|
+
* @returns {boolean} True if there are incomplete tasks
|
|
18960
|
+
*/
|
|
18961
|
+
hasIncompleteTasks() {
|
|
18962
|
+
for (const task of this.tasks.values()) {
|
|
18963
|
+
if (task.status === "pending" || task.status === "in_progress") {
|
|
18964
|
+
return true;
|
|
18965
|
+
}
|
|
18966
|
+
}
|
|
18967
|
+
return false;
|
|
18968
|
+
}
|
|
18969
|
+
/**
|
|
18970
|
+
* Get incomplete tasks (pending or in_progress)
|
|
18971
|
+
* @returns {Task[]} Array of incomplete tasks
|
|
18972
|
+
*/
|
|
18973
|
+
getIncompleteTasks() {
|
|
18974
|
+
return Array.from(this.tasks.values()).filter(
|
|
18975
|
+
(t) => t.status === "pending" || t.status === "in_progress"
|
|
18976
|
+
);
|
|
18977
|
+
}
|
|
18978
|
+
/**
|
|
18979
|
+
* Check if a dependency is resolved (completed or cancelled)
|
|
18980
|
+
* @param {string} depId - Dependency task ID
|
|
18981
|
+
* @returns {boolean} True if dependency is resolved or doesn't exist
|
|
18982
|
+
* @private
|
|
18983
|
+
*/
|
|
18984
|
+
_isDependencyResolved(depId) {
|
|
18985
|
+
const dep = this.tasks.get(depId);
|
|
18986
|
+
if (!dep) return true;
|
|
18987
|
+
return dep.status === "completed" || dep.status === "cancelled";
|
|
18988
|
+
}
|
|
18989
|
+
/**
|
|
18990
|
+
* Get tasks that are ready to start (all dependencies completed)
|
|
18991
|
+
* @returns {Task[]} Array of ready tasks
|
|
18992
|
+
*/
|
|
18993
|
+
getReadyTasks() {
|
|
18994
|
+
return Array.from(this.tasks.values()).filter((task) => {
|
|
18995
|
+
if (task.status !== "pending") return false;
|
|
18996
|
+
return task.dependencies.every((depId) => this._isDependencyResolved(depId));
|
|
18997
|
+
});
|
|
18998
|
+
}
|
|
18999
|
+
/**
|
|
19000
|
+
* Get blocked tasks (have incomplete dependencies)
|
|
19001
|
+
* @returns {Task[]} Array of blocked tasks
|
|
19002
|
+
*/
|
|
19003
|
+
getBlockedTasks() {
|
|
19004
|
+
return Array.from(this.tasks.values()).filter((task) => {
|
|
19005
|
+
if (task.status !== "pending") return false;
|
|
19006
|
+
return task.dependencies.some((depId) => !this._isDependencyResolved(depId));
|
|
19007
|
+
});
|
|
19008
|
+
}
|
|
19009
|
+
/**
|
|
19010
|
+
* Get human-readable task summary for checkpoint messages
|
|
19011
|
+
* @returns {string} Formatted task summary
|
|
19012
|
+
*/
|
|
19013
|
+
getTaskSummary() {
|
|
19014
|
+
const tasks = this.listTasks();
|
|
19015
|
+
if (tasks.length === 0) {
|
|
19016
|
+
return "No tasks created.";
|
|
19017
|
+
}
|
|
19018
|
+
const lines = ["Tasks:"];
|
|
19019
|
+
for (const task of tasks) {
|
|
19020
|
+
let line = `- [${task.status}] ${task.id}: ${task.title}`;
|
|
19021
|
+
if (task.status === "pending" && task.dependencies.length > 0) {
|
|
19022
|
+
const blockers = task.dependencies.filter((depId) => !this._isDependencyResolved(depId));
|
|
19023
|
+
if (blockers.length > 0) {
|
|
19024
|
+
line += ` (blocked by: ${blockers.join(", ")})`;
|
|
19025
|
+
}
|
|
19026
|
+
}
|
|
19027
|
+
lines.push(line);
|
|
19028
|
+
}
|
|
19029
|
+
return lines.join("\n");
|
|
19030
|
+
}
|
|
19031
|
+
/**
|
|
19032
|
+
* Escape XML special characters to prevent injection
|
|
19033
|
+
* @param {string} str - String to escape
|
|
19034
|
+
* @returns {string} Escaped string
|
|
19035
|
+
* @private
|
|
19036
|
+
*/
|
|
19037
|
+
_escapeXml(str) {
|
|
19038
|
+
if (typeof str !== "string") return String(str);
|
|
19039
|
+
return str.replace(/[<>&'"]/g, (c) => ({
|
|
19040
|
+
"<": "<",
|
|
19041
|
+
">": ">",
|
|
19042
|
+
"&": "&",
|
|
19043
|
+
"'": "'",
|
|
19044
|
+
'"': """
|
|
19045
|
+
})[c]);
|
|
19046
|
+
}
|
|
19047
|
+
/**
|
|
19048
|
+
* Format tasks for inclusion in AI prompts
|
|
19049
|
+
* @returns {string} XML-formatted task list
|
|
19050
|
+
*/
|
|
19051
|
+
formatTasksForPrompt() {
|
|
19052
|
+
const tasks = this.listTasks();
|
|
19053
|
+
if (tasks.length === 0) {
|
|
19054
|
+
return "<task_status>No tasks created.</task_status>";
|
|
19055
|
+
}
|
|
19056
|
+
const taskLines = tasks.map((task) => {
|
|
19057
|
+
const blockers = task.dependencies.filter((depId) => !this._isDependencyResolved(depId));
|
|
19058
|
+
let line = ` <task id="${this._escapeXml(task.id)}" status="${this._escapeXml(task.status)}"`;
|
|
19059
|
+
if (task.priority) line += ` priority="${this._escapeXml(task.priority)}"`;
|
|
19060
|
+
if (blockers.length > 0) line += ` blocked_by="${this._escapeXml(blockers.join(","))}"`;
|
|
19061
|
+
line += `>${this._escapeXml(task.title)}</task>`;
|
|
19062
|
+
return line;
|
|
19063
|
+
});
|
|
19064
|
+
return `<task_status>
|
|
19065
|
+
${taskLines.join("\n")}
|
|
19066
|
+
</task_status>`;
|
|
19067
|
+
}
|
|
19068
|
+
/**
|
|
19069
|
+
* Clear all tasks
|
|
19070
|
+
*/
|
|
19071
|
+
clear() {
|
|
19072
|
+
this.tasks.clear();
|
|
19073
|
+
this.taskCounter = 0;
|
|
19074
|
+
if (this.debug) {
|
|
19075
|
+
console.log("[TaskManager] Cleared all tasks");
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
/**
|
|
19079
|
+
* Export tasks for persistence
|
|
19080
|
+
* @returns {Object} Serializable task data
|
|
19081
|
+
*/
|
|
19082
|
+
export() {
|
|
19083
|
+
return {
|
|
19084
|
+
tasks: Array.from(this.tasks.entries()),
|
|
19085
|
+
taskCounter: this.taskCounter
|
|
19086
|
+
};
|
|
19087
|
+
}
|
|
19088
|
+
/**
|
|
19089
|
+
* Import tasks from exported data
|
|
19090
|
+
* @param {Object} data - Exported task data
|
|
19091
|
+
* @throws {Error} If data is invalid or malformed
|
|
19092
|
+
*/
|
|
19093
|
+
import(data) {
|
|
19094
|
+
if (!data || typeof data !== "object") {
|
|
19095
|
+
throw new Error("Invalid import data: must be an object");
|
|
19096
|
+
}
|
|
19097
|
+
if (Object.prototype.hasOwnProperty.call(data, "__proto__") || Object.prototype.hasOwnProperty.call(data, "constructor")) {
|
|
19098
|
+
throw new Error("Invalid import data: prototype pollution attempt detected");
|
|
19099
|
+
}
|
|
19100
|
+
if (!Array.isArray(data.tasks)) {
|
|
19101
|
+
throw new Error("Invalid import data: tasks must be an array");
|
|
19102
|
+
}
|
|
19103
|
+
if (typeof data.taskCounter !== "number" || !Number.isInteger(data.taskCounter) || data.taskCounter < 0) {
|
|
19104
|
+
throw new Error("Invalid import data: taskCounter must be a non-negative integer");
|
|
19105
|
+
}
|
|
19106
|
+
for (const entry of data.tasks) {
|
|
19107
|
+
if (!Array.isArray(entry) || entry.length !== 2) {
|
|
19108
|
+
throw new Error("Invalid import data: each task entry must be a [id, task] tuple");
|
|
19109
|
+
}
|
|
19110
|
+
const [id, task] = entry;
|
|
19111
|
+
if (typeof id !== "string") {
|
|
19112
|
+
throw new Error("Invalid import data: task id must be a string");
|
|
19113
|
+
}
|
|
19114
|
+
if (!task || typeof task !== "object") {
|
|
19115
|
+
throw new Error("Invalid import data: task must be an object");
|
|
19116
|
+
}
|
|
19117
|
+
if (Object.prototype.hasOwnProperty.call(task, "__proto__") || Object.prototype.hasOwnProperty.call(task, "constructor")) {
|
|
19118
|
+
throw new Error("Invalid import data: prototype pollution attempt detected in task");
|
|
19119
|
+
}
|
|
19120
|
+
}
|
|
19121
|
+
this.tasks = new Map(data.tasks);
|
|
19122
|
+
this.taskCounter = data.taskCounter;
|
|
19123
|
+
}
|
|
19124
|
+
/**
|
|
19125
|
+
* Get list of available task IDs for error messages
|
|
19126
|
+
* @returns {string} Comma-separated list of task IDs
|
|
19127
|
+
* @private
|
|
19128
|
+
*/
|
|
19129
|
+
_getAvailableTaskIds() {
|
|
19130
|
+
const ids = Array.from(this.tasks.keys());
|
|
19131
|
+
return ids.length > 0 ? ids.join(", ") : "(none)";
|
|
19132
|
+
}
|
|
19133
|
+
/**
|
|
19134
|
+
* Get tasks that depend on a given task
|
|
19135
|
+
* @param {string} taskId - Task ID
|
|
19136
|
+
* @returns {string[]} Array of dependent task IDs
|
|
19137
|
+
* @private
|
|
19138
|
+
*/
|
|
19139
|
+
_getDependents(taskId) {
|
|
19140
|
+
const dependents = [];
|
|
19141
|
+
for (const [id, task] of this.tasks) {
|
|
19142
|
+
if (task.dependencies.includes(taskId)) {
|
|
19143
|
+
dependents.push(id);
|
|
19144
|
+
}
|
|
19145
|
+
}
|
|
19146
|
+
return dependents;
|
|
19147
|
+
}
|
|
19148
|
+
/**
|
|
19149
|
+
* Validate that adding dependencies won't create a cycle
|
|
19150
|
+
* Uses DFS to detect cycles
|
|
19151
|
+
* @param {string} taskId - Task being updated
|
|
19152
|
+
* @param {string[]} newDependencies - New dependencies to add
|
|
19153
|
+
* @returns {boolean} True if no cycle would be created
|
|
19154
|
+
* @private
|
|
19155
|
+
*/
|
|
19156
|
+
_validateNoCycle(taskId, newDependencies) {
|
|
19157
|
+
const graph = /* @__PURE__ */ new Map();
|
|
19158
|
+
for (const [id, task] of this.tasks) {
|
|
19159
|
+
graph.set(id, [...task.dependencies]);
|
|
19160
|
+
}
|
|
19161
|
+
graph.set(taskId, newDependencies);
|
|
19162
|
+
const visited = /* @__PURE__ */ new Set();
|
|
19163
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
19164
|
+
const hasCycle = (nodeId) => {
|
|
19165
|
+
if (recursionStack.has(nodeId)) {
|
|
19166
|
+
return true;
|
|
19167
|
+
}
|
|
19168
|
+
if (visited.has(nodeId)) {
|
|
19169
|
+
return false;
|
|
19170
|
+
}
|
|
19171
|
+
visited.add(nodeId);
|
|
19172
|
+
recursionStack.add(nodeId);
|
|
19173
|
+
const deps = graph.get(nodeId) || [];
|
|
19174
|
+
for (const depId of deps) {
|
|
19175
|
+
if (hasCycle(depId)) {
|
|
19176
|
+
return true;
|
|
19177
|
+
}
|
|
19178
|
+
}
|
|
19179
|
+
recursionStack.delete(nodeId);
|
|
19180
|
+
return false;
|
|
19181
|
+
};
|
|
19182
|
+
return !hasCycle(taskId);
|
|
19183
|
+
}
|
|
19184
|
+
};
|
|
19185
|
+
}
|
|
19186
|
+
});
|
|
19187
|
+
|
|
19188
|
+
// src/agent/tasks/index.js
|
|
19189
|
+
var init_tasks = __esm({
|
|
19190
|
+
"src/agent/tasks/index.js"() {
|
|
19191
|
+
"use strict";
|
|
19192
|
+
init_TaskManager();
|
|
19193
|
+
init_taskTool();
|
|
19194
|
+
}
|
|
19195
|
+
});
|
|
19196
|
+
|
|
17935
19197
|
// src/index.js
|
|
17936
19198
|
import dotenv from "dotenv";
|
|
17937
19199
|
var init_index = __esm({
|
|
@@ -17956,6 +19218,7 @@ var init_index = __esm({
|
|
|
17956
19218
|
init_probeTool();
|
|
17957
19219
|
init_storage();
|
|
17958
19220
|
init_hooks();
|
|
19221
|
+
init_tasks();
|
|
17959
19222
|
dotenv.config();
|
|
17960
19223
|
}
|
|
17961
19224
|
});
|
|
@@ -18115,6 +19378,7 @@ var init_tools2 = __esm({
|
|
|
18115
19378
|
"use strict";
|
|
18116
19379
|
init_index();
|
|
18117
19380
|
init_xmlParsingUtils();
|
|
19381
|
+
init_tasks();
|
|
18118
19382
|
implementToolDefinition = `
|
|
18119
19383
|
## implement
|
|
18120
19384
|
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
@@ -30449,6 +31713,8 @@ var init_semantics = __esm({
|
|
|
30449
31713
|
constructor(ctx, knownIds, knownEdgeIds) {
|
|
30450
31714
|
super();
|
|
30451
31715
|
this.edgeCount = 0;
|
|
31716
|
+
this.subgraphStack = [];
|
|
31717
|
+
this.reportedSubgraphIdCollision = /* @__PURE__ */ new Set();
|
|
30452
31718
|
this.validateVisitor();
|
|
30453
31719
|
this.ctx = ctx;
|
|
30454
31720
|
this.knownIds = knownIds;
|
|
@@ -30641,8 +31907,13 @@ var init_semantics = __esm({
|
|
|
30641
31907
|
}
|
|
30642
31908
|
}
|
|
30643
31909
|
subgraph(ctx) {
|
|
31910
|
+
const idTok = ctx.subgraphIdOrFirstWord && ctx.subgraphIdOrFirstWord[0];
|
|
31911
|
+
if (idTok)
|
|
31912
|
+
this.subgraphStack.push({ id: String(idTok.image) });
|
|
30644
31913
|
if (ctx.subgraphStatement)
|
|
30645
31914
|
ctx.subgraphStatement.forEach((s) => this.visit(s));
|
|
31915
|
+
if (idTok)
|
|
31916
|
+
this.subgraphStack.pop();
|
|
30646
31917
|
}
|
|
30647
31918
|
subgraphStatement(ctx) {
|
|
30648
31919
|
for (const k of Object.keys(ctx)) {
|
|
@@ -30705,6 +31976,27 @@ var init_semantics = __esm({
|
|
|
30705
31976
|
}
|
|
30706
31977
|
if (ctx.nodeShape)
|
|
30707
31978
|
ctx.nodeShape.forEach((n) => this.visit(n));
|
|
31979
|
+
const idTok = ctx.nodeId && ctx.nodeId[0];
|
|
31980
|
+
const idNumTok = ctx.nodeIdNum && ctx.nodeIdNum[0];
|
|
31981
|
+
const idToken = idTok || idNumTok;
|
|
31982
|
+
if (idToken && this.subgraphStack.length > 0) {
|
|
31983
|
+
const id = String(idToken.image);
|
|
31984
|
+
const hasCollision = this.subgraphStack.some((sg) => sg.id === id);
|
|
31985
|
+
if (hasCollision) {
|
|
31986
|
+
const key = `${id}:${idToken.startLine ?? 1}:${idToken.startColumn ?? 1}`;
|
|
31987
|
+
if (!this.reportedSubgraphIdCollision.has(key)) {
|
|
31988
|
+
this.reportedSubgraphIdCollision.add(key);
|
|
31989
|
+
this.ctx.errors.push({
|
|
31990
|
+
line: idToken.startLine ?? 1,
|
|
31991
|
+
column: idToken.startColumn ?? 1,
|
|
31992
|
+
severity: "error",
|
|
31993
|
+
code: "FL-SUBGRAPH-ID-COLLISION",
|
|
31994
|
+
message: `Node id '${id}' conflicts with an enclosing subgraph id and creates a cycle in Mermaid.`,
|
|
31995
|
+
hint: "Rename the subgraph id or the node id, or use a quoted subgraph title with no explicit id."
|
|
31996
|
+
});
|
|
31997
|
+
}
|
|
31998
|
+
}
|
|
31999
|
+
}
|
|
30708
32000
|
if (hasAttr) {
|
|
30709
32001
|
const attr = ctx.attrObject?.[0];
|
|
30710
32002
|
const pairs = attr?.children?.attrPair || [];
|
|
@@ -34745,6 +36037,7 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
34745
36037
|
const patchedLines = /* @__PURE__ */ new Set();
|
|
34746
36038
|
const seen = /* @__PURE__ */ new Set();
|
|
34747
36039
|
const piQuoteClosedLines = /* @__PURE__ */ new Set();
|
|
36040
|
+
const subgraphCollisionRename = /* @__PURE__ */ new Map();
|
|
34748
36041
|
function sanitizeAllQuotedSegmentsInShapes(lineText, lineNo) {
|
|
34749
36042
|
const shapes = [
|
|
34750
36043
|
{ open: "[[", close: "]]" },
|
|
@@ -35464,6 +36757,28 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
35464
36757
|
}
|
|
35465
36758
|
continue;
|
|
35466
36759
|
}
|
|
36760
|
+
if (is("FL-SUBGRAPH-ID-COLLISION", e)) {
|
|
36761
|
+
if (level === "all") {
|
|
36762
|
+
const lineText = lineTextAt(text, e.line);
|
|
36763
|
+
if (lineText.trimStart().startsWith("subgraph"))
|
|
36764
|
+
continue;
|
|
36765
|
+
const caret0 = Math.max(0, e.column - 1);
|
|
36766
|
+
const slice = lineText.slice(caret0);
|
|
36767
|
+
const m = slice.match(/^([A-Za-z_][A-Za-z0-9_]*(?:-[A-Za-z0-9_]+)*)/);
|
|
36768
|
+
if (m) {
|
|
36769
|
+
const id = m[1];
|
|
36770
|
+
let newId = subgraphCollisionRename.get(id);
|
|
36771
|
+
if (!newId) {
|
|
36772
|
+
newId = id.endsWith("_node") ? `${id}2` : `${id}_node`;
|
|
36773
|
+
subgraphCollisionRename.set(id, newId);
|
|
36774
|
+
}
|
|
36775
|
+
if (newId !== id) {
|
|
36776
|
+
edits.push(replaceRange(text, { line: e.line, column: caret0 + 1 }, id.length, newId));
|
|
36777
|
+
}
|
|
36778
|
+
}
|
|
36779
|
+
}
|
|
36780
|
+
continue;
|
|
36781
|
+
}
|
|
35467
36782
|
if (is("FL-LABEL-QUOTE-IN-UNQUOTED", e)) {
|
|
35468
36783
|
if (level === "safe" || level === "all") {
|
|
35469
36784
|
const lineText = lineTextAt(text, e.line);
|
|
@@ -45125,7 +46440,7 @@ var init_styles = __esm({
|
|
|
45125
46440
|
});
|
|
45126
46441
|
|
|
45127
46442
|
// node_modules/@probelabs/maid/out/renderer/utils.js
|
|
45128
|
-
function
|
|
46443
|
+
function escapeXml2(text) {
|
|
45129
46444
|
return String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
45130
46445
|
}
|
|
45131
46446
|
function measureText(text, fontSize = 12) {
|
|
@@ -45180,7 +46495,7 @@ function blockOverlay(x, y, width, height, title, branchYs = [], titleYOffset =
|
|
|
45180
46495
|
const parts = [];
|
|
45181
46496
|
parts.push(`<g class="cluster-overlay" transform="translate(${x},${y})">`);
|
|
45182
46497
|
parts.push(`<rect class="cluster-border" x="0" y="0" width="${width}" height="${height}" rx="${radius}"/>`);
|
|
45183
|
-
const titleText = title ?
|
|
46498
|
+
const titleText = title ? escapeXml2(title) : "";
|
|
45184
46499
|
if (titleText) {
|
|
45185
46500
|
const titleW = Math.max(24, measureText(titleText, 12) + 10);
|
|
45186
46501
|
const yBg = -2 + titleYOffset;
|
|
@@ -45199,7 +46514,7 @@ function blockOverlay(x, y, width, height, title, branchYs = [], titleYOffset =
|
|
|
45199
46514
|
const yRel = br.y - y;
|
|
45200
46515
|
parts.push(`<line x1="0" y1="${yRel}" x2="${width}" y2="${yRel}" class="cluster-border" />`);
|
|
45201
46516
|
if (br.title) {
|
|
45202
|
-
const text =
|
|
46517
|
+
const text = escapeXml2(br.title);
|
|
45203
46518
|
const bw = Math.max(24, measureText(text, 12) + 10);
|
|
45204
46519
|
const xBg = 6;
|
|
45205
46520
|
parts.push(`<rect class="cluster-title-bg" x="${xBg}" y="${yRel - 10}" width="${bw}" height="18" rx="3"/>`);
|
|
@@ -46368,7 +47683,7 @@ function renderPie(model, opts = {}) {
|
|
|
46368
47683
|
</style>`;
|
|
46369
47684
|
if (model.title) {
|
|
46370
47685
|
svg += `
|
|
46371
|
-
<text class="pie-title" x="${cx}" y="${pad + 8}" text-anchor="middle">${
|
|
47686
|
+
<text class="pie-title" x="${cx}" y="${pad + 8}" text-anchor="middle">${escapeXml2(model.title)}</text>`;
|
|
46372
47687
|
}
|
|
46373
47688
|
svg += `
|
|
46374
47689
|
<g class="pie" aria-label="pie">`;
|
|
@@ -46392,7 +47707,7 @@ function renderPie(model, opts = {}) {
|
|
|
46392
47707
|
const mid = (start + end) / 2;
|
|
46393
47708
|
const cos = Math.cos(mid);
|
|
46394
47709
|
const sin = Math.sin(mid);
|
|
46395
|
-
const percentLabel =
|
|
47710
|
+
const percentLabel = escapeXml2(formatPercent(s.value, total));
|
|
46396
47711
|
if (angle < minOutsideAngle) {
|
|
46397
47712
|
const r1 = radius * 0.9;
|
|
46398
47713
|
const r2 = radius * 1.06;
|
|
@@ -46434,7 +47749,7 @@ function renderPie(model, opts = {}) {
|
|
|
46434
47749
|
slices.forEach((s, i) => {
|
|
46435
47750
|
const y = legendY + i * LEG_VSPACE;
|
|
46436
47751
|
const fill = s.color || palette(i);
|
|
46437
|
-
const text =
|
|
47752
|
+
const text = escapeXml2(`${s.label}${model.showData ? ` ${formatNumber(Number(s.value))}` : ""}`);
|
|
46438
47753
|
svg += `
|
|
46439
47754
|
<rect x="${legendX}" y="${y - LEG_SW + 6}" width="${LEG_SW}" height="${LEG_SW}" fill="${fill}" stroke="${fill}" stroke-width="1" />`;
|
|
46440
47755
|
svg += `
|
|
@@ -47010,11 +48325,11 @@ function renderSequence(model, opts = {}) {
|
|
|
47010
48325
|
const accTitle = model.accTitle || model.title || void 0;
|
|
47011
48326
|
const accDesc = model.accDescr || void 0;
|
|
47012
48327
|
if (accTitle)
|
|
47013
|
-
svgParts.push(` <title>${
|
|
48328
|
+
svgParts.push(` <title>${escapeXml2(accTitle)}</title>`);
|
|
47014
48329
|
if (accDesc)
|
|
47015
|
-
svgParts.push(` <desc>${
|
|
48330
|
+
svgParts.push(` <desc>${escapeXml2(accDesc)}</desc>`);
|
|
47016
48331
|
if (model.title) {
|
|
47017
|
-
const t =
|
|
48332
|
+
const t = escapeXml2(model.title);
|
|
47018
48333
|
const tW = Math.max(20, measureText(model.title, 16));
|
|
47019
48334
|
const xMid = width / 2;
|
|
47020
48335
|
svgParts.push(` <text class="node-label" x="${xMid}" y="0" text-anchor="middle" font-size="16">${t}</text>`);
|
|
@@ -47055,7 +48370,7 @@ function renderSequence(model, opts = {}) {
|
|
|
47055
48370
|
function drawParticipant(out, p) {
|
|
47056
48371
|
out.push(` <g class="actor" transform="translate(${p.x},${p.y})">`);
|
|
47057
48372
|
out.push(` <rect class="node-shape" width="${p.width}" height="${p.height}" rx="0"/>`);
|
|
47058
|
-
out.push(` <text class="node-label" x="${p.width / 2}" y="${p.height / 2}" text-anchor="middle" dominant-baseline="middle">${
|
|
48373
|
+
out.push(` <text class="node-label" x="${p.width / 2}" y="${p.height / 2}" text-anchor="middle" dominant-baseline="middle">${escapeXml2(p.display)}</text>`);
|
|
47059
48374
|
out.push(" </g>");
|
|
47060
48375
|
}
|
|
47061
48376
|
function drawParticipantBottom(out, p, layout) {
|
|
@@ -47063,7 +48378,7 @@ function drawParticipantBottom(out, p, layout) {
|
|
|
47063
48378
|
const y = lifeline ? lifeline.y2 : layout.height - 28;
|
|
47064
48379
|
out.push(` <g class="actor" transform="translate(${p.x},${y})">`);
|
|
47065
48380
|
out.push(` <rect class="node-shape" width="${p.width}" height="${p.height}" rx="0"/>`);
|
|
47066
|
-
out.push(` <text class="node-label" x="${p.width / 2}" y="${p.height / 2}" text-anchor="middle" dominant-baseline="middle">${
|
|
48381
|
+
out.push(` <text class="node-label" x="${p.width / 2}" y="${p.height / 2}" text-anchor="middle" dominant-baseline="middle">${escapeXml2(p.display)}</text>`);
|
|
47067
48382
|
out.push(" </g>");
|
|
47068
48383
|
}
|
|
47069
48384
|
function drawMessage(out, m) {
|
|
@@ -47101,12 +48416,12 @@ function drawMessageLabel(out, m, label, _counter) {
|
|
|
47101
48416
|
const x = xMid - w / 2;
|
|
47102
48417
|
const y = m.y - 14 - h / 2;
|
|
47103
48418
|
out.push(` <rect class="msg-label-bg" x="${x}" y="${y}" width="${w}" height="${h}" rx="3"/>`);
|
|
47104
|
-
out.push(` <text class="msg-label" x="${xMid}" y="${y + h / 2}" text-anchor="middle">${
|
|
48419
|
+
out.push(` <text class="msg-label" x="${xMid}" y="${y + h / 2}" text-anchor="middle">${escapeXml2(label)}</text>`);
|
|
47105
48420
|
}
|
|
47106
48421
|
function drawNote(out, n) {
|
|
47107
48422
|
out.push(` <g class="note" transform="translate(${n.x},${n.y})">`);
|
|
47108
48423
|
out.push(` <rect width="${n.width}" height="${n.height}" rx="0"/>`);
|
|
47109
|
-
out.push(` <text class="note-text" x="${n.width / 2}" y="${n.height / 2 + 4}" text-anchor="middle">${
|
|
48424
|
+
out.push(` <text class="note-text" x="${n.width / 2}" y="${n.height / 2 + 4}" text-anchor="middle">${escapeXml2(n.text)}</text>`);
|
|
47110
48425
|
out.push(" </g>");
|
|
47111
48426
|
}
|
|
47112
48427
|
function applySequenceTheme(svg, theme) {
|
|
@@ -47872,13 +49187,13 @@ function renderClass(model, opts = {}) {
|
|
|
47872
49187
|
let cy = y + padY + lineH;
|
|
47873
49188
|
parts.push(` <g transform="translate(${x},${y})">`);
|
|
47874
49189
|
parts.push(` <rect class="node-shape" width="${w}" height="${h}" rx="0"/>`);
|
|
47875
|
-
parts.push(` <text class="node-label class-title" x="${w / 2}" y="${padY + 12}" text-anchor="middle" dominant-baseline="middle">${
|
|
49190
|
+
parts.push(` <text class="node-label class-title" x="${w / 2}" y="${padY + 12}" text-anchor="middle" dominant-baseline="middle">${escapeXml2(title)}</text>`);
|
|
47876
49191
|
let yCursor = padY + 18;
|
|
47877
49192
|
if (attrs.length) {
|
|
47878
49193
|
parts.push(` <line class="class-divider" x1="0" y1="${yCursor}" x2="${w}" y2="${yCursor}"/>`);
|
|
47879
49194
|
yCursor += 8;
|
|
47880
49195
|
for (const a of attrs) {
|
|
47881
|
-
parts.push(` <text class="node-label class-member" x="${padX}" y="${yCursor}" dominant-baseline="hanging">${
|
|
49196
|
+
parts.push(` <text class="node-label class-member" x="${padX}" y="${yCursor}" dominant-baseline="hanging">${escapeXml2(a)}</text>`);
|
|
47882
49197
|
yCursor += lineH;
|
|
47883
49198
|
}
|
|
47884
49199
|
}
|
|
@@ -47886,7 +49201,7 @@ function renderClass(model, opts = {}) {
|
|
|
47886
49201
|
parts.push(` <line class="class-divider" x1="0" y1="${yCursor}" x2="${w}" y2="${yCursor}"/>`);
|
|
47887
49202
|
yCursor += 8;
|
|
47888
49203
|
for (const m of methods) {
|
|
47889
|
-
parts.push(` <text class="node-label class-member" x="${padX}" y="${yCursor}" dominant-baseline="hanging">${
|
|
49204
|
+
parts.push(` <text class="node-label class-member" x="${padX}" y="${yCursor}" dominant-baseline="hanging">${escapeXml2(m)}</text>`);
|
|
47890
49205
|
yCursor += lineH;
|
|
47891
49206
|
}
|
|
47892
49207
|
}
|
|
@@ -47912,7 +49227,7 @@ function renderClass(model, opts = {}) {
|
|
|
47912
49227
|
const x = pt.x + ux * away + nx * perp;
|
|
47913
49228
|
const y = pt.y + uy * away + ny * perp;
|
|
47914
49229
|
const anchor = side === "start" ? "end" : "start";
|
|
47915
|
-
parts.push(`<text class="edge-label-text" x="${x}" y="${y}" text-anchor="${anchor}">${
|
|
49230
|
+
parts.push(`<text class="edge-label-text" x="${x}" y="${y}" text-anchor="${anchor}">${escapeXml2(text)}</text>`);
|
|
47916
49231
|
};
|
|
47917
49232
|
if (rel.leftCard && pts.length >= 2)
|
|
47918
49233
|
placeEndpoint(pts[0], pts[1], rel.leftCard, "start");
|
|
@@ -47941,7 +49256,7 @@ function renderClass(model, opts = {}) {
|
|
|
47941
49256
|
lines.push(cur);
|
|
47942
49257
|
const dy = 14;
|
|
47943
49258
|
let y0 = mid.y - (lines.length - 1) * dy / 2 - 10;
|
|
47944
|
-
parts.push(`<text class="edge-label-text" x="${mid.x}" y="${y0}" text-anchor="middle">` + lines.map((ln, i2) => `<tspan x="${mid.x}" dy="${i2 === 0 ? 0 : dy}">${
|
|
49259
|
+
parts.push(`<text class="edge-label-text" x="${mid.x}" y="${y0}" text-anchor="middle">` + lines.map((ln, i2) => `<tspan x="${mid.x}" dy="${i2 === 0 ? 0 : dy}">${escapeXml2(ln)}</tspan>`).join("") + `</text>`);
|
|
47945
49260
|
}
|
|
47946
49261
|
const pStart = [pts[0], pts[1] ?? pts[0]];
|
|
47947
49262
|
const pEnd = [pts[pts.length - 2] ?? pts[pts.length - 1], pts[pts.length - 1]];
|
|
@@ -48007,7 +49322,7 @@ function renderClass(model, opts = {}) {
|
|
|
48007
49322
|
}
|
|
48008
49323
|
parts.push(` <g class="note" transform="translate(${nx},${ny})">`);
|
|
48009
49324
|
parts.push(` <rect width="${noteW}" height="${noteH}" rx="0"/>`);
|
|
48010
|
-
parts.push(` <text class="note-text" x="${noteW / 2}" y="${noteH / 2 + 4}" text-anchor="middle">${
|
|
49325
|
+
parts.push(` <text class="note-text" x="${noteW / 2}" y="${noteH / 2 + 4}" text-anchor="middle">${escapeXml2(note.text)}</text>`);
|
|
48011
49326
|
parts.push(" </g>");
|
|
48012
49327
|
const ax = anchor.x + anchor.width;
|
|
48013
49328
|
const ay = anchor.y + 16;
|
|
@@ -64944,7 +66259,7 @@ var init_registry = __esm({
|
|
|
64944
66259
|
});
|
|
64945
66260
|
|
|
64946
66261
|
// src/agent/skills/formatting.js
|
|
64947
|
-
function
|
|
66262
|
+
function escapeXml3(value) {
|
|
64948
66263
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
64949
66264
|
}
|
|
64950
66265
|
function formatAvailableSkillsXml(skills) {
|
|
@@ -64952,8 +66267,8 @@ function formatAvailableSkillsXml(skills) {
|
|
|
64952
66267
|
const lines = ["<available_skills>"];
|
|
64953
66268
|
for (const skill of skills) {
|
|
64954
66269
|
lines.push(" <skill>");
|
|
64955
|
-
lines.push(` <name>${
|
|
64956
|
-
lines.push(` <description>${
|
|
66270
|
+
lines.push(` <name>${escapeXml3(skill.name)}</name>`);
|
|
66271
|
+
lines.push(` <description>${escapeXml3(skill.description || "")}</description>`);
|
|
64957
66272
|
lines.push(" </skill>");
|
|
64958
66273
|
}
|
|
64959
66274
|
lines.push("</available_skills>");
|
|
@@ -67306,6 +68621,8 @@ var init_ProbeAgent = __esm({
|
|
|
67306
68621
|
init_RetryManager();
|
|
67307
68622
|
init_FallbackManager();
|
|
67308
68623
|
init_contextCompactor();
|
|
68624
|
+
init_error_types();
|
|
68625
|
+
init_tasks();
|
|
67309
68626
|
dotenv2.config();
|
|
67310
68627
|
MAX_TOOL_ITERATIONS = (() => {
|
|
67311
68628
|
const val = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
|
|
@@ -67346,6 +68663,7 @@ var init_ProbeAgent = __esm({
|
|
|
67346
68663
|
* @param {string} [options.mcpConfigPath] - Path to MCP configuration file
|
|
67347
68664
|
* @param {Object} [options.mcpConfig] - MCP configuration object (overrides mcpConfigPath)
|
|
67348
68665
|
* @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
|
|
68666
|
+
* @param {boolean} [options.enableTasks=false] - Enable task management system for tracking progress
|
|
67349
68667
|
* @param {Object} [options.storageAdapter] - Custom storage adapter for history management
|
|
67350
68668
|
* @param {Object} [options.hooks] - Hook callbacks for events (e.g., {'tool:start': callback})
|
|
67351
68669
|
* @param {Array<string>|null} [options.allowedTools] - List of allowed tool names. Use ['*'] for all tools (default), [] or null for no tools (raw AI mode), or specific tool names like ['search', 'query', 'extract']. Supports exclusion with '!' prefix (e.g., ['*', '!bash'])
|
|
@@ -67441,6 +68759,8 @@ var init_ProbeAgent = __esm({
|
|
|
67441
68759
|
this.mcpServers = options.mcpServers || null;
|
|
67442
68760
|
this.mcpBridge = null;
|
|
67443
68761
|
this._mcpInitialized = false;
|
|
68762
|
+
this.enableTasks = !!options.enableTasks;
|
|
68763
|
+
this.taskManager = null;
|
|
67444
68764
|
this.retryConfig = options.retry || {};
|
|
67445
68765
|
this.retryManager = null;
|
|
67446
68766
|
this.fallbackConfig = options.fallback || null;
|
|
@@ -68816,6 +70136,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
68816
70136
|
}
|
|
68817
70137
|
if (this.enableBash && isToolAllowed("bash")) {
|
|
68818
70138
|
toolDefinitions += `${bashToolDefinition}
|
|
70139
|
+
`;
|
|
70140
|
+
}
|
|
70141
|
+
if (this.enableTasks && isToolAllowed("task")) {
|
|
70142
|
+
toolDefinitions += `${taskToolDefinition}
|
|
68819
70143
|
`;
|
|
68820
70144
|
}
|
|
68821
70145
|
if (isToolAllowed("attempt_completion")) {
|
|
@@ -68826,6 +70150,77 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
68826
70150
|
toolDefinitions += `${delegateToolDefinition}
|
|
68827
70151
|
`;
|
|
68828
70152
|
}
|
|
70153
|
+
let toolExamples = "";
|
|
70154
|
+
if (isToolAllowed("search")) {
|
|
70155
|
+
toolExamples += `
|
|
70156
|
+
<search>
|
|
70157
|
+
<query>error handling</query>
|
|
70158
|
+
<path>src/search</path>
|
|
70159
|
+
</search>
|
|
70160
|
+
`;
|
|
70161
|
+
}
|
|
70162
|
+
if (isToolAllowed("extract")) {
|
|
70163
|
+
toolExamples += `
|
|
70164
|
+
<extract>
|
|
70165
|
+
<targets>src/config.js:15-25</targets>
|
|
70166
|
+
</extract>
|
|
70167
|
+
`;
|
|
70168
|
+
}
|
|
70169
|
+
if (isToolAllowed("attempt_completion")) {
|
|
70170
|
+
toolExamples += `
|
|
70171
|
+
<attempt_completion>
|
|
70172
|
+
The configuration is loaded from src/config.js lines 15-25 which contains the database settings.
|
|
70173
|
+
</attempt_completion>
|
|
70174
|
+
`;
|
|
70175
|
+
}
|
|
70176
|
+
let availableToolsList = "";
|
|
70177
|
+
if (isToolAllowed("search")) {
|
|
70178
|
+
availableToolsList += `- search: Search code using keyword queries${this.searchDelegate ? " (returns extracted code blocks via a dedicated subagent)" : ""}.
|
|
70179
|
+
`;
|
|
70180
|
+
}
|
|
70181
|
+
if (isToolAllowed("query")) {
|
|
70182
|
+
availableToolsList += "- query: Search code using structural AST patterns.\n";
|
|
70183
|
+
}
|
|
70184
|
+
if (isToolAllowed("extract")) {
|
|
70185
|
+
availableToolsList += "- extract: Extract specific code blocks or lines from files.\n";
|
|
70186
|
+
}
|
|
70187
|
+
if (isToolAllowed("listFiles")) {
|
|
70188
|
+
availableToolsList += "- listFiles: List files and directories in a specified location.\n";
|
|
70189
|
+
}
|
|
70190
|
+
if (isToolAllowed("searchFiles")) {
|
|
70191
|
+
availableToolsList += "- searchFiles: Find files matching a glob pattern with recursive search capability.\n";
|
|
70192
|
+
}
|
|
70193
|
+
if (this.enableSkills && isToolAllowed("listSkills")) {
|
|
70194
|
+
availableToolsList += "- listSkills: List available agent skills discovered in the repository.\n";
|
|
70195
|
+
}
|
|
70196
|
+
if (this.enableSkills && isToolAllowed("useSkill")) {
|
|
70197
|
+
availableToolsList += "- useSkill: Load and activate a specific skill's instructions.\n";
|
|
70198
|
+
}
|
|
70199
|
+
if (isToolAllowed("readImage")) {
|
|
70200
|
+
availableToolsList += "- readImage: Read and load an image file for AI analysis.\n";
|
|
70201
|
+
}
|
|
70202
|
+
if (this.allowEdit && isToolAllowed("implement")) {
|
|
70203
|
+
availableToolsList += "- implement: Implement a feature or fix a bug using aider.\n";
|
|
70204
|
+
}
|
|
70205
|
+
if (this.allowEdit && isToolAllowed("edit")) {
|
|
70206
|
+
availableToolsList += "- edit: Edit files using exact string replacement.\n";
|
|
70207
|
+
}
|
|
70208
|
+
if (this.allowEdit && isToolAllowed("create")) {
|
|
70209
|
+
availableToolsList += "- create: Create new files with specified content.\n";
|
|
70210
|
+
}
|
|
70211
|
+
if (this.enableDelegate && isToolAllowed("delegate")) {
|
|
70212
|
+
availableToolsList += "- delegate: Delegate big distinct tasks to specialized probe subagents.\n";
|
|
70213
|
+
}
|
|
70214
|
+
if (this.enableBash && isToolAllowed("bash")) {
|
|
70215
|
+
availableToolsList += "- bash: Execute bash commands for system operations.\n";
|
|
70216
|
+
}
|
|
70217
|
+
if (this.enableTasks && isToolAllowed("task")) {
|
|
70218
|
+
availableToolsList += "- task: Manage tasks for tracking progress (create, update, complete, delete, list).\n";
|
|
70219
|
+
}
|
|
70220
|
+
if (isToolAllowed("attempt_completion")) {
|
|
70221
|
+
availableToolsList += "- attempt_completion: Finalize the task and provide the result to the user.\n";
|
|
70222
|
+
availableToolsList += "- attempt_complete: Quick completion using previous response (shorthand).\n";
|
|
70223
|
+
}
|
|
68829
70224
|
let xmlToolGuidelines = `
|
|
68830
70225
|
# Tool Use Formatting
|
|
68831
70226
|
|
|
@@ -68840,20 +70235,7 @@ Structure (note the closing tags):
|
|
|
68840
70235
|
...
|
|
68841
70236
|
</tool_name>
|
|
68842
70237
|
|
|
68843
|
-
Examples
|
|
68844
|
-
<search>
|
|
68845
|
-
<query>error handling</query>
|
|
68846
|
-
<path>src/search</path>
|
|
68847
|
-
</search>
|
|
68848
|
-
|
|
68849
|
-
<extract>
|
|
68850
|
-
<targets>src/config.js:15-25</targets>
|
|
68851
|
-
</extract>
|
|
68852
|
-
|
|
68853
|
-
<attempt_completion>
|
|
68854
|
-
The configuration is loaded from src/config.js lines 15-25 which contains the database settings.
|
|
68855
|
-
</attempt_completion>
|
|
68856
|
-
|
|
70238
|
+
Examples:${toolExamples}
|
|
68857
70239
|
# Special Case: Quick Completion
|
|
68858
70240
|
If your previous response was already correct and complete, you may respond with just:
|
|
68859
70241
|
<attempt_complete>
|
|
@@ -68882,16 +70264,7 @@ I need to find code related to error handling in the search module. The most app
|
|
|
68882
70264
|
10. If your previous response was already correct and complete, you may use \`<attempt_complete>\` as a shorthand.
|
|
68883
70265
|
|
|
68884
70266
|
Available Tools:
|
|
68885
|
-
|
|
68886
|
-
- query: Search code using structural AST patterns.
|
|
68887
|
-
- extract: Extract specific code blocks or lines from files.
|
|
68888
|
-
- listFiles: List files and directories in a specified location.
|
|
68889
|
-
- searchFiles: Find files matching a glob pattern with recursive search capability.
|
|
68890
|
-
${this.enableSkills ? "- listSkills: List available agent skills discovered in the repository.\n- useSkill: Load and activate a specific skill's instructions.\n" : ""}- readImage: Read and load an image file for AI analysis.
|
|
68891
|
-
${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n- edit: Edit files using exact string replacement.\n- create: Create new files with specified content.\n" : ""}${this.enableDelegate ? "- delegate: Delegate big distinct tasks to specialized probe subagents.\n" : ""}${this.enableBash ? "- bash: Execute bash commands for system operations.\n" : ""}
|
|
68892
|
-
- attempt_completion: Finalize the task and provide the result to the user.
|
|
68893
|
-
- attempt_complete: Quick completion using previous response (shorthand).
|
|
68894
|
-
`;
|
|
70267
|
+
${availableToolsList}`;
|
|
68895
70268
|
const commonInstructions = `<instructions>
|
|
68896
70269
|
Follow these instructions carefully:
|
|
68897
70270
|
1. Analyze the user's request.
|
|
@@ -68945,6 +70318,11 @@ To use a skill, call the useSkill tool with its name.
|
|
|
68945
70318
|
`;
|
|
68946
70319
|
}
|
|
68947
70320
|
}
|
|
70321
|
+
if (this.enableTasks) {
|
|
70322
|
+
systemMessage += `
|
|
70323
|
+
${taskSystemPrompt}
|
|
70324
|
+
`;
|
|
70325
|
+
}
|
|
68948
70326
|
if (this.mcpBridge && this.mcpBridge.getToolNames().length > 0) {
|
|
68949
70327
|
const allMcpTools = this.mcpBridge.getToolNames();
|
|
68950
70328
|
const allowedMcpTools = this._filterMcpTools(allMcpTools);
|
|
@@ -69030,6 +70408,30 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
69030
70408
|
}
|
|
69031
70409
|
try {
|
|
69032
70410
|
const oldHistoryLength = this.history.length;
|
|
70411
|
+
if (this.enableTasks) {
|
|
70412
|
+
try {
|
|
70413
|
+
this.taskManager = new TaskManager({ debug: this.debug });
|
|
70414
|
+
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
70415
|
+
if (isToolAllowed("task")) {
|
|
70416
|
+
this.toolImplementations.task = createTaskTool({
|
|
70417
|
+
taskManager: this.taskManager,
|
|
70418
|
+
tracer: this.tracer,
|
|
70419
|
+
debug: this.debug
|
|
70420
|
+
});
|
|
70421
|
+
}
|
|
70422
|
+
if (this.tracer && typeof this.tracer.recordTaskEvent === "function") {
|
|
70423
|
+
this.tracer.recordTaskEvent("session_started", {
|
|
70424
|
+
"task.enabled": true
|
|
70425
|
+
});
|
|
70426
|
+
}
|
|
70427
|
+
if (this.debug) {
|
|
70428
|
+
console.log("[DEBUG] Task management initialized for this request");
|
|
70429
|
+
}
|
|
70430
|
+
} catch (taskInitError) {
|
|
70431
|
+
console.error("[ProbeAgent] Failed to initialize task management:", taskInitError.message);
|
|
70432
|
+
this.taskManager = null;
|
|
70433
|
+
}
|
|
70434
|
+
}
|
|
69033
70435
|
await this.hooks.emit(HOOK_TYPES.MESSAGE_USER, {
|
|
69034
70436
|
sessionId: this.sessionId,
|
|
69035
70437
|
message,
|
|
@@ -69037,6 +70439,12 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
69037
70439
|
});
|
|
69038
70440
|
const systemMessage = await this.getSystemMessage();
|
|
69039
70441
|
let userMessage = { role: "user", content: message.trim() };
|
|
70442
|
+
if (this.enableTasks) {
|
|
70443
|
+
userMessage.content = userMessage.content + "\n\n" + taskGuidancePrompt;
|
|
70444
|
+
if (this.debug) {
|
|
70445
|
+
console.log("[DEBUG] Task guidance injected into user message");
|
|
70446
|
+
}
|
|
70447
|
+
}
|
|
69040
70448
|
if (options.schema && !options._schemaFormatted) {
|
|
69041
70449
|
const schemaInstructions = generateSchemaInstructions(options.schema, { debug: this.debug });
|
|
69042
70450
|
userMessage.content = message.trim() + schemaInstructions;
|
|
@@ -69257,9 +70665,12 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
69257
70665
|
return result;
|
|
69258
70666
|
};
|
|
69259
70667
|
if (this.tracer) {
|
|
70668
|
+
const inputPreview = message.length > 1e3 ? message.substring(0, 1e3) + "... [truncated]" : message;
|
|
69260
70669
|
await this.tracer.withSpan("ai.request", executeAIRequest, {
|
|
69261
70670
|
"ai.model": this.model,
|
|
69262
70671
|
"ai.provider": this.clientApiProvider || "auto",
|
|
70672
|
+
"ai.input": inputPreview,
|
|
70673
|
+
"ai.input_length": message.length,
|
|
69263
70674
|
"iteration": currentIteration,
|
|
69264
70675
|
"max_tokens": maxResponseTokens,
|
|
69265
70676
|
"temperature": 0.3,
|
|
@@ -69339,6 +70750,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
69339
70750
|
if (this.enableDelegate && this.allowedTools.isEnabled("delegate")) {
|
|
69340
70751
|
validTools.push("delegate");
|
|
69341
70752
|
}
|
|
70753
|
+
if (this.enableTasks && this.allowedTools.isEnabled("task")) {
|
|
70754
|
+
validTools.push("task");
|
|
70755
|
+
}
|
|
69342
70756
|
}
|
|
69343
70757
|
const nativeTools = validTools;
|
|
69344
70758
|
const parsedTool = this.mcpBridge && !options._disableTools ? parseHybridXmlToolCall(assistantResponseContent, nativeTools, this.mcpBridge) : parseXmlToolCallWithThinking(assistantResponseContent, validTools);
|
|
@@ -69347,6 +70761,32 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
69347
70761
|
if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
|
|
69348
70762
|
if (toolName === "attempt_completion") {
|
|
69349
70763
|
completionAttempted = true;
|
|
70764
|
+
if (this.enableTasks && this.taskManager && this.taskManager.hasIncompleteTasks()) {
|
|
70765
|
+
const taskSummary = this.taskManager.getTaskSummary();
|
|
70766
|
+
const blockedMessage = createTaskCompletionBlockedMessage(taskSummary);
|
|
70767
|
+
const incompleteTasks = this.taskManager.getIncompleteTasks();
|
|
70768
|
+
if (this.tracer && typeof this.tracer.recordTaskEvent === "function") {
|
|
70769
|
+
this.tracer.recordTaskEvent("completion_blocked", {
|
|
70770
|
+
"task.incomplete_count": incompleteTasks.length,
|
|
70771
|
+
"task.incomplete_ids": incompleteTasks.map((t) => t.id).join(", "),
|
|
70772
|
+
"task.iteration": currentIteration
|
|
70773
|
+
});
|
|
70774
|
+
}
|
|
70775
|
+
if (this.debug) {
|
|
70776
|
+
console.log("[DEBUG] Task checkpoint: Blocking completion due to incomplete tasks");
|
|
70777
|
+
console.log("[DEBUG] Incomplete tasks:", taskSummary);
|
|
70778
|
+
}
|
|
70779
|
+
currentMessages.push({
|
|
70780
|
+
role: "assistant",
|
|
70781
|
+
content: assistantResponseContent
|
|
70782
|
+
});
|
|
70783
|
+
currentMessages.push({
|
|
70784
|
+
role: "user",
|
|
70785
|
+
content: blockedMessage
|
|
70786
|
+
});
|
|
70787
|
+
completionAttempted = false;
|
|
70788
|
+
continue;
|
|
70789
|
+
}
|
|
69350
70790
|
if (params.result === "__PREVIOUS_RESPONSE__") {
|
|
69351
70791
|
const lastAssistantMessage = [...currentMessages].reverse().find(
|
|
69352
70792
|
(msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
|
|
@@ -69408,7 +70848,6 @@ ${toolResultContent}
|
|
|
69408
70848
|
</tool_result>` });
|
|
69409
70849
|
} catch (error) {
|
|
69410
70850
|
console.error(`Error executing MCP tool ${toolName}:`, error);
|
|
69411
|
-
const toolResultContent = `Error executing MCP tool ${toolName}: ${error.message}`;
|
|
69412
70851
|
if (this.debug) {
|
|
69413
70852
|
console.error(`[DEBUG] ========================================`);
|
|
69414
70853
|
console.error(`[DEBUG] MCP tool '${toolName}' failed with error:`);
|
|
@@ -69416,8 +70855,9 @@ ${toolResultContent}
|
|
|
69416
70855
|
console.error(`[DEBUG] ========================================
|
|
69417
70856
|
`);
|
|
69418
70857
|
}
|
|
70858
|
+
const errorXml = formatErrorForAI(error);
|
|
69419
70859
|
currentMessages.push({ role: "user", content: `<tool_result>
|
|
69420
|
-
${
|
|
70860
|
+
${errorXml}
|
|
69421
70861
|
</tool_result>` });
|
|
69422
70862
|
}
|
|
69423
70863
|
} else if (this.toolImplementations[toolName]) {
|
|
@@ -69475,6 +70915,8 @@ ${toolResultContent}
|
|
|
69475
70915
|
model: this.model,
|
|
69476
70916
|
// Inherit model
|
|
69477
70917
|
searchDelegate: this.searchDelegate,
|
|
70918
|
+
enableTasks: this.enableTasks,
|
|
70919
|
+
// Inherit task management (subagent gets isolated TaskManager)
|
|
69478
70920
|
debug: this.debug,
|
|
69479
70921
|
tracer: this.tracer
|
|
69480
70922
|
};
|
|
@@ -69554,10 +70996,11 @@ ${toolResultContent}
|
|
|
69554
70996
|
} catch (error) {
|
|
69555
70997
|
console.error(`[ERROR] Tool execution failed for ${toolName}:`, error);
|
|
69556
70998
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
70999
|
+
const errorXml = formatErrorForAI(error);
|
|
69557
71000
|
currentMessages.push({
|
|
69558
71001
|
role: "user",
|
|
69559
71002
|
content: `<tool_result>
|
|
69560
|
-
|
|
71003
|
+
${errorXml}
|
|
69561
71004
|
</tool_result>`
|
|
69562
71005
|
});
|
|
69563
71006
|
}
|
|
@@ -69570,7 +71013,10 @@ Error: ${error.message}
|
|
|
69570
71013
|
currentMessages.push({
|
|
69571
71014
|
role: "user",
|
|
69572
71015
|
content: `<tool_result>
|
|
69573
|
-
|
|
71016
|
+
<error type="parameter_error" recoverable="true">
|
|
71017
|
+
<message>Unknown tool '${toolName}'</message>
|
|
71018
|
+
<suggestion>Available tools: ${allAvailableTools.join(", ")}. Please use one of these tools.</suggestion>
|
|
71019
|
+
</error>
|
|
69574
71020
|
</tool_result>`
|
|
69575
71021
|
});
|
|
69576
71022
|
}
|
|
@@ -69587,7 +71033,20 @@ Error: Unknown tool '${toolName}'. Available tools: ${allAvailableTools.join(",
|
|
|
69587
71033
|
break;
|
|
69588
71034
|
}
|
|
69589
71035
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
69590
|
-
const
|
|
71036
|
+
const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
|
|
71037
|
+
let reminderContent;
|
|
71038
|
+
if (unrecognizedTool) {
|
|
71039
|
+
if (this.debug) {
|
|
71040
|
+
console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
|
|
71041
|
+
}
|
|
71042
|
+
const toolError = new ParameterError(`Tool '${unrecognizedTool}' is not available in this context.`, {
|
|
71043
|
+
suggestion: `Available tools: ${validTools.join(", ")}. Please use one of these tools instead.`
|
|
71044
|
+
});
|
|
71045
|
+
reminderContent = `<tool_result>
|
|
71046
|
+
${formatErrorForAI(toolError)}
|
|
71047
|
+
</tool_result>`;
|
|
71048
|
+
} else {
|
|
71049
|
+
reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
|
|
69591
71050
|
|
|
69592
71051
|
Remember: Use proper XML format with BOTH opening and closing tags:
|
|
69593
71052
|
|
|
@@ -69595,16 +71054,26 @@ Remember: Use proper XML format with BOTH opening and closing tags:
|
|
|
69595
71054
|
<parameter>value</parameter>
|
|
69596
71055
|
</tool_name>
|
|
69597
71056
|
|
|
69598
|
-
|
|
69599
|
-
<attempt_complete>
|
|
71057
|
+
Available tools: ${validTools.join(", ")}
|
|
69600
71058
|
|
|
69601
|
-
|
|
71059
|
+
To complete with a direct answer:
|
|
71060
|
+
<attempt_completion>Your final answer here</attempt_completion>
|
|
71061
|
+
|
|
71062
|
+
Or if your previous response already contains a complete, direct answer (not a thinking block or JSON):
|
|
71063
|
+
<attempt_complete></attempt_complete>
|
|
71064
|
+
|
|
71065
|
+
Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant message as the final answer. Only use this if that message was already a valid, complete response to the user's question.`;
|
|
71066
|
+
}
|
|
69602
71067
|
currentMessages.push({
|
|
69603
71068
|
role: "user",
|
|
69604
71069
|
content: reminderContent
|
|
69605
71070
|
});
|
|
69606
71071
|
if (this.debug) {
|
|
69607
|
-
|
|
71072
|
+
if (unrecognizedTool) {
|
|
71073
|
+
console.log(`[DEBUG] Unrecognized tool '${unrecognizedTool}' used. Providing error feedback.`);
|
|
71074
|
+
} else {
|
|
71075
|
+
console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
|
|
71076
|
+
}
|
|
69608
71077
|
}
|
|
69609
71078
|
}
|
|
69610
71079
|
if (currentMessages.length > MAX_HISTORY_MESSAGES) {
|
|
@@ -70296,7 +71765,7 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
70296
71765
|
if (content.includes("Your response does not match the expected JSON schema") || content.includes("Please provide a valid JSON response") || content.includes("Schema validation error:")) {
|
|
70297
71766
|
return true;
|
|
70298
71767
|
}
|
|
70299
|
-
if (content.includes("
|
|
71768
|
+
if (content.includes("<attempt_complete></attempt_complete>") && content.includes("reuses your PREVIOUS assistant message")) {
|
|
70300
71769
|
return true;
|
|
70301
71770
|
}
|
|
70302
71771
|
return false;
|
|
@@ -71109,6 +72578,9 @@ function parseArgs() {
|
|
|
71109
72578
|
// Disable skill discovery and activation
|
|
71110
72579
|
skillDirs: null,
|
|
71111
72580
|
// Comma-separated list of repo-relative skill directories
|
|
72581
|
+
// Task management
|
|
72582
|
+
enableTasks: false,
|
|
72583
|
+
// Enable task tracking for progress management
|
|
71112
72584
|
// Bash tool configuration
|
|
71113
72585
|
enableBash: false,
|
|
71114
72586
|
bashAllow: null,
|
|
@@ -71179,6 +72651,8 @@ function parseArgs() {
|
|
|
71179
72651
|
config.disableSkills = true;
|
|
71180
72652
|
} else if (arg === "--skills-dir" && i + 1 < args.length) {
|
|
71181
72653
|
config.skillDirs = args[++i].split(",").map((dir) => dir.trim()).filter(Boolean);
|
|
72654
|
+
} else if (arg === "--allow-tasks") {
|
|
72655
|
+
config.enableTasks = true;
|
|
71182
72656
|
} else if (arg === "--enable-bash") {
|
|
71183
72657
|
config.enableBash = true;
|
|
71184
72658
|
} else if (arg === "--bash-allow" && i + 1 < args.length) {
|
|
@@ -71233,6 +72707,7 @@ Options:
|
|
|
71233
72707
|
Convenience flag equivalent to --allowed-tools none
|
|
71234
72708
|
--skills-dir <dirs> Comma-separated list of repo-relative skill directories to scan
|
|
71235
72709
|
--no-skills Disable skill discovery and activation
|
|
72710
|
+
--allow-tasks Enable task management for tracking multi-step progress
|
|
71236
72711
|
--verbose Enable verbose output
|
|
71237
72712
|
--outline Use outline-xml format for code search results
|
|
71238
72713
|
--mcp Run as MCP server
|
|
@@ -71354,6 +72829,11 @@ var ProbeAgentMcpServer = class {
|
|
|
71354
72829
|
architecture_file: {
|
|
71355
72830
|
type: "string",
|
|
71356
72831
|
description: "Optional architecture context filename in repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present)."
|
|
72832
|
+
},
|
|
72833
|
+
enable_tasks: {
|
|
72834
|
+
type: "boolean",
|
|
72835
|
+
description: "Optional: Enable task management for tracking multi-step progress. When enabled, the agent can create, track, and complete tasks.",
|
|
72836
|
+
default: false
|
|
71357
72837
|
}
|
|
71358
72838
|
},
|
|
71359
72839
|
required: ["query"]
|
|
@@ -71456,7 +72936,8 @@ var ProbeAgentMcpServer = class {
|
|
|
71456
72936
|
maxResponseTokens: args.max_response_tokens,
|
|
71457
72937
|
disableMermaidValidation: !!args.no_mermaid_validation,
|
|
71458
72938
|
allowedTools: args.allowed_tools,
|
|
71459
|
-
disableTools: args.disable_tools
|
|
72939
|
+
disableTools: args.disable_tools,
|
|
72940
|
+
enableTasks: !!args.enable_tasks
|
|
71460
72941
|
};
|
|
71461
72942
|
this.agent = new ProbeAgent(agentConfig2);
|
|
71462
72943
|
await this.agent.initialize();
|
|
@@ -71700,7 +73181,8 @@ async function main() {
|
|
|
71700
73181
|
enableSkills: !config.disableSkills,
|
|
71701
73182
|
skillDirs: config.skillDirs,
|
|
71702
73183
|
enableBash: config.enableBash,
|
|
71703
|
-
bashConfig
|
|
73184
|
+
bashConfig,
|
|
73185
|
+
enableTasks: config.enableTasks
|
|
71704
73186
|
};
|
|
71705
73187
|
const agent = new ProbeAgent(agentConfig2);
|
|
71706
73188
|
await agent.initialize();
|