@wrongstack/core 0.280.1 → 0.281.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-subagent-runner-PoqNKiR4.d.ts → agent-subagent-runner-DyOSK2Wy.d.ts} +39 -8
- package/dist/{compactor-U3agvUIG.d.ts → compactor-BxddyCFu.d.ts} +1 -1
- package/dist/{config-Cr3312zc.d.ts → config-CN-vdDEI.d.ts} +82 -2
- package/dist/coordination/index.d.ts +203 -20
- package/dist/coordination/index.js +981 -52
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +26 -30
- package/dist/defaults/index.js +382 -30
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-Bs2fmldo.d.ts → events-C5hLU0AT.d.ts} +38 -2
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +161 -18
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/execution/prompt-enhancer.js +12 -3
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-Ct7IorLJ.d.ts → global-mailbox-CHM1QkCi.d.ts} +5 -2
- package/dist/{goal-store-C4F6DjC0.d.ts → goal-store-BLGZq16t.d.ts} +1 -1
- package/dist/hq/index.d.ts +6 -6
- package/dist/hq/index.js +35 -13
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-nP09-oP2.d.ts → index-BEFWNZYs.d.ts} +1 -2
- package/dist/{index-kidebiDh.d.ts → index-C1E-_71T.d.ts} +6 -6
- package/dist/index.d.ts +44 -48
- package/dist/index.js +14855 -13944
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +60 -8
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +11 -11
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-D910X5_r.d.ts → mcp-servers-EKRAcJDi.d.ts} +97 -4
- package/dist/models/index.d.ts +4 -5
- package/dist/models/index.js +22 -5
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-coordinator-CieyUoEL.d.ts → multi-agent-coordinator-IpHCuHUa.d.ts} +29 -1
- package/dist/{null-fleet-bus-DkdmZJ_W.d.ts → null-fleet-bus-ZBkKGR9j.d.ts} +105 -7
- package/dist/observability/index.d.ts +3 -3
- package/dist/{tool-executor-SiE1wlZo.d.ts → parallel-eternal-engine-Cc2CNgmv.d.ts} +6 -202
- package/dist/path-resolver-CMS5307d.d.ts +13 -0
- package/dist/{permission-Dx6dIqS2.d.ts → permission-B6YAtNFz.d.ts} +1 -1
- package/dist/{pipeline-BwAP21_4.d.ts → pipeline-E7_jqGyV.d.ts} +2 -2
- package/dist/{provider-model-resolve-CwQNZWt_.d.ts → provider-model-resolve-KgIdiEQ6.d.ts} +118 -5
- package/dist/{provider-runner-CYHFImzV.d.ts → provider-runner-DxWXNHL1.d.ts} +3 -3
- package/dist/{retry-policy-D4feSLk3.d.ts → retry-policy-V67lBOJz.d.ts} +2 -2
- package/dist/sdd/index.d.ts +9 -9
- package/dist/sdd/index.js +94 -10
- package/dist/sdd/index.js.map +1 -1
- package/dist/{permission-policy-C8vJcnX5.d.ts → secret-vault-D7jeyXUQ.d.ts} +119 -3
- package/dist/security/index.d.ts +5 -6
- package/dist/security/index.js +4 -1
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-Dy-MzKp1.d.ts → selector-Bx2HgUkU.d.ts} +1 -1
- package/dist/{session-event-bridge-CqdiGnfU.d.ts → session-event-bridge-vA2fLV58.d.ts} +1 -1
- package/dist/{session-reader-Hk0WbNm9.d.ts → session-reader-B-Fi9L6I.d.ts} +1 -1
- package/dist/session-registry-Dvg7i_IA.d.ts +129 -0
- package/dist/storage/index.d.ts +71 -142
- package/dist/storage/index.js +161 -13
- package/dist/storage/index.js.map +1 -1
- package/dist/{todos-checkpoint-Bk2uP7Ex.d.ts → todos-checkpoint-B1QVkoa8.d.ts} +23 -5
- package/dist/{tool-BkOgs_KL.d.ts → tool-LVSLiE1s.d.ts} +18 -4
- package/dist/{strategy-compactor-CQwhbErd.d.ts → tool-executor-BWtYwuPI.d.ts} +210 -12
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +17 -41
- package/dist/types/index.js +182 -5085
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +6 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-BjOFF6bt.d.ts → worktree-manager-C6CIilTT.d.ts} +1 -1
- package/dist/{wstack-paths-CMl_cYgq.d.ts → wstack-paths-C3K40Qst.d.ts} +2 -0
- package/instructions/agents/shadow-agent.md +23 -84
- package/instructions/coordination/director-preamble.md +12 -3
- package/instructions/coordination/subagent-baseline.md +39 -46
- package/instructions/leader-after-task.md +10 -28
- package/instructions/llm/llm-selector.md +1 -2
- package/instructions/modes/brief-system.md +7 -33
- package/instructions/modes/brief.md +5 -20
- package/instructions/modes/teach-system.md +12 -72
- package/instructions/modes/teach.md +12 -49
- package/instructions/sections/tool/commit-hygiene.md +5 -5
- package/instructions/sections/tool/context-management-full.md +6 -6
- package/instructions/sections/tool/delegation-compact.md +1 -1
- package/instructions/sections/tool/delegation-full.md +19 -23
- package/instructions/sections/tool/mailbox-full.md +11 -31
- package/instructions/sections/tool/mcp-full-control.md +5 -7
- package/instructions/sections/tool/mcp-full-use.md +7 -7
- package/instructions/system.md +22 -38
- package/package.json +1 -29
- package/dist/agent-bridge-DXC6QDJ4.d.ts +0 -35
- package/dist/logger-D3lV0cUZ.d.ts +0 -101
- package/dist/models-registry-CLkoOcHk.d.ts +0 -108
- package/dist/path-resolver-XfZ9eLxG.d.ts +0 -67
- package/dist/secret-vault-CImt2XrR.d.ts +0 -118
package/dist/defaults/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
|
-
import { randomBytes, randomUUID, createCipheriv, createDecipheriv, createHash, scryptSync } from 'crypto';
|
|
2
|
+
import { randomBytes, randomUUID, randomInt, createCipheriv, createDecipheriv, createHash, scryptSync } from 'crypto';
|
|
3
3
|
import * as fsp7 from 'fs/promises';
|
|
4
4
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
5
5
|
import * as path4 from 'path';
|
|
@@ -868,7 +868,7 @@ function walk(value, schema, path32, errors, depth) {
|
|
|
868
868
|
return;
|
|
869
869
|
}
|
|
870
870
|
if (schema.enum !== void 0) {
|
|
871
|
-
if (!schema.enum
|
|
871
|
+
if (!enumIncludes(schema.enum, value)) {
|
|
872
872
|
errors.push({
|
|
873
873
|
path: path32 || "<root>",
|
|
874
874
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
@@ -938,6 +938,10 @@ function joinPath(parent, key) {
|
|
|
938
938
|
if (!parent) return key;
|
|
939
939
|
return `${parent}.${key}`;
|
|
940
940
|
}
|
|
941
|
+
function enumIncludes(values, value) {
|
|
942
|
+
if (value === null || typeof value !== "object") return values.includes(value);
|
|
943
|
+
return values.some((candidate) => deepEqual(candidate, value));
|
|
944
|
+
}
|
|
941
945
|
function deepEqual(a, b) {
|
|
942
946
|
if (a === b) return true;
|
|
943
947
|
if (typeof a !== typeof b) return false;
|
|
@@ -1187,6 +1191,27 @@ function safeParse(input, maxBytes = 5e6) {
|
|
|
1187
1191
|
};
|
|
1188
1192
|
}
|
|
1189
1193
|
}
|
|
1194
|
+
function safeStringify(value, pretty = false) {
|
|
1195
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1196
|
+
const replacer = (_k, v) => {
|
|
1197
|
+
if (typeof v === "bigint") return v.toString();
|
|
1198
|
+
if (v instanceof Error) {
|
|
1199
|
+
return { name: v.name, message: v.message, stack: v.stack };
|
|
1200
|
+
}
|
|
1201
|
+
if (typeof v === "object" && v !== null) {
|
|
1202
|
+
if (seen.has(v)) return "[Circular]";
|
|
1203
|
+
seen.add(v);
|
|
1204
|
+
}
|
|
1205
|
+
return v;
|
|
1206
|
+
};
|
|
1207
|
+
try {
|
|
1208
|
+
return JSON.stringify(value, replacer, pretty ? 2 : void 0) ?? "null";
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
return JSON.stringify({
|
|
1211
|
+
__serialization_error: toErrorMessage(err)
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1190
1215
|
var init_safe_json = __esm({
|
|
1191
1216
|
"src/utils/safe-json.ts"() {
|
|
1192
1217
|
init_error();
|
|
@@ -2320,6 +2345,7 @@ function resolveWstackPaths(opts) {
|
|
|
2320
2345
|
projectHash: hash,
|
|
2321
2346
|
projectSlug: slug,
|
|
2322
2347
|
projectGoal: path4.join(projectDir, "goal.json"),
|
|
2348
|
+
projectInputHistory: path4.join(projectDir, "input-history.json"),
|
|
2323
2349
|
projectSpecs: path4.join(projectDir, "specs"),
|
|
2324
2350
|
projectTaskGraphs: path4.join(projectDir, "task-graphs"),
|
|
2325
2351
|
projectSddSession: path4.join(projectDir, "sdd-session.json"),
|
|
@@ -3448,20 +3474,32 @@ var TOOLS = {
|
|
|
3448
3474
|
/** Web research. */
|
|
3449
3475
|
research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
|
|
3450
3476
|
};
|
|
3477
|
+
var promptCache = /* @__PURE__ */ new Map();
|
|
3478
|
+
var candidateCache = /* @__PURE__ */ new Map();
|
|
3451
3479
|
function agentPrompt(id) {
|
|
3480
|
+
const envDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"] ?? "";
|
|
3481
|
+
const cacheKey = `${envDir}\0${id}`;
|
|
3482
|
+
const cached = promptCache.get(cacheKey);
|
|
3483
|
+
if (cached !== void 0) return cached;
|
|
3452
3484
|
const fileName = `${id}.md`;
|
|
3453
|
-
|
|
3485
|
+
let resolved = "";
|
|
3486
|
+
for (const dir of agentPromptDirCandidates(envDir)) {
|
|
3454
3487
|
try {
|
|
3455
|
-
|
|
3488
|
+
resolved = readFileSync(path4.join(dir, fileName), "utf8").trimEnd();
|
|
3489
|
+
break;
|
|
3456
3490
|
} catch {
|
|
3457
3491
|
}
|
|
3458
3492
|
}
|
|
3459
|
-
|
|
3493
|
+
promptCache.set(cacheKey, resolved);
|
|
3494
|
+
return resolved;
|
|
3460
3495
|
}
|
|
3461
|
-
function agentPromptDirCandidates() {
|
|
3462
|
-
const here = path4.dirname(fileURLToPath(import.meta.url));
|
|
3463
|
-
const explicitDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"];
|
|
3496
|
+
function agentPromptDirCandidates(envDir) {
|
|
3464
3497
|
const globalRoot = process.env["WRONGSTACK_HOME"] || path4.join(os.homedir(), ".wrongstack");
|
|
3498
|
+
const candKey = `${envDir}\0${globalRoot}`;
|
|
3499
|
+
const cached = candidateCache.get(candKey);
|
|
3500
|
+
if (cached !== void 0) return cached;
|
|
3501
|
+
const here = path4.dirname(fileURLToPath(import.meta.url));
|
|
3502
|
+
const explicitDir = envDir || void 0;
|
|
3465
3503
|
const candidates = [
|
|
3466
3504
|
...explicitDir ? [path4.resolve(explicitDir)] : [],
|
|
3467
3505
|
path4.join(globalRoot, "instructions", "agents"),
|
|
@@ -3471,7 +3509,9 @@ function agentPromptDirCandidates() {
|
|
|
3471
3509
|
path4.resolve(here, "../instructions/agents"),
|
|
3472
3510
|
path4.resolve(here, "instructions/agents")
|
|
3473
3511
|
];
|
|
3474
|
-
|
|
3512
|
+
const ordered = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
|
|
3513
|
+
candidateCache.set(candKey, ordered);
|
|
3514
|
+
return ordered;
|
|
3475
3515
|
}
|
|
3476
3516
|
function isDirectory(candidate) {
|
|
3477
3517
|
try {
|
|
@@ -6328,14 +6368,21 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
6328
6368
|
this.snapshot.files.length = 0;
|
|
6329
6369
|
}
|
|
6330
6370
|
};
|
|
6371
|
+
var textCache = /* @__PURE__ */ new Map();
|
|
6372
|
+
var rootCandidates;
|
|
6331
6373
|
function readBundledInstructionText(relativePath) {
|
|
6374
|
+
const cached = textCache.get(relativePath);
|
|
6375
|
+
if (cached !== void 0) return cached;
|
|
6376
|
+
let resolved = "";
|
|
6332
6377
|
for (const root of instructionRootCandidates()) {
|
|
6333
6378
|
try {
|
|
6334
|
-
|
|
6379
|
+
resolved = readFileSync(path4.join(root, relativePath), "utf8").trimEnd();
|
|
6380
|
+
break;
|
|
6335
6381
|
} catch {
|
|
6336
6382
|
}
|
|
6337
6383
|
}
|
|
6338
|
-
|
|
6384
|
+
textCache.set(relativePath, resolved);
|
|
6385
|
+
return resolved;
|
|
6339
6386
|
}
|
|
6340
6387
|
function renderInstructionTemplate(template, values) {
|
|
6341
6388
|
return template.replace(
|
|
@@ -6344,13 +6391,15 @@ function renderInstructionTemplate(template, values) {
|
|
|
6344
6391
|
);
|
|
6345
6392
|
}
|
|
6346
6393
|
function instructionRootCandidates() {
|
|
6394
|
+
if (rootCandidates !== void 0) return rootCandidates;
|
|
6347
6395
|
const here = path4.dirname(fileURLToPath(import.meta.url));
|
|
6348
6396
|
const candidates = [
|
|
6349
6397
|
path4.resolve(here, "../../instructions"),
|
|
6350
6398
|
path4.resolve(here, "../instructions"),
|
|
6351
6399
|
path4.resolve(here, "instructions")
|
|
6352
6400
|
];
|
|
6353
|
-
|
|
6401
|
+
rootCandidates = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
|
|
6402
|
+
return rootCandidates;
|
|
6354
6403
|
}
|
|
6355
6404
|
function isDirectory2(candidate) {
|
|
6356
6405
|
try {
|
|
@@ -6734,13 +6783,31 @@ function makeAssignTool(director) {
|
|
|
6734
6783
|
function makeAwaitTasksTool(director) {
|
|
6735
6784
|
return {
|
|
6736
6785
|
name: "await_tasks",
|
|
6737
|
-
description: "
|
|
6786
|
+
description: 'Wait for assigned tasks. mode:"all" (default) blocks until EVERY named task completes and returns all results. mode:"any" returns as soon as AT LEAST ONE completes \u2014 use it for independent tasks so you can handle each finisher immediately (reassign work, spawn helpers) instead of idling on the slowest; call again with the returned `pending` ids to pick up the next finisher.',
|
|
6738
6787
|
permission: "auto",
|
|
6739
6788
|
mutating: false,
|
|
6740
6789
|
capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
|
|
6741
|
-
inputSchema: {
|
|
6790
|
+
inputSchema: {
|
|
6791
|
+
type: "object",
|
|
6792
|
+
properties: {
|
|
6793
|
+
taskIds: { type: "array", items: { type: "string" }, description: "One or more task ids returned by `assign_task`." },
|
|
6794
|
+
mode: { type: "string", enum: ["all", "any"], description: '"all" (default): block until every task completes. "any": return on the first completion with the rest listed as pending.' },
|
|
6795
|
+
timeoutMs: { type: "number", minimum: 1, description: 'mode:"any" only \u2014 return {timedOut:true, completed:[]} if nothing completes within this window instead of blocking.' }
|
|
6796
|
+
},
|
|
6797
|
+
required: ["taskIds"]
|
|
6798
|
+
},
|
|
6742
6799
|
async execute(input) {
|
|
6743
6800
|
const i = input;
|
|
6801
|
+
if (i.mode === "any") {
|
|
6802
|
+
const r = await director.awaitTasksAny(i.taskIds, i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : void 0);
|
|
6803
|
+
return {
|
|
6804
|
+
mode: "any",
|
|
6805
|
+
completed: r.completed,
|
|
6806
|
+
pending: r.pending,
|
|
6807
|
+
...r.timedOut ? { timedOut: true } : {},
|
|
6808
|
+
...r.pending.length > 0 ? { hint: 'Handle the completed results now. Re-call await_tasks with the pending ids (mode:"any") for the next finisher \u2014 or assign new work to the now-idle subagent first.' } : {}
|
|
6809
|
+
};
|
|
6810
|
+
}
|
|
6744
6811
|
const results = await director.awaitTasks(i.taskIds);
|
|
6745
6812
|
return { results };
|
|
6746
6813
|
}
|
|
@@ -7847,6 +7914,30 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
7847
7914
|
results() {
|
|
7848
7915
|
return this.completedResults;
|
|
7849
7916
|
}
|
|
7917
|
+
/** Defensive snapshot of the still-queued (not yet dispatched) tasks. */
|
|
7918
|
+
listPendingTasks() {
|
|
7919
|
+
return this.pendingTasks.map((t) => ({ ...t }));
|
|
7920
|
+
}
|
|
7921
|
+
/**
|
|
7922
|
+
* Re-pin a still-PENDING task to a different subagent (`undefined` =
|
|
7923
|
+
* unpin, any idle worker may take it), then try to dispatch. Returns
|
|
7924
|
+
* `false` when the task is not in the pending queue — already
|
|
7925
|
+
* dispatched, completed, or unknown. Running tasks can never be pulled;
|
|
7926
|
+
* they can only be steered or terminated. The mutation is synchronous
|
|
7927
|
+
* (same tick as the check), so it cannot race a concurrent dispatch:
|
|
7928
|
+
* `tryDispatchNext` runs on this same call stack or a later one.
|
|
7929
|
+
*
|
|
7930
|
+
* The task keeps its id, so waiters, the report-back notifier, and
|
|
7931
|
+
* checkpoint bookkeeping resolve unchanged when it eventually completes.
|
|
7932
|
+
*/
|
|
7933
|
+
retargetPendingTask(taskId, subagentId) {
|
|
7934
|
+
const task = this.pendingTasks.find((t) => t.id === taskId);
|
|
7935
|
+
if (!task) return false;
|
|
7936
|
+
if (subagentId !== void 0 && !this.subagents.has(subagentId)) return false;
|
|
7937
|
+
task.subagentId = subagentId;
|
|
7938
|
+
this.tryDispatchNext();
|
|
7939
|
+
return true;
|
|
7940
|
+
}
|
|
7850
7941
|
/**
|
|
7851
7942
|
* Wait for one or more tasks to complete and return their results.
|
|
7852
7943
|
* If a task is already done when called, returns immediately.
|
|
@@ -7874,6 +7965,43 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
7874
7965
|
})
|
|
7875
7966
|
);
|
|
7876
7967
|
}
|
|
7968
|
+
/**
|
|
7969
|
+
* Wait until AT LEAST ONE of the named tasks completes. Returns every
|
|
7970
|
+
* requested result already available at that moment (drain-what's-done)
|
|
7971
|
+
* plus the ids still outstanding, so callers can loop "handle finishers,
|
|
7972
|
+
* re-await the remainder" instead of blocking on the whole batch.
|
|
7973
|
+
*
|
|
7974
|
+
* Unlike `awaitTasks`, this never rejects and deliberately does NOT
|
|
7975
|
+
* inherit `config.timeoutMs` — a "return whatever is done" call has no
|
|
7976
|
+
* business timing out unless the caller asks for a window explicitly.
|
|
7977
|
+
*/
|
|
7978
|
+
async awaitTasksAny(taskIds, opts) {
|
|
7979
|
+
const ids = new Set(taskIds);
|
|
7980
|
+
const completed = this.completedResults.filter((r) => ids.has(r.taskId));
|
|
7981
|
+
if (completed.length > 0 || ids.size === 0) {
|
|
7982
|
+
const done = new Set(completed.map((r) => r.taskId));
|
|
7983
|
+
return { completed, pending: taskIds.filter((id) => !done.has(id)) };
|
|
7984
|
+
}
|
|
7985
|
+
return new Promise((resolve12) => {
|
|
7986
|
+
let timer;
|
|
7987
|
+
const handler = ({ result }) => {
|
|
7988
|
+
if (!ids.has(result.taskId)) return;
|
|
7989
|
+
if (timer) clearTimeout(timer);
|
|
7990
|
+
this.off("task.completed", handler);
|
|
7991
|
+
resolve12({
|
|
7992
|
+
completed: [result],
|
|
7993
|
+
pending: taskIds.filter((id) => id !== result.taskId)
|
|
7994
|
+
});
|
|
7995
|
+
};
|
|
7996
|
+
if (opts?.timeoutMs !== void 0) {
|
|
7997
|
+
timer = setTimeout(() => {
|
|
7998
|
+
this.off("task.completed", handler);
|
|
7999
|
+
resolve12({ completed: [], pending: [...taskIds], timedOut: true });
|
|
8000
|
+
}, opts.timeoutMs);
|
|
8001
|
+
}
|
|
8002
|
+
this.on("task.completed", handler);
|
|
8003
|
+
});
|
|
8004
|
+
}
|
|
7877
8005
|
/**
|
|
7878
8006
|
* Manual completion — for callers that drive subagents without a runner
|
|
7879
8007
|
* (e.g. external orchestrators). When a runner is configured the coordinator
|
|
@@ -8498,6 +8626,13 @@ var Director = class _Director {
|
|
|
8498
8626
|
* coordinator emits `task.completed` for a given task id. Each entry
|
|
8499
8627
|
* is created lazily on first poll/await and cleared once consumed. */
|
|
8500
8628
|
taskWaiters = /* @__PURE__ */ new Map();
|
|
8629
|
+
/** First-completion waiters registered by `awaitTasksAny`. Kept separate
|
|
8630
|
+
* from `taskWaiters` on purpose: a `taskWaiters` entry marks a result as
|
|
8631
|
+
* "will be delivered in-band" and suppresses the fire-and-forget
|
|
8632
|
+
* report-back — but an any-await only consumes ONE of its ids, so the
|
|
8633
|
+
* losers must stay eligible for the mailbox notifier. Entries carry an
|
|
8634
|
+
* optional timeout timer so `shutdown()` can clear them. */
|
|
8635
|
+
anyWaiters = /* @__PURE__ */ new Set();
|
|
8501
8636
|
/** Cache of completed results in case the consumer asks AFTER the
|
|
8502
8637
|
* coordinator already fired the event — `awaitTasks(['t-1'])` after
|
|
8503
8638
|
* t-1 finished should resolve immediately, not hang. */
|
|
@@ -8519,6 +8654,8 @@ var Director = class _Director {
|
|
|
8519
8654
|
roster;
|
|
8520
8655
|
directorPreamble;
|
|
8521
8656
|
subagentBaseline;
|
|
8657
|
+
/** See {@link DirectorOptions.taskResultNotifier}. */
|
|
8658
|
+
taskResultNotifier;
|
|
8522
8659
|
/** Absolute path to the fleet's shared scratchpad directory, or null
|
|
8523
8660
|
* when none was configured. Exposed as a readonly getter for callers
|
|
8524
8661
|
* that need to surface the path to the user (e.g. the CLI logging
|
|
@@ -8616,6 +8753,7 @@ var Director = class _Director {
|
|
|
8616
8753
|
this.roster = opts.roster;
|
|
8617
8754
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
8618
8755
|
this.subagentBaseline = opts.subagentBaseline ?? DEFAULT_SUBAGENT_BASELINE;
|
|
8756
|
+
this.taskResultNotifier = opts.taskResultNotifier;
|
|
8619
8757
|
this.sharedScratchpadPath = opts.sharedScratchpadPath ?? null;
|
|
8620
8758
|
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
8621
8759
|
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
@@ -8688,8 +8826,38 @@ var Director = class _Director {
|
|
|
8688
8826
|
waiter.resolve(r);
|
|
8689
8827
|
this.taskWaiters.delete(r.taskId);
|
|
8690
8828
|
}
|
|
8829
|
+
let anyConsumed = false;
|
|
8830
|
+
for (const aw of [...this.anyWaiters]) {
|
|
8831
|
+
if (!aw.ids.has(r.taskId)) continue;
|
|
8832
|
+
if (aw.timer) clearTimeout(aw.timer);
|
|
8833
|
+
this.anyWaiters.delete(aw);
|
|
8834
|
+
aw.resolve(r);
|
|
8835
|
+
anyConsumed = true;
|
|
8836
|
+
}
|
|
8837
|
+
const consumedInBand = !!waiter || anyConsumed;
|
|
8691
8838
|
if (internalTask) return;
|
|
8692
8839
|
const title = this.taskDescriptions.get(r.taskId) ?? payload.task.description ?? r.taskId;
|
|
8840
|
+
if (!consumedInBand && this.taskResultNotifier) {
|
|
8841
|
+
const resultText = typeof r.result === "string" ? r.result : r.result !== void 0 ? safeStringify(r.result) : void 0;
|
|
8842
|
+
try {
|
|
8843
|
+
void Promise.resolve(
|
|
8844
|
+
this.taskResultNotifier({
|
|
8845
|
+
taskId: r.taskId,
|
|
8846
|
+
title,
|
|
8847
|
+
status: r.status,
|
|
8848
|
+
subagentId: r.subagentId,
|
|
8849
|
+
subagentName: this.manifestEntries.get(r.subagentId)?.name,
|
|
8850
|
+
resultText,
|
|
8851
|
+
errorText: r.error ? `${r.error.kind}: ${r.error.message}` : void 0,
|
|
8852
|
+
iterations: r.iterations,
|
|
8853
|
+
toolCalls: r.toolCalls,
|
|
8854
|
+
durationMs: r.durationMs
|
|
8855
|
+
})
|
|
8856
|
+
).catch(() => {
|
|
8857
|
+
});
|
|
8858
|
+
} catch {
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8693
8861
|
const failed = r.status !== "success";
|
|
8694
8862
|
const errorString = r.error ? `${r.error.kind}: ${r.error.message}` : void 0;
|
|
8695
8863
|
this.stateCheckpoint?.recordTaskStatus(r.taskId, {
|
|
@@ -9275,6 +9443,10 @@ var Director = class _Director {
|
|
|
9275
9443
|
this.coordinator.off("task.completed", this.taskCompletedListener);
|
|
9276
9444
|
this.taskCompletedListener = null;
|
|
9277
9445
|
}
|
|
9446
|
+
for (const aw of this.anyWaiters) {
|
|
9447
|
+
if (aw.timer) clearTimeout(aw.timer);
|
|
9448
|
+
}
|
|
9449
|
+
this.anyWaiters.clear();
|
|
9278
9450
|
if (this.toolExecFilter) {
|
|
9279
9451
|
this.toolExecFilter();
|
|
9280
9452
|
this.toolExecFilter = null;
|
|
@@ -9343,6 +9515,12 @@ var Director = class _Director {
|
|
|
9343
9515
|
waiter.resolve(synthetic);
|
|
9344
9516
|
this.taskWaiters.delete(taskWithId.id);
|
|
9345
9517
|
}
|
|
9518
|
+
for (const aw of [...this.anyWaiters]) {
|
|
9519
|
+
if (!aw.ids.has(taskWithId.id)) continue;
|
|
9520
|
+
if (aw.timer) clearTimeout(aw.timer);
|
|
9521
|
+
this.anyWaiters.delete(aw);
|
|
9522
|
+
aw.resolve(synthetic);
|
|
9523
|
+
}
|
|
9346
9524
|
return taskWithId.id;
|
|
9347
9525
|
}
|
|
9348
9526
|
if (task.subagentId) {
|
|
@@ -9413,6 +9591,85 @@ var Director = class _Director {
|
|
|
9413
9591
|
})
|
|
9414
9592
|
);
|
|
9415
9593
|
}
|
|
9594
|
+
/**
|
|
9595
|
+
* Wait until AT LEAST ONE of the named tasks completes, then return every
|
|
9596
|
+
* requested result already cached plus the still-pending ids. This is the
|
|
9597
|
+
* leader's "handle whichever finishes next" primitive — unlike
|
|
9598
|
+
* `awaitTasks` it never blocks on the slowest sibling, and the siblings
|
|
9599
|
+
* that complete later still reach the leader through the fire-and-forget
|
|
9600
|
+
* report-back (see the consumed-in-band rule in `taskCompletedListener`).
|
|
9601
|
+
*
|
|
9602
|
+
* Deliberately does NOT register `taskWaiters` entries for the pending
|
|
9603
|
+
* ids: a `taskWaiters` entry would mark those results as in-band-consumed
|
|
9604
|
+
* and silence the mailbox notifier for results this caller never receives.
|
|
9605
|
+
*
|
|
9606
|
+
* With `timeoutMs`, resolves (never rejects) `{timedOut: true}` and zero
|
|
9607
|
+
* completions when the window elapses first.
|
|
9608
|
+
*/
|
|
9609
|
+
awaitTasksAny(taskIds, opts) {
|
|
9610
|
+
const completed = taskIds.map((id) => this.completed.get(id)).filter((r) => r !== void 0);
|
|
9611
|
+
if (completed.length > 0 || taskIds.length === 0) {
|
|
9612
|
+
const done = new Set(completed.map((r) => r.taskId));
|
|
9613
|
+
return Promise.resolve({
|
|
9614
|
+
completed,
|
|
9615
|
+
pending: taskIds.filter((id) => !done.has(id))
|
|
9616
|
+
});
|
|
9617
|
+
}
|
|
9618
|
+
return new Promise((resolve12) => {
|
|
9619
|
+
const entry = {
|
|
9620
|
+
ids: new Set(taskIds),
|
|
9621
|
+
resolve: (r) => resolve12({
|
|
9622
|
+
completed: [r],
|
|
9623
|
+
pending: taskIds.filter((id) => id !== r.taskId)
|
|
9624
|
+
})
|
|
9625
|
+
};
|
|
9626
|
+
if (opts?.timeoutMs !== void 0) {
|
|
9627
|
+
entry.timer = setTimeout(() => {
|
|
9628
|
+
this.anyWaiters.delete(entry);
|
|
9629
|
+
resolve12({ completed: [], pending: [...taskIds], timedOut: true });
|
|
9630
|
+
}, opts.timeoutMs);
|
|
9631
|
+
}
|
|
9632
|
+
this.anyWaiters.add(entry);
|
|
9633
|
+
});
|
|
9634
|
+
}
|
|
9635
|
+
/** Defensive snapshot of tasks still queued (not yet dispatched). */
|
|
9636
|
+
listPendingTasks() {
|
|
9637
|
+
return this.coordinator.listPendingTasks();
|
|
9638
|
+
}
|
|
9639
|
+
/**
|
|
9640
|
+
* Re-pin a still-pending task to a different subagent (`undefined` =
|
|
9641
|
+
* unpin) — the rebalancing primitive used by the FleetSupervisor and
|
|
9642
|
+
* available to hosts. Only queued tasks can move; a dispatched task
|
|
9643
|
+
* returns `false` (steer or terminate its worker instead). Keeps
|
|
9644
|
+
* ownership bookkeeping (taskOwners, checkpoint, fleet manifest) in
|
|
9645
|
+
* sync with the coordinator's queue.
|
|
9646
|
+
*/
|
|
9647
|
+
retargetPendingTask(taskId, subagentId) {
|
|
9648
|
+
const ok = this.coordinator.retargetPendingTask(taskId, subagentId);
|
|
9649
|
+
if (!ok) return false;
|
|
9650
|
+
if (subagentId) {
|
|
9651
|
+
this.taskOwners.set(taskId, subagentId);
|
|
9652
|
+
if (this.fleetManager) {
|
|
9653
|
+
this.fleetManager.addTaskToSubagent(subagentId, taskId);
|
|
9654
|
+
} else {
|
|
9655
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
9656
|
+
if (entry && !entry.taskIds.includes(taskId)) entry.taskIds.push(taskId);
|
|
9657
|
+
}
|
|
9658
|
+
} else {
|
|
9659
|
+
this.taskOwners.delete(taskId);
|
|
9660
|
+
}
|
|
9661
|
+
const description = this.taskDescriptions.get(taskId) ?? taskId;
|
|
9662
|
+
this.fleetManager?.addPendingTask(taskId, subagentId ?? "", description);
|
|
9663
|
+
this.stateCheckpoint?.recordTaskAssigned({
|
|
9664
|
+
taskId,
|
|
9665
|
+
subagentId,
|
|
9666
|
+
description,
|
|
9667
|
+
status: "running",
|
|
9668
|
+
assignedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9669
|
+
});
|
|
9670
|
+
this.scheduleManifest();
|
|
9671
|
+
return true;
|
|
9672
|
+
}
|
|
9416
9673
|
async terminate(subagentId) {
|
|
9417
9674
|
await this.coordinator.stop(subagentId);
|
|
9418
9675
|
}
|
|
@@ -10349,6 +10606,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
10349
10606
|
dir;
|
|
10350
10607
|
events;
|
|
10351
10608
|
secretScrubber;
|
|
10609
|
+
isSessionInUse;
|
|
10352
10610
|
/**
|
|
10353
10611
|
* In-memory cache for load() results, keyed by session ID. The cache is
|
|
10354
10612
|
* invalidated when the file's mtimeMs or size changes (indicating the
|
|
@@ -10368,6 +10626,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
10368
10626
|
this.dir = opts.dir;
|
|
10369
10627
|
this.events = opts.events;
|
|
10370
10628
|
this.secretScrubber = opts.secretScrubber;
|
|
10629
|
+
this.isSessionInUse = opts.isSessionInUse;
|
|
10371
10630
|
}
|
|
10372
10631
|
/**
|
|
10373
10632
|
* Clear the load() cache. Useful for testing or when the caller knows
|
|
@@ -11259,19 +11518,74 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11259
11518
|
});
|
|
11260
11519
|
await this.writeTombstone(id);
|
|
11261
11520
|
}
|
|
11521
|
+
/**
|
|
11522
|
+
* Read the session id currently marked active in `active.json`, or `null`
|
|
11523
|
+
* when the lock is absent/unreadable. Shared by {@link delete} and
|
|
11524
|
+
* {@link prune} to avoid clobbering a session a live process is writing to.
|
|
11525
|
+
*/
|
|
11526
|
+
async readActiveSessionId() {
|
|
11527
|
+
try {
|
|
11528
|
+
const raw = await fsp7.readFile(path4.join(this.dir, "active.json"), "utf8");
|
|
11529
|
+
const active = JSON.parse(raw);
|
|
11530
|
+
return active.sessionId ?? null;
|
|
11531
|
+
} catch {
|
|
11532
|
+
return null;
|
|
11533
|
+
}
|
|
11534
|
+
}
|
|
11262
11535
|
async delete(id) {
|
|
11536
|
+
const activeId = await this.readActiveSessionId();
|
|
11537
|
+
if (activeId && id === activeId) {
|
|
11538
|
+
throw new Error(
|
|
11539
|
+
`Session ${id} is currently active in this project and cannot be deleted. Resume or start another session first.`
|
|
11540
|
+
);
|
|
11541
|
+
}
|
|
11542
|
+
if (this.isSessionInUse) {
|
|
11543
|
+
const reason = await this.isSessionInUse(id);
|
|
11544
|
+
if (reason) {
|
|
11545
|
+
throw new Error(`Session ${id} is in use (${reason}) and cannot be deleted.`);
|
|
11546
|
+
}
|
|
11547
|
+
}
|
|
11263
11548
|
await this.deleteSession(id);
|
|
11264
11549
|
}
|
|
11550
|
+
async rename(id, name) {
|
|
11551
|
+
const trimmed = name.trim();
|
|
11552
|
+
const manifest = this.sessionPath(id, ".summary.json");
|
|
11553
|
+
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
11554
|
+
try {
|
|
11555
|
+
await fsp7.stat(jsonlPath);
|
|
11556
|
+
} catch (err) {
|
|
11557
|
+
const code = err?.code;
|
|
11558
|
+
if (code === "ENOENT") {
|
|
11559
|
+
throw new Error(`Session not found: ${id}`);
|
|
11560
|
+
}
|
|
11561
|
+
throw err;
|
|
11562
|
+
}
|
|
11563
|
+
const summary = await this.summaryFor(id);
|
|
11564
|
+
const { name: _drop, ...rest } = summary;
|
|
11565
|
+
const updated = trimmed ? { ...rest, name: trimmed } : rest;
|
|
11566
|
+
const t0 = Date.now();
|
|
11567
|
+
let outcome = "success";
|
|
11568
|
+
let errorMsg;
|
|
11569
|
+
try {
|
|
11570
|
+
await atomicWrite(manifest, JSON.stringify(updated), { mode: 384 });
|
|
11571
|
+
} catch (err) {
|
|
11572
|
+
outcome = "failure";
|
|
11573
|
+
errorMsg = toErrorMessage(err);
|
|
11574
|
+
this.emitError(id, manifest, "rename", errorMsg, false);
|
|
11575
|
+
throw err;
|
|
11576
|
+
} finally {
|
|
11577
|
+
this.emitWrite(id, manifest, "close", outcome, Date.now() - t0, void 0, errorMsg);
|
|
11578
|
+
}
|
|
11579
|
+
await this.appendToIndex(updated);
|
|
11580
|
+
this.invalidateShardManifestBySessionId(id);
|
|
11581
|
+
this._indexCache = null;
|
|
11582
|
+
this.clearLoadCache(id);
|
|
11583
|
+
return updated;
|
|
11584
|
+
}
|
|
11265
11585
|
async prune(maxAgeDays = 30) {
|
|
11266
11586
|
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
11267
11587
|
let deleted = 0;
|
|
11268
|
-
|
|
11269
|
-
try {
|
|
11270
|
-
const raw = await fsp7.readFile(path4.join(this.dir, "active.json"), "utf8");
|
|
11271
|
-
const active = JSON.parse(raw);
|
|
11272
|
-
activeSessionId = active.sessionId ?? null;
|
|
11273
|
-
} catch {
|
|
11274
|
-
}
|
|
11588
|
+
const activeSessionId = await this.readActiveSessionId();
|
|
11275
11589
|
const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
11276
11590
|
const pruneFile = async (dir, name, prefix) => {
|
|
11277
11591
|
const jsonlPath = path4.join(dir, name);
|
|
@@ -15461,7 +15775,9 @@ async function runProviderWithRetry(opts) {
|
|
|
15461
15775
|
});
|
|
15462
15776
|
throw toWrongStackError(err);
|
|
15463
15777
|
}
|
|
15464
|
-
const delay = Math.round(
|
|
15778
|
+
const delay = Math.round(
|
|
15779
|
+
retry.delayMs(attempt, isProviderErr ? err : errAsErr)
|
|
15780
|
+
);
|
|
15465
15781
|
const attemptNum = attempt + 1;
|
|
15466
15782
|
const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
|
|
15467
15783
|
logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
|
|
@@ -15517,8 +15833,7 @@ var DefaultProviderRunner = class {
|
|
|
15517
15833
|
return runProviderWithRetry(opts);
|
|
15518
15834
|
}
|
|
15519
15835
|
};
|
|
15520
|
-
|
|
15521
|
-
// src/execution/retry-policy.ts
|
|
15836
|
+
var MAX_RETRY_AFTER_MS = 6e4;
|
|
15522
15837
|
var DefaultRetryPolicy = class {
|
|
15523
15838
|
shouldRetry(err, attempt) {
|
|
15524
15839
|
if (err instanceof ProviderError) {
|
|
@@ -15540,13 +15855,37 @@ var DefaultRetryPolicy = class {
|
|
|
15540
15855
|
}
|
|
15541
15856
|
return 2;
|
|
15542
15857
|
}
|
|
15543
|
-
|
|
15858
|
+
/**
|
|
15859
|
+
* Compute the retry delay.
|
|
15860
|
+
*
|
|
15861
|
+
* Precedence:
|
|
15862
|
+
* 1. If `err` is a `ProviderError` with a populated
|
|
15863
|
+
* `body.retryAfterMs`, honour it — clamped into
|
|
15864
|
+
* [0, MAX_RETRY_AFTER_MS]. The provider told us exactly when
|
|
15865
|
+
* to come back; we should listen.
|
|
15866
|
+
* 2. Otherwise fall through to the exponential-with-jitter
|
|
15867
|
+
* schedule (`1000 * 2^attempt + jitter`, capped at 30s).
|
|
15868
|
+
*
|
|
15869
|
+
* `err` is optional for back-compat with the existing interface;
|
|
15870
|
+
* callers should pass it whenever available.
|
|
15871
|
+
*/
|
|
15872
|
+
delayMs(attempt, err) {
|
|
15873
|
+
const hint = retryAfterMsFromError(err);
|
|
15874
|
+
if (hint !== void 0) {
|
|
15875
|
+
return Math.max(0, Math.min(hint, MAX_RETRY_AFTER_MS));
|
|
15876
|
+
}
|
|
15544
15877
|
const base = 1e3;
|
|
15545
15878
|
const exp = base * 2 ** attempt;
|
|
15546
|
-
const jitter =
|
|
15879
|
+
const jitter = randomInt(0, base);
|
|
15547
15880
|
return Math.min(3e4, exp + jitter);
|
|
15548
15881
|
}
|
|
15549
15882
|
};
|
|
15883
|
+
function retryAfterMsFromError(err) {
|
|
15884
|
+
if (!err || !(err instanceof ProviderError)) return void 0;
|
|
15885
|
+
const ms = err.body?.retryAfterMs;
|
|
15886
|
+
if (typeof ms !== "number" || !Number.isFinite(ms) || ms <= 0) return void 0;
|
|
15887
|
+
return ms;
|
|
15888
|
+
}
|
|
15550
15889
|
|
|
15551
15890
|
// src/models/llm-selector.ts
|
|
15552
15891
|
init_expect_defined();
|
|
@@ -18641,7 +18980,7 @@ function describeCatalogModel(m) {
|
|
|
18641
18980
|
]
|
|
18642
18981
|
};
|
|
18643
18982
|
}
|
|
18644
|
-
function resolveProviderModelList(savedModels, catalog) {
|
|
18983
|
+
function resolveProviderModelList(savedModels, catalog, providerHint) {
|
|
18645
18984
|
if (savedModels && savedModels.length > 0) {
|
|
18646
18985
|
const byId = new Map((catalog?.models ?? []).map((m) => [m.id, m]));
|
|
18647
18986
|
return savedModels.map((id) => {
|
|
@@ -18657,7 +18996,15 @@ function resolveProviderModelList(savedModels, catalog) {
|
|
|
18657
18996
|
return { id, name: id, capabilities: [] };
|
|
18658
18997
|
});
|
|
18659
18998
|
}
|
|
18660
|
-
if (catalog) return catalog.models.map(describeCatalogModel);
|
|
18999
|
+
if (catalog && catalog.models.length > 0) return catalog.models.map(describeCatalogModel);
|
|
19000
|
+
if (providerHint === "openai-codex") {
|
|
19001
|
+
return CODEX_MODELS.map((m) => ({
|
|
19002
|
+
id: m.id,
|
|
19003
|
+
name: m.name,
|
|
19004
|
+
description: m.description,
|
|
19005
|
+
capabilities: []
|
|
19006
|
+
}));
|
|
19007
|
+
}
|
|
18661
19008
|
return [];
|
|
18662
19009
|
}
|
|
18663
19010
|
|
|
@@ -26437,7 +26784,11 @@ var KNOWN_DENIED_IN_PROJECT = [
|
|
|
26437
26784
|
{ key: "yolo", reason: "Disables all permission confirmation prompts." },
|
|
26438
26785
|
{ key: "extensions", reason: "Per-plugin config can carry command/credential fields." },
|
|
26439
26786
|
{ key: "hq", reason: "Carries HQ client token credential and endpoint URL." },
|
|
26440
|
-
{ key: "acp", reason: "Per-agent ACP command/args/env override \u2192 arbitrary command exec (RCE)." }
|
|
26787
|
+
{ key: "acp", reason: "Per-agent ACP command/args/env override \u2192 arbitrary command exec (RCE)." },
|
|
26788
|
+
{
|
|
26789
|
+
key: "fleet",
|
|
26790
|
+
reason: "Fleet supervision knobs: a repo-committed config could enable autonomous subagent spawning/steering/termination and mailbox traffic on the victim machine."
|
|
26791
|
+
}
|
|
26441
26792
|
];
|
|
26442
26793
|
var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
26443
26794
|
"version",
|
|
@@ -26479,7 +26830,8 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
26479
26830
|
"hq",
|
|
26480
26831
|
"sync",
|
|
26481
26832
|
"extensions",
|
|
26482
|
-
"acp"
|
|
26833
|
+
"acp",
|
|
26834
|
+
"fleet"
|
|
26483
26835
|
]);
|
|
26484
26836
|
function assertInProjectAllowListComplete() {
|
|
26485
26837
|
const missingFromBoth = [];
|