opencode-swarm 7.79.6 → 7.80.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/brainstorm/SKILL.md +1 -1
- package/.opencode/skills/codebase-review-swarm/SKILL.md +6 -0
- package/.opencode/skills/codebase-review-swarm/references/review-protocol-v8.2.md +5 -5
- package/.opencode/skills/council/SKILL.md +10 -3
- package/.opencode/skills/deep-dive/SKILL.md +4 -2
- package/.opencode/skills/deep-research/SKILL.md +13 -5
- package/.opencode/skills/plan/SKILL.md +1 -1
- package/.opencode/skills/specify/SKILL.md +1 -1
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +18 -0
- package/.opencode/skills/swarm-pr-review/SKILL.md +5 -5
- package/dist/background/completion-observer.d.ts +5 -5
- package/dist/background/pending-delegations.d.ts +58 -17
- package/dist/background/task-envelope.d.ts +5 -0
- package/dist/cli/index.js +187 -18
- package/dist/config/schema.d.ts +2 -0
- package/dist/hooks/knowledge-curator.d.ts +2 -0
- package/dist/hooks/knowledge-types.d.ts +1 -0
- package/dist/index.js +889 -69
- package/dist/memory/schema.d.ts +4 -4
- package/dist/tools/dispatch-lanes.d.ts +118 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/manifest.d.ts +2 -0
- package/dist/tools/tool-metadata.d.ts +8 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.
|
|
72
|
+
version: "7.80.0",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -683,6 +683,14 @@ var init_tool_metadata = __esm(() => {
|
|
|
683
683
|
description: "dispatch multiple read-only exploration/review lanes concurrently and return a structured join result",
|
|
684
684
|
agents: ["architect"]
|
|
685
685
|
},
|
|
686
|
+
dispatch_lanes_async: {
|
|
687
|
+
description: "launch multiple read-only advisory lanes asynchronously and return a batch id for later collection",
|
|
688
|
+
agents: ["architect"]
|
|
689
|
+
},
|
|
690
|
+
collect_lane_results: {
|
|
691
|
+
description: "collect or poll results for a dispatch_lanes_async advisory batch without advancing workflow gates",
|
|
692
|
+
agents: ["architect"]
|
|
693
|
+
},
|
|
686
694
|
summarize_work: {
|
|
687
695
|
description: "emit a short structured summary of completed work (key decisions, assumptions, risks, constraints) at task completion; rolls up per phase for architecture-supervisor review. Advisory, never blocks.",
|
|
688
696
|
agents: [
|
|
@@ -15967,7 +15975,8 @@ var init_schema = __esm(() => {
|
|
|
15967
15975
|
}).optional(),
|
|
15968
15976
|
enrichment: exports_external.object({
|
|
15969
15977
|
max_calls_per_day: exports_external.number().int().min(0).max(1000).default(30),
|
|
15970
|
-
quota_window: exports_external.enum(["utc", "local"]).default("utc")
|
|
15978
|
+
quota_window: exports_external.enum(["utc", "local"]).default("utc"),
|
|
15979
|
+
batch_size: exports_external.number().int().min(1).max(100).optional()
|
|
15971
15980
|
}).default({ max_calls_per_day: 30, quota_window: "utc" })
|
|
15972
15981
|
});
|
|
15973
15982
|
MemoryConfigSchema = exports_external.object({
|
|
@@ -23590,6 +23599,21 @@ function rememberStandardWorktreeDispatch(dispatch) {
|
|
|
23590
23599
|
function hasStandardWorktreeDispatchCapacity() {
|
|
23591
23600
|
return standardWorktreeByCallID.size < MAX_TRACKED_STANDARD_WORKTREE_CALLS;
|
|
23592
23601
|
}
|
|
23602
|
+
function hasInFlightStandardWorktreeDispatch(parentSessionID) {
|
|
23603
|
+
for (const dispatch of standardWorktreeByCallID.values()) {
|
|
23604
|
+
if (dispatch.parentSessionID === parentSessionID)
|
|
23605
|
+
return true;
|
|
23606
|
+
}
|
|
23607
|
+
return false;
|
|
23608
|
+
}
|
|
23609
|
+
function handleStandardWorktreeFailure(parentSessionID, policy, message) {
|
|
23610
|
+
if (policy === "required")
|
|
23611
|
+
throw new Error(message);
|
|
23612
|
+
if (hasInFlightStandardWorktreeDispatch(parentSessionID)) {
|
|
23613
|
+
throw new Error(`STANDARD_WORKTREE_ISOLATION_UNSAFE: ${message} ` + `Sibling coder task(s) are isolated in worktrees for this session, so ` + `dispatching this coder un-isolated in the main tree would risk a ` + `merge-back collision. Wait for in-flight coder task(s) to be reviewed ` + `and merged, then retry.`);
|
|
23614
|
+
}
|
|
23615
|
+
serializeStandardWorktreeDispatches(parentSessionID, message);
|
|
23616
|
+
}
|
|
23593
23617
|
function serializeStandardWorktreeDispatches(sessionID, message) {
|
|
23594
23618
|
rememberStandardWorktreeSerializationSession(sessionID);
|
|
23595
23619
|
const session = ensureAgentSession(sessionID);
|
|
@@ -23643,9 +23667,7 @@ async function precreateStandardWorktreeSession(args2) {
|
|
|
23643
23667
|
const client = swarmState.opencodeClient;
|
|
23644
23668
|
if (!client) {
|
|
23645
23669
|
const message = "STANDARD_WORKTREE_ISOLATION_UNAVAILABLE: OpenCode SDK client is unavailable; standard parallel coder work cannot be isolated.";
|
|
23646
|
-
|
|
23647
|
-
throw new Error(message);
|
|
23648
|
-
serializeStandardWorktreeDispatches(args2.parentSessionID, message);
|
|
23670
|
+
handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
|
|
23649
23671
|
return;
|
|
23650
23672
|
}
|
|
23651
23673
|
const provisionResult = await _internals12.provisionWorktree(args2.directory, args2.taskId, args2.parentSessionID, {
|
|
@@ -23654,10 +23676,8 @@ async function precreateStandardWorktreeSession(args2) {
|
|
|
23654
23676
|
mergeStrategy: worktreeConfig.merge_strategy
|
|
23655
23677
|
});
|
|
23656
23678
|
if ("error" in provisionResult) {
|
|
23657
|
-
const message = `STANDARD_WORKTREE_PROVISION_FAILED: ${provisionResult.error}
|
|
23658
|
-
|
|
23659
|
-
throw new Error(message);
|
|
23660
|
-
serializeStandardWorktreeDispatches(args2.parentSessionID, `${message}.`);
|
|
23679
|
+
const message = `STANDARD_WORKTREE_PROVISION_FAILED: ${provisionResult.error}.`;
|
|
23680
|
+
handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
|
|
23661
23681
|
return;
|
|
23662
23682
|
}
|
|
23663
23683
|
const createResult = await client.session.create({
|
|
@@ -23671,10 +23691,8 @@ async function precreateStandardWorktreeSession(args2) {
|
|
|
23671
23691
|
await _internals12.removeWorktree(provisionResult.worktreePath, args2.directory).catch(() => {});
|
|
23672
23692
|
const createError = createResult.error;
|
|
23673
23693
|
const detail = typeof createError === "string" ? createError : JSON.stringify(createError ?? "missing session id");
|
|
23674
|
-
const message = `STANDARD_WORKTREE_SESSION_CREATE_FAILED: ${detail}
|
|
23675
|
-
|
|
23676
|
-
throw new Error(message);
|
|
23677
|
-
serializeStandardWorktreeDispatches(args2.parentSessionID, `${message}.`);
|
|
23694
|
+
const message = `STANDARD_WORKTREE_SESSION_CREATE_FAILED: ${detail}.`;
|
|
23695
|
+
handleStandardWorktreeFailure(args2.parentSessionID, worktreeConfig.policy, message);
|
|
23678
23696
|
return;
|
|
23679
23697
|
}
|
|
23680
23698
|
args2.outputArgs.task_id = createResult.data.id;
|
|
@@ -42738,7 +42756,47 @@ function parseTaskEnvelope(text) {
|
|
|
42738
42756
|
const state = match[2];
|
|
42739
42757
|
if (!sessionId)
|
|
42740
42758
|
return null;
|
|
42741
|
-
|
|
42759
|
+
const summary = extractTagText(text, "summary");
|
|
42760
|
+
const resultRaw = state === "error" ? extractTagText(text, "task_error") : extractTagText(text, "task_result");
|
|
42761
|
+
const bounded = boundEnvelopeText(resultRaw);
|
|
42762
|
+
return {
|
|
42763
|
+
sessionId,
|
|
42764
|
+
state,
|
|
42765
|
+
...summary !== undefined ? { summary } : {},
|
|
42766
|
+
...state === "error" && bounded ? {
|
|
42767
|
+
errorText: bounded.text,
|
|
42768
|
+
resultChars: bounded.chars,
|
|
42769
|
+
resultTruncated: bounded.truncated
|
|
42770
|
+
} : {},
|
|
42771
|
+
...state !== "error" && bounded ? {
|
|
42772
|
+
resultText: bounded.text,
|
|
42773
|
+
resultChars: bounded.chars,
|
|
42774
|
+
resultTruncated: bounded.truncated
|
|
42775
|
+
} : {}
|
|
42776
|
+
};
|
|
42777
|
+
}
|
|
42778
|
+
function extractTagText(text, tag) {
|
|
42779
|
+
const re = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`);
|
|
42780
|
+
const match = text.match(re);
|
|
42781
|
+
if (!match)
|
|
42782
|
+
return;
|
|
42783
|
+
return match[1];
|
|
42784
|
+
}
|
|
42785
|
+
function boundEnvelopeText(text) {
|
|
42786
|
+
if (text === undefined)
|
|
42787
|
+
return;
|
|
42788
|
+
if (text.length <= MAX_TASK_ENVELOPE_TEXT_CHARS) {
|
|
42789
|
+
return { text, chars: text.length, truncated: false };
|
|
42790
|
+
}
|
|
42791
|
+
const omitted = text.length - MAX_TASK_ENVELOPE_TEXT_CHARS;
|
|
42792
|
+
const suffix = `
|
|
42793
|
+
[... ${omitted} chars truncated by task-envelope ...]`;
|
|
42794
|
+
const maxContent = Math.max(0, MAX_TASK_ENVELOPE_TEXT_CHARS - suffix.length);
|
|
42795
|
+
return {
|
|
42796
|
+
text: `${text.slice(0, maxContent)}${suffix}`,
|
|
42797
|
+
chars: text.length,
|
|
42798
|
+
truncated: true
|
|
42799
|
+
};
|
|
42742
42800
|
}
|
|
42743
42801
|
function extractDispatchIds(output) {
|
|
42744
42802
|
let subagentSessionId = null;
|
|
@@ -42768,7 +42826,7 @@ function extractDispatchIds(output) {
|
|
|
42768
42826
|
}
|
|
42769
42827
|
return { subagentSessionId, jobId };
|
|
42770
42828
|
}
|
|
42771
|
-
var TASK_ENVELOPE_RE;
|
|
42829
|
+
var TASK_ENVELOPE_RE, MAX_TASK_ENVELOPE_TEXT_CHARS = 20000;
|
|
42772
42830
|
var init_task_envelope = __esm(() => {
|
|
42773
42831
|
TASK_ENVELOPE_RE = /<task\s+id="([^"]+)"\s+state="(running|completed|error)"\s*>/;
|
|
42774
42832
|
});
|
|
@@ -42779,7 +42837,10 @@ __export(exports_pending_delegations, {
|
|
|
42779
42837
|
sweepStaleDelegations: () => sweepStaleDelegations,
|
|
42780
42838
|
recordPendingDelegation: () => recordPendingDelegation,
|
|
42781
42839
|
readDelegations: () => readDelegations,
|
|
42840
|
+
findOpenAsyncLaneBatches: () => findOpenAsyncLaneBatches,
|
|
42782
42841
|
findByCorrelationId: () => findByCorrelationId,
|
|
42842
|
+
findByBatchId: () => findByBatchId,
|
|
42843
|
+
appendDelegationTransition: () => appendDelegationTransition,
|
|
42783
42844
|
BACKGROUND_DELEGATIONS_FILE: () => BACKGROUND_DELEGATIONS_FILE
|
|
42784
42845
|
});
|
|
42785
42846
|
import * as fs15 from "node:fs";
|
|
@@ -42836,7 +42897,7 @@ function appendRecord(directory, record2) {
|
|
|
42836
42897
|
async function recordPendingDelegation(directory, input, options = {}) {
|
|
42837
42898
|
const now = Date.now();
|
|
42838
42899
|
const record2 = {
|
|
42839
|
-
schemaVersion: 1,
|
|
42900
|
+
schemaVersion: input.batchId ? 2 : 1,
|
|
42840
42901
|
correlationId: input.correlationId,
|
|
42841
42902
|
jobId: input.jobId,
|
|
42842
42903
|
subagentSessionId: input.subagentSessionId,
|
|
@@ -42848,7 +42909,13 @@ async function recordPendingDelegation(directory, input, options = {}) {
|
|
|
42848
42909
|
evidenceTaskId: input.evidenceTaskId,
|
|
42849
42910
|
status: "pending",
|
|
42850
42911
|
createdAt: now,
|
|
42851
|
-
updatedAt: now
|
|
42912
|
+
updatedAt: now,
|
|
42913
|
+
...input.batchId ? { batchId: input.batchId } : {},
|
|
42914
|
+
...input.laneId ? { laneId: input.laneId } : {},
|
|
42915
|
+
...input.mode ? { mode: input.mode } : {},
|
|
42916
|
+
...input.promptHash ? { promptHash: input.promptHash } : {},
|
|
42917
|
+
...input.workspace ? { workspace: input.workspace } : {},
|
|
42918
|
+
...input.generation !== undefined ? { generation: input.generation } : {}
|
|
42852
42919
|
};
|
|
42853
42920
|
try {
|
|
42854
42921
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
@@ -42863,10 +42930,49 @@ async function recordPendingDelegation(directory, input, options = {}) {
|
|
|
42863
42930
|
return null;
|
|
42864
42931
|
}
|
|
42865
42932
|
}
|
|
42933
|
+
async function appendDelegationTransition(directory, correlationId, transition) {
|
|
42934
|
+
const now = Date.now();
|
|
42935
|
+
try {
|
|
42936
|
+
let next = null;
|
|
42937
|
+
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
42938
|
+
const current = findByCorrelationId(directory, correlationId);
|
|
42939
|
+
if (!current)
|
|
42940
|
+
return;
|
|
42941
|
+
if (isTerminal(current.status) && transition.status !== "consumed") {
|
|
42942
|
+
next = current;
|
|
42943
|
+
return;
|
|
42944
|
+
}
|
|
42945
|
+
next = {
|
|
42946
|
+
...current,
|
|
42947
|
+
schemaVersion: current.schemaVersion === 1 ? 2 : current.schemaVersion,
|
|
42948
|
+
status: transition.status,
|
|
42949
|
+
updatedAt: now,
|
|
42950
|
+
...transition.completedAt !== undefined ? { completedAt: transition.completedAt } : transition.status === "completed" || transition.status === "error" ? { completedAt: now } : {},
|
|
42951
|
+
...transition.result ? { result: transition.result } : {}
|
|
42952
|
+
};
|
|
42953
|
+
appendRecord(directory, next);
|
|
42954
|
+
});
|
|
42955
|
+
return next;
|
|
42956
|
+
} catch (err2) {
|
|
42957
|
+
warn(`[background] appendDelegationTransition failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
42958
|
+
return null;
|
|
42959
|
+
}
|
|
42960
|
+
}
|
|
42961
|
+
function findByBatchId(directory, batchId, opts) {
|
|
42962
|
+
if (!batchId)
|
|
42963
|
+
return [];
|
|
42964
|
+
return readDelegations(directory).filter((record2) => record2.batchId === batchId && (opts?.parentSessionId === undefined || record2.parentSessionId === opts.parentSessionId));
|
|
42965
|
+
}
|
|
42966
|
+
function findOpenAsyncLaneBatches(directory) {
|
|
42967
|
+
return readDelegations(directory).filter((record2) => record2.batchId !== undefined && (record2.status === "pending" || record2.status === "running"));
|
|
42968
|
+
}
|
|
42969
|
+
function isTerminal(status) {
|
|
42970
|
+
return status === "completed" || status === "error" || status === "cancelled" || status === "stale" || status === "consumed";
|
|
42971
|
+
}
|
|
42866
42972
|
function sweepStaleLocked(directory, timeoutMs, now) {
|
|
42867
42973
|
let swept = 0;
|
|
42868
42974
|
for (const record2 of readDelegations(directory)) {
|
|
42869
|
-
if (record2.status !== "pending")
|
|
42975
|
+
if (record2.status !== "pending" && record2.status !== "running")
|
|
42870
42976
|
continue;
|
|
42871
42977
|
if (now - record2.updatedAt <= timeoutMs)
|
|
42872
42978
|
continue;
|
|
@@ -42889,14 +42995,28 @@ async function sweepStaleDelegations(directory, timeoutMs) {
|
|
|
42889
42995
|
return 0;
|
|
42890
42996
|
}
|
|
42891
42997
|
}
|
|
42892
|
-
var BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl", STORE_LOCK_AGENT = "background", STORE_LOCK_TASK = "background-delegations", RecordSchema;
|
|
42998
|
+
var BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl", STORE_LOCK_AGENT = "background", STORE_LOCK_TASK = "background-delegations", ResultSchema, WorkspaceSchema, RecordSchema;
|
|
42893
42999
|
var init_pending_delegations = __esm(() => {
|
|
42894
43000
|
init_zod();
|
|
42895
43001
|
init_lock();
|
|
42896
43002
|
init_utils2();
|
|
42897
43003
|
init_logger();
|
|
43004
|
+
ResultSchema = exports_external.object({
|
|
43005
|
+
text: exports_external.string().optional(),
|
|
43006
|
+
error: exports_external.string().optional(),
|
|
43007
|
+
chars: exports_external.number(),
|
|
43008
|
+
truncated: exports_external.boolean(),
|
|
43009
|
+
digest: exports_external.string()
|
|
43010
|
+
}).strict();
|
|
43011
|
+
WorkspaceSchema = exports_external.object({
|
|
43012
|
+
directory: exports_external.string(),
|
|
43013
|
+
gitHead: exports_external.string().nullable(),
|
|
43014
|
+
dirtyHash: exports_external.string().nullable(),
|
|
43015
|
+
prHeadSha: exports_external.string().nullable(),
|
|
43016
|
+
scope: exports_external.string().nullable()
|
|
43017
|
+
}).strict();
|
|
42898
43018
|
RecordSchema = exports_external.object({
|
|
42899
|
-
schemaVersion: exports_external.literal(1),
|
|
43019
|
+
schemaVersion: exports_external.union([exports_external.literal(1), exports_external.literal(2)]),
|
|
42900
43020
|
correlationId: exports_external.string().min(1),
|
|
42901
43021
|
jobId: exports_external.string().nullable(),
|
|
42902
43022
|
subagentSessionId: exports_external.string().min(1),
|
|
@@ -42906,9 +43026,25 @@ var init_pending_delegations = __esm(() => {
|
|
|
42906
43026
|
swarmPrefixedAgent: exports_external.string(),
|
|
42907
43027
|
planTaskId: exports_external.string().nullable(),
|
|
42908
43028
|
evidenceTaskId: exports_external.string().nullable(),
|
|
42909
|
-
status: exports_external.enum([
|
|
43029
|
+
status: exports_external.enum([
|
|
43030
|
+
"pending",
|
|
43031
|
+
"running",
|
|
43032
|
+
"completed",
|
|
43033
|
+
"error",
|
|
43034
|
+
"cancelled",
|
|
43035
|
+
"stale",
|
|
43036
|
+
"consumed"
|
|
43037
|
+
]),
|
|
42910
43038
|
createdAt: exports_external.number(),
|
|
42911
|
-
updatedAt: exports_external.number()
|
|
43039
|
+
updatedAt: exports_external.number(),
|
|
43040
|
+
batchId: exports_external.string().optional(),
|
|
43041
|
+
laneId: exports_external.string().optional(),
|
|
43042
|
+
mode: exports_external.string().optional(),
|
|
43043
|
+
promptHash: exports_external.string().optional(),
|
|
43044
|
+
workspace: WorkspaceSchema.optional(),
|
|
43045
|
+
generation: exports_external.number().optional(),
|
|
43046
|
+
result: ResultSchema.optional(),
|
|
43047
|
+
completedAt: exports_external.number().optional()
|
|
42912
43048
|
}).strict();
|
|
42913
43049
|
});
|
|
42914
43050
|
|
|
@@ -43470,6 +43606,14 @@ function createDelegationGateHook(config2, directory) {
|
|
|
43470
43606
|
const session = ensureAgentSession(input.sessionID);
|
|
43471
43607
|
if (!session || !session.taskWorkflowStates)
|
|
43472
43608
|
return;
|
|
43609
|
+
const plan = await _internals20.loadPlanJsonOnly(directory);
|
|
43610
|
+
const profile = plan?.execution_profile;
|
|
43611
|
+
const parallelEnabled = profile?.parallelization_enabled === true;
|
|
43612
|
+
const maxConcurrent = profile?.max_concurrent_tasks ?? 10;
|
|
43613
|
+
const effectiveMaxConcurrent = session.maxConcurrencyOverride ?? maxConcurrent;
|
|
43614
|
+
const parallelModeActive = parallelEnabled && effectiveMaxConcurrent > 1 && !hasActiveLeanTurbo(input.sessionID);
|
|
43615
|
+
const planTaskIds = plan ? new Set(plan.phases.flatMap((phase) => phase.tasks.map((t) => t.id))) : new Set;
|
|
43616
|
+
const incomingCoderTaskId = resolveDelegatedPlanTaskId(args2, planTaskIds);
|
|
43473
43617
|
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
43474
43618
|
if (state !== "coder_delegated")
|
|
43475
43619
|
continue;
|
|
@@ -43487,24 +43631,29 @@ function createDelegationGateHook(config2, directory) {
|
|
|
43487
43631
|
if (!isTier3)
|
|
43488
43632
|
continue;
|
|
43489
43633
|
}
|
|
43634
|
+
if (parallelModeActive && incomingCoderTaskId && taskId !== incomingCoderTaskId) {
|
|
43635
|
+
continue;
|
|
43636
|
+
}
|
|
43490
43637
|
throw new Error(`REVIEWER_GATE_VIOLATION: Cannot re-delegate to coder without reviewer delegation. ` + `Task ${taskId} state: coder_delegated. Delegate to reviewer first. ` + `If this is stale state from a prior session, run /swarm reset-session to clear workflow state.`);
|
|
43491
43638
|
}
|
|
43639
|
+
if (parallelModeActive) {
|
|
43640
|
+
let coderDelegatedCount = 0;
|
|
43641
|
+
for (const s of session.taskWorkflowStates.values()) {
|
|
43642
|
+
if (s === "coder_delegated")
|
|
43643
|
+
coderDelegatedCount++;
|
|
43644
|
+
}
|
|
43645
|
+
if (coderDelegatedCount >= effectiveMaxConcurrent) {
|
|
43646
|
+
throw new Error(`PARALLEL_SLOTS_EXHAUSTED: ${coderDelegatedCount} coder task(s) are awaiting review ` + `(max_concurrent_tasks=${effectiveMaxConcurrent}). Dispatch reviewer/test_engineer for an ` + `in-flight coder task before starting another coder.`);
|
|
43647
|
+
}
|
|
43648
|
+
}
|
|
43492
43649
|
if (standardWorktreeSerializationSessions.has(input.sessionID)) {
|
|
43493
43650
|
throw new Error("STANDARD_WORKTREE_ISOLATION_SERIALIZED: prior standard worktree isolation setup failed in this session; wait for the active coder task to finish before dispatching another coder.");
|
|
43494
43651
|
}
|
|
43495
|
-
const plan = await _internals20.loadPlanJsonOnly(directory);
|
|
43496
43652
|
if (!plan)
|
|
43497
43653
|
return;
|
|
43498
|
-
|
|
43499
|
-
const parallelEnabled = profile?.parallelization_enabled === true;
|
|
43500
|
-
const maxConcurrent = profile?.max_concurrent_tasks ?? 10;
|
|
43501
|
-
const effectiveMaxConcurrent = session.maxConcurrencyOverride ?? maxConcurrent;
|
|
43502
|
-
if (!parallelEnabled || effectiveMaxConcurrent <= 1)
|
|
43654
|
+
if (!parallelModeActive)
|
|
43503
43655
|
return;
|
|
43504
|
-
|
|
43505
|
-
return;
|
|
43506
|
-
const planTaskIds = new Set(plan.phases.flatMap((phase) => phase.tasks.map((task) => task.id)));
|
|
43507
|
-
const resolvedTaskId = resolveDelegatedPlanTaskId(args2, planTaskIds);
|
|
43656
|
+
const resolvedTaskId = incomingCoderTaskId;
|
|
43508
43657
|
await precreateStandardWorktreeSession({
|
|
43509
43658
|
config: config2,
|
|
43510
43659
|
directory,
|
|
@@ -62861,10 +63010,12 @@ function isSkillMaturityEligible(entry, opts, outcomes = entry.retrieval_outcome
|
|
|
62861
63010
|
if (outcomeSignal < 0)
|
|
62862
63011
|
return false;
|
|
62863
63012
|
const strongOutcomes = hasStrongSkillOutcomeRecord(outcomes);
|
|
63013
|
+
if (outcomeSignal > 0 && strongOutcomes)
|
|
63014
|
+
return true;
|
|
62864
63015
|
if (entry.confidence < opts.minConfidence && !strongOutcomes)
|
|
62865
63016
|
return false;
|
|
62866
|
-
const
|
|
62867
|
-
return
|
|
63017
|
+
const distinctPhases = new Set((entry.confirmed_by ?? []).map((c) => c.phase_number).filter((p) => typeof p === "number")).size;
|
|
63018
|
+
return distinctPhases >= opts.minConfirmations || strongOutcomes;
|
|
62868
63019
|
}
|
|
62869
63020
|
function jaccardSimilarity(setA, setB) {
|
|
62870
63021
|
const normA = setA.map((s) => s.toLowerCase());
|
|
@@ -65459,7 +65610,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config3, know
|
|
|
65459
65610
|
try {
|
|
65460
65611
|
const skillModule = await Promise.resolve().then(() => (init_skill_generator(), exports_skill_generator));
|
|
65461
65612
|
for (const cand of skillCandidates) {
|
|
65462
|
-
if (cand.confidence < (config3.min_skill_confidence ??
|
|
65613
|
+
if (cand.confidence < (config3.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
|
|
65463
65614
|
continue;
|
|
65464
65615
|
}
|
|
65465
65616
|
await skillModule.generateSkills({
|
|
@@ -67827,6 +67978,35 @@ function buildV3EnrichmentPrompt(lesson, category, tags) {
|
|
|
67827
67978
|
].join(`
|
|
67828
67979
|
`);
|
|
67829
67980
|
}
|
|
67981
|
+
function buildV3BatchEnrichmentPrompt(lessons) {
|
|
67982
|
+
const lessonLines = lessons.map((item, idx) => `${idx + 1}. LESSON: ${item.lesson}
|
|
67983
|
+
CATEGORY: ${item.category}
|
|
67984
|
+
TAGS: ${item.tags.join(", ")}`).join(`
|
|
67985
|
+
`);
|
|
67986
|
+
return [
|
|
67987
|
+
"Convert each prose lesson below into an actionable knowledge directive.",
|
|
67988
|
+
"Output ONLY a JSON array (no code fences, no commentary).",
|
|
67989
|
+
`The array length MUST be exactly ${lessons.length}.`,
|
|
67990
|
+
"Each array element at position i maps to lesson i (1-indexed above).",
|
|
67991
|
+
"",
|
|
67992
|
+
"For EACH element, mandatory requirements:",
|
|
67993
|
+
"- At least ONE scope field non-empty:",
|
|
67994
|
+
' "applies_to_agents": string[] — roles from: architect, coder, reviewer, test_engineer, sme, docs, designer, critic, curator',
|
|
67995
|
+
' "applies_to_tools": string[] — tool names from: edit, write, patch, bash, read, grep, glob',
|
|
67996
|
+
"- At least ONE predicate field non-empty:",
|
|
67997
|
+
' "forbidden_actions": string[] | "required_actions": string[] | "verification_checks": string[]',
|
|
67998
|
+
"",
|
|
67999
|
+
"Optional per element:",
|
|
68000
|
+
'"triggers": string[], "directive_priority": "low" | "medium" | "high" | "critical"',
|
|
68001
|
+
"",
|
|
68002
|
+
"Example array:",
|
|
68003
|
+
'[{"applies_to_agents":["coder"],"required_actions":["run focused tests before commit"],"directive_priority":"high"}]',
|
|
68004
|
+
"",
|
|
68005
|
+
"LESSONS:",
|
|
68006
|
+
lessonLines
|
|
68007
|
+
].join(`
|
|
68008
|
+
`);
|
|
68009
|
+
}
|
|
67830
68010
|
function parseV3EnrichmentResponse(text) {
|
|
67831
68011
|
if (!text || typeof text !== "string") {
|
|
67832
68012
|
return { missing: ["valid JSON object"] };
|
|
@@ -67867,6 +68047,109 @@ function parseV3EnrichmentResponse(text) {
|
|
|
67867
68047
|
}
|
|
67868
68048
|
return { fields };
|
|
67869
68049
|
}
|
|
68050
|
+
function parseV3BatchEnrichmentResponse(text, expectedLength) {
|
|
68051
|
+
const empty = Array.from({ length: expectedLength }, () => null);
|
|
68052
|
+
if (!text || typeof text !== "string") {
|
|
68053
|
+
return { fields: empty, missing: ["valid JSON array"] };
|
|
68054
|
+
}
|
|
68055
|
+
const start2 = text.indexOf("[");
|
|
68056
|
+
const end = text.lastIndexOf("]");
|
|
68057
|
+
if (start2 < 0 || end <= start2) {
|
|
68058
|
+
return { fields: empty, missing: ["valid JSON array"] };
|
|
68059
|
+
}
|
|
68060
|
+
let parsed;
|
|
68061
|
+
try {
|
|
68062
|
+
parsed = JSON.parse(text.slice(start2, end + 1));
|
|
68063
|
+
} catch {
|
|
68064
|
+
return { fields: empty, missing: ["valid JSON array"] };
|
|
68065
|
+
}
|
|
68066
|
+
if (!Array.isArray(parsed)) {
|
|
68067
|
+
return { fields: empty, missing: ["valid JSON array"] };
|
|
68068
|
+
}
|
|
68069
|
+
const fields = Array.from({ length: expectedLength }, () => null);
|
|
68070
|
+
const missing = [];
|
|
68071
|
+
for (let i2 = 0;i2 < expectedLength; i2++) {
|
|
68072
|
+
const item = parsed[i2];
|
|
68073
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
68074
|
+
missing.push(`item ${i2 + 1}: valid JSON object`);
|
|
68075
|
+
continue;
|
|
68076
|
+
}
|
|
68077
|
+
const raw = item;
|
|
68078
|
+
const candidate = {};
|
|
68079
|
+
for (const key of ENRICHMENT_ALLOWED_FIELDS) {
|
|
68080
|
+
if (raw[key] !== undefined) {
|
|
68081
|
+
candidate[key] = raw[key];
|
|
68082
|
+
}
|
|
68083
|
+
}
|
|
68084
|
+
const shape = validateActionableFields(candidate);
|
|
68085
|
+
if (!shape.valid) {
|
|
68086
|
+
missing.push(`item ${i2 + 1}: ${shape.errors.join("; ")}`);
|
|
68087
|
+
continue;
|
|
68088
|
+
}
|
|
68089
|
+
const actionability = validateActionability(candidate);
|
|
68090
|
+
if (!actionability.actionable) {
|
|
68091
|
+
const expected = [];
|
|
68092
|
+
if (actionability.reason === "missing_predicate" || actionability.reason === "missing_predicate_and_scope") {
|
|
68093
|
+
expected.push("a non-empty predicate field (forbidden_actions, required_actions, or verification_checks)");
|
|
68094
|
+
}
|
|
68095
|
+
if (actionability.reason === "missing_scope" || actionability.reason === "missing_predicate_and_scope") {
|
|
68096
|
+
expected.push("a non-empty scope field (applies_to_agents or applies_to_tools)");
|
|
68097
|
+
}
|
|
68098
|
+
missing.push(`item ${i2 + 1}: ${expected.join("; ")}`);
|
|
68099
|
+
continue;
|
|
68100
|
+
}
|
|
68101
|
+
fields[i2] = candidate;
|
|
68102
|
+
}
|
|
68103
|
+
if (parsed.length < expectedLength) {
|
|
68104
|
+
missing.push(`expected ${expectedLength} items but got ${parsed.length}`);
|
|
68105
|
+
} else if (parsed.length > expectedLength) {
|
|
68106
|
+
warn(`[knowledge-curator] parseV3BatchEnrichmentResponse received ${parsed.length} items but expected ${expectedLength}; extras will be discarded`);
|
|
68107
|
+
missing.push(`got ${parsed.length} items but only first ${expectedLength} will be used; extras discarded`);
|
|
68108
|
+
}
|
|
68109
|
+
return { fields, missing };
|
|
68110
|
+
}
|
|
68111
|
+
async function enrichLessonsToV3Batched(params) {
|
|
68112
|
+
const quota = params.quota ?? { maxCalls: 10, window: "utc" };
|
|
68113
|
+
const out2 = Array.from({ length: params.lessons.length }, () => null);
|
|
68114
|
+
const batchSize = params.batchSize ?? ENRICHMENT_BATCH_SIZE;
|
|
68115
|
+
for (let start2 = 0;start2 < params.lessons.length; start2 += batchSize) {
|
|
68116
|
+
const batch = params.lessons.slice(start2, start2 + batchSize);
|
|
68117
|
+
const prompt = buildV3BatchEnrichmentPrompt(batch);
|
|
68118
|
+
let userInput = prompt;
|
|
68119
|
+
let best = Array.from({ length: batch.length }, () => null);
|
|
68120
|
+
let retryHint = "";
|
|
68121
|
+
for (let attempt = 0;attempt < 2; attempt++) {
|
|
68122
|
+
try {
|
|
68123
|
+
const reservation = await reserveQuota(params.directory, {
|
|
68124
|
+
nCalls: 1,
|
|
68125
|
+
maxCalls: quota.maxCalls,
|
|
68126
|
+
window: quota.window,
|
|
68127
|
+
scope: "knowledge-enrichment"
|
|
68128
|
+
});
|
|
68129
|
+
if (!reservation.allowed)
|
|
68130
|
+
break;
|
|
68131
|
+
const response = await params.llmDelegate("", userInput, AbortSignal.timeout(ENRICHMENT_LLM_TIMEOUT_MS));
|
|
68132
|
+
const parsed = parseV3BatchEnrichmentResponse(response, batch.length);
|
|
68133
|
+
best = best.map((current, idx) => current ?? parsed.fields[idx]);
|
|
68134
|
+
const unresolved = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields === null).map((item) => item.idx + 1);
|
|
68135
|
+
if (unresolved.length === 0)
|
|
68136
|
+
break;
|
|
68137
|
+
retryHint = parsed.missing.join("; ");
|
|
68138
|
+
const resolvedList = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields !== null).map((item) => item.idx + 1);
|
|
68139
|
+
const preserveClause = resolvedList.length > 0 ? `Preserve the already-valid entries for items ${resolvedList.join(", ")} exactly as you returned them previously. ` : "";
|
|
68140
|
+
userInput = `${prompt}
|
|
68141
|
+
|
|
68142
|
+
RETRY: your last output still missed valid directives for items ${unresolved.join(", ")}. ${retryHint} ${preserveClause}Return a full JSON array with valid entries for every item.`;
|
|
68143
|
+
} catch (err2) {
|
|
68144
|
+
warn(`[knowledge-curator] v3 batch enrichment attempt ${attempt + 1} failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
68145
|
+
}
|
|
68146
|
+
}
|
|
68147
|
+
for (let i2 = 0;i2 < best.length; i2++) {
|
|
68148
|
+
out2[start2 + i2] = best[i2];
|
|
68149
|
+
}
|
|
68150
|
+
}
|
|
68151
|
+
return out2;
|
|
68152
|
+
}
|
|
67870
68153
|
async function enrichLessonToV3(params) {
|
|
67871
68154
|
const quota = params.quota ?? { maxCalls: 10, window: "utc" };
|
|
67872
68155
|
const prompt = buildV3EnrichmentPrompt(params.lesson, params.category, params.tags);
|
|
@@ -68004,6 +68287,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
68004
68287
|
const snapshotPlusNew = [...snapshot];
|
|
68005
68288
|
const toAdd = [];
|
|
68006
68289
|
const pendingReinforcementIds = new Set;
|
|
68290
|
+
const pendingBatchEnrichment = [];
|
|
68007
68291
|
for (const lesson of lessons) {
|
|
68008
68292
|
const tags = inferTags(lesson);
|
|
68009
68293
|
let category = "process";
|
|
@@ -68066,20 +68350,10 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
68066
68350
|
project_name: projectName,
|
|
68067
68351
|
auto_generated: true
|
|
68068
68352
|
};
|
|
68069
|
-
|
|
68353
|
+
const actionability = validateActionability(entry);
|
|
68070
68354
|
if (!actionability.actionable && options?.llmDelegate) {
|
|
68071
|
-
|
|
68072
|
-
|
|
68073
|
-
llmDelegate: options.llmDelegate,
|
|
68074
|
-
lesson,
|
|
68075
|
-
category,
|
|
68076
|
-
tags,
|
|
68077
|
-
quota: options.enrichmentQuota
|
|
68078
|
-
});
|
|
68079
|
-
if (enriched) {
|
|
68080
|
-
Object.assign(entry, enriched);
|
|
68081
|
-
actionability = validateActionability(entry);
|
|
68082
|
-
}
|
|
68355
|
+
pendingBatchEnrichment.push({ entry, lesson, category, tags });
|
|
68356
|
+
continue;
|
|
68083
68357
|
}
|
|
68084
68358
|
if (!actionability.actionable) {
|
|
68085
68359
|
quarantined++;
|
|
@@ -68096,6 +68370,41 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
68096
68370
|
toAdd.push(entry);
|
|
68097
68371
|
snapshotPlusNew.push(entry);
|
|
68098
68372
|
}
|
|
68373
|
+
if (pendingBatchEnrichment.length > 0 && options?.llmDelegate) {
|
|
68374
|
+
const enrichedBatch = await enrichLessonsToV3Batched({
|
|
68375
|
+
directory,
|
|
68376
|
+
llmDelegate: options.llmDelegate,
|
|
68377
|
+
lessons: pendingBatchEnrichment.map((item) => ({
|
|
68378
|
+
lesson: item.lesson,
|
|
68379
|
+
category: item.category,
|
|
68380
|
+
tags: item.tags
|
|
68381
|
+
})),
|
|
68382
|
+
quota: options.enrichmentQuota,
|
|
68383
|
+
batchSize: config3.enrichment?.batch_size
|
|
68384
|
+
});
|
|
68385
|
+
for (let i2 = 0;i2 < pendingBatchEnrichment.length; i2++) {
|
|
68386
|
+
const pending = pendingBatchEnrichment[i2];
|
|
68387
|
+
const enriched = enrichedBatch[i2];
|
|
68388
|
+
if (enriched) {
|
|
68389
|
+
Object.assign(pending.entry, enriched);
|
|
68390
|
+
}
|
|
68391
|
+
const actionability = validateActionability(pending.entry);
|
|
68392
|
+
if (!actionability.actionable) {
|
|
68393
|
+
quarantined++;
|
|
68394
|
+
try {
|
|
68395
|
+
await appendUnactionable(directory, pending.entry, actionability.reason ?? "unactionable");
|
|
68396
|
+
} catch {}
|
|
68397
|
+
await appendCuratorSkippedEvent(directory, {
|
|
68398
|
+
entry_id: pending.entry.id,
|
|
68399
|
+
lesson: pending.lesson,
|
|
68400
|
+
reason: actionability.reason ?? "unactionable"
|
|
68401
|
+
});
|
|
68402
|
+
continue;
|
|
68403
|
+
}
|
|
68404
|
+
toAdd.push(pending.entry);
|
|
68405
|
+
snapshotPlusNew.push(pending.entry);
|
|
68406
|
+
}
|
|
68407
|
+
}
|
|
68099
68408
|
try {
|
|
68100
68409
|
const insights = await consumeInsightCandidates(directory);
|
|
68101
68410
|
for (const cand of insights) {
|
|
@@ -68299,7 +68608,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
|
|
|
68299
68608
|
};
|
|
68300
68609
|
return safeHook(handler);
|
|
68301
68610
|
}
|
|
68302
|
-
var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals31;
|
|
68611
|
+
var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, ENRICHMENT_BATCH_SIZE = 6, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals31;
|
|
68303
68612
|
var init_knowledge_curator = __esm(() => {
|
|
68304
68613
|
init_skill_improver_quota();
|
|
68305
68614
|
init_synonym_map();
|
|
@@ -95299,7 +95608,7 @@ Present all three items together in a single message. One message, defaults pre-
|
|
|
95299
95608
|
|
|
95300
95609
|
**1. QA Gates** — accept defaults or customize (the eleven gates listed above).
|
|
95301
95610
|
|
|
95302
|
-
**2. Parallel Coders** — "How many coders should run in parallel? (default: 1, range: 1-4)"
|
|
95611
|
+
**2. Parallel Coders** — Parallel coders each run in their own isolated git worktree (a separate working directory on its own branch); each coder's work is committed and merged back to the main tree automatically when it finishes, so concurrent coders never overwrite each other's files. This is safe and faster — but only for tasks whose declared file scopes do NOT overlap. Before you ask, INSPECT the plan's tasks: group the dependency-ready tasks whose file scopes are disjoint, and let your RECOMMENDED count be the number of such independent groups, clamped to the 1-4 range. If task scopes overlap or you cannot determine them, recommend 1 (serial). File-scope disjointness is your recommendation to make, not a runtime-enforced guarantee: if overlapping tasks run in parallel a merge conflict will preserve the work in its worktree and surface an advisory, but it stalls progress — so prefer serial whenever you are unsure. Ask: "How many coders should run in parallel? (default: 1, range: 1-4; my recommendation: <N>, because <independent task groups>)"
|
|
95303
95612
|
|
|
95304
95613
|
**3. Commit Frequency** — "Commit frequency for completed tasks? (default: phase-level only; optional per-task checkpoint commit after each task completion)"
|
|
95305
95614
|
|
|
@@ -95311,7 +95620,7 @@ Wait for the user to answer all three in a single reply. Then apply:
|
|
|
95311
95620
|
## Pending Parallelization Config
|
|
95312
95621
|
- parallelization_enabled: true
|
|
95313
95622
|
- max_concurrent_tasks: <user's number>
|
|
95314
|
-
- council_parallel:
|
|
95623
|
+
- council_parallel: false
|
|
95315
95624
|
- locked: true
|
|
95316
95625
|
- recorded_at: <ISO timestamp>
|
|
95317
95626
|
\`\`\`
|
|
@@ -95648,8 +95957,8 @@ If a tool modifies a file, it is a CODER tool. Delegate.
|
|
|
95648
95957
|
- Rationale: declare_scope persists the allowed set to disk (.swarm/scopes/scope-\${taskId}.json) so it survives cross-process delegation. Without a call, the coder or test_engineer process reads an empty scope and every Edit/Write is denied.
|
|
95649
95958
|
<!-- BEHAVIORAL_GUIDANCE_END -->
|
|
95650
95959
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
95651
|
-
Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required.
|
|
95652
|
-
|
|
95960
|
+
Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required. This exception NEVER applies to coder delegations. Preserve ONE task per coder call.
|
|
95961
|
+
Separate parallel-mode exception (distinct from the Stage B exception above, and the ONLY case where more than one coder may be dispatched before waiting): when an active \`[PARALLEL EXECUTION PROFILE]\` directive is present in your context (parallelization_enabled=true), you MAY dispatch multiple {{AGENT_PREFIX}}coder agents in a single message — up to the stated max_concurrent_tasks — but ONLY for distinct, dependency-ready tasks whose declared file scopes do NOT overlap. Each coder still requires its own \`declare_scope\` call and carries exactly ONE task (Rule 3 still holds: never batch multiple objectives into one coder). Parallel coders each run in an isolated git worktree, so their writes never collide and are merged back automatically. If no \`[PARALLEL EXECUTION PROFILE]\` directive is present, dispatch coders one at a time.
|
|
95653
95962
|
3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
|
|
95654
95963
|
3a. PRE-DELEGATION SCOPE CALL (required): BEFORE every {{AGENT_PREFIX}}coder delegation, you MUST call \`declare_scope\` with { taskId, files } listing the exact file(s) this task will modify (including generated/lockfile paths). No \`declare_scope\` call → no coder delegation. See Rule 1a.
|
|
95655
95964
|
3b. PRE-DELEGATION SCOPE CALL (test_engineer): BEFORE any {{AGENT_PREFIX}}test_engineer delegation that will CREATE or MODIFY test files, you MUST call \`declare_scope\` with { taskId, files } listing the exact test file path(s) to write. Omitting this call leaves the write scope undeclared and will block the write. See Rule 1a.
|
|
@@ -96580,6 +96889,7 @@ RULES:
|
|
|
96580
96889
|
- Respect CONSTRAINT
|
|
96581
96890
|
- No web searches or documentation lookups — but DO use the search tool for cross-codebase pattern lookup before using any function
|
|
96582
96891
|
- Verify all import paths exist before using them
|
|
96892
|
+
- WORKTREE ISOLATION: when the orchestrator runs coders in parallel, you may be working inside an isolated git worktree — a separate working directory on its own branch. Work exactly as normal: read and edit files at the paths you are given. Your changes are committed and merged back to the main tree automatically when you finish. If a merge conflict arises during merge-back, your work is preserved in its worktree and an advisory is surfaced to the orchestrator — your changes are never lost. Do NOT run git worktree/branch/checkout/merge commands yourself, and do NOT switch directories. Stay strictly within your declared FILE scope so coders working in sibling worktrees never collide with you.
|
|
96583
96893
|
|
|
96584
96894
|
## ANTI-HALLUCINATION PROTOCOL (MANDATORY)
|
|
96585
96895
|
Before importing ANY function, type, or class from an existing project module:
|
|
@@ -106828,6 +107138,7 @@ init_worker();
|
|
|
106828
107138
|
init_logger();
|
|
106829
107139
|
init_pending_delegations();
|
|
106830
107140
|
init_task_envelope();
|
|
107141
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
106831
107142
|
function createBackgroundCompletionObserver(opts) {
|
|
106832
107143
|
const { config: config3, directory } = opts;
|
|
106833
107144
|
const event = async (input) => {
|
|
@@ -106852,16 +107163,37 @@ function createBackgroundCompletionObserver(opts) {
|
|
|
106852
107163
|
const pending = findByCorrelationId(directory, envelope.sessionId);
|
|
106853
107164
|
const parentSessionId = typeof part.sessionID === "string" ? part.sessionID : "unknown";
|
|
106854
107165
|
if (!pending) {
|
|
106855
|
-
log(`[background] observed synthetic completion (state=${envelope.state}) for subagent ${envelope.sessionId} in parent ${parentSessionId} with NO matching pending record — ignored
|
|
107166
|
+
log(`[background] observed synthetic completion (state=${envelope.state}) for subagent ${envelope.sessionId} in parent ${parentSessionId} with NO matching pending record — ignored`);
|
|
107167
|
+
return;
|
|
107168
|
+
}
|
|
107169
|
+
if (pending.parentSessionId !== parentSessionId) {
|
|
107170
|
+
warn(`[background] observed synthetic completion for ${envelope.sessionId} with parent mismatch: expected=${pending.parentSessionId} observed=${parentSessionId}; ignored`);
|
|
107171
|
+
return;
|
|
107172
|
+
}
|
|
107173
|
+
if (pending.status !== "pending" && pending.status !== "running") {
|
|
107174
|
+
log(`[background] observed duplicate/late completion for ${envelope.sessionId}; current status=${pending.status}; ignored`);
|
|
106856
107175
|
return;
|
|
106857
107176
|
}
|
|
106858
|
-
|
|
107177
|
+
const text = envelope.state === "error" ? envelope.errorText ?? "" : envelope.resultText ?? "";
|
|
107178
|
+
await appendDelegationTransition(directory, envelope.sessionId, {
|
|
107179
|
+
status: envelope.state === "error" ? "error" : "completed",
|
|
107180
|
+
result: {
|
|
107181
|
+
...envelope.state === "error" ? { error: text } : { text },
|
|
107182
|
+
chars: envelope.resultChars ?? text.length,
|
|
107183
|
+
truncated: envelope.resultTruncated ?? false,
|
|
107184
|
+
digest: digest(text)
|
|
107185
|
+
}
|
|
107186
|
+
});
|
|
107187
|
+
log(`[background] observed trusted completion (state=${envelope.state}) correlated to pending delegation: ` + `agent=${pending.normalizedAgent} task=${pending.evidenceTaskId ?? pending.planTaskId ?? "unknown"} ` + `parent=${pending.parentSessionId} observedParent=${parentSessionId} pendingStatus=${pending.status} ` + "(advisory ledger update only — no gate effect)");
|
|
106859
107188
|
} catch (err2) {
|
|
106860
107189
|
warn(`[background] completion observer error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
106861
107190
|
}
|
|
106862
107191
|
};
|
|
106863
107192
|
return { event };
|
|
106864
107193
|
}
|
|
107194
|
+
function digest(text) {
|
|
107195
|
+
return createHash11("sha256").update(text).digest("hex");
|
|
107196
|
+
}
|
|
106865
107197
|
|
|
106866
107198
|
// src/index.ts
|
|
106867
107199
|
init_pr_subscriptions();
|
|
@@ -117072,7 +117404,7 @@ init_knowledge_events();
|
|
|
117072
117404
|
// src/hooks/knowledge-injector.ts
|
|
117073
117405
|
init_schema();
|
|
117074
117406
|
init_manager();
|
|
117075
|
-
import { createHash as
|
|
117407
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
117076
117408
|
|
|
117077
117409
|
// src/services/run-memory.ts
|
|
117078
117410
|
import * as crypto10 from "node:crypto";
|
|
@@ -117576,7 +117908,7 @@ function createKnowledgeInjectorHook(directory, config3, modelLimitOverrides = {
|
|
|
117576
117908
|
ctx.taskId ?? "",
|
|
117577
117909
|
(ctx.filePaths ?? []).slice(0, 8).join(",")
|
|
117578
117910
|
].join("|");
|
|
117579
|
-
return
|
|
117911
|
+
return createHash15("sha1").update(parts2).digest("hex").slice(0, 16);
|
|
117580
117912
|
}
|
|
117581
117913
|
let lastSeenCacheKey = null;
|
|
117582
117914
|
let cachedInjectionText = null;
|
|
@@ -125572,6 +125904,9 @@ var diff_summary = createSwarmTool({
|
|
|
125572
125904
|
}
|
|
125573
125905
|
});
|
|
125574
125906
|
|
|
125907
|
+
// src/tools/dispatch-lanes.ts
|
|
125908
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
125909
|
+
|
|
125575
125910
|
// node_modules/yocto-queue/index.js
|
|
125576
125911
|
class Node2 {
|
|
125577
125912
|
value;
|
|
@@ -125730,6 +126065,7 @@ function validateConcurrency(concurrency) {
|
|
|
125730
126065
|
|
|
125731
126066
|
// src/tools/dispatch-lanes.ts
|
|
125732
126067
|
init_zod();
|
|
126068
|
+
init_pending_delegations();
|
|
125733
126069
|
init_constants();
|
|
125734
126070
|
init_schema();
|
|
125735
126071
|
|
|
@@ -125794,6 +126130,12 @@ var MAX_TIMEOUT_MS2 = 1800000;
|
|
|
125794
126130
|
var MAX_LANE_OUTPUT_CHARS = 20000;
|
|
125795
126131
|
var MAX_ERROR_CHARS = 200;
|
|
125796
126132
|
var ERROR_TRUNCATION_SUFFIX = "...";
|
|
126133
|
+
var MAX_BATCH_ID_CHARS = 120;
|
|
126134
|
+
var DEFAULT_ASYNC_STALE_TIMEOUT_MS = 30 * 60000;
|
|
126135
|
+
var DEFAULT_COLLECT_TIMEOUT_MS = DEFAULT_ASYNC_STALE_TIMEOUT_MS;
|
|
126136
|
+
var MAX_COLLECT_TIMEOUT_MS = 60 * 60000;
|
|
126137
|
+
var COLLECT_POLL_INTERVAL_MS = 500;
|
|
126138
|
+
var MAX_COLLECT_POLL_INTERVAL_MS = 1e4;
|
|
125797
126139
|
var AGENT_NAME_SEPARATORS = ["_", "-", " "];
|
|
125798
126140
|
var READ_ONLY_LANE_ROLES = new Set([
|
|
125799
126141
|
"explorer",
|
|
@@ -125845,11 +126187,25 @@ var DispatchLanesArgsSchema = exports_external.object({
|
|
|
125845
126187
|
max_concurrent: exports_external.number().int().min(1).max(MAX_LANES).optional().describe("Maximum lanes in flight at once; defaults to lane count"),
|
|
125846
126188
|
timeout_ms: exports_external.number().int().min(10).max(MAX_TIMEOUT_MS2).optional().describe("Per-lane session create/prompt timeout in milliseconds")
|
|
125847
126189
|
});
|
|
126190
|
+
var DispatchLanesAsyncArgsSchema = DispatchLanesArgsSchema.extend({
|
|
126191
|
+
batch_id: exports_external.string().min(1).max(MAX_BATCH_ID_CHARS).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/).optional().describe("Stable async batch id for later collection; generated when omitted"),
|
|
126192
|
+
mode: exports_external.string().min(1).max(80).optional().describe("Advisory workflow mode, such as deep-dive or swarm-pr-review"),
|
|
126193
|
+
pr_head_sha: exports_external.string().min(1).max(80).optional(),
|
|
126194
|
+
scope: exports_external.string().min(1).max(500).optional()
|
|
126195
|
+
});
|
|
126196
|
+
var CollectLaneResultsArgsSchema = exports_external.object({
|
|
126197
|
+
batch_id: exports_external.string().min(1).max(MAX_BATCH_ID_CHARS),
|
|
126198
|
+
wait: exports_external.boolean().optional().describe("Poll until all lanes settle or timeout"),
|
|
126199
|
+
timeout_ms: exports_external.number().int().min(0).max(MAX_COLLECT_TIMEOUT_MS).optional().describe("Total wait budget when wait=true"),
|
|
126200
|
+
include_pending: exports_external.boolean().optional(),
|
|
126201
|
+
cancel_pending: exports_external.boolean().optional().describe("Abort and mark pending/running lanes cancelled")
|
|
126202
|
+
});
|
|
125848
126203
|
var _internals81 = {
|
|
125849
126204
|
getSessionOps: () => swarmState.opencodeClient?.session ?? null,
|
|
125850
126205
|
getGeneratedAgentNames: () => swarmState.generatedAgentNames,
|
|
125851
126206
|
createParallelDispatcher,
|
|
125852
|
-
now: () => Date.now()
|
|
126207
|
+
now: () => Date.now(),
|
|
126208
|
+
sleep: sleep2
|
|
125853
126209
|
};
|
|
125854
126210
|
async function executeDispatchLanes(args2, directory, context = {}) {
|
|
125855
126211
|
const parsed = DispatchLanesArgsSchema.safeParse(args2);
|
|
@@ -125891,6 +126247,322 @@ async function executeDispatchLanes(args2, directory, context = {}) {
|
|
|
125891
126247
|
dispatcher.shutdown();
|
|
125892
126248
|
}
|
|
125893
126249
|
}
|
|
126250
|
+
async function executeDispatchLanesAsync(args2, directory, context = {}) {
|
|
126251
|
+
const parsed = DispatchLanesAsyncArgsSchema.safeParse(args2);
|
|
126252
|
+
if (!parsed.success) {
|
|
126253
|
+
return asyncFailureResult({
|
|
126254
|
+
failure_class: "invalid_args",
|
|
126255
|
+
message: "Invalid dispatch_lanes_async arguments",
|
|
126256
|
+
errors: parsed.error.issues.map((issue3) => `${issue3.path.join(".")}: ${issue3.message}`)
|
|
126257
|
+
});
|
|
126258
|
+
}
|
|
126259
|
+
const duplicateLaneIds = findDuplicateLaneIds(parsed.data.lanes);
|
|
126260
|
+
if (duplicateLaneIds.length > 0) {
|
|
126261
|
+
return asyncFailureResult({
|
|
126262
|
+
failure_class: "invalid_args",
|
|
126263
|
+
message: "Lane IDs must be unique within one dispatch_lanes_async batch",
|
|
126264
|
+
errors: duplicateLaneIds.map((id) => `Duplicate lane id: ${id}`)
|
|
126265
|
+
});
|
|
126266
|
+
}
|
|
126267
|
+
const session = _internals81.getSessionOps();
|
|
126268
|
+
if (!session || typeof session.promptAsync !== "function") {
|
|
126269
|
+
return asyncFailureResult({
|
|
126270
|
+
failure_class: "no_client",
|
|
126271
|
+
message: "OpenCode session promptAsync client is not available"
|
|
126272
|
+
});
|
|
126273
|
+
}
|
|
126274
|
+
const lanes = parsed.data.lanes;
|
|
126275
|
+
const batchId = parsed.data.batch_id ?? makeBatchId();
|
|
126276
|
+
if (findByBatchId(directory, batchId).length > 0) {
|
|
126277
|
+
return asyncFailureResult({
|
|
126278
|
+
failure_class: "invalid_args",
|
|
126279
|
+
message: `Async lane batch already exists: ${batchId}`,
|
|
126280
|
+
errors: [`batch_id must be unique: ${batchId}`]
|
|
126281
|
+
});
|
|
126282
|
+
}
|
|
126283
|
+
const maxConcurrent = Math.min(parsed.data.max_concurrent ?? lanes.length, lanes.length, MAX_LANES);
|
|
126284
|
+
const timeoutMs = parsed.data.timeout_ms ?? DEFAULT_TIMEOUT_MS3;
|
|
126285
|
+
const dispatcher = _internals81.createParallelDispatcher({
|
|
126286
|
+
enabled: true,
|
|
126287
|
+
maxConcurrentTasks: maxConcurrent,
|
|
126288
|
+
evidenceLockTimeoutMs: 0
|
|
126289
|
+
});
|
|
126290
|
+
const limit = pLimit(maxConcurrent);
|
|
126291
|
+
try {
|
|
126292
|
+
const laneResults = await Promise.all(lanes.map((lane) => limit(() => launchAsyncLane({
|
|
126293
|
+
session,
|
|
126294
|
+
dispatcher,
|
|
126295
|
+
lane,
|
|
126296
|
+
directory,
|
|
126297
|
+
timeoutMs,
|
|
126298
|
+
context,
|
|
126299
|
+
batchId,
|
|
126300
|
+
mode: parsed.data.mode,
|
|
126301
|
+
prHeadSha: parsed.data.pr_head_sha,
|
|
126302
|
+
scope: parsed.data.scope
|
|
126303
|
+
}))));
|
|
126304
|
+
const failed = laneResults.filter((lane) => lane.status === "failed");
|
|
126305
|
+
const rejected = laneResults.filter((lane) => lane.status === "rejected");
|
|
126306
|
+
const pending = laneResults.filter((lane) => lane.status === "pending");
|
|
126307
|
+
return {
|
|
126308
|
+
success: failed.length === 0 && rejected.length === 0,
|
|
126309
|
+
batch_id: batchId,
|
|
126310
|
+
dispatched: laneResults.length,
|
|
126311
|
+
pending: pending.length,
|
|
126312
|
+
failed: failed.length,
|
|
126313
|
+
rejected: rejected.length,
|
|
126314
|
+
max_concurrent: maxConcurrent,
|
|
126315
|
+
timeout_ms: timeoutMs,
|
|
126316
|
+
lane_results: laneResults
|
|
126317
|
+
};
|
|
126318
|
+
} finally {
|
|
126319
|
+
dispatcher.shutdown();
|
|
126320
|
+
}
|
|
126321
|
+
}
|
|
126322
|
+
async function executeCollectLaneResults(args2, directory, context = {}) {
|
|
126323
|
+
const parsed = CollectLaneResultsArgsSchema.safeParse(args2);
|
|
126324
|
+
if (!parsed.success) {
|
|
126325
|
+
return collectFailureResult({
|
|
126326
|
+
failure_class: "invalid_args",
|
|
126327
|
+
batch_id: "",
|
|
126328
|
+
message: "Invalid collect_lane_results arguments",
|
|
126329
|
+
errors: parsed.error.issues.map((issue3) => `${issue3.path.join(".")}: ${issue3.message}`)
|
|
126330
|
+
});
|
|
126331
|
+
}
|
|
126332
|
+
const session = _internals81.getSessionOps();
|
|
126333
|
+
if (!session || typeof session.messages !== "function") {
|
|
126334
|
+
return collectFailureResult({
|
|
126335
|
+
failure_class: "no_client",
|
|
126336
|
+
batch_id: parsed.data.batch_id,
|
|
126337
|
+
message: "OpenCode session messages client is not available"
|
|
126338
|
+
});
|
|
126339
|
+
}
|
|
126340
|
+
const timeoutMs = parsed.data.timeout_ms ?? DEFAULT_COLLECT_TIMEOUT_MS;
|
|
126341
|
+
const deadline = _internals81.now() + timeoutMs;
|
|
126342
|
+
const batchFilter = context.sessionID !== undefined ? { parentSessionId: context.sessionID } : undefined;
|
|
126343
|
+
await sweepStaleDelegations(directory, DEFAULT_ASYNC_STALE_TIMEOUT_MS);
|
|
126344
|
+
let records = findByBatchId(directory, parsed.data.batch_id, batchFilter);
|
|
126345
|
+
if (records.length === 0) {
|
|
126346
|
+
return collectFailureResult({
|
|
126347
|
+
failure_class: "not_found",
|
|
126348
|
+
batch_id: parsed.data.batch_id,
|
|
126349
|
+
message: `No async lane batch found for ${parsed.data.batch_id}`
|
|
126350
|
+
});
|
|
126351
|
+
}
|
|
126352
|
+
let keepPolling = true;
|
|
126353
|
+
let pollIntervalMs = COLLECT_POLL_INTERVAL_MS;
|
|
126354
|
+
while (keepPolling) {
|
|
126355
|
+
await collectOnce(session, directory, records, parsed.data.cancel_pending === true);
|
|
126356
|
+
await sweepStaleDelegations(directory, DEFAULT_ASYNC_STALE_TIMEOUT_MS);
|
|
126357
|
+
records = findByBatchId(directory, parsed.data.batch_id, batchFilter);
|
|
126358
|
+
if (allSettled(records) || parsed.data.wait !== true) {
|
|
126359
|
+
keepPolling = false;
|
|
126360
|
+
continue;
|
|
126361
|
+
}
|
|
126362
|
+
if (_internals81.now() >= deadline) {
|
|
126363
|
+
keepPolling = false;
|
|
126364
|
+
continue;
|
|
126365
|
+
}
|
|
126366
|
+
await _internals81.sleep(Math.min(pollIntervalMs, Math.max(0, deadline - _internals81.now())));
|
|
126367
|
+
pollIntervalMs = nextCollectPollInterval(pollIntervalMs);
|
|
126368
|
+
}
|
|
126369
|
+
return buildCollectResult(parsed.data.batch_id, records, parsed.data.include_pending === true);
|
|
126370
|
+
}
|
|
126371
|
+
async function launchAsyncLane(args2) {
|
|
126372
|
+
const validation2 = validateLaneAgent(args2.lane.agent, args2.context);
|
|
126373
|
+
const role = validation2.role;
|
|
126374
|
+
const startedAt = isoNow();
|
|
126375
|
+
if (!validation2.ok) {
|
|
126376
|
+
return {
|
|
126377
|
+
id: args2.lane.id,
|
|
126378
|
+
agent: args2.lane.agent,
|
|
126379
|
+
role,
|
|
126380
|
+
status: "rejected",
|
|
126381
|
+
started_at: startedAt,
|
|
126382
|
+
completed_at: isoNow(),
|
|
126383
|
+
error: validation2.error
|
|
126384
|
+
};
|
|
126385
|
+
}
|
|
126386
|
+
const decision = args2.dispatcher.dispatch(args2.lane.id);
|
|
126387
|
+
if (decision.action !== "dispatch") {
|
|
126388
|
+
return {
|
|
126389
|
+
id: args2.lane.id,
|
|
126390
|
+
agent: args2.lane.agent,
|
|
126391
|
+
role,
|
|
126392
|
+
status: "failed",
|
|
126393
|
+
started_at: startedAt,
|
|
126394
|
+
completed_at: isoNow(),
|
|
126395
|
+
error: `dispatcher ${decision.action}: ${decision.reason}`
|
|
126396
|
+
};
|
|
126397
|
+
}
|
|
126398
|
+
try {
|
|
126399
|
+
const createTimeoutMessage = `Lane "${args2.lane.id}" session.create timed out after ${args2.timeoutMs}ms`;
|
|
126400
|
+
const createPromise = args2.session.create({
|
|
126401
|
+
query: { directory: args2.directory }
|
|
126402
|
+
});
|
|
126403
|
+
let createTimedOut = false;
|
|
126404
|
+
createPromise.then((createResult2) => {
|
|
126405
|
+
if (createTimedOut && createResult2.data?.id) {
|
|
126406
|
+
scheduleSessionCleanup(args2.session, createResult2.data.id);
|
|
126407
|
+
}
|
|
126408
|
+
}).catch(() => {
|
|
126409
|
+
return;
|
|
126410
|
+
});
|
|
126411
|
+
const createResult = await withTimeout2(createPromise, args2.timeoutMs, createTimeoutMessage).catch((error93) => {
|
|
126412
|
+
if (formatError3(error93) === createTimeoutMessage) {
|
|
126413
|
+
createTimedOut = true;
|
|
126414
|
+
}
|
|
126415
|
+
throw error93;
|
|
126416
|
+
});
|
|
126417
|
+
const sessionId = createResult.data?.id;
|
|
126418
|
+
if (!sessionId) {
|
|
126419
|
+
return failedLane(args2.lane, role, startedAt, `session.create failed: ${formatError3(createResult.error)}`, decision.slot.slotId, decision.slot.runId);
|
|
126420
|
+
}
|
|
126421
|
+
const pendingRecord = await recordPendingDelegation(args2.directory, {
|
|
126422
|
+
correlationId: sessionId,
|
|
126423
|
+
jobId: null,
|
|
126424
|
+
subagentSessionId: sessionId,
|
|
126425
|
+
parentSessionId: args2.context.sessionID ?? `dispatch_lanes_async:${args2.batchId}`,
|
|
126426
|
+
callID: args2.batchId,
|
|
126427
|
+
normalizedAgent: role,
|
|
126428
|
+
swarmPrefixedAgent: args2.lane.agent,
|
|
126429
|
+
planTaskId: null,
|
|
126430
|
+
evidenceTaskId: null,
|
|
126431
|
+
batchId: args2.batchId,
|
|
126432
|
+
laneId: args2.lane.id,
|
|
126433
|
+
mode: args2.mode ?? "advisory",
|
|
126434
|
+
promptHash: promptHash(args2.lane, args2.directory, args2.batchId),
|
|
126435
|
+
workspace: {
|
|
126436
|
+
directory: args2.directory,
|
|
126437
|
+
gitHead: null,
|
|
126438
|
+
dirtyHash: null,
|
|
126439
|
+
prHeadSha: args2.prHeadSha ?? null,
|
|
126440
|
+
scope: args2.scope ?? null
|
|
126441
|
+
},
|
|
126442
|
+
generation: 1
|
|
126443
|
+
}, { staleTimeoutMs: DEFAULT_ASYNC_STALE_TIMEOUT_MS });
|
|
126444
|
+
if (!pendingRecord) {
|
|
126445
|
+
cleanupAsyncLaunchSession(args2.session, sessionId);
|
|
126446
|
+
return failedLane(args2.lane, role, startedAt, "Failed to record async lane in background delegation ledger", decision.slot.slotId, decision.slot.runId);
|
|
126447
|
+
}
|
|
126448
|
+
const promptController = new AbortController;
|
|
126449
|
+
let promptResult;
|
|
126450
|
+
try {
|
|
126451
|
+
promptResult = await withTimeout2(args2.session.promptAsync({
|
|
126452
|
+
path: { id: sessionId },
|
|
126453
|
+
query: { directory: args2.directory },
|
|
126454
|
+
body: {
|
|
126455
|
+
agent: args2.lane.agent,
|
|
126456
|
+
tools: buildReadOnlyTools(),
|
|
126457
|
+
parts: [{ type: "text", text: args2.lane.prompt }]
|
|
126458
|
+
},
|
|
126459
|
+
signal: promptController.signal
|
|
126460
|
+
}), args2.timeoutMs, `Lane "${args2.lane.id}" session.promptAsync timed out after ${args2.timeoutMs}ms`, promptController);
|
|
126461
|
+
} catch (error93) {
|
|
126462
|
+
const message = formatError3(error93);
|
|
126463
|
+
await appendDelegationTransition(args2.directory, sessionId, {
|
|
126464
|
+
status: "error",
|
|
126465
|
+
result: {
|
|
126466
|
+
error: message,
|
|
126467
|
+
chars: message.length,
|
|
126468
|
+
truncated: false,
|
|
126469
|
+
digest: digestText(message)
|
|
126470
|
+
}
|
|
126471
|
+
});
|
|
126472
|
+
cleanupAsyncLaunchSession(args2.session, sessionId);
|
|
126473
|
+
return failedLane(args2.lane, role, startedAt, message, decision.slot.slotId, decision.slot.runId);
|
|
126474
|
+
}
|
|
126475
|
+
if (promptResult.error) {
|
|
126476
|
+
const error93 = `session.promptAsync failed: ${formatError3(promptResult.error)}`;
|
|
126477
|
+
await appendDelegationTransition(args2.directory, sessionId, {
|
|
126478
|
+
status: "error",
|
|
126479
|
+
result: {
|
|
126480
|
+
error: error93,
|
|
126481
|
+
chars: error93.length,
|
|
126482
|
+
truncated: false,
|
|
126483
|
+
digest: digestText(error93)
|
|
126484
|
+
}
|
|
126485
|
+
});
|
|
126486
|
+
cleanupAsyncLaunchSession(args2.session, sessionId);
|
|
126487
|
+
return failedLane(args2.lane, role, startedAt, error93, decision.slot.slotId, decision.slot.runId);
|
|
126488
|
+
}
|
|
126489
|
+
await appendDelegationTransition(args2.directory, sessionId, {
|
|
126490
|
+
status: "running"
|
|
126491
|
+
});
|
|
126492
|
+
return {
|
|
126493
|
+
id: args2.lane.id,
|
|
126494
|
+
agent: args2.lane.agent,
|
|
126495
|
+
role,
|
|
126496
|
+
status: "pending",
|
|
126497
|
+
session_id: sessionId,
|
|
126498
|
+
slot_id: decision.slot.slotId,
|
|
126499
|
+
run_id: decision.slot.runId,
|
|
126500
|
+
started_at: startedAt,
|
|
126501
|
+
completed_at: isoNow()
|
|
126502
|
+
};
|
|
126503
|
+
} catch (error93) {
|
|
126504
|
+
return failedLane(args2.lane, role, startedAt, formatError3(error93), decision.slot.slotId, decision.slot.runId);
|
|
126505
|
+
} finally {
|
|
126506
|
+
args2.dispatcher.releaseSlot(decision.slot.slotId);
|
|
126507
|
+
}
|
|
126508
|
+
}
|
|
126509
|
+
async function collectOnce(session, directory, records, cancelPending) {
|
|
126510
|
+
for (const record3 of records) {
|
|
126511
|
+
if (record3.status !== "pending" && record3.status !== "running")
|
|
126512
|
+
continue;
|
|
126513
|
+
if (cancelPending) {
|
|
126514
|
+
if (typeof session.abort === "function") {
|
|
126515
|
+
await session.abort({ path: { id: record3.subagentSessionId } }).catch(() => {
|
|
126516
|
+
return;
|
|
126517
|
+
});
|
|
126518
|
+
}
|
|
126519
|
+
await appendDelegationTransition(directory, record3.correlationId, {
|
|
126520
|
+
status: "cancelled"
|
|
126521
|
+
});
|
|
126522
|
+
continue;
|
|
126523
|
+
}
|
|
126524
|
+
let messages;
|
|
126525
|
+
try {
|
|
126526
|
+
messages = await session.messages({
|
|
126527
|
+
path: { id: record3.subagentSessionId },
|
|
126528
|
+
query: { directory, limit: 50 }
|
|
126529
|
+
});
|
|
126530
|
+
} catch {
|
|
126531
|
+
continue;
|
|
126532
|
+
}
|
|
126533
|
+
if (!messages.data)
|
|
126534
|
+
continue;
|
|
126535
|
+
const text = extractLastAssistantText(messages.data);
|
|
126536
|
+
if (!text)
|
|
126537
|
+
continue;
|
|
126538
|
+
const bounded = boundLaneOutput(text);
|
|
126539
|
+
await appendDelegationTransition(directory, record3.correlationId, {
|
|
126540
|
+
status: "completed",
|
|
126541
|
+
result: {
|
|
126542
|
+
text: bounded.output,
|
|
126543
|
+
chars: bounded.output_chars,
|
|
126544
|
+
truncated: bounded.output_truncated,
|
|
126545
|
+
digest: digestText(text)
|
|
126546
|
+
}
|
|
126547
|
+
});
|
|
126548
|
+
}
|
|
126549
|
+
}
|
|
126550
|
+
function extractLastAssistantText(messages) {
|
|
126551
|
+
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
126552
|
+
const message = messages[i2];
|
|
126553
|
+
if (message.info?.role !== "assistant")
|
|
126554
|
+
continue;
|
|
126555
|
+
const text = extractText3(message.parts);
|
|
126556
|
+
if (text.trim().length > 0)
|
|
126557
|
+
return text;
|
|
126558
|
+
}
|
|
126559
|
+
return "";
|
|
126560
|
+
}
|
|
126561
|
+
function nextCollectPollInterval(currentMs) {
|
|
126562
|
+
if (currentMs <= 0)
|
|
126563
|
+
return COLLECT_POLL_INTERVAL_MS;
|
|
126564
|
+
return Math.min(currentMs * 2, MAX_COLLECT_POLL_INTERVAL_MS);
|
|
126565
|
+
}
|
|
125894
126566
|
async function runLane(session, dispatcher, lane, directory, timeoutMs, context) {
|
|
125895
126567
|
const validation2 = validateLaneAgent(lane.agent, context);
|
|
125896
126568
|
const role = validation2.role;
|
|
@@ -125991,6 +126663,49 @@ function buildResult(laneResults, maxConcurrent, timeoutMs) {
|
|
|
125991
126663
|
lane_results: laneResults
|
|
125992
126664
|
};
|
|
125993
126665
|
}
|
|
126666
|
+
function buildCollectResult(batchId, records, includePending) {
|
|
126667
|
+
const laneResults = records.filter((record3) => includePending || record3.status !== "pending" && record3.status !== "running").map(recordToLaneResult);
|
|
126668
|
+
const completed = records.filter((record3) => record3.status === "completed");
|
|
126669
|
+
const failed = records.filter((record3) => record3.status === "error");
|
|
126670
|
+
const cancelled = records.filter((record3) => record3.status === "cancelled");
|
|
126671
|
+
const stale = records.filter((record3) => record3.status === "stale");
|
|
126672
|
+
const pending = records.filter((record3) => record3.status === "pending" || record3.status === "running");
|
|
126673
|
+
const consumed = records.filter((record3) => record3.status === "consumed");
|
|
126674
|
+
return {
|
|
126675
|
+
success: pending.length === 0 && failed.length === 0 && cancelled.length === 0 && stale.length === 0,
|
|
126676
|
+
batch_id: batchId,
|
|
126677
|
+
total: records.length,
|
|
126678
|
+
completed: completed.length,
|
|
126679
|
+
failed: failed.length,
|
|
126680
|
+
cancelled: cancelled.length,
|
|
126681
|
+
stale: stale.length,
|
|
126682
|
+
pending: pending.length,
|
|
126683
|
+
consumed: consumed.length,
|
|
126684
|
+
all_settled: pending.length === 0,
|
|
126685
|
+
lane_results: laneResults
|
|
126686
|
+
};
|
|
126687
|
+
}
|
|
126688
|
+
function recordToLaneResult(record3) {
|
|
126689
|
+
const status = record3.status === "error" ? "failed" : record3.status === "running" ? "pending" : record3.status;
|
|
126690
|
+
return {
|
|
126691
|
+
id: record3.laneId ?? record3.correlationId,
|
|
126692
|
+
agent: record3.swarmPrefixedAgent,
|
|
126693
|
+
role: record3.normalizedAgent,
|
|
126694
|
+
status,
|
|
126695
|
+
session_id: record3.subagentSessionId,
|
|
126696
|
+
started_at: new Date(record3.createdAt).toISOString(),
|
|
126697
|
+
completed_at: new Date(record3.completedAt ?? record3.updatedAt).toISOString(),
|
|
126698
|
+
...record3.result?.text !== undefined ? {
|
|
126699
|
+
output: record3.result.text,
|
|
126700
|
+
output_chars: record3.result.chars,
|
|
126701
|
+
output_truncated: record3.result.truncated
|
|
126702
|
+
} : {},
|
|
126703
|
+
...record3.result?.error !== undefined ? { error: record3.result.error } : {}
|
|
126704
|
+
};
|
|
126705
|
+
}
|
|
126706
|
+
function allSettled(records) {
|
|
126707
|
+
return records.every((record3) => record3.status !== "pending" && record3.status !== "running");
|
|
126708
|
+
}
|
|
125994
126709
|
function failedLane(lane, role, startedAt, error93, slotId, runId, sessionId) {
|
|
125995
126710
|
return {
|
|
125996
126711
|
id: lane.id,
|
|
@@ -126093,6 +126808,40 @@ function failureResult(args2) {
|
|
|
126093
126808
|
errors: args2.errors
|
|
126094
126809
|
};
|
|
126095
126810
|
}
|
|
126811
|
+
function asyncFailureResult(args2) {
|
|
126812
|
+
return {
|
|
126813
|
+
success: false,
|
|
126814
|
+
failure_class: args2.failure_class,
|
|
126815
|
+
message: args2.message,
|
|
126816
|
+
batch_id: null,
|
|
126817
|
+
dispatched: 0,
|
|
126818
|
+
pending: 0,
|
|
126819
|
+
failed: 0,
|
|
126820
|
+
rejected: 0,
|
|
126821
|
+
max_concurrent: 0,
|
|
126822
|
+
timeout_ms: 0,
|
|
126823
|
+
lane_results: [],
|
|
126824
|
+
errors: args2.errors
|
|
126825
|
+
};
|
|
126826
|
+
}
|
|
126827
|
+
function collectFailureResult(args2) {
|
|
126828
|
+
return {
|
|
126829
|
+
success: false,
|
|
126830
|
+
failure_class: args2.failure_class,
|
|
126831
|
+
message: args2.message,
|
|
126832
|
+
batch_id: args2.batch_id,
|
|
126833
|
+
total: 0,
|
|
126834
|
+
completed: 0,
|
|
126835
|
+
failed: 0,
|
|
126836
|
+
cancelled: 0,
|
|
126837
|
+
stale: 0,
|
|
126838
|
+
pending: 0,
|
|
126839
|
+
consumed: 0,
|
|
126840
|
+
all_settled: false,
|
|
126841
|
+
lane_results: [],
|
|
126842
|
+
errors: args2.errors
|
|
126843
|
+
};
|
|
126844
|
+
}
|
|
126096
126845
|
function findDuplicateLaneIds(lanes) {
|
|
126097
126846
|
const seen = new Set;
|
|
126098
126847
|
const duplicates = new Set;
|
|
@@ -126108,6 +126857,14 @@ function scheduleSessionCleanup(session, sessionId) {
|
|
|
126108
126857
|
return;
|
|
126109
126858
|
});
|
|
126110
126859
|
}
|
|
126860
|
+
function cleanupAsyncLaunchSession(session, sessionId) {
|
|
126861
|
+
if (typeof session.abort === "function") {
|
|
126862
|
+
session.abort({ path: { id: sessionId } }).catch(() => {
|
|
126863
|
+
return;
|
|
126864
|
+
});
|
|
126865
|
+
}
|
|
126866
|
+
scheduleSessionCleanup(session, sessionId);
|
|
126867
|
+
}
|
|
126111
126868
|
async function withTimeout2(promise3, timeoutMs, message, controller) {
|
|
126112
126869
|
let timeout;
|
|
126113
126870
|
try {
|
|
@@ -126118,9 +126875,6 @@ async function withTimeout2(promise3, timeoutMs, message, controller) {
|
|
|
126118
126875
|
controller?.abort();
|
|
126119
126876
|
reject(new Error(message));
|
|
126120
126877
|
}, timeoutMs);
|
|
126121
|
-
if (typeof timeout.unref === "function") {
|
|
126122
|
-
timeout.unref();
|
|
126123
|
-
}
|
|
126124
126878
|
})
|
|
126125
126879
|
]);
|
|
126126
126880
|
} finally {
|
|
@@ -126148,6 +126902,29 @@ function boundErrorString(text) {
|
|
|
126148
126902
|
function isoNow() {
|
|
126149
126903
|
return new Date(_internals81.now()).toISOString();
|
|
126150
126904
|
}
|
|
126905
|
+
function makeBatchId() {
|
|
126906
|
+
return `lanes-${_internals81.now().toString(36)}`;
|
|
126907
|
+
}
|
|
126908
|
+
function promptHash(lane, directory, batchId) {
|
|
126909
|
+
return digestText(JSON.stringify({
|
|
126910
|
+
batchId,
|
|
126911
|
+
laneId: lane.id,
|
|
126912
|
+
agent: lane.agent,
|
|
126913
|
+
directory,
|
|
126914
|
+
prompt: lane.prompt.replace(/\r\n/g, `
|
|
126915
|
+
`)
|
|
126916
|
+
}));
|
|
126917
|
+
}
|
|
126918
|
+
function digestText(text) {
|
|
126919
|
+
return createHash16("sha256").update(text).digest("hex");
|
|
126920
|
+
}
|
|
126921
|
+
function sleep2(ms) {
|
|
126922
|
+
if (ms <= 0)
|
|
126923
|
+
return Promise.resolve();
|
|
126924
|
+
return new Promise((resolve57) => {
|
|
126925
|
+
setTimeout(resolve57, ms);
|
|
126926
|
+
});
|
|
126927
|
+
}
|
|
126151
126928
|
var dispatch_lanes = createSwarmTool({
|
|
126152
126929
|
description: "Dispatch multiple read-only exploration/review lanes concurrently through OpenCode sessions and return a structured join result.",
|
|
126153
126930
|
args: {
|
|
@@ -126162,12 +126939,53 @@ var dispatch_lanes = createSwarmTool({
|
|
|
126162
126939
|
return JSON.stringify(result, null, 2);
|
|
126163
126940
|
}
|
|
126164
126941
|
});
|
|
126942
|
+
var dispatch_lanes_async = createSwarmTool({
|
|
126943
|
+
description: "Launch multiple read-only advisory lanes with OpenCode promptAsync and return immediately with a batch id for collect_lane_results.",
|
|
126944
|
+
args: {
|
|
126945
|
+
lanes: DispatchLanesAsyncArgsSchema.shape.lanes,
|
|
126946
|
+
max_concurrent: DispatchLanesAsyncArgsSchema.shape.max_concurrent,
|
|
126947
|
+
timeout_ms: DispatchLanesAsyncArgsSchema.shape.timeout_ms,
|
|
126948
|
+
batch_id: DispatchLanesAsyncArgsSchema.shape.batch_id,
|
|
126949
|
+
mode: DispatchLanesAsyncArgsSchema.shape.mode,
|
|
126950
|
+
pr_head_sha: DispatchLanesAsyncArgsSchema.shape.pr_head_sha,
|
|
126951
|
+
scope: DispatchLanesAsyncArgsSchema.shape.scope
|
|
126952
|
+
},
|
|
126953
|
+
execute: async (args2, directory, ctx) => {
|
|
126954
|
+
const result = await executeDispatchLanesAsync(args2, directory, {
|
|
126955
|
+
callerAgent: getContextAgent(ctx),
|
|
126956
|
+
sessionID: getContextSessionID(ctx)
|
|
126957
|
+
});
|
|
126958
|
+
return JSON.stringify(result, null, 2);
|
|
126959
|
+
}
|
|
126960
|
+
});
|
|
126961
|
+
var collect_lane_results = createSwarmTool({
|
|
126962
|
+
description: "Collect or poll results for a dispatch_lanes_async batch; this is the required join barrier for advisory lane workflows and does not advance workflow gates.",
|
|
126963
|
+
args: {
|
|
126964
|
+
batch_id: CollectLaneResultsArgsSchema.shape.batch_id,
|
|
126965
|
+
wait: CollectLaneResultsArgsSchema.shape.wait,
|
|
126966
|
+
timeout_ms: CollectLaneResultsArgsSchema.shape.timeout_ms,
|
|
126967
|
+
include_pending: CollectLaneResultsArgsSchema.shape.include_pending,
|
|
126968
|
+
cancel_pending: CollectLaneResultsArgsSchema.shape.cancel_pending
|
|
126969
|
+
},
|
|
126970
|
+
execute: async (args2, directory, ctx) => {
|
|
126971
|
+
const result = await executeCollectLaneResults(args2, directory, {
|
|
126972
|
+
sessionID: getContextSessionID(ctx)
|
|
126973
|
+
});
|
|
126974
|
+
return JSON.stringify(result, null, 2);
|
|
126975
|
+
}
|
|
126976
|
+
});
|
|
126165
126977
|
function getContextAgent(ctx) {
|
|
126166
126978
|
if (!ctx || typeof ctx !== "object")
|
|
126167
126979
|
return;
|
|
126168
126980
|
const value = ctx.agent;
|
|
126169
126981
|
return typeof value === "string" ? value : undefined;
|
|
126170
126982
|
}
|
|
126983
|
+
function getContextSessionID(ctx) {
|
|
126984
|
+
if (!ctx || typeof ctx !== "object")
|
|
126985
|
+
return;
|
|
126986
|
+
const value = ctx.sessionID;
|
|
126987
|
+
return typeof value === "string" ? value : undefined;
|
|
126988
|
+
}
|
|
126171
126989
|
|
|
126172
126990
|
// src/tools/manifest.ts
|
|
126173
126991
|
init_doc_scan();
|
|
@@ -126848,11 +127666,11 @@ var external_skill_delete = createSwarmTool({
|
|
|
126848
127666
|
// src/tools/external-skill-discover.ts
|
|
126849
127667
|
init_zod();
|
|
126850
127668
|
init_loader();
|
|
126851
|
-
import { createHash as
|
|
127669
|
+
import { createHash as createHash18, randomUUID as randomUUID12 } from "node:crypto";
|
|
126852
127670
|
|
|
126853
127671
|
// src/services/external-skill-validator.ts
|
|
126854
127672
|
init_knowledge_validator();
|
|
126855
|
-
import { createHash as
|
|
127673
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
126856
127674
|
var PROMPT_INJECTION_PATTERNS = [
|
|
126857
127675
|
{
|
|
126858
127676
|
pattern: /system\s*:/i,
|
|
@@ -127387,7 +128205,7 @@ function evaluateCandidate(candidate, options) {
|
|
|
127387
128205
|
}
|
|
127388
128206
|
var _internals84 = {
|
|
127389
128207
|
getTimestamp: () => new Date().toISOString(),
|
|
127390
|
-
computeSha256: (content) =>
|
|
128208
|
+
computeSha256: (content) => createHash17("sha256").update(content).digest("hex"),
|
|
127391
128209
|
stripMarkdownCodeForUnsafeScan
|
|
127392
128210
|
};
|
|
127393
128211
|
|
|
@@ -127409,7 +128227,7 @@ var _internals85 = {
|
|
|
127409
128227
|
return { content, finalUrl: response.url };
|
|
127410
128228
|
},
|
|
127411
128229
|
getTimestamp: () => new Date().toISOString(),
|
|
127412
|
-
computeSha256: (content) =>
|
|
128230
|
+
computeSha256: (content) => createHash18("sha256").update(content).digest("hex"),
|
|
127413
128231
|
uuid: () => randomUUID12()
|
|
127414
128232
|
};
|
|
127415
128233
|
var SOURCE_TRUST_LEVELS = {
|
|
@@ -127795,7 +128613,7 @@ var external_skill_list = createSwarmTool({
|
|
|
127795
128613
|
// src/tools/external-skill-promote.ts
|
|
127796
128614
|
init_zod();
|
|
127797
128615
|
init_loader();
|
|
127798
|
-
import { createHash as
|
|
128616
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
127799
128617
|
import * as fs95 from "node:fs/promises";
|
|
127800
128618
|
import * as path149 from "node:path";
|
|
127801
128619
|
init_create_tool();
|
|
@@ -127960,7 +128778,7 @@ var external_skill_promote = createSwarmTool({
|
|
|
127960
128778
|
}
|
|
127961
128779
|
throw writeErr;
|
|
127962
128780
|
}
|
|
127963
|
-
const promotedContentHash =
|
|
128781
|
+
const promotedContentHash = createHash19("sha256").update(skillMarkdown).digest("hex");
|
|
127964
128782
|
const prePromotionHistory = candidate.evaluation_history;
|
|
127965
128783
|
const lastPrePromotionEntry = prePromotionHistory.length > 0 ? prePromotionHistory[prePromotionHistory.length - 1] : undefined;
|
|
127966
128784
|
const originalEvaluation = lastPrePromotionEntry ? {
|
|
@@ -145433,7 +146251,7 @@ import * as zlib from "node:zlib";
|
|
|
145433
146251
|
// src/evidence/documents.ts
|
|
145434
146252
|
init_utils2();
|
|
145435
146253
|
init_redaction();
|
|
145436
|
-
import { createHash as
|
|
146254
|
+
import { createHash as createHash21 } from "node:crypto";
|
|
145437
146255
|
import { appendFile as appendFile17, mkdir as mkdir34 } from "node:fs/promises";
|
|
145438
146256
|
import * as path189 from "node:path";
|
|
145439
146257
|
var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
|
|
@@ -145477,7 +146295,7 @@ function createEvidenceDocumentRecord(input, defaultCapturedAt) {
|
|
|
145477
146295
|
};
|
|
145478
146296
|
}
|
|
145479
146297
|
function createEvidenceDocumentId(input) {
|
|
145480
|
-
const hash4 =
|
|
146298
|
+
const hash4 = createHash21("sha256").update([
|
|
145481
146299
|
input.sourceType,
|
|
145482
146300
|
input.query ?? "",
|
|
145483
146301
|
input.title ?? "",
|
|
@@ -147274,6 +148092,8 @@ var TOOL_MANIFEST = defineHandlers({
|
|
|
147274
148092
|
swarm_memory_propose: () => swarm_memory_propose,
|
|
147275
148093
|
swarm_command: () => swarm_command,
|
|
147276
148094
|
dispatch_lanes: () => dispatch_lanes,
|
|
148095
|
+
dispatch_lanes_async: () => dispatch_lanes_async,
|
|
148096
|
+
collect_lane_results: () => collect_lane_results,
|
|
147277
148097
|
summarize_work: () => summarize_work,
|
|
147278
148098
|
write_architecture_supervisor_evidence: () => write_architecture_supervisor_evidence,
|
|
147279
148099
|
lean_turbo_plan_lanes: () => lean_turbo_plan_lanes,
|