@ai-setting/roy-agent-core 1.6.16 → 1.6.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env/agent/index.js +2 -2
- package/dist/env/event-source/index.js +3 -3
- package/dist/env/index.js +12 -12
- package/dist/env/prompt/index.js +2 -2
- package/dist/env/task/delegate/index.js +3 -3
- package/dist/env/task/index.js +5 -5
- package/dist/env/task/plugins/index.js +2 -2
- package/dist/env/task/tools/index.js +1 -1
- package/dist/env/tool/built-in/index.js +2 -2
- package/dist/env/tool/index.js +3 -3
- package/dist/env/workflow/index.js +2 -2
- package/dist/env/workflow/tools/index.js +1 -1
- package/dist/index.js +14 -14
- package/dist/shared/@ai-setting/{roy-agent-core-eaj0b1hm.js → roy-agent-core-44g3synv.js} +16 -8
- package/dist/shared/@ai-setting/{roy-agent-core-pq1q854s.js → roy-agent-core-65vpkh7x.js} +8 -0
- package/dist/shared/@ai-setting/{roy-agent-core-bsxgrqzq.js → roy-agent-core-6mz26khm.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-r0t34bbh.js → roy-agent-core-c7e3htaq.js} +146 -8
- package/dist/shared/@ai-setting/{roy-agent-core-vkesx61c.js → roy-agent-core-e9jf4w3z.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-x36fsm47.js → roy-agent-core-f6x6ksz3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-cfqm5w9b.js → roy-agent-core-h07kk80k.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4z7dzcgb.js → roy-agent-core-kkt2ndpf.js} +168 -7
- package/dist/shared/@ai-setting/{roy-agent-core-25ngkkpb.js → roy-agent-core-mvmgy9t0.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-8v0hzw96.js → roy-agent-core-n1fx2fx4.js} +680 -33
- package/dist/shared/@ai-setting/{roy-agent-core-yqxrekhg.js → roy-agent-core-nreqmmtz.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-ymz9dgw0.js → roy-agent-core-qyb2z42s.js} +28 -2
- package/dist/shared/@ai-setting/{roy-agent-core-dnns0v64.js → roy-agent-core-vc8e968b.js} +1 -1
- package/package.json +1 -1
- /package/dist/shared/@ai-setting/{roy-agent-core-b8wn8yhk.js → roy-agent-core-eskgj8nc.js} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProcessRegistry,
|
|
3
3
|
runWithProcessRegistryAsync
|
|
4
|
-
} from "./roy-agent-core-
|
|
4
|
+
} from "./roy-agent-core-44g3synv.js";
|
|
5
5
|
import {
|
|
6
6
|
builtInPrompts
|
|
7
|
-
} from "./roy-agent-core-
|
|
7
|
+
} from "./roy-agent-core-65vpkh7x.js";
|
|
8
8
|
import {
|
|
9
9
|
runWithBgTaskIdAsync
|
|
10
10
|
} from "./roy-agent-core-c1v263jn.js";
|
|
@@ -108,7 +108,631 @@ function listKnownSubagentDescriptions(registry) {
|
|
|
108
108
|
|
|
109
109
|
// src/env/task/delegate/delegate-tool.ts
|
|
110
110
|
init_global_hook_manager();
|
|
111
|
-
|
|
111
|
+
|
|
112
|
+
// src/env/task/task-creation-registry.ts
|
|
113
|
+
var TOKEN_PATTERNS = [
|
|
114
|
+
/\bsk-(?:proj-)?[A-Za-z0-9_-]{16,}\b/g,
|
|
115
|
+
/\bxox[abprs]-[A-Za-z0-9-]{8,}\b/g,
|
|
116
|
+
/\bghp_[A-Za-z0-9]{16,}\b/g,
|
|
117
|
+
/\bgithub_pat_[A-Za-z0-9_]{16,}\b/g,
|
|
118
|
+
/\bBearer\s+[A-Za-z0-9._\-+/=]{12,}\b/g,
|
|
119
|
+
/\bAKIA[0-9A-Z]{16}\b/g
|
|
120
|
+
];
|
|
121
|
+
function redactTokens(s) {
|
|
122
|
+
let out = s;
|
|
123
|
+
for (const pat of TOKEN_PATTERNS) {
|
|
124
|
+
out = out.replace(pat, "<redacted>");
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
function truncate(s, max) {
|
|
129
|
+
if (s.length <= max)
|
|
130
|
+
return s;
|
|
131
|
+
if (max <= 3)
|
|
132
|
+
return s.slice(0, max);
|
|
133
|
+
return s.slice(0, max - 1) + "…";
|
|
134
|
+
}
|
|
135
|
+
function sanitize(s, max) {
|
|
136
|
+
return truncate(redactTokens(s), max);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
class TaskCreationRegistry {
|
|
140
|
+
maxEntries;
|
|
141
|
+
ttlMs;
|
|
142
|
+
maxDescriptionChars;
|
|
143
|
+
records = new Map;
|
|
144
|
+
childrenIndex = new Map;
|
|
145
|
+
constructor(config = {}) {
|
|
146
|
+
this.maxEntries = config.maxEntries ?? 500;
|
|
147
|
+
this.ttlMs = config.ttlMs ?? 24 * 60 * 60 * 1000;
|
|
148
|
+
this.maxDescriptionChars = config.maxDescriptionChars ?? 240;
|
|
149
|
+
}
|
|
150
|
+
record(task, source = {}) {
|
|
151
|
+
const now = new Date().toISOString();
|
|
152
|
+
const record = this.toRecord(task, source, now);
|
|
153
|
+
this.upsert(record);
|
|
154
|
+
this.indexParentChild(record);
|
|
155
|
+
this.evictIfNeeded();
|
|
156
|
+
return record;
|
|
157
|
+
}
|
|
158
|
+
update(task, source) {
|
|
159
|
+
const existing = this.records.get(task.id);
|
|
160
|
+
if (!existing)
|
|
161
|
+
return;
|
|
162
|
+
const now = new Date().toISOString();
|
|
163
|
+
this.records.delete(task.id);
|
|
164
|
+
const merged = {
|
|
165
|
+
...existing,
|
|
166
|
+
...this.toRecord(task, source ?? existing.source, now),
|
|
167
|
+
createdAt: existing.createdAt,
|
|
168
|
+
source: source ?? existing.source
|
|
169
|
+
};
|
|
170
|
+
this.records.set(task.id, merged);
|
|
171
|
+
this.childrenIndex.delete(merged.taskId);
|
|
172
|
+
this.indexParentChild(merged);
|
|
173
|
+
this.evictIfNeeded();
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
remove(id) {
|
|
177
|
+
const rec = this.records.get(id);
|
|
178
|
+
if (!rec)
|
|
179
|
+
return false;
|
|
180
|
+
this.records.delete(id);
|
|
181
|
+
if (rec.parentTaskId !== undefined) {
|
|
182
|
+
const siblings = this.childrenIndex.get(rec.parentTaskId);
|
|
183
|
+
siblings?.delete(id);
|
|
184
|
+
if (siblings && siblings.size === 0) {
|
|
185
|
+
this.childrenIndex.delete(rec.parentTaskId);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
getById(id) {
|
|
191
|
+
const rec = this.records.get(id);
|
|
192
|
+
if (!rec)
|
|
193
|
+
return;
|
|
194
|
+
if (Date.now() - Date.parse(rec.updatedAt) > this.ttlMs) {
|
|
195
|
+
this.remove(id);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
return rec;
|
|
199
|
+
}
|
|
200
|
+
peekById(id) {
|
|
201
|
+
return this.records.get(id);
|
|
202
|
+
}
|
|
203
|
+
clear() {
|
|
204
|
+
this.records.clear();
|
|
205
|
+
this.childrenIndex.clear();
|
|
206
|
+
}
|
|
207
|
+
size() {
|
|
208
|
+
return this.records.size;
|
|
209
|
+
}
|
|
210
|
+
recent(options = {}) {
|
|
211
|
+
const limit = options.limit ?? 10;
|
|
212
|
+
const statuses = options.statuses;
|
|
213
|
+
const excludeIds = new Set(options.excludeIds ?? []);
|
|
214
|
+
const excludeAncestorId = options.excludeAncestorOf ?? options.excludeSelfOrDescendant;
|
|
215
|
+
const excludeAncestorIds = excludeAncestorId ? this.collectSubtree(excludeAncestorId) : new Set;
|
|
216
|
+
const sinceMs = options.sinceHours ? Date.now() - options.sinceHours * 60 * 60 * 1000 : 0;
|
|
217
|
+
const now = Date.now();
|
|
218
|
+
const excludeAll = new Set([...excludeIds, ...excludeAncestorIds]);
|
|
219
|
+
const all = [];
|
|
220
|
+
for (const rec of this.records.values()) {
|
|
221
|
+
if (now - Date.parse(rec.updatedAt) > this.ttlMs)
|
|
222
|
+
continue;
|
|
223
|
+
if (sinceMs > 0 && Date.parse(rec.updatedAt) < sinceMs)
|
|
224
|
+
continue;
|
|
225
|
+
if (excludeAll.has(rec.taskId))
|
|
226
|
+
continue;
|
|
227
|
+
if (options.scope) {
|
|
228
|
+
if (options.scope.sessionId !== undefined && rec.source.sessionId !== options.scope.sessionId)
|
|
229
|
+
continue;
|
|
230
|
+
if (options.scope.parentSessionId !== undefined && rec.source.parentSessionId !== options.scope.parentSessionId)
|
|
231
|
+
continue;
|
|
232
|
+
if (options.scope.subagentType !== undefined && rec.source.subagentType !== options.scope.subagentType)
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (statuses) {
|
|
236
|
+
if (!statuses.includes(rec.status))
|
|
237
|
+
continue;
|
|
238
|
+
} else {
|
|
239
|
+
if (rec.status === "archived")
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
all.push(rec);
|
|
243
|
+
}
|
|
244
|
+
if (options.activeChildFirst) {
|
|
245
|
+
const activeChildBoost = new Map;
|
|
246
|
+
for (const rec of all) {
|
|
247
|
+
activeChildBoost.set(rec.taskId, this.hasActiveChild(rec.taskId));
|
|
248
|
+
}
|
|
249
|
+
all.sort((a, b) => {
|
|
250
|
+
const aBoost = activeChildBoost.get(a.taskId) ? 1 : 0;
|
|
251
|
+
const bBoost = activeChildBoost.get(b.taskId) ? 1 : 0;
|
|
252
|
+
if (aBoost !== bBoost)
|
|
253
|
+
return bBoost - aBoost;
|
|
254
|
+
const cmp = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
|
|
255
|
+
if (cmp !== 0)
|
|
256
|
+
return cmp;
|
|
257
|
+
return b.taskId - a.taskId;
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
all.sort((a, b) => {
|
|
261
|
+
const cmp = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
|
|
262
|
+
if (cmp !== 0)
|
|
263
|
+
return cmp;
|
|
264
|
+
return b.taskId - a.taskId;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return all.slice(0, limit);
|
|
268
|
+
}
|
|
269
|
+
getSubtreeSummary(parentId, options = {}) {
|
|
270
|
+
const includeArchived = options.includeArchived === true;
|
|
271
|
+
const summary = {
|
|
272
|
+
descendantCount: 0,
|
|
273
|
+
activeCount: 0,
|
|
274
|
+
completedCount: 0,
|
|
275
|
+
maxDepth: 0
|
|
276
|
+
};
|
|
277
|
+
if (!this.records.has(parentId) && !this.childrenIndex.has(parentId)) {
|
|
278
|
+
return summary;
|
|
279
|
+
}
|
|
280
|
+
const directChildren = this.childrenIndex.get(parentId);
|
|
281
|
+
const currentLevel = directChildren ? Array.from(directChildren) : [];
|
|
282
|
+
let depth = currentLevel.length > 0 ? 1 : 0;
|
|
283
|
+
summary.maxDepth = depth;
|
|
284
|
+
let levelIds = currentLevel;
|
|
285
|
+
while (levelIds.length > 0) {
|
|
286
|
+
const nextLevel = [];
|
|
287
|
+
for (const id of levelIds) {
|
|
288
|
+
const rec = this.records.get(id);
|
|
289
|
+
if (!rec)
|
|
290
|
+
continue;
|
|
291
|
+
if (!includeArchived && rec.status === "archived")
|
|
292
|
+
continue;
|
|
293
|
+
summary.descendantCount++;
|
|
294
|
+
if (rec.status === "active") {
|
|
295
|
+
summary.activeCount++;
|
|
296
|
+
} else if (rec.status === "completed") {
|
|
297
|
+
summary.completedCount++;
|
|
298
|
+
}
|
|
299
|
+
const grandchildren = this.childrenIndex.get(id);
|
|
300
|
+
if (grandchildren && grandchildren.size > 0) {
|
|
301
|
+
for (const gc of grandchildren)
|
|
302
|
+
nextLevel.push(gc);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (nextLevel.length > 0) {
|
|
306
|
+
depth++;
|
|
307
|
+
summary.maxDepth = depth;
|
|
308
|
+
}
|
|
309
|
+
levelIds = nextLevel;
|
|
310
|
+
}
|
|
311
|
+
return summary;
|
|
312
|
+
}
|
|
313
|
+
hasActiveChild(parentId) {
|
|
314
|
+
const children = this.childrenIndex.get(parentId);
|
|
315
|
+
if (!children)
|
|
316
|
+
return false;
|
|
317
|
+
for (const childId of children) {
|
|
318
|
+
const rec = this.records.get(childId);
|
|
319
|
+
if (!rec)
|
|
320
|
+
continue;
|
|
321
|
+
if (rec.status === "active" || rec.status === "todo")
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
toRecord(task, source, isoNow) {
|
|
327
|
+
const parsedContext = this.safeParseContext(task.context);
|
|
328
|
+
const safeContext = parsedContext ? this.stripSensitiveKeys(parsedContext) : undefined;
|
|
329
|
+
return {
|
|
330
|
+
taskId: task.id,
|
|
331
|
+
title: task.title,
|
|
332
|
+
description: sanitize(task.description || "", this.maxDescriptionChars),
|
|
333
|
+
goals: sanitize(task.goals_and_expected_deliverables || "", this.maxDescriptionChars),
|
|
334
|
+
tags: task.tags ?? [],
|
|
335
|
+
status: task.status,
|
|
336
|
+
priority: task.priority,
|
|
337
|
+
type: task.type,
|
|
338
|
+
parentTaskId: task.parent_task_id,
|
|
339
|
+
source,
|
|
340
|
+
createdAt: task.createdAt || isoNow,
|
|
341
|
+
updatedAt: task.updatedAt || isoNow,
|
|
342
|
+
operationCount: 0,
|
|
343
|
+
project_path: task.project_path,
|
|
344
|
+
branch: this.pickString(safeContext, "branch"),
|
|
345
|
+
worktree_path: this.pickString(safeContext, "worktree_path"),
|
|
346
|
+
parentBranch: this.pickString(safeContext, "parent_branch"),
|
|
347
|
+
context: safeContext
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
safeParseContext(raw) {
|
|
351
|
+
if (!raw || !raw.trim())
|
|
352
|
+
return;
|
|
353
|
+
try {
|
|
354
|
+
const parsed = JSON.parse(raw);
|
|
355
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
356
|
+
return parsed;
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
} catch {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
pickString(obj, key) {
|
|
364
|
+
if (!obj)
|
|
365
|
+
return;
|
|
366
|
+
const v = obj[key];
|
|
367
|
+
return typeof v === "string" ? v : undefined;
|
|
368
|
+
}
|
|
369
|
+
static STRIPPED_KEYS = ["commit", "token", "secret", "password", "api_key"];
|
|
370
|
+
stripSensitiveKeys(obj) {
|
|
371
|
+
if (obj === null || obj === undefined)
|
|
372
|
+
return obj;
|
|
373
|
+
if (Array.isArray(obj))
|
|
374
|
+
return obj.map((v) => this.stripSensitiveKeys(v));
|
|
375
|
+
if (typeof obj !== "object")
|
|
376
|
+
return obj;
|
|
377
|
+
const out = {};
|
|
378
|
+
const strippedKeys = TaskCreationRegistry.STRIPPED_KEYS;
|
|
379
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
380
|
+
const lower = k.toLowerCase();
|
|
381
|
+
if (strippedKeys.some((s) => lower.includes(s)))
|
|
382
|
+
continue;
|
|
383
|
+
out[k] = this.stripSensitiveKeys(v);
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
upsert(record) {
|
|
388
|
+
this.records.set(record.taskId, record);
|
|
389
|
+
}
|
|
390
|
+
indexParentChild(record) {
|
|
391
|
+
if (record.parentTaskId === undefined || record.parentTaskId === null)
|
|
392
|
+
return;
|
|
393
|
+
const parentId = record.parentTaskId;
|
|
394
|
+
let set = this.childrenIndex.get(parentId);
|
|
395
|
+
if (!set) {
|
|
396
|
+
set = new Set;
|
|
397
|
+
this.childrenIndex.set(parentId, set);
|
|
398
|
+
}
|
|
399
|
+
set.add(record.taskId);
|
|
400
|
+
}
|
|
401
|
+
collectSubtree(rootId) {
|
|
402
|
+
const out = new Set([rootId]);
|
|
403
|
+
const queue = [rootId];
|
|
404
|
+
while (queue.length > 0) {
|
|
405
|
+
const id = queue.shift();
|
|
406
|
+
const children = this.childrenIndex.get(id);
|
|
407
|
+
if (!children)
|
|
408
|
+
continue;
|
|
409
|
+
for (const child of children) {
|
|
410
|
+
if (!out.has(child)) {
|
|
411
|
+
out.add(child);
|
|
412
|
+
queue.push(child);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
evictIfNeeded() {
|
|
419
|
+
if (this.records.size <= this.maxEntries)
|
|
420
|
+
return;
|
|
421
|
+
const overflow = this.records.size - this.maxEntries;
|
|
422
|
+
const keys = Array.from(this.records.keys()).slice(0, overflow);
|
|
423
|
+
for (const k of keys) {
|
|
424
|
+
this.remove(k);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/env/task/registry-hook-installer.ts
|
|
430
|
+
init_logger();
|
|
431
|
+
var logger = createLogger("registry-hook-installer");
|
|
432
|
+
function installTaskCreationRegistryHooks(taskComponent, registry, options = {}) {
|
|
433
|
+
const uniqueSuffix = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
434
|
+
const hookName = options.hookName ? `${options.hookName}:${uniqueSuffix}` : `registry-hook-installer:${uniqueSuffix}`;
|
|
435
|
+
const priority = options.priority ?? 100;
|
|
436
|
+
let uninstalled = false;
|
|
437
|
+
const safeRecord = (task, source) => {
|
|
438
|
+
try {
|
|
439
|
+
registry.record(task, source);
|
|
440
|
+
} catch (err) {
|
|
441
|
+
logger.warn(`[registry-hook-installer] record failed for task #${task.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const safeUpdate = (task, source) => {
|
|
445
|
+
try {
|
|
446
|
+
registry.update(task, source);
|
|
447
|
+
} catch (err) {
|
|
448
|
+
logger.warn(`[registry-hook-installer] update failed for task #${task.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const safeRemove = (id) => {
|
|
452
|
+
try {
|
|
453
|
+
registry.remove(id);
|
|
454
|
+
} catch (err) {
|
|
455
|
+
logger.warn(`[registry-hook-installer] remove failed for task #${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
const buildSource = () => ({
|
|
459
|
+
sessionId: options.sessionIdProvider?.(),
|
|
460
|
+
...options.staticSource ?? {}
|
|
461
|
+
});
|
|
462
|
+
const afterCreate = {
|
|
463
|
+
name: `${hookName}:after-create`,
|
|
464
|
+
priority,
|
|
465
|
+
execute: async (ctx) => {
|
|
466
|
+
const data = ctx.data;
|
|
467
|
+
if (data?.task)
|
|
468
|
+
safeRecord(data.task, buildSource());
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const afterUpdate = {
|
|
472
|
+
name: `${hookName}:after-update`,
|
|
473
|
+
priority,
|
|
474
|
+
execute: async (ctx) => {
|
|
475
|
+
const data = ctx.data;
|
|
476
|
+
if (!data?.task)
|
|
477
|
+
return;
|
|
478
|
+
const exists = registry.peekById(data.task.id);
|
|
479
|
+
if (exists) {
|
|
480
|
+
safeUpdate(data.task, buildSource());
|
|
481
|
+
} else {
|
|
482
|
+
safeRecord(data.task, buildSource());
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
const afterComplete = {
|
|
487
|
+
name: `${hookName}:after-complete`,
|
|
488
|
+
priority,
|
|
489
|
+
execute: async (ctx) => {
|
|
490
|
+
const data = ctx.data;
|
|
491
|
+
if (data?.task)
|
|
492
|
+
safeUpdate(data.task, buildSource());
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const afterDelete = {
|
|
496
|
+
name: `${hookName}:after-delete`,
|
|
497
|
+
priority,
|
|
498
|
+
execute: async (ctx) => {
|
|
499
|
+
const data = ctx.data;
|
|
500
|
+
if (typeof data?.id === "number")
|
|
501
|
+
safeRemove(data.id);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
taskComponent.registerHook(TaskHookPoints.AFTER_CREATE, afterCreate);
|
|
505
|
+
taskComponent.registerHook(TaskHookPoints.AFTER_UPDATE, afterUpdate);
|
|
506
|
+
taskComponent.registerHook(TaskHookPoints.AFTER_COMPLETE, afterComplete);
|
|
507
|
+
taskComponent.registerHook(TaskHookPoints.AFTER_DELETE, afterDelete);
|
|
508
|
+
return {
|
|
509
|
+
uninstall() {
|
|
510
|
+
if (uninstalled)
|
|
511
|
+
return;
|
|
512
|
+
uninstalled = true;
|
|
513
|
+
taskComponent.unregisterHook(TaskHookPoints.AFTER_CREATE, afterCreate.name);
|
|
514
|
+
taskComponent.unregisterHook(TaskHookPoints.AFTER_UPDATE, afterUpdate.name);
|
|
515
|
+
taskComponent.unregisterHook(TaskHookPoints.AFTER_COMPLETE, afterComplete.name);
|
|
516
|
+
taskComponent.unregisterHook(TaskHookPoints.AFTER_DELETE, afterDelete.name);
|
|
517
|
+
logger.debug(`[registry-hook-installer] uninstalled hooks (name=${hookName})`);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/env/task/task-reuse-registry.ts
|
|
523
|
+
init_logger();
|
|
524
|
+
var logger2 = createLogger("task-reuse-registry");
|
|
525
|
+
var DEFAULT_CONFIG = {
|
|
526
|
+
maxEntries: 500,
|
|
527
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
528
|
+
maxDescriptionChars: 240
|
|
529
|
+
};
|
|
530
|
+
var registries = new WeakMap;
|
|
531
|
+
function getOrCreateTaskReuseRegistry(env) {
|
|
532
|
+
const existing = registries.get(env);
|
|
533
|
+
if (existing)
|
|
534
|
+
return existing.registry;
|
|
535
|
+
const registry = new TaskCreationRegistry(DEFAULT_CONFIG);
|
|
536
|
+
let installHandle = { uninstall: () => {} };
|
|
537
|
+
try {
|
|
538
|
+
const taskComponent = env.getComponent?.("task");
|
|
539
|
+
if (taskComponent?.registerHook) {
|
|
540
|
+
installHandle = installTaskCreationRegistryHooks(taskComponent, registry, {
|
|
541
|
+
sessionIdProvider: () => {
|
|
542
|
+
try {
|
|
543
|
+
return env.currentSessionId ?? undefined;
|
|
544
|
+
} catch {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
staticSource: {
|
|
549
|
+
processId: typeof process !== "undefined" ? process.pid : undefined
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
logger2.debug("[task-reuse-registry] hook installed");
|
|
553
|
+
} else {
|
|
554
|
+
logger2.debug("[task-reuse-registry] no TaskComponent in env; registry stays empty");
|
|
555
|
+
}
|
|
556
|
+
} catch (err) {
|
|
557
|
+
logger2.warn(`[task-reuse-registry] hook install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
558
|
+
}
|
|
559
|
+
const entry = { registry, installHandle };
|
|
560
|
+
registries.set(env, entry);
|
|
561
|
+
return registry;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/env/task/task-reuse-context.ts
|
|
565
|
+
var TOKEN_PATTERNS2 = [
|
|
566
|
+
/\bsk-(?:proj-)?[A-Za-z0-9_-]{16,}\b/g,
|
|
567
|
+
/\bxox[abprs]-[A-Za-z0-9-]{8,}\b/g,
|
|
568
|
+
/\bghp_[A-Za-z0-9]{16,}\b/g,
|
|
569
|
+
/\bgithub_pat_[A-Za-z0-9_]{16,}\b/g,
|
|
570
|
+
/\bBearer\s+[A-Za-z0-9._\-+/=]{12,}\b/g,
|
|
571
|
+
/\bAKIA[0-9A-Z]{16}\b/g
|
|
572
|
+
];
|
|
573
|
+
function redact(s) {
|
|
574
|
+
let out = s;
|
|
575
|
+
for (const pat of TOKEN_PATTERNS2)
|
|
576
|
+
out = out.replace(pat, "<redacted>");
|
|
577
|
+
return out;
|
|
578
|
+
}
|
|
579
|
+
function truncate2(s, max) {
|
|
580
|
+
if (s.length <= max)
|
|
581
|
+
return s;
|
|
582
|
+
if (max <= 1)
|
|
583
|
+
return s.slice(0, max);
|
|
584
|
+
return s.slice(0, max - 1) + "…";
|
|
585
|
+
}
|
|
586
|
+
function buildReuseCandidateSection(options) {
|
|
587
|
+
const limit = options.limit ?? 5;
|
|
588
|
+
const maxChars = options.maxChars ?? 2000;
|
|
589
|
+
const maxDescChars = options.maxDescriptionChars ?? 240;
|
|
590
|
+
const heading = options.heading ?? "Recent task context (reuse candidates)";
|
|
591
|
+
const recent = options.registry.recent({
|
|
592
|
+
scope: options.scope,
|
|
593
|
+
limit,
|
|
594
|
+
statuses: options.statuses,
|
|
595
|
+
excludeIds: options.similarTaskIds ?? [],
|
|
596
|
+
excludeAncestorOf: options.excludeAncestorOf
|
|
597
|
+
});
|
|
598
|
+
if (recent.length === 0) {
|
|
599
|
+
return { markdown: "", candidateIds: [], isEmpty: true };
|
|
600
|
+
}
|
|
601
|
+
const lines = [];
|
|
602
|
+
lines.push(`## ${heading}`);
|
|
603
|
+
lines.push("");
|
|
604
|
+
lines.push("The following tasks were recently created in this process / session and may be related to your current work. " + "Before creating a brand-new task, check whether one of these is the same work continuing. " + "The list is a *suggestion only* — confirm with `task_get <id>` before reusing.");
|
|
605
|
+
lines.push("");
|
|
606
|
+
for (const rec of recent) {
|
|
607
|
+
lines.push(`### Task #${rec.taskId}: ${rec.title}`);
|
|
608
|
+
lines.push(`- Status: ${rec.status}`);
|
|
609
|
+
if (rec.parentTaskId !== undefined && rec.parentTaskId !== null) {
|
|
610
|
+
lines.push(`- Parent: #${rec.parentTaskId}`);
|
|
611
|
+
}
|
|
612
|
+
if (rec.source.subagentType) {
|
|
613
|
+
lines.push(`- Created by: ${rec.source.subagentType}`);
|
|
614
|
+
}
|
|
615
|
+
lines.push(`- Updated: ${rec.updatedAt}`);
|
|
616
|
+
if (rec.project_path) {
|
|
617
|
+
lines.push(`- Project: ${rec.project_path}`);
|
|
618
|
+
}
|
|
619
|
+
if (rec.worktree_path) {
|
|
620
|
+
lines.push(`- Worktree: ${rec.worktree_path}`);
|
|
621
|
+
}
|
|
622
|
+
if (rec.branch) {
|
|
623
|
+
lines.push(`- Branch: ${rec.branch}`);
|
|
624
|
+
}
|
|
625
|
+
if (rec.parentBranch) {
|
|
626
|
+
lines.push(`- Parent branch: ${rec.parentBranch}`);
|
|
627
|
+
}
|
|
628
|
+
let summary;
|
|
629
|
+
try {
|
|
630
|
+
summary = options.registry.getSubtreeSummary(rec.taskId);
|
|
631
|
+
} catch {
|
|
632
|
+
summary = undefined;
|
|
633
|
+
}
|
|
634
|
+
if (summary && summary.descendantCount > 0) {
|
|
635
|
+
lines.push(`- Children: ${summary.descendantCount} ` + `(active: ${summary.activeCount}, completed: ${summary.completedCount}, max depth: ${summary.maxDepth})`);
|
|
636
|
+
}
|
|
637
|
+
const hasActiveChild = summary !== undefined && summary.activeCount > 0;
|
|
638
|
+
if (rec.status === "completed" && hasActiveChild) {
|
|
639
|
+
lines.push(`- ⚠ Has active child — reopening this parent will conflict with running work; prefer creating a child task.`);
|
|
640
|
+
}
|
|
641
|
+
const safeDesc = redact(truncate2(rec.description, maxDescChars));
|
|
642
|
+
if (safeDesc) {
|
|
643
|
+
lines.push(`- Description: ${safeDesc}`);
|
|
644
|
+
}
|
|
645
|
+
if (rec.tags && rec.tags.length > 0) {
|
|
646
|
+
lines.push(`- Tags: ${rec.tags.join(", ")}`);
|
|
647
|
+
}
|
|
648
|
+
lines.push("");
|
|
649
|
+
}
|
|
650
|
+
lines.push("---");
|
|
651
|
+
lines.push("");
|
|
652
|
+
lines.push("### Reuse instructions");
|
|
653
|
+
lines.push("");
|
|
654
|
+
lines.push("1. **First** call `task_get <id>` on the most relevant candidate to read its description, goals, and recent operations.");
|
|
655
|
+
lines.push("2. **If it is the same work continuing** — prefer `task_update` over `task_create`:");
|
|
656
|
+
lines.push(" - Set `status` back to `active` (or `todo`) to reopen a completed task.");
|
|
657
|
+
lines.push(" - Append new context to `description` and `goals_and_expected_deliverables` (do not replace).");
|
|
658
|
+
lines.push(' - Update `current_status` to reflect the new scope, then call `task_operation_create` with `milestone_type: "decision"` to record the continuation.');
|
|
659
|
+
lines.push("3. **Only** if the candidate is clearly a different task, call `task_create` with a distinguishing title and `parent_task_id` if appropriate.");
|
|
660
|
+
lines.push("4. Do **not** create a duplicate task merely because the existing one is already `completed` — reopen it instead.");
|
|
661
|
+
lines.push("5. Do **not** reuse an unrelated candidate just to fill this list. Empty reuse is a valid outcome.");
|
|
662
|
+
lines.push("");
|
|
663
|
+
lines.push("### Decision matrix — three legal paths");
|
|
664
|
+
lines.push("");
|
|
665
|
+
lines.push("Pick exactly **one** of these paths before calling `task_create`. Reopen-first");
|
|
666
|
+
lines.push("remains the default, but the matrix adds two new paths for independent work.");
|
|
667
|
+
lines.push("");
|
|
668
|
+
lines.push("**Path A — Continuation (reopen existing task).** The new work is the SAME");
|
|
669
|
+
lines.push('logical task. Use `task_update(task_id=<id>, { status: "active", ... })` and');
|
|
670
|
+
lines.push("append description/goals. Do **not** create a new task id.");
|
|
671
|
+
lines.push("");
|
|
672
|
+
lines.push("```");
|
|
673
|
+
lines.push('task_update(task_id=<parent_id>, { status: "active", current_status: "..." })');
|
|
674
|
+
lines.push('task_operation_create({ task_id=<parent_id>, milestone_type: "decision",');
|
|
675
|
+
lines.push(' milestone_title: "Reopened for <new-scope>",');
|
|
676
|
+
lines.push(' milestone_description: "<why this is the same work>" })');
|
|
677
|
+
lines.push("```");
|
|
678
|
+
lines.push("");
|
|
679
|
+
lines.push("**Path B — Child task (parent_task_id=<id>).** The new work is related but");
|
|
680
|
+
lines.push("distinct: an independent bug fix, optimization, parallel delivery or");
|
|
681
|
+
lines.push("independent acceptance item under the same feature. Create a child task so");
|
|
682
|
+
lines.push("the audit trail is preserved. Always pair with an audit operation.");
|
|
683
|
+
lines.push("");
|
|
684
|
+
lines.push("```");
|
|
685
|
+
for (const rec of recent) {
|
|
686
|
+
const safeProj = rec.project_path ?? "<project_path>";
|
|
687
|
+
lines.push(`task_create(parent_task_id=${rec.taskId}, title="<distinguishing title>", ` + `project_path="${safeProj}", context=<inherit or override>)`);
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
lines.push('task_operation_create({ task_id=<new_id>, milestone_type: "milestone",');
|
|
691
|
+
lines.push(' milestone_title: "Child of #<parent_id>: <new-scope>",');
|
|
692
|
+
lines.push(' milestone_description: "<why this is a sibling/child, not a reopen>" })');
|
|
693
|
+
lines.push("```");
|
|
694
|
+
lines.push("");
|
|
695
|
+
lines.push("**Path C — New root (no parent_task_id).** The new work is unrelated to any");
|
|
696
|
+
lines.push("existing task. Create a brand-new root task. Use this sparingly — root count");
|
|
697
|
+
lines.push("should stay ≤ ~10 to keep the tree readable.");
|
|
698
|
+
lines.push("");
|
|
699
|
+
lines.push("```");
|
|
700
|
+
lines.push('task_create(parent_task_id=undefined, title="<distinct topic>", project_path="<project_path>", context=<json>)');
|
|
701
|
+
lines.push('task_operation_create({ task_id=<new_id>, milestone_type: "create",');
|
|
702
|
+
lines.push(' milestone_title: "New root: <topic>",');
|
|
703
|
+
lines.push(' milestone_description: "<why this is not a child of any existing task>" })');
|
|
704
|
+
lines.push("```");
|
|
705
|
+
lines.push("");
|
|
706
|
+
lines.push("**Operation audit templates.** Every `task_create`/`task_update` call MUST be");
|
|
707
|
+
lines.push("followed by a `task_operation_create` so the next agent sees *why* the path was");
|
|
708
|
+
lines.push("chosen. Use the appropriate `milestone_type`:");
|
|
709
|
+
lines.push(" - continuation → `decision` (and `progress` once the new scope makes headway)");
|
|
710
|
+
lines.push(" - child task → `milestone` (then `progress` as the child advances)");
|
|
711
|
+
lines.push(" - new root → `create` (then `progress` per phase)");
|
|
712
|
+
lines.push("");
|
|
713
|
+
lines.push("**Project + context inheritance.** For Path B/C, prefer inheriting the");
|
|
714
|
+
lines.push("candidate's `project_path`, `context.worktree_path`, `context.branch` and");
|
|
715
|
+
lines.push("`context.parent_branch` from the parent task. Sensitive keys (commits,");
|
|
716
|
+
lines.push("tokens, secrets) are stripped automatically. Use the helper:");
|
|
717
|
+
lines.push("");
|
|
718
|
+
lines.push("```");
|
|
719
|
+
lines.push("inheritChildContext({ parent: { taskId, projectPath, contextJson, tags }, overrides: {...} })");
|
|
720
|
+
lines.push("```");
|
|
721
|
+
lines.push("");
|
|
722
|
+
let markdown = lines.join(`
|
|
723
|
+
`);
|
|
724
|
+
if (markdown.length > maxChars) {
|
|
725
|
+
markdown = truncate2(markdown, maxChars);
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
markdown,
|
|
729
|
+
candidateIds: recent.map((r) => r.taskId),
|
|
730
|
+
isEmpty: false
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/env/task/delegate/delegate-tool.ts
|
|
735
|
+
var logger3 = createLogger("task:delegate");
|
|
112
736
|
var BackgroundTaskEventTypes = {
|
|
113
737
|
STARTED: "task.background.started",
|
|
114
738
|
PROGRESS: "task.background.progress",
|
|
@@ -202,7 +826,7 @@ function ensureSubAgentRegistered(agentComponent, subagentType, basePrompt, subA
|
|
|
202
826
|
if (registry) {
|
|
203
827
|
const agentDef = registry.get(subagentType);
|
|
204
828
|
if (agentDef?.type === "workflow") {
|
|
205
|
-
|
|
829
|
+
logger3.debug(`[delegate] Skipping registration for workflow agent: ${subagentType}`);
|
|
206
830
|
return null;
|
|
207
831
|
}
|
|
208
832
|
}
|
|
@@ -219,7 +843,7 @@ function ensureSubAgentRegistered(agentComponent, subagentType, basePrompt, subA
|
|
|
219
843
|
agentConfig.deniedTools = deniedTools;
|
|
220
844
|
}
|
|
221
845
|
agentInstance = agentComponent.registerAgent(subagentType, agentConfig);
|
|
222
|
-
|
|
846
|
+
logger3.debug(`[delegate] Registered subagent: ${subagentType}`);
|
|
223
847
|
}
|
|
224
848
|
return agentInstance;
|
|
225
849
|
}
|
|
@@ -316,11 +940,11 @@ class BackgroundTaskManager {
|
|
|
316
940
|
return { runIds: [], stopResults: [] };
|
|
317
941
|
}
|
|
318
942
|
const runIds = Array.from(set);
|
|
319
|
-
|
|
943
|
+
logger3.info(`[BackgroundTaskManager] propagateStopToWorkflowRuns count=${runIds.length} bgTaskId=${bgTaskId}`);
|
|
320
944
|
const stopResults = [];
|
|
321
945
|
for (const runId of runIds) {
|
|
322
946
|
if (typeof this.workflowService?.stopRun !== "function") {
|
|
323
|
-
|
|
947
|
+
logger3.warn(`[BackgroundTaskManager] workflowService not injected; cannot stop ${runId}`);
|
|
324
948
|
stopResults.push({
|
|
325
949
|
runId,
|
|
326
950
|
success: false,
|
|
@@ -333,7 +957,7 @@ class BackgroundTaskManager {
|
|
|
333
957
|
stopResults.push({ runId, success: true });
|
|
334
958
|
} catch (err) {
|
|
335
959
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
336
|
-
|
|
960
|
+
logger3.warn(`[BackgroundTaskManager] workflowService.stopRun(${runId}) failed (likely already terminal): ${errMsg}`);
|
|
337
961
|
stopResults.push({ runId, success: false, error: errMsg });
|
|
338
962
|
}
|
|
339
963
|
}
|
|
@@ -344,7 +968,7 @@ class BackgroundTaskManager {
|
|
|
344
968
|
resolveEntryAgent(subagentType) {
|
|
345
969
|
try {
|
|
346
970
|
if (BackgroundTaskManager.detectWorkflowShortcutCompat(subagentType)) {
|
|
347
|
-
|
|
971
|
+
logger3.info(`[BackgroundTaskManager] Compat rewrite: '${subagentType}' → '${BackgroundTaskManager.DEFAULT_WORKFLOW_ENTRY_AGENT}'`);
|
|
348
972
|
return BackgroundTaskManager.DEFAULT_WORKFLOW_ENTRY_AGENT;
|
|
349
973
|
}
|
|
350
974
|
const agentComponent = this.env?.getComponent?.("agent");
|
|
@@ -355,7 +979,7 @@ class BackgroundTaskManager {
|
|
|
355
979
|
}
|
|
356
980
|
return agent.entryAgent ?? BackgroundTaskManager.DEFAULT_WORKFLOW_ENTRY_AGENT;
|
|
357
981
|
} catch (err) {
|
|
358
|
-
|
|
982
|
+
logger3.warn(`[BackgroundTaskManager] resolveEntryAgent failed for ${subagentType}: ${err}`);
|
|
359
983
|
return;
|
|
360
984
|
}
|
|
361
985
|
}
|
|
@@ -376,7 +1000,7 @@ class BackgroundTaskManager {
|
|
|
376
1000
|
trigger_session_id: parentSessionId
|
|
377
1001
|
}
|
|
378
1002
|
});
|
|
379
|
-
|
|
1003
|
+
logger3.info(`[BackgroundTaskManager] Event published: ${type}`);
|
|
380
1004
|
}
|
|
381
1005
|
}
|
|
382
1006
|
async createTask(options) {
|
|
@@ -398,7 +1022,7 @@ class BackgroundTaskManager {
|
|
|
398
1022
|
}
|
|
399
1023
|
const createdBy = reusedSession?.info?.metadata?.created_by;
|
|
400
1024
|
if (createdBy && createdBy !== "subagent") {
|
|
401
|
-
|
|
1025
|
+
logger3.warn(`[BackgroundTaskManager] Session ${existingSubSessionId} was not created by a sub-agent (created_by=${createdBy}); reusing anyway`);
|
|
402
1026
|
}
|
|
403
1027
|
subSession = reusedSession;
|
|
404
1028
|
try {
|
|
@@ -411,9 +1035,9 @@ class BackgroundTaskManager {
|
|
|
411
1035
|
}
|
|
412
1036
|
});
|
|
413
1037
|
} catch (err) {
|
|
414
|
-
|
|
1038
|
+
logger3.warn(`[BackgroundTaskManager] Failed to update reused session metadata`, { existingSubSessionId, error: err instanceof Error ? err.message : String(err) });
|
|
415
1039
|
}
|
|
416
|
-
|
|
1040
|
+
logger3.info(`[BackgroundTaskManager] Reusing sub session: ${existingSubSessionId} for task ${taskIdGen}`);
|
|
417
1041
|
} else {
|
|
418
1042
|
const metadata = {
|
|
419
1043
|
subagent_type: subagentType,
|
|
@@ -476,7 +1100,7 @@ class BackgroundTaskManager {
|
|
|
476
1100
|
};
|
|
477
1101
|
this.publishBackgroundEvent(BackgroundTaskEventTypes.STARTED, startedPayload, parentSessionId);
|
|
478
1102
|
this.executeTask(taskIdGen, prompt, timeout, parentSessionId).catch((err) => {
|
|
479
|
-
|
|
1103
|
+
logger3.error(`[BackgroundTaskManager] executeTask unhandled rejection`, {
|
|
480
1104
|
taskId: taskIdGen,
|
|
481
1105
|
error: err instanceof Error ? err.message : String(err)
|
|
482
1106
|
});
|
|
@@ -486,7 +1110,7 @@ class BackgroundTaskManager {
|
|
|
486
1110
|
async executeTask(taskId, prompt, timeout, parentSessionId) {
|
|
487
1111
|
const task = this.tasks.get(taskId);
|
|
488
1112
|
if (!task) {
|
|
489
|
-
|
|
1113
|
+
logger3.warn(`[BackgroundTaskManager] executeTask: Task not found`, { taskId });
|
|
490
1114
|
return;
|
|
491
1115
|
}
|
|
492
1116
|
const startedAt = Date.now();
|
|
@@ -505,7 +1129,7 @@ class BackgroundTaskManager {
|
|
|
505
1129
|
const taskRegistry = this.processRegistries.get(taskId);
|
|
506
1130
|
const result = await this.executeWithAbort(subSession, prompt, timeoutMs, taskId, abortController?.signal, task.similarTaskIds, taskRegistry);
|
|
507
1131
|
if (abortController?.signal.aborted) {
|
|
508
|
-
|
|
1132
|
+
logger3.info(`[BackgroundTaskManager] Task was aborted`, { taskId });
|
|
509
1133
|
return;
|
|
510
1134
|
}
|
|
511
1135
|
const completedAt = Date.now();
|
|
@@ -532,7 +1156,7 @@ class BackgroundTaskManager {
|
|
|
532
1156
|
associatedTaskId: task.associatedTaskId
|
|
533
1157
|
};
|
|
534
1158
|
this.publishBackgroundEvent(BackgroundTaskEventTypes.COMPLETED, completedPayload, parentSessionId || task.parentSessionId);
|
|
535
|
-
|
|
1159
|
+
logger3.info(`[BackgroundTaskManager] Task completed successfully`, {
|
|
536
1160
|
taskId,
|
|
537
1161
|
executionTimeMs
|
|
538
1162
|
});
|
|
@@ -584,7 +1208,7 @@ class BackgroundTaskManager {
|
|
|
584
1208
|
this.publishBackgroundEvent(BackgroundTaskEventTypes.FAILED, failedPayload, parentSessionId || task.parentSessionId);
|
|
585
1209
|
}
|
|
586
1210
|
task.error = errorMessage;
|
|
587
|
-
|
|
1211
|
+
logger3.error(`[BackgroundTaskManager] Task execution error`, {
|
|
588
1212
|
taskId,
|
|
589
1213
|
status: task.status,
|
|
590
1214
|
error: errorMessage
|
|
@@ -667,7 +1291,7 @@ ${prompt}
|
|
|
667
1291
|
try {
|
|
668
1292
|
const similarTask = await taskComponent.getTask(similarId);
|
|
669
1293
|
if (!similarTask) {
|
|
670
|
-
|
|
1294
|
+
logger3.warn(`[delegate] Similar task #${similarId} not found, skipping`);
|
|
671
1295
|
continue;
|
|
672
1296
|
}
|
|
673
1297
|
const operations = await taskComponent.listOperations({ taskId: similarId, limit: 10 });
|
|
@@ -693,7 +1317,7 @@ ${prompt}
|
|
|
693
1317
|
}
|
|
694
1318
|
similarTasksSections.push(section);
|
|
695
1319
|
} catch (err) {
|
|
696
|
-
|
|
1320
|
+
logger3.warn(`[delegate] Failed to fetch similar task #${similarId}`, { error: err instanceof Error ? err.message : String(err) });
|
|
697
1321
|
}
|
|
698
1322
|
}
|
|
699
1323
|
if (similarTasksSections.length > 0) {
|
|
@@ -716,12 +1340,35 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
716
1340
|
}
|
|
717
1341
|
}
|
|
718
1342
|
} catch (err) {
|
|
719
|
-
|
|
1343
|
+
logger3.warn(`[delegate] Failed to inject similar tasks reference`, { error: err instanceof Error ? err.message : String(err) });
|
|
720
1344
|
}
|
|
721
1345
|
}
|
|
722
1346
|
if (!agentComponent) {
|
|
723
1347
|
throw new Error("AgentComponent not found");
|
|
724
1348
|
}
|
|
1349
|
+
try {
|
|
1350
|
+
if (this.env) {
|
|
1351
|
+
const reuseRegistry = getOrCreateTaskReuseRegistry(this.env);
|
|
1352
|
+
const reuseSection = buildReuseCandidateSection({
|
|
1353
|
+
registry: reuseRegistry,
|
|
1354
|
+
similarTaskIds: similarTaskIds ?? [],
|
|
1355
|
+
limit: 5,
|
|
1356
|
+
maxChars: 2000,
|
|
1357
|
+
maxDescriptionChars: 240
|
|
1358
|
+
});
|
|
1359
|
+
if (!reuseSection.isEmpty) {
|
|
1360
|
+
fullPrompt += `
|
|
1361
|
+
|
|
1362
|
+
---
|
|
1363
|
+
|
|
1364
|
+
${reuseSection.markdown}`;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
} catch (err) {
|
|
1368
|
+
logger3.warn(`[delegate] Failed to inject reuse candidate section`, {
|
|
1369
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
725
1372
|
const subAgentForRegister = subAgent ? { ...subAgent, allowedTools } : allowedTools ? {
|
|
726
1373
|
id: subagentType,
|
|
727
1374
|
name: subagentType,
|
|
@@ -738,7 +1385,7 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
738
1385
|
try {
|
|
739
1386
|
await processRegistry?.killAll();
|
|
740
1387
|
} catch (err) {
|
|
741
|
-
|
|
1388
|
+
logger3.warn(`[delegate] ProcessRegistry.killAll failed`, {
|
|
742
1389
|
error: err instanceof Error ? err.message : String(err)
|
|
743
1390
|
});
|
|
744
1391
|
}
|
|
@@ -798,7 +1445,7 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
798
1445
|
try {
|
|
799
1446
|
await this.processRegistries.get(taskId)?.killAll();
|
|
800
1447
|
} catch (err) {
|
|
801
|
-
|
|
1448
|
+
logger3.warn(`[delegate] stopTask killAll failed`, {
|
|
802
1449
|
taskId,
|
|
803
1450
|
error: err instanceof Error ? err.message : String(err)
|
|
804
1451
|
});
|
|
@@ -852,7 +1499,7 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
852
1499
|
stoppedTaskIds.push(taskId);
|
|
853
1500
|
}
|
|
854
1501
|
} catch (err) {
|
|
855
|
-
|
|
1502
|
+
logger3.warn(`[BackgroundTaskManager] stopAllForParent failed for ${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
856
1503
|
}
|
|
857
1504
|
}
|
|
858
1505
|
}
|
|
@@ -863,12 +1510,12 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
863
1510
|
try {
|
|
864
1511
|
await this.processRegistries.get(taskId)?.killAll();
|
|
865
1512
|
} catch (err) {
|
|
866
|
-
|
|
1513
|
+
logger3.warn(`[BackgroundTaskManager] forceStopAllAcrossParents killAll failed for ${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
867
1514
|
}
|
|
868
1515
|
try {
|
|
869
1516
|
await this.propagateStopToWorkflowRuns(taskId, "force stop all");
|
|
870
1517
|
} catch (err) {
|
|
871
|
-
|
|
1518
|
+
logger3.warn(`[BackgroundTaskManager] forceStopAllAcrossParents propagateStopToWorkflowRuns failed for ${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
872
1519
|
}
|
|
873
1520
|
let justMarkedStopped = false;
|
|
874
1521
|
if (task.status === "running" || task.status === "pending") {
|
|
@@ -879,7 +1526,7 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
879
1526
|
task.abortController?.abort();
|
|
880
1527
|
justMarkedStopped = true;
|
|
881
1528
|
} catch (err) {
|
|
882
|
-
|
|
1529
|
+
logger3.warn(`[BackgroundTaskManager] forceStopAllAcrossParents mark stopped failed for ${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
883
1530
|
}
|
|
884
1531
|
}
|
|
885
1532
|
if (justMarkedStopped) {
|
|
@@ -903,14 +1550,14 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
903
1550
|
async dispose() {
|
|
904
1551
|
for (const [taskId, controller] of this.abortControllers) {
|
|
905
1552
|
controller.abort();
|
|
906
|
-
|
|
1553
|
+
logger3.debug(`[BackgroundTaskManager] Aborted task: ${taskId}`);
|
|
907
1554
|
}
|
|
908
1555
|
this.abortControllers.clear();
|
|
909
1556
|
for (const [, registry] of this.processRegistries) {
|
|
910
1557
|
try {
|
|
911
1558
|
await registry.killAll();
|
|
912
1559
|
} catch (err) {
|
|
913
|
-
|
|
1560
|
+
logger3.warn(`[BackgroundTaskManager] dispose registry.killAll failed`, {
|
|
914
1561
|
error: err instanceof Error ? err.message : String(err)
|
|
915
1562
|
});
|
|
916
1563
|
}
|
|
@@ -921,7 +1568,7 @@ Use these as guidance to improve task execution efficiency.`;
|
|
|
921
1568
|
}
|
|
922
1569
|
this.workflowRunRegistries.clear();
|
|
923
1570
|
this.tasks.clear();
|
|
924
|
-
|
|
1571
|
+
logger3.info(`[BackgroundTaskManager] Disposed`);
|
|
925
1572
|
}
|
|
926
1573
|
}
|
|
927
1574
|
__legacyDecorateClassTS([
|
|
@@ -1035,7 +1682,7 @@ No need to poll — just wait for the notification and use \`task_get\` with the
|
|
|
1035
1682
|
const params = DelegateToolParameters.parse(args);
|
|
1036
1683
|
const { description, prompt, subagent_type = "general", timeout, reason, similar_task_ids } = params;
|
|
1037
1684
|
const parentSessionId = ctx.session_id || "default";
|
|
1038
|
-
|
|
1685
|
+
logger3.info(`[delegate_task] Called: description=${description}, subagent_type=${subagent_type}`);
|
|
1039
1686
|
const registry2 = getAgentRegistry(taskComponent);
|
|
1040
1687
|
if (!isKnownSubagentType(subagent_type, registry2)) {
|
|
1041
1688
|
return {
|
|
@@ -1083,7 +1730,7 @@ async function handleBackgroundTask(backgroundTaskManager, parentSessionId, desc
|
|
|
1083
1730
|
originalPrompt: prompt,
|
|
1084
1731
|
description
|
|
1085
1732
|
});
|
|
1086
|
-
|
|
1733
|
+
logger3.info(`[delegate_task] Routing workflow subagent '${subagentType}' → entry-agent '${entryAgent}'`);
|
|
1087
1734
|
}
|
|
1088
1735
|
const { taskId: bgProcessId, subSessionId } = await backgroundTaskManager.createTask({
|
|
1089
1736
|
parentSessionId,
|