@wrongstack/core 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-DPxcUVkn.d.ts → agent-bridge-Bad3ZGQ7.d.ts} +1 -1
- package/dist/{agent-subagent-runner-Cav3yEJM.d.ts → agent-subagent-runner-Cc8sRJOF.d.ts} +3 -3
- package/dist/coordination/index.d.ts +8 -8
- package/dist/coordination/index.js +8 -1
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +11 -11
- package/dist/defaults/index.js +41 -21
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-DyhxkstG.d.ts → events-yCpJD4wa.d.ts} +49 -0
- package/dist/execution/index.d.ts +7 -7
- package/dist/execution/index.js +22 -3
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +2 -2
- package/dist/index-BvZld4Dr.d.ts +200 -0
- package/dist/{index-oYZeWsuJ.d.ts → index-SxpnwRlJ.d.ts} +1 -1
- package/dist/index.d.ts +132 -21
- package/dist/index.js +862 -146
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/kernel/index.d.ts +16 -88
- package/dist/kernel/index.js +3 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{multi-agent-CRMznZmf.d.ts → multi-agent-C8uY45AY.d.ts} +9 -1
- package/dist/{multi-agent-coordinator-IQKrMfXz.d.ts → multi-agent-coordinator-C7YbZUY8.d.ts} +2 -2
- package/dist/{null-fleet-bus-sKnVwEd8.d.ts → null-fleet-bus-Bb2jVLKr.d.ts} +5 -5
- package/dist/observability/index.d.ts +1 -1
- package/dist/{path-resolver-1CIYbH2Q.d.ts → path-resolver-DnC1nOjb.d.ts} +1 -1
- package/dist/{plan-templates-BnlpEkX8.d.ts → plan-templates-Cqdy6rtM.d.ts} +2 -2
- package/dist/{provider-runner-BrA0XR-l.d.ts → provider-runner-C4ouU7--.d.ts} +1 -1
- package/dist/sdd/index.d.ts +11 -11
- 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 +3 -3
- package/dist/storage/index.js +1 -3
- package/dist/storage/index.js.map +1 -1
- package/dist/{tool-executor-QwfWnQZ8.d.ts → tool-executor-Ch25BDwa.d.ts} +1 -1
- package/dist/types/index.d.ts +7 -7
- 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 +41 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-gCrJ631C.d.ts → wstack-paths-CJYrjQU8.d.ts} +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash }
|
|
|
3
3
|
import * as fsp2 from 'fs/promises';
|
|
4
4
|
import { readFile, readdir, stat, mkdir } from 'fs/promises';
|
|
5
5
|
import * as path6 from 'path';
|
|
6
|
-
import { join, extname, relative } from 'path';
|
|
6
|
+
import { join, extname, relative, resolve, sep } from 'path';
|
|
7
7
|
import * as fs2 from 'fs';
|
|
8
8
|
import * as os5 from 'os';
|
|
9
9
|
import { execFile, spawn } from 'child_process';
|
|
@@ -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((resolve8) => setTimeout(resolve8, delays[i]));
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
throw lastErr;
|
|
@@ -830,7 +830,9 @@ var TOKENS = {
|
|
|
830
830
|
ModelsRegistry: t("ModelsRegistry"),
|
|
831
831
|
ModeStore: t("ModeStore"),
|
|
832
832
|
/** Replaces the entire provider call layer — retry, streaming, tracing. */
|
|
833
|
-
ProviderRunner: t("ProviderRunner")
|
|
833
|
+
ProviderRunner: t("ProviderRunner"),
|
|
834
|
+
/** Optional git-worktree lifecycle manager (per-phase isolation in AutoPhase). */
|
|
835
|
+
WorktreeManager: t("WorktreeManager")
|
|
834
836
|
};
|
|
835
837
|
|
|
836
838
|
// src/kernel/run-controller.ts
|
|
@@ -2692,7 +2694,7 @@ var InMemoryAgentBridge = class {
|
|
|
2692
2694
|
);
|
|
2693
2695
|
}
|
|
2694
2696
|
this.inflightGuards.add(correlationId);
|
|
2695
|
-
return new Promise((
|
|
2697
|
+
return new Promise((resolve8, reject) => {
|
|
2696
2698
|
const timer = setTimeout(() => {
|
|
2697
2699
|
this.inflightGuards.delete(correlationId);
|
|
2698
2700
|
this.pendingRequests.delete(correlationId);
|
|
@@ -2704,7 +2706,7 @@ var InMemoryAgentBridge = class {
|
|
|
2704
2706
|
return;
|
|
2705
2707
|
}
|
|
2706
2708
|
this.pendingRequests.set(correlationId, {
|
|
2707
|
-
resolve:
|
|
2709
|
+
resolve: resolve8,
|
|
2708
2710
|
reject,
|
|
2709
2711
|
timer
|
|
2710
2712
|
});
|
|
@@ -2930,7 +2932,7 @@ var ToolExecutor = class {
|
|
|
2930
2932
|
return { result, tool, durationMs: Date.now() - start };
|
|
2931
2933
|
}
|
|
2932
2934
|
if (hasMalformedArguments(use.input)) {
|
|
2933
|
-
const result = this.malformedInputResult(use);
|
|
2935
|
+
const result = this.malformedInputResult(use, extractMalformedRaw(use.input));
|
|
2934
2936
|
budget = this.decrementBudget(result, budget);
|
|
2935
2937
|
return { result, tool, durationMs: Date.now() - start };
|
|
2936
2938
|
}
|
|
@@ -3137,11 +3139,18 @@ var ToolExecutor = class {
|
|
|
3137
3139
|
is_error: true
|
|
3138
3140
|
};
|
|
3139
3141
|
}
|
|
3140
|
-
malformedInputResult(use) {
|
|
3142
|
+
malformedInputResult(use, raw) {
|
|
3143
|
+
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.`;
|
|
3144
|
+
if (raw) {
|
|
3145
|
+
const max = 800;
|
|
3146
|
+
const excerpt = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
|
|
3147
|
+
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:
|
|
3148
|
+
${excerpt}`;
|
|
3149
|
+
}
|
|
3141
3150
|
return {
|
|
3142
3151
|
type: "tool_result",
|
|
3143
3152
|
tool_use_id: use.id,
|
|
3144
|
-
content
|
|
3153
|
+
content,
|
|
3145
3154
|
is_error: true
|
|
3146
3155
|
};
|
|
3147
3156
|
}
|
|
@@ -3197,6 +3206,18 @@ function hasMalformedArguments(input) {
|
|
|
3197
3206
|
const keys = Object.keys(obj);
|
|
3198
3207
|
return keys.length === 1 && MALFORMED_ARG_MARKERS.includes(keys[0]);
|
|
3199
3208
|
}
|
|
3209
|
+
function extractMalformedRaw(input) {
|
|
3210
|
+
if (!hasMalformedArguments(input)) return void 0;
|
|
3211
|
+
const obj = input;
|
|
3212
|
+
const value = obj[Object.keys(obj)[0]];
|
|
3213
|
+
if (value === void 0 || value === null) return void 0;
|
|
3214
|
+
if (typeof value === "string") return value;
|
|
3215
|
+
try {
|
|
3216
|
+
return JSON.stringify(value);
|
|
3217
|
+
} catch {
|
|
3218
|
+
return String(value);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3200
3221
|
|
|
3201
3222
|
// src/utils/regex-guard.ts
|
|
3202
3223
|
var MAX_PATTERN_LEN = 512;
|
|
@@ -3536,6 +3557,7 @@ function sanitizeJsonString(s) {
|
|
|
3536
3557
|
let out = s.trim();
|
|
3537
3558
|
out = stripSingleLineComments(out);
|
|
3538
3559
|
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
3560
|
+
out = escapeControlCharsInStrings(out);
|
|
3539
3561
|
try {
|
|
3540
3562
|
JSON.parse(out);
|
|
3541
3563
|
return out;
|
|
@@ -3543,6 +3565,43 @@ function sanitizeJsonString(s) {
|
|
|
3543
3565
|
return null;
|
|
3544
3566
|
}
|
|
3545
3567
|
}
|
|
3568
|
+
function escapeControlCharsInStrings(s) {
|
|
3569
|
+
let inString = false;
|
|
3570
|
+
let out = "";
|
|
3571
|
+
for (let i = 0; i < s.length; i++) {
|
|
3572
|
+
const c = s[i];
|
|
3573
|
+
if (c === '"' && (i === 0 || s[i - 1] !== "\\")) {
|
|
3574
|
+
inString = !inString;
|
|
3575
|
+
out += c;
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
const code = c.charCodeAt(0);
|
|
3579
|
+
if (inString && code < 32) {
|
|
3580
|
+
switch (c) {
|
|
3581
|
+
case "\n":
|
|
3582
|
+
out += "\\n";
|
|
3583
|
+
break;
|
|
3584
|
+
case "\r":
|
|
3585
|
+
out += "\\r";
|
|
3586
|
+
break;
|
|
3587
|
+
case " ":
|
|
3588
|
+
out += "\\t";
|
|
3589
|
+
break;
|
|
3590
|
+
case "\b":
|
|
3591
|
+
out += "\\b";
|
|
3592
|
+
break;
|
|
3593
|
+
case "\f":
|
|
3594
|
+
out += "\\f";
|
|
3595
|
+
break;
|
|
3596
|
+
default:
|
|
3597
|
+
out += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
3598
|
+
}
|
|
3599
|
+
continue;
|
|
3600
|
+
}
|
|
3601
|
+
out += c;
|
|
3602
|
+
}
|
|
3603
|
+
return out;
|
|
3604
|
+
}
|
|
3546
3605
|
function stripSingleLineComments(s) {
|
|
3547
3606
|
let inString = false;
|
|
3548
3607
|
const chars = [];
|
|
@@ -3863,12 +3922,14 @@ function resolveWstackPaths(opts) {
|
|
|
3863
3922
|
projectLocalConfig: path6.join(projectDir, "config.local.json"),
|
|
3864
3923
|
inProjectAgentsFile: path6.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
3865
3924
|
inProjectSkills: path6.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
3925
|
+
inProjectWorktrees: path6.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
3866
3926
|
projectHash: hash,
|
|
3867
3927
|
projectGoal: path6.join(projectDir, "goal.json"),
|
|
3868
3928
|
projectSpecs: path6.join(projectDir, "specs"),
|
|
3869
3929
|
projectTaskGraphs: path6.join(projectDir, "task-graphs"),
|
|
3870
3930
|
projectSddSession: path6.join(projectDir, "sdd-session.json"),
|
|
3871
|
-
projectPlan: path6.join(projectDir, "plan.json")
|
|
3931
|
+
projectPlan: path6.join(projectDir, "plan.json"),
|
|
3932
|
+
projectAutophase: path6.join(projectDir, "autophase")
|
|
3872
3933
|
};
|
|
3873
3934
|
}
|
|
3874
3935
|
|
|
@@ -3965,11 +4026,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3965
4026
|
walk2(value, schema, "", errors);
|
|
3966
4027
|
return { ok: errors.length === 0, errors };
|
|
3967
4028
|
}
|
|
3968
|
-
function walk2(value, schema,
|
|
4029
|
+
function walk2(value, schema, path28, errors) {
|
|
3969
4030
|
if (schema.enum !== void 0) {
|
|
3970
4031
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3971
4032
|
errors.push({
|
|
3972
|
-
path:
|
|
4033
|
+
path: path28 || "<root>",
|
|
3973
4034
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3974
4035
|
});
|
|
3975
4036
|
return;
|
|
@@ -3978,7 +4039,7 @@ function walk2(value, schema, path27, errors) {
|
|
|
3978
4039
|
if (typeof schema.type === "string") {
|
|
3979
4040
|
if (!checkType(value, schema.type)) {
|
|
3980
4041
|
errors.push({
|
|
3981
|
-
path:
|
|
4042
|
+
path: path28 || "<root>",
|
|
3982
4043
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3983
4044
|
});
|
|
3984
4045
|
return;
|
|
@@ -3988,19 +4049,19 @@ function walk2(value, schema, path27, errors) {
|
|
|
3988
4049
|
const obj = value;
|
|
3989
4050
|
for (const req of schema.required ?? []) {
|
|
3990
4051
|
if (!(req in obj)) {
|
|
3991
|
-
errors.push({ path: joinPath(
|
|
4052
|
+
errors.push({ path: joinPath(path28, req), message: "required property missing" });
|
|
3992
4053
|
}
|
|
3993
4054
|
}
|
|
3994
4055
|
if (schema.properties) {
|
|
3995
4056
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3996
4057
|
if (key in obj) {
|
|
3997
|
-
walk2(obj[key], subSchema, joinPath(
|
|
4058
|
+
walk2(obj[key], subSchema, joinPath(path28, key), errors);
|
|
3998
4059
|
}
|
|
3999
4060
|
}
|
|
4000
4061
|
}
|
|
4001
4062
|
}
|
|
4002
4063
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
4003
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
4064
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path28}[${i}]`, errors));
|
|
4004
4065
|
}
|
|
4005
4066
|
}
|
|
4006
4067
|
function checkType(value, type) {
|
|
@@ -5321,9 +5382,7 @@ var RecoveryLock = class {
|
|
|
5321
5382
|
hostname: this.hostname,
|
|
5322
5383
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5323
5384
|
};
|
|
5324
|
-
|
|
5325
|
-
await fsp2.writeFile(tmp, JSON.stringify(lock), { mode: 384 });
|
|
5326
|
-
await fsp2.rename(tmp, this.file);
|
|
5385
|
+
await atomicWrite(this.file, JSON.stringify(lock), { mode: 384 });
|
|
5327
5386
|
}
|
|
5328
5387
|
/**
|
|
5329
5388
|
* Release the lock. Idempotent — silently succeeds if the file is
|
|
@@ -6565,8 +6624,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
6565
6624
|
try {
|
|
6566
6625
|
await Promise.race([
|
|
6567
6626
|
Promise.resolve(iter.return?.()),
|
|
6568
|
-
new Promise((
|
|
6569
|
-
drainTimer = setTimeout(
|
|
6627
|
+
new Promise((resolve8) => {
|
|
6628
|
+
drainTimer = setTimeout(resolve8, 500);
|
|
6570
6629
|
})
|
|
6571
6630
|
]);
|
|
6572
6631
|
} finally {
|
|
@@ -6627,7 +6686,7 @@ async function runProviderWithRetry(opts) {
|
|
|
6627
6686
|
description
|
|
6628
6687
|
});
|
|
6629
6688
|
}
|
|
6630
|
-
await new Promise((
|
|
6689
|
+
await new Promise((resolve8, reject) => {
|
|
6631
6690
|
let settled = false;
|
|
6632
6691
|
const onAbort = () => {
|
|
6633
6692
|
if (settled) return;
|
|
@@ -6640,7 +6699,7 @@ async function runProviderWithRetry(opts) {
|
|
|
6640
6699
|
settled = true;
|
|
6641
6700
|
clearTimeout(t2);
|
|
6642
6701
|
signal.removeEventListener("abort", onAbort);
|
|
6643
|
-
|
|
6702
|
+
resolve8();
|
|
6644
6703
|
}, delay);
|
|
6645
6704
|
if (signal.aborted) {
|
|
6646
6705
|
onAbort();
|
|
@@ -8261,8 +8320,8 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8261
8320
|
await saveGoal(this.goalPath, abandoned);
|
|
8262
8321
|
}
|
|
8263
8322
|
try {
|
|
8264
|
-
const { unlink:
|
|
8265
|
-
await
|
|
8323
|
+
const { unlink: unlink12 } = await import('fs/promises');
|
|
8324
|
+
await unlink12(this.goalPath);
|
|
8266
8325
|
} catch {
|
|
8267
8326
|
}
|
|
8268
8327
|
this.opts.onEternalStop?.();
|
|
@@ -8284,7 +8343,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8284
8343
|
}
|
|
8285
8344
|
};
|
|
8286
8345
|
function sleep(ms) {
|
|
8287
|
-
return new Promise((
|
|
8346
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
8288
8347
|
}
|
|
8289
8348
|
|
|
8290
8349
|
// src/coordination/subagent-budget.ts
|
|
@@ -8434,12 +8493,12 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
8434
8493
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
8435
8494
|
return Promise.resolve("stop");
|
|
8436
8495
|
}
|
|
8437
|
-
return new Promise((
|
|
8496
|
+
return new Promise((resolve8) => {
|
|
8438
8497
|
let resolved = false;
|
|
8439
8498
|
const respond = (d) => {
|
|
8440
8499
|
if (resolved) return;
|
|
8441
8500
|
resolved = true;
|
|
8442
|
-
|
|
8501
|
+
resolve8(d);
|
|
8443
8502
|
};
|
|
8444
8503
|
const fallback = setTimeout(
|
|
8445
8504
|
() => respond("stop"),
|
|
@@ -11480,7 +11539,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
11480
11539
|
taskIds.map((id) => {
|
|
11481
11540
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
11482
11541
|
if (cached) return cached;
|
|
11483
|
-
return new Promise((
|
|
11542
|
+
return new Promise((resolve8, reject) => {
|
|
11484
11543
|
const timeout = setTimeout(() => {
|
|
11485
11544
|
this.off("task.completed", handler);
|
|
11486
11545
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -11489,7 +11548,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
11489
11548
|
if (result.taskId === id) {
|
|
11490
11549
|
clearTimeout(timeout);
|
|
11491
11550
|
this.off("task.completed", handler);
|
|
11492
|
-
|
|
11551
|
+
resolve8(result);
|
|
11493
11552
|
}
|
|
11494
11553
|
};
|
|
11495
11554
|
this.on("task.completed", handler);
|
|
@@ -11919,7 +11978,7 @@ function providerErrorToSubagentError(err, message, cause) {
|
|
|
11919
11978
|
|
|
11920
11979
|
// src/execution/parallel-eternal-engine.ts
|
|
11921
11980
|
function sleep2(ms) {
|
|
11922
|
-
return new Promise((
|
|
11981
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
11923
11982
|
}
|
|
11924
11983
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
11925
11984
|
var ParallelEternalEngine = class {
|
|
@@ -12700,7 +12759,7 @@ function makeSpawnTool(director, roster) {
|
|
|
12700
12759
|
catalog: roster
|
|
12701
12760
|
});
|
|
12702
12761
|
const dispatchRole = dispatchResult.role;
|
|
12703
|
-
if (roster
|
|
12762
|
+
if (roster?.[dispatchRole]) {
|
|
12704
12763
|
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole]);
|
|
12705
12764
|
} else {
|
|
12706
12765
|
const def = dispatchResult.definition;
|
|
@@ -13190,6 +13249,13 @@ var Director = class {
|
|
|
13190
13249
|
extendCounts.delete(guardKey);
|
|
13191
13250
|
return;
|
|
13192
13251
|
}
|
|
13252
|
+
if (payload.kind === "cost" && this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
13253
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
13254
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
13255
|
+
payload.deny();
|
|
13256
|
+
return;
|
|
13257
|
+
}
|
|
13258
|
+
}
|
|
13193
13259
|
extendCounts.set(guardKey, prior + 1);
|
|
13194
13260
|
setImmediate(() => {
|
|
13195
13261
|
const extra = {};
|
|
@@ -13554,11 +13620,11 @@ var Director = class {
|
|
|
13554
13620
|
if (cached) return cached;
|
|
13555
13621
|
const existing = this.taskWaiters.get(id);
|
|
13556
13622
|
if (existing) return existing.promise;
|
|
13557
|
-
let
|
|
13623
|
+
let resolve8;
|
|
13558
13624
|
const promise = new Promise((res) => {
|
|
13559
|
-
|
|
13625
|
+
resolve8 = res;
|
|
13560
13626
|
});
|
|
13561
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
13627
|
+
this.taskWaiters.set(id, { promise, resolve: resolve8 });
|
|
13562
13628
|
return promise;
|
|
13563
13629
|
})
|
|
13564
13630
|
);
|
|
@@ -13874,7 +13940,7 @@ function createDelegateTool(opts) {
|
|
|
13874
13940
|
subagentId
|
|
13875
13941
|
});
|
|
13876
13942
|
const dir = director;
|
|
13877
|
-
const result = await new Promise((
|
|
13943
|
+
const result = await new Promise((resolve8) => {
|
|
13878
13944
|
let settled = false;
|
|
13879
13945
|
let timer;
|
|
13880
13946
|
const finish = (value) => {
|
|
@@ -13883,7 +13949,7 @@ function createDelegateTool(opts) {
|
|
|
13883
13949
|
if (timer) clearTimeout(timer);
|
|
13884
13950
|
offTool();
|
|
13885
13951
|
offIter();
|
|
13886
|
-
|
|
13952
|
+
resolve8(value);
|
|
13887
13953
|
};
|
|
13888
13954
|
const arm = () => {
|
|
13889
13955
|
if (timer) clearTimeout(timer);
|
|
@@ -14688,6 +14754,13 @@ var TaskTracker = class {
|
|
|
14688
14754
|
opts;
|
|
14689
14755
|
graph = null;
|
|
14690
14756
|
transitions = [];
|
|
14757
|
+
/**
|
|
14758
|
+
* Attach an existing graph (used by PhaseOrchestrator to associate a tracker
|
|
14759
|
+
* with a phase's pre-built task graph without re-creating it).
|
|
14760
|
+
*/
|
|
14761
|
+
setGraph(graph) {
|
|
14762
|
+
this.graph = graph;
|
|
14763
|
+
}
|
|
14691
14764
|
async createGraph(specId, title) {
|
|
14692
14765
|
this.graph = {
|
|
14693
14766
|
id: crypto.randomUUID(),
|
|
@@ -14760,10 +14833,6 @@ var TaskTracker = class {
|
|
|
14760
14833
|
this.graph.updatedAt = now;
|
|
14761
14834
|
this.persist();
|
|
14762
14835
|
}
|
|
14763
|
-
/**
|
|
14764
|
-
* Update node fields (title, description, priority, estimateHours, tags).
|
|
14765
|
-
* Does NOT change status. Use updateNodeStatus for status changes.
|
|
14766
|
-
*/
|
|
14767
14836
|
updateNode(id, patch) {
|
|
14768
14837
|
if (!this.graph) throw new Error("No graph loaded");
|
|
14769
14838
|
const node = this.graph.nodes.get(id);
|
|
@@ -14841,8 +14910,7 @@ var TaskTracker = class {
|
|
|
14841
14910
|
}
|
|
14842
14911
|
return computeTaskProgress(this.graph);
|
|
14843
14912
|
}
|
|
14844
|
-
getTransitions(
|
|
14845
|
-
if (!taskId) return [...this.transitions];
|
|
14913
|
+
getTransitions(_taskId) {
|
|
14846
14914
|
return [...this.transitions];
|
|
14847
14915
|
}
|
|
14848
14916
|
unblockDependents(completedId) {
|
|
@@ -14882,18 +14950,12 @@ var TaskTracker = class {
|
|
|
14882
14950
|
* Fire-and-forget persistence with attached error handler.
|
|
14883
14951
|
* Synchronous mutators (addNode/addEdge/updateNodeStatus) use this to
|
|
14884
14952
|
* avoid forcing an async cascade through every caller; if the store
|
|
14885
|
-
*
|
|
14886
|
-
* surfaced instead of swallowed by an unhandled promise rejection.
|
|
14953
|
+
* is missing or throwing, the error is surfaced via onPersistError.
|
|
14887
14954
|
*/
|
|
14888
14955
|
persist() {
|
|
14889
14956
|
if (!this.graph) return;
|
|
14890
14957
|
this.opts.store.saveGraph(this.graph).catch((err) => {
|
|
14891
|
-
|
|
14892
|
-
else
|
|
14893
|
-
console.warn(
|
|
14894
|
-
"[task-tracker] saveGraph failed:",
|
|
14895
|
-
err instanceof Error ? err.message : String(err)
|
|
14896
|
-
);
|
|
14958
|
+
this.opts.onPersistError ? this.opts.onPersistError(err) : console.warn("[task-tracker] saveGraph failed:", err instanceof Error ? err.message : String(err));
|
|
14897
14959
|
});
|
|
14898
14960
|
}
|
|
14899
14961
|
};
|
|
@@ -15514,10 +15576,10 @@ var AISpecBuilder = class {
|
|
|
15514
15576
|
async saveSession() {
|
|
15515
15577
|
if (!this.sessionPath) return;
|
|
15516
15578
|
try {
|
|
15517
|
-
const
|
|
15518
|
-
const
|
|
15579
|
+
const fsp18 = await import('fs/promises');
|
|
15580
|
+
const path28 = await import('path');
|
|
15519
15581
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
15520
|
-
await
|
|
15582
|
+
await fsp18.mkdir(path28.dirname(this.sessionPath), { recursive: true });
|
|
15521
15583
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
15522
15584
|
} catch {
|
|
15523
15585
|
}
|
|
@@ -15526,8 +15588,8 @@ var AISpecBuilder = class {
|
|
|
15526
15588
|
async loadSession() {
|
|
15527
15589
|
if (!this.sessionPath) return false;
|
|
15528
15590
|
try {
|
|
15529
|
-
const
|
|
15530
|
-
const raw = await
|
|
15591
|
+
const fsp18 = await import('fs/promises');
|
|
15592
|
+
const raw = await fsp18.readFile(this.sessionPath, "utf8");
|
|
15531
15593
|
const loaded = JSON.parse(raw);
|
|
15532
15594
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
15533
15595
|
this.session = loaded;
|
|
@@ -15541,8 +15603,8 @@ var AISpecBuilder = class {
|
|
|
15541
15603
|
async deleteSession() {
|
|
15542
15604
|
if (!this.sessionPath) return;
|
|
15543
15605
|
try {
|
|
15544
|
-
const
|
|
15545
|
-
await
|
|
15606
|
+
const fsp18 = await import('fs/promises');
|
|
15607
|
+
await fsp18.unlink(this.sessionPath);
|
|
15546
15608
|
} catch {
|
|
15547
15609
|
}
|
|
15548
15610
|
}
|
|
@@ -16227,15 +16289,15 @@ function computeCriticalPath(graph, topoOrder, blockedByMap) {
|
|
|
16227
16289
|
maxId = id;
|
|
16228
16290
|
}
|
|
16229
16291
|
}
|
|
16230
|
-
const
|
|
16292
|
+
const path28 = [];
|
|
16231
16293
|
let current = maxId;
|
|
16232
16294
|
const visited = /* @__PURE__ */ new Set();
|
|
16233
16295
|
while (current && !visited.has(current)) {
|
|
16234
16296
|
visited.add(current);
|
|
16235
|
-
|
|
16297
|
+
path28.unshift(current);
|
|
16236
16298
|
current = prev.get(current) ?? null;
|
|
16237
16299
|
}
|
|
16238
|
-
return
|
|
16300
|
+
return path28;
|
|
16239
16301
|
}
|
|
16240
16302
|
function computeParallelGroups(graph, blockedByMap) {
|
|
16241
16303
|
const groups = [];
|
|
@@ -17030,9 +17092,9 @@ var DefaultHealthRegistry = class {
|
|
|
17030
17092
|
}
|
|
17031
17093
|
async runOne(check) {
|
|
17032
17094
|
let timer = null;
|
|
17033
|
-
const timeout = new Promise((
|
|
17095
|
+
const timeout = new Promise((resolve8) => {
|
|
17034
17096
|
timer = setTimeout(
|
|
17035
|
-
() =>
|
|
17097
|
+
() => resolve8({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
17036
17098
|
this.timeoutMs
|
|
17037
17099
|
);
|
|
17038
17100
|
});
|
|
@@ -17215,7 +17277,7 @@ async function startMetricsServer(opts) {
|
|
|
17215
17277
|
const tls = opts.tls;
|
|
17216
17278
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
17217
17279
|
const host = opts.host ?? "127.0.0.1";
|
|
17218
|
-
const
|
|
17280
|
+
const path28 = opts.path ?? "/metrics";
|
|
17219
17281
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
17220
17282
|
const healthRegistry = opts.healthRegistry;
|
|
17221
17283
|
const listener = (req, res) => {
|
|
@@ -17225,7 +17287,7 @@ async function startMetricsServer(opts) {
|
|
|
17225
17287
|
return;
|
|
17226
17288
|
}
|
|
17227
17289
|
const url = req.url.split("?")[0];
|
|
17228
|
-
if (url ===
|
|
17290
|
+
if (url === path28) {
|
|
17229
17291
|
let body;
|
|
17230
17292
|
try {
|
|
17231
17293
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -17271,14 +17333,14 @@ async function startMetricsServer(opts) {
|
|
|
17271
17333
|
const { createServer } = await import('http');
|
|
17272
17334
|
server = createServer(listener);
|
|
17273
17335
|
}
|
|
17274
|
-
await new Promise((
|
|
17336
|
+
await new Promise((resolve8, reject) => {
|
|
17275
17337
|
const onError = (err) => {
|
|
17276
17338
|
server.off("listening", onListening);
|
|
17277
17339
|
reject(err);
|
|
17278
17340
|
};
|
|
17279
17341
|
const onListening = () => {
|
|
17280
17342
|
server.off("error", onError);
|
|
17281
|
-
|
|
17343
|
+
resolve8();
|
|
17282
17344
|
};
|
|
17283
17345
|
server.once("error", onError);
|
|
17284
17346
|
server.once("listening", onListening);
|
|
@@ -17289,9 +17351,9 @@ async function startMetricsServer(opts) {
|
|
|
17289
17351
|
const protocol = useHttps ? "https" : "http";
|
|
17290
17352
|
return {
|
|
17291
17353
|
port: boundPort,
|
|
17292
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
17293
|
-
close: () => new Promise((
|
|
17294
|
-
server.close((err) => err ? reject(err) :
|
|
17354
|
+
url: `${protocol}://${host}:${boundPort}${path28}`,
|
|
17355
|
+
close: () => new Promise((resolve8, reject) => {
|
|
17356
|
+
server.close((err) => err ? reject(err) : resolve8());
|
|
17295
17357
|
})
|
|
17296
17358
|
};
|
|
17297
17359
|
}
|
|
@@ -17990,6 +18052,12 @@ async function extractTar(buf, destDir) {
|
|
|
17990
18052
|
const relPath = stripTopDir(fullPath);
|
|
17991
18053
|
if (relPath && relPath !== "." && relPath !== "..") {
|
|
17992
18054
|
const destPath = path6.join(destDir, relPath);
|
|
18055
|
+
const resolvedDest = path6.resolve(destPath);
|
|
18056
|
+
const resolvedRoot = path6.resolve(destDir);
|
|
18057
|
+
if (resolvedDest !== resolvedRoot && !resolvedDest.startsWith(resolvedRoot + path6.sep)) {
|
|
18058
|
+
offset += 512 + Math.ceil(size / 512) * 512;
|
|
18059
|
+
continue;
|
|
18060
|
+
}
|
|
17993
18061
|
if (typeflag === 53 || typeflag === 0) {
|
|
17994
18062
|
if (relPath.endsWith("/") || typeflag === 53) {
|
|
17995
18063
|
await fsp2.mkdir(destPath, { recursive: true });
|
|
@@ -19409,8 +19477,8 @@ var ReportGenerator = class {
|
|
|
19409
19477
|
try {
|
|
19410
19478
|
await stat(this.options.outputDir);
|
|
19411
19479
|
} catch {
|
|
19412
|
-
const { mkdir:
|
|
19413
|
-
await
|
|
19480
|
+
const { mkdir: mkdir14 } = await import('fs/promises');
|
|
19481
|
+
await mkdir14(this.options.outputDir, { recursive: true });
|
|
19414
19482
|
}
|
|
19415
19483
|
}
|
|
19416
19484
|
generateMarkdown(result) {
|
|
@@ -19686,7 +19754,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
19686
19754
|
const delay = Math.round(policy.delayMs(attempt));
|
|
19687
19755
|
const status = isProviderErr ? err.status : 0;
|
|
19688
19756
|
console.warn(`[SecurityScanner] retry ${attempt + 1} after ${delay}ms (status=${status}) \u2014 ${errAsErr.message}`);
|
|
19689
|
-
await new Promise((
|
|
19757
|
+
await new Promise((resolve8) => setTimeout(resolve8, delay));
|
|
19690
19758
|
return this.completeWithRetry(provider, request, abortController, attempt + 1);
|
|
19691
19759
|
}
|
|
19692
19760
|
}
|
|
@@ -20332,16 +20400,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
20332
20400
|
}
|
|
20333
20401
|
const index = Number.parseInt(reportId, 10) - 1;
|
|
20334
20402
|
if (!Number.isNaN(index) && reports[index]) {
|
|
20335
|
-
const { readFile:
|
|
20336
|
-
const content = await
|
|
20403
|
+
const { readFile: readFile32 } = await import('fs/promises');
|
|
20404
|
+
const content = await readFile32(join(reportsDir, reports[index]), "utf-8");
|
|
20337
20405
|
return { message: `# Security Report
|
|
20338
20406
|
|
|
20339
20407
|
${content}` };
|
|
20340
20408
|
}
|
|
20341
20409
|
const match = reports.find((r) => r.includes(reportId));
|
|
20342
20410
|
if (match) {
|
|
20343
|
-
const { readFile:
|
|
20344
|
-
const content = await
|
|
20411
|
+
const { readFile: readFile32 } = await import('fs/promises');
|
|
20412
|
+
const content = await readFile32(join(reportsDir, match), "utf-8");
|
|
20345
20413
|
return { message: `# Security Report
|
|
20346
20414
|
|
|
20347
20415
|
${content}` };
|
|
@@ -21183,12 +21251,12 @@ function makeContinueToNextIterationTool() {
|
|
|
21183
21251
|
// src/core/iteration-limit.ts
|
|
21184
21252
|
function requestLimitExtension(opts) {
|
|
21185
21253
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
21186
|
-
return new Promise((
|
|
21254
|
+
return new Promise((resolve8) => {
|
|
21187
21255
|
let resolved = false;
|
|
21188
21256
|
const timerFired = () => {
|
|
21189
21257
|
if (!resolved) {
|
|
21190
21258
|
resolved = true;
|
|
21191
|
-
|
|
21259
|
+
resolve8(0);
|
|
21192
21260
|
}
|
|
21193
21261
|
};
|
|
21194
21262
|
const timer = setTimeout(timerFired, timeoutMs);
|
|
@@ -21197,14 +21265,14 @@ function requestLimitExtension(opts) {
|
|
|
21197
21265
|
if (!resolved) {
|
|
21198
21266
|
resolved = true;
|
|
21199
21267
|
clearTimeout(timer);
|
|
21200
|
-
|
|
21268
|
+
resolve8(0);
|
|
21201
21269
|
}
|
|
21202
21270
|
};
|
|
21203
21271
|
const grant = (extra) => {
|
|
21204
21272
|
if (!resolved) {
|
|
21205
21273
|
resolved = true;
|
|
21206
21274
|
clearTimeout(timer);
|
|
21207
|
-
|
|
21275
|
+
resolve8(Math.max(0, extra));
|
|
21208
21276
|
}
|
|
21209
21277
|
};
|
|
21210
21278
|
events.emit("iteration.limit_reached", {
|
|
@@ -21218,7 +21286,7 @@ function requestLimitExtension(opts) {
|
|
|
21218
21286
|
if (!resolved) {
|
|
21219
21287
|
resolved = true;
|
|
21220
21288
|
clearTimeout(timer);
|
|
21221
|
-
|
|
21289
|
+
resolve8(100);
|
|
21222
21290
|
}
|
|
21223
21291
|
});
|
|
21224
21292
|
}
|
|
@@ -21787,13 +21855,13 @@ var Agent = class {
|
|
|
21787
21855
|
}
|
|
21788
21856
|
}
|
|
21789
21857
|
waitForConfirm(info) {
|
|
21790
|
-
return new Promise((
|
|
21858
|
+
return new Promise((resolve8) => {
|
|
21791
21859
|
this.events.emit("tool.confirm_needed", {
|
|
21792
21860
|
tool: info.tool,
|
|
21793
21861
|
input: info.input,
|
|
21794
21862
|
toolUseId: info.toolUseId,
|
|
21795
21863
|
suggestedPattern: info.suggestedPattern,
|
|
21796
|
-
resolve:
|
|
21864
|
+
resolve: resolve8
|
|
21797
21865
|
});
|
|
21798
21866
|
});
|
|
21799
21867
|
}
|
|
@@ -22510,12 +22578,12 @@ ${mem}`);
|
|
|
22510
22578
|
}
|
|
22511
22579
|
}
|
|
22512
22580
|
async gitStatus(root) {
|
|
22513
|
-
return new Promise((
|
|
22581
|
+
return new Promise((resolve8) => {
|
|
22514
22582
|
let settled = false;
|
|
22515
22583
|
const finish = (s) => {
|
|
22516
22584
|
if (settled) return;
|
|
22517
22585
|
settled = true;
|
|
22518
|
-
|
|
22586
|
+
resolve8(s);
|
|
22519
22587
|
};
|
|
22520
22588
|
let proc;
|
|
22521
22589
|
const timer = setTimeout(() => {
|
|
@@ -23281,7 +23349,7 @@ var PhaseGraphBuilder = class _PhaseGraphBuilder {
|
|
|
23281
23349
|
const tmpl = this.opts.phases[i];
|
|
23282
23350
|
const phaseId = crypto.randomUUID();
|
|
23283
23351
|
phaseIds.push(phaseId);
|
|
23284
|
-
const store = new DefaultTaskStore();
|
|
23352
|
+
const store = this.opts.externalTaskStore ?? new DefaultTaskStore();
|
|
23285
23353
|
const tracker = new TaskTracker({ store });
|
|
23286
23354
|
const taskGraph = await tracker.createGraph(phaseId, `${tmpl.name} Tasks`);
|
|
23287
23355
|
if (tmpl.taskTemplates && tmpl.taskTemplates.length > 0) {
|
|
@@ -23388,10 +23456,21 @@ var PhaseOrchestrator = class {
|
|
|
23388
23456
|
paused = false;
|
|
23389
23457
|
runningPhases = /* @__PURE__ */ new Set();
|
|
23390
23458
|
tickInterval = null;
|
|
23459
|
+
trackerCache = /* @__PURE__ */ new Map();
|
|
23460
|
+
taskRetryCounts = /* @__PURE__ */ new Map();
|
|
23461
|
+
// ── Git-worktree isolation (optional) ──────────────────────────────────────
|
|
23462
|
+
worktrees;
|
|
23463
|
+
/** Per-phase worktree handles, keyed by phase id. */
|
|
23464
|
+
phaseWorktrees = /* @__PURE__ */ new Map();
|
|
23465
|
+
/** Serializes all merges back to the base branch (one at a time). */
|
|
23466
|
+
mergeQueue = Promise.resolve();
|
|
23467
|
+
/** Per-phase merge promise, so a phase merges only after its deps do. */
|
|
23468
|
+
phaseMergePromise = /* @__PURE__ */ new Map();
|
|
23391
23469
|
constructor(opts) {
|
|
23392
23470
|
this.graph = opts.graph;
|
|
23393
23471
|
this.ctx = opts.ctx;
|
|
23394
23472
|
this.events = opts.events ?? this.createNoopEventBus();
|
|
23473
|
+
this.worktrees = opts.worktrees;
|
|
23395
23474
|
this.opts = {
|
|
23396
23475
|
maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
|
|
23397
23476
|
maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
|
|
@@ -23416,14 +23495,24 @@ var PhaseOrchestrator = class {
|
|
|
23416
23495
|
while (readyPhases.length > 0 && !this.stopped) {
|
|
23417
23496
|
const batch = readyPhases.slice(0, this.opts.maxConcurrentPhases);
|
|
23418
23497
|
await Promise.all(batch.map((p) => this.startPhase(p)));
|
|
23498
|
+
if (this.opts.phaseDelayMs > 0) {
|
|
23499
|
+
await this.delay(this.opts.phaseDelayMs);
|
|
23500
|
+
}
|
|
23419
23501
|
readyPhases = this.getReadyPhases().filter(
|
|
23420
23502
|
(p) => !this.runningPhases.has(p.id) && p.status !== "completed" && p.status !== "failed"
|
|
23421
23503
|
);
|
|
23422
23504
|
}
|
|
23505
|
+
await this.drainMerges();
|
|
23423
23506
|
if (this.opts.autonomous) {
|
|
23424
23507
|
this.tickInterval = setInterval(() => this.tick(), 1e3);
|
|
23425
23508
|
}
|
|
23426
23509
|
}
|
|
23510
|
+
/** Bekleyen tüm faz merge'lerini (dep-sıralı + global seri) bekle. */
|
|
23511
|
+
async drainMerges() {
|
|
23512
|
+
await Promise.allSettled([...this.phaseMergePromise.values()]);
|
|
23513
|
+
await this.mergeQueue.catch(() => {
|
|
23514
|
+
});
|
|
23515
|
+
}
|
|
23427
23516
|
/** Duraklat — aktif fazlar çalışmaya devam eder ama yeni faz başlamaz */
|
|
23428
23517
|
pause() {
|
|
23429
23518
|
this.paused = true;
|
|
@@ -23431,7 +23520,9 @@ var PhaseOrchestrator = class {
|
|
|
23431
23520
|
/** Devam et — yeni fazlar başlayabilir */
|
|
23432
23521
|
resume() {
|
|
23433
23522
|
this.paused = false;
|
|
23434
|
-
|
|
23523
|
+
this.tick().catch((err) => {
|
|
23524
|
+
console.error("[phase-orchestrator] tick failed:", err instanceof Error ? err.message : String(err));
|
|
23525
|
+
});
|
|
23435
23526
|
}
|
|
23436
23527
|
/** Tamamen durdur — aktif fazlar da durdurulur */
|
|
23437
23528
|
stop() {
|
|
@@ -23446,21 +23537,29 @@ var PhaseOrchestrator = class {
|
|
|
23446
23537
|
this.updatePhaseStatus(phase, "paused");
|
|
23447
23538
|
}
|
|
23448
23539
|
}
|
|
23540
|
+
if (this.worktrees) {
|
|
23541
|
+
for (const handle of this.worktrees.list()) {
|
|
23542
|
+
void this.worktrees.release(handle, { keep: true }).catch(() => {
|
|
23543
|
+
});
|
|
23544
|
+
}
|
|
23545
|
+
}
|
|
23449
23546
|
}
|
|
23450
23547
|
// ─── Tick Loop (Autonomous) ───────────────────────────────────────────────
|
|
23451
23548
|
async tick() {
|
|
23452
23549
|
if (this.stopped || this.paused) return;
|
|
23453
23550
|
const active = this.getActivePhases();
|
|
23454
|
-
const
|
|
23551
|
+
const queued = this.getReadyPhases();
|
|
23455
23552
|
this.emit("autonomous.tick", {
|
|
23456
23553
|
activePhases: active.map((p) => p.id),
|
|
23457
|
-
queuedPhases:
|
|
23554
|
+
queuedPhases: queued.map((p) => p.id)
|
|
23458
23555
|
});
|
|
23459
|
-
this.ctx.onTick?.({ activePhases: active, readyPhases:
|
|
23556
|
+
this.ctx.onTick?.({ activePhases: active, readyPhases: queued });
|
|
23460
23557
|
const availableSlots = this.opts.maxConcurrentPhases - active.length;
|
|
23461
|
-
if (availableSlots > 0 &&
|
|
23462
|
-
for (const phase of
|
|
23463
|
-
|
|
23558
|
+
if (availableSlots > 0 && queued.length > 0) {
|
|
23559
|
+
for (const phase of queued.slice(0, availableSlots)) {
|
|
23560
|
+
if (phase.status === "pending") {
|
|
23561
|
+
await this.startPhase(phase);
|
|
23562
|
+
}
|
|
23464
23563
|
}
|
|
23465
23564
|
}
|
|
23466
23565
|
if (this.isComplete()) {
|
|
@@ -23482,6 +23581,16 @@ var PhaseOrchestrator = class {
|
|
|
23482
23581
|
phase.startedAt = Date.now();
|
|
23483
23582
|
this.runningPhases.add(phase.id);
|
|
23484
23583
|
this.graph.activePhaseIds.push(phase.id);
|
|
23584
|
+
if (this.worktrees && !this.phaseWorktrees.has(phase.id)) {
|
|
23585
|
+
try {
|
|
23586
|
+
const handle = await this.worktrees.allocate(phase.id, {
|
|
23587
|
+
slugHint: phase.name,
|
|
23588
|
+
ownerLabel: phase.name
|
|
23589
|
+
});
|
|
23590
|
+
if (handle.status === "active") this.phaseWorktrees.set(phase.id, handle);
|
|
23591
|
+
} catch {
|
|
23592
|
+
}
|
|
23593
|
+
}
|
|
23485
23594
|
this.emit("phase.started", { phaseId: phase.id, name: phase.name });
|
|
23486
23595
|
try {
|
|
23487
23596
|
await this.executePhaseTasks(phase);
|
|
@@ -23497,17 +23606,20 @@ var PhaseOrchestrator = class {
|
|
|
23497
23606
|
phase.completedAt = Date.now();
|
|
23498
23607
|
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23499
23608
|
this.runningPhases.delete(phase.id);
|
|
23609
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23500
23610
|
this.emit("phase.failed", {
|
|
23501
23611
|
phaseId: phase.id,
|
|
23502
23612
|
name: phase.name,
|
|
23503
23613
|
error: `${failedTasks} task(s) failed`
|
|
23504
23614
|
});
|
|
23505
23615
|
this.ctx.onPhaseFail?.(phase, new Error(`${failedTasks} task(s) failed`));
|
|
23616
|
+
await this.keepWorktreeForReview(phase);
|
|
23506
23617
|
} else {
|
|
23507
23618
|
this.updatePhaseStatus(phase, "completed");
|
|
23508
23619
|
phase.completedAt = Date.now();
|
|
23509
23620
|
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23510
23621
|
this.runningPhases.delete(phase.id);
|
|
23622
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23511
23623
|
this.graph.completedPhaseIds.push(phase.id);
|
|
23512
23624
|
this.emit("phase.completed", {
|
|
23513
23625
|
phaseId: phase.id,
|
|
@@ -23515,12 +23627,14 @@ var PhaseOrchestrator = class {
|
|
|
23515
23627
|
durationMs: phase.actualDurationMs
|
|
23516
23628
|
});
|
|
23517
23629
|
this.ctx.onPhaseComplete?.(phase);
|
|
23630
|
+
await this.commitAndEnqueueMerge(phase);
|
|
23518
23631
|
}
|
|
23519
23632
|
} catch (error) {
|
|
23520
23633
|
this.updatePhaseStatus(phase, "failed");
|
|
23521
23634
|
phase.completedAt = Date.now();
|
|
23522
23635
|
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
23523
23636
|
this.runningPhases.delete(phase.id);
|
|
23637
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
23524
23638
|
this.graph.failedPhaseIds.push(phase.id);
|
|
23525
23639
|
this.emit("phase.failed", {
|
|
23526
23640
|
phaseId: phase.id,
|
|
@@ -23528,10 +23642,57 @@ var PhaseOrchestrator = class {
|
|
|
23528
23642
|
error: error instanceof Error ? error.message : String(error)
|
|
23529
23643
|
});
|
|
23530
23644
|
this.ctx.onPhaseFail?.(phase, error instanceof Error ? error : new Error(String(error)));
|
|
23645
|
+
await this.keepWorktreeForReview(phase);
|
|
23646
|
+
}
|
|
23647
|
+
}
|
|
23648
|
+
// ─── Worktree integration ───────────────────────────────────────────────────
|
|
23649
|
+
/**
|
|
23650
|
+
* Commit the phase's worktree changes, then enqueue the merge back into the
|
|
23651
|
+
* base branch. Merges run dependency-ordered (a phase merges only after its
|
|
23652
|
+
* `dependsOn` phases) and globally serialized (one at a time) to avoid
|
|
23653
|
+
* concurrent writes to the base tree.
|
|
23654
|
+
*/
|
|
23655
|
+
async commitAndEnqueueMerge(phase) {
|
|
23656
|
+
const handle = this.phaseWorktrees.get(phase.id);
|
|
23657
|
+
if (!this.worktrees || !handle) return;
|
|
23658
|
+
try {
|
|
23659
|
+
await this.worktrees.commitAll(handle, `autophase(${phase.name}): ${phase.id}`);
|
|
23660
|
+
} catch {
|
|
23661
|
+
}
|
|
23662
|
+
const depPromises = phase.dependsOn.map((d) => this.phaseMergePromise.get(d)).filter((p) => Boolean(p));
|
|
23663
|
+
const merged = (async () => {
|
|
23664
|
+
await Promise.allSettled(depPromises);
|
|
23665
|
+
this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle));
|
|
23666
|
+
await this.mergeQueue;
|
|
23667
|
+
})();
|
|
23668
|
+
this.phaseMergePromise.set(phase.id, merged);
|
|
23669
|
+
}
|
|
23670
|
+
/** Squash-merge one phase. Conflicts mark the worktree needs-review (run continues). */
|
|
23671
|
+
async mergeOne(phase, handle) {
|
|
23672
|
+
if (!this.worktrees) return;
|
|
23673
|
+
try {
|
|
23674
|
+
const result = await this.worktrees.merge(handle, { squash: true });
|
|
23675
|
+
await this.worktrees.release(handle, { keep: !result.ok });
|
|
23676
|
+
} catch (err) {
|
|
23677
|
+
this.emit("phase.failed", {
|
|
23678
|
+
phaseId: phase.id,
|
|
23679
|
+
name: phase.name,
|
|
23680
|
+
error: `worktree merge failed: ${err instanceof Error ? err.message : String(err)}`
|
|
23681
|
+
});
|
|
23531
23682
|
}
|
|
23532
23683
|
}
|
|
23684
|
+
/** A failed phase keeps its worktree on disk for inspection (no merge). */
|
|
23685
|
+
async keepWorktreeForReview(phase) {
|
|
23686
|
+
const handle = this.phaseWorktrees.get(phase.id);
|
|
23687
|
+
if (!this.worktrees || !handle) return;
|
|
23688
|
+
try {
|
|
23689
|
+
await this.worktrees.commitAll(handle, `autophase(${phase.name}) [failed]: ${phase.id}`);
|
|
23690
|
+
} catch {
|
|
23691
|
+
}
|
|
23692
|
+
await this.worktrees.release(handle, { keep: true }).catch(() => {
|
|
23693
|
+
});
|
|
23694
|
+
}
|
|
23533
23695
|
async executePhaseTasks(phase) {
|
|
23534
|
-
phase.taskGraph;
|
|
23535
23696
|
const pendingTasks = this.getExecutableTasks(phase);
|
|
23536
23697
|
while (pendingTasks.length > 0 && !this.stopped) {
|
|
23537
23698
|
const batch = pendingTasks.splice(0, this.opts.maxConcurrentTasks);
|
|
@@ -23556,7 +23717,8 @@ var PhaseOrchestrator = class {
|
|
|
23556
23717
|
async executeSingleTask(task, phase) {
|
|
23557
23718
|
const tracker = this.getTrackerForPhase(phase);
|
|
23558
23719
|
tracker.updateNodeStatus(task.id, "in_progress");
|
|
23559
|
-
|
|
23720
|
+
const handle = this.phaseWorktrees.get(phase.id);
|
|
23721
|
+
return this.ctx.executeTask(task, phase.id, { cwd: handle?.dir, branch: handle?.branch });
|
|
23560
23722
|
}
|
|
23561
23723
|
markTaskCompleted(phase, task) {
|
|
23562
23724
|
const tracker = this.getTrackerForPhase(phase);
|
|
@@ -23569,13 +23731,27 @@ var PhaseOrchestrator = class {
|
|
|
23569
23731
|
}
|
|
23570
23732
|
markTaskFailed(phase, task, error) {
|
|
23571
23733
|
const tracker = this.getTrackerForPhase(phase);
|
|
23572
|
-
|
|
23573
|
-
this.
|
|
23574
|
-
|
|
23575
|
-
|
|
23576
|
-
|
|
23577
|
-
|
|
23578
|
-
|
|
23734
|
+
const taskKey = `${phase.id}:${task.id}`;
|
|
23735
|
+
const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
|
|
23736
|
+
if (currentRetries < this.opts.maxRetries) {
|
|
23737
|
+
this.taskRetryCounts.set(taskKey, currentRetries + 1);
|
|
23738
|
+
tracker.updateNodeStatus(task.id, "pending", `Retry ${currentRetries + 1}/${this.opts.maxRetries}`);
|
|
23739
|
+
this.emit("phase.taskRetrying", {
|
|
23740
|
+
phaseId: phase.id,
|
|
23741
|
+
taskId: task.id,
|
|
23742
|
+
taskTitle: task.title,
|
|
23743
|
+
attempt: currentRetries + 1,
|
|
23744
|
+
maxRetries: this.opts.maxRetries
|
|
23745
|
+
});
|
|
23746
|
+
} else {
|
|
23747
|
+
tracker.updateNodeStatus(task.id, "failed", error instanceof Error ? error.message : String(error));
|
|
23748
|
+
this.emit("phase.taskFailed", {
|
|
23749
|
+
phaseId: phase.id,
|
|
23750
|
+
taskId: task.id,
|
|
23751
|
+
taskTitle: task.title,
|
|
23752
|
+
error: error instanceof Error ? error.message : String(error)
|
|
23753
|
+
});
|
|
23754
|
+
}
|
|
23579
23755
|
}
|
|
23580
23756
|
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
23581
23757
|
getReadyPhases() {
|
|
@@ -23586,7 +23762,7 @@ var PhaseOrchestrator = class {
|
|
|
23586
23762
|
const dep = this.graph.phases.get(depId);
|
|
23587
23763
|
return dep?.status === "completed" || dep?.status === "skipped";
|
|
23588
23764
|
});
|
|
23589
|
-
if (depsDone) {
|
|
23765
|
+
if (depsDone || phase.parallelizable) {
|
|
23590
23766
|
ready.push(phase);
|
|
23591
23767
|
}
|
|
23592
23768
|
}
|
|
@@ -23605,18 +23781,20 @@ var PhaseOrchestrator = class {
|
|
|
23605
23781
|
});
|
|
23606
23782
|
}
|
|
23607
23783
|
getTrackerForPhase(phase) {
|
|
23784
|
+
if (this.trackerCache.has(phase.id)) {
|
|
23785
|
+
return this.trackerCache.get(phase.id);
|
|
23786
|
+
}
|
|
23608
23787
|
const store = new DefaultTaskStore();
|
|
23609
23788
|
const tracker = new TaskTracker({ store });
|
|
23610
|
-
tracker.
|
|
23789
|
+
tracker.setGraph(phase.taskGraph);
|
|
23790
|
+
this.trackerCache.set(phase.id, tracker);
|
|
23611
23791
|
return tracker;
|
|
23612
23792
|
}
|
|
23613
23793
|
getFailedTaskCount(phase) {
|
|
23614
|
-
|
|
23615
|
-
return tracker.getAllNodes({ status: ["failed"] }).length;
|
|
23794
|
+
return this.getTrackerForPhase(phase).getAllNodes({ status: ["failed"] }).length;
|
|
23616
23795
|
}
|
|
23617
23796
|
getCompletedTaskCount(phase) {
|
|
23618
|
-
|
|
23619
|
-
return tracker.getAllNodes({ status: ["completed"] }).length;
|
|
23797
|
+
return this.getTrackerForPhase(phase).getAllNodes({ status: ["completed"] }).length;
|
|
23620
23798
|
}
|
|
23621
23799
|
updatePhaseStatus(phase, status) {
|
|
23622
23800
|
const from = phase.status;
|
|
@@ -23648,8 +23826,18 @@ var PhaseOrchestrator = class {
|
|
|
23648
23826
|
// ─── Progress ─────────────────────────────────────────────────────────────
|
|
23649
23827
|
getProgress() {
|
|
23650
23828
|
const phases = Array.from(this.graph.phases.values());
|
|
23651
|
-
let pending = 0
|
|
23652
|
-
let
|
|
23829
|
+
let pending = 0;
|
|
23830
|
+
let ready = 0;
|
|
23831
|
+
let running = 0;
|
|
23832
|
+
let paused = 0;
|
|
23833
|
+
let completed = 0;
|
|
23834
|
+
let failed = 0;
|
|
23835
|
+
let skipped = 0;
|
|
23836
|
+
let totalTasks = 0;
|
|
23837
|
+
let completedTasks = 0;
|
|
23838
|
+
let failedTasks = 0;
|
|
23839
|
+
let estimatedHours = 0;
|
|
23840
|
+
let actualHours = 0;
|
|
23653
23841
|
for (const p of phases) {
|
|
23654
23842
|
switch (p.status) {
|
|
23655
23843
|
case "pending":
|
|
@@ -23752,6 +23940,9 @@ var PhaseOrchestrator = class {
|
|
|
23752
23940
|
}
|
|
23753
23941
|
};
|
|
23754
23942
|
}
|
|
23943
|
+
delay(ms) {
|
|
23944
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
23945
|
+
}
|
|
23755
23946
|
};
|
|
23756
23947
|
|
|
23757
23948
|
// src/autophase/auto-phase-runner.ts
|
|
@@ -23760,6 +23951,26 @@ var AutoPhaseRunner = class {
|
|
|
23760
23951
|
orchestrator = null;
|
|
23761
23952
|
opts;
|
|
23762
23953
|
progressInterval = null;
|
|
23954
|
+
graphCompletedHandler = (payload) => {
|
|
23955
|
+
const p = payload;
|
|
23956
|
+
if (this.graph && p.graphId === this.graph.id) {
|
|
23957
|
+
this.opts.onComplete?.(this.graph);
|
|
23958
|
+
this.cleanup();
|
|
23959
|
+
}
|
|
23960
|
+
};
|
|
23961
|
+
graphFailedHandler = (payload) => {
|
|
23962
|
+
const p = payload;
|
|
23963
|
+
if (this.graph && p.graphId === this.graph.id) {
|
|
23964
|
+
const failedPhase = this.graph.phases.get(p.failedPhaseId);
|
|
23965
|
+
if (failedPhase) {
|
|
23966
|
+
this.opts.onFail?.(this.graph, failedPhase, new Error(p.error));
|
|
23967
|
+
}
|
|
23968
|
+
this.cleanup();
|
|
23969
|
+
}
|
|
23970
|
+
};
|
|
23971
|
+
/** Stores the unsubscribe function returned by EventBus.on() */
|
|
23972
|
+
unsubscribeCompleted = null;
|
|
23973
|
+
unsubscribeFailed = null;
|
|
23763
23974
|
constructor(opts) {
|
|
23764
23975
|
this.opts = opts;
|
|
23765
23976
|
}
|
|
@@ -23801,31 +24012,11 @@ var AutoPhaseRunner = class {
|
|
|
23801
24012
|
this.opts.onProgress(progress);
|
|
23802
24013
|
}, 2e3);
|
|
23803
24014
|
}
|
|
23804
|
-
|
|
23805
|
-
|
|
23806
|
-
events.on
|
|
23807
|
-
|
|
23808
|
-
|
|
23809
|
-
const p = payload;
|
|
23810
|
-
if (this.graph && p.graphId === this.graph.id) {
|
|
23811
|
-
this.opts.onComplete?.(this.graph);
|
|
23812
|
-
this.cleanup();
|
|
23813
|
-
}
|
|
23814
|
-
}
|
|
23815
|
-
);
|
|
23816
|
-
events.on(
|
|
23817
|
-
"graph.failed",
|
|
23818
|
-
(payload) => {
|
|
23819
|
-
const p = payload;
|
|
23820
|
-
if (this.graph && p.graphId === this.graph.id) {
|
|
23821
|
-
const failedPhase = this.graph.phases.get(p.failedPhaseId);
|
|
23822
|
-
if (failedPhase) {
|
|
23823
|
-
this.opts.onFail?.(this.graph, failedPhase, new Error(p.error));
|
|
23824
|
-
}
|
|
23825
|
-
this.cleanup();
|
|
23826
|
-
}
|
|
23827
|
-
}
|
|
23828
|
-
);
|
|
24015
|
+
if (this.opts.events) {
|
|
24016
|
+
const events = this.opts.events;
|
|
24017
|
+
const onUntyped = events.on;
|
|
24018
|
+
this.unsubscribeCompleted = onUntyped("graph.completed", this.graphCompletedHandler);
|
|
24019
|
+
this.unsubscribeFailed = onUntyped("graph.failed", this.graphFailedHandler);
|
|
23829
24020
|
}
|
|
23830
24021
|
await this.orchestrator.start();
|
|
23831
24022
|
return this.graph;
|
|
@@ -23863,6 +24054,10 @@ var AutoPhaseRunner = class {
|
|
|
23863
24054
|
clearInterval(this.progressInterval);
|
|
23864
24055
|
this.progressInterval = null;
|
|
23865
24056
|
}
|
|
24057
|
+
this.unsubscribeCompleted?.();
|
|
24058
|
+
this.unsubscribeCompleted = null;
|
|
24059
|
+
this.unsubscribeFailed?.();
|
|
24060
|
+
this.unsubscribeFailed = null;
|
|
23866
24061
|
}
|
|
23867
24062
|
};
|
|
23868
24063
|
async function createAutoPhaseFromTaskGraph(taskGraph, options) {
|
|
@@ -23883,6 +24078,181 @@ async function createAutoPhaseFromTaskGraph(taskGraph, options) {
|
|
|
23883
24078
|
phases
|
|
23884
24079
|
});
|
|
23885
24080
|
}
|
|
24081
|
+
|
|
24082
|
+
// src/autophase/auto-phase-planner.ts
|
|
24083
|
+
var VALID_TASK_TYPES = /* @__PURE__ */ new Set([
|
|
24084
|
+
"feature",
|
|
24085
|
+
"bugfix",
|
|
24086
|
+
"refactor",
|
|
24087
|
+
"docs",
|
|
24088
|
+
"test",
|
|
24089
|
+
"chore"
|
|
24090
|
+
]);
|
|
24091
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set([
|
|
24092
|
+
"critical",
|
|
24093
|
+
"high",
|
|
24094
|
+
"medium",
|
|
24095
|
+
"low"
|
|
24096
|
+
]);
|
|
24097
|
+
var AutoPhasePlanner = class {
|
|
24098
|
+
constructor(opts) {
|
|
24099
|
+
this.opts = opts;
|
|
24100
|
+
}
|
|
24101
|
+
opts;
|
|
24102
|
+
/** Hedefi faz+todo planına dönüştür. */
|
|
24103
|
+
async plan() {
|
|
24104
|
+
const prompt = this.buildPrompt();
|
|
24105
|
+
const raw = await this.opts.runOnce(prompt);
|
|
24106
|
+
const phases = this.parse(raw);
|
|
24107
|
+
return { phases, raw, parseFailed: phases.length === 0 };
|
|
24108
|
+
}
|
|
24109
|
+
/** Modelin üreteceği plan için talimat prompt'u. */
|
|
24110
|
+
buildPrompt() {
|
|
24111
|
+
const minP = this.opts.minPhases ?? 3;
|
|
24112
|
+
const maxP = this.opts.maxPhases ?? 8;
|
|
24113
|
+
const todos = this.opts.todosPerPhase ?? 6;
|
|
24114
|
+
const ctx = this.opts.projectContext?.trim();
|
|
24115
|
+
return [
|
|
24116
|
+
"You are an expert software project planner. Break the following goal into",
|
|
24117
|
+
`a dependency-ordered list of ${minP}\u2013${maxP} PHASES. Each phase must contain`,
|
|
24118
|
+
`roughly ${todos} concrete, individually-actionable TODO tasks.`,
|
|
24119
|
+
"",
|
|
24120
|
+
`GOAL: ${this.opts.goal}`,
|
|
24121
|
+
ctx ? `
|
|
24122
|
+
PROJECT CONTEXT:
|
|
24123
|
+
${ctx}
|
|
24124
|
+
` : "",
|
|
24125
|
+
"Rules:",
|
|
24126
|
+
"- Phases run in order; earlier phases are prerequisites for later ones.",
|
|
24127
|
+
"- Each todo must be small enough for one focused work session.",
|
|
24128
|
+
"- Each todo must be self-contained (an agent will execute it in isolation).",
|
|
24129
|
+
'- Prefer concrete verbs ("Add X", "Refactor Y", "Write tests for Z").',
|
|
24130
|
+
"",
|
|
24131
|
+
"Respond with ONLY a JSON array inside a ```json code fence. No prose before",
|
|
24132
|
+
"or after. Schema (TypeScript):",
|
|
24133
|
+
"",
|
|
24134
|
+
"```json",
|
|
24135
|
+
"[",
|
|
24136
|
+
" {",
|
|
24137
|
+
' "name": "Phase name",',
|
|
24138
|
+
' "description": "What this phase accomplishes",',
|
|
24139
|
+
' "priority": "critical" | "high" | "medium" | "low",',
|
|
24140
|
+
' "estimateHours": number,',
|
|
24141
|
+
' "parallelizable": boolean,',
|
|
24142
|
+
' "tasks": [',
|
|
24143
|
+
" {",
|
|
24144
|
+
' "title": "Short task title",',
|
|
24145
|
+
' "description": "What to do and how to know it is done",',
|
|
24146
|
+
' "type": "feature" | "bugfix" | "refactor" | "docs" | "test" | "chore",',
|
|
24147
|
+
' "priority": "critical" | "high" | "medium" | "low",',
|
|
24148
|
+
' "estimateHours": number,',
|
|
24149
|
+
' "tags": ["optional", "labels"]',
|
|
24150
|
+
" }",
|
|
24151
|
+
" ]",
|
|
24152
|
+
" }",
|
|
24153
|
+
"]",
|
|
24154
|
+
"```"
|
|
24155
|
+
].filter((l) => l !== "").join("\n");
|
|
24156
|
+
}
|
|
24157
|
+
/** Ham çıktıdan JSON'u çıkar, doğrula ve PhaseTemplate[]'e dönüştür. */
|
|
24158
|
+
parse(raw) {
|
|
24159
|
+
const json = extractJSONArray(raw);
|
|
24160
|
+
if (!json) return [];
|
|
24161
|
+
let data;
|
|
24162
|
+
try {
|
|
24163
|
+
data = JSON.parse(json);
|
|
24164
|
+
} catch {
|
|
24165
|
+
return [];
|
|
24166
|
+
}
|
|
24167
|
+
if (!Array.isArray(data)) return [];
|
|
24168
|
+
const phases = [];
|
|
24169
|
+
for (const entry of data) {
|
|
24170
|
+
const phase = this.coercePhase(entry);
|
|
24171
|
+
if (phase) phases.push(phase);
|
|
24172
|
+
}
|
|
24173
|
+
return phases;
|
|
24174
|
+
}
|
|
24175
|
+
coercePhase(entry) {
|
|
24176
|
+
if (!entry || typeof entry !== "object") return null;
|
|
24177
|
+
const e = entry;
|
|
24178
|
+
const name = typeof e.name === "string" ? e.name.trim() : "";
|
|
24179
|
+
if (!name) return null;
|
|
24180
|
+
const rawTasks = Array.isArray(e.tasks) ? e.tasks : Array.isArray(e.taskTemplates) ? e.taskTemplates : [];
|
|
24181
|
+
const taskTemplates = rawTasks.map((t2) => this.coerceTask(t2)).filter((t2) => t2 !== null);
|
|
24182
|
+
return {
|
|
24183
|
+
name,
|
|
24184
|
+
description: typeof e.description === "string" ? e.description : "",
|
|
24185
|
+
priority: coercePriority(e.priority),
|
|
24186
|
+
estimateHours: coerceHours(e.estimateHours, 4),
|
|
24187
|
+
parallelizable: e.parallelizable === true,
|
|
24188
|
+
taskTemplates
|
|
24189
|
+
};
|
|
24190
|
+
}
|
|
24191
|
+
coerceTask(t2) {
|
|
24192
|
+
if (!t2 || typeof t2 !== "object") return null;
|
|
24193
|
+
const o = t2;
|
|
24194
|
+
const title = typeof o.title === "string" ? o.title.trim() : "";
|
|
24195
|
+
if (!title) return null;
|
|
24196
|
+
const type = VALID_TASK_TYPES.has(o.type) ? o.type : "feature";
|
|
24197
|
+
return {
|
|
24198
|
+
title,
|
|
24199
|
+
description: typeof o.description === "string" ? o.description : "",
|
|
24200
|
+
type,
|
|
24201
|
+
priority: coercePriority(o.priority),
|
|
24202
|
+
estimateHours: coerceHours(o.estimateHours, 2),
|
|
24203
|
+
tags: Array.isArray(o.tags) ? o.tags.map(String) : []
|
|
24204
|
+
};
|
|
24205
|
+
}
|
|
24206
|
+
};
|
|
24207
|
+
function coercePriority(value) {
|
|
24208
|
+
return VALID_PRIORITIES.has(value) ? value : "medium";
|
|
24209
|
+
}
|
|
24210
|
+
function coerceHours(value, fallback) {
|
|
24211
|
+
const n = Number(value);
|
|
24212
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
24213
|
+
}
|
|
24214
|
+
function extractJSONArray(text) {
|
|
24215
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
24216
|
+
const candidates = [];
|
|
24217
|
+
if (fence?.[1]) candidates.push(fence[1]);
|
|
24218
|
+
candidates.push(text);
|
|
24219
|
+
for (const candidate of candidates) {
|
|
24220
|
+
const balanced = firstBalancedArray(candidate);
|
|
24221
|
+
if (balanced) return balanced;
|
|
24222
|
+
}
|
|
24223
|
+
return null;
|
|
24224
|
+
}
|
|
24225
|
+
function firstBalancedArray(text) {
|
|
24226
|
+
const start = text.indexOf("[");
|
|
24227
|
+
if (start === -1) return null;
|
|
24228
|
+
let depth = 0;
|
|
24229
|
+
let inString = false;
|
|
24230
|
+
let escaped = false;
|
|
24231
|
+
for (let i = start; i < text.length; i++) {
|
|
24232
|
+
const ch = text[i];
|
|
24233
|
+
if (inString) {
|
|
24234
|
+
if (escaped) {
|
|
24235
|
+
escaped = false;
|
|
24236
|
+
} else if (ch === "\\") {
|
|
24237
|
+
escaped = true;
|
|
24238
|
+
} else if (ch === '"') {
|
|
24239
|
+
inString = false;
|
|
24240
|
+
}
|
|
24241
|
+
continue;
|
|
24242
|
+
}
|
|
24243
|
+
if (ch === '"') {
|
|
24244
|
+
inString = true;
|
|
24245
|
+
} else if (ch === "[") {
|
|
24246
|
+
depth++;
|
|
24247
|
+
} else if (ch === "]") {
|
|
24248
|
+
depth--;
|
|
24249
|
+
if (depth === 0) {
|
|
24250
|
+
return text.slice(start, i + 1);
|
|
24251
|
+
}
|
|
24252
|
+
}
|
|
24253
|
+
}
|
|
24254
|
+
return null;
|
|
24255
|
+
}
|
|
23886
24256
|
var PhaseStore = class {
|
|
23887
24257
|
baseDir;
|
|
23888
24258
|
constructor(opts) {
|
|
@@ -24046,22 +24416,26 @@ var PhaseStore = class {
|
|
|
24046
24416
|
specId: serialized.specId,
|
|
24047
24417
|
title: serialized.title,
|
|
24048
24418
|
nodes,
|
|
24049
|
-
edges: serialized.edges,
|
|
24050
|
-
rootNodes: serialized.rootNodes,
|
|
24419
|
+
edges: serialized.edges ?? [],
|
|
24420
|
+
rootNodes: serialized.rootNodes ?? [],
|
|
24051
24421
|
createdAt: serialized.createdAt,
|
|
24052
24422
|
updatedAt: serialized.updatedAt
|
|
24053
24423
|
};
|
|
24054
24424
|
}
|
|
24055
24425
|
};
|
|
24056
|
-
|
|
24057
|
-
// src/autophase/checkpoint.ts
|
|
24058
24426
|
var CheckpointManager = class {
|
|
24059
24427
|
store;
|
|
24060
24428
|
maxCheckpoints;
|
|
24061
24429
|
checkpoints = /* @__PURE__ */ new Map();
|
|
24430
|
+
baseDir;
|
|
24062
24431
|
constructor(opts) {
|
|
24063
24432
|
this.store = opts.store;
|
|
24064
24433
|
this.maxCheckpoints = opts.maxCheckpoints ?? 10;
|
|
24434
|
+
this.baseDir = opts.baseDir ?? path6.join(opts.store.baseDir, ".checkpoints");
|
|
24435
|
+
}
|
|
24436
|
+
async initialize() {
|
|
24437
|
+
await fsp2.mkdir(this.baseDir, { recursive: true });
|
|
24438
|
+
await this.loadFromDisk();
|
|
24065
24439
|
}
|
|
24066
24440
|
async saveCheckpoint(graph, label) {
|
|
24067
24441
|
await this.store.save(graph);
|
|
@@ -24082,11 +24456,16 @@ var CheckpointManager = class {
|
|
|
24082
24456
|
label
|
|
24083
24457
|
};
|
|
24084
24458
|
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
24085
|
-
this.
|
|
24459
|
+
await this.saveToDisk(checkpoint);
|
|
24460
|
+
await this.pruneCheckpoints();
|
|
24086
24461
|
return checkpoint;
|
|
24087
24462
|
}
|
|
24088
24463
|
async restoreCheckpoint(checkpointId) {
|
|
24089
|
-
|
|
24464
|
+
let checkpoint = this.checkpoints.get(checkpointId);
|
|
24465
|
+
if (!checkpoint) {
|
|
24466
|
+
await this.loadFromDisk();
|
|
24467
|
+
checkpoint = this.checkpoints.get(checkpointId);
|
|
24468
|
+
}
|
|
24090
24469
|
if (!checkpoint) return null;
|
|
24091
24470
|
const graph = await this.store.load(checkpoint.graphId);
|
|
24092
24471
|
if (!graph) return null;
|
|
@@ -24110,20 +24489,357 @@ var CheckpointManager = class {
|
|
|
24110
24489
|
const filtered = graphId ? all.filter((c) => c.graphId === graphId) : all;
|
|
24111
24490
|
return filtered.sort((a, b) => b.timestamp - a.timestamp);
|
|
24112
24491
|
}
|
|
24113
|
-
deleteCheckpoint(checkpointId) {
|
|
24114
|
-
|
|
24492
|
+
async deleteCheckpoint(checkpointId) {
|
|
24493
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
24494
|
+
if (!checkpoint) return false;
|
|
24495
|
+
this.checkpoints.delete(checkpointId);
|
|
24496
|
+
await this.deleteFromDisk(checkpointId);
|
|
24497
|
+
return true;
|
|
24498
|
+
}
|
|
24499
|
+
async saveToDisk(checkpoint) {
|
|
24500
|
+
await fsp2.mkdir(this.baseDir, { recursive: true });
|
|
24501
|
+
const filePath = path6.join(this.baseDir, `${checkpoint.graphId}.json`);
|
|
24502
|
+
const serialized = {
|
|
24503
|
+
...checkpoint
|
|
24504
|
+
};
|
|
24505
|
+
let existing = [];
|
|
24506
|
+
try {
|
|
24507
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24508
|
+
const parsed = JSON.parse(raw);
|
|
24509
|
+
if (Array.isArray(parsed)) {
|
|
24510
|
+
existing = parsed;
|
|
24511
|
+
}
|
|
24512
|
+
} catch {
|
|
24513
|
+
}
|
|
24514
|
+
existing.push(serialized);
|
|
24515
|
+
await fsp2.writeFile(filePath, JSON.stringify(existing, null, 2), "utf8");
|
|
24115
24516
|
}
|
|
24116
|
-
|
|
24517
|
+
async deleteFromDisk(checkpointId) {
|
|
24518
|
+
let entries;
|
|
24519
|
+
try {
|
|
24520
|
+
entries = await fsp2.readdir(this.baseDir);
|
|
24521
|
+
} catch {
|
|
24522
|
+
return;
|
|
24523
|
+
}
|
|
24524
|
+
for (const filename of entries) {
|
|
24525
|
+
if (!filename.endsWith(".json")) continue;
|
|
24526
|
+
const filePath = path6.join(this.baseDir, filename);
|
|
24527
|
+
try {
|
|
24528
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24529
|
+
const parsed = JSON.parse(raw);
|
|
24530
|
+
if (!Array.isArray(parsed)) continue;
|
|
24531
|
+
const existing = parsed;
|
|
24532
|
+
const filtered = existing.filter((c) => c.id !== checkpointId);
|
|
24533
|
+
if (filtered.length !== existing.length) {
|
|
24534
|
+
if (filtered.length === 0) {
|
|
24535
|
+
await fsp2.unlink(filePath);
|
|
24536
|
+
} else {
|
|
24537
|
+
await fsp2.writeFile(filePath, JSON.stringify(filtered, null, 2), "utf8");
|
|
24538
|
+
}
|
|
24539
|
+
}
|
|
24540
|
+
} catch {
|
|
24541
|
+
}
|
|
24542
|
+
}
|
|
24543
|
+
}
|
|
24544
|
+
async loadFromDisk() {
|
|
24545
|
+
let entries;
|
|
24546
|
+
try {
|
|
24547
|
+
entries = await fsp2.readdir(this.baseDir);
|
|
24548
|
+
} catch {
|
|
24549
|
+
return;
|
|
24550
|
+
}
|
|
24551
|
+
for (const filename of entries) {
|
|
24552
|
+
if (!filename.endsWith(".json")) continue;
|
|
24553
|
+
const filePath = path6.join(this.baseDir, filename);
|
|
24554
|
+
try {
|
|
24555
|
+
const raw = await fsp2.readFile(filePath, "utf8");
|
|
24556
|
+
const parsed = JSON.parse(raw);
|
|
24557
|
+
if (!Array.isArray(parsed)) continue;
|
|
24558
|
+
const checkpoints = parsed;
|
|
24559
|
+
for (const sc of checkpoints) {
|
|
24560
|
+
const checkpoint = {
|
|
24561
|
+
id: sc.id,
|
|
24562
|
+
graphId: sc.graphId,
|
|
24563
|
+
phaseId: sc.phaseId,
|
|
24564
|
+
phaseStatus: sc.phaseStatus,
|
|
24565
|
+
taskStatuses: sc.taskStatuses,
|
|
24566
|
+
timestamp: sc.timestamp,
|
|
24567
|
+
label: sc.label
|
|
24568
|
+
};
|
|
24569
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
24570
|
+
}
|
|
24571
|
+
} catch {
|
|
24572
|
+
}
|
|
24573
|
+
}
|
|
24574
|
+
}
|
|
24575
|
+
async pruneCheckpoints() {
|
|
24117
24576
|
const all = Array.from(this.checkpoints.values()).sort(
|
|
24118
24577
|
(a, b) => a.timestamp - b.timestamp
|
|
24119
24578
|
);
|
|
24120
24579
|
while (all.length > this.maxCheckpoints) {
|
|
24121
24580
|
const oldest = all.shift();
|
|
24122
|
-
if (oldest)
|
|
24581
|
+
if (oldest) {
|
|
24582
|
+
this.checkpoints.delete(oldest.id);
|
|
24583
|
+
await this.deleteFromDisk(oldest.id);
|
|
24584
|
+
}
|
|
24123
24585
|
}
|
|
24124
24586
|
}
|
|
24125
24587
|
};
|
|
24588
|
+
var MAX_SLUG = 40;
|
|
24589
|
+
var WorktreeManager = class {
|
|
24590
|
+
projectRoot;
|
|
24591
|
+
events;
|
|
24592
|
+
gitBin;
|
|
24593
|
+
runGit;
|
|
24594
|
+
/** Keyed by ownerId. */
|
|
24595
|
+
handles = /* @__PURE__ */ new Map();
|
|
24596
|
+
usedSlugs = /* @__PURE__ */ new Set();
|
|
24597
|
+
constructor(opts) {
|
|
24598
|
+
this.projectRoot = resolve(opts.projectRoot);
|
|
24599
|
+
this.events = opts.events;
|
|
24600
|
+
this.gitBin = opts.gitBin ?? "git";
|
|
24601
|
+
this.runGit = opts.run ?? ((args, cwd) => this.defaultRun(args, cwd));
|
|
24602
|
+
}
|
|
24603
|
+
/** Create a fresh worktree + branch forked from the current base branch. */
|
|
24604
|
+
async allocate(ownerId, opts = {}) {
|
|
24605
|
+
const existing = this.handles.get(ownerId);
|
|
24606
|
+
if (existing && (existing.status === "allocating" || existing.status === "active")) {
|
|
24607
|
+
return existing;
|
|
24608
|
+
}
|
|
24609
|
+
const slug = this.makeSlug(opts.slugHint ?? ownerId);
|
|
24610
|
+
const branch = `wstack/ap/${slug}`;
|
|
24611
|
+
const dir = join(this.worktreesRoot(), slug);
|
|
24612
|
+
assertSafePath(dir, this.projectRoot);
|
|
24613
|
+
const baseBranch = opts.baseBranch ?? await this.detectBaseBranch();
|
|
24614
|
+
const handle = {
|
|
24615
|
+
id: slug,
|
|
24616
|
+
ownerId,
|
|
24617
|
+
ownerLabel: opts.ownerLabel ?? opts.slugHint ?? ownerId,
|
|
24618
|
+
slug,
|
|
24619
|
+
dir,
|
|
24620
|
+
branch,
|
|
24621
|
+
baseBranch,
|
|
24622
|
+
status: "allocating",
|
|
24623
|
+
createdAt: Date.now(),
|
|
24624
|
+
updatedAt: Date.now(),
|
|
24625
|
+
insertions: 0,
|
|
24626
|
+
deletions: 0,
|
|
24627
|
+
files: 0
|
|
24628
|
+
};
|
|
24629
|
+
this.handles.set(ownerId, handle);
|
|
24630
|
+
try {
|
|
24631
|
+
await mkdir(this.worktreesRoot(), { recursive: true });
|
|
24632
|
+
const res = await this.runGit(
|
|
24633
|
+
["worktree", "add", "-b", branch, dir, baseBranch],
|
|
24634
|
+
this.projectRoot
|
|
24635
|
+
);
|
|
24636
|
+
if (res.code !== 0) {
|
|
24637
|
+
return this.fail(handle, res.stderr || "git worktree add failed");
|
|
24638
|
+
}
|
|
24639
|
+
} catch (err) {
|
|
24640
|
+
return this.fail(handle, err instanceof Error ? err.message : String(err));
|
|
24641
|
+
}
|
|
24642
|
+
this.setStatus(handle, "active");
|
|
24643
|
+
this.emit("worktree.allocated", {
|
|
24644
|
+
handleId: handle.id,
|
|
24645
|
+
ownerId: handle.ownerId,
|
|
24646
|
+
ownerLabel: handle.ownerLabel,
|
|
24647
|
+
slug: handle.slug,
|
|
24648
|
+
dir: handle.dir,
|
|
24649
|
+
branch: handle.branch,
|
|
24650
|
+
baseBranch: handle.baseBranch
|
|
24651
|
+
});
|
|
24652
|
+
return handle;
|
|
24653
|
+
}
|
|
24654
|
+
/** Stage everything and commit inside the worktree. */
|
|
24655
|
+
async commitAll(handle, message) {
|
|
24656
|
+
this.setStatus(handle, "committing");
|
|
24657
|
+
await this.runGit(["add", "-A"], handle.dir);
|
|
24658
|
+
const staged = await this.runGit(["diff", "--cached", "--quiet"], handle.dir);
|
|
24659
|
+
if (staged.code === 0) {
|
|
24660
|
+
this.emitCommitted(handle, false);
|
|
24661
|
+
return { committed: false };
|
|
24662
|
+
}
|
|
24663
|
+
const committed = await this.runGit(["commit", "-m", message], handle.dir);
|
|
24664
|
+
if (committed.code !== 0) {
|
|
24665
|
+
this.fail(handle, committed.stderr || "git commit failed");
|
|
24666
|
+
return { committed: false };
|
|
24667
|
+
}
|
|
24668
|
+
const stats = await this.collectStats(handle.dir);
|
|
24669
|
+
handle.insertions = stats.insertions;
|
|
24670
|
+
handle.deletions = stats.deletions;
|
|
24671
|
+
handle.files = stats.files;
|
|
24672
|
+
handle.sha = stats.sha;
|
|
24673
|
+
handle.updatedAt = Date.now();
|
|
24674
|
+
this.emitCommitted(handle, true);
|
|
24675
|
+
return { committed: true };
|
|
24676
|
+
}
|
|
24677
|
+
/** Merge the worktree branch back into the base branch (squash by default). */
|
|
24678
|
+
async merge(handle, opts = {}) {
|
|
24679
|
+
const squash = opts.squash ?? true;
|
|
24680
|
+
this.setStatus(handle, "merging");
|
|
24681
|
+
const checkout = await this.runGit(["checkout", handle.baseBranch], this.projectRoot);
|
|
24682
|
+
if (checkout.code !== 0) {
|
|
24683
|
+
this.fail(handle, checkout.stderr || `checkout ${handle.baseBranch} failed`);
|
|
24684
|
+
return { ok: false, stderr: checkout.stderr };
|
|
24685
|
+
}
|
|
24686
|
+
const mergeArgs = squash ? ["merge", "--squash", handle.branch] : ["merge", "--no-ff", handle.branch];
|
|
24687
|
+
const merged = await this.runGit(mergeArgs, this.projectRoot);
|
|
24688
|
+
if (merged.code !== 0) {
|
|
24689
|
+
const conflictFiles = await this.unmergedFiles();
|
|
24690
|
+
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot);
|
|
24691
|
+
handle.conflictFiles = conflictFiles;
|
|
24692
|
+
this.setStatus(handle, "needs-review", { lastError: merged.stderr });
|
|
24693
|
+
this.emit("worktree.conflict", {
|
|
24694
|
+
handleId: handle.id,
|
|
24695
|
+
ownerId: handle.ownerId,
|
|
24696
|
+
branch: handle.branch,
|
|
24697
|
+
conflictFiles
|
|
24698
|
+
});
|
|
24699
|
+
return { ok: false, conflict: true, conflictFiles, stderr: merged.stderr };
|
|
24700
|
+
}
|
|
24701
|
+
if (squash) {
|
|
24702
|
+
const msg = opts.message ?? `merge ${handle.branch} (squash)`;
|
|
24703
|
+
const commit = await this.runGit(["commit", "-m", msg], this.projectRoot);
|
|
24704
|
+
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
24705
|
+
this.fail(handle, commit.stderr || "squash commit failed");
|
|
24706
|
+
return { ok: false, stderr: commit.stderr };
|
|
24707
|
+
}
|
|
24708
|
+
}
|
|
24709
|
+
this.setStatus(handle, "merged");
|
|
24710
|
+
this.emit("worktree.merged", {
|
|
24711
|
+
handleId: handle.id,
|
|
24712
|
+
ownerId: handle.ownerId,
|
|
24713
|
+
branch: handle.branch,
|
|
24714
|
+
baseBranch: handle.baseBranch,
|
|
24715
|
+
squash
|
|
24716
|
+
});
|
|
24717
|
+
return { ok: true };
|
|
24718
|
+
}
|
|
24719
|
+
/**
|
|
24720
|
+
* Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
|
|
24721
|
+
* are left on disk for inspection.
|
|
24722
|
+
*/
|
|
24723
|
+
async release(handle, opts = {}) {
|
|
24724
|
+
const keep = opts.keep || handle.status === "needs-review" || handle.status === "failed";
|
|
24725
|
+
if (!keep) {
|
|
24726
|
+
await this.runGit(["worktree", "remove", "--force", handle.dir], this.projectRoot);
|
|
24727
|
+
await this.runGit(["branch", "-D", handle.branch], this.projectRoot);
|
|
24728
|
+
await this.runGit(["worktree", "prune"], this.projectRoot);
|
|
24729
|
+
this.handles.delete(handle.ownerId);
|
|
24730
|
+
}
|
|
24731
|
+
this.emit("worktree.released", {
|
|
24732
|
+
handleId: handle.id,
|
|
24733
|
+
ownerId: handle.ownerId,
|
|
24734
|
+
branch: handle.branch,
|
|
24735
|
+
kept: keep
|
|
24736
|
+
});
|
|
24737
|
+
}
|
|
24738
|
+
get(ownerId) {
|
|
24739
|
+
return this.handles.get(ownerId);
|
|
24740
|
+
}
|
|
24741
|
+
list() {
|
|
24742
|
+
return [...this.handles.values()];
|
|
24743
|
+
}
|
|
24744
|
+
// ── internals ────────────────────────────────────────────────────────────
|
|
24745
|
+
worktreesRoot() {
|
|
24746
|
+
return join(this.projectRoot, ".wrongstack", "worktrees");
|
|
24747
|
+
}
|
|
24748
|
+
async detectBaseBranch() {
|
|
24749
|
+
const head = await this.runGit(["rev-parse", "--abbrev-ref", "HEAD"], this.projectRoot);
|
|
24750
|
+
const name = head.stdout.trim();
|
|
24751
|
+
if (name && name !== "HEAD") return name;
|
|
24752
|
+
const sha = await this.runGit(["rev-parse", "HEAD"], this.projectRoot);
|
|
24753
|
+
return sha.stdout.trim() || "HEAD";
|
|
24754
|
+
}
|
|
24755
|
+
makeSlug(hint) {
|
|
24756
|
+
let base = hint.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+/, "").replace(/[-.]+$/, "").slice(0, MAX_SLUG).replace(/[-.]+$/, "");
|
|
24757
|
+
if (!base) base = "wt";
|
|
24758
|
+
let slug = `${base}-${crypto.randomUUID().slice(0, 6)}`;
|
|
24759
|
+
while (this.usedSlugs.has(slug)) slug = `${base}-${crypto.randomUUID().slice(0, 6)}`;
|
|
24760
|
+
this.usedSlugs.add(slug);
|
|
24761
|
+
return slug;
|
|
24762
|
+
}
|
|
24763
|
+
async collectStats(dir) {
|
|
24764
|
+
const sha = (await this.runGit(["rev-parse", "HEAD"], dir)).stdout.trim();
|
|
24765
|
+
const numstat = await this.runGit(["show", "--numstat", "--format=", "HEAD"], dir);
|
|
24766
|
+
let insertions = 0;
|
|
24767
|
+
let deletions = 0;
|
|
24768
|
+
let files = 0;
|
|
24769
|
+
for (const line of numstat.stdout.split("\n")) {
|
|
24770
|
+
const m = line.trim().match(/^(\d+|-)\t(\d+|-)\t(.+)$/);
|
|
24771
|
+
if (!m) continue;
|
|
24772
|
+
files++;
|
|
24773
|
+
if (m[1] !== "-") insertions += Number(m[1]);
|
|
24774
|
+
if (m[2] !== "-") deletions += Number(m[2]);
|
|
24775
|
+
}
|
|
24776
|
+
return { insertions, deletions, files, sha };
|
|
24777
|
+
}
|
|
24778
|
+
async unmergedFiles() {
|
|
24779
|
+
const res = await this.runGit(
|
|
24780
|
+
["diff", "--name-only", "--diff-filter=U"],
|
|
24781
|
+
this.projectRoot
|
|
24782
|
+
);
|
|
24783
|
+
return res.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
24784
|
+
}
|
|
24785
|
+
emitCommitted(handle, committed) {
|
|
24786
|
+
this.emit("worktree.committed", {
|
|
24787
|
+
handleId: handle.id,
|
|
24788
|
+
ownerId: handle.ownerId,
|
|
24789
|
+
branch: handle.branch,
|
|
24790
|
+
committed,
|
|
24791
|
+
insertions: handle.insertions,
|
|
24792
|
+
deletions: handle.deletions,
|
|
24793
|
+
files: handle.files,
|
|
24794
|
+
sha: handle.sha
|
|
24795
|
+
});
|
|
24796
|
+
}
|
|
24797
|
+
fail(handle, error) {
|
|
24798
|
+
this.setStatus(handle, "failed", { lastError: error });
|
|
24799
|
+
this.emit("worktree.failed", {
|
|
24800
|
+
handleId: handle.id,
|
|
24801
|
+
ownerId: handle.ownerId,
|
|
24802
|
+
branch: handle.branch,
|
|
24803
|
+
error
|
|
24804
|
+
});
|
|
24805
|
+
return handle;
|
|
24806
|
+
}
|
|
24807
|
+
setStatus(handle, status, patch) {
|
|
24808
|
+
handle.status = status;
|
|
24809
|
+
handle.updatedAt = Date.now();
|
|
24810
|
+
if (patch) Object.assign(handle, patch);
|
|
24811
|
+
}
|
|
24812
|
+
emit(event, payload) {
|
|
24813
|
+
this.events?.emit(event, payload);
|
|
24814
|
+
}
|
|
24815
|
+
defaultRun(args, cwd) {
|
|
24816
|
+
return new Promise((res) => {
|
|
24817
|
+
let stdout = "";
|
|
24818
|
+
let stderr = "";
|
|
24819
|
+
const child = spawn(this.gitBin, args, {
|
|
24820
|
+
cwd,
|
|
24821
|
+
env: buildChildEnv(),
|
|
24822
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
24823
|
+
});
|
|
24824
|
+
child.stdout?.on("data", (c) => {
|
|
24825
|
+
stdout += c.toString();
|
|
24826
|
+
});
|
|
24827
|
+
child.stderr?.on("data", (c) => {
|
|
24828
|
+
stderr += c.toString();
|
|
24829
|
+
});
|
|
24830
|
+
child.on("error", (err) => res({ code: 1, stdout, stderr: err.message }));
|
|
24831
|
+
child.on("close", (code) => res({ code: code ?? 1, stdout, stderr }));
|
|
24832
|
+
});
|
|
24833
|
+
}
|
|
24834
|
+
};
|
|
24835
|
+
function assertSafePath(dir, projectRoot) {
|
|
24836
|
+
const root = resolve(projectRoot);
|
|
24837
|
+
const abs = resolve(dir);
|
|
24838
|
+
if (abs !== root && !abs.startsWith(root + sep)) {
|
|
24839
|
+
throw new Error(`worktree path escapes project root: ${dir}`);
|
|
24840
|
+
}
|
|
24841
|
+
}
|
|
24126
24842
|
|
|
24127
|
-
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, 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 };
|
|
24843
|
+
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, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, 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 };
|
|
24128
24844
|
//# sourceMappingURL=index.js.map
|
|
24129
24845
|
//# sourceMappingURL=index.js.map
|