iosm-cli 0.2.5 → 0.2.6
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/CHANGELOG.md +29 -0
- package/README.md +460 -304
- package/dist/core/agent-profiles.js +1 -1
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/shared-memory.d.ts +14 -0
- package/dist/core/shared-memory.d.ts.map +1 -1
- package/dist/core/shared-memory.js +63 -0
- package/dist/core/shared-memory.js.map +1 -1
- package/dist/core/tools/shared-memory.d.ts.map +1 -1
- package/dist/core/tools/shared-memory.js +45 -5
- package/dist/core/tools/shared-memory.js.map +1 -1
- package/dist/core/tools/task.d.ts +12 -1
- package/dist/core/tools/task.d.ts.map +1 -1
- package/dist/core/tools/task.js +564 -8
- package/dist/core/tools/task.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +129 -4
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/package.json +1 -1
package/dist/core/tools/task.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getTeamRun, updateTeamTaskStatus } from "../agent-teams.js";
|
|
|
6
6
|
import { buildRetrospectiveDirective, classifyFailureCause, formatFailureCauseCounts, isRetrospectiveRetryable, } from "../failure-retrospective.js";
|
|
7
7
|
import { MAX_ORCHESTRATION_AGENTS, MAX_ORCHESTRATION_PARALLEL, MAX_SUBAGENT_DELEGATE_PARALLEL, MAX_SUBAGENT_DELEGATION_DEPTH, MAX_SUBAGENT_DELEGATIONS_PER_TASK, } from "../orchestration-limits.js";
|
|
8
8
|
import { AGENT_PROFILES, isReadOnlyProfileName, isValidProfileName, } from "../agent-profiles.js";
|
|
9
|
+
import { readSharedMemory, summarizeSharedMemoryUsage, writeSharedMemory, } from "../shared-memory.js";
|
|
9
10
|
const taskSchema = Type.Object({
|
|
10
11
|
description: Type.Optional(Type.String({
|
|
11
12
|
description: "Optional short 3-5 word description of what the subagent will do. If omitted, it is derived from prompt.",
|
|
@@ -62,7 +63,7 @@ const systemPromptByProfile = {
|
|
|
62
63
|
explore: "You are a fast read-only codebase explorer. Answer concisely. Never write or edit files.",
|
|
63
64
|
plan: "You are a technical architect. Analyze the codebase and produce a clear implementation plan. Do not write or edit files.",
|
|
64
65
|
iosm: "You are an IOSM execution agent. Use IOSM methodology and keep IOSM artifacts synchronized with implementation.",
|
|
65
|
-
meta: "You are a meta orchestration agent. Your main job is to maximize safe parallel execution through delegates, not to personally do most of the implementation. Start with bounded read-only recon, then form a concrete execution graph: subtasks, delegate subtasks, dependencies, lock domains, and verification steps. The parent agent remains responsible for orchestration and synthesis, so decompose work aggressively instead of collapsing complex work into one worker. For any non-trivial task, orchestration is required: after recon, launch multiple focused delegates instead of continuing manual implementation in the parent agent, avoid direct write/edit work in the parent agent before delegation unless the task is clearly trivial, and do not hand the whole task to one specialist child when independent workstreams exist. If a delegated workstream still contains multiple independent slices, split it again with nested <delegate_task> blocks. Default to aggressive safe parallelism. If the user requested a specific degree of parallelism, honor it when feasible or explain the exact blocker. Use shared_memory as the default coordination channel between delegates: use stable namespaced keys, prefer read-before-write, and use CAS (if_version) for contested updates; reserve append mode for timeline/log keys. When delegation is not used for non-trivial work, explain why in one line and include DELEGATION_IMPOSSIBLE. Enforce test verification for code changes, complete only after all delegated branches are resolved, and explicitly justify any no-code path where tests are skipped.",
|
|
66
|
+
meta: "You are a meta orchestration agent. Your main job is to maximize safe parallel execution through delegates, not to personally do most of the implementation. Start with bounded read-only recon, then form a concrete execution graph: subtasks, delegate subtasks, dependencies, lock domains, and verification steps. The parent agent remains responsible for orchestration and synthesis, so decompose work aggressively instead of collapsing complex work into one worker. For any non-trivial task, orchestration is required: after recon, launch multiple focused delegates instead of continuing manual implementation in the parent agent, avoid direct write/edit work in the parent agent before delegation unless the task is clearly trivial, and do not hand the whole task to one specialist child when independent workstreams exist. If a delegated workstream still contains multiple independent slices, split it again with nested <delegate_task> blocks. Default to aggressive safe parallelism. If the user requested a specific degree of parallelism, honor it when feasible or explain the exact blocker. Use shared_memory as the default coordination channel between delegates: use stable namespaced keys, prefer read-before-write, and use CAS (if_version) for contested updates; reserve append mode for timeline/log keys. When delegation is not used for non-trivial work, explain why in one line and include DELEGATION_IMPOSSIBLE. Enforce test verification for code changes, complete only after all delegated branches are resolved, and explicitly justify any no-code path where tests are skipped. For any metrics (speedup, compliance, conflict counts, quality scores), report only values backed by observed runtime evidence; if evidence is missing, mark the metric as unknown. Do not claim report files/artifacts unless they were produced in this run or verified on disk.",
|
|
66
67
|
iosm_analyst: "You are an IOSM metrics analyst. Analyze .iosm/ artifacts and codebase metrics. Be precise and evidence-based.",
|
|
67
68
|
iosm_verifier: "You are an IOSM verifier. Validate checks and update only required IOSM artifacts with deterministic reasoning.",
|
|
68
69
|
cycle_planner: "You are an IOSM cycle planner. Propose and align cycle goals with measurable outcomes and concrete risks.",
|
|
@@ -360,6 +361,48 @@ function truncateForDelegationContext(text, maxChars = 2200) {
|
|
|
360
361
|
return normalized;
|
|
361
362
|
return `${normalized.slice(0, Math.max(100, maxChars - 3)).trimEnd()}...`;
|
|
362
363
|
}
|
|
364
|
+
function stripDelegatedSectionHeading(section) {
|
|
365
|
+
return section.replace(/^####\s+[^\n]*\n?/i, "").trim();
|
|
366
|
+
}
|
|
367
|
+
function normalizeDelegatedSectionBody(section) {
|
|
368
|
+
return stripDelegatedSectionHeading(section)
|
|
369
|
+
.toLowerCase()
|
|
370
|
+
.replace(/[`"'*_#~[\](){}<>\\|]/g, " ")
|
|
371
|
+
.replace(/[^\w\s]/g, " ")
|
|
372
|
+
.replace(/\s+/g, " ")
|
|
373
|
+
.trim();
|
|
374
|
+
}
|
|
375
|
+
function detectDuplicateDelegatedSections(sections) {
|
|
376
|
+
const duplicatePairs = [];
|
|
377
|
+
const normalizedSections = sections.map((section) => normalizeDelegatedSectionBody(section));
|
|
378
|
+
for (let index = 0; index < normalizedSections.length; index += 1) {
|
|
379
|
+
const current = normalizedSections[index] ?? "";
|
|
380
|
+
if (current.length < 60)
|
|
381
|
+
continue;
|
|
382
|
+
for (let previous = 0; previous < index; previous += 1) {
|
|
383
|
+
const baseline = normalizedSections[previous] ?? "";
|
|
384
|
+
if (baseline.length < 60)
|
|
385
|
+
continue;
|
|
386
|
+
if (current === baseline) {
|
|
387
|
+
duplicatePairs.push({ duplicate: index + 1, original: previous + 1 });
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
const shorter = current.length <= baseline.length ? current : baseline;
|
|
391
|
+
const longer = current.length > baseline.length ? current : baseline;
|
|
392
|
+
if (shorter.length >= 100 && longer.includes(shorter)) {
|
|
393
|
+
const coverage = shorter.length / Math.max(1, longer.length);
|
|
394
|
+
if (coverage >= 0.92) {
|
|
395
|
+
duplicatePairs.push({ duplicate: index + 1, original: previous + 1 });
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
duplicates: duplicatePairs.length,
|
|
403
|
+
duplicatePairs,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
363
406
|
function extractDelegationWorkstreams(text, maxItems) {
|
|
364
407
|
if (maxItems <= 0)
|
|
365
408
|
return [];
|
|
@@ -453,6 +496,75 @@ function buildAutoDelegationPrompt(input) {
|
|
|
453
496
|
objective,
|
|
454
497
|
].join("\n"));
|
|
455
498
|
}
|
|
499
|
+
function uniquifyWorkstreamTitles(titles) {
|
|
500
|
+
const counts = new Map();
|
|
501
|
+
return titles.map((title) => {
|
|
502
|
+
const key = title
|
|
503
|
+
.toLowerCase()
|
|
504
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
505
|
+
.trim();
|
|
506
|
+
const next = (counts.get(key) ?? 0) + 1;
|
|
507
|
+
counts.set(key, next);
|
|
508
|
+
if (next <= 1)
|
|
509
|
+
return title;
|
|
510
|
+
return `${title} (${next})`;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
function semanticallyDeduplicateWorkstreamTitles(titles) {
|
|
514
|
+
const kept = [];
|
|
515
|
+
const tokenized = (value) => new Set(value
|
|
516
|
+
.toLowerCase()
|
|
517
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
518
|
+
.split(/\s+/)
|
|
519
|
+
.map((token) => token.trim())
|
|
520
|
+
.filter((token) => token.length >= 3));
|
|
521
|
+
const jaccard = (left, right) => {
|
|
522
|
+
if (left.size === 0 || right.size === 0)
|
|
523
|
+
return 0;
|
|
524
|
+
let intersection = 0;
|
|
525
|
+
for (const token of left) {
|
|
526
|
+
if (right.has(token))
|
|
527
|
+
intersection += 1;
|
|
528
|
+
}
|
|
529
|
+
const union = left.size + right.size - intersection;
|
|
530
|
+
return union > 0 ? intersection / union : 0;
|
|
531
|
+
};
|
|
532
|
+
for (const candidate of titles) {
|
|
533
|
+
const candidateTokens = tokenized(candidate);
|
|
534
|
+
const duplicate = kept.some((existing) => {
|
|
535
|
+
const existingTokens = tokenized(existing);
|
|
536
|
+
const similarity = jaccard(existingTokens, candidateTokens);
|
|
537
|
+
return similarity >= 0.82;
|
|
538
|
+
});
|
|
539
|
+
if (!duplicate) {
|
|
540
|
+
kept.push(candidate);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return kept;
|
|
544
|
+
}
|
|
545
|
+
function deriveAutoSynthLockKey(streamTitle, ordinal) {
|
|
546
|
+
const pathLike = streamTitle.match(/\b(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\b/)?.[0];
|
|
547
|
+
if (pathLike) {
|
|
548
|
+
const normalizedPath = pathLike
|
|
549
|
+
.replace(/^[./]+/, "")
|
|
550
|
+
.split("/")
|
|
551
|
+
.filter((segment) => segment.length > 0)
|
|
552
|
+
.slice(0, 2)
|
|
553
|
+
.join("/");
|
|
554
|
+
return `auto-synth:${toSharedMemoryKeySegment(normalizedPath, `stream-${ordinal}`)}`;
|
|
555
|
+
}
|
|
556
|
+
if (/\b(auth|rbac|acl|permission)\b/i.test(streamTitle))
|
|
557
|
+
return "auto-synth:auth";
|
|
558
|
+
if (/\b(db|database|sql|storage|schema)\b/i.test(streamTitle))
|
|
559
|
+
return "auto-synth:data";
|
|
560
|
+
if (/\b(ui|ux|frontend|view|component)\b/i.test(streamTitle))
|
|
561
|
+
return "auto-synth:ui";
|
|
562
|
+
if (/\b(test|qa|verification|coverage)\b/i.test(streamTitle))
|
|
563
|
+
return "auto-synth:test";
|
|
564
|
+
if (/\b(api|gateway|route|http)\b/i.test(streamTitle))
|
|
565
|
+
return "auto-synth:api";
|
|
566
|
+
return `auto-synth:${toSharedMemoryKeySegment(streamTitle, `stream-${ordinal}`)}`;
|
|
567
|
+
}
|
|
456
568
|
function synthesizeDelegationRequests(input) {
|
|
457
569
|
const desiredTotal = Math.max(0, Math.min(input.maxDelegations, input.minDelegationsPreferred));
|
|
458
570
|
const missing = Math.max(0, desiredTotal - input.currentDelegates);
|
|
@@ -479,9 +591,13 @@ function synthesizeDelegationRequests(input) {
|
|
|
479
591
|
while (titles.length < missing) {
|
|
480
592
|
titles.push(`Independent workstream ${titles.length + 1}`);
|
|
481
593
|
}
|
|
594
|
+
const semanticallyDeduped = semanticallyDeduplicateWorkstreamTitles(titles);
|
|
595
|
+
while (semanticallyDeduped.length < missing) {
|
|
596
|
+
semanticallyDeduped.push(`Independent workstream ${semanticallyDeduped.length + 1}`);
|
|
597
|
+
}
|
|
598
|
+
const uniqueTitles = uniquifyWorkstreamTitles(semanticallyDeduped);
|
|
482
599
|
const defaultProfile = deriveAutoDelegateProfile(input.baseProfile, input.description, input.prompt);
|
|
483
|
-
|
|
484
|
-
return titles.map((streamTitle, index) => ({
|
|
600
|
+
return uniqueTitles.map((streamTitle, index) => ({
|
|
485
601
|
description: `Auto: ${streamTitle}`,
|
|
486
602
|
profile: defaultProfile,
|
|
487
603
|
agent: pickAutoDelegateAgent(streamTitle, input.availableCustomNames),
|
|
@@ -490,10 +606,10 @@ function synthesizeDelegationRequests(input) {
|
|
|
490
606
|
rootDescription: input.description,
|
|
491
607
|
rootPrompt: input.prompt,
|
|
492
608
|
ordinal: input.currentDelegates + index + 1,
|
|
493
|
-
total: input.currentDelegates +
|
|
609
|
+
total: input.currentDelegates + uniqueTitles.length,
|
|
494
610
|
}),
|
|
495
611
|
cwd: undefined,
|
|
496
|
-
lockKey:
|
|
612
|
+
lockKey: writeCapableProfiles.has(defaultProfile) ? deriveAutoSynthLockKey(streamTitle, index + 1) : undefined,
|
|
497
613
|
model: undefined,
|
|
498
614
|
isolation: undefined,
|
|
499
615
|
dependsOn: undefined,
|
|
@@ -523,6 +639,71 @@ function buildSharedMemoryGuidance(runId, taskId) {
|
|
|
523
639
|
"[/SHARED_MEMORY]",
|
|
524
640
|
].join("\n");
|
|
525
641
|
}
|
|
642
|
+
function toSharedMemoryKeySegment(raw, fallback) {
|
|
643
|
+
const normalized = (raw ?? "")
|
|
644
|
+
.trim()
|
|
645
|
+
.toLowerCase()
|
|
646
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
647
|
+
.replace(/-+/g, "-")
|
|
648
|
+
.replace(/^-+|-+$/g, "");
|
|
649
|
+
if (!normalized)
|
|
650
|
+
return fallback;
|
|
651
|
+
return normalized.slice(0, 64);
|
|
652
|
+
}
|
|
653
|
+
function buildTaskPlanSharedMemoryKey(taskId) {
|
|
654
|
+
return `plan/${toSharedMemoryKeySegment(taskId, "task")}`;
|
|
655
|
+
}
|
|
656
|
+
function buildDelegateFindingSharedMemoryKey(taskId, delegateLabel) {
|
|
657
|
+
return `findings/${toSharedMemoryKeySegment(taskId, "task")}/${toSharedMemoryKeySegment(delegateLabel, "stream")}`;
|
|
658
|
+
}
|
|
659
|
+
function extractClaimCandidates(text, maxItems = 3) {
|
|
660
|
+
const matches = text.match(/\b(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\b/g) ?? [];
|
|
661
|
+
const normalized = new Set();
|
|
662
|
+
for (const match of matches) {
|
|
663
|
+
const cleaned = match
|
|
664
|
+
.replace(/^[./]+/, "")
|
|
665
|
+
.replace(/\/+/g, "/")
|
|
666
|
+
.trim();
|
|
667
|
+
if (!cleaned)
|
|
668
|
+
continue;
|
|
669
|
+
normalized.add(cleaned);
|
|
670
|
+
if (normalized.size >= maxItems)
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
return Array.from(normalized);
|
|
674
|
+
}
|
|
675
|
+
function buildClaimKey(pathLike) {
|
|
676
|
+
const segments = pathLike
|
|
677
|
+
.split("/")
|
|
678
|
+
.map((segment) => toSharedMemoryKeySegment(segment, "segment"))
|
|
679
|
+
.filter((segment) => segment.length > 0);
|
|
680
|
+
if (segments.length === 0) {
|
|
681
|
+
return "claims/unknown";
|
|
682
|
+
}
|
|
683
|
+
return `claims/${segments.slice(0, 6).join("/")}`;
|
|
684
|
+
}
|
|
685
|
+
function buildDelegateCoordinationGuidance(input) {
|
|
686
|
+
const planKey = buildTaskPlanSharedMemoryKey(input.taskId);
|
|
687
|
+
const findingKey = buildDelegateFindingSharedMemoryKey(input.taskId, input.delegateLabel);
|
|
688
|
+
return [
|
|
689
|
+
"[DELEGATE_COORDINATION]",
|
|
690
|
+
`delegate_label: ${input.delegateLabel}`,
|
|
691
|
+
`delegate_description: ${input.delegateDescription}`,
|
|
692
|
+
`read_first_key: ${planKey}`,
|
|
693
|
+
`publish_key: ${findingKey}`,
|
|
694
|
+
"Before heavy repository reads, check current coordination state via shared_memory_read.",
|
|
695
|
+
"Use claims/<path> run-scoped keys with CAS (if_version) to announce file ownership and reduce duplicate reads.",
|
|
696
|
+
"Before responding, publish concise stream findings via shared_memory_write.",
|
|
697
|
+
"Keep ownership strict: do not duplicate sibling streams.",
|
|
698
|
+
"[/DELEGATE_COORDINATION]",
|
|
699
|
+
].join("\n");
|
|
700
|
+
}
|
|
701
|
+
function createSharedMemoryExcerpt(value, maxChars = 1200) {
|
|
702
|
+
const normalized = normalizeSpacing(value);
|
|
703
|
+
if (normalized.length <= maxChars)
|
|
704
|
+
return normalized;
|
|
705
|
+
return `${normalized.slice(0, Math.max(120, maxChars - 3)).trimEnd()}...`;
|
|
706
|
+
}
|
|
526
707
|
function parseDelegationRequests(output, maxRequests) {
|
|
527
708
|
const requests = [];
|
|
528
709
|
const warnings = [];
|
|
@@ -1166,6 +1347,131 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1166
1347
|
taskId: sharedMemoryTaskId,
|
|
1167
1348
|
profile: effectiveProfile,
|
|
1168
1349
|
};
|
|
1350
|
+
const publishTaskCoordinationPlan = async () => {
|
|
1351
|
+
const key = buildTaskPlanSharedMemoryKey(sharedMemoryTaskId);
|
|
1352
|
+
const payload = JSON.stringify({
|
|
1353
|
+
taskId: sharedMemoryTaskId,
|
|
1354
|
+
description,
|
|
1355
|
+
profile: effectiveProfile,
|
|
1356
|
+
objective: createSharedMemoryExcerpt(prompt, 900),
|
|
1357
|
+
});
|
|
1358
|
+
try {
|
|
1359
|
+
await writeSharedMemory(rootSharedMemoryContext, {
|
|
1360
|
+
key,
|
|
1361
|
+
value: payload,
|
|
1362
|
+
scope: "run",
|
|
1363
|
+
mode: "set",
|
|
1364
|
+
}, _signal);
|
|
1365
|
+
}
|
|
1366
|
+
catch (error) {
|
|
1367
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1368
|
+
delegationWarnings.push(`Shared memory plan publish skipped: ${message}`);
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
const publishDelegateFinding = async (input) => {
|
|
1372
|
+
const key = buildDelegateFindingSharedMemoryKey(sharedMemoryTaskId, input.delegateLabel);
|
|
1373
|
+
const payload = JSON.stringify({
|
|
1374
|
+
taskId: sharedMemoryTaskId,
|
|
1375
|
+
delegate: input.delegateLabel,
|
|
1376
|
+
description: input.delegateDescription,
|
|
1377
|
+
profile: input.delegateProfile,
|
|
1378
|
+
status: input.status,
|
|
1379
|
+
summary: createSharedMemoryExcerpt(input.content, 1000),
|
|
1380
|
+
});
|
|
1381
|
+
try {
|
|
1382
|
+
await writeSharedMemory({
|
|
1383
|
+
rootCwd: cwd,
|
|
1384
|
+
runId: sharedMemoryRunId,
|
|
1385
|
+
taskId: sharedMemoryTaskId,
|
|
1386
|
+
delegateId: input.delegateLabel,
|
|
1387
|
+
profile: input.delegateProfile,
|
|
1388
|
+
}, {
|
|
1389
|
+
key,
|
|
1390
|
+
value: payload,
|
|
1391
|
+
scope: "run",
|
|
1392
|
+
mode: "set",
|
|
1393
|
+
}, _signal);
|
|
1394
|
+
}
|
|
1395
|
+
catch (error) {
|
|
1396
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1397
|
+
delegationWarnings.push(`Shared memory finding publish skipped for delegate ${input.delegateLabel}: ${message}`);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
const publishStreamClaims = async (input) => {
|
|
1401
|
+
const claimPaths = extractClaimCandidates(`${input.description}\n${input.promptText}`, 3);
|
|
1402
|
+
if (claimPaths.length === 0)
|
|
1403
|
+
return;
|
|
1404
|
+
for (const claimPath of claimPaths) {
|
|
1405
|
+
const key = buildClaimKey(claimPath);
|
|
1406
|
+
let lastError;
|
|
1407
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1408
|
+
try {
|
|
1409
|
+
const snapshot = await readSharedMemory(rootSharedMemoryContext, {
|
|
1410
|
+
scope: "run",
|
|
1411
|
+
key,
|
|
1412
|
+
includeValues: true,
|
|
1413
|
+
}, _signal);
|
|
1414
|
+
const current = snapshot.items[0];
|
|
1415
|
+
if (!current) {
|
|
1416
|
+
await writeSharedMemory(rootSharedMemoryContext, {
|
|
1417
|
+
key,
|
|
1418
|
+
value: JSON.stringify({
|
|
1419
|
+
path: claimPath,
|
|
1420
|
+
owners: [input.owner],
|
|
1421
|
+
updatedAt: new Date().toISOString(),
|
|
1422
|
+
}),
|
|
1423
|
+
scope: "run",
|
|
1424
|
+
mode: "set",
|
|
1425
|
+
}, _signal);
|
|
1426
|
+
lastError = undefined;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
let existingOwners = [];
|
|
1430
|
+
if (current.value) {
|
|
1431
|
+
try {
|
|
1432
|
+
const parsed = JSON.parse(current.value);
|
|
1433
|
+
if (Array.isArray(parsed.owners)) {
|
|
1434
|
+
existingOwners = parsed.owners
|
|
1435
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
1436
|
+
.slice(0, 12);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
catch {
|
|
1440
|
+
// tolerate malformed payload and overwrite with normalized shape
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
if (existingOwners.includes(input.owner)) {
|
|
1444
|
+
lastError = undefined;
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
const nextOwners = [...existingOwners, input.owner].slice(0, 12);
|
|
1448
|
+
await writeSharedMemory(rootSharedMemoryContext, {
|
|
1449
|
+
key,
|
|
1450
|
+
value: JSON.stringify({
|
|
1451
|
+
path: claimPath,
|
|
1452
|
+
owners: nextOwners,
|
|
1453
|
+
updatedAt: new Date().toISOString(),
|
|
1454
|
+
}),
|
|
1455
|
+
scope: "run",
|
|
1456
|
+
mode: "set",
|
|
1457
|
+
ifVersion: current.version,
|
|
1458
|
+
}, _signal);
|
|
1459
|
+
lastError = undefined;
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
catch (error) {
|
|
1463
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1464
|
+
lastError = message;
|
|
1465
|
+
if (!/version mismatch/i.test(message)) {
|
|
1466
|
+
break;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (lastError) {
|
|
1471
|
+
delegationWarnings.push(`Shared memory claim publish skipped (${key}) for ${input.owner}: ${lastError}`);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1169
1475
|
try {
|
|
1170
1476
|
const runRootPass = async (runPrompt) => {
|
|
1171
1477
|
let emptyAttempt = 0;
|
|
@@ -1266,6 +1572,12 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1266
1572
|
activeTool: undefined,
|
|
1267
1573
|
});
|
|
1268
1574
|
}
|
|
1575
|
+
await publishTaskCoordinationPlan();
|
|
1576
|
+
await publishStreamClaims({
|
|
1577
|
+
owner: "root",
|
|
1578
|
+
description,
|
|
1579
|
+
promptText: prompt,
|
|
1580
|
+
});
|
|
1269
1581
|
const firstPass = await runRootPass(rootPrompt);
|
|
1270
1582
|
output = firstPass.output;
|
|
1271
1583
|
subagentSessionId = firstPass.sessionId;
|
|
@@ -1377,6 +1689,13 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1377
1689
|
}
|
|
1378
1690
|
const causeLabel = cause ? ` [cause=${cause}]` : "";
|
|
1379
1691
|
delegatedSections[index] = `#### ${index + 1}. ${request.description} (${formatDelegateTarget(request)})\nERROR${causeLabel}: ${message}`;
|
|
1692
|
+
void publishDelegateFinding({
|
|
1693
|
+
delegateLabel: String(index + 1),
|
|
1694
|
+
delegateDescription: request.description,
|
|
1695
|
+
delegateProfile: formatDelegateTarget(request),
|
|
1696
|
+
status: "failed",
|
|
1697
|
+
content: message,
|
|
1698
|
+
});
|
|
1380
1699
|
emitProgress({
|
|
1381
1700
|
kind: "subagent_progress",
|
|
1382
1701
|
phase: "running",
|
|
@@ -1419,6 +1738,13 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1419
1738
|
delegatedFailed += 1;
|
|
1420
1739
|
sectionsByIndex[nestedIndex] =
|
|
1421
1740
|
`###### ${requestLabel}. ${nestedRequest.description} (${nestedProfileLabel})\nERROR [cause=${cause}]: ${message}`;
|
|
1741
|
+
void publishDelegateFinding({
|
|
1742
|
+
delegateLabel: requestLabel,
|
|
1743
|
+
delegateDescription: nestedRequest.description,
|
|
1744
|
+
delegateProfile: nestedProfileLabel,
|
|
1745
|
+
status: "failed",
|
|
1746
|
+
content: message,
|
|
1747
|
+
});
|
|
1422
1748
|
};
|
|
1423
1749
|
const runNestedDelegate = async (nestedIndex) => {
|
|
1424
1750
|
const nestedRequest = requests[nestedIndex];
|
|
@@ -1478,7 +1804,17 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1478
1804
|
}
|
|
1479
1805
|
const nestedPromptWithInstructions = nestedRequest.prompt;
|
|
1480
1806
|
const nestedSharedMemoryGuidance = buildSharedMemoryGuidance(sharedMemoryRunId, sharedMemoryTaskId);
|
|
1481
|
-
const
|
|
1807
|
+
const nestedCoordinationGuidance = buildDelegateCoordinationGuidance({
|
|
1808
|
+
taskId: sharedMemoryTaskId,
|
|
1809
|
+
delegateLabel: requestLabel,
|
|
1810
|
+
delegateDescription: nestedRequest.description,
|
|
1811
|
+
});
|
|
1812
|
+
const nestedPrompt = `${nestedPromptWithInstructions}\n\n${nestedSharedMemoryGuidance}\n\n${nestedCoordinationGuidance}`;
|
|
1813
|
+
await publishStreamClaims({
|
|
1814
|
+
owner: requestLabel,
|
|
1815
|
+
description: nestedRequest.description,
|
|
1816
|
+
promptText: nestedRequest.prompt,
|
|
1817
|
+
});
|
|
1482
1818
|
const nestedModelOverride = nestedRequest.model?.trim() || nestedCustomSubagent?.model?.trim() || undefined;
|
|
1483
1819
|
const nestedSharedMemoryContext = {
|
|
1484
1820
|
rootCwd: cwd,
|
|
@@ -1526,6 +1862,13 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1526
1862
|
nestedSection = `${nestedSection}\n\n##### Nested Delegated Subtasks\n\n${deeper.sections.join("\n\n")}`;
|
|
1527
1863
|
}
|
|
1528
1864
|
}
|
|
1865
|
+
await publishDelegateFinding({
|
|
1866
|
+
delegateLabel: requestLabel,
|
|
1867
|
+
delegateDescription: nestedRequest.description,
|
|
1868
|
+
delegateProfile: nestedProfileLabel,
|
|
1869
|
+
status: "done",
|
|
1870
|
+
content: nestedSection,
|
|
1871
|
+
});
|
|
1529
1872
|
sectionsByIndex[nestedIndex] = nestedSection;
|
|
1530
1873
|
}
|
|
1531
1874
|
catch (error) {
|
|
@@ -1685,7 +2028,12 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1685
2028
|
const delegateMeta = formatMetaCheckpoint(options?.getMetaMessages?.());
|
|
1686
2029
|
const childPromptWithInstructions = request.prompt;
|
|
1687
2030
|
const delegateSharedMemoryGuidance = buildSharedMemoryGuidance(sharedMemoryRunId, sharedMemoryTaskId);
|
|
1688
|
-
const
|
|
2031
|
+
const delegateCoordinationGuidance = buildDelegateCoordinationGuidance({
|
|
2032
|
+
taskId: sharedMemoryTaskId,
|
|
2033
|
+
delegateLabel: String(index + 1),
|
|
2034
|
+
delegateDescription: request.description,
|
|
2035
|
+
});
|
|
2036
|
+
const delegatePromptBase = `${childPromptWithInstructions}\n\n${delegateSharedMemoryGuidance}\n\n${delegateCoordinationGuidance}`;
|
|
1689
2037
|
const delegatePrompt = delegateMeta.section && delegateMeta.appliedCount > 0
|
|
1690
2038
|
? `${delegatePromptBase}\n\n${delegateMeta.section}`
|
|
1691
2039
|
: delegatePromptBase;
|
|
@@ -1703,6 +2051,11 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1703
2051
|
delegateItems,
|
|
1704
2052
|
});
|
|
1705
2053
|
}
|
|
2054
|
+
await publishStreamClaims({
|
|
2055
|
+
owner: String(index + 1),
|
|
2056
|
+
description: request.description,
|
|
2057
|
+
promptText: request.prompt,
|
|
2058
|
+
});
|
|
1706
2059
|
const childModelOverride = request.model?.trim() || childCustomSubagent?.model?.trim() || undefined;
|
|
1707
2060
|
const childSharedMemoryContext = {
|
|
1708
2061
|
rootCwd: cwd,
|
|
@@ -1899,6 +2252,13 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1899
2252
|
: normalizedChildOutput;
|
|
1900
2253
|
delegatedSections[index] =
|
|
1901
2254
|
`#### ${index + 1}. ${request.description} (${childProfileLabel})\n${childOutputExcerpt}${nestedSection}`;
|
|
2255
|
+
await publishDelegateFinding({
|
|
2256
|
+
delegateLabel: String(index + 1),
|
|
2257
|
+
delegateDescription: request.description,
|
|
2258
|
+
delegateProfile: childProfileLabel,
|
|
2259
|
+
status: "done",
|
|
2260
|
+
content: `${childOutputExcerpt}${nestedSection}`,
|
|
2261
|
+
});
|
|
1902
2262
|
emitProgress({
|
|
1903
2263
|
kind: "subagent_progress",
|
|
1904
2264
|
phase: "running",
|
|
@@ -2059,11 +2419,205 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
2059
2419
|
}
|
|
2060
2420
|
const normalizedOutput = output.trim().length > 0 ? output.trim() : "(Subagent completed with no output)";
|
|
2061
2421
|
const finalSections = [normalizedOutput];
|
|
2422
|
+
const delegatedBlocks = delegatedSections.filter((section) => typeof section === "string" && section.trim().length > 0);
|
|
2423
|
+
let duplicateDelegatedOutputs = 0;
|
|
2062
2424
|
if (delegatedTasks > 0) {
|
|
2063
2425
|
const header = `### Delegated Subtasks (${delegatedSucceeded}/${delegatedTasks} done)`;
|
|
2064
|
-
|
|
2426
|
+
if (delegatedBlocks.length > 1) {
|
|
2427
|
+
const duplicateReport = detectDuplicateDelegatedSections(delegatedBlocks);
|
|
2428
|
+
duplicateDelegatedOutputs = duplicateReport.duplicates;
|
|
2429
|
+
if (duplicateReport.duplicates > 0) {
|
|
2430
|
+
const duplicateHints = duplicateReport.duplicatePairs
|
|
2431
|
+
.slice(0, 5)
|
|
2432
|
+
.map((pair) => `${pair.duplicate}->${pair.original}`)
|
|
2433
|
+
.join(", ");
|
|
2434
|
+
delegationWarnings.push(`Delegation quality: detected ${duplicateReport.duplicates} near-duplicate delegated output(s) (${duplicateHints}). Consider stricter stream partitioning or stronger shared-memory coordination keys.`);
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2065
2437
|
finalSections.push([header, ...delegatedBlocks].join("\n\n"));
|
|
2066
2438
|
}
|
|
2439
|
+
let sharedMemorySummaryKey;
|
|
2440
|
+
if (orchestrationRunId && orchestrationTaskId) {
|
|
2441
|
+
const summaryExcerpt = normalizedOutput.length > 1200
|
|
2442
|
+
? `${normalizedOutput.slice(0, 1197).trimEnd()}...`
|
|
2443
|
+
: normalizedOutput;
|
|
2444
|
+
const summaryPayload = JSON.stringify({
|
|
2445
|
+
taskId: orchestrationTaskId,
|
|
2446
|
+
description,
|
|
2447
|
+
profile: effectiveProfile,
|
|
2448
|
+
delegated: {
|
|
2449
|
+
total: delegatedTasks,
|
|
2450
|
+
succeeded: delegatedSucceeded,
|
|
2451
|
+
failed: delegatedFailed,
|
|
2452
|
+
duplicatesDetected: duplicateDelegatedOutputs,
|
|
2453
|
+
},
|
|
2454
|
+
retrospective: {
|
|
2455
|
+
attempts: retrospectiveAttempts,
|
|
2456
|
+
recovered: retrospectiveRecovered,
|
|
2457
|
+
failureCauses: failureCauses,
|
|
2458
|
+
},
|
|
2459
|
+
summary: summaryExcerpt,
|
|
2460
|
+
});
|
|
2461
|
+
try {
|
|
2462
|
+
const summaryWrite = await writeSharedMemory({
|
|
2463
|
+
rootCwd: cwd,
|
|
2464
|
+
runId: sharedMemoryRunId,
|
|
2465
|
+
taskId: sharedMemoryTaskId,
|
|
2466
|
+
profile: effectiveProfile,
|
|
2467
|
+
}, {
|
|
2468
|
+
key: `results/${orchestrationTaskId}`,
|
|
2469
|
+
value: summaryPayload,
|
|
2470
|
+
scope: "run",
|
|
2471
|
+
mode: "set",
|
|
2472
|
+
}, _signal);
|
|
2473
|
+
sharedMemorySummaryKey = summaryWrite.key;
|
|
2474
|
+
}
|
|
2475
|
+
catch (error) {
|
|
2476
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2477
|
+
delegationWarnings.push(`Shared memory summary write skipped: ${message}`);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
let coordinationSummary;
|
|
2481
|
+
let claimSummary;
|
|
2482
|
+
let sharedFindingsSnapshot;
|
|
2483
|
+
if (delegatedTasks > 0 || (orchestrationRunId && orchestrationTaskId)) {
|
|
2484
|
+
try {
|
|
2485
|
+
const usage = await summarizeSharedMemoryUsage({
|
|
2486
|
+
rootCwd: cwd,
|
|
2487
|
+
runId: sharedMemoryRunId,
|
|
2488
|
+
taskId: sharedMemoryTaskId,
|
|
2489
|
+
}, _signal);
|
|
2490
|
+
if (delegatedTasks > 1 && usage.currentTaskDelegateWrites === 0) {
|
|
2491
|
+
delegationWarnings.push("No shared_memory writes detected from delegates in this task. Cross-stream coordination may be weak; use stable keys (findings/<stream>, risks/<stream>, plan/<stream>).");
|
|
2492
|
+
}
|
|
2493
|
+
coordinationSummary = {
|
|
2494
|
+
sharedMemoryWrites: usage.totalWrites,
|
|
2495
|
+
currentTaskWrites: usage.currentTaskWrites,
|
|
2496
|
+
currentTaskDelegateWrites: usage.currentTaskDelegateWrites,
|
|
2497
|
+
runScopeWrites: usage.runScopeWrites,
|
|
2498
|
+
taskScopeWrites: usage.taskScopeWrites,
|
|
2499
|
+
duplicatesDetected: duplicateDelegatedOutputs,
|
|
2500
|
+
claimKeysMatched: 0,
|
|
2501
|
+
claimCollisions: 0,
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
catch {
|
|
2505
|
+
// shared-memory summary is advisory; never fail task completion on analytics path.
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
if (delegatedTasks > 0) {
|
|
2509
|
+
try {
|
|
2510
|
+
const claims = await readSharedMemory({
|
|
2511
|
+
rootCwd: cwd,
|
|
2512
|
+
runId: sharedMemoryRunId,
|
|
2513
|
+
taskId: sharedMemoryTaskId,
|
|
2514
|
+
}, {
|
|
2515
|
+
scope: "run",
|
|
2516
|
+
prefix: "claims/",
|
|
2517
|
+
includeValues: true,
|
|
2518
|
+
limit: Math.max(40, delegatedTasks * 8),
|
|
2519
|
+
}, _signal);
|
|
2520
|
+
const collisions = [];
|
|
2521
|
+
for (const item of claims.items) {
|
|
2522
|
+
if (!item.value)
|
|
2523
|
+
continue;
|
|
2524
|
+
try {
|
|
2525
|
+
const parsed = JSON.parse(item.value);
|
|
2526
|
+
if (!Array.isArray(parsed.owners))
|
|
2527
|
+
continue;
|
|
2528
|
+
const owners = Array.from(new Set(parsed.owners
|
|
2529
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
2530
|
+
.map((value) => value.trim())));
|
|
2531
|
+
if (owners.length > 1) {
|
|
2532
|
+
collisions.push({ key: item.key, owners: owners.slice(0, 8) });
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
catch {
|
|
2536
|
+
// ignore malformed claim entries and continue best-effort aggregation
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
claimSummary = {
|
|
2540
|
+
keysMatched: claims.totalMatched,
|
|
2541
|
+
collisions: collisions.length,
|
|
2542
|
+
examples: collisions.slice(0, 6).map((item) => `${item.key}: ${item.owners.join(", ")}`),
|
|
2543
|
+
};
|
|
2544
|
+
if (coordinationSummary) {
|
|
2545
|
+
coordinationSummary.claimKeysMatched = claimSummary.keysMatched;
|
|
2546
|
+
coordinationSummary.claimCollisions = claimSummary.collisions;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
catch {
|
|
2550
|
+
// advisory only
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
if (delegatedTasks > 0) {
|
|
2554
|
+
try {
|
|
2555
|
+
const findingsPrefix = `findings/${toSharedMemoryKeySegment(sharedMemoryTaskId, "task")}/`;
|
|
2556
|
+
const findings = await readSharedMemory({
|
|
2557
|
+
rootCwd: cwd,
|
|
2558
|
+
runId: sharedMemoryRunId,
|
|
2559
|
+
taskId: sharedMemoryTaskId,
|
|
2560
|
+
}, {
|
|
2561
|
+
scope: "run",
|
|
2562
|
+
prefix: findingsPrefix,
|
|
2563
|
+
includeValues: true,
|
|
2564
|
+
limit: Math.max(20, delegatedTasks * 4),
|
|
2565
|
+
}, _signal);
|
|
2566
|
+
const examples = findings.items.slice(0, 6).map((item) => {
|
|
2567
|
+
let excerpt = "";
|
|
2568
|
+
if (item.value) {
|
|
2569
|
+
try {
|
|
2570
|
+
const parsed = JSON.parse(item.value);
|
|
2571
|
+
if (typeof parsed.summary === "string" && parsed.summary.trim().length > 0) {
|
|
2572
|
+
excerpt = parsed.summary.trim().replace(/\s+/g, " ").slice(0, 120);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
catch {
|
|
2576
|
+
excerpt = item.value.trim().replace(/\s+/g, " ").slice(0, 120);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return excerpt.length > 0 ? `${item.key}: ${excerpt}` : item.key;
|
|
2580
|
+
});
|
|
2581
|
+
sharedFindingsSnapshot = {
|
|
2582
|
+
keysMatched: findings.totalMatched,
|
|
2583
|
+
examples,
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
catch {
|
|
2587
|
+
// advisory only
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (coordinationSummary && delegatedTasks > 0) {
|
|
2591
|
+
finalSections.push([
|
|
2592
|
+
"### Orchestration Summary",
|
|
2593
|
+
`- delegated: ${delegatedSucceeded}/${delegatedTasks} succeeded (${delegatedFailed} failed)`,
|
|
2594
|
+
`- duplicate_delegated_outputs: ${coordinationSummary.duplicatesDetected}`,
|
|
2595
|
+
`- shared_memory_writes_total: ${coordinationSummary.sharedMemoryWrites}`,
|
|
2596
|
+
`- shared_memory_writes_current_task: ${coordinationSummary.currentTaskWrites}`,
|
|
2597
|
+
`- shared_memory_delegate_writes_current_task: ${coordinationSummary.currentTaskDelegateWrites}`,
|
|
2598
|
+
`- shared_memory_scope_distribution: run=${coordinationSummary.runScopeWrites}, task=${coordinationSummary.taskScopeWrites}`,
|
|
2599
|
+
`- claims_keys_matched: ${coordinationSummary.claimKeysMatched}`,
|
|
2600
|
+
`- claims_collisions: ${coordinationSummary.claimCollisions}`,
|
|
2601
|
+
sharedMemorySummaryKey ? `- shared_memory_summary_key: ${sharedMemorySummaryKey}` : undefined,
|
|
2602
|
+
]
|
|
2603
|
+
.filter((line) => typeof line === "string" && line.trim().length > 0)
|
|
2604
|
+
.join("\n"));
|
|
2605
|
+
}
|
|
2606
|
+
if (claimSummary && delegatedTasks > 0) {
|
|
2607
|
+
finalSections.push([
|
|
2608
|
+
"### Claim Overlap Snapshot",
|
|
2609
|
+
`- matched_keys: ${claimSummary.keysMatched}`,
|
|
2610
|
+
`- collisions: ${claimSummary.collisions}`,
|
|
2611
|
+
...claimSummary.examples.map((line) => `- ${line}`),
|
|
2612
|
+
].join("\n"));
|
|
2613
|
+
}
|
|
2614
|
+
if (sharedFindingsSnapshot && delegatedTasks > 0) {
|
|
2615
|
+
finalSections.push([
|
|
2616
|
+
"### Shared Findings Snapshot",
|
|
2617
|
+
`- matched_keys: ${sharedFindingsSnapshot.keysMatched}`,
|
|
2618
|
+
...sharedFindingsSnapshot.examples.map((line) => `- ${line}`),
|
|
2619
|
+
].join("\n"));
|
|
2620
|
+
}
|
|
2067
2621
|
if (delegationWarnings.length > 0) {
|
|
2068
2622
|
finalSections.push(`### Delegation Notes\n${delegationWarnings.map((w) => `- ${w}`).join("\n")}`);
|
|
2069
2623
|
}
|
|
@@ -2145,6 +2699,8 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
2145
2699
|
retrospectiveAttempts: retrospectiveAttempts > 0 ? retrospectiveAttempts : undefined,
|
|
2146
2700
|
retrospectiveRecovered: retrospectiveRecovered > 0 ? retrospectiveRecovered : undefined,
|
|
2147
2701
|
failureCauses: hasFailureCauses ? { ...failureCauses } : undefined,
|
|
2702
|
+
coordination: coordinationSummary,
|
|
2703
|
+
sharedMemorySummaryKey,
|
|
2148
2704
|
};
|
|
2149
2705
|
updateTrackedTaskStatus("done");
|
|
2150
2706
|
return { text, details };
|