@probelabs/probe 0.6.0-rc312 → 0.6.0-rc314
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-rc312-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc314-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc312-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc314-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc312-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc314-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc312-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc314-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc312-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc314-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.js +8 -2
- package/build/agent/simpleTelemetry.js +20 -1
- package/build/agent/tasks/TaskManager.js +16 -1
- package/build/agent/tasks/taskTool.js +279 -62
- package/build/delegate.js +4 -1
- package/cjs/agent/ProbeAgent.cjs +327 -97
- package/cjs/agent/simpleTelemetry.cjs +20 -2
- package/cjs/index.cjs +327 -97
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +8 -2
- package/src/agent/simpleTelemetry.js +20 -1
- package/src/agent/tasks/TaskManager.js +16 -1
- package/src/agent/tasks/taskTool.js +279 -62
- package/src/delegate.js +4 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -350,6 +350,7 @@ export class ProbeAgent {
|
|
|
350
350
|
// Task management configuration
|
|
351
351
|
this.enableTasks = !!options.enableTasks;
|
|
352
352
|
this.taskManager = null; // Initialized per-request in answer()
|
|
353
|
+
this.delegationTask = options.delegationTask || null; // Task description when this is a subagent
|
|
353
354
|
|
|
354
355
|
// Per-instance delegation manager for concurrent delegation limits
|
|
355
356
|
// Each ProbeAgent instance has its own limits, not shared globally
|
|
@@ -3350,14 +3351,19 @@ Follow these instructions carefully:
|
|
|
3350
3351
|
this.toolImplementations.task = createTaskTool({
|
|
3351
3352
|
taskManager: this.taskManager,
|
|
3352
3353
|
tracer: this.tracer,
|
|
3353
|
-
debug: this.debug
|
|
3354
|
+
debug: this.debug,
|
|
3355
|
+
delegationTask: this.delegationTask
|
|
3354
3356
|
});
|
|
3355
3357
|
}
|
|
3356
3358
|
|
|
3357
3359
|
// Record telemetry for task initialization
|
|
3358
3360
|
if (this.tracer && typeof this.tracer.recordTaskEvent === 'function') {
|
|
3359
3361
|
this.tracer.recordTaskEvent('session_started', {
|
|
3360
|
-
'task.enabled': true
|
|
3362
|
+
'task.enabled': true,
|
|
3363
|
+
'agent.session_id': this.tracer?.sessionId ?? null,
|
|
3364
|
+
'agent.parent_session_id': this.tracer?.parentSessionId ?? null,
|
|
3365
|
+
'agent.root_session_id': this.tracer?.rootSessionId ?? null,
|
|
3366
|
+
'agent.kind': this.tracer?.agentKind ?? 'main',
|
|
3361
3367
|
});
|
|
3362
3368
|
}
|
|
3363
3369
|
|
|
@@ -136,9 +136,28 @@ export class SimpleTelemetry {
|
|
|
136
136
|
* Simple tracer for application-level tracing
|
|
137
137
|
*/
|
|
138
138
|
export class SimpleAppTracer {
|
|
139
|
-
constructor(telemetry, sessionId = null) {
|
|
139
|
+
constructor(telemetry, sessionId = null, options = {}) {
|
|
140
140
|
this.telemetry = telemetry;
|
|
141
141
|
this.sessionId = sessionId || this.generateSessionId();
|
|
142
|
+
this.parentSessionId = options.parentSessionId || null;
|
|
143
|
+
this.rootSessionId = options.rootSessionId || this.sessionId;
|
|
144
|
+
this.agentKind = options.agentKind || 'main';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a child tracer for a delegated subagent.
|
|
149
|
+
* Inherits the same telemetry backend but scopes events to the child session.
|
|
150
|
+
* @param {string} childSessionId - The subagent's session ID
|
|
151
|
+
* @param {Object} [options] - Additional options
|
|
152
|
+
* @param {string} [options.agentKind='delegate'] - Kind of child agent
|
|
153
|
+
* @returns {SimpleAppTracer} A new tracer scoped to the child session
|
|
154
|
+
*/
|
|
155
|
+
createChildTracer(childSessionId, options = {}) {
|
|
156
|
+
return new SimpleAppTracer(this.telemetry, childSessionId, {
|
|
157
|
+
parentSessionId: this.sessionId,
|
|
158
|
+
rootSessionId: this.rootSessionId,
|
|
159
|
+
agentKind: options.agentKind || 'delegate',
|
|
160
|
+
});
|
|
142
161
|
}
|
|
143
162
|
|
|
144
163
|
generateSessionId() {
|
|
@@ -500,13 +500,28 @@ export class TaskManager {
|
|
|
500
500
|
|
|
501
501
|
let line = ` <task id="${this._escapeXml(task.id)}" status="${this._escapeXml(task.status)}"`;
|
|
502
502
|
if (task.priority) line += ` priority="${this._escapeXml(task.priority)}"`;
|
|
503
|
+
if (task.dependencies.length > 0) line += ` depends_on="${this._escapeXml(task.dependencies.join(','))}"`;
|
|
503
504
|
if (blockers.length > 0) line += ` blocked_by="${this._escapeXml(blockers.join(','))}"`;
|
|
504
505
|
line += `>${this._escapeXml(task.title)}</task>`;
|
|
505
506
|
|
|
506
507
|
return line;
|
|
507
508
|
});
|
|
508
509
|
|
|
509
|
-
|
|
510
|
+
// Add a brief status line so the AI can quickly assess progress
|
|
511
|
+
let completed = 0, inProgress = 0, pending = 0, cancelled = 0;
|
|
512
|
+
for (const t of tasks) {
|
|
513
|
+
if (t.status === 'completed') completed++;
|
|
514
|
+
else if (t.status === 'in_progress') inProgress++;
|
|
515
|
+
else if (t.status === 'pending') pending++;
|
|
516
|
+
else if (t.status === 'cancelled') cancelled++;
|
|
517
|
+
}
|
|
518
|
+
const statusLine = ` <!-- ${completed}/${tasks.length} completed` +
|
|
519
|
+
(inProgress > 0 ? `, ${inProgress} in progress` : '') +
|
|
520
|
+
(pending > 0 ? `, ${pending} pending` : '') +
|
|
521
|
+
(cancelled > 0 ? `, ${cancelled} cancelled` : '') +
|
|
522
|
+
` -->`;
|
|
523
|
+
|
|
524
|
+
return `<task_status>\n${statusLine}\n${taskLines.join('\n')}\n</task_status>`;
|
|
510
525
|
}
|
|
511
526
|
|
|
512
527
|
/**
|
|
@@ -42,50 +42,150 @@ export const taskToolDefinition = '';
|
|
|
42
42
|
/**
|
|
43
43
|
* Task system prompt addition - guidance for AI on when and how to use tasks
|
|
44
44
|
*/
|
|
45
|
-
export const taskSystemPrompt =
|
|
45
|
+
export const taskSystemPrompt = `<task_management_system>
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
<purpose>
|
|
48
|
+
Track progress on requests with multiple distinct goals using the task tool.
|
|
49
|
+
Tasks are visible to the user in real time — keeping them accurate is critical.
|
|
50
|
+
</purpose>
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
CREATE tasks when the request has **multiple separate deliverables**:
|
|
52
|
+
<when_to_use_tasks>
|
|
53
|
+
CREATE tasks when the request has multiple separate deliverables:
|
|
52
54
|
- "Fix bug A AND add feature B" → two tasks
|
|
53
55
|
- "Investigate auth, payments, AND notifications" → three tasks
|
|
54
|
-
- "Implement X, then add tests, then update docs" → three sequential tasks
|
|
56
|
+
- "Implement X, then add tests, then update docs" → three sequential tasks with dependencies
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
- "How does ranking work?"
|
|
58
|
-
- "Explain the authentication flow"
|
|
58
|
+
DO NOT create tasks for single-goal requests, even complex ones:
|
|
59
|
+
- "How does ranking work?" → just investigate and answer
|
|
60
|
+
- "Explain the authentication flow" → just trace and explain
|
|
59
61
|
Multiple internal steps (search, read, analyze) for one goal ≠ multiple tasks.
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Tasks = logical units of work, not files or steps.
|
|
63
|
+
Granularity: tasks = logical units of work, not individual files or steps.
|
|
64
64
|
- "Fix 8 similar test files" → ONE task (same fix repeated)
|
|
65
65
|
- "Update API + tests + docs" → THREE tasks (different work types)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
</when_to_use_tasks>
|
|
67
|
+
|
|
68
|
+
<dependency_chains>
|
|
69
|
+
Use dependencies to enforce ordering. A task CANNOT start until ALL its dependencies are completed.
|
|
70
|
+
|
|
71
|
+
<pattern name="sequential_chain" description="Tasks that must happen in strict order">
|
|
72
|
+
tasks: [
|
|
73
|
+
{ id: "design", title: "Design the API schema" },
|
|
74
|
+
{ id: "implement", title: "Implement endpoints", dependencies: ["design"] },
|
|
75
|
+
{ id: "test", title: "Write integration tests", dependencies: ["implement"] }
|
|
76
|
+
]
|
|
77
|
+
Result: design → implement → test. "implement" is blocked until "design" is completed.
|
|
78
|
+
</pattern>
|
|
79
|
+
|
|
80
|
+
<pattern name="fan_out" description="Multiple tasks unlock after one prerequisite">
|
|
81
|
+
tasks: [
|
|
82
|
+
{ id: "setup", title: "Set up database" },
|
|
83
|
+
{ id: "auth", title: "Add auth module", dependencies: ["setup"] },
|
|
84
|
+
{ id: "api", title: "Add API routes", dependencies: ["setup"] }
|
|
85
|
+
]
|
|
86
|
+
Result: both "auth" and "api" become ready once "setup" is completed.
|
|
87
|
+
</pattern>
|
|
88
|
+
|
|
89
|
+
<pattern name="fan_in" description="One task waits for multiple prerequisites">
|
|
90
|
+
tasks: [
|
|
91
|
+
{ id: "auth", title: "Auth module" },
|
|
92
|
+
{ id: "api", title: "API routes" },
|
|
93
|
+
{ id: "e2e", title: "End-to-end tests", dependencies: ["auth", "api"] }
|
|
94
|
+
]
|
|
95
|
+
Result: "e2e" is blocked until both "auth" and "api" are completed.
|
|
96
|
+
</pattern>
|
|
97
|
+
</dependency_chains>
|
|
98
|
+
|
|
99
|
+
<modifying_tasks_mid_work>
|
|
100
|
+
When new requirements emerge during execution, modify the task plan dynamically.
|
|
101
|
+
|
|
102
|
+
<technique name="add_subtask">
|
|
103
|
+
Add a new task after an existing one, with a dependency to enforce order:
|
|
104
|
+
action: "create", id: "fix-edge-case", title: "Handle null input edge case",
|
|
105
|
+
dependencies: ["implement"], after: "implement"
|
|
106
|
+
</technique>
|
|
107
|
+
|
|
108
|
+
<technique name="split_task">
|
|
109
|
+
If a task is bigger than expected, create subtasks with dependencies on it, then complete or cancel the original.
|
|
110
|
+
</technique>
|
|
111
|
+
|
|
112
|
+
<technique name="insert_into_chain">
|
|
113
|
+
Insert a new task between two existing tasks in a chain:
|
|
114
|
+
Step 1 — Create the new task depending on the predecessor:
|
|
115
|
+
action: "create", id: "review", title: "Code review", dependencies: ["implement"], after: "implement"
|
|
116
|
+
Step 2 — Update the successor to depend on the new task:
|
|
117
|
+
action: "update", id: "test", dependencies: ["review"]
|
|
118
|
+
Original: design → implement → test
|
|
119
|
+
Result: design → implement → review → test
|
|
120
|
+
</technique>
|
|
121
|
+
</modifying_tasks_mid_work>
|
|
122
|
+
|
|
123
|
+
<workflow_rules>
|
|
124
|
+
These rules are MANDATORY. Violating them produces incorrect progress tracking.
|
|
125
|
+
|
|
126
|
+
<rule id="1" name="plan_first">
|
|
127
|
+
Call task tool with action="create" and a tasks array BEFORE starting any work.
|
|
128
|
+
Use dependencies to express ordering constraints between tasks.
|
|
129
|
+
</rule>
|
|
130
|
+
|
|
131
|
+
<rule id="2" name="mark_in_progress">
|
|
132
|
+
Set the current task to status="in_progress" BEFORE you begin working on it.
|
|
133
|
+
Only one task should be in_progress at a time.
|
|
134
|
+
</rule>
|
|
135
|
+
|
|
136
|
+
<rule id="3" name="complete_immediately" priority="critical">
|
|
137
|
+
The MOMENT you finish a task's work, call the task tool with action="complete" for that task.
|
|
138
|
+
Do this in the SAME response — not in the next step, not at the end, not batched with other completions.
|
|
139
|
+
Every task must be completed the instant its work is verified done.
|
|
140
|
+
</rule>
|
|
141
|
+
|
|
142
|
+
<rule id="4" name="verify_before_completing">
|
|
143
|
+
Do NOT mark a task completed unless you have verified the result:
|
|
144
|
+
- Code compiles without errors
|
|
145
|
+
- Tests pass
|
|
146
|
+
- Output is correct and complete
|
|
147
|
+
"I wrote the code" is NOT sufficient — you must confirm it works.
|
|
148
|
+
</rule>
|
|
149
|
+
|
|
150
|
+
<rule id="5" name="respect_dependencies">
|
|
151
|
+
NEVER work on a task whose dependencies are not yet completed.
|
|
152
|
+
The task list shows blocked_by attributes — check them. If a task is blocked, complete its blockers first.
|
|
153
|
+
</rule>
|
|
154
|
+
|
|
155
|
+
<rule id="6" name="adapt_the_plan">
|
|
156
|
+
If you discover new work during execution, create new tasks with proper dependencies.
|
|
157
|
+
Do NOT silently do extra work without tracking it in the task list.
|
|
158
|
+
If a task needs subtasks, create them as new tasks depending on the current one.
|
|
159
|
+
</rule>
|
|
160
|
+
|
|
161
|
+
<rule id="7" name="finish_clean">
|
|
162
|
+
ALL tasks must be "completed" or "cancelled" before you provide your final answer.
|
|
163
|
+
You CANNOT finish with pending or in_progress tasks. The system will block you.
|
|
164
|
+
</rule>
|
|
165
|
+
</workflow_rules>
|
|
166
|
+
|
|
167
|
+
<system_enforcement>
|
|
168
|
+
- Dependencies are strictly enforced: blocked tasks cannot be started
|
|
77
169
|
- Circular dependencies are rejected
|
|
78
|
-
-
|
|
170
|
+
- You will be blocked from finishing while tasks remain unresolved
|
|
171
|
+
- Task status is visible to the user in real time — stale status is misleading
|
|
172
|
+
</system_enforcement>
|
|
173
|
+
|
|
174
|
+
</task_management_system>
|
|
79
175
|
`;
|
|
80
176
|
|
|
81
177
|
/**
|
|
82
178
|
* Task guidance to inject at start of request
|
|
83
179
|
*/
|
|
84
|
-
export const taskGuidancePrompt =
|
|
180
|
+
export const taskGuidancePrompt = `<task_decision>
|
|
181
|
+
Does this request have MULTIPLE DISTINCT GOALS?
|
|
85
182
|
- "Do A AND B AND C" (multiple goals) → Create tasks for each goal
|
|
183
|
+
- "Do X, then Y, then Z" (sequential goals) → Create tasks with dependencies: Y depends on X, Z depends on Y
|
|
86
184
|
- "Investigate/explain/find X" (single goal) → Skip tasks, just answer directly
|
|
87
185
|
Multiple internal steps for ONE goal = NO tasks needed.
|
|
88
|
-
If creating tasks
|
|
186
|
+
If creating tasks: call the task tool with action="create" and a tasks array FIRST, using dependencies for ordering.
|
|
187
|
+
CRITICAL: Complete each task IMMEDIATELY when its work is done — do not defer completions.
|
|
188
|
+
</task_decision>`;
|
|
89
189
|
|
|
90
190
|
/**
|
|
91
191
|
* Create task completion blocked message
|
|
@@ -93,40 +193,129 @@ If creating tasks, use the task tool with action="create" first.`;
|
|
|
93
193
|
* @returns {string} Formatted message
|
|
94
194
|
*/
|
|
95
195
|
export function createTaskCompletionBlockedMessage(taskSummary) {
|
|
96
|
-
return
|
|
196
|
+
return `<task_completion_blocked>
|
|
197
|
+
You CANNOT provide a final answer yet. There are unresolved tasks:
|
|
97
198
|
|
|
98
199
|
${taskSummary}
|
|
99
200
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
-
|
|
201
|
+
<required_actions>
|
|
202
|
+
For EACH unresolved task, do ONE of the following RIGHT NOW:
|
|
203
|
+
- Work is DONE → action="complete", id="<task-id>"
|
|
204
|
+
- Work is NOT needed → action="update", id="<task-id>", status="cancelled"
|
|
205
|
+
- Work is BLOCKED → complete its dependencies first, then return to it
|
|
206
|
+
- Work is NOT started → set to "in_progress" and do the work, then complete it
|
|
207
|
+
</required_actions>
|
|
208
|
+
|
|
209
|
+
You will continue to be blocked until every task is completed or cancelled.
|
|
210
|
+
</task_completion_blocked>`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Monotonic event sequence counter for deterministic replay ordering.
|
|
215
|
+
* Shared across all task tool instances within the same process.
|
|
216
|
+
* Node.js is single-threaded so no race condition is possible.
|
|
217
|
+
* Resets at Number.MAX_SAFE_INTEGER to prevent overflow.
|
|
218
|
+
*/
|
|
219
|
+
let _globalSequence = 0;
|
|
220
|
+
|
|
221
|
+
/** @internal Reset the global sequence counter (for testing only). */
|
|
222
|
+
export function _resetSequence() { _globalSequence = 0; }
|
|
103
223
|
|
|
104
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Safe JSON.stringify wrapper that returns '[]' on failure.
|
|
226
|
+
* @param {*} value - Value to serialize
|
|
227
|
+
* @returns {string} JSON string or fallback
|
|
228
|
+
*/
|
|
229
|
+
function safeStringify(value) {
|
|
230
|
+
try {
|
|
231
|
+
return JSON.stringify(value);
|
|
232
|
+
} catch {
|
|
233
|
+
return '[]';
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Serialize a task object into a flat telemetry-friendly payload.
|
|
239
|
+
* @param {Object} task - Task from TaskManager
|
|
240
|
+
* @param {number} index - Position in the task list (0-based)
|
|
241
|
+
* @returns {Object} Flat task payload
|
|
242
|
+
*/
|
|
243
|
+
function serializeTask(task, index) {
|
|
244
|
+
return {
|
|
245
|
+
id: task.id,
|
|
246
|
+
title: task.title,
|
|
247
|
+
status: task.status,
|
|
248
|
+
priority: task.priority || null,
|
|
249
|
+
dependencies: task.dependencies || [],
|
|
250
|
+
after: null, // 'after' is an insertion hint, not stored on the task
|
|
251
|
+
order: index,
|
|
252
|
+
};
|
|
105
253
|
}
|
|
106
254
|
|
|
107
255
|
/**
|
|
108
256
|
* Create task tool instance
|
|
109
257
|
* @param {Object} options - Configuration options
|
|
110
258
|
* @param {import('./TaskManager.js').TaskManager} options.taskManager - TaskManager instance
|
|
111
|
-
* @param {Object} [options.tracer] - Optional tracer for telemetry
|
|
259
|
+
* @param {Object} [options.tracer] - Optional tracer for telemetry (SimpleAppTracer with session hierarchy)
|
|
112
260
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
261
|
+
* @param {string} [options.delegationTask] - Description of the delegated task (if this is a subagent)
|
|
113
262
|
* @returns {Object} Tool instance with execute function
|
|
114
263
|
*/
|
|
115
264
|
export function createTaskTool(options = {}) {
|
|
116
|
-
const { taskManager, tracer, debug = false } = options;
|
|
265
|
+
const { taskManager, tracer, debug = false, delegationTask = null } = options;
|
|
117
266
|
|
|
118
267
|
if (!taskManager) {
|
|
119
268
|
throw new Error('TaskManager instance is required');
|
|
120
269
|
}
|
|
121
270
|
|
|
122
271
|
/**
|
|
123
|
-
*
|
|
272
|
+
* Build the agent scope fields from the tracer's session hierarchy.
|
|
273
|
+
* These fields are included in every emitted task event so consumers
|
|
274
|
+
* can group events by agent/subagent without relying on span ancestry.
|
|
275
|
+
* @returns {Object} Agent scope attributes
|
|
276
|
+
*/
|
|
277
|
+
const getAgentScope = () => {
|
|
278
|
+
if (!tracer) return {};
|
|
279
|
+
return {
|
|
280
|
+
'agent.session_id': tracer.sessionId || null,
|
|
281
|
+
'agent.parent_session_id': tracer.parentSessionId || null,
|
|
282
|
+
'agent.root_session_id': tracer.rootSessionId || null,
|
|
283
|
+
'agent.kind': tracer.agentKind || 'main',
|
|
284
|
+
...(delegationTask ? { 'delegation.task': delegationTask } : {}),
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Build global task-list context fields (total count, incomplete remaining).
|
|
290
|
+
* @param {Array} [allTasks] - Pre-fetched task list to avoid redundant calls
|
|
291
|
+
* @returns {Object}
|
|
292
|
+
*/
|
|
293
|
+
const getListContext = (allTasks) => {
|
|
294
|
+
const all = allTasks || taskManager.listTasks();
|
|
295
|
+
const incompleteCount = all.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
296
|
+
return {
|
|
297
|
+
'task.total_count': all.length,
|
|
298
|
+
'task.incomplete_remaining': incompleteCount,
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Record task telemetry event with agent scope and monotonic sequence.
|
|
124
304
|
* @param {string} eventType - Event type (created, updated, completed, deleted, listed, error)
|
|
125
305
|
* @param {Object} data - Event data
|
|
126
306
|
*/
|
|
127
307
|
const recordTaskEvent = (eventType, data = {}) => {
|
|
128
308
|
if (tracer && typeof tracer.recordTaskEvent === 'function') {
|
|
129
|
-
|
|
309
|
+
if (_globalSequence >= Number.MAX_SAFE_INTEGER) _globalSequence = 0;
|
|
310
|
+
try {
|
|
311
|
+
tracer.recordTaskEvent(eventType, {
|
|
312
|
+
'task.sequence': ++_globalSequence,
|
|
313
|
+
...getAgentScope(),
|
|
314
|
+
...data
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (debug) console.error('[TaskTool] Failed to record telemetry event:', err);
|
|
318
|
+
}
|
|
130
319
|
}
|
|
131
320
|
};
|
|
132
321
|
|
|
@@ -167,25 +356,30 @@ export function createTaskTool(options = {}) {
|
|
|
167
356
|
if (tasks && Array.isArray(tasks)) {
|
|
168
357
|
// Batch create
|
|
169
358
|
const created = taskManager.createTasks(tasks);
|
|
170
|
-
const
|
|
359
|
+
const allTasks = taskManager.listTasks();
|
|
360
|
+
const taskIndex = new Map(allTasks.map((t, i) => [t.id, i]));
|
|
171
361
|
recordTaskEvent('batch_created', {
|
|
172
362
|
'task.action': 'create',
|
|
173
363
|
'task.count': created.length,
|
|
174
|
-
'task.
|
|
175
|
-
|
|
364
|
+
'task.items_json': safeStringify(created.map(t => serializeTask(t, taskIndex.get(t.id) ?? 0))),
|
|
365
|
+
...getListContext(allTasks)
|
|
176
366
|
});
|
|
177
|
-
return `Created ${created.length} tasks: ${
|
|
367
|
+
return `Created ${created.length} tasks: ${created.map(t => t.id).join(', ')}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
178
368
|
} else if (title) {
|
|
179
369
|
// Single create
|
|
180
370
|
const task = taskManager.createTask({ title, description, priority, dependencies, after });
|
|
371
|
+
const allTasks = taskManager.listTasks();
|
|
372
|
+
const order = allTasks.findIndex(t => t.id === task.id);
|
|
181
373
|
recordTaskEvent('created', {
|
|
182
374
|
'task.action': 'create',
|
|
183
375
|
'task.id': task.id,
|
|
184
|
-
'task.title': title,
|
|
185
|
-
'task.
|
|
186
|
-
'task.
|
|
187
|
-
'task.
|
|
188
|
-
'task.
|
|
376
|
+
'task.title': task.title,
|
|
377
|
+
'task.status': task.status,
|
|
378
|
+
'task.priority': task.priority || null,
|
|
379
|
+
'task.dependencies': safeStringify(task.dependencies || []),
|
|
380
|
+
'task.after': after || null,
|
|
381
|
+
'task.order': order,
|
|
382
|
+
...getListContext(allTasks)
|
|
189
383
|
});
|
|
190
384
|
return `Created task ${task.id}: ${task.title}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
191
385
|
} else {
|
|
@@ -197,13 +391,15 @@ export function createTaskTool(options = {}) {
|
|
|
197
391
|
if (tasks && Array.isArray(tasks)) {
|
|
198
392
|
// Batch update
|
|
199
393
|
const updated = taskManager.updateTasks(tasks);
|
|
200
|
-
const
|
|
394
|
+
const allTasks = taskManager.listTasks();
|
|
395
|
+
const taskIndex = new Map(allTasks.map((t, i) => [t.id, i]));
|
|
201
396
|
recordTaskEvent('batch_updated', {
|
|
202
397
|
'task.action': 'update',
|
|
203
398
|
'task.count': updated.length,
|
|
204
|
-
'task.
|
|
399
|
+
'task.items_json': safeStringify(updated.map(t => serializeTask(t, taskIndex.get(t.id) ?? 0))),
|
|
400
|
+
...getListContext(allTasks)
|
|
205
401
|
});
|
|
206
|
-
return `Updated ${updated.length} tasks: ${
|
|
402
|
+
return `Updated ${updated.length} tasks: ${updated.map(t => t.id).join(', ')}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
207
403
|
} else if (id) {
|
|
208
404
|
// Single update
|
|
209
405
|
const updates = {};
|
|
@@ -214,11 +410,18 @@ export function createTaskTool(options = {}) {
|
|
|
214
410
|
if (dependencies) updates.dependencies = dependencies;
|
|
215
411
|
|
|
216
412
|
const task = taskManager.updateTask(id, updates);
|
|
413
|
+
const allTasks = taskManager.listTasks();
|
|
414
|
+
const order = allTasks.findIndex(t => t.id === task.id);
|
|
217
415
|
recordTaskEvent('updated', {
|
|
218
416
|
'task.action': 'update',
|
|
219
|
-
'task.id': id,
|
|
220
|
-
'task.
|
|
221
|
-
'task.
|
|
417
|
+
'task.id': task.id,
|
|
418
|
+
'task.title': task.title,
|
|
419
|
+
'task.status': task.status,
|
|
420
|
+
'task.priority': task.priority || null,
|
|
421
|
+
'task.dependencies': safeStringify(task.dependencies || []),
|
|
422
|
+
'task.order': order,
|
|
423
|
+
'task.fields_updated': Object.keys(updates).join(', '),
|
|
424
|
+
...getListContext(allTasks)
|
|
222
425
|
});
|
|
223
426
|
return `Updated task ${task.id}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
224
427
|
} else {
|
|
@@ -235,21 +438,29 @@ export function createTaskTool(options = {}) {
|
|
|
235
438
|
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
236
439
|
});
|
|
237
440
|
const completed = taskManager.completeTasks(ids);
|
|
441
|
+
const allTasks = taskManager.listTasks();
|
|
442
|
+
const taskIndex = new Map(allTasks.map((t, i) => [t.id, i]));
|
|
238
443
|
recordTaskEvent('batch_completed', {
|
|
239
444
|
'task.action': 'complete',
|
|
240
445
|
'task.count': completed.length,
|
|
241
|
-
'task.
|
|
242
|
-
|
|
446
|
+
'task.items_json': safeStringify(completed.map(t => serializeTask(t, taskIndex.get(t.id) ?? 0))),
|
|
447
|
+
...getListContext(allTasks)
|
|
243
448
|
});
|
|
244
449
|
return `Completed ${completed.length} tasks\n\n${taskManager.formatTasksForPrompt()}`;
|
|
245
450
|
} else if (id) {
|
|
246
451
|
// Single complete
|
|
247
452
|
const task = taskManager.completeTask(id);
|
|
453
|
+
const allTasks = taskManager.listTasks();
|
|
454
|
+
const order = allTasks.findIndex(t => t.id === task.id);
|
|
248
455
|
recordTaskEvent('completed', {
|
|
249
456
|
'task.action': 'complete',
|
|
250
|
-
'task.id': id,
|
|
457
|
+
'task.id': task.id,
|
|
251
458
|
'task.title': task.title,
|
|
252
|
-
'task.
|
|
459
|
+
'task.status': task.status,
|
|
460
|
+
'task.priority': task.priority || null,
|
|
461
|
+
'task.dependencies': safeStringify(task.dependencies || []),
|
|
462
|
+
'task.order': order,
|
|
463
|
+
...getListContext(allTasks)
|
|
253
464
|
});
|
|
254
465
|
return `Completed task ${task.id}: ${task.title}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
255
466
|
} else {
|
|
@@ -265,21 +476,26 @@ export function createTaskTool(options = {}) {
|
|
|
265
476
|
if (t && typeof t.id === 'string') return t.id;
|
|
266
477
|
throw new Error(`Invalid task item at index ${index}: must be a string ID or object with 'id' property`);
|
|
267
478
|
});
|
|
479
|
+
// Capture task data before deletion for the event
|
|
480
|
+
const tasksBefore = ids.map(tid => taskManager.getTask(tid)).filter(Boolean);
|
|
268
481
|
const deleted = taskManager.deleteTasks(ids);
|
|
269
482
|
recordTaskEvent('batch_deleted', {
|
|
270
483
|
'task.action': 'delete',
|
|
271
484
|
'task.count': deleted.length,
|
|
272
|
-
'task.
|
|
273
|
-
|
|
485
|
+
'task.items_json': safeStringify(tasksBefore.map((t, i) => ({ id: t.id, title: t.title, status: t.status }))),
|
|
486
|
+
...getListContext()
|
|
274
487
|
});
|
|
275
488
|
return `Deleted ${deleted.length} tasks: ${deleted.join(', ')}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
276
489
|
} else if (id) {
|
|
277
|
-
//
|
|
490
|
+
// Capture task data before deletion
|
|
491
|
+
const taskBefore = taskManager.getTask(id);
|
|
278
492
|
taskManager.deleteTask(id);
|
|
279
493
|
recordTaskEvent('deleted', {
|
|
280
494
|
'task.action': 'delete',
|
|
281
495
|
'task.id': id,
|
|
282
|
-
'task.
|
|
496
|
+
'task.title': taskBefore?.title || null,
|
|
497
|
+
'task.status': taskBefore?.status || null,
|
|
498
|
+
...getListContext()
|
|
283
499
|
});
|
|
284
500
|
return `Deleted task ${id}\n\n${taskManager.formatTasksForPrompt()}`;
|
|
285
501
|
} else {
|
|
@@ -289,12 +505,13 @@ export function createTaskTool(options = {}) {
|
|
|
289
505
|
|
|
290
506
|
case 'list': {
|
|
291
507
|
const allTasks = taskManager.listTasks();
|
|
292
|
-
const
|
|
508
|
+
const ctx = getListContext(allTasks);
|
|
293
509
|
recordTaskEvent('listed', {
|
|
294
510
|
'task.action': 'list',
|
|
295
|
-
'task.total_count':
|
|
296
|
-
'task.incomplete_count':
|
|
297
|
-
'task.completed_count':
|
|
511
|
+
'task.total_count': ctx['task.total_count'],
|
|
512
|
+
'task.incomplete_count': ctx['task.incomplete_remaining'],
|
|
513
|
+
'task.completed_count': ctx['task.total_count'] - ctx['task.incomplete_remaining'],
|
|
514
|
+
'task.items_json': safeStringify(allTasks.map((t, i) => serializeTask(t, i)))
|
|
298
515
|
});
|
|
299
516
|
return taskManager.formatTasksForPrompt();
|
|
300
517
|
}
|
package/build/delegate.js
CHANGED
|
@@ -484,7 +484,10 @@ export async function delegate({
|
|
|
484
484
|
disableJsonValidation: true, // Simpler responses
|
|
485
485
|
maxIterations: remainingIterations,
|
|
486
486
|
debug,
|
|
487
|
-
tracer
|
|
487
|
+
tracer: tracer && typeof tracer.createChildTracer === 'function'
|
|
488
|
+
? tracer.createChildTracer(sessionId, { agentKind: 'delegate' })
|
|
489
|
+
: (tracer && debug && console.warn('[delegate] createChildTracer not available, using parent tracer — events will not be scoped to subagent'), tracer),
|
|
490
|
+
delegationTask: task,
|
|
488
491
|
path, // Workspace root (from delegateTool)
|
|
489
492
|
allowedFolders, // Inherit allowed folders to keep architecture context root consistent
|
|
490
493
|
cwd: path, // Explicitly set cwd to workspace root to prevent path doubling
|