@wrongstack/core 0.282.0 → 0.282.2
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/README.md +1 -1
- package/dist/{agent-subagent-runner-Dksf0QVU.d.ts → agent-subagent-runner-DCczSoQj.d.ts} +6 -6
- package/dist/{config-BCAEJicb.d.ts → config-DkCbA3Bw.d.ts} +22 -0
- package/dist/coordination/index.d.ts +15 -15
- package/dist/coordination/index.js +802 -139
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-azFprTB3.d.ts → default-config-Buho8yge.d.ts} +1 -0
- package/dist/defaults/index.d.ts +18 -18
- package/dist/defaults/index.js +811 -119
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-CovDylaH.d.ts → events-BOv8h6I1.d.ts} +1 -1
- package/dist/execution/index.d.ts +9 -9
- package/dist/execution/index.js +8 -1
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +4 -4
- package/dist/{global-mailbox-CNfgvRw3.d.ts → global-mailbox-MDDFhLYh.d.ts} +1 -1
- package/dist/{goal-store-CBd749M4.d.ts → goal-store-BEmDmSKF.d.ts} +1 -1
- package/dist/hq/index.d.ts +4 -4
- package/dist/{index-D5HB7WkV.d.ts → index-Dd-PJJ8A.d.ts} +3 -3
- package/dist/index.d.ts +209 -30
- package/dist/index.js +29562 -28136
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +3 -3
- package/dist/infrastructure/index.js +8 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +7 -7
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DJYwZXyH.d.ts → mcp-servers-BUx9A_AQ.d.ts} +1 -1
- package/dist/models/index.d.ts +4 -4
- package/dist/models/index.js +18 -20
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-coordinator-4DC31QWu.d.ts → multi-agent-coordinator-BRqtpn-a.d.ts} +1 -1
- package/dist/{null-fleet-bus-DuvA8PIR.d.ts → null-fleet-bus-BkptvIVo.d.ts} +7 -6
- package/dist/observability/index.d.ts +1 -1
- package/dist/{parallel-eternal-engine-BwcA0bAt.d.ts → parallel-eternal-engine-BXECuOVG.d.ts} +4 -4
- package/dist/{pipeline-DM-CPTUs.d.ts → pipeline-ixSFe5jg.d.ts} +2 -2
- package/dist/{provider-model-resolve-hNUbewMd.d.ts → provider-model-resolve-DIxKL_T-.d.ts} +1 -1
- package/dist/{provider-runner-Bz4GyUqo.d.ts → provider-runner-CJtCs1Rw.d.ts} +1 -1
- package/dist/sdd/index.d.ts +7 -7
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.js +6 -0
- package/dist/security/index.js.map +1 -1
- package/dist/storage/index.d.ts +9 -9
- package/dist/storage/index.js +10 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/{task-format-vGOIftmK.d.ts → task-format-GX0B1mMC.d.ts} +1 -1
- package/dist/{task-graph-u1q9Jkyk.d.ts → task-graph-CH3acNQ7.d.ts} +10 -1
- package/dist/{todos-checkpoint-addCdQ9f.d.ts → todos-checkpoint-Bw83WMh9.d.ts} +2 -2
- package/dist/{tool-executor-B3spV_Q6.d.ts → tool-executor-4mWN2vHW.d.ts} +7 -7
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +12 -12
- package/dist/types/index.js +22 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-BBXdJq-Y.d.ts → worktree-manager-BrtjUFYk.d.ts} +1 -1
- package/instructions/leader-after-task.md +4 -4
- package/instructions/system.md +11 -0
- package/package.json +1 -1
- package/skills/api-design/SKILL.md +1 -1
- package/skills/audit-log/SKILL.md +3 -3
- package/skills/bug-hunter/SKILL.md +3 -3
- package/skills/chimera/SKILL.md +3 -3
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/git-flow/SKILL.md +3 -3
- package/skills/mailbox-bridge/SKILL.md +1 -1
- package/skills/multi-agent/SKILL.md +3 -3
- package/skills/node-modern/SKILL.md +1 -1
- package/skills/observability/SKILL.md +1 -1
- package/skills/output-standards/SKILL.md +31 -30
- package/skills/output-standards/SKILL.save.md +5 -5
- package/skills/plugin-author/SKILL.md +1 -1
- package/skills/prompt-engineering/SKILL.md +1 -1
- package/skills/react-modern/SKILL.md +1 -1
- package/skills/refactor-planner/SKILL.md +3 -3
- package/skills/research-web/SKILL.md +1 -1
- package/skills/sdd/SKILL.md +1 -1
- package/skills/security-scanner/SKILL.md +3 -3
- package/skills/skill-creator/SKILL.md +1 -1
- package/skills/tech-stack/SKILL.md +3 -3
- package/skills/testing/SKILL.md +1 -1
- package/skills/typescript-strict/SKILL.md +1 -1
package/dist/defaults/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as path4 from 'path';
|
|
|
6
6
|
import { isAbsolute, join, resolve, sep } from 'path';
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
import { hostname } from 'os';
|
|
9
|
-
import * as
|
|
9
|
+
import * as fs13 from 'fs';
|
|
10
10
|
import { readFileSync, statSync, createReadStream, existsSync } from 'fs';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { EventEmitter } from 'events';
|
|
@@ -70,8 +70,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
70
70
|
}
|
|
71
71
|
let mode;
|
|
72
72
|
try {
|
|
73
|
-
const
|
|
74
|
-
mode =
|
|
73
|
+
const stat12 = await fsp7.stat(targetPath);
|
|
74
|
+
mode = stat12.mode & 511;
|
|
75
75
|
} catch {
|
|
76
76
|
mode = opts.mode;
|
|
77
77
|
}
|
|
@@ -117,8 +117,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
117
117
|
}
|
|
118
118
|
if (code !== "EEXIST") throw err;
|
|
119
119
|
try {
|
|
120
|
-
const
|
|
121
|
-
if (Date.now() -
|
|
120
|
+
const stat12 = await fsp7.stat(lockPath);
|
|
121
|
+
if (Date.now() - stat12.mtimeMs > staleMs) {
|
|
122
122
|
await fsp7.unlink(lockPath);
|
|
123
123
|
continue;
|
|
124
124
|
}
|
|
@@ -133,7 +133,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
133
133
|
context: { timeoutMs }
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
-
await new Promise((
|
|
136
|
+
await new Promise((resolve13) => setTimeout(resolve13, 25));
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
try {
|
|
@@ -166,7 +166,7 @@ async function renameWithRetry(from, to) {
|
|
|
166
166
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
167
167
|
throw err;
|
|
168
168
|
}
|
|
169
|
-
await new Promise((
|
|
169
|
+
await new Promise((resolve13) => setTimeout(resolve13, delays[i]));
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
throw lastErr;
|
|
@@ -629,8 +629,8 @@ async function expandGlob(pattern) {
|
|
|
629
629
|
for (const e of entries) {
|
|
630
630
|
const full = `${dir}${SEP}${e}`;
|
|
631
631
|
try {
|
|
632
|
-
const
|
|
633
|
-
if (
|
|
632
|
+
const stat12 = await fsp7.stat(full);
|
|
633
|
+
if (stat12.isDirectory()) await walk3(full, rest);
|
|
634
634
|
} catch {
|
|
635
635
|
}
|
|
636
636
|
}
|
|
@@ -647,8 +647,8 @@ async function expandGlob(pattern) {
|
|
|
647
647
|
if (entries.includes(seg)) {
|
|
648
648
|
const full = `${dir}${SEP}${seg}`;
|
|
649
649
|
try {
|
|
650
|
-
const
|
|
651
|
-
if (
|
|
650
|
+
const stat12 = await fsp7.stat(full);
|
|
651
|
+
if (stat12.isDirectory()) await walk3(full, rest);
|
|
652
652
|
} catch {
|
|
653
653
|
}
|
|
654
654
|
}
|
|
@@ -859,10 +859,10 @@ function validateAgainstSchema(value, schema) {
|
|
|
859
859
|
walk(value, schema, "", errors, 0);
|
|
860
860
|
return { ok: errors.length === 0, errors };
|
|
861
861
|
}
|
|
862
|
-
function walk(value, schema,
|
|
862
|
+
function walk(value, schema, path33, errors, depth) {
|
|
863
863
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
864
864
|
errors.push({
|
|
865
|
-
path:
|
|
865
|
+
path: path33 || "<root>",
|
|
866
866
|
message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
|
|
867
867
|
});
|
|
868
868
|
return;
|
|
@@ -870,7 +870,7 @@ function walk(value, schema, path32, errors, depth) {
|
|
|
870
870
|
if (schema.enum !== void 0) {
|
|
871
871
|
if (!enumIncludes(schema.enum, value)) {
|
|
872
872
|
errors.push({
|
|
873
|
-
path:
|
|
873
|
+
path: path33 || "<root>",
|
|
874
874
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
875
875
|
});
|
|
876
876
|
return;
|
|
@@ -879,7 +879,7 @@ function walk(value, schema, path32, errors, depth) {
|
|
|
879
879
|
if (typeof schema.type === "string") {
|
|
880
880
|
if (!checkType(value, schema.type)) {
|
|
881
881
|
errors.push({
|
|
882
|
-
path:
|
|
882
|
+
path: path33 || "<root>",
|
|
883
883
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
884
884
|
});
|
|
885
885
|
return;
|
|
@@ -889,20 +889,20 @@ function walk(value, schema, path32, errors, depth) {
|
|
|
889
889
|
const obj = value;
|
|
890
890
|
for (const req of schema.required ?? []) {
|
|
891
891
|
if (!(req in obj)) {
|
|
892
|
-
errors.push({ path: joinPath(
|
|
892
|
+
errors.push({ path: joinPath(path33, req), message: "required property missing" });
|
|
893
893
|
}
|
|
894
894
|
}
|
|
895
895
|
if (schema.properties) {
|
|
896
896
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
897
897
|
if (key in obj) {
|
|
898
|
-
walk(obj[key], subSchema, joinPath(
|
|
898
|
+
walk(obj[key], subSchema, joinPath(path33, key), errors, depth + 1);
|
|
899
899
|
}
|
|
900
900
|
}
|
|
901
901
|
}
|
|
902
902
|
}
|
|
903
903
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
904
904
|
for (let i = 0; i < value.length; i++) {
|
|
905
|
-
walk(value[i], schema.items, `${
|
|
905
|
+
walk(value[i], schema.items, `${path33}[${i}]`, errors, depth + 1);
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
}
|
|
@@ -1283,7 +1283,7 @@ var init_ulid = __esm({
|
|
|
1283
1283
|
|
|
1284
1284
|
// src/utils/sleep.ts
|
|
1285
1285
|
function sleep(ms) {
|
|
1286
|
-
return new Promise((
|
|
1286
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
1287
1287
|
}
|
|
1288
1288
|
var init_sleep = __esm({
|
|
1289
1289
|
"src/utils/sleep.ts"() {
|
|
@@ -2697,7 +2697,7 @@ var InMemoryAgentBridge = class {
|
|
|
2697
2697
|
});
|
|
2698
2698
|
}
|
|
2699
2699
|
this.inflightGuards.add(correlationId);
|
|
2700
|
-
return new Promise((
|
|
2700
|
+
return new Promise((resolve13, reject) => {
|
|
2701
2701
|
const timer = setTimeout(() => {
|
|
2702
2702
|
this.inflightGuards.delete(correlationId);
|
|
2703
2703
|
this.pendingRequests.delete(correlationId);
|
|
@@ -2716,7 +2716,7 @@ var InMemoryAgentBridge = class {
|
|
|
2716
2716
|
return;
|
|
2717
2717
|
}
|
|
2718
2718
|
this.pendingRequests.set(correlationId, {
|
|
2719
|
-
resolve:
|
|
2719
|
+
resolve: resolve13,
|
|
2720
2720
|
reject,
|
|
2721
2721
|
timer
|
|
2722
2722
|
});
|
|
@@ -3068,13 +3068,13 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
3068
3068
|
if (!bus?.hasListenerFor("budget.threshold_reached")) {
|
|
3069
3069
|
return Promise.resolve("stop");
|
|
3070
3070
|
}
|
|
3071
|
-
return new Promise((
|
|
3071
|
+
return new Promise((resolve13) => {
|
|
3072
3072
|
let resolved = false;
|
|
3073
3073
|
const respond = (d) => {
|
|
3074
3074
|
if (resolved) return;
|
|
3075
3075
|
resolved = true;
|
|
3076
3076
|
clearTimeout(fallback);
|
|
3077
|
-
|
|
3077
|
+
resolve13(d);
|
|
3078
3078
|
};
|
|
3079
3079
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
3080
3080
|
const sessionId = this.currentSessionId();
|
|
@@ -5448,7 +5448,7 @@ function createDelegateTool(opts) {
|
|
|
5448
5448
|
subagentId
|
|
5449
5449
|
});
|
|
5450
5450
|
const dir = director;
|
|
5451
|
-
const result = await new Promise((
|
|
5451
|
+
const result = await new Promise((resolve13) => {
|
|
5452
5452
|
let settled = false;
|
|
5453
5453
|
let timer;
|
|
5454
5454
|
const finish = (value) => {
|
|
@@ -5458,7 +5458,7 @@ function createDelegateTool(opts) {
|
|
|
5458
5458
|
offTool();
|
|
5459
5459
|
offIter();
|
|
5460
5460
|
offProgress();
|
|
5461
|
-
|
|
5461
|
+
resolve13(value);
|
|
5462
5462
|
};
|
|
5463
5463
|
const arm = () => {
|
|
5464
5464
|
if (timer) clearTimeout(timer);
|
|
@@ -5902,7 +5902,14 @@ var DirectorStateCheckpoint = class {
|
|
|
5902
5902
|
mode: 384
|
|
5903
5903
|
});
|
|
5904
5904
|
} catch (err) {
|
|
5905
|
-
console.warn(
|
|
5905
|
+
console.warn(
|
|
5906
|
+
JSON.stringify({
|
|
5907
|
+
level: "warn",
|
|
5908
|
+
event: "director_state.checkpoint_write_failed",
|
|
5909
|
+
message: toErrorMessage(err),
|
|
5910
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
5911
|
+
})
|
|
5912
|
+
);
|
|
5906
5913
|
} finally {
|
|
5907
5914
|
this.writing = false;
|
|
5908
5915
|
if (this.rewriteRequested) {
|
|
@@ -6008,7 +6015,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
6008
6015
|
}
|
|
6009
6016
|
for (const filePath of allFiles) {
|
|
6010
6017
|
try {
|
|
6011
|
-
const [content,
|
|
6018
|
+
const [content, stat12] = await Promise.all([
|
|
6012
6019
|
fsp7.readFile(filePath, "utf8"),
|
|
6013
6020
|
fsp7.stat(filePath)
|
|
6014
6021
|
]);
|
|
@@ -6018,8 +6025,8 @@ var CollabSession = class extends EventEmitter {
|
|
|
6018
6025
|
path: filePath,
|
|
6019
6026
|
content,
|
|
6020
6027
|
language,
|
|
6021
|
-
snapshotMtimeMs:
|
|
6022
|
-
snapshotSizeBytes:
|
|
6028
|
+
snapshotMtimeMs: stat12.mtimeMs,
|
|
6029
|
+
snapshotSizeBytes: stat12.size
|
|
6023
6030
|
});
|
|
6024
6031
|
} catch {
|
|
6025
6032
|
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
@@ -6432,9 +6439,9 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
6432
6439
|
for (const file of this.snapshot.files) {
|
|
6433
6440
|
if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
|
|
6434
6441
|
try {
|
|
6435
|
-
const
|
|
6436
|
-
const mtimeChanged = file.snapshotMtimeMs !== void 0 &&
|
|
6437
|
-
const sizeChanged = file.snapshotSizeBytes !== void 0 &&
|
|
6442
|
+
const stat12 = await fsp7.stat(file.path);
|
|
6443
|
+
const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat12.mtimeMs > file.snapshotMtimeMs + 1;
|
|
6444
|
+
const sizeChanged = file.snapshotSizeBytes !== void 0 && stat12.size !== file.snapshotSizeBytes;
|
|
6438
6445
|
if (mtimeChanged || sizeChanged) {
|
|
6439
6446
|
warnings.push(`${file.path} changed after the collab snapshot was captured.`);
|
|
6440
6447
|
}
|
|
@@ -6652,6 +6659,366 @@ function rosterSummaryFromConfigs(roster) {
|
|
|
6652
6659
|
}
|
|
6653
6660
|
return lines.join("\n");
|
|
6654
6661
|
}
|
|
6662
|
+
|
|
6663
|
+
// src/kanban/storage.ts
|
|
6664
|
+
init_atomic_write();
|
|
6665
|
+
|
|
6666
|
+
// src/kanban/types.ts
|
|
6667
|
+
var DEFAULT_COLUMNS = [
|
|
6668
|
+
{ id: "backlog", title: "Backlog", order: 0, wipLimit: 0, color: "#64748b" },
|
|
6669
|
+
{ id: "todo", title: "To Do", order: 1, wipLimit: 0, color: "#2563eb" },
|
|
6670
|
+
{ id: "in-progress", title: "In Progress", order: 2, wipLimit: 5, color: "#d97706" },
|
|
6671
|
+
{ id: "review", title: "Review", order: 3, wipLimit: 0, color: "#7c3aed" },
|
|
6672
|
+
{ id: "done", title: "Done", order: 4, wipLimit: 0, color: "#16a34a" }
|
|
6673
|
+
];
|
|
6674
|
+
var CURRENT_KANBAN_VERSION = 1;
|
|
6675
|
+
|
|
6676
|
+
// src/kanban/storage.ts
|
|
6677
|
+
var KANBANS_DIR = "kanbans";
|
|
6678
|
+
var BOARD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
6679
|
+
function getKanbanDir(projectRoot) {
|
|
6680
|
+
return path4.join(projectRoot, ".wrongstack", KANBANS_DIR);
|
|
6681
|
+
}
|
|
6682
|
+
function getKanbanPath(projectRoot, boardId) {
|
|
6683
|
+
assertValidBoardId(boardId);
|
|
6684
|
+
const dir = path4.resolve(getKanbanDir(projectRoot));
|
|
6685
|
+
const resolved = path4.resolve(dir, `${boardId}.json`);
|
|
6686
|
+
const rel = path4.relative(dir, resolved);
|
|
6687
|
+
if (rel.startsWith("..") || path4.isAbsolute(rel)) {
|
|
6688
|
+
throw invalidBoardId(boardId);
|
|
6689
|
+
}
|
|
6690
|
+
return resolved;
|
|
6691
|
+
}
|
|
6692
|
+
function isValidBoardId(boardId) {
|
|
6693
|
+
return BOARD_ID_RE.test(boardId) && !boardId.includes("..");
|
|
6694
|
+
}
|
|
6695
|
+
function assertValidBoardId(boardId) {
|
|
6696
|
+
if (!isValidBoardId(boardId)) throw invalidBoardId(boardId);
|
|
6697
|
+
}
|
|
6698
|
+
function invalidBoardId(boardId) {
|
|
6699
|
+
return new Error(`Invalid kanban board id: ${boardId}`);
|
|
6700
|
+
}
|
|
6701
|
+
function isEnoent(err) {
|
|
6702
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
6703
|
+
}
|
|
6704
|
+
async function pathExists(filePath) {
|
|
6705
|
+
try {
|
|
6706
|
+
await fsp7.stat(filePath);
|
|
6707
|
+
return true;
|
|
6708
|
+
} catch (err) {
|
|
6709
|
+
if (isEnoent(err)) return false;
|
|
6710
|
+
throw err;
|
|
6711
|
+
}
|
|
6712
|
+
}
|
|
6713
|
+
async function listBoardIds(projectRoot) {
|
|
6714
|
+
const dir = getKanbanDir(projectRoot);
|
|
6715
|
+
try {
|
|
6716
|
+
const entries = await fsp7.readdir(dir, { withFileTypes: true });
|
|
6717
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -5)).filter(isValidBoardId).sort();
|
|
6718
|
+
} catch (err) {
|
|
6719
|
+
if (isEnoent(err)) return [];
|
|
6720
|
+
throw err;
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6723
|
+
async function resolveBoardRef(projectRoot, boardRef) {
|
|
6724
|
+
assertValidBoardId(boardRef);
|
|
6725
|
+
if (await pathExists(getKanbanPath(projectRoot, boardRef))) return boardRef;
|
|
6726
|
+
const matches = (await listBoardIds(projectRoot)).filter((id) => id.startsWith(boardRef));
|
|
6727
|
+
if (matches.length === 0) return null;
|
|
6728
|
+
if (matches.length > 1) {
|
|
6729
|
+
throw new Error(`Ambiguous kanban board id "${boardRef}": ${matches.slice(0, 5).join(", ")}`);
|
|
6730
|
+
}
|
|
6731
|
+
return matches[0] ?? null;
|
|
6732
|
+
}
|
|
6733
|
+
async function readBoard(projectRoot, boardRef) {
|
|
6734
|
+
const boardId = await resolveBoardRef(projectRoot, boardRef);
|
|
6735
|
+
if (!boardId) return null;
|
|
6736
|
+
try {
|
|
6737
|
+
const raw = await fsp7.readFile(getKanbanPath(projectRoot, boardId), "utf8");
|
|
6738
|
+
return normalizeBoard(JSON.parse(raw));
|
|
6739
|
+
} catch (err) {
|
|
6740
|
+
if (isEnoent(err)) return null;
|
|
6741
|
+
throw err;
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
async function mutateBoard(projectRoot, boardRef, mutator) {
|
|
6745
|
+
const boardId = await resolveBoardRef(projectRoot, boardRef);
|
|
6746
|
+
if (!boardId) return null;
|
|
6747
|
+
const filePath = getKanbanPath(projectRoot, boardId);
|
|
6748
|
+
return withFileLock(filePath, async () => {
|
|
6749
|
+
let board;
|
|
6750
|
+
try {
|
|
6751
|
+
const raw = await fsp7.readFile(filePath, "utf8");
|
|
6752
|
+
board = normalizeBoard(JSON.parse(raw));
|
|
6753
|
+
} catch (err) {
|
|
6754
|
+
if (isEnoent(err)) return null;
|
|
6755
|
+
throw err;
|
|
6756
|
+
}
|
|
6757
|
+
const result = await mutator(board);
|
|
6758
|
+
await writeBoardUnlocked(filePath, normalizeBoard(board));
|
|
6759
|
+
return { board, result };
|
|
6760
|
+
});
|
|
6761
|
+
}
|
|
6762
|
+
function summarizeBoard(board) {
|
|
6763
|
+
const total = board.tasks.length;
|
|
6764
|
+
const completed = board.tasks.filter((task) => task.status === "completed").length;
|
|
6765
|
+
const lastActivity = latestActivity(board);
|
|
6766
|
+
return {
|
|
6767
|
+
id: board.id,
|
|
6768
|
+
title: board.title,
|
|
6769
|
+
createdAt: board.createdAt,
|
|
6770
|
+
updatedAt: board.updatedAt,
|
|
6771
|
+
columnCount: board.columns.length,
|
|
6772
|
+
taskCount: total,
|
|
6773
|
+
completedTaskCount: completed,
|
|
6774
|
+
...board.description !== void 0 ? { description: board.description } : {},
|
|
6775
|
+
...board.tags !== void 0 ? { tags: board.tags } : {},
|
|
6776
|
+
...lastActivity !== void 0 ? { lastActivity } : {}
|
|
6777
|
+
};
|
|
6778
|
+
}
|
|
6779
|
+
async function listBoardSummaries(projectRoot) {
|
|
6780
|
+
const summaries = [];
|
|
6781
|
+
for (const id of await listBoardIds(projectRoot)) {
|
|
6782
|
+
const board = await readBoard(projectRoot, id);
|
|
6783
|
+
if (board) summaries.push(summarizeBoard(board));
|
|
6784
|
+
}
|
|
6785
|
+
return summaries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
6786
|
+
}
|
|
6787
|
+
function normalizeBoard(board) {
|
|
6788
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6789
|
+
const columns = [...board.columns?.length ? board.columns : DEFAULT_COLUMNS].map((column, index) => ({
|
|
6790
|
+
...column,
|
|
6791
|
+
order: column.order ?? index
|
|
6792
|
+
})).sort((a, b) => a.order - b.order).map((column, index) => ({ ...column, order: index }));
|
|
6793
|
+
const columnIds = new Set(columns.map((column) => column.id));
|
|
6794
|
+
const fallbackColumnId = columns[0]?.id ?? "backlog";
|
|
6795
|
+
const tasks = normalizeTaskOrders(
|
|
6796
|
+
(board.tasks ?? []).map((task, index) => ({
|
|
6797
|
+
...task,
|
|
6798
|
+
columnId: columnIds.has(task.columnId) ? task.columnId : fallbackColumnId,
|
|
6799
|
+
order: task.order ?? index,
|
|
6800
|
+
priority: task.priority ?? "medium",
|
|
6801
|
+
status: task.status ?? "pending",
|
|
6802
|
+
createdAt: task.createdAt ?? now,
|
|
6803
|
+
updatedAt: task.updatedAt ?? now
|
|
6804
|
+
}))
|
|
6805
|
+
);
|
|
6806
|
+
return {
|
|
6807
|
+
...board,
|
|
6808
|
+
version: board.version ?? CURRENT_KANBAN_VERSION,
|
|
6809
|
+
createdAt: board.createdAt ?? now,
|
|
6810
|
+
updatedAt: board.updatedAt ?? now,
|
|
6811
|
+
columns,
|
|
6812
|
+
tasks
|
|
6813
|
+
};
|
|
6814
|
+
}
|
|
6815
|
+
function latestActivity(board) {
|
|
6816
|
+
return [board.updatedAt, ...board.tasks.map((task) => task.updatedAt)].filter(Boolean).sort().reverse()[0];
|
|
6817
|
+
}
|
|
6818
|
+
async function writeBoardUnlocked(filePath, board) {
|
|
6819
|
+
await atomicWrite(filePath, `${JSON.stringify(board, null, 2)}
|
|
6820
|
+
`, { encoding: "utf8" });
|
|
6821
|
+
}
|
|
6822
|
+
function normalizeTaskOrders(tasks) {
|
|
6823
|
+
const byColumn = /* @__PURE__ */ new Map();
|
|
6824
|
+
for (const task of tasks) {
|
|
6825
|
+
const columnTasks = byColumn.get(task.columnId) ?? [];
|
|
6826
|
+
columnTasks.push(task);
|
|
6827
|
+
byColumn.set(task.columnId, columnTasks);
|
|
6828
|
+
}
|
|
6829
|
+
for (const columnTasks of byColumn.values()) {
|
|
6830
|
+
columnTasks.sort((a, b) => a.order - b.order || a.createdAt.localeCompare(b.createdAt)).forEach((task, index) => {
|
|
6831
|
+
task.order = index;
|
|
6832
|
+
});
|
|
6833
|
+
}
|
|
6834
|
+
return tasks;
|
|
6835
|
+
}
|
|
6836
|
+
|
|
6837
|
+
// src/kanban/manager.ts
|
|
6838
|
+
async function listBoards(projectRoot) {
|
|
6839
|
+
return listBoardSummaries(projectRoot);
|
|
6840
|
+
}
|
|
6841
|
+
async function updateTaskAssignment(projectRoot, boardId, taskId, patch) {
|
|
6842
|
+
const updated = await mutateBoard(projectRoot, boardId, (board) => {
|
|
6843
|
+
const task = findTask(board, taskId);
|
|
6844
|
+
if (!task) return null;
|
|
6845
|
+
const nextAssignment = {
|
|
6846
|
+
...task.assignment ?? { status: "assigned" }
|
|
6847
|
+
};
|
|
6848
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
6849
|
+
if (value !== void 0) {
|
|
6850
|
+
nextAssignment[key] = value;
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
6853
|
+
task.assignment = nextAssignment;
|
|
6854
|
+
if (task.assignment.agentId) task.assignedAgent = task.assignment.agentId;
|
|
6855
|
+
if (task.assignment.status === "completed") {
|
|
6856
|
+
task.assignment.completedAt = task.assignment.completedAt ?? nowIso();
|
|
6857
|
+
task.status = "completed";
|
|
6858
|
+
task.completedAt = task.assignment.completedAt;
|
|
6859
|
+
if (patch.error === void 0) delete task.assignment.error;
|
|
6860
|
+
} else if (task.assignment.status === "running") {
|
|
6861
|
+
delete task.assignment.completedAt;
|
|
6862
|
+
if (patch.error === void 0) delete task.assignment.error;
|
|
6863
|
+
task.status = "in_progress";
|
|
6864
|
+
delete task.completedAt;
|
|
6865
|
+
} else if (task.assignment.status === "failed") {
|
|
6866
|
+
delete task.assignment.completedAt;
|
|
6867
|
+
task.status = "failed";
|
|
6868
|
+
delete task.completedAt;
|
|
6869
|
+
} else if (task.assignment.status === "cancelled") {
|
|
6870
|
+
delete task.assignment.completedAt;
|
|
6871
|
+
if (patch.error === void 0) delete task.assignment.error;
|
|
6872
|
+
task.status = "blocked";
|
|
6873
|
+
delete task.completedAt;
|
|
6874
|
+
} else if (task.assignment.status === "queued" || task.assignment.status === "assigned") {
|
|
6875
|
+
delete task.assignment.completedAt;
|
|
6876
|
+
if (patch.error === void 0) delete task.assignment.error;
|
|
6877
|
+
if (task.status === "completed" || task.status === "failed") {
|
|
6878
|
+
task.status = task.assignment.status === "queued" ? "ready" : "pending";
|
|
6879
|
+
}
|
|
6880
|
+
delete task.completedAt;
|
|
6881
|
+
}
|
|
6882
|
+
task.updatedAt = nowIso();
|
|
6883
|
+
board.updatedAt = task.updatedAt;
|
|
6884
|
+
return task;
|
|
6885
|
+
});
|
|
6886
|
+
return updated?.result ? updated.board : null;
|
|
6887
|
+
}
|
|
6888
|
+
async function claimReadyTask(projectRoot, input = {}) {
|
|
6889
|
+
if (input.boardId) return claimReadyTaskOnBoard(projectRoot, input.boardId, input);
|
|
6890
|
+
for (const board of await listBoards(projectRoot)) {
|
|
6891
|
+
const claimed = await claimReadyTaskOnBoard(projectRoot, board.id, input);
|
|
6892
|
+
if (claimed) return claimed;
|
|
6893
|
+
}
|
|
6894
|
+
return null;
|
|
6895
|
+
}
|
|
6896
|
+
async function searchKanban(projectRoot, input = {}) {
|
|
6897
|
+
const boardIds = input.boardId ? [input.boardId] : (await listBoards(projectRoot)).map((b) => b.id);
|
|
6898
|
+
const results = [];
|
|
6899
|
+
for (const boardId of boardIds) {
|
|
6900
|
+
const board = await readBoard(projectRoot, boardId);
|
|
6901
|
+
if (!board) continue;
|
|
6902
|
+
for (const task of board.tasks) {
|
|
6903
|
+
if (!matchesKanbanSearch(board, task, input)) continue;
|
|
6904
|
+
results.push({ board: summarizeBoard(board), task });
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
return results;
|
|
6908
|
+
}
|
|
6909
|
+
async function listReadyTasks(projectRoot, input = {}) {
|
|
6910
|
+
const results = await searchKanban(projectRoot, { ...input, readyOnly: true });
|
|
6911
|
+
return input.limit && input.limit > 0 ? results.slice(0, input.limit) : results;
|
|
6912
|
+
}
|
|
6913
|
+
function areDependenciesMet(board, taskId) {
|
|
6914
|
+
const task = findTask(board, taskId);
|
|
6915
|
+
if (!task?.dependsOn?.length) return true;
|
|
6916
|
+
return task.dependsOn.every((depId) => {
|
|
6917
|
+
const depTask = board.tasks.find((candidate) => candidate.id === depId);
|
|
6918
|
+
return depTask?.status === "completed";
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
function buildAssignment(input) {
|
|
6922
|
+
return {
|
|
6923
|
+
status: input.status ?? "assigned",
|
|
6924
|
+
...input.agentId !== void 0 ? { agentId: input.agentId } : {},
|
|
6925
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
6926
|
+
...input.role !== void 0 ? { role: input.role } : {},
|
|
6927
|
+
...input.provider !== void 0 ? { provider: input.provider } : {},
|
|
6928
|
+
...input.model !== void 0 ? { model: input.model } : {},
|
|
6929
|
+
...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
|
|
6930
|
+
...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
|
|
6931
|
+
...input.tools !== void 0 ? { tools: input.tools } : {},
|
|
6932
|
+
...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {}
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
async function claimReadyTaskOnBoard(projectRoot, boardId, input) {
|
|
6936
|
+
const updated = await mutateBoard(projectRoot, boardId, (board) => {
|
|
6937
|
+
const candidates = input.taskId ? [findTask(board, input.taskId)].filter((task2) => Boolean(task2)) : board.tasks.filter((task2) => isTaskReadyForWork(board, task2)).sort(compareTasksForWork);
|
|
6938
|
+
const task = candidates.find((candidate) => isTaskReadyForWork(board, candidate));
|
|
6939
|
+
if (!task) return null;
|
|
6940
|
+
const current = task.assignment;
|
|
6941
|
+
const assignment = buildAssignment({
|
|
6942
|
+
...current?.agentId !== void 0 ? { agentId: current.agentId } : {},
|
|
6943
|
+
...current?.name !== void 0 ? { name: current.name } : {},
|
|
6944
|
+
...current?.role !== void 0 ? { role: current.role } : {},
|
|
6945
|
+
...current?.provider !== void 0 ? { provider: current.provider } : {},
|
|
6946
|
+
...current?.model !== void 0 ? { model: current.model } : {},
|
|
6947
|
+
...current?.fallbackProfile !== void 0 ? { fallbackProfile: current.fallbackProfile } : {},
|
|
6948
|
+
...current?.fallbackModels !== void 0 ? { fallbackModels: current.fallbackModels } : {},
|
|
6949
|
+
...current?.tools !== void 0 ? { tools: current.tools } : {},
|
|
6950
|
+
...current?.allowedCapabilities !== void 0 ? { allowedCapabilities: current.allowedCapabilities } : {},
|
|
6951
|
+
...input.agentId !== void 0 ? { agentId: input.agentId } : {},
|
|
6952
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
6953
|
+
...input.role !== void 0 ? { role: input.role } : {},
|
|
6954
|
+
...input.provider !== void 0 ? { provider: input.provider } : {},
|
|
6955
|
+
...input.model !== void 0 ? { model: input.model } : {},
|
|
6956
|
+
...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
|
|
6957
|
+
...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
|
|
6958
|
+
...input.tools !== void 0 ? { tools: input.tools } : {},
|
|
6959
|
+
...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {},
|
|
6960
|
+
...input.assignee !== void 0 ? { assignee: input.assignee } : {},
|
|
6961
|
+
status: input.status ?? "queued"
|
|
6962
|
+
});
|
|
6963
|
+
assignment.dispatchedAt = assignment.dispatchedAt ?? nowIso();
|
|
6964
|
+
task.assignment = assignment;
|
|
6965
|
+
if (assignment.agentId ?? assignment.role ?? assignment.name) {
|
|
6966
|
+
task.assignedAgent = assignment.agentId ?? assignment.role ?? assignment.name;
|
|
6967
|
+
}
|
|
6968
|
+
if (input.assignee ?? assignment.name ?? assignment.agentId) {
|
|
6969
|
+
task.assignee = input.assignee ?? assignment.name ?? assignment.agentId;
|
|
6970
|
+
}
|
|
6971
|
+
task.status = assignment.status === "running" ? "in_progress" : "ready";
|
|
6972
|
+
delete task.completedAt;
|
|
6973
|
+
task.updatedAt = nowIso();
|
|
6974
|
+
board.updatedAt = task.updatedAt;
|
|
6975
|
+
return task;
|
|
6976
|
+
});
|
|
6977
|
+
return updated?.result ? { board: updated.board, task: updated.result } : null;
|
|
6978
|
+
}
|
|
6979
|
+
function compareTasksForWork(a, b) {
|
|
6980
|
+
const priorityRank = {
|
|
6981
|
+
critical: 0,
|
|
6982
|
+
high: 1,
|
|
6983
|
+
medium: 2,
|
|
6984
|
+
low: 3
|
|
6985
|
+
};
|
|
6986
|
+
return priorityRank[a.priority] - priorityRank[b.priority] || a.columnId.localeCompare(b.columnId) || a.order - b.order || a.createdAt.localeCompare(b.createdAt);
|
|
6987
|
+
}
|
|
6988
|
+
function matchesKanbanSearch(board, task, input) {
|
|
6989
|
+
const query = input.query?.trim().toLowerCase();
|
|
6990
|
+
if (input.assignedAgent && task.assignedAgent !== input.assignedAgent) return false;
|
|
6991
|
+
if (input.status && task.status !== input.status) return false;
|
|
6992
|
+
if (input.priority && task.priority !== input.priority) return false;
|
|
6993
|
+
if (input.label && !task.labels?.includes(input.label)) return false;
|
|
6994
|
+
if (input.chainId && task.chain?.chainId !== input.chainId) return false;
|
|
6995
|
+
if (input.readyOnly && !isTaskReadyForWork(board, task)) return false;
|
|
6996
|
+
if (!query) return true;
|
|
6997
|
+
return [task.title, task.description, task.assignedAgent, task.assignee, ...task.labels ?? []].filter(Boolean).some((value) => String(value).toLowerCase().includes(query));
|
|
6998
|
+
}
|
|
6999
|
+
function findTask(board, taskId) {
|
|
7000
|
+
const exact = board.tasks.find((task) => task.id === taskId);
|
|
7001
|
+
if (exact) return exact;
|
|
7002
|
+
const matches = board.tasks.filter((task) => task.id.startsWith(taskId));
|
|
7003
|
+
if (matches.length > 1) {
|
|
7004
|
+
throw new Error(
|
|
7005
|
+
`Ambiguous kanban task id "${taskId}": ${matches.slice(0, 5).map((task) => task.id).join(", ")}`
|
|
7006
|
+
);
|
|
7007
|
+
}
|
|
7008
|
+
return matches[0];
|
|
7009
|
+
}
|
|
7010
|
+
function isTaskReadyForWork(board, task) {
|
|
7011
|
+
if (!["pending", "ready"].includes(task.status)) return false;
|
|
7012
|
+
if (task.assignment && ["queued", "running"].includes(task.assignment.status)) return false;
|
|
7013
|
+
if (task.mergedIntoTaskId) return false;
|
|
7014
|
+
if (!areDependenciesMet(board, task.id)) return false;
|
|
7015
|
+
return true;
|
|
7016
|
+
}
|
|
7017
|
+
function nowIso() {
|
|
7018
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7019
|
+
}
|
|
7020
|
+
|
|
7021
|
+
// src/coordination/director-tools.ts
|
|
6655
7022
|
init_error();
|
|
6656
7023
|
|
|
6657
7024
|
// src/coordination/dispatcher.ts
|
|
@@ -7332,6 +7699,298 @@ function makeAssignTool(director) {
|
|
|
7332
7699
|
}
|
|
7333
7700
|
};
|
|
7334
7701
|
}
|
|
7702
|
+
function makeKanbanQueueTool(director, roster) {
|
|
7703
|
+
return {
|
|
7704
|
+
name: "kanban_queue",
|
|
7705
|
+
description: "Claim dependency-ready Kanban tasks and dispatch them into the Director fleet. Preserves per-task provider/model/role/tool routing metadata, assigns each claimed task to a spawned subagent, and can await results while writing completion back to Kanban.",
|
|
7706
|
+
usageHint: 'Use action:"dispatch_ready" with optional boardId/taskId/maxTasks. Set awaitCompletion:true when you want this call to update Kanban to completed/failed before returning; otherwise use await_tasks and kanban mark_assignment later.',
|
|
7707
|
+
permission: "auto",
|
|
7708
|
+
mutating: true,
|
|
7709
|
+
capabilities: [ToolCapabilities.SUBAGENT_SPAWN, ToolCapabilities.FS_WRITE],
|
|
7710
|
+
inputSchema: {
|
|
7711
|
+
type: "object",
|
|
7712
|
+
properties: {
|
|
7713
|
+
action: { type: "string", enum: ["dispatch_ready"] },
|
|
7714
|
+
boardId: { type: "string", description: "Optional board id/prefix to claim from." },
|
|
7715
|
+
taskId: { type: "string", description: "Optional exact task id/prefix to claim." },
|
|
7716
|
+
query: { type: "string", description: "Optional natural-language filter for ready tasks." },
|
|
7717
|
+
maxTasks: {
|
|
7718
|
+
type: "number",
|
|
7719
|
+
minimum: 1,
|
|
7720
|
+
maximum: 20,
|
|
7721
|
+
description: "Maximum number of ready tasks to claim and dispatch. Default 1."
|
|
7722
|
+
},
|
|
7723
|
+
awaitCompletion: {
|
|
7724
|
+
type: "boolean",
|
|
7725
|
+
description: "When true, waits for assigned fleet task results and writes completed/failed back to Kanban."
|
|
7726
|
+
},
|
|
7727
|
+
timeoutMs: {
|
|
7728
|
+
type: "number",
|
|
7729
|
+
minimum: 1,
|
|
7730
|
+
description: "Optional per-assigned-task timeout in ms."
|
|
7731
|
+
},
|
|
7732
|
+
maxToolCalls: {
|
|
7733
|
+
type: "number",
|
|
7734
|
+
minimum: 1,
|
|
7735
|
+
description: "Optional per-assigned-task tool-call cap."
|
|
7736
|
+
},
|
|
7737
|
+
agentId: { type: "string" },
|
|
7738
|
+
name: { type: "string" },
|
|
7739
|
+
role: { type: "string" },
|
|
7740
|
+
provider: { type: "string" },
|
|
7741
|
+
model: { type: "string" },
|
|
7742
|
+
fallbackModels: { type: "array", items: { type: "string" } },
|
|
7743
|
+
tools: { type: "array", items: { type: "string" } },
|
|
7744
|
+
allowedCapabilities: { type: "array", items: { type: "string" } },
|
|
7745
|
+
worktree: {
|
|
7746
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }]
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
},
|
|
7750
|
+
async execute(input, ctx) {
|
|
7751
|
+
const i = normalizeKanbanQueueInput(input);
|
|
7752
|
+
const rawAction = input?.action;
|
|
7753
|
+
if (rawAction !== void 0 && rawAction !== "dispatch_ready") {
|
|
7754
|
+
return { error: `Unknown kanban_queue action: ${String(rawAction)}` };
|
|
7755
|
+
}
|
|
7756
|
+
const projectRoot = ctx.projectRoot;
|
|
7757
|
+
if (!projectRoot) return { error: "kanban_queue requires ctx.projectRoot." };
|
|
7758
|
+
const maxTasks = Math.max(1, Math.min(20, Math.floor(i.maxTasks ?? 1)));
|
|
7759
|
+
const candidateTaskIds = i.taskId !== void 0 ? [i.taskId] : i.query ? (await listReadyTasks(projectRoot, {
|
|
7760
|
+
...i.boardId !== void 0 ? { boardId: i.boardId } : {}
|
|
7761
|
+
})).filter((candidate) => matchesKanbanQueueQuery(candidate.task, i.query ?? "")).slice(0, maxTasks).map((candidate) => candidate.task.id) : void 0;
|
|
7762
|
+
const dispatches = [];
|
|
7763
|
+
const errors = [];
|
|
7764
|
+
const resultFailures = [];
|
|
7765
|
+
for (let index = 0; index < maxTasks; index++) {
|
|
7766
|
+
const candidateTaskId = candidateTaskIds?.[index];
|
|
7767
|
+
if (candidateTaskIds && !candidateTaskId) break;
|
|
7768
|
+
const claim = await claimReadyTask(projectRoot, {
|
|
7769
|
+
...i.boardId !== void 0 ? { boardId: i.boardId } : {},
|
|
7770
|
+
...candidateTaskId !== void 0 ? { taskId: candidateTaskId } : {},
|
|
7771
|
+
...i.agentId !== void 0 ? { agentId: i.agentId } : {},
|
|
7772
|
+
...i.name !== void 0 ? { name: i.name } : {},
|
|
7773
|
+
...i.role !== void 0 ? { role: i.role } : {},
|
|
7774
|
+
...i.provider !== void 0 ? { provider: i.provider } : {},
|
|
7775
|
+
...i.model !== void 0 ? { model: i.model } : {},
|
|
7776
|
+
...i.fallbackModels !== void 0 ? { fallbackModels: i.fallbackModels } : {},
|
|
7777
|
+
...i.tools !== void 0 ? { tools: i.tools } : {},
|
|
7778
|
+
...i.allowedCapabilities !== void 0 ? { allowedCapabilities: i.allowedCapabilities } : {},
|
|
7779
|
+
status: "queued"
|
|
7780
|
+
});
|
|
7781
|
+
if (!claim) {
|
|
7782
|
+
if (candidateTaskIds) continue;
|
|
7783
|
+
break;
|
|
7784
|
+
}
|
|
7785
|
+
let subagentId;
|
|
7786
|
+
let runTaskId;
|
|
7787
|
+
try {
|
|
7788
|
+
const config = buildKanbanSubagentConfig(claim.task, i, roster);
|
|
7789
|
+
subagentId = await director.spawn(config);
|
|
7790
|
+
runTaskId = await director.assign({
|
|
7791
|
+
id: randomUUID(),
|
|
7792
|
+
subagentId,
|
|
7793
|
+
description: buildKanbanFleetTaskPrompt(claim.board, claim.task),
|
|
7794
|
+
...i.maxToolCalls !== void 0 ? { maxToolCalls: i.maxToolCalls } : {},
|
|
7795
|
+
...i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : {},
|
|
7796
|
+
context: {
|
|
7797
|
+
kanban: {
|
|
7798
|
+
boardId: claim.board.id,
|
|
7799
|
+
taskId: claim.task.id,
|
|
7800
|
+
origin: claim.task.origin
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
});
|
|
7804
|
+
await updateTaskAssignment(projectRoot, claim.board.id, claim.task.id, {
|
|
7805
|
+
...claim.task.assignment ?? {},
|
|
7806
|
+
status: "running",
|
|
7807
|
+
subagentId,
|
|
7808
|
+
runTaskId
|
|
7809
|
+
});
|
|
7810
|
+
dispatches.push({
|
|
7811
|
+
boardId: claim.board.id,
|
|
7812
|
+
taskId: claim.task.id,
|
|
7813
|
+
title: claim.task.title,
|
|
7814
|
+
subagentId,
|
|
7815
|
+
runTaskId,
|
|
7816
|
+
...config.provider !== void 0 ? { provider: config.provider } : {},
|
|
7817
|
+
...config.model !== void 0 ? { model: config.model } : {}
|
|
7818
|
+
});
|
|
7819
|
+
} catch (err) {
|
|
7820
|
+
let message = toErrorMessage(err);
|
|
7821
|
+
if (subagentId && !runTaskId) {
|
|
7822
|
+
try {
|
|
7823
|
+
await director.terminate(subagentId);
|
|
7824
|
+
} catch (cleanupErr) {
|
|
7825
|
+
message = `${message}; cleanup failed for spawned subagent ${subagentId}: ${toErrorMessage(cleanupErr)}`;
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
await updateTaskAssignment(projectRoot, claim.board.id, claim.task.id, {
|
|
7829
|
+
...claim.task.assignment ?? {},
|
|
7830
|
+
status: "failed",
|
|
7831
|
+
...subagentId !== void 0 ? { subagentId } : {},
|
|
7832
|
+
...runTaskId !== void 0 ? { runTaskId } : {},
|
|
7833
|
+
error: message
|
|
7834
|
+
});
|
|
7835
|
+
errors.push({ taskId: claim.task.id, error: message });
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
let results;
|
|
7839
|
+
if (i.awaitCompletion && dispatches.length > 0) {
|
|
7840
|
+
results = await director.awaitTasks(dispatches.map((dispatch) => dispatch.runTaskId));
|
|
7841
|
+
for (const result of results) {
|
|
7842
|
+
const dispatch = dispatches.find((candidate) => candidate.runTaskId === result.taskId);
|
|
7843
|
+
if (!dispatch) continue;
|
|
7844
|
+
await updateTaskAssignment(projectRoot, dispatch.boardId, dispatch.taskId, {
|
|
7845
|
+
status: result.status === "success" ? "completed" : "failed",
|
|
7846
|
+
subagentId: result.subagentId,
|
|
7847
|
+
runTaskId: result.taskId,
|
|
7848
|
+
...result.status === "success" ? { lastResult: resultToText(result) } : { error: resultErrorText(result) }
|
|
7849
|
+
});
|
|
7850
|
+
if (result.status !== "success") {
|
|
7851
|
+
resultFailures.push({
|
|
7852
|
+
taskId: dispatch.taskId,
|
|
7853
|
+
runTaskId: result.taskId,
|
|
7854
|
+
status: result.status,
|
|
7855
|
+
error: resultErrorText(result)
|
|
7856
|
+
});
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7860
|
+
return {
|
|
7861
|
+
ok: errors.length === 0 && resultFailures.length === 0,
|
|
7862
|
+
dispatched: dispatches,
|
|
7863
|
+
count: dispatches.length,
|
|
7864
|
+
...results !== void 0 ? { results } : {},
|
|
7865
|
+
...errors.length > 0 ? { errors } : {},
|
|
7866
|
+
...resultFailures.length > 0 ? { resultFailures } : {},
|
|
7867
|
+
message: dispatches.length > 0 ? `Dispatched ${dispatches.length} kanban task(s).` : "No ready kanban task matched."
|
|
7868
|
+
};
|
|
7869
|
+
}
|
|
7870
|
+
};
|
|
7871
|
+
}
|
|
7872
|
+
function normalizeKanbanQueueInput(input) {
|
|
7873
|
+
const raw = input ?? {};
|
|
7874
|
+
return {
|
|
7875
|
+
action: raw.action === "dispatch_ready" ? "dispatch_ready" : void 0,
|
|
7876
|
+
boardId: typeof raw.boardId === "string" ? raw.boardId : void 0,
|
|
7877
|
+
taskId: typeof raw.taskId === "string" ? raw.taskId : void 0,
|
|
7878
|
+
query: typeof raw.query === "string" ? raw.query : void 0,
|
|
7879
|
+
maxTasks: typeof raw.maxTasks === "number" ? raw.maxTasks : void 0,
|
|
7880
|
+
awaitCompletion: typeof raw.awaitCompletion === "boolean" ? raw.awaitCompletion : void 0,
|
|
7881
|
+
timeoutMs: typeof raw.timeoutMs === "number" ? raw.timeoutMs : void 0,
|
|
7882
|
+
maxToolCalls: typeof raw.maxToolCalls === "number" ? raw.maxToolCalls : void 0,
|
|
7883
|
+
agentId: typeof raw.agentId === "string" ? raw.agentId : void 0,
|
|
7884
|
+
name: typeof raw.name === "string" ? raw.name : void 0,
|
|
7885
|
+
role: typeof raw.role === "string" ? raw.role : void 0,
|
|
7886
|
+
provider: typeof raw.provider === "string" ? raw.provider : void 0,
|
|
7887
|
+
model: typeof raw.model === "string" ? raw.model : void 0,
|
|
7888
|
+
fallbackModels: stringArray(raw.fallbackModels),
|
|
7889
|
+
tools: stringArray(raw.tools),
|
|
7890
|
+
allowedCapabilities: stringArray(raw.allowedCapabilities),
|
|
7891
|
+
worktree: normalizeWorktreeOverride(raw.worktree)
|
|
7892
|
+
};
|
|
7893
|
+
}
|
|
7894
|
+
function buildKanbanSubagentConfig(task, input, roster) {
|
|
7895
|
+
const assignment = task.assignment;
|
|
7896
|
+
const role = input.role ?? assignment?.role;
|
|
7897
|
+
const base = role && roster?.[role] ? instantiateRosterConfig2(role, roster[role] ?? {}) : void 0;
|
|
7898
|
+
const tools = input.tools ?? assignment?.tools ?? base?.tools;
|
|
7899
|
+
const name = input.name ?? assignment?.name ?? input.agentId ?? assignment?.agentId ?? task.assignedAgent ?? role ?? "kanban-agent";
|
|
7900
|
+
return {
|
|
7901
|
+
...base ?? { name },
|
|
7902
|
+
name: base?.name ?? name,
|
|
7903
|
+
...role !== void 0 ? { role } : {},
|
|
7904
|
+
...input.provider ?? assignment?.provider ? { provider: input.provider ?? assignment?.provider } : {},
|
|
7905
|
+
...input.model ?? assignment?.model ? { model: input.model ?? assignment?.model } : {},
|
|
7906
|
+
...input.fallbackModels ?? assignment?.fallbackModels ? { fallbackModels: input.fallbackModels ?? assignment?.fallbackModels } : {},
|
|
7907
|
+
...tools ? { tools: ensureKanbanTool(tools) } : {},
|
|
7908
|
+
...input.allowedCapabilities ?? assignment?.allowedCapabilities ? { allowedCapabilities: input.allowedCapabilities ?? assignment?.allowedCapabilities } : {},
|
|
7909
|
+
...input.worktree !== void 0 ? { worktree: input.worktree } : {}
|
|
7910
|
+
};
|
|
7911
|
+
}
|
|
7912
|
+
function ensureKanbanTool(tools) {
|
|
7913
|
+
return tools.includes("kanban") ? [...tools] : [...tools, "kanban"];
|
|
7914
|
+
}
|
|
7915
|
+
function matchesKanbanQueueQuery(task, query) {
|
|
7916
|
+
const normalized = query.trim().toLowerCase();
|
|
7917
|
+
if (!normalized) return true;
|
|
7918
|
+
return [
|
|
7919
|
+
task.title,
|
|
7920
|
+
task.description,
|
|
7921
|
+
task.assignedAgent,
|
|
7922
|
+
task.assignee,
|
|
7923
|
+
task.origin?.system,
|
|
7924
|
+
task.origin?.graphId,
|
|
7925
|
+
task.origin?.phaseId,
|
|
7926
|
+
...task.labels ?? []
|
|
7927
|
+
].filter(Boolean).some((value) => String(value).toLowerCase().includes(normalized));
|
|
7928
|
+
}
|
|
7929
|
+
function buildKanbanFleetTaskPrompt(board, task) {
|
|
7930
|
+
const dependencyLines = (task.dependsOn ?? []).map((depId) => board.tasks.find((candidate) => candidate.id === depId)).filter((dep) => Boolean(dep)).map((dep) => `- ${dep.title} [${dep.status}] (${dep.id})`);
|
|
7931
|
+
const checks = task.successCriteria?.map((check) => `- ${check.description}`).join("\n");
|
|
7932
|
+
const metrics = task.goalMetrics?.map(
|
|
7933
|
+
(metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
|
|
7934
|
+
).join("\n");
|
|
7935
|
+
const chain = task.chain ? [
|
|
7936
|
+
`chainId: ${task.chain.chainId}`,
|
|
7937
|
+
`order: ${task.chain.order}`,
|
|
7938
|
+
task.chain.previousTaskId ? `previous: ${task.chain.previousTaskId}` : "",
|
|
7939
|
+
task.chain.nextTaskId ? `next: ${task.chain.nextTaskId}` : ""
|
|
7940
|
+
].filter(Boolean).join("\n") : "";
|
|
7941
|
+
const routing = [
|
|
7942
|
+
task.assignment?.role ? `role: ${task.assignment.role}` : "",
|
|
7943
|
+
task.assignment?.provider ? `provider: ${task.assignment.provider}` : "",
|
|
7944
|
+
task.assignment?.model ? `model: ${task.assignment.model}` : "",
|
|
7945
|
+
task.assignment?.fallbackProfile ? `fallbackProfile: ${task.assignment.fallbackProfile}` : "",
|
|
7946
|
+
task.assignment?.fallbackModels?.length ? `fallbackModels: ${task.assignment.fallbackModels.join(", ")}` : ""
|
|
7947
|
+
].filter(Boolean);
|
|
7948
|
+
const origin = task.origin ? [
|
|
7949
|
+
`system: ${task.origin.system}`,
|
|
7950
|
+
task.origin.graphId ? `graphId: ${task.origin.graphId}` : "",
|
|
7951
|
+
task.origin.phaseId ? `phaseId: ${task.origin.phaseId}` : "",
|
|
7952
|
+
task.origin.taskId ? `sourceTaskId: ${task.origin.taskId}` : ""
|
|
7953
|
+
].filter(Boolean).join("\n") : "";
|
|
7954
|
+
return [
|
|
7955
|
+
"You are processing a claimed WrongStack Kanban task.",
|
|
7956
|
+
"",
|
|
7957
|
+
`Board: ${board.title} (${board.id})`,
|
|
7958
|
+
`Task: ${task.title} (${task.id})`,
|
|
7959
|
+
`Status: ${task.status}`,
|
|
7960
|
+
`Priority: ${task.priority}`,
|
|
7961
|
+
task.description ? `Description:
|
|
7962
|
+
${task.description}` : "",
|
|
7963
|
+
origin ? `Origin:
|
|
7964
|
+
${origin}` : "",
|
|
7965
|
+
routing.length ? `Routing hints:
|
|
7966
|
+
${routing.join("\n")}` : "",
|
|
7967
|
+
chain ? `Task chain:
|
|
7968
|
+
${chain}` : "",
|
|
7969
|
+
dependencyLines.length ? `Dependencies:
|
|
7970
|
+
${dependencyLines.join("\n")}` : "",
|
|
7971
|
+
checks ? `Success criteria:
|
|
7972
|
+
${checks}` : "",
|
|
7973
|
+
metrics ? `Goal metrics:
|
|
7974
|
+
${metrics}` : "",
|
|
7975
|
+
task.labels?.length ? `Labels: ${task.labels.join(", ")}` : "",
|
|
7976
|
+
"",
|
|
7977
|
+
"Work this task end-to-end. If scope is too broad or too small, use the kanban tool to split_task or merge_tasks instead of losing traceability.",
|
|
7978
|
+
`When you start or finish, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}", and assignmentStatus "running", "completed", or "failed". Include lastResult or error when you finish.`,
|
|
7979
|
+
"When finished, report what changed, what you verified, and any remaining blockers."
|
|
7980
|
+
].filter(Boolean).join("\n");
|
|
7981
|
+
}
|
|
7982
|
+
function resultToText(result) {
|
|
7983
|
+
if (typeof result.result === "string") return result.result;
|
|
7984
|
+
if (result.result === void 0) return "";
|
|
7985
|
+
try {
|
|
7986
|
+
return JSON.stringify(result.result);
|
|
7987
|
+
} catch {
|
|
7988
|
+
return String(result.result);
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7991
|
+
function resultErrorText(result) {
|
|
7992
|
+
return result.error ? `${result.error.kind}: ${result.error.message}` : result.status;
|
|
7993
|
+
}
|
|
7335
7994
|
function makeAwaitTasksTool(director) {
|
|
7336
7995
|
return {
|
|
7337
7996
|
name: "await_tasks",
|
|
@@ -8557,7 +9216,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8557
9216
|
taskIds.map((id) => {
|
|
8558
9217
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
8559
9218
|
if (cached) return cached;
|
|
8560
|
-
return new Promise((
|
|
9219
|
+
return new Promise((resolve13, reject) => {
|
|
8561
9220
|
const timeout = setTimeout(() => {
|
|
8562
9221
|
this.off("task.completed", handler);
|
|
8563
9222
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -8566,7 +9225,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8566
9225
|
if (result.taskId === id) {
|
|
8567
9226
|
clearTimeout(timeout);
|
|
8568
9227
|
this.off("task.completed", handler);
|
|
8569
|
-
|
|
9228
|
+
resolve13(result);
|
|
8570
9229
|
}
|
|
8571
9230
|
};
|
|
8572
9231
|
this.on("task.completed", handler);
|
|
@@ -8591,13 +9250,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8591
9250
|
const done = new Set(completed.map((r) => r.taskId));
|
|
8592
9251
|
return { completed, pending: taskIds.filter((id) => !done.has(id)) };
|
|
8593
9252
|
}
|
|
8594
|
-
return new Promise((
|
|
9253
|
+
return new Promise((resolve13) => {
|
|
8595
9254
|
let timer;
|
|
8596
9255
|
const handler = ({ result }) => {
|
|
8597
9256
|
if (!ids.has(result.taskId)) return;
|
|
8598
9257
|
if (timer) clearTimeout(timer);
|
|
8599
9258
|
this.off("task.completed", handler);
|
|
8600
|
-
|
|
9259
|
+
resolve13({
|
|
8601
9260
|
completed: [result],
|
|
8602
9261
|
pending: taskIds.filter((id) => id !== result.taskId)
|
|
8603
9262
|
});
|
|
@@ -8605,7 +9264,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8605
9264
|
if (opts?.timeoutMs !== void 0) {
|
|
8606
9265
|
timer = setTimeout(() => {
|
|
8607
9266
|
this.off("task.completed", handler);
|
|
8608
|
-
|
|
9267
|
+
resolve13({ completed: [], pending: [...taskIds], timedOut: true });
|
|
8609
9268
|
}, opts.timeoutMs);
|
|
8610
9269
|
}
|
|
8611
9270
|
this.on("task.completed", handler);
|
|
@@ -8875,12 +9534,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8875
9534
|
}
|
|
8876
9535
|
return new Promise((resolveDecision) => {
|
|
8877
9536
|
let settled = false;
|
|
8878
|
-
const
|
|
9537
|
+
const resolve13 = (d) => {
|
|
8879
9538
|
if (settled) return;
|
|
8880
9539
|
settled = true;
|
|
8881
9540
|
resolveDecision(d);
|
|
8882
9541
|
};
|
|
8883
|
-
const fallback = setTimeout(() =>
|
|
9542
|
+
const fallback = setTimeout(() => resolve13("stop"), DECISION_TIMEOUT_MS);
|
|
8884
9543
|
const sessionId = this.currentSessionId();
|
|
8885
9544
|
budget._events?.emit("budget.threshold_reached", {
|
|
8886
9545
|
...sessionId ? { sessionId } : {},
|
|
@@ -8898,11 +9557,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8898
9557
|
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
8899
9558
|
extend: (extra) => {
|
|
8900
9559
|
clearTimeout(fallback);
|
|
8901
|
-
queueMicrotask(() =>
|
|
9560
|
+
queueMicrotask(() => resolve13({ extend: extra }));
|
|
8902
9561
|
},
|
|
8903
9562
|
deny: () => {
|
|
8904
9563
|
clearTimeout(fallback);
|
|
8905
|
-
|
|
9564
|
+
resolve13("stop");
|
|
8906
9565
|
}
|
|
8907
9566
|
});
|
|
8908
9567
|
});
|
|
@@ -10367,11 +11026,11 @@ var Director = class _Director {
|
|
|
10367
11026
|
if (cached) return cached;
|
|
10368
11027
|
const existing = this.taskWaiters.get(id);
|
|
10369
11028
|
if (existing) return existing.promise;
|
|
10370
|
-
let
|
|
11029
|
+
let resolve13;
|
|
10371
11030
|
const promise = new Promise((res) => {
|
|
10372
|
-
|
|
11031
|
+
resolve13 = res;
|
|
10373
11032
|
});
|
|
10374
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
11033
|
+
this.taskWaiters.set(id, { promise, resolve: resolve13 });
|
|
10375
11034
|
return promise;
|
|
10376
11035
|
})
|
|
10377
11036
|
);
|
|
@@ -10400,10 +11059,10 @@ var Director = class _Director {
|
|
|
10400
11059
|
pending: taskIds.filter((id) => !done.has(id))
|
|
10401
11060
|
});
|
|
10402
11061
|
}
|
|
10403
|
-
return new Promise((
|
|
11062
|
+
return new Promise((resolve13) => {
|
|
10404
11063
|
const entry = {
|
|
10405
11064
|
ids: new Set(taskIds),
|
|
10406
|
-
resolve: (r) =>
|
|
11065
|
+
resolve: (r) => resolve13({
|
|
10407
11066
|
completed: [r],
|
|
10408
11067
|
pending: taskIds.filter((id) => id !== r.taskId)
|
|
10409
11068
|
})
|
|
@@ -10411,7 +11070,7 @@ var Director = class _Director {
|
|
|
10411
11070
|
if (opts?.timeoutMs !== void 0) {
|
|
10412
11071
|
entry.timer = setTimeout(() => {
|
|
10413
11072
|
this.anyWaiters.delete(entry);
|
|
10414
|
-
|
|
11073
|
+
resolve13({ completed: [], pending: [...taskIds], timedOut: true });
|
|
10415
11074
|
}, opts.timeoutMs);
|
|
10416
11075
|
}
|
|
10417
11076
|
this.anyWaiters.add(entry);
|
|
@@ -10643,6 +11302,7 @@ var Director = class _Director {
|
|
|
10643
11302
|
const t = [
|
|
10644
11303
|
makeSpawnTool(this, effectiveRoster),
|
|
10645
11304
|
makeAssignTool(this),
|
|
11305
|
+
makeKanbanQueueTool(this, effectiveRoster),
|
|
10646
11306
|
makeAwaitTasksTool(this),
|
|
10647
11307
|
makeAskTool(this),
|
|
10648
11308
|
makeAskResultTool(this),
|
|
@@ -11600,9 +12260,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11600
12260
|
let cacheHit = false;
|
|
11601
12261
|
try {
|
|
11602
12262
|
const s = await fsp7.stat(file);
|
|
11603
|
-
const
|
|
12263
|
+
const stat12 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
11604
12264
|
const cached = this._loadCache.get(id);
|
|
11605
|
-
if (cached && cached.mtimeMs ===
|
|
12265
|
+
if (cached && cached.mtimeMs === stat12.mtimeMs && cached.size === stat12.size) {
|
|
11606
12266
|
cacheHit = true;
|
|
11607
12267
|
this._loadCache.delete(id);
|
|
11608
12268
|
this._loadCache.set(id, cached);
|
|
@@ -11725,7 +12385,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11725
12385
|
this._loadCache.delete(oldest);
|
|
11726
12386
|
}
|
|
11727
12387
|
}
|
|
11728
|
-
this._loadCache.set(id, { mtimeMs:
|
|
12388
|
+
this._loadCache.set(id, { mtimeMs: stat12.mtimeMs, size: stat12.size, data });
|
|
11729
12389
|
}
|
|
11730
12390
|
return data;
|
|
11731
12391
|
} catch (err) {
|
|
@@ -11767,14 +12427,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11767
12427
|
const limit = opts?.limit;
|
|
11768
12428
|
const signal = opts?.signal;
|
|
11769
12429
|
const out = [];
|
|
11770
|
-
let
|
|
12430
|
+
let stat12;
|
|
11771
12431
|
try {
|
|
11772
|
-
|
|
12432
|
+
stat12 = await fsp7.stat(file);
|
|
11773
12433
|
} catch (err) {
|
|
11774
12434
|
if (err.code === "ENOENT") return [];
|
|
11775
12435
|
throw err;
|
|
11776
12436
|
}
|
|
11777
|
-
if (
|
|
12437
|
+
if (stat12.size === 0) return [];
|
|
11778
12438
|
let fh;
|
|
11779
12439
|
try {
|
|
11780
12440
|
fh = await fsp7.open(file, "r");
|
|
@@ -11957,15 +12617,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11957
12617
|
* Returns empty array when the index doesn't exist or is corrupt.
|
|
11958
12618
|
*/
|
|
11959
12619
|
async readIndex() {
|
|
11960
|
-
let
|
|
12620
|
+
let stat12;
|
|
11961
12621
|
try {
|
|
11962
12622
|
const s = await fsp7.stat(this.indexFile);
|
|
11963
|
-
|
|
12623
|
+
stat12 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
11964
12624
|
} catch {
|
|
11965
12625
|
this._indexCache = null;
|
|
11966
12626
|
return [];
|
|
11967
12627
|
}
|
|
11968
|
-
if (this._indexCache !== null && this._indexCache.mtimeMs ===
|
|
12628
|
+
if (this._indexCache !== null && this._indexCache.mtimeMs === stat12.mtimeMs && this._indexCache.size === stat12.size) {
|
|
11969
12629
|
return [...this._indexCache.summaries];
|
|
11970
12630
|
}
|
|
11971
12631
|
let raw;
|
|
@@ -11998,7 +12658,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
11998
12658
|
if (a.startedAt > b.startedAt) return -1;
|
|
11999
12659
|
return a.id.localeCompare(b.id);
|
|
12000
12660
|
});
|
|
12001
|
-
this._indexCache = { ...
|
|
12661
|
+
this._indexCache = { ...stat12, summaries };
|
|
12002
12662
|
return [...summaries];
|
|
12003
12663
|
}
|
|
12004
12664
|
/**
|
|
@@ -12165,8 +12825,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
12165
12825
|
if (fromManifest) return fromManifest;
|
|
12166
12826
|
try {
|
|
12167
12827
|
const full = this.sessionPath(id, ".jsonl");
|
|
12168
|
-
const
|
|
12169
|
-
const summary = await this.summarize(id,
|
|
12828
|
+
const stat12 = await fsp7.stat(full);
|
|
12829
|
+
const summary = await this.summarize(id, stat12.mtime.toISOString());
|
|
12170
12830
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
12171
12831
|
const msg = toErrorMessage(err);
|
|
12172
12832
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
@@ -12209,18 +12869,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
12209
12869
|
async summaryHeaderFor(ref) {
|
|
12210
12870
|
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
12211
12871
|
try {
|
|
12212
|
-
const
|
|
12213
|
-
if (!
|
|
12872
|
+
const stat12 = await fsp7.stat(ref.filePath);
|
|
12873
|
+
if (!stat12.isFile()) {
|
|
12214
12874
|
return {
|
|
12215
12875
|
id: ref.id,
|
|
12216
12876
|
title: "(damaged)",
|
|
12217
|
-
startedAt:
|
|
12877
|
+
startedAt: stat12.mtime.toISOString(),
|
|
12218
12878
|
model: "unknown",
|
|
12219
12879
|
provider: "unknown",
|
|
12220
12880
|
tokenTotal: 0
|
|
12221
12881
|
};
|
|
12222
12882
|
}
|
|
12223
|
-
mtime =
|
|
12883
|
+
mtime = stat12.mtime.toISOString();
|
|
12224
12884
|
} catch {
|
|
12225
12885
|
return null;
|
|
12226
12886
|
}
|
|
@@ -12376,8 +13036,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
12376
13036
|
const pruneFile = async (dir, name, prefix) => {
|
|
12377
13037
|
const jsonlPath = path4.join(dir, name);
|
|
12378
13038
|
try {
|
|
12379
|
-
const
|
|
12380
|
-
if (
|
|
13039
|
+
const stat12 = await fsp7.stat(jsonlPath);
|
|
13040
|
+
if (stat12.mtimeMs >= cutoff) return;
|
|
12381
13041
|
} catch {
|
|
12382
13042
|
return;
|
|
12383
13043
|
}
|
|
@@ -13712,6 +14372,7 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
|
13712
14372
|
defaultExecutionStrategy: "smart",
|
|
13713
14373
|
maxIterations: 100,
|
|
13714
14374
|
iterationTimeoutMs: 3e5,
|
|
14375
|
+
maxToolTimeoutMs: 3e5,
|
|
13715
14376
|
sessionTimeoutMs: 18e5,
|
|
13716
14377
|
perIterationOutputCapBytes: 1e5,
|
|
13717
14378
|
descriptionMode: Object.freeze({}),
|
|
@@ -14355,12 +15016,12 @@ function verifyFiles(tokens, files) {
|
|
|
14355
15016
|
const violations = [];
|
|
14356
15017
|
let onPalette = 0;
|
|
14357
15018
|
let offPalette = 0;
|
|
14358
|
-
for (const { path:
|
|
15019
|
+
for (const { path: path33, text } of files) {
|
|
14359
15020
|
const lines = text.split("\n");
|
|
14360
15021
|
lines.forEach((lineText, i) => {
|
|
14361
15022
|
const lineNo = i + 1;
|
|
14362
15023
|
const flag = (snippet, reason) => {
|
|
14363
|
-
violations.push({ file:
|
|
15024
|
+
violations.push({ file: path33, line: lineNo, snippet: snippet.slice(0, 80), reason });
|
|
14364
15025
|
};
|
|
14365
15026
|
for (const re of [HEX_RE, FUNC_COLOR_RE]) {
|
|
14366
15027
|
re.lastIndex = 0;
|
|
@@ -14412,14 +15073,14 @@ var SKIP_DIR = /* @__PURE__ */ new Set([
|
|
|
14412
15073
|
".design"
|
|
14413
15074
|
]);
|
|
14414
15075
|
async function walkUiFiles(root, max = 200) {
|
|
14415
|
-
const { default:
|
|
15076
|
+
const { default: fs17 } = await import('fs/promises');
|
|
14416
15077
|
const { default: nodePath } = await import('path');
|
|
14417
15078
|
const found = [];
|
|
14418
15079
|
async function rec(dir, depth) {
|
|
14419
15080
|
if (found.length >= max || depth > 8) return;
|
|
14420
15081
|
let entries;
|
|
14421
15082
|
try {
|
|
14422
|
-
entries = await
|
|
15083
|
+
entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
14423
15084
|
} catch {
|
|
14424
15085
|
return;
|
|
14425
15086
|
}
|
|
@@ -14437,13 +15098,13 @@ async function walkUiFiles(root, max = 200) {
|
|
|
14437
15098
|
return found;
|
|
14438
15099
|
}
|
|
14439
15100
|
async function runDesignVerify(projectRoot, tokens, explicitFiles) {
|
|
14440
|
-
const { default:
|
|
15101
|
+
const { default: fs17 } = await import('fs/promises');
|
|
14441
15102
|
const { default: nodePath } = await import('path');
|
|
14442
15103
|
const abs = explicitFiles && explicitFiles.length > 0 ? explicitFiles.map((f) => nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)) : await walkUiFiles(projectRoot);
|
|
14443
15104
|
const files = [];
|
|
14444
15105
|
for (const a of abs) {
|
|
14445
15106
|
try {
|
|
14446
|
-
files.push({ path: nodePath.relative(projectRoot, a), text: await
|
|
15107
|
+
files.push({ path: nodePath.relative(projectRoot, a), text: await fs17.readFile(a, "utf8") });
|
|
14447
15108
|
} catch {
|
|
14448
15109
|
}
|
|
14449
15110
|
}
|
|
@@ -14574,10 +15235,10 @@ function makeDesignVerifyToolCallMiddleware() {
|
|
|
14574
15235
|
const p = typeof input?.path === "string" ? input.path : "";
|
|
14575
15236
|
if (!p || !detectFrontendFile(p)) return out;
|
|
14576
15237
|
const ctx = out.ctx;
|
|
14577
|
-
const { default:
|
|
15238
|
+
const { default: fs17 } = await import('fs/promises');
|
|
14578
15239
|
const { default: nodePath } = await import('path');
|
|
14579
15240
|
const abs = nodePath.isAbsolute(p) ? p : nodePath.join(ctx.projectRoot, p);
|
|
14580
|
-
const text = await
|
|
15241
|
+
const text = await fs17.readFile(abs, "utf8").catch(() => "");
|
|
14581
15242
|
if (!text) return out;
|
|
14582
15243
|
const loader = getDesignKitLoader(ctx.projectRoot);
|
|
14583
15244
|
const rawTokens = await loader.readTokens(state.activeKit);
|
|
@@ -15529,8 +16190,8 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
15529
16190
|
await saveGoal(this.goalPath, abandoned, this.opts.events);
|
|
15530
16191
|
}
|
|
15531
16192
|
try {
|
|
15532
|
-
const { unlink:
|
|
15533
|
-
await
|
|
16193
|
+
const { unlink: unlink16 } = await import('fs/promises');
|
|
16194
|
+
await unlink16(this.goalPath);
|
|
15534
16195
|
} catch {
|
|
15535
16196
|
}
|
|
15536
16197
|
this.opts.onEternalStop?.();
|
|
@@ -16472,8 +17133,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
16472
17133
|
});
|
|
16473
17134
|
await Promise.race([
|
|
16474
17135
|
drainPromise,
|
|
16475
|
-
new Promise((
|
|
16476
|
-
drainTimer = setTimeout(
|
|
17136
|
+
new Promise((resolve13) => {
|
|
17137
|
+
drainTimer = setTimeout(resolve13, STREAM_DRAIN_TIMEOUT_MS);
|
|
16477
17138
|
})
|
|
16478
17139
|
]);
|
|
16479
17140
|
} finally {
|
|
@@ -16584,7 +17245,7 @@ async function runProviderWithRetry(opts) {
|
|
|
16584
17245
|
description
|
|
16585
17246
|
});
|
|
16586
17247
|
}
|
|
16587
|
-
await new Promise((
|
|
17248
|
+
await new Promise((resolve13, reject) => {
|
|
16588
17249
|
let settled = false;
|
|
16589
17250
|
const cleanup = () => {
|
|
16590
17251
|
clearTimeout(t);
|
|
@@ -16600,7 +17261,7 @@ async function runProviderWithRetry(opts) {
|
|
|
16600
17261
|
if (settled) return;
|
|
16601
17262
|
settled = true;
|
|
16602
17263
|
cleanup();
|
|
16603
|
-
|
|
17264
|
+
resolve13();
|
|
16604
17265
|
}, delay);
|
|
16605
17266
|
if (signal.aborted) {
|
|
16606
17267
|
onAbort();
|
|
@@ -16750,7 +17411,14 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
16750
17411
|
raw = textBlocks.map((b) => b.text).join("\n").trim();
|
|
16751
17412
|
} catch (err) {
|
|
16752
17413
|
if (err instanceof Error) {
|
|
16753
|
-
console.warn(
|
|
17414
|
+
console.warn(
|
|
17415
|
+
JSON.stringify({
|
|
17416
|
+
level: "warn",
|
|
17417
|
+
event: "llm_selector.call_failed",
|
|
17418
|
+
message: `selector call failed, using recency fallback: ${err.message}`,
|
|
17419
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
17420
|
+
})
|
|
17421
|
+
);
|
|
16754
17422
|
}
|
|
16755
17423
|
return this.fallbackSelect(messages, effectiveBudget);
|
|
16756
17424
|
} finally {
|
|
@@ -19017,6 +19685,7 @@ var githubServer = () => ({
|
|
|
19017
19685
|
transport: "stdio",
|
|
19018
19686
|
command: "npx",
|
|
19019
19687
|
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
19688
|
+
passthroughEnv: ["GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_TOKEN"],
|
|
19020
19689
|
permission: "confirm"
|
|
19021
19690
|
});
|
|
19022
19691
|
var context7Server = () => ({
|
|
@@ -19032,6 +19701,7 @@ var braveSearchServer = () => ({
|
|
|
19032
19701
|
transport: "stdio",
|
|
19033
19702
|
command: "npx",
|
|
19034
19703
|
args: ["-y", "@modelcontextprotocol/server-brave-search"],
|
|
19704
|
+
passthroughEnv: ["BRAVE_SEARCH_API_KEY"],
|
|
19035
19705
|
permission: "confirm"
|
|
19036
19706
|
});
|
|
19037
19707
|
var blockServer = () => ({
|
|
@@ -19048,6 +19718,7 @@ var everArtServer = () => ({
|
|
|
19048
19718
|
transport: "stdio",
|
|
19049
19719
|
command: "npx",
|
|
19050
19720
|
args: ["-y", "@modelcontextprotocol/server-everart"],
|
|
19721
|
+
passthroughEnv: ["EVERART_API_KEY"],
|
|
19051
19722
|
permission: "confirm"
|
|
19052
19723
|
});
|
|
19053
19724
|
var slackServer = () => ({
|
|
@@ -19056,6 +19727,7 @@ var slackServer = () => ({
|
|
|
19056
19727
|
transport: "stdio",
|
|
19057
19728
|
command: "npx",
|
|
19058
19729
|
args: ["-y", "@modelcontextprotocol/server-slack"],
|
|
19730
|
+
passthroughEnv: ["SLACK_BOT_TOKEN", "SLACK_TEAM_ID"],
|
|
19059
19731
|
permission: "confirm"
|
|
19060
19732
|
});
|
|
19061
19733
|
var awsServer = () => ({
|
|
@@ -19064,6 +19736,7 @@ var awsServer = () => ({
|
|
|
19064
19736
|
transport: "stdio",
|
|
19065
19737
|
command: "npx",
|
|
19066
19738
|
args: ["-y", "@modelcontextprotocol/server-aws"],
|
|
19739
|
+
passthroughEnv: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_SESSION_TOKEN"],
|
|
19067
19740
|
permission: "confirm"
|
|
19068
19741
|
});
|
|
19069
19742
|
var googleMapsServer = () => ({
|
|
@@ -19072,6 +19745,7 @@ var googleMapsServer = () => ({
|
|
|
19072
19745
|
transport: "stdio",
|
|
19073
19746
|
command: "npx",
|
|
19074
19747
|
args: ["-y", "@modelcontextprotocol/server-google-maps"],
|
|
19748
|
+
passthroughEnv: ["GOOGLE_MAPS_API_KEY"],
|
|
19075
19749
|
permission: "confirm"
|
|
19076
19750
|
});
|
|
19077
19751
|
var sentinelServer = () => ({
|
|
@@ -19089,6 +19763,7 @@ var zaiVisionServer = () => ({
|
|
|
19089
19763
|
command: "npx",
|
|
19090
19764
|
args: ["-y", "@z_ai/mcp-server@latest"],
|
|
19091
19765
|
env: { Z_AI_MODE: "ZAI" },
|
|
19766
|
+
passthroughEnv: ["Z_AI_API_KEY"],
|
|
19092
19767
|
allowedTools: [
|
|
19093
19768
|
"image_analysis",
|
|
19094
19769
|
"extract_text_from_screenshot",
|
|
@@ -19118,6 +19793,7 @@ var miniMaxVisionServer = () => ({
|
|
|
19118
19793
|
MINIMAX_API_HOST: "https://api.minimax.io",
|
|
19119
19794
|
MINIMAX_API_RESOURCE_MODE: "url"
|
|
19120
19795
|
},
|
|
19796
|
+
passthroughEnv: ["MINIMAX_API_KEY"],
|
|
19121
19797
|
allowedTools: ["understand_image"],
|
|
19122
19798
|
permission: "auto"
|
|
19123
19799
|
});
|
|
@@ -19392,8 +20068,8 @@ async function loadProjectModes(modesDir) {
|
|
|
19392
20068
|
for (const entry of entries) {
|
|
19393
20069
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
19394
20070
|
const filePath = path4.join(modesDir, entry);
|
|
19395
|
-
const
|
|
19396
|
-
if (!
|
|
20071
|
+
const stat12 = await fsp7.stat(filePath);
|
|
20072
|
+
if (!stat12.isFile()) continue;
|
|
19397
20073
|
const content = await fsp7.readFile(filePath, "utf8");
|
|
19398
20074
|
const id = path4.basename(entry, path4.extname(entry));
|
|
19399
20075
|
modes.push({
|
|
@@ -19452,10 +20128,19 @@ var FAMILY_BY_NPM = {
|
|
|
19452
20128
|
"@ai-sdk/deepseek": "openai-compatible",
|
|
19453
20129
|
"@ai-sdk/google": "google"
|
|
19454
20130
|
};
|
|
20131
|
+
var FAMILY_BY_PROVIDER_ID = {
|
|
20132
|
+
"anthropic-oauth": "anthropic-oauth",
|
|
20133
|
+
"github-copilot": "github-copilot",
|
|
20134
|
+
"openai-codex": "openai-codex"
|
|
20135
|
+
};
|
|
19455
20136
|
function classifyFamily(npm) {
|
|
19456
20137
|
if (!npm) return "unsupported";
|
|
19457
20138
|
return FAMILY_BY_NPM[npm] ?? "unsupported";
|
|
19458
20139
|
}
|
|
20140
|
+
function classifyProviderFamily(p) {
|
|
20141
|
+
const byNpm = classifyFamily(p.npm);
|
|
20142
|
+
return byNpm !== "unsupported" ? byNpm : FAMILY_BY_PROVIDER_ID[p.id] ?? "unsupported";
|
|
20143
|
+
}
|
|
19459
20144
|
var DefaultModelsRegistry = class {
|
|
19460
20145
|
/** Merged (base + overlay) payload — what every reader sees. */
|
|
19461
20146
|
payload;
|
|
@@ -19721,7 +20406,7 @@ var DefaultModelsRegistry = class {
|
|
|
19721
20406
|
return {
|
|
19722
20407
|
id: p.id,
|
|
19723
20408
|
name: p.name,
|
|
19724
|
-
family:
|
|
20409
|
+
family: classifyProviderFamily(p),
|
|
19725
20410
|
apiBase: p.api,
|
|
19726
20411
|
envVars: p.env ?? [],
|
|
19727
20412
|
doc: p.doc,
|
|
@@ -19968,9 +20653,9 @@ var DefaultHealthRegistry = class {
|
|
|
19968
20653
|
}
|
|
19969
20654
|
async runOne(check) {
|
|
19970
20655
|
let timer = null;
|
|
19971
|
-
const timeout = new Promise((
|
|
20656
|
+
const timeout = new Promise((resolve13) => {
|
|
19972
20657
|
timer = setTimeout(
|
|
19973
|
-
() =>
|
|
20658
|
+
() => resolve13({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
19974
20659
|
this.timeoutMs
|
|
19975
20660
|
);
|
|
19976
20661
|
});
|
|
@@ -20154,7 +20839,7 @@ async function startMetricsServer(opts) {
|
|
|
20154
20839
|
const tls = opts.tls;
|
|
20155
20840
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
20156
20841
|
const host = opts.host ?? "127.0.0.1";
|
|
20157
|
-
const
|
|
20842
|
+
const path33 = opts.path ?? "/metrics";
|
|
20158
20843
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
20159
20844
|
const healthRegistry = opts.healthRegistry;
|
|
20160
20845
|
const listener = (req, res) => {
|
|
@@ -20164,7 +20849,7 @@ async function startMetricsServer(opts) {
|
|
|
20164
20849
|
return;
|
|
20165
20850
|
}
|
|
20166
20851
|
const url = req.url.split("?")[0];
|
|
20167
|
-
if (url ===
|
|
20852
|
+
if (url === path33) {
|
|
20168
20853
|
let body;
|
|
20169
20854
|
try {
|
|
20170
20855
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -20210,14 +20895,14 @@ async function startMetricsServer(opts) {
|
|
|
20210
20895
|
const { createServer } = await import('http');
|
|
20211
20896
|
server = createServer(listener);
|
|
20212
20897
|
}
|
|
20213
|
-
await new Promise((
|
|
20898
|
+
await new Promise((resolve13, reject) => {
|
|
20214
20899
|
const onError = (err) => {
|
|
20215
20900
|
server.off("listening", onListening);
|
|
20216
20901
|
reject(err);
|
|
20217
20902
|
};
|
|
20218
20903
|
const onListening = () => {
|
|
20219
20904
|
server.off("error", onError);
|
|
20220
|
-
|
|
20905
|
+
resolve13();
|
|
20221
20906
|
};
|
|
20222
20907
|
server.once("error", onError);
|
|
20223
20908
|
server.once("listening", onListening);
|
|
@@ -20228,9 +20913,9 @@ async function startMetricsServer(opts) {
|
|
|
20228
20913
|
const protocol = useHttps ? "https" : "http";
|
|
20229
20914
|
return {
|
|
20230
20915
|
port: boundPort,
|
|
20231
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
20232
|
-
close: () => new Promise((
|
|
20233
|
-
server.close((err) => err ? reject(err) :
|
|
20916
|
+
url: `${protocol}://${host}:${boundPort}${path33}`,
|
|
20917
|
+
close: () => new Promise((resolve13, reject) => {
|
|
20918
|
+
server.close((err) => err ? reject(err) : resolve13());
|
|
20234
20919
|
})
|
|
20235
20920
|
};
|
|
20236
20921
|
}
|
|
@@ -20699,15 +21384,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
20699
21384
|
maxId = id;
|
|
20700
21385
|
}
|
|
20701
21386
|
}
|
|
20702
|
-
const
|
|
21387
|
+
const path33 = [];
|
|
20703
21388
|
let current = maxId;
|
|
20704
21389
|
const visited = /* @__PURE__ */ new Set();
|
|
20705
21390
|
while (current && !visited.has(current)) {
|
|
20706
21391
|
visited.add(current);
|
|
20707
|
-
|
|
21392
|
+
path33.unshift(current);
|
|
20708
21393
|
current = prev.get(current) ?? null;
|
|
20709
21394
|
}
|
|
20710
|
-
return
|
|
21395
|
+
return path33;
|
|
20711
21396
|
}
|
|
20712
21397
|
function computeParallelGroups(graph, blockedByMap) {
|
|
20713
21398
|
const groups = [];
|
|
@@ -21773,9 +22458,9 @@ var AISpecBuilder = class {
|
|
|
21773
22458
|
if (!this.sessionPath) return;
|
|
21774
22459
|
try {
|
|
21775
22460
|
const fsp20 = await import('fs/promises');
|
|
21776
|
-
const
|
|
22461
|
+
const path33 = await import('path');
|
|
21777
22462
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
21778
|
-
await fsp20.mkdir(
|
|
22463
|
+
await fsp20.mkdir(path33.dirname(this.sessionPath), { recursive: true });
|
|
21779
22464
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
21780
22465
|
} catch {
|
|
21781
22466
|
}
|
|
@@ -25984,7 +26669,7 @@ function makeCommandVerifier(options = {}) {
|
|
|
25984
26669
|
return async function verifyTask(info) {
|
|
25985
26670
|
const cmd = info.task.metadata?.[metadataKey];
|
|
25986
26671
|
if (typeof cmd !== "string" || !cmd.trim()) return { ok: true };
|
|
25987
|
-
return await new Promise((
|
|
26672
|
+
return await new Promise((resolve13) => {
|
|
25988
26673
|
const isWindows = process.platform === "win32";
|
|
25989
26674
|
const [shell, ...shellArgs] = isWindows ? ["cmd", "/d", "/c"] : ["sh", "-c"];
|
|
25990
26675
|
const child = spawn(shell, [...shellArgs, cmd], {
|
|
@@ -25997,19 +26682,19 @@ function makeCommandVerifier(options = {}) {
|
|
|
25997
26682
|
const timer = setTimeout(() => {
|
|
25998
26683
|
timedOut = true;
|
|
25999
26684
|
child.kill();
|
|
26000
|
-
|
|
26685
|
+
resolve13({ ok: false, reason: `verification timed out: ${cmd}` });
|
|
26001
26686
|
}, timeoutMs);
|
|
26002
26687
|
child.on("exit", (code) => {
|
|
26003
26688
|
clearTimeout(timer);
|
|
26004
26689
|
if (timedOut) return;
|
|
26005
|
-
|
|
26690
|
+
resolve13(
|
|
26006
26691
|
code === 0 ? { ok: true } : { ok: false, reason: `verification failed (exit ${code}): ${cmd}` }
|
|
26007
26692
|
);
|
|
26008
26693
|
});
|
|
26009
26694
|
child.on("error", (err) => {
|
|
26010
26695
|
clearTimeout(timer);
|
|
26011
26696
|
if (timedOut) return;
|
|
26012
|
-
|
|
26697
|
+
resolve13({ ok: false, reason: `verification spawn error: ${String(err)}` });
|
|
26013
26698
|
});
|
|
26014
26699
|
});
|
|
26015
26700
|
};
|
|
@@ -26110,8 +26795,8 @@ function pathLooksInsideProject(rawPath, projectRoot) {
|
|
|
26110
26795
|
if (!projectRoot) return false;
|
|
26111
26796
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
26112
26797
|
const resolved = path4.resolve(projectRoot, rawPath);
|
|
26113
|
-
const
|
|
26114
|
-
return !!
|
|
26798
|
+
const relative5 = path4.relative(projectRoot, resolved);
|
|
26799
|
+
return !!relative5 && !relative5.startsWith("..") && !path4.isAbsolute(relative5);
|
|
26115
26800
|
}
|
|
26116
26801
|
function tokenizeShell(command) {
|
|
26117
26802
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
@@ -26427,6 +27112,12 @@ var DefaultPermissionPolicy = class {
|
|
|
26427
27112
|
return false;
|
|
26428
27113
|
}
|
|
26429
27114
|
isDestructiveYoloCall(tool, input, ctx) {
|
|
27115
|
+
if (tool.name === "mcp_control") {
|
|
27116
|
+
const action = getInputString(input, "action");
|
|
27117
|
+
if (action === "list" || action === "search" || action === "activate" || action === "deactivate") {
|
|
27118
|
+
return false;
|
|
27119
|
+
}
|
|
27120
|
+
}
|
|
26430
27121
|
if (this.isDestructiveByCapability(tool)) {
|
|
26431
27122
|
const caps = tool.capabilities ?? [];
|
|
26432
27123
|
if (caps.includes(ToolCapabilities.SHELL_ARBITRARY) || caps.includes(ToolCapabilities.SHELL_RESTRICTED) || caps.includes(ToolCapabilities.SHELL_EXEC)) {
|
|
@@ -26833,8 +27524,8 @@ function unwrapDataKey(buf, keyFile) {
|
|
|
26833
27524
|
function checkKeyFilePermissions(keyFile) {
|
|
26834
27525
|
if (process.platform === "win32") return;
|
|
26835
27526
|
try {
|
|
26836
|
-
const
|
|
26837
|
-
const actualMode =
|
|
27527
|
+
const stat12 = fs13.statSync(keyFile);
|
|
27528
|
+
const actualMode = stat12.mode & 511;
|
|
26838
27529
|
if (actualMode !== KEY_FILE_MODE) {
|
|
26839
27530
|
console.warn(JSON.stringify({
|
|
26840
27531
|
level: "warn",
|
|
@@ -26922,16 +27613,16 @@ var DefaultSecretVault = class {
|
|
|
26922
27613
|
const oldVersion = this._keyVersion;
|
|
26923
27614
|
const newKey = randomBytes(KEY_BYTES);
|
|
26924
27615
|
const newVersion = oldVersion + 1;
|
|
26925
|
-
|
|
27616
|
+
fs13.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
|
|
26926
27617
|
const passphrase = getVaultPassphrase();
|
|
26927
27618
|
if (passphrase) {
|
|
26928
|
-
|
|
27619
|
+
fs13.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
|
|
26929
27620
|
} else {
|
|
26930
27621
|
const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
|
|
26931
27622
|
KEY_FILE_MAGIC.copy(keyFileBuf, 0);
|
|
26932
27623
|
keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
|
|
26933
27624
|
newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
|
|
26934
|
-
|
|
27625
|
+
fs13.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
|
|
26935
27626
|
}
|
|
26936
27627
|
checkKeyFilePermissions(this.keyFile);
|
|
26937
27628
|
this.key = newKey;
|
|
@@ -26949,7 +27640,7 @@ var DefaultSecretVault = class {
|
|
|
26949
27640
|
const passphrase = getVaultPassphrase();
|
|
26950
27641
|
if (!passphrase || !this.key) return;
|
|
26951
27642
|
try {
|
|
26952
|
-
|
|
27643
|
+
fs13.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
|
|
26953
27644
|
mode: 384
|
|
26954
27645
|
});
|
|
26955
27646
|
checkKeyFilePermissions(this.keyFile);
|
|
@@ -26959,7 +27650,7 @@ var DefaultSecretVault = class {
|
|
|
26959
27650
|
loadOrCreateKey() {
|
|
26960
27651
|
if (this.key) return this.key;
|
|
26961
27652
|
try {
|
|
26962
|
-
const buf =
|
|
27653
|
+
const buf = fs13.readFileSync(this.keyFile);
|
|
26963
27654
|
if (isWrappedKeyFile(buf)) {
|
|
26964
27655
|
const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
|
|
26965
27656
|
this.key = key2;
|
|
@@ -27006,15 +27697,15 @@ var DefaultSecretVault = class {
|
|
|
27006
27697
|
} catch (err) {
|
|
27007
27698
|
if (err.code !== "ENOENT") throw err;
|
|
27008
27699
|
}
|
|
27009
|
-
|
|
27700
|
+
fs13.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
|
|
27010
27701
|
const key = randomBytes(KEY_BYTES);
|
|
27011
27702
|
const passphrase = getVaultPassphrase();
|
|
27012
27703
|
const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
|
|
27013
27704
|
try {
|
|
27014
|
-
|
|
27705
|
+
fs13.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
|
|
27015
27706
|
} catch (err) {
|
|
27016
27707
|
if (err.code !== "EEXIST") throw err;
|
|
27017
|
-
const buf =
|
|
27708
|
+
const buf = fs13.readFileSync(this.keyFile);
|
|
27018
27709
|
if (isWrappedKeyFile(buf)) {
|
|
27019
27710
|
const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
|
|
27020
27711
|
this.key = winnerKey;
|
|
@@ -27409,6 +28100,7 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
27409
28100
|
defaultExecutionStrategy: DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,
|
|
27410
28101
|
maxIterations: DEFAULT_TOOLS_CONFIG.maxIterations,
|
|
27411
28102
|
iterationTimeoutMs: DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,
|
|
28103
|
+
maxToolTimeoutMs: DEFAULT_TOOLS_CONFIG.maxToolTimeoutMs,
|
|
27412
28104
|
sessionTimeoutMs: DEFAULT_TOOLS_CONFIG.sessionTimeoutMs,
|
|
27413
28105
|
perIterationOutputCapBytes: DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
|
|
27414
28106
|
descriptionMode: DEFAULT_TOOLS_CONFIG.descriptionMode,
|
|
@@ -28057,8 +28749,8 @@ var DefaultConfigLoader = class {
|
|
|
28057
28749
|
const t0 = Date.now();
|
|
28058
28750
|
let mtimeMs = null;
|
|
28059
28751
|
try {
|
|
28060
|
-
const
|
|
28061
|
-
mtimeMs =
|
|
28752
|
+
const stat12 = await fsp7.stat(file);
|
|
28753
|
+
mtimeMs = stat12.mtimeMs;
|
|
28062
28754
|
const cached = this.jsonCache.get(file);
|
|
28063
28755
|
if (cached && cached.mtimeMs === mtimeMs) {
|
|
28064
28756
|
return structuredClone(cached.value);
|
|
@@ -28501,8 +29193,8 @@ var FileMemoryBackend = class {
|
|
|
28501
29193
|
}
|
|
28502
29194
|
async getMtime(file) {
|
|
28503
29195
|
try {
|
|
28504
|
-
const
|
|
28505
|
-
return
|
|
29196
|
+
const stat12 = await fsp7.stat(file);
|
|
29197
|
+
return stat12.mtimeMs;
|
|
28506
29198
|
} catch {
|
|
28507
29199
|
return 0;
|
|
28508
29200
|
}
|
|
@@ -30104,8 +30796,8 @@ var DefaultSessionReader = class _DefaultSessionReader {
|
|
|
30104
30796
|
const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
|
|
30105
30797
|
let mtimeMs = null;
|
|
30106
30798
|
try {
|
|
30107
|
-
const
|
|
30108
|
-
mtimeMs =
|
|
30799
|
+
const stat12 = await fsp7.stat(sessionPath);
|
|
30800
|
+
mtimeMs = stat12.mtimeMs;
|
|
30109
30801
|
} catch {
|
|
30110
30802
|
this.eventCache.delete(sessionId);
|
|
30111
30803
|
this.eventCacheMtimes.delete(sessionId);
|