@wrongstack/core 0.8.2 → 0.8.5
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/coordination/index.js +8 -1
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +2 -2
- package/dist/defaults/index.js +52 -26
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +11 -5
- package/dist/execution/index.js +33 -8
- package/dist/execution/index.js.map +1 -1
- package/dist/index.d.ts +497 -5
- package/dist/index.js +1328 -89
- package/dist/index.js.map +1 -1
- package/dist/{plan-templates-BnlpEkX8.d.ts → plan-templates-D8zQgZzc.d.ts} +1 -1
- package/dist/sdd/index.d.ts +7 -7
- package/dist/sdd/index.js +10 -14
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/skills/index.js +7 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +1 -3
- package/dist/storage/index.js.map +1 -1
- package/dist/types/index.js +22 -3
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +40 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-gCrJ631C.d.ts → wstack-paths-Bxik3CsK.d.ts} +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -93,7 +93,7 @@ async function renameWithRetry(from, to) {
|
|
|
93
93
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
94
94
|
throw err;
|
|
95
95
|
}
|
|
96
|
-
await new Promise((
|
|
96
|
+
await new Promise((resolve7) => setTimeout(resolve7, delays[i]));
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
throw lastErr;
|
|
@@ -2692,7 +2692,7 @@ var InMemoryAgentBridge = class {
|
|
|
2692
2692
|
);
|
|
2693
2693
|
}
|
|
2694
2694
|
this.inflightGuards.add(correlationId);
|
|
2695
|
-
return new Promise((
|
|
2695
|
+
return new Promise((resolve7, reject) => {
|
|
2696
2696
|
const timer = setTimeout(() => {
|
|
2697
2697
|
this.inflightGuards.delete(correlationId);
|
|
2698
2698
|
this.pendingRequests.delete(correlationId);
|
|
@@ -2704,7 +2704,7 @@ var InMemoryAgentBridge = class {
|
|
|
2704
2704
|
return;
|
|
2705
2705
|
}
|
|
2706
2706
|
this.pendingRequests.set(correlationId, {
|
|
2707
|
-
resolve:
|
|
2707
|
+
resolve: resolve7,
|
|
2708
2708
|
reject,
|
|
2709
2709
|
timer
|
|
2710
2710
|
});
|
|
@@ -2930,7 +2930,7 @@ var ToolExecutor = class {
|
|
|
2930
2930
|
return { result, tool, durationMs: Date.now() - start };
|
|
2931
2931
|
}
|
|
2932
2932
|
if (hasMalformedArguments(use.input)) {
|
|
2933
|
-
const result = this.malformedInputResult(use);
|
|
2933
|
+
const result = this.malformedInputResult(use, extractMalformedRaw(use.input));
|
|
2934
2934
|
budget = this.decrementBudget(result, budget);
|
|
2935
2935
|
return { result, tool, durationMs: Date.now() - start };
|
|
2936
2936
|
}
|
|
@@ -3137,11 +3137,18 @@ var ToolExecutor = class {
|
|
|
3137
3137
|
is_error: true
|
|
3138
3138
|
};
|
|
3139
3139
|
}
|
|
3140
|
-
malformedInputResult(use) {
|
|
3140
|
+
malformedInputResult(use, raw) {
|
|
3141
|
+
let content = `Tool "${use.name}" received arguments that were not a valid JSON object, so they could not be parsed. Re-issue the call with the arguments encoded as a single well-formed JSON object matching the tool's input schema.`;
|
|
3142
|
+
if (raw) {
|
|
3143
|
+
const max = 800;
|
|
3144
|
+
const excerpt = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
|
|
3145
|
+
content += ` Common cause: a string field (e.g. code in old_string/new_string) contains literal newlines, quotes, or backslashes that must be JSON-escaped, or the payload was cut off mid-stream. The raw arguments received were:
|
|
3146
|
+
${excerpt}`;
|
|
3147
|
+
}
|
|
3141
3148
|
return {
|
|
3142
3149
|
type: "tool_result",
|
|
3143
3150
|
tool_use_id: use.id,
|
|
3144
|
-
content
|
|
3151
|
+
content,
|
|
3145
3152
|
is_error: true
|
|
3146
3153
|
};
|
|
3147
3154
|
}
|
|
@@ -3197,6 +3204,18 @@ function hasMalformedArguments(input) {
|
|
|
3197
3204
|
const keys = Object.keys(obj);
|
|
3198
3205
|
return keys.length === 1 && MALFORMED_ARG_MARKERS.includes(keys[0]);
|
|
3199
3206
|
}
|
|
3207
|
+
function extractMalformedRaw(input) {
|
|
3208
|
+
if (!hasMalformedArguments(input)) return void 0;
|
|
3209
|
+
const obj = input;
|
|
3210
|
+
const value = obj[Object.keys(obj)[0]];
|
|
3211
|
+
if (value === void 0 || value === null) return void 0;
|
|
3212
|
+
if (typeof value === "string") return value;
|
|
3213
|
+
try {
|
|
3214
|
+
return JSON.stringify(value);
|
|
3215
|
+
} catch {
|
|
3216
|
+
return String(value);
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3200
3219
|
|
|
3201
3220
|
// src/utils/regex-guard.ts
|
|
3202
3221
|
var MAX_PATTERN_LEN = 512;
|
|
@@ -3536,6 +3555,7 @@ function sanitizeJsonString(s) {
|
|
|
3536
3555
|
let out = s.trim();
|
|
3537
3556
|
out = stripSingleLineComments(out);
|
|
3538
3557
|
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
3558
|
+
out = escapeControlCharsInStrings(out);
|
|
3539
3559
|
try {
|
|
3540
3560
|
JSON.parse(out);
|
|
3541
3561
|
return out;
|
|
@@ -3543,6 +3563,43 @@ function sanitizeJsonString(s) {
|
|
|
3543
3563
|
return null;
|
|
3544
3564
|
}
|
|
3545
3565
|
}
|
|
3566
|
+
function escapeControlCharsInStrings(s) {
|
|
3567
|
+
let inString = false;
|
|
3568
|
+
let out = "";
|
|
3569
|
+
for (let i = 0; i < s.length; i++) {
|
|
3570
|
+
const c = s[i];
|
|
3571
|
+
if (c === '"' && (i === 0 || s[i - 1] !== "\\")) {
|
|
3572
|
+
inString = !inString;
|
|
3573
|
+
out += c;
|
|
3574
|
+
continue;
|
|
3575
|
+
}
|
|
3576
|
+
const code = c.charCodeAt(0);
|
|
3577
|
+
if (inString && code < 32) {
|
|
3578
|
+
switch (c) {
|
|
3579
|
+
case "\n":
|
|
3580
|
+
out += "\\n";
|
|
3581
|
+
break;
|
|
3582
|
+
case "\r":
|
|
3583
|
+
out += "\\r";
|
|
3584
|
+
break;
|
|
3585
|
+
case " ":
|
|
3586
|
+
out += "\\t";
|
|
3587
|
+
break;
|
|
3588
|
+
case "\b":
|
|
3589
|
+
out += "\\b";
|
|
3590
|
+
break;
|
|
3591
|
+
case "\f":
|
|
3592
|
+
out += "\\f";
|
|
3593
|
+
break;
|
|
3594
|
+
default:
|
|
3595
|
+
out += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
3596
|
+
}
|
|
3597
|
+
continue;
|
|
3598
|
+
}
|
|
3599
|
+
out += c;
|
|
3600
|
+
}
|
|
3601
|
+
return out;
|
|
3602
|
+
}
|
|
3546
3603
|
function stripSingleLineComments(s) {
|
|
3547
3604
|
let inString = false;
|
|
3548
3605
|
const chars = [];
|
|
@@ -3868,7 +3925,8 @@ function resolveWstackPaths(opts) {
|
|
|
3868
3925
|
projectSpecs: path6.join(projectDir, "specs"),
|
|
3869
3926
|
projectTaskGraphs: path6.join(projectDir, "task-graphs"),
|
|
3870
3927
|
projectSddSession: path6.join(projectDir, "sdd-session.json"),
|
|
3871
|
-
projectPlan: path6.join(projectDir, "plan.json")
|
|
3928
|
+
projectPlan: path6.join(projectDir, "plan.json"),
|
|
3929
|
+
projectAutophase: path6.join(projectDir, "autophase")
|
|
3872
3930
|
};
|
|
3873
3931
|
}
|
|
3874
3932
|
|
|
@@ -3965,11 +4023,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3965
4023
|
walk2(value, schema, "", errors);
|
|
3966
4024
|
return { ok: errors.length === 0, errors };
|
|
3967
4025
|
}
|
|
3968
|
-
function walk2(value, schema,
|
|
4026
|
+
function walk2(value, schema, path28, errors) {
|
|
3969
4027
|
if (schema.enum !== void 0) {
|
|
3970
4028
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3971
4029
|
errors.push({
|
|
3972
|
-
path:
|
|
4030
|
+
path: path28 || "<root>",
|
|
3973
4031
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3974
4032
|
});
|
|
3975
4033
|
return;
|
|
@@ -3978,7 +4036,7 @@ function walk2(value, schema, path26, errors) {
|
|
|
3978
4036
|
if (typeof schema.type === "string") {
|
|
3979
4037
|
if (!checkType(value, schema.type)) {
|
|
3980
4038
|
errors.push({
|
|
3981
|
-
path:
|
|
4039
|
+
path: path28 || "<root>",
|
|
3982
4040
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3983
4041
|
});
|
|
3984
4042
|
return;
|
|
@@ -3988,19 +4046,19 @@ function walk2(value, schema, path26, errors) {
|
|
|
3988
4046
|
const obj = value;
|
|
3989
4047
|
for (const req of schema.required ?? []) {
|
|
3990
4048
|
if (!(req in obj)) {
|
|
3991
|
-
errors.push({ path: joinPath(
|
|
4049
|
+
errors.push({ path: joinPath(path28, req), message: "required property missing" });
|
|
3992
4050
|
}
|
|
3993
4051
|
}
|
|
3994
4052
|
if (schema.properties) {
|
|
3995
4053
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3996
4054
|
if (key in obj) {
|
|
3997
|
-
walk2(obj[key], subSchema, joinPath(
|
|
4055
|
+
walk2(obj[key], subSchema, joinPath(path28, key), errors);
|
|
3998
4056
|
}
|
|
3999
4057
|
}
|
|
4000
4058
|
}
|
|
4001
4059
|
}
|
|
4002
4060
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
4003
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
4061
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path28}[${i}]`, errors));
|
|
4004
4062
|
}
|
|
4005
4063
|
}
|
|
4006
4064
|
function checkType(value, type) {
|
|
@@ -5321,9 +5379,7 @@ var RecoveryLock = class {
|
|
|
5321
5379
|
hostname: this.hostname,
|
|
5322
5380
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5323
5381
|
};
|
|
5324
|
-
|
|
5325
|
-
await fsp2.writeFile(tmp, JSON.stringify(lock), { mode: 384 });
|
|
5326
|
-
await fsp2.rename(tmp, this.file);
|
|
5382
|
+
await atomicWrite(this.file, JSON.stringify(lock), { mode: 384 });
|
|
5327
5383
|
}
|
|
5328
5384
|
/**
|
|
5329
5385
|
* Release the lock. Idempotent — silently succeeds if the file is
|
|
@@ -6565,8 +6621,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
6565
6621
|
try {
|
|
6566
6622
|
await Promise.race([
|
|
6567
6623
|
Promise.resolve(iter.return?.()),
|
|
6568
|
-
new Promise((
|
|
6569
|
-
drainTimer = setTimeout(
|
|
6624
|
+
new Promise((resolve7) => {
|
|
6625
|
+
drainTimer = setTimeout(resolve7, 500);
|
|
6570
6626
|
})
|
|
6571
6627
|
]);
|
|
6572
6628
|
} finally {
|
|
@@ -6627,7 +6683,7 @@ async function runProviderWithRetry(opts) {
|
|
|
6627
6683
|
description
|
|
6628
6684
|
});
|
|
6629
6685
|
}
|
|
6630
|
-
await new Promise((
|
|
6686
|
+
await new Promise((resolve7, reject) => {
|
|
6631
6687
|
let settled = false;
|
|
6632
6688
|
const onAbort = () => {
|
|
6633
6689
|
if (settled) return;
|
|
@@ -6640,7 +6696,7 @@ async function runProviderWithRetry(opts) {
|
|
|
6640
6696
|
settled = true;
|
|
6641
6697
|
clearTimeout(t2);
|
|
6642
6698
|
signal.removeEventListener("abort", onAbort);
|
|
6643
|
-
|
|
6699
|
+
resolve7();
|
|
6644
6700
|
}, delay);
|
|
6645
6701
|
if (signal.aborted) {
|
|
6646
6702
|
onAbort();
|
|
@@ -7303,12 +7359,18 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
7303
7359
|
failureMode;
|
|
7304
7360
|
policyProvider;
|
|
7305
7361
|
/**
|
|
7306
|
-
*
|
|
7307
|
-
*
|
|
7308
|
-
*
|
|
7309
|
-
*
|
|
7362
|
+
* Calibration factor applied to the estimator output. The estimator is now
|
|
7363
|
+
* expected to already include messages + system prompt + tool definitions
|
|
7364
|
+
* (see `estimateRequestTokens.total`), so no overhead boost is needed. Kept
|
|
7365
|
+
* as a constant so a future calibration against real provider tokenization
|
|
7366
|
+
* (BPE vs the chars/3.5 rough estimator) can dial this without touching the
|
|
7367
|
+
* threshold-check math.
|
|
7368
|
+
*
|
|
7369
|
+
* Historical note: was 1.3 when the estimator only counted messages — that
|
|
7370
|
+
* double-counted system+tools once the estimator was upgraded, firing
|
|
7371
|
+
* compaction ~30% earlier than intended (e.g. real 56% load showed as 73%).
|
|
7310
7372
|
*/
|
|
7311
|
-
static OVERHEAD_FACTOR = 1
|
|
7373
|
+
static OVERHEAD_FACTOR = 1;
|
|
7312
7374
|
/**
|
|
7313
7375
|
* Once a compaction attempt reduces nothing (preserveK protects everything,
|
|
7314
7376
|
* no oversized tool_results remain to elide), retrying on every iteration
|
|
@@ -8255,8 +8317,8 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8255
8317
|
await saveGoal(this.goalPath, abandoned);
|
|
8256
8318
|
}
|
|
8257
8319
|
try {
|
|
8258
|
-
const { unlink:
|
|
8259
|
-
await
|
|
8320
|
+
const { unlink: unlink12 } = await import('fs/promises');
|
|
8321
|
+
await unlink12(this.goalPath);
|
|
8260
8322
|
} catch {
|
|
8261
8323
|
}
|
|
8262
8324
|
this.opts.onEternalStop?.();
|
|
@@ -8278,7 +8340,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8278
8340
|
}
|
|
8279
8341
|
};
|
|
8280
8342
|
function sleep(ms) {
|
|
8281
|
-
return new Promise((
|
|
8343
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
8282
8344
|
}
|
|
8283
8345
|
|
|
8284
8346
|
// src/coordination/subagent-budget.ts
|
|
@@ -8428,12 +8490,12 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
8428
8490
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
8429
8491
|
return Promise.resolve("stop");
|
|
8430
8492
|
}
|
|
8431
|
-
return new Promise((
|
|
8493
|
+
return new Promise((resolve7) => {
|
|
8432
8494
|
let resolved = false;
|
|
8433
8495
|
const respond = (d) => {
|
|
8434
8496
|
if (resolved) return;
|
|
8435
8497
|
resolved = true;
|
|
8436
|
-
|
|
8498
|
+
resolve7(d);
|
|
8437
8499
|
};
|
|
8438
8500
|
const fallback = setTimeout(
|
|
8439
8501
|
() => respond("stop"),
|
|
@@ -11474,7 +11536,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
11474
11536
|
taskIds.map((id) => {
|
|
11475
11537
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
11476
11538
|
if (cached) return cached;
|
|
11477
|
-
return new Promise((
|
|
11539
|
+
return new Promise((resolve7, reject) => {
|
|
11478
11540
|
const timeout = setTimeout(() => {
|
|
11479
11541
|
this.off("task.completed", handler);
|
|
11480
11542
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -11483,7 +11545,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
11483
11545
|
if (result.taskId === id) {
|
|
11484
11546
|
clearTimeout(timeout);
|
|
11485
11547
|
this.off("task.completed", handler);
|
|
11486
|
-
|
|
11548
|
+
resolve7(result);
|
|
11487
11549
|
}
|
|
11488
11550
|
};
|
|
11489
11551
|
this.on("task.completed", handler);
|
|
@@ -11913,7 +11975,7 @@ function providerErrorToSubagentError(err, message, cause) {
|
|
|
11913
11975
|
|
|
11914
11976
|
// src/execution/parallel-eternal-engine.ts
|
|
11915
11977
|
function sleep2(ms) {
|
|
11916
|
-
return new Promise((
|
|
11978
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
11917
11979
|
}
|
|
11918
11980
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
11919
11981
|
var ParallelEternalEngine = class {
|
|
@@ -12694,7 +12756,7 @@ function makeSpawnTool(director, roster) {
|
|
|
12694
12756
|
catalog: roster
|
|
12695
12757
|
});
|
|
12696
12758
|
const dispatchRole = dispatchResult.role;
|
|
12697
|
-
if (roster
|
|
12759
|
+
if (roster?.[dispatchRole]) {
|
|
12698
12760
|
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole]);
|
|
12699
12761
|
} else {
|
|
12700
12762
|
const def = dispatchResult.definition;
|
|
@@ -13184,6 +13246,13 @@ var Director = class {
|
|
|
13184
13246
|
extendCounts.delete(guardKey);
|
|
13185
13247
|
return;
|
|
13186
13248
|
}
|
|
13249
|
+
if (payload.kind === "cost" && this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
13250
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
13251
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
13252
|
+
payload.deny();
|
|
13253
|
+
return;
|
|
13254
|
+
}
|
|
13255
|
+
}
|
|
13187
13256
|
extendCounts.set(guardKey, prior + 1);
|
|
13188
13257
|
setImmediate(() => {
|
|
13189
13258
|
const extra = {};
|
|
@@ -13548,11 +13617,11 @@ var Director = class {
|
|
|
13548
13617
|
if (cached) return cached;
|
|
13549
13618
|
const existing = this.taskWaiters.get(id);
|
|
13550
13619
|
if (existing) return existing.promise;
|
|
13551
|
-
let
|
|
13620
|
+
let resolve7;
|
|
13552
13621
|
const promise = new Promise((res) => {
|
|
13553
|
-
|
|
13622
|
+
resolve7 = res;
|
|
13554
13623
|
});
|
|
13555
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
13624
|
+
this.taskWaiters.set(id, { promise, resolve: resolve7 });
|
|
13556
13625
|
return promise;
|
|
13557
13626
|
})
|
|
13558
13627
|
);
|
|
@@ -13868,7 +13937,7 @@ function createDelegateTool(opts) {
|
|
|
13868
13937
|
subagentId
|
|
13869
13938
|
});
|
|
13870
13939
|
const dir = director;
|
|
13871
|
-
const result = await new Promise((
|
|
13940
|
+
const result = await new Promise((resolve7) => {
|
|
13872
13941
|
let settled = false;
|
|
13873
13942
|
let timer;
|
|
13874
13943
|
const finish = (value) => {
|
|
@@ -13877,7 +13946,7 @@ function createDelegateTool(opts) {
|
|
|
13877
13946
|
if (timer) clearTimeout(timer);
|
|
13878
13947
|
offTool();
|
|
13879
13948
|
offIter();
|
|
13880
|
-
|
|
13949
|
+
resolve7(value);
|
|
13881
13950
|
};
|
|
13882
13951
|
const arm = () => {
|
|
13883
13952
|
if (timer) clearTimeout(timer);
|
|
@@ -14682,6 +14751,13 @@ var TaskTracker = class {
|
|
|
14682
14751
|
opts;
|
|
14683
14752
|
graph = null;
|
|
14684
14753
|
transitions = [];
|
|
14754
|
+
/**
|
|
14755
|
+
* Attach an existing graph (used by PhaseOrchestrator to associate a tracker
|
|
14756
|
+
* with a phase's pre-built task graph without re-creating it).
|
|
14757
|
+
*/
|
|
14758
|
+
setGraph(graph) {
|
|
14759
|
+
this.graph = graph;
|
|
14760
|
+
}
|
|
14685
14761
|
async createGraph(specId, title) {
|
|
14686
14762
|
this.graph = {
|
|
14687
14763
|
id: crypto.randomUUID(),
|
|
@@ -14754,10 +14830,6 @@ var TaskTracker = class {
|
|
|
14754
14830
|
this.graph.updatedAt = now;
|
|
14755
14831
|
this.persist();
|
|
14756
14832
|
}
|
|
14757
|
-
/**
|
|
14758
|
-
* Update node fields (title, description, priority, estimateHours, tags).
|
|
14759
|
-
* Does NOT change status. Use updateNodeStatus for status changes.
|
|
14760
|
-
*/
|
|
14761
14833
|
updateNode(id, patch) {
|
|
14762
14834
|
if (!this.graph) throw new Error("No graph loaded");
|
|
14763
14835
|
const node = this.graph.nodes.get(id);
|
|
@@ -14835,8 +14907,7 @@ var TaskTracker = class {
|
|
|
14835
14907
|
}
|
|
14836
14908
|
return computeTaskProgress(this.graph);
|
|
14837
14909
|
}
|
|
14838
|
-
getTransitions(
|
|
14839
|
-
if (!taskId) return [...this.transitions];
|
|
14910
|
+
getTransitions(_taskId) {
|
|
14840
14911
|
return [...this.transitions];
|
|
14841
14912
|
}
|
|
14842
14913
|
unblockDependents(completedId) {
|
|
@@ -14876,18 +14947,12 @@ var TaskTracker = class {
|
|
|
14876
14947
|
* Fire-and-forget persistence with attached error handler.
|
|
14877
14948
|
* Synchronous mutators (addNode/addEdge/updateNodeStatus) use this to
|
|
14878
14949
|
* avoid forcing an async cascade through every caller; if the store
|
|
14879
|
-
*
|
|
14880
|
-
* surfaced instead of swallowed by an unhandled promise rejection.
|
|
14950
|
+
* is missing or throwing, the error is surfaced via onPersistError.
|
|
14881
14951
|
*/
|
|
14882
14952
|
persist() {
|
|
14883
14953
|
if (!this.graph) return;
|
|
14884
14954
|
this.opts.store.saveGraph(this.graph).catch((err) => {
|
|
14885
|
-
|
|
14886
|
-
else
|
|
14887
|
-
console.warn(
|
|
14888
|
-
"[task-tracker] saveGraph failed:",
|
|
14889
|
-
err instanceof Error ? err.message : String(err)
|
|
14890
|
-
);
|
|
14955
|
+
this.opts.onPersistError ? this.opts.onPersistError(err) : console.warn("[task-tracker] saveGraph failed:", err instanceof Error ? err.message : String(err));
|
|
14891
14956
|
});
|
|
14892
14957
|
}
|
|
14893
14958
|
};
|
|
@@ -15508,10 +15573,10 @@ var AISpecBuilder = class {
|
|
|
15508
15573
|
async saveSession() {
|
|
15509
15574
|
if (!this.sessionPath) return;
|
|
15510
15575
|
try {
|
|
15511
|
-
const
|
|
15512
|
-
const
|
|
15576
|
+
const fsp18 = await import('fs/promises');
|
|
15577
|
+
const path28 = await import('path');
|
|
15513
15578
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
15514
|
-
await
|
|
15579
|
+
await fsp18.mkdir(path28.dirname(this.sessionPath), { recursive: true });
|
|
15515
15580
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
15516
15581
|
} catch {
|
|
15517
15582
|
}
|
|
@@ -15520,8 +15585,8 @@ var AISpecBuilder = class {
|
|
|
15520
15585
|
async loadSession() {
|
|
15521
15586
|
if (!this.sessionPath) return false;
|
|
15522
15587
|
try {
|
|
15523
|
-
const
|
|
15524
|
-
const raw = await
|
|
15588
|
+
const fsp18 = await import('fs/promises');
|
|
15589
|
+
const raw = await fsp18.readFile(this.sessionPath, "utf8");
|
|
15525
15590
|
const loaded = JSON.parse(raw);
|
|
15526
15591
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
15527
15592
|
this.session = loaded;
|
|
@@ -15535,8 +15600,8 @@ var AISpecBuilder = class {
|
|
|
15535
15600
|
async deleteSession() {
|
|
15536
15601
|
if (!this.sessionPath) return;
|
|
15537
15602
|
try {
|
|
15538
|
-
const
|
|
15539
|
-
await
|
|
15603
|
+
const fsp18 = await import('fs/promises');
|
|
15604
|
+
await fsp18.unlink(this.sessionPath);
|
|
15540
15605
|
} catch {
|
|
15541
15606
|
}
|
|
15542
15607
|
}
|
|
@@ -16221,15 +16286,15 @@ function computeCriticalPath(graph, topoOrder, blockedByMap) {
|
|
|
16221
16286
|
maxId = id;
|
|
16222
16287
|
}
|
|
16223
16288
|
}
|
|
16224
|
-
const
|
|
16289
|
+
const path28 = [];
|
|
16225
16290
|
let current = maxId;
|
|
16226
16291
|
const visited = /* @__PURE__ */ new Set();
|
|
16227
16292
|
while (current && !visited.has(current)) {
|
|
16228
16293
|
visited.add(current);
|
|
16229
|
-
|
|
16294
|
+
path28.unshift(current);
|
|
16230
16295
|
current = prev.get(current) ?? null;
|
|
16231
16296
|
}
|
|
16232
|
-
return
|
|
16297
|
+
return path28;
|
|
16233
16298
|
}
|
|
16234
16299
|
function computeParallelGroups(graph, blockedByMap) {
|
|
16235
16300
|
const groups = [];
|
|
@@ -17024,9 +17089,9 @@ var DefaultHealthRegistry = class {
|
|
|
17024
17089
|
}
|
|
17025
17090
|
async runOne(check) {
|
|
17026
17091
|
let timer = null;
|
|
17027
|
-
const timeout = new Promise((
|
|
17092
|
+
const timeout = new Promise((resolve7) => {
|
|
17028
17093
|
timer = setTimeout(
|
|
17029
|
-
() =>
|
|
17094
|
+
() => resolve7({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
17030
17095
|
this.timeoutMs
|
|
17031
17096
|
);
|
|
17032
17097
|
});
|
|
@@ -17209,7 +17274,7 @@ async function startMetricsServer(opts) {
|
|
|
17209
17274
|
const tls = opts.tls;
|
|
17210
17275
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
17211
17276
|
const host = opts.host ?? "127.0.0.1";
|
|
17212
|
-
const
|
|
17277
|
+
const path28 = opts.path ?? "/metrics";
|
|
17213
17278
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
17214
17279
|
const healthRegistry = opts.healthRegistry;
|
|
17215
17280
|
const listener = (req, res) => {
|
|
@@ -17219,7 +17284,7 @@ async function startMetricsServer(opts) {
|
|
|
17219
17284
|
return;
|
|
17220
17285
|
}
|
|
17221
17286
|
const url = req.url.split("?")[0];
|
|
17222
|
-
if (url ===
|
|
17287
|
+
if (url === path28) {
|
|
17223
17288
|
let body;
|
|
17224
17289
|
try {
|
|
17225
17290
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -17265,14 +17330,14 @@ async function startMetricsServer(opts) {
|
|
|
17265
17330
|
const { createServer } = await import('http');
|
|
17266
17331
|
server = createServer(listener);
|
|
17267
17332
|
}
|
|
17268
|
-
await new Promise((
|
|
17333
|
+
await new Promise((resolve7, reject) => {
|
|
17269
17334
|
const onError = (err) => {
|
|
17270
17335
|
server.off("listening", onListening);
|
|
17271
17336
|
reject(err);
|
|
17272
17337
|
};
|
|
17273
17338
|
const onListening = () => {
|
|
17274
17339
|
server.off("error", onError);
|
|
17275
|
-
|
|
17340
|
+
resolve7();
|
|
17276
17341
|
};
|
|
17277
17342
|
server.once("error", onError);
|
|
17278
17343
|
server.once("listening", onListening);
|
|
@@ -17283,9 +17348,9 @@ async function startMetricsServer(opts) {
|
|
|
17283
17348
|
const protocol = useHttps ? "https" : "http";
|
|
17284
17349
|
return {
|
|
17285
17350
|
port: boundPort,
|
|
17286
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
17287
|
-
close: () => new Promise((
|
|
17288
|
-
server.close((err) => err ? reject(err) :
|
|
17351
|
+
url: `${protocol}://${host}:${boundPort}${path28}`,
|
|
17352
|
+
close: () => new Promise((resolve7, reject) => {
|
|
17353
|
+
server.close((err) => err ? reject(err) : resolve7());
|
|
17289
17354
|
})
|
|
17290
17355
|
};
|
|
17291
17356
|
}
|
|
@@ -17984,6 +18049,12 @@ async function extractTar(buf, destDir) {
|
|
|
17984
18049
|
const relPath = stripTopDir(fullPath);
|
|
17985
18050
|
if (relPath && relPath !== "." && relPath !== "..") {
|
|
17986
18051
|
const destPath = path6.join(destDir, relPath);
|
|
18052
|
+
const resolvedDest = path6.resolve(destPath);
|
|
18053
|
+
const resolvedRoot = path6.resolve(destDir);
|
|
18054
|
+
if (resolvedDest !== resolvedRoot && !resolvedDest.startsWith(resolvedRoot + path6.sep)) {
|
|
18055
|
+
offset += 512 + Math.ceil(size / 512) * 512;
|
|
18056
|
+
continue;
|
|
18057
|
+
}
|
|
17987
18058
|
if (typeflag === 53 || typeflag === 0) {
|
|
17988
18059
|
if (relPath.endsWith("/") || typeflag === 53) {
|
|
17989
18060
|
await fsp2.mkdir(destPath, { recursive: true });
|
|
@@ -19403,8 +19474,8 @@ var ReportGenerator = class {
|
|
|
19403
19474
|
try {
|
|
19404
19475
|
await stat(this.options.outputDir);
|
|
19405
19476
|
} catch {
|
|
19406
|
-
const { mkdir:
|
|
19407
|
-
await
|
|
19477
|
+
const { mkdir: mkdir13 } = await import('fs/promises');
|
|
19478
|
+
await mkdir13(this.options.outputDir, { recursive: true });
|
|
19408
19479
|
}
|
|
19409
19480
|
}
|
|
19410
19481
|
generateMarkdown(result) {
|
|
@@ -19680,7 +19751,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
19680
19751
|
const delay = Math.round(policy.delayMs(attempt));
|
|
19681
19752
|
const status = isProviderErr ? err.status : 0;
|
|
19682
19753
|
console.warn(`[SecurityScanner] retry ${attempt + 1} after ${delay}ms (status=${status}) \u2014 ${errAsErr.message}`);
|
|
19683
|
-
await new Promise((
|
|
19754
|
+
await new Promise((resolve7) => setTimeout(resolve7, delay));
|
|
19684
19755
|
return this.completeWithRetry(provider, request, abortController, attempt + 1);
|
|
19685
19756
|
}
|
|
19686
19757
|
}
|
|
@@ -20326,16 +20397,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
20326
20397
|
}
|
|
20327
20398
|
const index = Number.parseInt(reportId, 10) - 1;
|
|
20328
20399
|
if (!Number.isNaN(index) && reports[index]) {
|
|
20329
|
-
const { readFile:
|
|
20330
|
-
const content = await
|
|
20400
|
+
const { readFile: readFile32 } = await import('fs/promises');
|
|
20401
|
+
const content = await readFile32(join(reportsDir, reports[index]), "utf-8");
|
|
20331
20402
|
return { message: `# Security Report
|
|
20332
20403
|
|
|
20333
20404
|
${content}` };
|
|
20334
20405
|
}
|
|
20335
20406
|
const match = reports.find((r) => r.includes(reportId));
|
|
20336
20407
|
if (match) {
|
|
20337
|
-
const { readFile:
|
|
20338
|
-
const content = await
|
|
20408
|
+
const { readFile: readFile32 } = await import('fs/promises');
|
|
20409
|
+
const content = await readFile32(join(reportsDir, match), "utf-8");
|
|
20339
20410
|
return { message: `# Security Report
|
|
20340
20411
|
|
|
20341
20412
|
${content}` };
|
|
@@ -21177,12 +21248,12 @@ function makeContinueToNextIterationTool() {
|
|
|
21177
21248
|
// src/core/iteration-limit.ts
|
|
21178
21249
|
function requestLimitExtension(opts) {
|
|
21179
21250
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
21180
|
-
return new Promise((
|
|
21251
|
+
return new Promise((resolve7) => {
|
|
21181
21252
|
let resolved = false;
|
|
21182
21253
|
const timerFired = () => {
|
|
21183
21254
|
if (!resolved) {
|
|
21184
21255
|
resolved = true;
|
|
21185
|
-
|
|
21256
|
+
resolve7(0);
|
|
21186
21257
|
}
|
|
21187
21258
|
};
|
|
21188
21259
|
const timer = setTimeout(timerFired, timeoutMs);
|
|
@@ -21191,14 +21262,14 @@ function requestLimitExtension(opts) {
|
|
|
21191
21262
|
if (!resolved) {
|
|
21192
21263
|
resolved = true;
|
|
21193
21264
|
clearTimeout(timer);
|
|
21194
|
-
|
|
21265
|
+
resolve7(0);
|
|
21195
21266
|
}
|
|
21196
21267
|
};
|
|
21197
21268
|
const grant = (extra) => {
|
|
21198
21269
|
if (!resolved) {
|
|
21199
21270
|
resolved = true;
|
|
21200
21271
|
clearTimeout(timer);
|
|
21201
|
-
|
|
21272
|
+
resolve7(Math.max(0, extra));
|
|
21202
21273
|
}
|
|
21203
21274
|
};
|
|
21204
21275
|
events.emit("iteration.limit_reached", {
|
|
@@ -21212,7 +21283,7 @@ function requestLimitExtension(opts) {
|
|
|
21212
21283
|
if (!resolved) {
|
|
21213
21284
|
resolved = true;
|
|
21214
21285
|
clearTimeout(timer);
|
|
21215
|
-
|
|
21286
|
+
resolve7(100);
|
|
21216
21287
|
}
|
|
21217
21288
|
});
|
|
21218
21289
|
}
|
|
@@ -21781,13 +21852,13 @@ var Agent = class {
|
|
|
21781
21852
|
}
|
|
21782
21853
|
}
|
|
21783
21854
|
waitForConfirm(info) {
|
|
21784
|
-
return new Promise((
|
|
21855
|
+
return new Promise((resolve7) => {
|
|
21785
21856
|
this.events.emit("tool.confirm_needed", {
|
|
21786
21857
|
tool: info.tool,
|
|
21787
21858
|
input: info.input,
|
|
21788
21859
|
toolUseId: info.toolUseId,
|
|
21789
21860
|
suggestedPattern: info.suggestedPattern,
|
|
21790
|
-
resolve:
|
|
21861
|
+
resolve: resolve7
|
|
21791
21862
|
});
|
|
21792
21863
|
});
|
|
21793
21864
|
}
|
|
@@ -22504,12 +22575,12 @@ ${mem}`);
|
|
|
22504
22575
|
}
|
|
22505
22576
|
}
|
|
22506
22577
|
async gitStatus(root) {
|
|
22507
|
-
return new Promise((
|
|
22578
|
+
return new Promise((resolve7) => {
|
|
22508
22579
|
let settled = false;
|
|
22509
22580
|
const finish = (s) => {
|
|
22510
22581
|
if (settled) return;
|
|
22511
22582
|
settled = true;
|
|
22512
|
-
|
|
22583
|
+
resolve7(s);
|
|
22513
22584
|
};
|
|
22514
22585
|
let proc;
|
|
22515
22586
|
const timer = setTimeout(() => {
|
|
@@ -23261,6 +23332,1174 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
|
|
|
23261
23332
|
});
|
|
23262
23333
|
}
|
|
23263
23334
|
|
|
23264
|
-
|
|
23335
|
+
// src/autophase/phase-graph-builder.ts
|
|
23336
|
+
var PhaseGraphBuilder = class _PhaseGraphBuilder {
|
|
23337
|
+
constructor(opts) {
|
|
23338
|
+
this.opts = opts;
|
|
23339
|
+
}
|
|
23340
|
+
opts;
|
|
23341
|
+
async build() {
|
|
23342
|
+
const graphId = crypto.randomUUID();
|
|
23343
|
+
const phases = /* @__PURE__ */ new Map();
|
|
23344
|
+
const phaseIds = [];
|
|
23345
|
+
for (let i = 0; i < this.opts.phases.length; i++) {
|
|
23346
|
+
const tmpl = this.opts.phases[i];
|
|
23347
|
+
const phaseId = crypto.randomUUID();
|
|
23348
|
+
phaseIds.push(phaseId);
|
|
23349
|
+
const store = this.opts.externalTaskStore ?? new DefaultTaskStore();
|
|
23350
|
+
const tracker = new TaskTracker({ store });
|
|
23351
|
+
const taskGraph = await tracker.createGraph(phaseId, `${tmpl.name} Tasks`);
|
|
23352
|
+
if (tmpl.taskTemplates && tmpl.taskTemplates.length > 0) {
|
|
23353
|
+
for (const tt of tmpl.taskTemplates) {
|
|
23354
|
+
tracker.addNode({
|
|
23355
|
+
title: tt.title,
|
|
23356
|
+
description: tt.description,
|
|
23357
|
+
type: tt.type,
|
|
23358
|
+
priority: tt.priority,
|
|
23359
|
+
status: "pending",
|
|
23360
|
+
estimateHours: tt.estimateHours,
|
|
23361
|
+
tags: tt.tags ?? []
|
|
23362
|
+
});
|
|
23363
|
+
}
|
|
23364
|
+
}
|
|
23365
|
+
const phase = {
|
|
23366
|
+
id: phaseId,
|
|
23367
|
+
name: tmpl.name,
|
|
23368
|
+
description: tmpl.description,
|
|
23369
|
+
status: "pending",
|
|
23370
|
+
taskGraph,
|
|
23371
|
+
dependsOn: i > 0 ? [phaseIds[i - 1]] : [],
|
|
23372
|
+
nextPhases: i < this.opts.phases.length - 1 ? [phaseIds[i + 1]] : [],
|
|
23373
|
+
parallelizable: tmpl.parallelizable,
|
|
23374
|
+
priority: tmpl.priority,
|
|
23375
|
+
estimateHours: tmpl.estimateHours,
|
|
23376
|
+
assignedAgents: [],
|
|
23377
|
+
createdAt: Date.now(),
|
|
23378
|
+
updatedAt: Date.now()
|
|
23379
|
+
};
|
|
23380
|
+
phases.set(phaseId, phase);
|
|
23381
|
+
}
|
|
23382
|
+
const phaseArray = Array.from(phases.values());
|
|
23383
|
+
for (let i = 0; i < phaseArray.length; i++) {
|
|
23384
|
+
const phase = phaseArray[i];
|
|
23385
|
+
phase.nextPhases = i < phaseArray.length - 1 ? [phaseArray[i + 1].id] : [];
|
|
23386
|
+
phase.dependsOn = i > 0 ? [phaseArray[i - 1].id] : [];
|
|
23387
|
+
}
|
|
23388
|
+
const graph = {
|
|
23389
|
+
id: graphId,
|
|
23390
|
+
title: this.opts.title,
|
|
23391
|
+
description: this.opts.description ?? "",
|
|
23392
|
+
phases,
|
|
23393
|
+
rootPhaseIds: phaseIds.length > 0 ? [phaseIds[0]] : [],
|
|
23394
|
+
activePhaseIds: [],
|
|
23395
|
+
completedPhaseIds: [],
|
|
23396
|
+
failedPhaseIds: [],
|
|
23397
|
+
autonomous: this.opts.autonomous ?? true,
|
|
23398
|
+
stopOnComplete: true,
|
|
23399
|
+
createdAt: Date.now(),
|
|
23400
|
+
updatedAt: Date.now()
|
|
23401
|
+
};
|
|
23402
|
+
return graph;
|
|
23403
|
+
}
|
|
23404
|
+
/**
|
|
23405
|
+
* Var olan bir TaskGraph'tan fazlar oluştur.
|
|
23406
|
+
* Task'ları priority/type göre gruplayarak fazlara ayırır.
|
|
23407
|
+
*/
|
|
23408
|
+
static fromTaskGraph(taskGraph, options) {
|
|
23409
|
+
const tasksPerPhase = options.tasksPerPhase ?? 5;
|
|
23410
|
+
const nodes = Array.from(taskGraph.nodes.values());
|
|
23411
|
+
const sorted = [...nodes].sort((a, b) => {
|
|
23412
|
+
const prioOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
23413
|
+
return (prioOrder[a.priority] ?? 4) - (prioOrder[b.priority] ?? 4);
|
|
23414
|
+
});
|
|
23415
|
+
const groups = [];
|
|
23416
|
+
for (let i = 0; i < sorted.length; i += tasksPerPhase) {
|
|
23417
|
+
groups.push(sorted.slice(i, i + tasksPerPhase));
|
|
23418
|
+
}
|
|
23419
|
+
const phaseTemplates = groups.map((group, idx) => {
|
|
23420
|
+
const hasCritical = group.some((t2) => t2.priority === "critical");
|
|
23421
|
+
const totalHours = group.reduce((sum, t2) => sum + (t2.estimateHours ?? 2), 0);
|
|
23422
|
+
return {
|
|
23423
|
+
name: `Phase ${idx + 1}: ${group[0]?.title.slice(0, 30) ?? "Tasks"}`,
|
|
23424
|
+
description: group.map((t2) => t2.title).join(", "),
|
|
23425
|
+
priority: hasCritical ? "critical" : "high",
|
|
23426
|
+
estimateHours: totalHours,
|
|
23427
|
+
parallelizable: false,
|
|
23428
|
+
taskTemplates: group.map((t2) => ({
|
|
23429
|
+
title: t2.title,
|
|
23430
|
+
description: t2.description,
|
|
23431
|
+
type: t2.type,
|
|
23432
|
+
priority: t2.priority,
|
|
23433
|
+
estimateHours: t2.estimateHours ?? 2,
|
|
23434
|
+
tags: t2.tags ?? []
|
|
23435
|
+
}))
|
|
23436
|
+
};
|
|
23437
|
+
});
|
|
23438
|
+
const builder = new _PhaseGraphBuilder({
|
|
23439
|
+
...options,
|
|
23440
|
+
phases: phaseTemplates
|
|
23441
|
+
});
|
|
23442
|
+
return builder.build();
|
|
23443
|
+
}
|
|
23444
|
+
};
|
|
23445
|
+
|
|
23446
|
+
// src/autophase/phase-orchestrator.ts
|
|
23447
|
+
var PhaseOrchestrator = class {
|
|
23448
|
+
graph;
|
|
23449
|
+
ctx;
|
|
23450
|
+
opts;
|
|
23451
|
+
events;
|
|
23452
|
+
stopped = false;
|
|
23453
|
+
paused = false;
|
|
23454
|
+
runningPhases = /* @__PURE__ */ new Set();
|
|
23455
|
+
tickInterval = null;
|
|
23456
|
+
trackerCache = /* @__PURE__ */ new Map();
|
|
23457
|
+
taskRetryCounts = /* @__PURE__ */ new Map();
|
|
23458
|
+
constructor(opts) {
|
|
23459
|
+
this.graph = opts.graph;
|
|
23460
|
+
this.ctx = opts.ctx;
|
|
23461
|
+
this.events = opts.events ?? this.createNoopEventBus();
|
|
23462
|
+
this.opts = {
|
|
23463
|
+
maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
|
|
23464
|
+
maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
|
|
23465
|
+
maxRetries: opts.maxRetries ?? 2,
|
|
23466
|
+
autonomous: opts.autonomous ?? true,
|
|
23467
|
+
phaseDelayMs: opts.phaseDelayMs ?? 0,
|
|
23468
|
+
stopOnFailure: opts.stopOnFailure ?? true,
|
|
23469
|
+
events: this.events
|
|
23470
|
+
};
|
|
23471
|
+
}
|
|
23472
|
+
// ─── Lifecycle ────────────────────────────────────────────────────────────
|
|
23473
|
+
/**
|
|
23474
|
+
* Tüm faz akışını başlat.
|
|
23475
|
+
* Autonomous mode'da: kök faz(lar)ı başlatır, bitince sonrakini otomatik başlatır.
|
|
23476
|
+
*/
|
|
23477
|
+
async start() {
|
|
23478
|
+
this.stopped = false;
|
|
23479
|
+
this.paused = false;
|
|
23480
|
+
this.graph.startedAt = Date.now();
|
|
23481
|
+
this.graph.updatedAt = Date.now();
|
|
23482
|
+
let readyPhases = this.getReadyPhases();
|
|
23483
|
+
while (readyPhases.length > 0 && !this.stopped) {
|
|
23484
|
+
const batch = readyPhases.slice(0, this.opts.maxConcurrentPhases);
|
|
23485
|
+
await Promise.all(batch.map((p) => this.startPhase(p)));
|
|
23486
|
+
if (this.opts.phaseDelayMs > 0) {
|
|
23487
|
+
await this.delay(this.opts.phaseDelayMs);
|
|
23488
|
+
}
|
|
23489
|
+
readyPhases = this.getReadyPhases().filter(
|
|
23490
|
+
(p) => !this.runningPhases.has(p.id) && p.status !== "completed" && p.status !== "failed"
|
|
23491
|
+
);
|
|
23492
|
+
}
|
|
23493
|
+
if (this.opts.autonomous) {
|
|
23494
|
+
this.tickInterval = setInterval(() => this.tick(), 1e3);
|
|
23495
|
+
}
|
|
23496
|
+
}
|
|
23497
|
+
/** Duraklat — aktif fazlar çalışmaya devam eder ama yeni faz başlamaz */
|
|
23498
|
+
pause() {
|
|
23499
|
+
this.paused = true;
|
|
23500
|
+
}
|
|
23501
|
+
/** Devam et — yeni fazlar başlayabilir */
|
|
23502
|
+
resume() {
|
|
23503
|
+
this.paused = false;
|
|
23504
|
+
this.tick().catch((err) => {
|
|
23505
|
+
console.error("[phase-orchestrator] tick failed:", err instanceof Error ? err.message : String(err));
|
|
23506
|
+
});
|
|
23507
|
+
}
|
|
23508
|
+
/** Tamamen durdur — aktif fazlar da durdurulur */
|
|
23509
|
+
stop() {
|
|
23510
|
+
this.stopped = true;
|
|
23511
|
+
if (this.tickInterval) {
|
|
23512
|
+
clearInterval(this.tickInterval);
|
|
23513
|
+
this.tickInterval = null;
|
|
23514
|
+
}
|
|
23515
|
+
for (const phaseId of this.runningPhases) {
|
|
23516
|
+
const phase = this.graph.phases.get(phaseId);
|
|
23517
|
+
if (phase) {
|
|
23518
|
+
this.updatePhaseStatus(phase, "paused");
|
|
23519
|
+
}
|
|
23520
|
+
}
|
|
23521
|
+
}
|
|
23522
|
+
// ─── Tick Loop (Autonomous) ───────────────────────────────────────────────
|
|
23523
|
+
async tick() {
|
|
23524
|
+
if (this.stopped || this.paused) return;
|
|
23525
|
+
const active = this.getActivePhases();
|
|
23526
|
+
const queued = this.getReadyPhases();
|
|
23527
|
+
this.emit("autonomous.tick", {
|
|
23528
|
+
activePhases: active.map((p) => p.id),
|
|
23529
|
+
queuedPhases: queued.map((p) => p.id)
|
|
23530
|
+
});
|
|
23531
|
+
this.ctx.onTick?.({ activePhases: active, readyPhases: queued });
|
|
23532
|
+
const availableSlots = this.opts.maxConcurrentPhases - active.length;
|
|
23533
|
+
if (availableSlots > 0 && queued.length > 0) {
|
|
23534
|
+
for (const phase of queued.slice(0, availableSlots)) {
|
|
23535
|
+
if (phase.status === "pending") {
|
|
23536
|
+
await this.startPhase(phase);
|
|
23537
|
+
}
|
|
23538
|
+
}
|
|
23539
|
+
}
|
|
23540
|
+
if (this.isComplete()) {
|
|
23541
|
+
this.onGraphComplete();
|
|
23542
|
+
return;
|
|
23543
|
+
}
|
|
23544
|
+
if (this.opts.stopOnFailure && this.graph.failedPhaseIds.length > 0) {
|
|
23545
|
+
const failedPhase = this.graph.phases.get(this.graph.failedPhaseIds[0]);
|
|
23546
|
+
if (failedPhase) {
|
|
23547
|
+
this.onGraphFailed(failedPhase);
|
|
23548
|
+
}
|
|
23549
|
+
return;
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
23552
|
+
// ─── Phase Execution ──────────────────────────────────────────────────────
|
|
23553
|
+
async startPhase(phase) {
|
|
23554
|
+
if (phase.status !== "pending" && phase.status !== "ready") return;
|
|
23555
|
+
this.updatePhaseStatus(phase, "running");
|
|
23556
|
+
phase.startedAt = Date.now();
|
|
23557
|
+
this.runningPhases.add(phase.id);
|
|
23558
|
+
this.graph.activePhaseIds.push(phase.id);
|
|
23559
|
+
this.emit("phase.started", { phaseId: phase.id, name: phase.name });
|
|
23560
|
+
try {
|
|
23561
|
+
await this.executePhaseTasks(phase);
|
|
23562
|
+
const failedTasks = this.getFailedTaskCount(phase);
|
|
23563
|
+
const completedTasks = this.getCompletedTaskCount(phase);
|
|
23564
|
+
this.emit("phase.allTasksDone", {
|
|
23565
|
+
phaseId: phase.id,
|
|
23566
|
+
completed: completedTasks,
|
|
23567
|
+
failed: failedTasks
|
|
23568
|
+
});
|
|
23569
|
+
if (failedTasks > 0 && this.opts.stopOnFailure) {
|
|
23570
|
+
this.updatePhaseStatus(phase, "failed");
|
|
23571
|
+
phase.completedAt = Date.now();
|
|
23572
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23573
|
+
this.runningPhases.delete(phase.id);
|
|
23574
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23575
|
+
this.emit("phase.failed", {
|
|
23576
|
+
phaseId: phase.id,
|
|
23577
|
+
name: phase.name,
|
|
23578
|
+
error: `${failedTasks} task(s) failed`
|
|
23579
|
+
});
|
|
23580
|
+
this.ctx.onPhaseFail?.(phase, new Error(`${failedTasks} task(s) failed`));
|
|
23581
|
+
} else {
|
|
23582
|
+
this.updatePhaseStatus(phase, "completed");
|
|
23583
|
+
phase.completedAt = Date.now();
|
|
23584
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23585
|
+
this.runningPhases.delete(phase.id);
|
|
23586
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23587
|
+
this.graph.completedPhaseIds.push(phase.id);
|
|
23588
|
+
this.emit("phase.completed", {
|
|
23589
|
+
phaseId: phase.id,
|
|
23590
|
+
name: phase.name,
|
|
23591
|
+
durationMs: phase.actualDurationMs
|
|
23592
|
+
});
|
|
23593
|
+
this.ctx.onPhaseComplete?.(phase);
|
|
23594
|
+
}
|
|
23595
|
+
} catch (error) {
|
|
23596
|
+
this.updatePhaseStatus(phase, "failed");
|
|
23597
|
+
phase.completedAt = Date.now();
|
|
23598
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23599
|
+
this.runningPhases.delete(phase.id);
|
|
23600
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23601
|
+
this.graph.failedPhaseIds.push(phase.id);
|
|
23602
|
+
this.emit("phase.failed", {
|
|
23603
|
+
phaseId: phase.id,
|
|
23604
|
+
name: phase.name,
|
|
23605
|
+
error: error instanceof Error ? error.message : String(error)
|
|
23606
|
+
});
|
|
23607
|
+
this.ctx.onPhaseFail?.(phase, error instanceof Error ? error : new Error(String(error)));
|
|
23608
|
+
}
|
|
23609
|
+
}
|
|
23610
|
+
async executePhaseTasks(phase) {
|
|
23611
|
+
const pendingTasks = this.getExecutableTasks(phase);
|
|
23612
|
+
while (pendingTasks.length > 0 && !this.stopped) {
|
|
23613
|
+
const batch = pendingTasks.splice(0, this.opts.maxConcurrentTasks);
|
|
23614
|
+
const results = await Promise.allSettled(
|
|
23615
|
+
batch.map((task) => this.executeSingleTask(task, phase))
|
|
23616
|
+
);
|
|
23617
|
+
for (let i = 0; i < results.length; i++) {
|
|
23618
|
+
const result = results[i];
|
|
23619
|
+
const task = batch[i];
|
|
23620
|
+
if (!result || !task) continue;
|
|
23621
|
+
if (result.status === "fulfilled") {
|
|
23622
|
+
this.markTaskCompleted(phase, task);
|
|
23623
|
+
} else {
|
|
23624
|
+
this.markTaskFailed(phase, task, result.reason);
|
|
23625
|
+
}
|
|
23626
|
+
}
|
|
23627
|
+
const newReady = this.getExecutableTasks(phase);
|
|
23628
|
+
pendingTasks.length = 0;
|
|
23629
|
+
pendingTasks.push(...newReady);
|
|
23630
|
+
}
|
|
23631
|
+
}
|
|
23632
|
+
async executeSingleTask(task, phase) {
|
|
23633
|
+
const tracker = this.getTrackerForPhase(phase);
|
|
23634
|
+
tracker.updateNodeStatus(task.id, "in_progress");
|
|
23635
|
+
return this.ctx.executeTask(task, phase.id);
|
|
23636
|
+
}
|
|
23637
|
+
markTaskCompleted(phase, task) {
|
|
23638
|
+
const tracker = this.getTrackerForPhase(phase);
|
|
23639
|
+
tracker.updateNodeStatus(task.id, "completed");
|
|
23640
|
+
this.emit("phase.taskCompleted", {
|
|
23641
|
+
phaseId: phase.id,
|
|
23642
|
+
taskId: task.id,
|
|
23643
|
+
taskTitle: task.title
|
|
23644
|
+
});
|
|
23645
|
+
}
|
|
23646
|
+
markTaskFailed(phase, task, error) {
|
|
23647
|
+
const tracker = this.getTrackerForPhase(phase);
|
|
23648
|
+
const taskKey = `${phase.id}:${task.id}`;
|
|
23649
|
+
const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
|
|
23650
|
+
if (currentRetries < this.opts.maxRetries) {
|
|
23651
|
+
this.taskRetryCounts.set(taskKey, currentRetries + 1);
|
|
23652
|
+
tracker.updateNodeStatus(task.id, "pending", `Retry ${currentRetries + 1}/${this.opts.maxRetries}`);
|
|
23653
|
+
this.emit("phase.taskRetrying", {
|
|
23654
|
+
phaseId: phase.id,
|
|
23655
|
+
taskId: task.id,
|
|
23656
|
+
taskTitle: task.title,
|
|
23657
|
+
attempt: currentRetries + 1,
|
|
23658
|
+
maxRetries: this.opts.maxRetries
|
|
23659
|
+
});
|
|
23660
|
+
} else {
|
|
23661
|
+
tracker.updateNodeStatus(task.id, "failed", error instanceof Error ? error.message : String(error));
|
|
23662
|
+
this.emit("phase.taskFailed", {
|
|
23663
|
+
phaseId: phase.id,
|
|
23664
|
+
taskId: task.id,
|
|
23665
|
+
taskTitle: task.title,
|
|
23666
|
+
error: error instanceof Error ? error.message : String(error)
|
|
23667
|
+
});
|
|
23668
|
+
}
|
|
23669
|
+
}
|
|
23670
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
23671
|
+
getReadyPhases() {
|
|
23672
|
+
const ready = [];
|
|
23673
|
+
for (const phase of this.graph.phases.values()) {
|
|
23674
|
+
if (phase.status !== "pending") continue;
|
|
23675
|
+
const depsDone = phase.dependsOn.every((depId) => {
|
|
23676
|
+
const dep = this.graph.phases.get(depId);
|
|
23677
|
+
return dep?.status === "completed" || dep?.status === "skipped";
|
|
23678
|
+
});
|
|
23679
|
+
if (depsDone || phase.parallelizable) {
|
|
23680
|
+
ready.push(phase);
|
|
23681
|
+
}
|
|
23682
|
+
}
|
|
23683
|
+
const prioOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
23684
|
+
ready.sort((a, b) => (prioOrder[a.priority] ?? 4) - (prioOrder[b.priority] ?? 4));
|
|
23685
|
+
return ready;
|
|
23686
|
+
}
|
|
23687
|
+
getActivePhases() {
|
|
23688
|
+
return Array.from(this.graph.phases.values()).filter((p) => p.status === "running");
|
|
23689
|
+
}
|
|
23690
|
+
getExecutableTasks(phase) {
|
|
23691
|
+
const tracker = this.getTrackerForPhase(phase);
|
|
23692
|
+
return tracker.getAllNodes({ status: ["pending", "blocked"] }).filter((n) => n.status === "pending" && tracker.canStart(n.id)).sort((a, b) => {
|
|
23693
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
23694
|
+
return (priorityOrder[a.priority] ?? 4) - (priorityOrder[b.priority] ?? 4);
|
|
23695
|
+
});
|
|
23696
|
+
}
|
|
23697
|
+
getTrackerForPhase(phase) {
|
|
23698
|
+
if (this.trackerCache.has(phase.id)) {
|
|
23699
|
+
return this.trackerCache.get(phase.id);
|
|
23700
|
+
}
|
|
23701
|
+
const store = new DefaultTaskStore();
|
|
23702
|
+
const tracker = new TaskTracker({ store });
|
|
23703
|
+
tracker.setGraph(phase.taskGraph);
|
|
23704
|
+
this.trackerCache.set(phase.id, tracker);
|
|
23705
|
+
return tracker;
|
|
23706
|
+
}
|
|
23707
|
+
getFailedTaskCount(phase) {
|
|
23708
|
+
return this.getTrackerForPhase(phase).getAllNodes({ status: ["failed"] }).length;
|
|
23709
|
+
}
|
|
23710
|
+
getCompletedTaskCount(phase) {
|
|
23711
|
+
return this.getTrackerForPhase(phase).getAllNodes({ status: ["completed"] }).length;
|
|
23712
|
+
}
|
|
23713
|
+
updatePhaseStatus(phase, status) {
|
|
23714
|
+
const from = phase.status;
|
|
23715
|
+
phase.status = status;
|
|
23716
|
+
phase.updatedAt = Date.now();
|
|
23717
|
+
this.graph.updatedAt = Date.now();
|
|
23718
|
+
this.emit("phase.statusChange", { phaseId: phase.id, from, to: status });
|
|
23719
|
+
}
|
|
23720
|
+
isComplete() {
|
|
23721
|
+
const allPhases = Array.from(this.graph.phases.values());
|
|
23722
|
+
return allPhases.every(
|
|
23723
|
+
(p) => p.status === "completed" || p.status === "skipped" || p.status === "failed"
|
|
23724
|
+
);
|
|
23725
|
+
}
|
|
23726
|
+
onGraphComplete() {
|
|
23727
|
+
this.graph.completedAt = Date.now();
|
|
23728
|
+
const durationMs = this.graph.completedAt - (this.graph.startedAt ?? this.graph.completedAt);
|
|
23729
|
+
this.emit("graph.completed", { graphId: this.graph.id, durationMs });
|
|
23730
|
+
this.stop();
|
|
23731
|
+
}
|
|
23732
|
+
onGraphFailed(failedPhase) {
|
|
23733
|
+
this.emit("graph.failed", {
|
|
23734
|
+
graphId: this.graph.id,
|
|
23735
|
+
failedPhaseId: failedPhase.id,
|
|
23736
|
+
error: `Phase "${failedPhase.name}" failed`
|
|
23737
|
+
});
|
|
23738
|
+
this.stop();
|
|
23739
|
+
}
|
|
23740
|
+
// ─── Progress ─────────────────────────────────────────────────────────────
|
|
23741
|
+
getProgress() {
|
|
23742
|
+
const phases = Array.from(this.graph.phases.values());
|
|
23743
|
+
let pending = 0;
|
|
23744
|
+
let ready = 0;
|
|
23745
|
+
let running = 0;
|
|
23746
|
+
let paused = 0;
|
|
23747
|
+
let completed = 0;
|
|
23748
|
+
let failed = 0;
|
|
23749
|
+
let skipped = 0;
|
|
23750
|
+
let totalTasks = 0;
|
|
23751
|
+
let completedTasks = 0;
|
|
23752
|
+
let failedTasks = 0;
|
|
23753
|
+
let estimatedHours = 0;
|
|
23754
|
+
let actualHours = 0;
|
|
23755
|
+
for (const p of phases) {
|
|
23756
|
+
switch (p.status) {
|
|
23757
|
+
case "pending":
|
|
23758
|
+
pending++;
|
|
23759
|
+
break;
|
|
23760
|
+
case "ready":
|
|
23761
|
+
ready++;
|
|
23762
|
+
break;
|
|
23763
|
+
case "running":
|
|
23764
|
+
running++;
|
|
23765
|
+
break;
|
|
23766
|
+
case "paused":
|
|
23767
|
+
paused++;
|
|
23768
|
+
break;
|
|
23769
|
+
case "completed":
|
|
23770
|
+
completed++;
|
|
23771
|
+
break;
|
|
23772
|
+
case "failed":
|
|
23773
|
+
failed++;
|
|
23774
|
+
break;
|
|
23775
|
+
case "skipped":
|
|
23776
|
+
skipped++;
|
|
23777
|
+
break;
|
|
23778
|
+
}
|
|
23779
|
+
estimatedHours += p.estimateHours;
|
|
23780
|
+
if (p.actualDurationMs) actualHours += p.actualDurationMs / 36e5;
|
|
23781
|
+
const tracker = this.getTrackerForPhase(p);
|
|
23782
|
+
const progress = tracker.getProgress();
|
|
23783
|
+
totalTasks += progress.total;
|
|
23784
|
+
completedTasks += progress.completed;
|
|
23785
|
+
failedTasks += progress.failed;
|
|
23786
|
+
}
|
|
23787
|
+
const totalPhases = phases.length;
|
|
23788
|
+
const done = completed + skipped;
|
|
23789
|
+
return {
|
|
23790
|
+
totalPhases,
|
|
23791
|
+
pending,
|
|
23792
|
+
ready,
|
|
23793
|
+
running,
|
|
23794
|
+
paused,
|
|
23795
|
+
completed,
|
|
23796
|
+
failed,
|
|
23797
|
+
skipped,
|
|
23798
|
+
percentComplete: totalPhases > 0 ? Math.round(done / totalPhases * 100) : 0,
|
|
23799
|
+
totalTasks,
|
|
23800
|
+
completedTasks,
|
|
23801
|
+
failedTasks,
|
|
23802
|
+
estimatedHours,
|
|
23803
|
+
actualHours
|
|
23804
|
+
};
|
|
23805
|
+
}
|
|
23806
|
+
getGraph() {
|
|
23807
|
+
return this.graph;
|
|
23808
|
+
}
|
|
23809
|
+
isRunning() {
|
|
23810
|
+
return !this.stopped && this.runningPhases.size > 0;
|
|
23811
|
+
}
|
|
23812
|
+
isPaused() {
|
|
23813
|
+
return this.paused;
|
|
23814
|
+
}
|
|
23815
|
+
// ─── Agent Assignment ─────────────────────────────────────────────────────
|
|
23816
|
+
assignAgent(phaseId, agentId) {
|
|
23817
|
+
const phase = this.graph.phases.get(phaseId);
|
|
23818
|
+
if (!phase) return;
|
|
23819
|
+
if (!phase.assignedAgents.includes(agentId)) {
|
|
23820
|
+
phase.assignedAgents.push(agentId);
|
|
23821
|
+
this.emit("agent.assigned", { phaseId, agentId });
|
|
23822
|
+
}
|
|
23823
|
+
}
|
|
23824
|
+
releaseAgent(phaseId, agentId) {
|
|
23825
|
+
const phase = this.graph.phases.get(phaseId);
|
|
23826
|
+
if (!phase) return;
|
|
23827
|
+
phase.assignedAgents = phase.assignedAgents.filter((id) => id !== agentId);
|
|
23828
|
+
this.emit("agent.released", { phaseId, agentId });
|
|
23829
|
+
}
|
|
23830
|
+
// ─── Events ───────────────────────────────────────────────────────────────
|
|
23831
|
+
emit(event, payload) {
|
|
23832
|
+
this.events.emit(event, payload);
|
|
23833
|
+
}
|
|
23834
|
+
createNoopEventBus() {
|
|
23835
|
+
return {
|
|
23836
|
+
emit: () => {
|
|
23837
|
+
},
|
|
23838
|
+
on: () => {
|
|
23839
|
+
},
|
|
23840
|
+
off: () => {
|
|
23841
|
+
},
|
|
23842
|
+
once: () => {
|
|
23843
|
+
},
|
|
23844
|
+
listeners: /* @__PURE__ */ new Map(),
|
|
23845
|
+
wildcards: /* @__PURE__ */ new Set(),
|
|
23846
|
+
setLogger: () => {
|
|
23847
|
+
},
|
|
23848
|
+
onAny: () => {
|
|
23849
|
+
},
|
|
23850
|
+
offAny: () => {
|
|
23851
|
+
},
|
|
23852
|
+
emitAsync: async () => [],
|
|
23853
|
+
waitFor: async () => {
|
|
23854
|
+
}
|
|
23855
|
+
};
|
|
23856
|
+
}
|
|
23857
|
+
delay(ms) {
|
|
23858
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
23859
|
+
}
|
|
23860
|
+
};
|
|
23861
|
+
|
|
23862
|
+
// src/autophase/auto-phase-runner.ts
|
|
23863
|
+
var AutoPhaseRunner = class {
|
|
23864
|
+
graph = null;
|
|
23865
|
+
orchestrator = null;
|
|
23866
|
+
opts;
|
|
23867
|
+
progressInterval = null;
|
|
23868
|
+
graphCompletedHandler = (payload) => {
|
|
23869
|
+
const p = payload;
|
|
23870
|
+
if (this.graph && p.graphId === this.graph.id) {
|
|
23871
|
+
this.opts.onComplete?.(this.graph);
|
|
23872
|
+
this.cleanup();
|
|
23873
|
+
}
|
|
23874
|
+
};
|
|
23875
|
+
graphFailedHandler = (payload) => {
|
|
23876
|
+
const p = payload;
|
|
23877
|
+
if (this.graph && p.graphId === this.graph.id) {
|
|
23878
|
+
const failedPhase = this.graph.phases.get(p.failedPhaseId);
|
|
23879
|
+
if (failedPhase) {
|
|
23880
|
+
this.opts.onFail?.(this.graph, failedPhase, new Error(p.error));
|
|
23881
|
+
}
|
|
23882
|
+
this.cleanup();
|
|
23883
|
+
}
|
|
23884
|
+
};
|
|
23885
|
+
/** Stores the unsubscribe function returned by EventBus.on() */
|
|
23886
|
+
unsubscribeCompleted = null;
|
|
23887
|
+
unsubscribeFailed = null;
|
|
23888
|
+
constructor(opts) {
|
|
23889
|
+
this.opts = opts;
|
|
23890
|
+
}
|
|
23891
|
+
async start() {
|
|
23892
|
+
const builder = new PhaseGraphBuilder({
|
|
23893
|
+
title: this.opts.title,
|
|
23894
|
+
description: this.opts.description,
|
|
23895
|
+
phases: this.opts.phases,
|
|
23896
|
+
autonomous: this.opts.autonomous,
|
|
23897
|
+
stopOnFailure: this.opts.stopOnFailure
|
|
23898
|
+
});
|
|
23899
|
+
this.graph = await builder.build();
|
|
23900
|
+
const ctx = {
|
|
23901
|
+
executeTask: this.opts.executeTask,
|
|
23902
|
+
onPhaseComplete: (phase) => {
|
|
23903
|
+
this.opts.onPhaseComplete?.(phase);
|
|
23904
|
+
},
|
|
23905
|
+
onPhaseFail: (phase, error) => {
|
|
23906
|
+
this.opts.onPhaseFail?.(phase, error);
|
|
23907
|
+
},
|
|
23908
|
+
onTick: (tickCtx) => {
|
|
23909
|
+
this.opts.onTick?.(tickCtx);
|
|
23910
|
+
}
|
|
23911
|
+
};
|
|
23912
|
+
this.orchestrator = new PhaseOrchestrator({
|
|
23913
|
+
graph: this.graph,
|
|
23914
|
+
ctx,
|
|
23915
|
+
maxConcurrentPhases: this.opts.maxConcurrentPhases,
|
|
23916
|
+
maxConcurrentTasks: this.opts.maxConcurrentTasks,
|
|
23917
|
+
maxRetries: this.opts.maxRetries,
|
|
23918
|
+
autonomous: this.opts.autonomous,
|
|
23919
|
+
phaseDelayMs: this.opts.phaseDelayMs,
|
|
23920
|
+
stopOnFailure: this.opts.stopOnFailure,
|
|
23921
|
+
events: this.opts.events
|
|
23922
|
+
});
|
|
23923
|
+
if (this.opts.onProgress) {
|
|
23924
|
+
this.progressInterval = setInterval(() => {
|
|
23925
|
+
const progress = this.orchestrator.getProgress();
|
|
23926
|
+
this.opts.onProgress(progress);
|
|
23927
|
+
}, 2e3);
|
|
23928
|
+
}
|
|
23929
|
+
if (this.opts.events) {
|
|
23930
|
+
const events = this.opts.events;
|
|
23931
|
+
const onUntyped = events.on;
|
|
23932
|
+
this.unsubscribeCompleted = onUntyped("graph.completed", this.graphCompletedHandler);
|
|
23933
|
+
this.unsubscribeFailed = onUntyped("graph.failed", this.graphFailedHandler);
|
|
23934
|
+
}
|
|
23935
|
+
await this.orchestrator.start();
|
|
23936
|
+
return this.graph;
|
|
23937
|
+
}
|
|
23938
|
+
pause() {
|
|
23939
|
+
this.orchestrator?.pause();
|
|
23940
|
+
}
|
|
23941
|
+
resume() {
|
|
23942
|
+
this.orchestrator?.resume();
|
|
23943
|
+
}
|
|
23944
|
+
stop() {
|
|
23945
|
+
this.orchestrator?.stop();
|
|
23946
|
+
this.cleanup();
|
|
23947
|
+
}
|
|
23948
|
+
getProgress() {
|
|
23949
|
+
return this.orchestrator?.getProgress() ?? null;
|
|
23950
|
+
}
|
|
23951
|
+
getGraph() {
|
|
23952
|
+
return this.graph;
|
|
23953
|
+
}
|
|
23954
|
+
isRunning() {
|
|
23955
|
+
return this.orchestrator?.isRunning() ?? false;
|
|
23956
|
+
}
|
|
23957
|
+
isPaused() {
|
|
23958
|
+
return this.orchestrator?.isPaused() ?? false;
|
|
23959
|
+
}
|
|
23960
|
+
assignAgent(phaseId, agentId) {
|
|
23961
|
+
this.orchestrator?.assignAgent(phaseId, agentId);
|
|
23962
|
+
}
|
|
23963
|
+
releaseAgent(phaseId, agentId) {
|
|
23964
|
+
this.orchestrator?.releaseAgent(phaseId, agentId);
|
|
23965
|
+
}
|
|
23966
|
+
cleanup() {
|
|
23967
|
+
if (this.progressInterval) {
|
|
23968
|
+
clearInterval(this.progressInterval);
|
|
23969
|
+
this.progressInterval = null;
|
|
23970
|
+
}
|
|
23971
|
+
this.unsubscribeCompleted?.();
|
|
23972
|
+
this.unsubscribeCompleted = null;
|
|
23973
|
+
this.unsubscribeFailed?.();
|
|
23974
|
+
this.unsubscribeFailed = null;
|
|
23975
|
+
}
|
|
23976
|
+
};
|
|
23977
|
+
async function createAutoPhaseFromTaskGraph(taskGraph, options) {
|
|
23978
|
+
const graph = await PhaseGraphBuilder.fromTaskGraph(taskGraph, {
|
|
23979
|
+
title: options.title ?? taskGraph.title,
|
|
23980
|
+
tasksPerPhase: options.tasksPerPhase
|
|
23981
|
+
});
|
|
23982
|
+
const phases = Array.from(graph.phases.values()).map((p) => ({
|
|
23983
|
+
name: p.name,
|
|
23984
|
+
description: p.description,
|
|
23985
|
+
priority: p.priority,
|
|
23986
|
+
estimateHours: p.estimateHours,
|
|
23987
|
+
parallelizable: p.parallelizable
|
|
23988
|
+
}));
|
|
23989
|
+
return new AutoPhaseRunner({
|
|
23990
|
+
...options,
|
|
23991
|
+
title: options.title ?? taskGraph.title,
|
|
23992
|
+
phases
|
|
23993
|
+
});
|
|
23994
|
+
}
|
|
23995
|
+
|
|
23996
|
+
// src/autophase/auto-phase-planner.ts
|
|
23997
|
+
var VALID_TASK_TYPES = /* @__PURE__ */ new Set([
|
|
23998
|
+
"feature",
|
|
23999
|
+
"bugfix",
|
|
24000
|
+
"refactor",
|
|
24001
|
+
"docs",
|
|
24002
|
+
"test",
|
|
24003
|
+
"chore"
|
|
24004
|
+
]);
|
|
24005
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set([
|
|
24006
|
+
"critical",
|
|
24007
|
+
"high",
|
|
24008
|
+
"medium",
|
|
24009
|
+
"low"
|
|
24010
|
+
]);
|
|
24011
|
+
var AutoPhasePlanner = class {
|
|
24012
|
+
constructor(opts) {
|
|
24013
|
+
this.opts = opts;
|
|
24014
|
+
}
|
|
24015
|
+
opts;
|
|
24016
|
+
/** Hedefi faz+todo planına dönüştür. */
|
|
24017
|
+
async plan() {
|
|
24018
|
+
const prompt = this.buildPrompt();
|
|
24019
|
+
const raw = await this.opts.runOnce(prompt);
|
|
24020
|
+
const phases = this.parse(raw);
|
|
24021
|
+
return { phases, raw, parseFailed: phases.length === 0 };
|
|
24022
|
+
}
|
|
24023
|
+
/** Modelin üreteceği plan için talimat prompt'u. */
|
|
24024
|
+
buildPrompt() {
|
|
24025
|
+
const minP = this.opts.minPhases ?? 3;
|
|
24026
|
+
const maxP = this.opts.maxPhases ?? 8;
|
|
24027
|
+
const todos = this.opts.todosPerPhase ?? 6;
|
|
24028
|
+
const ctx = this.opts.projectContext?.trim();
|
|
24029
|
+
return [
|
|
24030
|
+
"You are an expert software project planner. Break the following goal into",
|
|
24031
|
+
`a dependency-ordered list of ${minP}\u2013${maxP} PHASES. Each phase must contain`,
|
|
24032
|
+
`roughly ${todos} concrete, individually-actionable TODO tasks.`,
|
|
24033
|
+
"",
|
|
24034
|
+
`GOAL: ${this.opts.goal}`,
|
|
24035
|
+
ctx ? `
|
|
24036
|
+
PROJECT CONTEXT:
|
|
24037
|
+
${ctx}
|
|
24038
|
+
` : "",
|
|
24039
|
+
"Rules:",
|
|
24040
|
+
"- Phases run in order; earlier phases are prerequisites for later ones.",
|
|
24041
|
+
"- Each todo must be small enough for one focused work session.",
|
|
24042
|
+
"- Each todo must be self-contained (an agent will execute it in isolation).",
|
|
24043
|
+
'- Prefer concrete verbs ("Add X", "Refactor Y", "Write tests for Z").',
|
|
24044
|
+
"",
|
|
24045
|
+
"Respond with ONLY a JSON array inside a ```json code fence. No prose before",
|
|
24046
|
+
"or after. Schema (TypeScript):",
|
|
24047
|
+
"",
|
|
24048
|
+
"```json",
|
|
24049
|
+
"[",
|
|
24050
|
+
" {",
|
|
24051
|
+
' "name": "Phase name",',
|
|
24052
|
+
' "description": "What this phase accomplishes",',
|
|
24053
|
+
' "priority": "critical" | "high" | "medium" | "low",',
|
|
24054
|
+
' "estimateHours": number,',
|
|
24055
|
+
' "parallelizable": boolean,',
|
|
24056
|
+
' "tasks": [',
|
|
24057
|
+
" {",
|
|
24058
|
+
' "title": "Short task title",',
|
|
24059
|
+
' "description": "What to do and how to know it is done",',
|
|
24060
|
+
' "type": "feature" | "bugfix" | "refactor" | "docs" | "test" | "chore",',
|
|
24061
|
+
' "priority": "critical" | "high" | "medium" | "low",',
|
|
24062
|
+
' "estimateHours": number,',
|
|
24063
|
+
' "tags": ["optional", "labels"]',
|
|
24064
|
+
" }",
|
|
24065
|
+
" ]",
|
|
24066
|
+
" }",
|
|
24067
|
+
"]",
|
|
24068
|
+
"```"
|
|
24069
|
+
].filter((l) => l !== "").join("\n");
|
|
24070
|
+
}
|
|
24071
|
+
/** Ham çıktıdan JSON'u çıkar, doğrula ve PhaseTemplate[]'e dönüştür. */
|
|
24072
|
+
parse(raw) {
|
|
24073
|
+
const json = extractJSONArray(raw);
|
|
24074
|
+
if (!json) return [];
|
|
24075
|
+
let data;
|
|
24076
|
+
try {
|
|
24077
|
+
data = JSON.parse(json);
|
|
24078
|
+
} catch {
|
|
24079
|
+
return [];
|
|
24080
|
+
}
|
|
24081
|
+
if (!Array.isArray(data)) return [];
|
|
24082
|
+
const phases = [];
|
|
24083
|
+
for (const entry of data) {
|
|
24084
|
+
const phase = this.coercePhase(entry);
|
|
24085
|
+
if (phase) phases.push(phase);
|
|
24086
|
+
}
|
|
24087
|
+
return phases;
|
|
24088
|
+
}
|
|
24089
|
+
coercePhase(entry) {
|
|
24090
|
+
if (!entry || typeof entry !== "object") return null;
|
|
24091
|
+
const e = entry;
|
|
24092
|
+
const name = typeof e.name === "string" ? e.name.trim() : "";
|
|
24093
|
+
if (!name) return null;
|
|
24094
|
+
const rawTasks = Array.isArray(e.tasks) ? e.tasks : Array.isArray(e.taskTemplates) ? e.taskTemplates : [];
|
|
24095
|
+
const taskTemplates = rawTasks.map((t2) => this.coerceTask(t2)).filter((t2) => t2 !== null);
|
|
24096
|
+
return {
|
|
24097
|
+
name,
|
|
24098
|
+
description: typeof e.description === "string" ? e.description : "",
|
|
24099
|
+
priority: coercePriority(e.priority),
|
|
24100
|
+
estimateHours: coerceHours(e.estimateHours, 4),
|
|
24101
|
+
parallelizable: e.parallelizable === true,
|
|
24102
|
+
taskTemplates
|
|
24103
|
+
};
|
|
24104
|
+
}
|
|
24105
|
+
coerceTask(t2) {
|
|
24106
|
+
if (!t2 || typeof t2 !== "object") return null;
|
|
24107
|
+
const o = t2;
|
|
24108
|
+
const title = typeof o.title === "string" ? o.title.trim() : "";
|
|
24109
|
+
if (!title) return null;
|
|
24110
|
+
const type = VALID_TASK_TYPES.has(o.type) ? o.type : "feature";
|
|
24111
|
+
return {
|
|
24112
|
+
title,
|
|
24113
|
+
description: typeof o.description === "string" ? o.description : "",
|
|
24114
|
+
type,
|
|
24115
|
+
priority: coercePriority(o.priority),
|
|
24116
|
+
estimateHours: coerceHours(o.estimateHours, 2),
|
|
24117
|
+
tags: Array.isArray(o.tags) ? o.tags.map(String) : []
|
|
24118
|
+
};
|
|
24119
|
+
}
|
|
24120
|
+
};
|
|
24121
|
+
function coercePriority(value) {
|
|
24122
|
+
return VALID_PRIORITIES.has(value) ? value : "medium";
|
|
24123
|
+
}
|
|
24124
|
+
function coerceHours(value, fallback) {
|
|
24125
|
+
const n = Number(value);
|
|
24126
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
24127
|
+
}
|
|
24128
|
+
function extractJSONArray(text) {
|
|
24129
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
24130
|
+
const candidates = [];
|
|
24131
|
+
if (fence?.[1]) candidates.push(fence[1]);
|
|
24132
|
+
candidates.push(text);
|
|
24133
|
+
for (const candidate of candidates) {
|
|
24134
|
+
const balanced = firstBalancedArray(candidate);
|
|
24135
|
+
if (balanced) return balanced;
|
|
24136
|
+
}
|
|
24137
|
+
return null;
|
|
24138
|
+
}
|
|
24139
|
+
function firstBalancedArray(text) {
|
|
24140
|
+
const start = text.indexOf("[");
|
|
24141
|
+
if (start === -1) return null;
|
|
24142
|
+
let depth = 0;
|
|
24143
|
+
let inString = false;
|
|
24144
|
+
let escaped = false;
|
|
24145
|
+
for (let i = start; i < text.length; i++) {
|
|
24146
|
+
const ch = text[i];
|
|
24147
|
+
if (inString) {
|
|
24148
|
+
if (escaped) {
|
|
24149
|
+
escaped = false;
|
|
24150
|
+
} else if (ch === "\\") {
|
|
24151
|
+
escaped = true;
|
|
24152
|
+
} else if (ch === '"') {
|
|
24153
|
+
inString = false;
|
|
24154
|
+
}
|
|
24155
|
+
continue;
|
|
24156
|
+
}
|
|
24157
|
+
if (ch === '"') {
|
|
24158
|
+
inString = true;
|
|
24159
|
+
} else if (ch === "[") {
|
|
24160
|
+
depth++;
|
|
24161
|
+
} else if (ch === "]") {
|
|
24162
|
+
depth--;
|
|
24163
|
+
if (depth === 0) {
|
|
24164
|
+
return text.slice(start, i + 1);
|
|
24165
|
+
}
|
|
24166
|
+
}
|
|
24167
|
+
}
|
|
24168
|
+
return null;
|
|
24169
|
+
}
|
|
24170
|
+
var PhaseStore = class {
|
|
24171
|
+
baseDir;
|
|
24172
|
+
constructor(opts) {
|
|
24173
|
+
this.baseDir = opts.baseDir;
|
|
24174
|
+
}
|
|
24175
|
+
async save(graph) {
|
|
24176
|
+
const filePath = this.getFilePath(graph.id);
|
|
24177
|
+
await fsp2.mkdir(path6.dirname(filePath), { recursive: true });
|
|
24178
|
+
const serialized = this.serializeGraph(graph);
|
|
24179
|
+
await fsp2.writeFile(filePath, JSON.stringify(serialized, null, 2), "utf8");
|
|
24180
|
+
}
|
|
24181
|
+
async load(graphId) {
|
|
24182
|
+
const filePath = this.getFilePath(graphId);
|
|
24183
|
+
try {
|
|
24184
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24185
|
+
const serialized = JSON.parse(raw);
|
|
24186
|
+
return this.deserializeGraph(serialized);
|
|
24187
|
+
} catch {
|
|
24188
|
+
return null;
|
|
24189
|
+
}
|
|
24190
|
+
}
|
|
24191
|
+
async delete(graphId) {
|
|
24192
|
+
const filePath = this.getFilePath(graphId);
|
|
24193
|
+
try {
|
|
24194
|
+
await fsp2.unlink(filePath);
|
|
24195
|
+
} catch {
|
|
24196
|
+
}
|
|
24197
|
+
}
|
|
24198
|
+
async list() {
|
|
24199
|
+
try {
|
|
24200
|
+
const entries = await fsp2.readdir(this.baseDir, { withFileTypes: true });
|
|
24201
|
+
const graphs = [];
|
|
24202
|
+
for (const entry of entries) {
|
|
24203
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
24204
|
+
try {
|
|
24205
|
+
const raw = await fsp2.readFile(path6.join(this.baseDir, entry.name), "utf8");
|
|
24206
|
+
const serialized = JSON.parse(raw);
|
|
24207
|
+
const done = serialized.completedPhaseIds.length;
|
|
24208
|
+
const total = serialized.phases.length;
|
|
24209
|
+
graphs.push({
|
|
24210
|
+
id: serialized.id,
|
|
24211
|
+
title: serialized.title,
|
|
24212
|
+
updatedAt: serialized.updatedAt,
|
|
24213
|
+
status: done === total ? "completed" : done > 0 ? "in_progress" : "pending"
|
|
24214
|
+
});
|
|
24215
|
+
} catch {
|
|
24216
|
+
}
|
|
24217
|
+
}
|
|
24218
|
+
return graphs.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
24219
|
+
} catch {
|
|
24220
|
+
return [];
|
|
24221
|
+
}
|
|
24222
|
+
}
|
|
24223
|
+
getFilePath(graphId) {
|
|
24224
|
+
return path6.join(this.baseDir, `${graphId}.json`);
|
|
24225
|
+
}
|
|
24226
|
+
serializeGraph(graph) {
|
|
24227
|
+
return {
|
|
24228
|
+
id: graph.id,
|
|
24229
|
+
title: graph.title,
|
|
24230
|
+
description: graph.description,
|
|
24231
|
+
phases: Array.from(graph.phases.values()).map((p) => this.serializePhase(p)),
|
|
24232
|
+
rootPhaseIds: graph.rootPhaseIds,
|
|
24233
|
+
activePhaseIds: graph.activePhaseIds,
|
|
24234
|
+
completedPhaseIds: graph.completedPhaseIds,
|
|
24235
|
+
failedPhaseIds: graph.failedPhaseIds,
|
|
24236
|
+
autonomous: graph.autonomous,
|
|
24237
|
+
stopOnComplete: graph.stopOnComplete,
|
|
24238
|
+
createdAt: graph.createdAt,
|
|
24239
|
+
updatedAt: graph.updatedAt,
|
|
24240
|
+
startedAt: graph.startedAt,
|
|
24241
|
+
completedAt: graph.completedAt
|
|
24242
|
+
};
|
|
24243
|
+
}
|
|
24244
|
+
serializePhase(phase) {
|
|
24245
|
+
return {
|
|
24246
|
+
id: phase.id,
|
|
24247
|
+
name: phase.name,
|
|
24248
|
+
description: phase.description,
|
|
24249
|
+
status: phase.status,
|
|
24250
|
+
taskGraph: this.serializeTaskGraph(phase.taskGraph),
|
|
24251
|
+
dependsOn: phase.dependsOn,
|
|
24252
|
+
nextPhases: phase.nextPhases,
|
|
24253
|
+
parallelizable: phase.parallelizable,
|
|
24254
|
+
priority: phase.priority,
|
|
24255
|
+
estimateHours: phase.estimateHours,
|
|
24256
|
+
actualDurationMs: phase.actualDurationMs,
|
|
24257
|
+
startedAt: phase.startedAt,
|
|
24258
|
+
completedAt: phase.completedAt,
|
|
24259
|
+
assignedAgents: phase.assignedAgents,
|
|
24260
|
+
metadata: phase.metadata,
|
|
24261
|
+
createdAt: phase.createdAt,
|
|
24262
|
+
updatedAt: phase.updatedAt
|
|
24263
|
+
};
|
|
24264
|
+
}
|
|
24265
|
+
serializeTaskGraph(graph) {
|
|
24266
|
+
return {
|
|
24267
|
+
id: graph.id,
|
|
24268
|
+
specId: graph.specId,
|
|
24269
|
+
title: graph.title,
|
|
24270
|
+
nodes: Array.from(graph.nodes.values()).map((n) => this.serializeTaskNode(n)),
|
|
24271
|
+
edges: graph.edges,
|
|
24272
|
+
rootNodes: graph.rootNodes,
|
|
24273
|
+
createdAt: graph.createdAt,
|
|
24274
|
+
updatedAt: graph.updatedAt
|
|
24275
|
+
};
|
|
24276
|
+
}
|
|
24277
|
+
serializeTaskNode(node) {
|
|
24278
|
+
return { ...node };
|
|
24279
|
+
}
|
|
24280
|
+
deserializeGraph(serialized) {
|
|
24281
|
+
const phases = /* @__PURE__ */ new Map();
|
|
24282
|
+
for (const sp of serialized.phases) {
|
|
24283
|
+
phases.set(sp.id, this.deserializePhase(sp));
|
|
24284
|
+
}
|
|
24285
|
+
return {
|
|
24286
|
+
id: serialized.id,
|
|
24287
|
+
title: serialized.title,
|
|
24288
|
+
description: serialized.description,
|
|
24289
|
+
phases,
|
|
24290
|
+
rootPhaseIds: serialized.rootPhaseIds,
|
|
24291
|
+
activePhaseIds: serialized.activePhaseIds,
|
|
24292
|
+
completedPhaseIds: serialized.completedPhaseIds,
|
|
24293
|
+
failedPhaseIds: serialized.failedPhaseIds,
|
|
24294
|
+
autonomous: serialized.autonomous,
|
|
24295
|
+
stopOnComplete: serialized.stopOnComplete,
|
|
24296
|
+
createdAt: serialized.createdAt,
|
|
24297
|
+
updatedAt: serialized.updatedAt,
|
|
24298
|
+
startedAt: serialized.startedAt,
|
|
24299
|
+
completedAt: serialized.completedAt
|
|
24300
|
+
};
|
|
24301
|
+
}
|
|
24302
|
+
deserializePhase(serialized) {
|
|
24303
|
+
return {
|
|
24304
|
+
id: serialized.id,
|
|
24305
|
+
name: serialized.name,
|
|
24306
|
+
description: serialized.description,
|
|
24307
|
+
status: serialized.status,
|
|
24308
|
+
taskGraph: this.deserializeTaskGraph(serialized.taskGraph),
|
|
24309
|
+
dependsOn: serialized.dependsOn,
|
|
24310
|
+
nextPhases: serialized.nextPhases,
|
|
24311
|
+
parallelizable: serialized.parallelizable,
|
|
24312
|
+
priority: serialized.priority,
|
|
24313
|
+
estimateHours: serialized.estimateHours,
|
|
24314
|
+
actualDurationMs: serialized.actualDurationMs,
|
|
24315
|
+
startedAt: serialized.startedAt,
|
|
24316
|
+
completedAt: serialized.completedAt,
|
|
24317
|
+
assignedAgents: serialized.assignedAgents,
|
|
24318
|
+
metadata: serialized.metadata,
|
|
24319
|
+
createdAt: serialized.createdAt,
|
|
24320
|
+
updatedAt: serialized.updatedAt
|
|
24321
|
+
};
|
|
24322
|
+
}
|
|
24323
|
+
deserializeTaskGraph(serialized) {
|
|
24324
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
24325
|
+
for (const sn of serialized.nodes) {
|
|
24326
|
+
nodes.set(sn.id, sn);
|
|
24327
|
+
}
|
|
24328
|
+
return {
|
|
24329
|
+
id: serialized.id,
|
|
24330
|
+
specId: serialized.specId,
|
|
24331
|
+
title: serialized.title,
|
|
24332
|
+
nodes,
|
|
24333
|
+
edges: serialized.edges ?? [],
|
|
24334
|
+
rootNodes: serialized.rootNodes ?? [],
|
|
24335
|
+
createdAt: serialized.createdAt,
|
|
24336
|
+
updatedAt: serialized.updatedAt
|
|
24337
|
+
};
|
|
24338
|
+
}
|
|
24339
|
+
};
|
|
24340
|
+
var CheckpointManager = class {
|
|
24341
|
+
store;
|
|
24342
|
+
maxCheckpoints;
|
|
24343
|
+
checkpoints = /* @__PURE__ */ new Map();
|
|
24344
|
+
baseDir;
|
|
24345
|
+
constructor(opts) {
|
|
24346
|
+
this.store = opts.store;
|
|
24347
|
+
this.maxCheckpoints = opts.maxCheckpoints ?? 10;
|
|
24348
|
+
this.baseDir = opts.baseDir ?? path6.join(opts.store.baseDir, ".checkpoints");
|
|
24349
|
+
}
|
|
24350
|
+
async initialize() {
|
|
24351
|
+
await fsp2.mkdir(this.baseDir, { recursive: true });
|
|
24352
|
+
await this.loadFromDisk();
|
|
24353
|
+
}
|
|
24354
|
+
async saveCheckpoint(graph, label) {
|
|
24355
|
+
await this.store.save(graph);
|
|
24356
|
+
const activePhase = Array.from(graph.phases.values()).find(
|
|
24357
|
+
(p) => p.status === "running" || p.status === "paused"
|
|
24358
|
+
);
|
|
24359
|
+
const checkpoint = {
|
|
24360
|
+
id: crypto.randomUUID(),
|
|
24361
|
+
graphId: graph.id,
|
|
24362
|
+
phaseId: activePhase?.id ?? graph.rootPhaseIds[0] ?? "",
|
|
24363
|
+
phaseStatus: activePhase?.status ?? "pending",
|
|
24364
|
+
taskStatuses: activePhase ? Array.from(activePhase.taskGraph.nodes.values()).map((t2) => ({
|
|
24365
|
+
taskId: t2.id,
|
|
24366
|
+
status: t2.status,
|
|
24367
|
+
title: t2.title
|
|
24368
|
+
})) : [],
|
|
24369
|
+
timestamp: Date.now(),
|
|
24370
|
+
label
|
|
24371
|
+
};
|
|
24372
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
24373
|
+
await this.saveToDisk(checkpoint);
|
|
24374
|
+
await this.pruneCheckpoints();
|
|
24375
|
+
return checkpoint;
|
|
24376
|
+
}
|
|
24377
|
+
async restoreCheckpoint(checkpointId) {
|
|
24378
|
+
let checkpoint = this.checkpoints.get(checkpointId);
|
|
24379
|
+
if (!checkpoint) {
|
|
24380
|
+
await this.loadFromDisk();
|
|
24381
|
+
checkpoint = this.checkpoints.get(checkpointId);
|
|
24382
|
+
}
|
|
24383
|
+
if (!checkpoint) return null;
|
|
24384
|
+
const graph = await this.store.load(checkpoint.graphId);
|
|
24385
|
+
if (!graph) return null;
|
|
24386
|
+
const phase = graph.phases.get(checkpoint.phaseId);
|
|
24387
|
+
if (phase) {
|
|
24388
|
+
phase.status = checkpoint.phaseStatus;
|
|
24389
|
+
phase.updatedAt = Date.now();
|
|
24390
|
+
for (const ts of checkpoint.taskStatuses) {
|
|
24391
|
+
const task = phase.taskGraph.nodes.get(ts.taskId);
|
|
24392
|
+
if (task) {
|
|
24393
|
+
task.status = ts.status;
|
|
24394
|
+
task.updatedAt = Date.now();
|
|
24395
|
+
}
|
|
24396
|
+
}
|
|
24397
|
+
}
|
|
24398
|
+
graph.updatedAt = Date.now();
|
|
24399
|
+
return graph;
|
|
24400
|
+
}
|
|
24401
|
+
listCheckpoints(graphId) {
|
|
24402
|
+
const all = Array.from(this.checkpoints.values());
|
|
24403
|
+
const filtered = graphId ? all.filter((c) => c.graphId === graphId) : all;
|
|
24404
|
+
return filtered.sort((a, b) => b.timestamp - a.timestamp);
|
|
24405
|
+
}
|
|
24406
|
+
async deleteCheckpoint(checkpointId) {
|
|
24407
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
24408
|
+
if (!checkpoint) return false;
|
|
24409
|
+
this.checkpoints.delete(checkpointId);
|
|
24410
|
+
await this.deleteFromDisk(checkpointId);
|
|
24411
|
+
return true;
|
|
24412
|
+
}
|
|
24413
|
+
async saveToDisk(checkpoint) {
|
|
24414
|
+
await fsp2.mkdir(this.baseDir, { recursive: true });
|
|
24415
|
+
const filePath = path6.join(this.baseDir, `${checkpoint.graphId}.json`);
|
|
24416
|
+
const serialized = {
|
|
24417
|
+
...checkpoint
|
|
24418
|
+
};
|
|
24419
|
+
let existing = [];
|
|
24420
|
+
try {
|
|
24421
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24422
|
+
const parsed = JSON.parse(raw);
|
|
24423
|
+
if (Array.isArray(parsed)) {
|
|
24424
|
+
existing = parsed;
|
|
24425
|
+
}
|
|
24426
|
+
} catch {
|
|
24427
|
+
}
|
|
24428
|
+
existing.push(serialized);
|
|
24429
|
+
await fsp2.writeFile(filePath, JSON.stringify(existing, null, 2), "utf8");
|
|
24430
|
+
}
|
|
24431
|
+
async deleteFromDisk(checkpointId) {
|
|
24432
|
+
let entries;
|
|
24433
|
+
try {
|
|
24434
|
+
entries = await fsp2.readdir(this.baseDir);
|
|
24435
|
+
} catch {
|
|
24436
|
+
return;
|
|
24437
|
+
}
|
|
24438
|
+
for (const filename of entries) {
|
|
24439
|
+
if (!filename.endsWith(".json")) continue;
|
|
24440
|
+
const filePath = path6.join(this.baseDir, filename);
|
|
24441
|
+
try {
|
|
24442
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24443
|
+
const parsed = JSON.parse(raw);
|
|
24444
|
+
if (!Array.isArray(parsed)) continue;
|
|
24445
|
+
const existing = parsed;
|
|
24446
|
+
const filtered = existing.filter((c) => c.id !== checkpointId);
|
|
24447
|
+
if (filtered.length !== existing.length) {
|
|
24448
|
+
if (filtered.length === 0) {
|
|
24449
|
+
await fsp2.unlink(filePath);
|
|
24450
|
+
} else {
|
|
24451
|
+
await fsp2.writeFile(filePath, JSON.stringify(filtered, null, 2), "utf8");
|
|
24452
|
+
}
|
|
24453
|
+
}
|
|
24454
|
+
} catch {
|
|
24455
|
+
}
|
|
24456
|
+
}
|
|
24457
|
+
}
|
|
24458
|
+
async loadFromDisk() {
|
|
24459
|
+
let entries;
|
|
24460
|
+
try {
|
|
24461
|
+
entries = await fsp2.readdir(this.baseDir);
|
|
24462
|
+
} catch {
|
|
24463
|
+
return;
|
|
24464
|
+
}
|
|
24465
|
+
for (const filename of entries) {
|
|
24466
|
+
if (!filename.endsWith(".json")) continue;
|
|
24467
|
+
const filePath = path6.join(this.baseDir, filename);
|
|
24468
|
+
try {
|
|
24469
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24470
|
+
const parsed = JSON.parse(raw);
|
|
24471
|
+
if (!Array.isArray(parsed)) continue;
|
|
24472
|
+
const checkpoints = parsed;
|
|
24473
|
+
for (const sc of checkpoints) {
|
|
24474
|
+
const checkpoint = {
|
|
24475
|
+
id: sc.id,
|
|
24476
|
+
graphId: sc.graphId,
|
|
24477
|
+
phaseId: sc.phaseId,
|
|
24478
|
+
phaseStatus: sc.phaseStatus,
|
|
24479
|
+
taskStatuses: sc.taskStatuses,
|
|
24480
|
+
timestamp: sc.timestamp,
|
|
24481
|
+
label: sc.label
|
|
24482
|
+
};
|
|
24483
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
24484
|
+
}
|
|
24485
|
+
} catch {
|
|
24486
|
+
}
|
|
24487
|
+
}
|
|
24488
|
+
}
|
|
24489
|
+
async pruneCheckpoints() {
|
|
24490
|
+
const all = Array.from(this.checkpoints.values()).sort(
|
|
24491
|
+
(a, b) => a.timestamp - b.timestamp
|
|
24492
|
+
);
|
|
24493
|
+
while (all.length > this.maxCheckpoints) {
|
|
24494
|
+
const oldest = all.shift();
|
|
24495
|
+
if (oldest) {
|
|
24496
|
+
this.checkpoints.delete(oldest.id);
|
|
24497
|
+
await this.deleteFromDisk(oldest.id);
|
|
24498
|
+
}
|
|
24499
|
+
}
|
|
24500
|
+
}
|
|
24501
|
+
};
|
|
24502
|
+
|
|
24503
|
+
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CheckpointManager, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMcpControlTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeLLMClassifier, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
23265
24504
|
//# sourceMappingURL=index.js.map
|
|
23266
24505
|
//# sourceMappingURL=index.js.map
|