pi-crew 0.9.17 → 0.9.18
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/CHANGELOG.md +49 -0
- package/dist/build-meta.json +154 -160
- package/dist/index.mjs +630 -531
- package/dist/index.mjs.map +4 -4
- package/docs/migration/atomic-write-v2-migration.md +297 -0
- package/docs/perf/performance-review-2026-07.md +287 -0
- package/package.json +1 -1
- package/src/config/config.ts +137 -1
- package/src/extension/register.ts +1 -1
- package/src/extension/team-onboard.ts +1 -3
- package/src/extension/team-tool/anchor.ts +1 -1
- package/src/extension/team-tool/auto-summarize.ts +1 -1
- package/src/runtime/async-runner.ts +12 -1
- package/src/runtime/child-pi.ts +1 -1
- package/src/runtime/coalesce-tasks.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +1 -1
- package/src/runtime/hidden-handoff.ts +1 -1
- package/src/runtime/mcp-proxy.ts +2 -2
- package/src/runtime/result-extractor.ts +1 -4
- package/src/runtime/retry-runner.ts +1 -1
- package/src/runtime/run-coalesced-task-group.ts +8 -8
- package/src/runtime/task-id.ts +1 -1
- package/src/runtime/task-runner/state-helpers.ts +22 -7
- package/src/runtime/team-runner.ts +3 -3
- package/src/runtime/verification-integrity.ts +1 -3
- package/src/state/atomic-write.ts +1 -1
- package/src/state/state-store.ts +8 -2
- package/src/ui/terminal-status.ts +1 -3
- package/src/ui/tool-renderers/index.ts +1 -1
- package/src/utils/safe-paths.ts +1 -5
package/dist/index.mjs
CHANGED
|
@@ -9058,7 +9058,7 @@ function flushOnePendingAtomicWrite(filePath) {
|
|
|
9058
9058
|
pendingAtomicWrites.delete(filePath);
|
|
9059
9059
|
throw error;
|
|
9060
9060
|
}
|
|
9061
|
-
const backoffMs = Math.min(3e4, current2.coalesceMs *
|
|
9061
|
+
const backoffMs = Math.min(3e4, current2.coalesceMs * 2 ** (current2.retryCount - 1));
|
|
9062
9062
|
const timer = setTimeout(() => flushOnePendingAtomicWrite(filePath), backoffMs);
|
|
9063
9063
|
timer.unref();
|
|
9064
9064
|
current2.timer = timer;
|
|
@@ -9701,9 +9701,14 @@ var init_suggestions = __esm({
|
|
|
9701
9701
|
// src/config/config.ts
|
|
9702
9702
|
var config_exports = {};
|
|
9703
9703
|
__export(config_exports, {
|
|
9704
|
+
__test__configCacheSize: () => __test__configCacheSize,
|
|
9705
|
+
__test__getConfigCacheEntry: () => __test__getConfigCacheEntry,
|
|
9706
|
+
__test__getConfigCacheTtlMs: () => __test__getConfigCacheTtlMs,
|
|
9707
|
+
__test__setConfigCacheTtlMs: () => __test__setConfigCacheTtlMs,
|
|
9704
9708
|
asRecord: () => asRecord,
|
|
9705
9709
|
configPath: () => configPath,
|
|
9706
9710
|
effectiveAutonomousConfig: () => effectiveAutonomousConfig,
|
|
9711
|
+
invalidateConfigCache: () => invalidateConfigCache,
|
|
9707
9712
|
legacyConfigPath: () => legacyConfigPath,
|
|
9708
9713
|
loadConfig: () => loadConfig,
|
|
9709
9714
|
parseConfig: () => parseConfig,
|
|
@@ -9716,6 +9721,70 @@ __export(config_exports, {
|
|
|
9716
9721
|
import * as fs5 from "node:fs";
|
|
9717
9722
|
import * as os3 from "node:os";
|
|
9718
9723
|
import * as path5 from "node:path";
|
|
9724
|
+
function __test__setConfigCacheTtlMs(ttlMs) {
|
|
9725
|
+
configCacheTtlMsOverride = ttlMs;
|
|
9726
|
+
}
|
|
9727
|
+
function __test__getConfigCacheTtlMs() {
|
|
9728
|
+
return configCacheTtlMsOverride ?? CONFIG_CACHE_TTL_MS;
|
|
9729
|
+
}
|
|
9730
|
+
function __test__getConfigCacheEntry(parts) {
|
|
9731
|
+
return configCache.get(buildConfigCacheKey(parts));
|
|
9732
|
+
}
|
|
9733
|
+
function __test__configCacheSize() {
|
|
9734
|
+
return configCache.size;
|
|
9735
|
+
}
|
|
9736
|
+
function buildConfigCacheKey(parts) {
|
|
9737
|
+
return JSON.stringify([parts.filePath, parts.legacyPath, parts.projectPath, parts.projectPiCrewJsonPath, parts.cwd]);
|
|
9738
|
+
}
|
|
9739
|
+
function statMtimeMs(filePath) {
|
|
9740
|
+
try {
|
|
9741
|
+
return fs5.statSync(filePath).mtimeMs;
|
|
9742
|
+
} catch (error) {
|
|
9743
|
+
if (error.code === "ENOENT") return void 0;
|
|
9744
|
+
throw error;
|
|
9745
|
+
}
|
|
9746
|
+
}
|
|
9747
|
+
function readCachedConfigParts(filePath, legacyPath, cwd) {
|
|
9748
|
+
const projectPath2 = cwd ? projectConfigPath(cwd) : "";
|
|
9749
|
+
const piCrewJsonPath = cwd ? projectPiCrewJsonPath(cwd) : "";
|
|
9750
|
+
return {
|
|
9751
|
+
filePath,
|
|
9752
|
+
legacyPath,
|
|
9753
|
+
projectPath: projectPath2,
|
|
9754
|
+
projectPiCrewJsonPath: piCrewJsonPath,
|
|
9755
|
+
cwd: cwd ?? null
|
|
9756
|
+
};
|
|
9757
|
+
}
|
|
9758
|
+
function readCacheMtimes(parts) {
|
|
9759
|
+
const mtimes = {};
|
|
9760
|
+
for (const p of [parts.filePath, parts.legacyPath, parts.projectPath, parts.projectPiCrewJsonPath]) {
|
|
9761
|
+
if (!p) continue;
|
|
9762
|
+
const m = statMtimeMs(p);
|
|
9763
|
+
if (m !== void 0) mtimes[p] = m;
|
|
9764
|
+
}
|
|
9765
|
+
return mtimes;
|
|
9766
|
+
}
|
|
9767
|
+
function matchesCachedMtimes(cached, current2) {
|
|
9768
|
+
const cachedKeys = Object.keys(cached);
|
|
9769
|
+
const currentKeys = Object.keys(current2);
|
|
9770
|
+
if (cachedKeys.length !== currentKeys.length) return false;
|
|
9771
|
+
for (const key of cachedKeys) {
|
|
9772
|
+
if (current2[key] !== cached[key]) return false;
|
|
9773
|
+
}
|
|
9774
|
+
return true;
|
|
9775
|
+
}
|
|
9776
|
+
function setConfigCache(key, value, mtimes) {
|
|
9777
|
+
if (configCache.has(key)) configCache.delete(key);
|
|
9778
|
+
configCache.set(key, { value, mtimes, cachedAt: Date.now() });
|
|
9779
|
+
const ttlMs = configCacheTtlMsOverride ?? CONFIG_CACHE_TTL_MS;
|
|
9780
|
+
const now = Date.now();
|
|
9781
|
+
for (const [k, entry] of configCache.entries()) {
|
|
9782
|
+
if (now - entry.cachedAt > ttlMs) configCache.delete(k);
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
function invalidateConfigCache() {
|
|
9786
|
+
configCache.clear();
|
|
9787
|
+
}
|
|
9719
9788
|
function resolveHomeDir() {
|
|
9720
9789
|
const envValue = process.env.PI_TEAMS_HOME?.trim();
|
|
9721
9790
|
const defaultHome = os3.homedir();
|
|
@@ -10448,6 +10517,18 @@ function readOptionalConfig(filePath) {
|
|
|
10448
10517
|
function loadConfig(cwd) {
|
|
10449
10518
|
const filePath = configPath();
|
|
10450
10519
|
const legacyPath = legacyConfigPath();
|
|
10520
|
+
const cacheParts = readCachedConfigParts(filePath, legacyPath, cwd);
|
|
10521
|
+
const cacheKey2 = buildConfigCacheKey(cacheParts);
|
|
10522
|
+
const cached = configCache.get(cacheKey2);
|
|
10523
|
+
const ttlMs = configCacheTtlMsOverride ?? CONFIG_CACHE_TTL_MS;
|
|
10524
|
+
if (cached && Date.now() - cached.cachedAt <= ttlMs) {
|
|
10525
|
+
const currentMtimes = readCacheMtimes(cacheParts);
|
|
10526
|
+
if (matchesCachedMtimes(cached.mtimes, currentMtimes)) {
|
|
10527
|
+
configCache.delete(cacheKey2);
|
|
10528
|
+
configCache.set(cacheKey2, cached);
|
|
10529
|
+
return cached.value;
|
|
10530
|
+
}
|
|
10531
|
+
}
|
|
10451
10532
|
const paths = cwd ? [filePath, projectConfigPath(cwd)] : [filePath];
|
|
10452
10533
|
const warnings = [];
|
|
10453
10534
|
const legacyConfig = readOptionalConfig(legacyPath);
|
|
@@ -10480,12 +10561,16 @@ function loadConfig(cwd) {
|
|
|
10480
10561
|
paths.push(piCrewJsonPath);
|
|
10481
10562
|
}
|
|
10482
10563
|
}
|
|
10483
|
-
|
|
10564
|
+
const result4 = {
|
|
10484
10565
|
path: filePath,
|
|
10485
10566
|
paths,
|
|
10486
10567
|
config,
|
|
10487
10568
|
warnings: warnings.length > 0 ? warnings : void 0
|
|
10488
10569
|
};
|
|
10570
|
+
if (Object.keys(readCacheMtimes(cacheParts)).length > 0) {
|
|
10571
|
+
setConfigCache(cacheKey2, result4, readCacheMtimes(cacheParts));
|
|
10572
|
+
}
|
|
10573
|
+
return result4;
|
|
10489
10574
|
}
|
|
10490
10575
|
function updateConfig(patch, options = {}) {
|
|
10491
10576
|
const filePath = options.scope === "project" && options.cwd ? projectConfigPath(options.cwd) : configPath();
|
|
@@ -10507,6 +10592,7 @@ function updateConfig(patch, options = {}) {
|
|
|
10507
10592
|
fs5.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
10508
10593
|
atomicWriteFile(filePath, `${JSON.stringify(merged, null, 2)}
|
|
10509
10594
|
`);
|
|
10595
|
+
invalidateConfigCache();
|
|
10510
10596
|
return { path: filePath, config: merged };
|
|
10511
10597
|
});
|
|
10512
10598
|
}
|
|
@@ -10525,10 +10611,11 @@ function updateAutonomousConfig(patch) {
|
|
|
10525
10611
|
current2.autonomous = { ...currentAutonomous, ...patch };
|
|
10526
10612
|
atomicWriteFile(filePath, `${JSON.stringify(current2, null, 2)}
|
|
10527
10613
|
`);
|
|
10614
|
+
invalidateConfigCache();
|
|
10528
10615
|
return { path: filePath, config: parseConfig(current2) };
|
|
10529
10616
|
});
|
|
10530
10617
|
}
|
|
10531
|
-
var KNOWN_TOP_LEVEL_KEYS, LIMIT_CEILINGS, DANGEROUS_OBJECT_KEYS;
|
|
10618
|
+
var CONFIG_CACHE_TTL_MS, configCache, configCacheTtlMsOverride, KNOWN_TOP_LEVEL_KEYS, LIMIT_CEILINGS, DANGEROUS_OBJECT_KEYS;
|
|
10532
10619
|
var init_config = __esm({
|
|
10533
10620
|
"src/config/config.ts"() {
|
|
10534
10621
|
"use strict";
|
|
@@ -10540,6 +10627,9 @@ var init_config = __esm({
|
|
|
10540
10627
|
init_internal_error();
|
|
10541
10628
|
init_paths();
|
|
10542
10629
|
init_suggestions();
|
|
10630
|
+
CONFIG_CACHE_TTL_MS = 2e3;
|
|
10631
|
+
configCache = /* @__PURE__ */ new Map();
|
|
10632
|
+
configCacheTtlMsOverride = null;
|
|
10543
10633
|
KNOWN_TOP_LEVEL_KEYS = Object.keys(PiTeamsConfigSchema.properties ?? {});
|
|
10544
10634
|
LIMIT_CEILINGS = {
|
|
10545
10635
|
maxConcurrentWorkers: 1024,
|
|
@@ -12231,7 +12321,7 @@ function resolveCanonicalPath(p) {
|
|
|
12231
12321
|
}
|
|
12232
12322
|
function resolveWindowsCanonical(p) {
|
|
12233
12323
|
try {
|
|
12234
|
-
|
|
12324
|
+
const real = fs9.realpathSync.native(p);
|
|
12235
12325
|
if (real.includes("$Extend") || real.includes("$Deleted")) throw new Error("NTFS internal path");
|
|
12236
12326
|
return real;
|
|
12237
12327
|
} catch {
|
|
@@ -12331,7 +12421,6 @@ function resolveRealContainedPath(baseDir, targetPath2) {
|
|
|
12331
12421
|
if (error.code === "EPERM" && process.platform === "win32") continue;
|
|
12332
12422
|
if (error.code !== "ENOENT") throw error;
|
|
12333
12423
|
if (i2 === resolvedParts.length - 1) continue;
|
|
12334
|
-
continue;
|
|
12335
12424
|
}
|
|
12336
12425
|
}
|
|
12337
12426
|
let targetFd;
|
|
@@ -12945,6 +13034,7 @@ __export(state_store_exports, {
|
|
|
12945
13034
|
saveRunManifestAsync: () => saveRunManifestAsync,
|
|
12946
13035
|
saveRunTasks: () => saveRunTasks,
|
|
12947
13036
|
saveRunTasksAsync: () => saveRunTasksAsync,
|
|
13037
|
+
saveRunTasksCoalesced: () => saveRunTasksCoalesced,
|
|
12948
13038
|
updateRunStatus: () => updateRunStatus
|
|
12949
13039
|
});
|
|
12950
13040
|
import * as fs12 from "node:fs";
|
|
@@ -13199,6 +13289,15 @@ function saveRunTasks(manifest, tasks) {
|
|
|
13199
13289
|
tasksSize: tasksStat.size
|
|
13200
13290
|
});
|
|
13201
13291
|
}
|
|
13292
|
+
function saveRunTasksCoalesced(manifest, tasks) {
|
|
13293
|
+
invalidateRunCache(manifest.stateRoot);
|
|
13294
|
+
try {
|
|
13295
|
+
fs12.statSync(manifest.stateRoot);
|
|
13296
|
+
} catch {
|
|
13297
|
+
return;
|
|
13298
|
+
}
|
|
13299
|
+
atomicWriteJsonCoalesced(manifest.tasksPath, tasks);
|
|
13300
|
+
}
|
|
13202
13301
|
async function saveRunTasksAsync(manifest, tasks) {
|
|
13203
13302
|
invalidateRunCache(manifest.stateRoot);
|
|
13204
13303
|
try {
|
|
@@ -18590,7 +18689,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
|
|
|
18590
18689
|
pendingOperations.clear();
|
|
18591
18690
|
};
|
|
18592
18691
|
let softLimitReached = false;
|
|
18593
|
-
|
|
18692
|
+
const steerInjectionFailed = false;
|
|
18594
18693
|
const maxTurns = input.maxTurns;
|
|
18595
18694
|
let graceTurns = input.graceTurns;
|
|
18596
18695
|
if (graceTurns !== void 0 && graceTurns > 1e3) graceTurns = 1e3;
|
|
@@ -44753,7 +44852,6 @@ function loadRunSummaries(cwd, options = {}) {
|
|
|
44753
44852
|
// tasks stored separately, not in manifest
|
|
44754
44853
|
});
|
|
44755
44854
|
} catch {
|
|
44756
|
-
continue;
|
|
44757
44855
|
}
|
|
44758
44856
|
}
|
|
44759
44857
|
return summaries;
|
|
@@ -47299,7 +47397,6 @@ function snapshotManifests(cwd) {
|
|
|
47299
47397
|
const content = fs59.readFileSync(abs);
|
|
47300
47398
|
snapshot[rel] = createHash6("sha256").update(content).digest("hex");
|
|
47301
47399
|
} catch {
|
|
47302
|
-
continue;
|
|
47303
47400
|
}
|
|
47304
47401
|
}
|
|
47305
47402
|
return snapshot;
|
|
@@ -47481,7 +47578,7 @@ function getBackgroundRunnerCommand(runnerPath, cwd, runId, loaderInput = resolv
|
|
|
47481
47578
|
};
|
|
47482
47579
|
}
|
|
47483
47580
|
async function spawnBackgroundTeamRun(manifest) {
|
|
47484
|
-
const runnerPath = path52.join(
|
|
47581
|
+
const runnerPath = path52.join(packageRoot(), "src", "runtime", "background-runner.ts");
|
|
47485
47582
|
const logPath = path52.join(manifest.stateRoot, "background.log");
|
|
47486
47583
|
fs60.mkdirSync(manifest.stateRoot, { recursive: true });
|
|
47487
47584
|
const filteredEnv = sanitizeEnvSecrets(process.env, {
|
|
@@ -47571,6 +47668,7 @@ var init_async_runner = __esm({
|
|
|
47571
47668
|
init_env_allowlist();
|
|
47572
47669
|
init_env_filter();
|
|
47573
47670
|
init_internal_error();
|
|
47671
|
+
init_paths();
|
|
47574
47672
|
init_orphan_worker_registry();
|
|
47575
47673
|
init_peer_dep();
|
|
47576
47674
|
requireFromHere = createRequire3(import.meta.url);
|
|
@@ -53140,18 +53238,6 @@ var init_branch_freshness = __esm({
|
|
|
53140
53238
|
}
|
|
53141
53239
|
});
|
|
53142
53240
|
|
|
53143
|
-
// src/runtime/team-runner-artifacts.ts
|
|
53144
|
-
function mergeArtifacts(items) {
|
|
53145
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
53146
|
-
for (const item of items) byPath.set(item.path, item);
|
|
53147
|
-
return [...byPath.values()];
|
|
53148
|
-
}
|
|
53149
|
-
var init_team_runner_artifacts = __esm({
|
|
53150
|
-
"src/runtime/team-runner-artifacts.ts"() {
|
|
53151
|
-
"use strict";
|
|
53152
|
-
}
|
|
53153
|
-
});
|
|
53154
|
-
|
|
53155
53241
|
// src/runtime/coalesce-tasks.ts
|
|
53156
53242
|
function planCoalescedGroups(readyTaskIds, tasks, workflow, enabled, maxGroupSize = DEFAULT_MAX_GROUP_SIZE, maxGroupBytes = DEFAULT_MAX_GROUP_BYTES) {
|
|
53157
53243
|
if (!enabled || readyTaskIds.length === 0) return [];
|
|
@@ -53278,490 +53364,6 @@ var init_coalesce_tasks = __esm({
|
|
|
53278
53364
|
}
|
|
53279
53365
|
});
|
|
53280
53366
|
|
|
53281
|
-
// src/runtime/task-runner/output-splitter.ts
|
|
53282
|
-
function splitCoalescedOutput(rawOutput, taskIds) {
|
|
53283
|
-
if (taskIds.length === 0) return [];
|
|
53284
|
-
if (taskIds.length === 1) {
|
|
53285
|
-
return [{ taskId: taskIds[0], text: rawOutput, strategy: "delimiter" }];
|
|
53286
|
-
}
|
|
53287
|
-
const byId = /* @__PURE__ */ new Map();
|
|
53288
|
-
const delimiterHits = /* @__PURE__ */ new Set();
|
|
53289
|
-
const delimiterRegex = /<<<TASK_RESULT:([^\s>]+)>>>([\s\S]*?)<<<END_TASK_RESULT>>>/g;
|
|
53290
|
-
let match;
|
|
53291
|
-
while ((match = delimiterRegex.exec(rawOutput)) !== null) {
|
|
53292
|
-
const id = match[1];
|
|
53293
|
-
const body = match[2].trim();
|
|
53294
|
-
if (taskIds.includes(id) && !byId.has(id)) {
|
|
53295
|
-
byId.set(id, body);
|
|
53296
|
-
delimiterHits.add(id);
|
|
53297
|
-
}
|
|
53298
|
-
}
|
|
53299
|
-
if (delimiterHits.size === taskIds.length) {
|
|
53300
|
-
return taskIds.map((id) => ({
|
|
53301
|
-
taskId: id,
|
|
53302
|
-
text: byId.get(id) ?? "",
|
|
53303
|
-
strategy: "delimiter"
|
|
53304
|
-
}));
|
|
53305
|
-
}
|
|
53306
|
-
if (delimiterHits.size === 0) {
|
|
53307
|
-
const bySection = parseBySectionHeadings(rawOutput, taskIds);
|
|
53308
|
-
if (bySection.size === taskIds.length) {
|
|
53309
|
-
return taskIds.map((id) => ({
|
|
53310
|
-
taskId: id,
|
|
53311
|
-
text: bySection.get(id) ?? "",
|
|
53312
|
-
strategy: "section"
|
|
53313
|
-
}));
|
|
53314
|
-
}
|
|
53315
|
-
}
|
|
53316
|
-
return taskIds.map((id) => ({
|
|
53317
|
-
taskId: id,
|
|
53318
|
-
text: rawOutput,
|
|
53319
|
-
strategy: "broadcast"
|
|
53320
|
-
}));
|
|
53321
|
-
}
|
|
53322
|
-
function parseBySectionHeadings(rawOutput, taskIds) {
|
|
53323
|
-
const result4 = /* @__PURE__ */ new Map();
|
|
53324
|
-
const numberedRegex = /^(#{2,3})\s+Task\s+(\d+)\s+of\s+\d+.*$/gm;
|
|
53325
|
-
const sections = [];
|
|
53326
|
-
let m;
|
|
53327
|
-
while ((m = numberedRegex.exec(rawOutput)) !== null) {
|
|
53328
|
-
sections.push({ num: Number(m[2]), start: m.index + m[0].length });
|
|
53329
|
-
}
|
|
53330
|
-
if (sections.length >= taskIds.length) {
|
|
53331
|
-
sections.sort((a, b) => a.start - b.start);
|
|
53332
|
-
for (let i2 = 0; i2 < sections.length && i2 < taskIds.length; i2 += 1) {
|
|
53333
|
-
const section2 = sections[i2];
|
|
53334
|
-
const end = sections[i2 + 1]?.start ?? rawOutput.length;
|
|
53335
|
-
const text = rawOutput.slice(section2.start, end).trim();
|
|
53336
|
-
const taskId = taskIds[i2];
|
|
53337
|
-
if (taskId) result4.set(taskId, text);
|
|
53338
|
-
}
|
|
53339
|
-
return result4;
|
|
53340
|
-
}
|
|
53341
|
-
const idHeaderRegex = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
53342
|
-
while ((m = idHeaderRegex.exec(rawOutput)) !== null) {
|
|
53343
|
-
const headerText = m[2].trim();
|
|
53344
|
-
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
53345
|
-
if (matchedId && !result4.has(matchedId)) {
|
|
53346
|
-
result4.set(matchedId, "");
|
|
53347
|
-
}
|
|
53348
|
-
}
|
|
53349
|
-
if (result4.size === taskIds.length) {
|
|
53350
|
-
const headerPositions = [];
|
|
53351
|
-
const idHeaderRegex2 = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
53352
|
-
while ((m = idHeaderRegex2.exec(rawOutput)) !== null) {
|
|
53353
|
-
const headerText = m[2].trim();
|
|
53354
|
-
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
53355
|
-
if (matchedId) headerPositions.push({ taskId: matchedId, start: m.index + m[0].length });
|
|
53356
|
-
}
|
|
53357
|
-
headerPositions.sort((a, b) => a.start - b.start);
|
|
53358
|
-
for (let i2 = 0; i2 < headerPositions.length; i2 += 1) {
|
|
53359
|
-
const cur = headerPositions[i2];
|
|
53360
|
-
const next = headerPositions[i2 + 1];
|
|
53361
|
-
const end = next ? rawOutput.lastIndexOf("\n#", next.start) : rawOutput.length;
|
|
53362
|
-
const text = rawOutput.slice(cur.start, end > 0 ? end : rawOutput.length).trim();
|
|
53363
|
-
result4.set(cur.taskId, text);
|
|
53364
|
-
}
|
|
53365
|
-
}
|
|
53366
|
-
return result4;
|
|
53367
|
-
}
|
|
53368
|
-
var init_output_splitter = __esm({
|
|
53369
|
-
"src/runtime/task-runner/output-splitter.ts"() {
|
|
53370
|
-
"use strict";
|
|
53371
|
-
}
|
|
53372
|
-
});
|
|
53373
|
-
|
|
53374
|
-
// src/runtime/workspace-tree.ts
|
|
53375
|
-
import * as fs73 from "node:fs/promises";
|
|
53376
|
-
import * as path61 from "node:path";
|
|
53377
|
-
function formatBytes2(bytes) {
|
|
53378
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
53379
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
53380
|
-
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
53381
|
-
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
53382
|
-
}
|
|
53383
|
-
function formatAge(seconds2) {
|
|
53384
|
-
if (seconds2 < 60) return "just now";
|
|
53385
|
-
if (seconds2 < 3600) return `${Math.floor(seconds2 / 60)}m`;
|
|
53386
|
-
if (seconds2 < 86400) return `${Math.floor(seconds2 / 3600)}h`;
|
|
53387
|
-
if (seconds2 < 86400 * 14) return `${Math.floor(seconds2 / 86400)}d`;
|
|
53388
|
-
return `${Math.floor(seconds2 / (86400 * 7))}w`;
|
|
53389
|
-
}
|
|
53390
|
-
function compareByRecency(a, b) {
|
|
53391
|
-
const diff = b.mtimeMs - a.mtimeMs;
|
|
53392
|
-
if (diff !== 0) return diff;
|
|
53393
|
-
return a.name.localeCompare(b.name);
|
|
53394
|
-
}
|
|
53395
|
-
function applyDirLimit(children, limit) {
|
|
53396
|
-
if (children.length <= limit) {
|
|
53397
|
-
return { visible: children, dropped: 0 };
|
|
53398
|
-
}
|
|
53399
|
-
if (limit <= 1) {
|
|
53400
|
-
return {
|
|
53401
|
-
visible: children.slice(0, limit),
|
|
53402
|
-
dropped: children.length - limit
|
|
53403
|
-
};
|
|
53404
|
-
}
|
|
53405
|
-
const recent = children.slice(0, limit - 1);
|
|
53406
|
-
const oldest = children[children.length - 1];
|
|
53407
|
-
const visible = oldest ? [...recent, oldest] : recent;
|
|
53408
|
-
return { visible, dropped: children.length - limit };
|
|
53409
|
-
}
|
|
53410
|
-
async function readChildren(rootPath, parent, excludedDirs) {
|
|
53411
|
-
const dirPath = parent.relativePath ? path61.join(rootPath, parent.relativePath) : rootPath;
|
|
53412
|
-
let names;
|
|
53413
|
-
try {
|
|
53414
|
-
names = await fs73.readdir(dirPath);
|
|
53415
|
-
} catch {
|
|
53416
|
-
return [];
|
|
53417
|
-
}
|
|
53418
|
-
const nodes = await Promise.all(
|
|
53419
|
-
names.map(async (name) => {
|
|
53420
|
-
if (name.startsWith(".")) return null;
|
|
53421
|
-
const relativePath = parent.relativePath ? `${parent.relativePath}/${name}` : name;
|
|
53422
|
-
const absolutePath = path61.join(rootPath, relativePath);
|
|
53423
|
-
try {
|
|
53424
|
-
const stat2 = await fs73.stat(absolutePath);
|
|
53425
|
-
if (stat2.isDirectory() && excludedDirs.has(name)) return null;
|
|
53426
|
-
return {
|
|
53427
|
-
name,
|
|
53428
|
-
relativePath,
|
|
53429
|
-
depth: parent.depth + 1,
|
|
53430
|
-
isDirectory: stat2.isDirectory(),
|
|
53431
|
-
mtimeMs: stat2.mtimeMs,
|
|
53432
|
-
size: stat2.size,
|
|
53433
|
-
children: [],
|
|
53434
|
-
droppedChildCount: 0
|
|
53435
|
-
};
|
|
53436
|
-
} catch {
|
|
53437
|
-
return null;
|
|
53438
|
-
}
|
|
53439
|
-
})
|
|
53440
|
-
);
|
|
53441
|
-
return nodes.filter((n) => n !== null).sort(compareByRecency);
|
|
53442
|
-
}
|
|
53443
|
-
async function collectTree(rootPath, maxDepth, dirLimit, excludedDirs) {
|
|
53444
|
-
const rootStat = await fs73.stat(rootPath);
|
|
53445
|
-
const root = {
|
|
53446
|
-
name: ".",
|
|
53447
|
-
relativePath: "",
|
|
53448
|
-
depth: 0,
|
|
53449
|
-
isDirectory: true,
|
|
53450
|
-
mtimeMs: rootStat.mtimeMs,
|
|
53451
|
-
size: rootStat.size,
|
|
53452
|
-
children: [],
|
|
53453
|
-
droppedChildCount: 0
|
|
53454
|
-
};
|
|
53455
|
-
let truncated = false;
|
|
53456
|
-
const queue = [root];
|
|
53457
|
-
let cursor = 0;
|
|
53458
|
-
while (cursor < queue.length) {
|
|
53459
|
-
const parent = queue[cursor];
|
|
53460
|
-
cursor += 1;
|
|
53461
|
-
if (!parent || parent.depth >= maxDepth) continue;
|
|
53462
|
-
const children = await readChildren(rootPath, parent, excludedDirs);
|
|
53463
|
-
const { visible, dropped } = applyDirLimit(children, dirLimit);
|
|
53464
|
-
parent.children = visible;
|
|
53465
|
-
parent.droppedChildCount = dropped;
|
|
53466
|
-
if (dropped > 0) truncated = true;
|
|
53467
|
-
for (const child of visible) {
|
|
53468
|
-
if (child.isDirectory) queue.push(child);
|
|
53469
|
-
}
|
|
53470
|
-
}
|
|
53471
|
-
return { root, truncated };
|
|
53472
|
-
}
|
|
53473
|
-
function collectLines(node, nowMs3, lines) {
|
|
53474
|
-
if (node.depth === 0) {
|
|
53475
|
-
lines.push({ text: ".", depth: 0, isRoot: true });
|
|
53476
|
-
} else {
|
|
53477
|
-
const indent = " ".repeat(node.depth);
|
|
53478
|
-
const suffix = node.isDirectory ? "/" : "";
|
|
53479
|
-
const label = `${indent}- ${node.name}${suffix}`;
|
|
53480
|
-
if (node.isDirectory) {
|
|
53481
|
-
lines.push({ text: label, depth: node.depth, isRoot: false });
|
|
53482
|
-
} else {
|
|
53483
|
-
const ageSeconds = Math.max(0, Math.floor((nowMs3 - node.mtimeMs) / 1e3));
|
|
53484
|
-
const size = formatBytes2(node.size);
|
|
53485
|
-
const age = formatAge(ageSeconds);
|
|
53486
|
-
lines.push({
|
|
53487
|
-
text: `${label} ${size} ${age}`,
|
|
53488
|
-
depth: node.depth,
|
|
53489
|
-
isRoot: false
|
|
53490
|
-
});
|
|
53491
|
-
}
|
|
53492
|
-
}
|
|
53493
|
-
if (node.droppedChildCount > 0) {
|
|
53494
|
-
const recentChildren = node.children.slice(0, -1);
|
|
53495
|
-
const oldestChild = node.children[node.children.length - 1];
|
|
53496
|
-
for (const child of recentChildren) collectLines(child, nowMs3, lines);
|
|
53497
|
-
const childDepth = node.depth + 1;
|
|
53498
|
-
const indent = " ".repeat(childDepth);
|
|
53499
|
-
lines.push({
|
|
53500
|
-
text: `${indent}- \u2026 ${node.droppedChildCount} more`,
|
|
53501
|
-
depth: childDepth,
|
|
53502
|
-
isRoot: false
|
|
53503
|
-
});
|
|
53504
|
-
if (oldestChild) collectLines(oldestChild, nowMs3, lines);
|
|
53505
|
-
} else {
|
|
53506
|
-
for (const child of node.children) collectLines(child, nowMs3, lines);
|
|
53507
|
-
}
|
|
53508
|
-
}
|
|
53509
|
-
function applyLineCap(lines, cap) {
|
|
53510
|
-
if (lines.length <= cap) return { lines, elided: 0 };
|
|
53511
|
-
const target = Math.max(1, cap - 1);
|
|
53512
|
-
const removeCount = lines.length - target;
|
|
53513
|
-
const removable = lines.map((line4, index) => ({ line: line4, index })).filter((item) => !item.line.isRoot).sort((a, b) => b.line.depth - a.line.depth || b.index - a.index).slice(0, removeCount);
|
|
53514
|
-
if (removable.length === 0) return { lines, elided: 0 };
|
|
53515
|
-
const removedIndexes = new Set(removable.map((item) => item.index));
|
|
53516
|
-
const kept = lines.filter((_, index) => !removedIndexes.has(index));
|
|
53517
|
-
kept.push({
|
|
53518
|
-
text: `\u2026 (${removable.length} lines elided)`,
|
|
53519
|
-
depth: 0,
|
|
53520
|
-
isRoot: false
|
|
53521
|
-
});
|
|
53522
|
-
return { lines: kept, elided: removable.length };
|
|
53523
|
-
}
|
|
53524
|
-
function treeCacheKey(cwd, options) {
|
|
53525
|
-
return `${path61.resolve(cwd)}|${options?.maxDepth ?? ""}|${options?.dirLimit ?? ""}|${options?.lineCap ?? ""}`;
|
|
53526
|
-
}
|
|
53527
|
-
async function buildWorkspaceTree(cwd, options) {
|
|
53528
|
-
const rootPath = path61.resolve(cwd);
|
|
53529
|
-
const cacheKey2 = treeCacheKey(cwd, options);
|
|
53530
|
-
const cached = treeCache.get(cacheKey2);
|
|
53531
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
53532
|
-
return cached.tree;
|
|
53533
|
-
}
|
|
53534
|
-
try {
|
|
53535
|
-
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
53536
|
-
const dirLimit = options?.dirLimit ?? DEFAULT_DIR_LIMIT;
|
|
53537
|
-
const lineCap = options?.lineCap ?? DEFAULT_LINE_CAP;
|
|
53538
|
-
const excludedDirs = options?.excludedDirs ?? DEFAULT_EXCLUDED_DIRS;
|
|
53539
|
-
const { root, truncated: dirTruncated } = await collectTree(rootPath, maxDepth, dirLimit, excludedDirs);
|
|
53540
|
-
const nowMs3 = Date.now();
|
|
53541
|
-
const lines = [];
|
|
53542
|
-
collectLines(root, nowMs3, lines);
|
|
53543
|
-
const { lines: capped, elided } = applyLineCap(lines, lineCap);
|
|
53544
|
-
const rendered = capped.map((l) => l.text).join("\n");
|
|
53545
|
-
const result4 = {
|
|
53546
|
-
rootPath,
|
|
53547
|
-
rendered,
|
|
53548
|
-
truncated: dirTruncated || elided > 0,
|
|
53549
|
-
totalLines: capped.length
|
|
53550
|
-
};
|
|
53551
|
-
treeCache.set(cacheKey2, {
|
|
53552
|
-
tree: result4,
|
|
53553
|
-
expiresAt: Date.now() + TREE_CACHE_TTL_MS
|
|
53554
|
-
});
|
|
53555
|
-
return result4;
|
|
53556
|
-
} catch {
|
|
53557
|
-
return emptyResult(rootPath);
|
|
53558
|
-
}
|
|
53559
|
-
}
|
|
53560
|
-
var DEFAULT_MAX_DEPTH, DEFAULT_DIR_LIMIT, DEFAULT_LINE_CAP, DEFAULT_EXCLUDED_DIRS, emptyResult, TREE_CACHE_TTL_MS, treeCache;
|
|
53561
|
-
var init_workspace_tree = __esm({
|
|
53562
|
-
"src/runtime/workspace-tree.ts"() {
|
|
53563
|
-
"use strict";
|
|
53564
|
-
DEFAULT_MAX_DEPTH = 3;
|
|
53565
|
-
DEFAULT_DIR_LIMIT = 12;
|
|
53566
|
-
DEFAULT_LINE_CAP = 120;
|
|
53567
|
-
DEFAULT_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
53568
|
-
"node_modules",
|
|
53569
|
-
".git",
|
|
53570
|
-
".next",
|
|
53571
|
-
"dist",
|
|
53572
|
-
"build",
|
|
53573
|
-
"target",
|
|
53574
|
-
".venv",
|
|
53575
|
-
".cache",
|
|
53576
|
-
".turbo"
|
|
53577
|
-
]);
|
|
53578
|
-
emptyResult = (rootPath) => ({
|
|
53579
|
-
rootPath,
|
|
53580
|
-
rendered: "",
|
|
53581
|
-
truncated: false,
|
|
53582
|
-
totalLines: 0
|
|
53583
|
-
});
|
|
53584
|
-
TREE_CACHE_TTL_MS = 3e4;
|
|
53585
|
-
treeCache = /* @__PURE__ */ new Map();
|
|
53586
|
-
}
|
|
53587
|
-
});
|
|
53588
|
-
|
|
53589
|
-
// src/runtime/run-coalesced-task-group.ts
|
|
53590
|
-
async function runCoalescedTaskGroup(input) {
|
|
53591
|
-
const { manifest, groupTasks, step, agent, signal, executeWorkers } = input;
|
|
53592
|
-
const groupId = groupTasks.map((t2) => t2.id).join("+");
|
|
53593
|
-
const firstTask = groupTasks[0];
|
|
53594
|
-
const taskIds = groupTasks.map((t2) => t2.id);
|
|
53595
|
-
let updatedTasks = input.tasks.map((t2) => {
|
|
53596
|
-
if (taskIds.includes(t2.id) && t2.status !== "running") {
|
|
53597
|
-
return { ...t2, status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
53598
|
-
}
|
|
53599
|
-
return t2;
|
|
53600
|
-
});
|
|
53601
|
-
saveRunTasks(manifest, updatedTasks);
|
|
53602
|
-
await appendEventAsync(manifest.eventsPath, {
|
|
53603
|
-
type: "task.coalesced_dispatch_start",
|
|
53604
|
-
runId: manifest.runId,
|
|
53605
|
-
message: `Dispatching ${groupTasks.length} coalesced tasks in 1 worker (role=${firstTask.role}, cwd=${firstTask.cwd})`,
|
|
53606
|
-
data: { groupId, role: firstTask.role, cwd: firstTask.cwd, taskIds }
|
|
53607
|
-
});
|
|
53608
|
-
const combinedPrompt = await buildCoalescedPrompt(manifest, step, groupTasks, agent);
|
|
53609
|
-
updatedTasks = updatedTasks.map((t2) => {
|
|
53610
|
-
if (!taskIds.includes(t2.id)) return t2;
|
|
53611
|
-
return {
|
|
53612
|
-
...t2,
|
|
53613
|
-
heartbeat: t2.heartbeat ?? createWorkerHeartbeat(t2.id)
|
|
53614
|
-
};
|
|
53615
|
-
});
|
|
53616
|
-
saveRunTasks(manifest, updatedTasks);
|
|
53617
|
-
let rawOutput = "";
|
|
53618
|
-
let success = false;
|
|
53619
|
-
if (!executeWorkers) {
|
|
53620
|
-
rawOutput = buildScaffoldOutput(groupTasks);
|
|
53621
|
-
success = true;
|
|
53622
|
-
} else {
|
|
53623
|
-
const heartbeatTimer = setInterval(() => {
|
|
53624
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
53625
|
-
updatedTasks = updatedTasks.map((t2) => {
|
|
53626
|
-
if (!taskIds.includes(t2.id)) return t2;
|
|
53627
|
-
return {
|
|
53628
|
-
...t2,
|
|
53629
|
-
heartbeat: touchWorkerHeartbeat(t2.heartbeat ?? createWorkerHeartbeat(t2.id), { alive: true })
|
|
53630
|
-
};
|
|
53631
|
-
});
|
|
53632
|
-
try {
|
|
53633
|
-
saveRunTasks(manifest, updatedTasks);
|
|
53634
|
-
} catch {
|
|
53635
|
-
}
|
|
53636
|
-
}, 15e3);
|
|
53637
|
-
try {
|
|
53638
|
-
const result4 = await runChildPi({
|
|
53639
|
-
cwd: firstTask.cwd,
|
|
53640
|
-
task: combinedPrompt,
|
|
53641
|
-
agent,
|
|
53642
|
-
signal,
|
|
53643
|
-
excludeContextBash: true,
|
|
53644
|
-
maxTurns: 5,
|
|
53645
|
-
onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e)
|
|
53646
|
-
});
|
|
53647
|
-
rawOutput = result4.rawFinalText ?? result4.stdout ?? "";
|
|
53648
|
-
success = result4.exitStatus?.exitCode === 0;
|
|
53649
|
-
} catch (err2) {
|
|
53650
|
-
rawOutput = `Worker dispatch failed: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
53651
|
-
success = false;
|
|
53652
|
-
} finally {
|
|
53653
|
-
clearInterval(heartbeatTimer);
|
|
53654
|
-
}
|
|
53655
|
-
}
|
|
53656
|
-
const split = splitCoalescedOutput(rawOutput, taskIds);
|
|
53657
|
-
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
53658
|
-
const newArtifacts = [];
|
|
53659
|
-
updatedTasks = updatedTasks.map((t2) => {
|
|
53660
|
-
if (!taskIds.includes(t2.id)) return t2;
|
|
53661
|
-
const entry = split.find((s) => s.taskId === t2.id);
|
|
53662
|
-
const ok = success && Boolean(entry?.text);
|
|
53663
|
-
const text = entry?.text ?? rawOutput;
|
|
53664
|
-
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
53665
|
-
kind: "result",
|
|
53666
|
-
relativePath: `results/${t2.id}.txt`,
|
|
53667
|
-
content: text,
|
|
53668
|
-
producer: t2.id
|
|
53669
|
-
});
|
|
53670
|
-
newArtifacts.push(resultArtifact);
|
|
53671
|
-
return {
|
|
53672
|
-
...t2,
|
|
53673
|
-
status: ok ? "completed" : "failed",
|
|
53674
|
-
finishedAt,
|
|
53675
|
-
result: {
|
|
53676
|
-
text,
|
|
53677
|
-
producer: groupId,
|
|
53678
|
-
strategy: entry?.strategy ?? "broadcast"
|
|
53679
|
-
},
|
|
53680
|
-
resultArtifact
|
|
53681
|
-
};
|
|
53682
|
-
});
|
|
53683
|
-
saveRunTasks(manifest, updatedTasks);
|
|
53684
|
-
let updatedManifest = {
|
|
53685
|
-
...manifest,
|
|
53686
|
-
artifacts: mergeArtifacts([...manifest.artifacts, ...newArtifacts])
|
|
53687
|
-
};
|
|
53688
|
-
if (success) {
|
|
53689
|
-
updatedManifest = updateRunStatus(updatedManifest, "running");
|
|
53690
|
-
}
|
|
53691
|
-
await appendEventAsync(updatedManifest.eventsPath, {
|
|
53692
|
-
type: "task.coalesced_dispatch_end",
|
|
53693
|
-
runId: manifest.runId,
|
|
53694
|
-
message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
|
|
53695
|
-
data: { groupId, taskIds, success, strategy: split[0]?.strategy }
|
|
53696
|
-
});
|
|
53697
|
-
return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
|
|
53698
|
-
}
|
|
53699
|
-
async function buildCoalescedPrompt(manifest, step, groupTasks, agent) {
|
|
53700
|
-
const tree = await buildWorkspaceTree(groupTasks[0].cwd);
|
|
53701
|
-
const treeBlock = tree.rendered ? `# Workspace Structure
|
|
53702
|
-
${tree.rendered}` : "";
|
|
53703
|
-
const roleInstructions = permissionForRole(groupTasks[0].role) === "read_only" ? `You are running in READ-ONLY mode. Do not create, modify, delete, or move files. Emit your findings as TEXT in your final output.` : "";
|
|
53704
|
-
const taskBlocks = groupTasks.map((task, idx) => {
|
|
53705
|
-
return [
|
|
53706
|
-
`### Task ${idx + 1} of ${groupTasks.length} (id: ${task.id})`,
|
|
53707
|
-
`Step: ${step.id}`,
|
|
53708
|
-
`Role: ${step.role}`,
|
|
53709
|
-
`Task: ${step.task.replaceAll("{goal}", manifest.goal)}`
|
|
53710
|
-
].join("\n");
|
|
53711
|
-
}).join("\n\n---\n\n");
|
|
53712
|
-
const outputInstructions = groupTasks.map((task) => `<<<TASK_RESULT:${task.id}>>>`).join(" ... ");
|
|
53713
|
-
return [
|
|
53714
|
-
"# pi-crew Coalesced Worker Prompt",
|
|
53715
|
-
`Run ID: ${manifest.runId}`,
|
|
53716
|
-
`Team: ${manifest.team}`,
|
|
53717
|
-
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
53718
|
-
`Goal: ${manifest.goal}`,
|
|
53719
|
-
`Tasks in this batch: ${groupTasks.length}`,
|
|
53720
|
-
``,
|
|
53721
|
-
roleInstructions,
|
|
53722
|
-
``,
|
|
53723
|
-
treeBlock,
|
|
53724
|
-
``,
|
|
53725
|
-
`# Your Tasks`,
|
|
53726
|
-
`Complete ALL ${groupTasks.length} tasks below. For each, structure your final output using the delimiters shown.`,
|
|
53727
|
-
``,
|
|
53728
|
-
taskBlocks,
|
|
53729
|
-
``,
|
|
53730
|
-
`# Output Format (CRITICAL)`,
|
|
53731
|
-
`After completing all tasks, structure your final output using these delimiters:`,
|
|
53732
|
-
``,
|
|
53733
|
-
outputInstructions,
|
|
53734
|
-
``,
|
|
53735
|
-
`Wrap each task's result between the start and end delimiters:`,
|
|
53736
|
-
`<<<TASK_RESULT:{taskId}>>>`,
|
|
53737
|
-
`...your result for this task...`,
|
|
53738
|
-
`<<<END_TASK_RESULT>>>`,
|
|
53739
|
-
``,
|
|
53740
|
-
`If delimiters don't fit your workflow, use \`### Task N of M\` headings and we'll parse those instead.`
|
|
53741
|
-
].filter(Boolean).join("\n");
|
|
53742
|
-
}
|
|
53743
|
-
function buildScaffoldOutput(groupTasks) {
|
|
53744
|
-
return groupTasks.map(
|
|
53745
|
-
(task, idx) => `<<<TASK_RESULT:${task.id}>>>
|
|
53746
|
-
Scaffold result for task ${idx + 1} of ${groupTasks.length}: ${task.id}
|
|
53747
|
-
<<<END_TASK_RESULT>>>`
|
|
53748
|
-
).join("\n\n");
|
|
53749
|
-
}
|
|
53750
|
-
var init_run_coalesced_task_group = __esm({
|
|
53751
|
-
"src/runtime/run-coalesced-task-group.ts"() {
|
|
53752
|
-
"use strict";
|
|
53753
|
-
init_role_permission();
|
|
53754
|
-
init_child_pi();
|
|
53755
|
-
init_output_splitter();
|
|
53756
|
-
init_workspace_tree();
|
|
53757
|
-
init_state_store();
|
|
53758
|
-
init_artifact_store();
|
|
53759
|
-
init_event_log();
|
|
53760
|
-
init_worker_heartbeat();
|
|
53761
|
-
init_team_runner_artifacts();
|
|
53762
|
-
}
|
|
53763
|
-
});
|
|
53764
|
-
|
|
53765
53367
|
// src/runtime/concurrency.ts
|
|
53766
53368
|
function defaultWorkflowConcurrency(workflowName, workflowMaxConcurrency) {
|
|
53767
53369
|
if (workflowMaxConcurrency !== void 0) return workflowMaxConcurrency;
|
|
@@ -54023,12 +53625,12 @@ var init_tool_output_pruner = __esm({
|
|
|
54023
53625
|
});
|
|
54024
53626
|
|
|
54025
53627
|
// src/runtime/task-output-context.ts
|
|
54026
|
-
import * as
|
|
54027
|
-
import * as
|
|
53628
|
+
import * as fs73 from "node:fs";
|
|
53629
|
+
import * as path61 from "node:path";
|
|
54028
53630
|
function containedExists(filePath, baseDir) {
|
|
54029
53631
|
try {
|
|
54030
53632
|
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
54031
|
-
return
|
|
53633
|
+
return fs73.existsSync(safePath);
|
|
54032
53634
|
} catch {
|
|
54033
53635
|
return false;
|
|
54034
53636
|
}
|
|
@@ -54038,12 +53640,12 @@ function safeTeeName(taskId, artifactName) {
|
|
|
54038
53640
|
return `${safe(taskId)}-${safe(artifactName)}.full.txt`;
|
|
54039
53641
|
}
|
|
54040
53642
|
function teePathForArtifact(artifactsRoot, taskId, artifactName) {
|
|
54041
|
-
return
|
|
53643
|
+
return path61.join(artifactsRoot, "tee", safeTeeName(taskId, artifactName));
|
|
54042
53644
|
}
|
|
54043
53645
|
function writeTeeFile(fullOutputPath, content) {
|
|
54044
53646
|
try {
|
|
54045
|
-
|
|
54046
|
-
|
|
53647
|
+
fs73.mkdirSync(path61.dirname(fullOutputPath), { recursive: true });
|
|
53648
|
+
fs73.writeFileSync(fullOutputPath, content, "utf-8");
|
|
54047
53649
|
return true;
|
|
54048
53650
|
} catch {
|
|
54049
53651
|
return false;
|
|
@@ -54053,7 +53655,7 @@ function readIfSmallWithTee(filePath, opts = {}) {
|
|
|
54053
53655
|
const maxChars = MAX_RESULT_INLINE_BYTES;
|
|
54054
53656
|
try {
|
|
54055
53657
|
const safePath = opts.baseDir ? resolveRealContainedPath(opts.baseDir, filePath) : filePath;
|
|
54056
|
-
const content =
|
|
53658
|
+
const content = fs73.readFileSync(safePath, "utf-8");
|
|
54057
53659
|
if (content.length > maxChars) {
|
|
54058
53660
|
let fullOutputPath;
|
|
54059
53661
|
if (opts.tee && content.length > maxChars * TEE_THRESHOLD_MULTIPLIER) {
|
|
@@ -54087,15 +53689,15 @@ function readIfSmall(filePath, baseDir) {
|
|
|
54087
53689
|
}
|
|
54088
53690
|
function safeSharedName(name) {
|
|
54089
53691
|
const normalized = name.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
54090
|
-
if (!normalized || normalized.split("/").some((segment) => segment === "..") ||
|
|
53692
|
+
if (!normalized || normalized.split("/").some((segment) => segment === "..") || path61.isAbsolute(normalized))
|
|
54091
53693
|
throw new Error(`Invalid shared artifact name: ${name}`);
|
|
54092
53694
|
return normalized;
|
|
54093
53695
|
}
|
|
54094
53696
|
function sharedPath(manifest, name) {
|
|
54095
|
-
const sharedRoot =
|
|
54096
|
-
const resolved =
|
|
54097
|
-
const relative8 =
|
|
54098
|
-
if (relative8.startsWith("..") ||
|
|
53697
|
+
const sharedRoot = path61.resolve(manifest.artifactsRoot, "shared");
|
|
53698
|
+
const resolved = path61.resolve(sharedRoot, safeSharedName(name));
|
|
53699
|
+
const relative8 = path61.relative(sharedRoot, resolved);
|
|
53700
|
+
if (relative8.startsWith("..") || path61.isAbsolute(relative8)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
54099
53701
|
return resolved;
|
|
54100
53702
|
}
|
|
54101
53703
|
function tryParseJson(text) {
|
|
@@ -54110,7 +53712,7 @@ function listTaskArtifacts(manifest, taskId) {
|
|
|
54110
53712
|
const produced = manifest.artifacts.filter((a) => a.producer === taskId);
|
|
54111
53713
|
if (produced.length === 0) return void 0;
|
|
54112
53714
|
return produced.map((a) => {
|
|
54113
|
-
const relative8 =
|
|
53715
|
+
const relative8 = path61.relative(manifest.artifactsRoot, a.path);
|
|
54114
53716
|
return relative8.startsWith("..") ? a.path : relative8;
|
|
54115
53717
|
});
|
|
54116
53718
|
}
|
|
@@ -54168,7 +53770,7 @@ function pruneSharedReads(reads, dependencies, artifactsRoot) {
|
|
|
54168
53770
|
for (const artifact of produced) {
|
|
54169
53771
|
if (typeof artifact !== "string") continue;
|
|
54170
53772
|
fileEdits.push({
|
|
54171
|
-
target:
|
|
53773
|
+
target: path61.resolve(artifactsRoot, artifact),
|
|
54172
53774
|
index: reads.length + depIndex
|
|
54173
53775
|
});
|
|
54174
53776
|
}
|
|
@@ -54221,7 +53823,7 @@ function collectDependencyOutputContext(manifest, tasks, task, step) {
|
|
|
54221
53823
|
const filePath = sharedPath(manifest, name);
|
|
54222
53824
|
const teePath = teePathForArtifact(manifest.artifactsRoot, task.id, name);
|
|
54223
53825
|
const teeResult = readIfSmallWithTee(filePath, {
|
|
54224
|
-
baseDir:
|
|
53826
|
+
baseDir: path61.resolve(manifest.artifactsRoot, "shared"),
|
|
54225
53827
|
tee: { fullOutputPath: teePath }
|
|
54226
53828
|
});
|
|
54227
53829
|
if (teeResult === void 0) return { name, path: filePath, content: "" };
|
|
@@ -54733,6 +54335,502 @@ var init_retry_executor = __esm({
|
|
|
54733
54335
|
}
|
|
54734
54336
|
});
|
|
54735
54337
|
|
|
54338
|
+
// src/runtime/task-runner/output-splitter.ts
|
|
54339
|
+
function splitCoalescedOutput(rawOutput, taskIds) {
|
|
54340
|
+
if (taskIds.length === 0) return [];
|
|
54341
|
+
if (taskIds.length === 1) {
|
|
54342
|
+
return [{ taskId: taskIds[0], text: rawOutput, strategy: "delimiter" }];
|
|
54343
|
+
}
|
|
54344
|
+
const byId = /* @__PURE__ */ new Map();
|
|
54345
|
+
const delimiterHits = /* @__PURE__ */ new Set();
|
|
54346
|
+
const delimiterRegex = /<<<TASK_RESULT:([^\s>]+)>>>([\s\S]*?)<<<END_TASK_RESULT>>>/g;
|
|
54347
|
+
let match;
|
|
54348
|
+
while ((match = delimiterRegex.exec(rawOutput)) !== null) {
|
|
54349
|
+
const id = match[1];
|
|
54350
|
+
const body = match[2].trim();
|
|
54351
|
+
if (taskIds.includes(id) && !byId.has(id)) {
|
|
54352
|
+
byId.set(id, body);
|
|
54353
|
+
delimiterHits.add(id);
|
|
54354
|
+
}
|
|
54355
|
+
}
|
|
54356
|
+
if (delimiterHits.size === taskIds.length) {
|
|
54357
|
+
return taskIds.map((id) => ({
|
|
54358
|
+
taskId: id,
|
|
54359
|
+
text: byId.get(id) ?? "",
|
|
54360
|
+
strategy: "delimiter"
|
|
54361
|
+
}));
|
|
54362
|
+
}
|
|
54363
|
+
if (delimiterHits.size === 0) {
|
|
54364
|
+
const bySection = parseBySectionHeadings(rawOutput, taskIds);
|
|
54365
|
+
if (bySection.size === taskIds.length) {
|
|
54366
|
+
return taskIds.map((id) => ({
|
|
54367
|
+
taskId: id,
|
|
54368
|
+
text: bySection.get(id) ?? "",
|
|
54369
|
+
strategy: "section"
|
|
54370
|
+
}));
|
|
54371
|
+
}
|
|
54372
|
+
}
|
|
54373
|
+
return taskIds.map((id) => ({
|
|
54374
|
+
taskId: id,
|
|
54375
|
+
text: rawOutput,
|
|
54376
|
+
strategy: "broadcast"
|
|
54377
|
+
}));
|
|
54378
|
+
}
|
|
54379
|
+
function parseBySectionHeadings(rawOutput, taskIds) {
|
|
54380
|
+
const result4 = /* @__PURE__ */ new Map();
|
|
54381
|
+
const numberedRegex = /^(#{2,3})\s+Task\s+(\d+)\s+of\s+\d+.*$/gm;
|
|
54382
|
+
const sections = [];
|
|
54383
|
+
let m;
|
|
54384
|
+
while ((m = numberedRegex.exec(rawOutput)) !== null) {
|
|
54385
|
+
sections.push({ num: Number(m[2]), start: m.index + m[0].length });
|
|
54386
|
+
}
|
|
54387
|
+
if (sections.length >= taskIds.length) {
|
|
54388
|
+
sections.sort((a, b) => a.start - b.start);
|
|
54389
|
+
for (let i2 = 0; i2 < sections.length && i2 < taskIds.length; i2 += 1) {
|
|
54390
|
+
const section2 = sections[i2];
|
|
54391
|
+
const end = sections[i2 + 1]?.start ?? rawOutput.length;
|
|
54392
|
+
const text = rawOutput.slice(section2.start, end).trim();
|
|
54393
|
+
const taskId = taskIds[i2];
|
|
54394
|
+
if (taskId) result4.set(taskId, text);
|
|
54395
|
+
}
|
|
54396
|
+
return result4;
|
|
54397
|
+
}
|
|
54398
|
+
const idHeaderRegex = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
54399
|
+
while ((m = idHeaderRegex.exec(rawOutput)) !== null) {
|
|
54400
|
+
const headerText = m[2].trim();
|
|
54401
|
+
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
54402
|
+
if (matchedId && !result4.has(matchedId)) {
|
|
54403
|
+
result4.set(matchedId, "");
|
|
54404
|
+
}
|
|
54405
|
+
}
|
|
54406
|
+
if (result4.size === taskIds.length) {
|
|
54407
|
+
const headerPositions = [];
|
|
54408
|
+
const idHeaderRegex2 = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
54409
|
+
while ((m = idHeaderRegex2.exec(rawOutput)) !== null) {
|
|
54410
|
+
const headerText = m[2].trim();
|
|
54411
|
+
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
54412
|
+
if (matchedId) headerPositions.push({ taskId: matchedId, start: m.index + m[0].length });
|
|
54413
|
+
}
|
|
54414
|
+
headerPositions.sort((a, b) => a.start - b.start);
|
|
54415
|
+
for (let i2 = 0; i2 < headerPositions.length; i2 += 1) {
|
|
54416
|
+
const cur = headerPositions[i2];
|
|
54417
|
+
const next = headerPositions[i2 + 1];
|
|
54418
|
+
const end = next ? rawOutput.lastIndexOf("\n#", next.start) : rawOutput.length;
|
|
54419
|
+
const text = rawOutput.slice(cur.start, end > 0 ? end : rawOutput.length).trim();
|
|
54420
|
+
result4.set(cur.taskId, text);
|
|
54421
|
+
}
|
|
54422
|
+
}
|
|
54423
|
+
return result4;
|
|
54424
|
+
}
|
|
54425
|
+
var init_output_splitter = __esm({
|
|
54426
|
+
"src/runtime/task-runner/output-splitter.ts"() {
|
|
54427
|
+
"use strict";
|
|
54428
|
+
}
|
|
54429
|
+
});
|
|
54430
|
+
|
|
54431
|
+
// src/runtime/team-runner-artifacts.ts
|
|
54432
|
+
function mergeArtifacts(items) {
|
|
54433
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
54434
|
+
for (const item of items) byPath.set(item.path, item);
|
|
54435
|
+
return [...byPath.values()];
|
|
54436
|
+
}
|
|
54437
|
+
var init_team_runner_artifacts = __esm({
|
|
54438
|
+
"src/runtime/team-runner-artifacts.ts"() {
|
|
54439
|
+
"use strict";
|
|
54440
|
+
}
|
|
54441
|
+
});
|
|
54442
|
+
|
|
54443
|
+
// src/runtime/workspace-tree.ts
|
|
54444
|
+
import * as fs74 from "node:fs/promises";
|
|
54445
|
+
import * as path62 from "node:path";
|
|
54446
|
+
function formatBytes2(bytes) {
|
|
54447
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
54448
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
54449
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
54450
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
54451
|
+
}
|
|
54452
|
+
function formatAge(seconds2) {
|
|
54453
|
+
if (seconds2 < 60) return "just now";
|
|
54454
|
+
if (seconds2 < 3600) return `${Math.floor(seconds2 / 60)}m`;
|
|
54455
|
+
if (seconds2 < 86400) return `${Math.floor(seconds2 / 3600)}h`;
|
|
54456
|
+
if (seconds2 < 86400 * 14) return `${Math.floor(seconds2 / 86400)}d`;
|
|
54457
|
+
return `${Math.floor(seconds2 / (86400 * 7))}w`;
|
|
54458
|
+
}
|
|
54459
|
+
function compareByRecency(a, b) {
|
|
54460
|
+
const diff = b.mtimeMs - a.mtimeMs;
|
|
54461
|
+
if (diff !== 0) return diff;
|
|
54462
|
+
return a.name.localeCompare(b.name);
|
|
54463
|
+
}
|
|
54464
|
+
function applyDirLimit(children, limit) {
|
|
54465
|
+
if (children.length <= limit) {
|
|
54466
|
+
return { visible: children, dropped: 0 };
|
|
54467
|
+
}
|
|
54468
|
+
if (limit <= 1) {
|
|
54469
|
+
return {
|
|
54470
|
+
visible: children.slice(0, limit),
|
|
54471
|
+
dropped: children.length - limit
|
|
54472
|
+
};
|
|
54473
|
+
}
|
|
54474
|
+
const recent = children.slice(0, limit - 1);
|
|
54475
|
+
const oldest = children[children.length - 1];
|
|
54476
|
+
const visible = oldest ? [...recent, oldest] : recent;
|
|
54477
|
+
return { visible, dropped: children.length - limit };
|
|
54478
|
+
}
|
|
54479
|
+
async function readChildren(rootPath, parent, excludedDirs) {
|
|
54480
|
+
const dirPath = parent.relativePath ? path62.join(rootPath, parent.relativePath) : rootPath;
|
|
54481
|
+
let names;
|
|
54482
|
+
try {
|
|
54483
|
+
names = await fs74.readdir(dirPath);
|
|
54484
|
+
} catch {
|
|
54485
|
+
return [];
|
|
54486
|
+
}
|
|
54487
|
+
const nodes = await Promise.all(
|
|
54488
|
+
names.map(async (name) => {
|
|
54489
|
+
if (name.startsWith(".")) return null;
|
|
54490
|
+
const relativePath = parent.relativePath ? `${parent.relativePath}/${name}` : name;
|
|
54491
|
+
const absolutePath = path62.join(rootPath, relativePath);
|
|
54492
|
+
try {
|
|
54493
|
+
const stat2 = await fs74.stat(absolutePath);
|
|
54494
|
+
if (stat2.isDirectory() && excludedDirs.has(name)) return null;
|
|
54495
|
+
return {
|
|
54496
|
+
name,
|
|
54497
|
+
relativePath,
|
|
54498
|
+
depth: parent.depth + 1,
|
|
54499
|
+
isDirectory: stat2.isDirectory(),
|
|
54500
|
+
mtimeMs: stat2.mtimeMs,
|
|
54501
|
+
size: stat2.size,
|
|
54502
|
+
children: [],
|
|
54503
|
+
droppedChildCount: 0
|
|
54504
|
+
};
|
|
54505
|
+
} catch {
|
|
54506
|
+
return null;
|
|
54507
|
+
}
|
|
54508
|
+
})
|
|
54509
|
+
);
|
|
54510
|
+
return nodes.filter((n) => n !== null).sort(compareByRecency);
|
|
54511
|
+
}
|
|
54512
|
+
async function collectTree(rootPath, maxDepth, dirLimit, excludedDirs) {
|
|
54513
|
+
const rootStat = await fs74.stat(rootPath);
|
|
54514
|
+
const root = {
|
|
54515
|
+
name: ".",
|
|
54516
|
+
relativePath: "",
|
|
54517
|
+
depth: 0,
|
|
54518
|
+
isDirectory: true,
|
|
54519
|
+
mtimeMs: rootStat.mtimeMs,
|
|
54520
|
+
size: rootStat.size,
|
|
54521
|
+
children: [],
|
|
54522
|
+
droppedChildCount: 0
|
|
54523
|
+
};
|
|
54524
|
+
let truncated = false;
|
|
54525
|
+
const queue = [root];
|
|
54526
|
+
let cursor = 0;
|
|
54527
|
+
while (cursor < queue.length) {
|
|
54528
|
+
const parent = queue[cursor];
|
|
54529
|
+
cursor += 1;
|
|
54530
|
+
if (!parent || parent.depth >= maxDepth) continue;
|
|
54531
|
+
const children = await readChildren(rootPath, parent, excludedDirs);
|
|
54532
|
+
const { visible, dropped } = applyDirLimit(children, dirLimit);
|
|
54533
|
+
parent.children = visible;
|
|
54534
|
+
parent.droppedChildCount = dropped;
|
|
54535
|
+
if (dropped > 0) truncated = true;
|
|
54536
|
+
for (const child of visible) {
|
|
54537
|
+
if (child.isDirectory) queue.push(child);
|
|
54538
|
+
}
|
|
54539
|
+
}
|
|
54540
|
+
return { root, truncated };
|
|
54541
|
+
}
|
|
54542
|
+
function collectLines(node, nowMs3, lines) {
|
|
54543
|
+
if (node.depth === 0) {
|
|
54544
|
+
lines.push({ text: ".", depth: 0, isRoot: true });
|
|
54545
|
+
} else {
|
|
54546
|
+
const indent = " ".repeat(node.depth);
|
|
54547
|
+
const suffix = node.isDirectory ? "/" : "";
|
|
54548
|
+
const label = `${indent}- ${node.name}${suffix}`;
|
|
54549
|
+
if (node.isDirectory) {
|
|
54550
|
+
lines.push({ text: label, depth: node.depth, isRoot: false });
|
|
54551
|
+
} else {
|
|
54552
|
+
const ageSeconds = Math.max(0, Math.floor((nowMs3 - node.mtimeMs) / 1e3));
|
|
54553
|
+
const size = formatBytes2(node.size);
|
|
54554
|
+
const age = formatAge(ageSeconds);
|
|
54555
|
+
lines.push({
|
|
54556
|
+
text: `${label} ${size} ${age}`,
|
|
54557
|
+
depth: node.depth,
|
|
54558
|
+
isRoot: false
|
|
54559
|
+
});
|
|
54560
|
+
}
|
|
54561
|
+
}
|
|
54562
|
+
if (node.droppedChildCount > 0) {
|
|
54563
|
+
const recentChildren = node.children.slice(0, -1);
|
|
54564
|
+
const oldestChild = node.children[node.children.length - 1];
|
|
54565
|
+
for (const child of recentChildren) collectLines(child, nowMs3, lines);
|
|
54566
|
+
const childDepth = node.depth + 1;
|
|
54567
|
+
const indent = " ".repeat(childDepth);
|
|
54568
|
+
lines.push({
|
|
54569
|
+
text: `${indent}- \u2026 ${node.droppedChildCount} more`,
|
|
54570
|
+
depth: childDepth,
|
|
54571
|
+
isRoot: false
|
|
54572
|
+
});
|
|
54573
|
+
if (oldestChild) collectLines(oldestChild, nowMs3, lines);
|
|
54574
|
+
} else {
|
|
54575
|
+
for (const child of node.children) collectLines(child, nowMs3, lines);
|
|
54576
|
+
}
|
|
54577
|
+
}
|
|
54578
|
+
function applyLineCap(lines, cap) {
|
|
54579
|
+
if (lines.length <= cap) return { lines, elided: 0 };
|
|
54580
|
+
const target = Math.max(1, cap - 1);
|
|
54581
|
+
const removeCount = lines.length - target;
|
|
54582
|
+
const removable = lines.map((line4, index) => ({ line: line4, index })).filter((item) => !item.line.isRoot).sort((a, b) => b.line.depth - a.line.depth || b.index - a.index).slice(0, removeCount);
|
|
54583
|
+
if (removable.length === 0) return { lines, elided: 0 };
|
|
54584
|
+
const removedIndexes = new Set(removable.map((item) => item.index));
|
|
54585
|
+
const kept = lines.filter((_, index) => !removedIndexes.has(index));
|
|
54586
|
+
kept.push({
|
|
54587
|
+
text: `\u2026 (${removable.length} lines elided)`,
|
|
54588
|
+
depth: 0,
|
|
54589
|
+
isRoot: false
|
|
54590
|
+
});
|
|
54591
|
+
return { lines: kept, elided: removable.length };
|
|
54592
|
+
}
|
|
54593
|
+
function treeCacheKey(cwd, options) {
|
|
54594
|
+
return `${path62.resolve(cwd)}|${options?.maxDepth ?? ""}|${options?.dirLimit ?? ""}|${options?.lineCap ?? ""}`;
|
|
54595
|
+
}
|
|
54596
|
+
async function buildWorkspaceTree(cwd, options) {
|
|
54597
|
+
const rootPath = path62.resolve(cwd);
|
|
54598
|
+
const cacheKey2 = treeCacheKey(cwd, options);
|
|
54599
|
+
const cached = treeCache.get(cacheKey2);
|
|
54600
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
54601
|
+
return cached.tree;
|
|
54602
|
+
}
|
|
54603
|
+
try {
|
|
54604
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
54605
|
+
const dirLimit = options?.dirLimit ?? DEFAULT_DIR_LIMIT;
|
|
54606
|
+
const lineCap = options?.lineCap ?? DEFAULT_LINE_CAP;
|
|
54607
|
+
const excludedDirs = options?.excludedDirs ?? DEFAULT_EXCLUDED_DIRS;
|
|
54608
|
+
const { root, truncated: dirTruncated } = await collectTree(rootPath, maxDepth, dirLimit, excludedDirs);
|
|
54609
|
+
const nowMs3 = Date.now();
|
|
54610
|
+
const lines = [];
|
|
54611
|
+
collectLines(root, nowMs3, lines);
|
|
54612
|
+
const { lines: capped, elided } = applyLineCap(lines, lineCap);
|
|
54613
|
+
const rendered = capped.map((l) => l.text).join("\n");
|
|
54614
|
+
const result4 = {
|
|
54615
|
+
rootPath,
|
|
54616
|
+
rendered,
|
|
54617
|
+
truncated: dirTruncated || elided > 0,
|
|
54618
|
+
totalLines: capped.length
|
|
54619
|
+
};
|
|
54620
|
+
treeCache.set(cacheKey2, {
|
|
54621
|
+
tree: result4,
|
|
54622
|
+
expiresAt: Date.now() + TREE_CACHE_TTL_MS
|
|
54623
|
+
});
|
|
54624
|
+
return result4;
|
|
54625
|
+
} catch {
|
|
54626
|
+
return emptyResult(rootPath);
|
|
54627
|
+
}
|
|
54628
|
+
}
|
|
54629
|
+
var DEFAULT_MAX_DEPTH, DEFAULT_DIR_LIMIT, DEFAULT_LINE_CAP, DEFAULT_EXCLUDED_DIRS, emptyResult, TREE_CACHE_TTL_MS, treeCache;
|
|
54630
|
+
var init_workspace_tree = __esm({
|
|
54631
|
+
"src/runtime/workspace-tree.ts"() {
|
|
54632
|
+
"use strict";
|
|
54633
|
+
DEFAULT_MAX_DEPTH = 3;
|
|
54634
|
+
DEFAULT_DIR_LIMIT = 12;
|
|
54635
|
+
DEFAULT_LINE_CAP = 120;
|
|
54636
|
+
DEFAULT_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
54637
|
+
"node_modules",
|
|
54638
|
+
".git",
|
|
54639
|
+
".next",
|
|
54640
|
+
"dist",
|
|
54641
|
+
"build",
|
|
54642
|
+
"target",
|
|
54643
|
+
".venv",
|
|
54644
|
+
".cache",
|
|
54645
|
+
".turbo"
|
|
54646
|
+
]);
|
|
54647
|
+
emptyResult = (rootPath) => ({
|
|
54648
|
+
rootPath,
|
|
54649
|
+
rendered: "",
|
|
54650
|
+
truncated: false,
|
|
54651
|
+
totalLines: 0
|
|
54652
|
+
});
|
|
54653
|
+
TREE_CACHE_TTL_MS = 3e4;
|
|
54654
|
+
treeCache = /* @__PURE__ */ new Map();
|
|
54655
|
+
}
|
|
54656
|
+
});
|
|
54657
|
+
|
|
54658
|
+
// src/runtime/run-coalesced-task-group.ts
|
|
54659
|
+
async function runCoalescedTaskGroup(input) {
|
|
54660
|
+
const { manifest, groupTasks, step, agent, signal, executeWorkers } = input;
|
|
54661
|
+
const groupId = groupTasks.map((t2) => t2.id).join("+");
|
|
54662
|
+
const firstTask = groupTasks[0];
|
|
54663
|
+
const taskIds = groupTasks.map((t2) => t2.id);
|
|
54664
|
+
let updatedTasks = input.tasks.map((t2) => {
|
|
54665
|
+
if (taskIds.includes(t2.id) && t2.status !== "running") {
|
|
54666
|
+
return { ...t2, status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
54667
|
+
}
|
|
54668
|
+
return t2;
|
|
54669
|
+
});
|
|
54670
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54671
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
54672
|
+
type: "task.coalesced_dispatch_start",
|
|
54673
|
+
runId: manifest.runId,
|
|
54674
|
+
message: `Dispatching ${groupTasks.length} coalesced tasks in 1 worker (role=${firstTask.role}, cwd=${firstTask.cwd})`,
|
|
54675
|
+
data: { groupId, role: firstTask.role, cwd: firstTask.cwd, taskIds }
|
|
54676
|
+
});
|
|
54677
|
+
const combinedPrompt = await buildCoalescedPrompt(manifest, step, groupTasks, agent);
|
|
54678
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54679
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54680
|
+
return {
|
|
54681
|
+
...t2,
|
|
54682
|
+
heartbeat: t2.heartbeat ?? createWorkerHeartbeat(t2.id)
|
|
54683
|
+
};
|
|
54684
|
+
});
|
|
54685
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54686
|
+
let rawOutput = "";
|
|
54687
|
+
let success = false;
|
|
54688
|
+
if (!executeWorkers) {
|
|
54689
|
+
rawOutput = buildScaffoldOutput(groupTasks);
|
|
54690
|
+
success = true;
|
|
54691
|
+
} else {
|
|
54692
|
+
const heartbeatTimer = setInterval(() => {
|
|
54693
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
54694
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54695
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54696
|
+
return {
|
|
54697
|
+
...t2,
|
|
54698
|
+
heartbeat: touchWorkerHeartbeat(t2.heartbeat ?? createWorkerHeartbeat(t2.id), { alive: true })
|
|
54699
|
+
};
|
|
54700
|
+
});
|
|
54701
|
+
try {
|
|
54702
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54703
|
+
} catch {
|
|
54704
|
+
}
|
|
54705
|
+
}, 15e3);
|
|
54706
|
+
try {
|
|
54707
|
+
const result4 = await runChildPi({
|
|
54708
|
+
cwd: firstTask.cwd,
|
|
54709
|
+
task: combinedPrompt,
|
|
54710
|
+
agent,
|
|
54711
|
+
signal,
|
|
54712
|
+
excludeContextBash: true,
|
|
54713
|
+
maxTurns: 5,
|
|
54714
|
+
onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e)
|
|
54715
|
+
});
|
|
54716
|
+
rawOutput = result4.rawFinalText ?? result4.stdout ?? "";
|
|
54717
|
+
success = result4.exitStatus?.exitCode === 0;
|
|
54718
|
+
} catch (err2) {
|
|
54719
|
+
rawOutput = `Worker dispatch failed: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
54720
|
+
success = false;
|
|
54721
|
+
} finally {
|
|
54722
|
+
clearInterval(heartbeatTimer);
|
|
54723
|
+
}
|
|
54724
|
+
}
|
|
54725
|
+
const split = splitCoalescedOutput(rawOutput, taskIds);
|
|
54726
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
54727
|
+
const newArtifacts = [];
|
|
54728
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54729
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54730
|
+
const entry = split.find((s) => s.taskId === t2.id);
|
|
54731
|
+
const ok = success && Boolean(entry?.text);
|
|
54732
|
+
const text = entry?.text ?? rawOutput;
|
|
54733
|
+
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
54734
|
+
kind: "result",
|
|
54735
|
+
relativePath: `results/${t2.id}.txt`,
|
|
54736
|
+
content: text,
|
|
54737
|
+
producer: t2.id
|
|
54738
|
+
});
|
|
54739
|
+
newArtifacts.push(resultArtifact);
|
|
54740
|
+
return {
|
|
54741
|
+
...t2,
|
|
54742
|
+
status: ok ? "completed" : "failed",
|
|
54743
|
+
finishedAt,
|
|
54744
|
+
result: {
|
|
54745
|
+
text,
|
|
54746
|
+
producer: groupId,
|
|
54747
|
+
strategy: entry?.strategy ?? "broadcast"
|
|
54748
|
+
},
|
|
54749
|
+
resultArtifact
|
|
54750
|
+
};
|
|
54751
|
+
});
|
|
54752
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54753
|
+
let updatedManifest = {
|
|
54754
|
+
...manifest,
|
|
54755
|
+
artifacts: mergeArtifacts([...manifest.artifacts, ...newArtifacts])
|
|
54756
|
+
};
|
|
54757
|
+
if (success) {
|
|
54758
|
+
updatedManifest = updateRunStatus(updatedManifest, "running");
|
|
54759
|
+
}
|
|
54760
|
+
await appendEventAsync(updatedManifest.eventsPath, {
|
|
54761
|
+
type: "task.coalesced_dispatch_end",
|
|
54762
|
+
runId: manifest.runId,
|
|
54763
|
+
message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
|
|
54764
|
+
data: { groupId, taskIds, success, strategy: split[0]?.strategy }
|
|
54765
|
+
});
|
|
54766
|
+
return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
|
|
54767
|
+
}
|
|
54768
|
+
async function buildCoalescedPrompt(manifest, step, groupTasks, agent) {
|
|
54769
|
+
const tree = await buildWorkspaceTree(groupTasks[0].cwd);
|
|
54770
|
+
const treeBlock = tree.rendered ? `# Workspace Structure
|
|
54771
|
+
${tree.rendered}` : "";
|
|
54772
|
+
const roleInstructions = permissionForRole(groupTasks[0].role) === "read_only" ? `You are running in READ-ONLY mode. Do not create, modify, delete, or move files. Emit your findings as TEXT in your final output.` : "";
|
|
54773
|
+
const taskBlocks = groupTasks.map((task, idx) => {
|
|
54774
|
+
return [
|
|
54775
|
+
`### Task ${idx + 1} of ${groupTasks.length} (id: ${task.id})`,
|
|
54776
|
+
`Step: ${step.id}`,
|
|
54777
|
+
`Role: ${step.role}`,
|
|
54778
|
+
`Task: ${step.task.replaceAll("{goal}", manifest.goal)}`
|
|
54779
|
+
].join("\n");
|
|
54780
|
+
}).join("\n\n---\n\n");
|
|
54781
|
+
const outputInstructions = groupTasks.map((task) => `<<<TASK_RESULT:${task.id}>>>`).join(" ... ");
|
|
54782
|
+
return [
|
|
54783
|
+
"# pi-crew Coalesced Worker Prompt",
|
|
54784
|
+
`Run ID: ${manifest.runId}`,
|
|
54785
|
+
`Team: ${manifest.team}`,
|
|
54786
|
+
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
54787
|
+
`Goal: ${manifest.goal}`,
|
|
54788
|
+
`Tasks in this batch: ${groupTasks.length}`,
|
|
54789
|
+
``,
|
|
54790
|
+
roleInstructions,
|
|
54791
|
+
``,
|
|
54792
|
+
treeBlock,
|
|
54793
|
+
``,
|
|
54794
|
+
`# Your Tasks`,
|
|
54795
|
+
`Complete ALL ${groupTasks.length} tasks below. For each, structure your final output using the delimiters shown.`,
|
|
54796
|
+
``,
|
|
54797
|
+
taskBlocks,
|
|
54798
|
+
``,
|
|
54799
|
+
`# Output Format (CRITICAL)`,
|
|
54800
|
+
`After completing all tasks, structure your final output using these delimiters:`,
|
|
54801
|
+
``,
|
|
54802
|
+
outputInstructions,
|
|
54803
|
+
``,
|
|
54804
|
+
`Wrap each task's result between the start and end delimiters:`,
|
|
54805
|
+
`<<<TASK_RESULT:{taskId}>>>`,
|
|
54806
|
+
`...your result for this task...`,
|
|
54807
|
+
`<<<END_TASK_RESULT>>>`,
|
|
54808
|
+
``,
|
|
54809
|
+
`If delimiters don't fit your workflow, use \`### Task N of M\` headings and we'll parse those instead.`
|
|
54810
|
+
].filter(Boolean).join("\n");
|
|
54811
|
+
}
|
|
54812
|
+
function buildScaffoldOutput(groupTasks) {
|
|
54813
|
+
return groupTasks.map(
|
|
54814
|
+
(task, idx) => `<<<TASK_RESULT:${task.id}>>>
|
|
54815
|
+
Scaffold result for task ${idx + 1} of ${groupTasks.length}: ${task.id}
|
|
54816
|
+
<<<END_TASK_RESULT>>>`
|
|
54817
|
+
).join("\n\n");
|
|
54818
|
+
}
|
|
54819
|
+
var init_run_coalesced_task_group = __esm({
|
|
54820
|
+
"src/runtime/run-coalesced-task-group.ts"() {
|
|
54821
|
+
"use strict";
|
|
54822
|
+
init_artifact_store();
|
|
54823
|
+
init_event_log();
|
|
54824
|
+
init_state_store();
|
|
54825
|
+
init_child_pi();
|
|
54826
|
+
init_role_permission();
|
|
54827
|
+
init_output_splitter();
|
|
54828
|
+
init_team_runner_artifacts();
|
|
54829
|
+
init_worker_heartbeat();
|
|
54830
|
+
init_workspace_tree();
|
|
54831
|
+
}
|
|
54832
|
+
});
|
|
54833
|
+
|
|
54736
54834
|
// src/runtime/runtime-policy.ts
|
|
54737
54835
|
function resolveTaskRuntimeKind(globalKind, role, isolationPolicy, env = process.env) {
|
|
54738
54836
|
if (globalKind === "scaffold") return "scaffold";
|
|
@@ -55719,7 +55817,7 @@ function hashToBase36(content, length) {
|
|
|
55719
55817
|
}
|
|
55720
55818
|
function calculateAdaptiveLength(existingCount, config = DEFAULT_CONFIG) {
|
|
55721
55819
|
for (let length = config.minLength; length <= config.maxLength; length++) {
|
|
55722
|
-
const totalPossibilities =
|
|
55820
|
+
const totalPossibilities = 36 ** length;
|
|
55723
55821
|
const probability = 1 - Math.exp(-(existingCount * existingCount) / (2 * totalPossibilities));
|
|
55724
55822
|
if (probability <= config.maxCollisionProbability) {
|
|
55725
55823
|
return length;
|
|
@@ -56812,7 +56910,8 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56812
56910
|
} : updated;
|
|
56813
56911
|
try {
|
|
56814
56912
|
return withRunLockSync(manifest, () => {
|
|
56815
|
-
|
|
56913
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
56914
|
+
flushPendingAtomicWrites();
|
|
56816
56915
|
const latest = loadRunManifestById(manifest.cwd, manifest.runId)?.tasks ?? fallbackTasks;
|
|
56817
56916
|
merged = updateTask(latest, taskWithCheckpoint);
|
|
56818
56917
|
let currentMtime;
|
|
@@ -56823,7 +56922,7 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56823
56922
|
}
|
|
56824
56923
|
if (currentMtime !== baseMtime) {
|
|
56825
56924
|
baseMtime = currentMtime;
|
|
56826
|
-
continue
|
|
56925
|
+
continue;
|
|
56827
56926
|
}
|
|
56828
56927
|
let recheckMtime;
|
|
56829
56928
|
try {
|
|
@@ -56833,7 +56932,7 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56833
56932
|
}
|
|
56834
56933
|
if (recheckMtime !== baseMtime) {
|
|
56835
56934
|
baseMtime = recheckMtime;
|
|
56836
|
-
continue
|
|
56935
|
+
continue;
|
|
56837
56936
|
}
|
|
56838
56937
|
let preWriteMtime;
|
|
56839
56938
|
try {
|
|
@@ -56843,16 +56942,16 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56843
56942
|
}
|
|
56844
56943
|
if (preWriteMtime !== baseMtime) {
|
|
56845
56944
|
baseMtime = preWriteMtime;
|
|
56846
|
-
continue
|
|
56945
|
+
continue;
|
|
56847
56946
|
}
|
|
56848
|
-
break
|
|
56947
|
+
break;
|
|
56849
56948
|
}
|
|
56850
56949
|
if (merged === void 0) {
|
|
56851
56950
|
logInternalError("persistSingleTaskUpdate", new Error("failed to converge after 50 attempts"));
|
|
56852
56951
|
throw new Error("persistSingleTaskUpdate: failed to converge after 50 attempts");
|
|
56853
56952
|
}
|
|
56854
56953
|
try {
|
|
56855
|
-
|
|
56954
|
+
saveRunTasksCoalesced(manifest, merged);
|
|
56856
56955
|
} catch (err2) {
|
|
56857
56956
|
logInternalError("persistSingleTaskUpdate", err2);
|
|
56858
56957
|
throw err2;
|
|
@@ -56884,6 +56983,7 @@ function checkpointTask(manifest, tasks, task, phase, childPid) {
|
|
|
56884
56983
|
var init_state_helpers = __esm({
|
|
56885
56984
|
"src/runtime/task-runner/state-helpers.ts"() {
|
|
56886
56985
|
"use strict";
|
|
56986
|
+
init_atomic_write();
|
|
56887
56987
|
init_locks();
|
|
56888
56988
|
init_state_store();
|
|
56889
56989
|
init_internal_error();
|
|
@@ -60494,10 +60594,8 @@ var init_team_runner = __esm({
|
|
|
60494
60594
|
init_usage();
|
|
60495
60595
|
init_internal_error();
|
|
60496
60596
|
init_branch_freshness();
|
|
60497
|
-
init_team_runner_artifacts();
|
|
60498
60597
|
init_cancellation();
|
|
60499
60598
|
init_coalesce_tasks();
|
|
60500
|
-
init_run_coalesced_task_group();
|
|
60501
60599
|
init_concurrency();
|
|
60502
60600
|
init_crew_agent_records();
|
|
60503
60601
|
init_crew_hooks();
|
|
@@ -60512,6 +60610,7 @@ var init_team_runner = __esm({
|
|
|
60512
60610
|
init_recovery_recipes();
|
|
60513
60611
|
init_retry_executor();
|
|
60514
60612
|
init_role_permission();
|
|
60613
|
+
init_run_coalesced_task_group();
|
|
60515
60614
|
init_run_tracker();
|
|
60516
60615
|
init_runtime_policy();
|
|
60517
60616
|
init_task_display();
|
|
@@ -60519,6 +60618,7 @@ var init_team_runner = __esm({
|
|
|
60519
60618
|
init_task_graph_scheduler();
|
|
60520
60619
|
init_task_output_context();
|
|
60521
60620
|
init_task_runner();
|
|
60621
|
+
init_team_runner_artifacts();
|
|
60522
60622
|
init_usage_tracker();
|
|
60523
60623
|
init_workflow_state();
|
|
60524
60624
|
init_adaptive_plan();
|
|
@@ -68516,7 +68616,6 @@ function tryScanJson(text) {
|
|
|
68516
68616
|
try {
|
|
68517
68617
|
return JSON.parse(candidate);
|
|
68518
68618
|
} catch {
|
|
68519
|
-
continue;
|
|
68520
68619
|
}
|
|
68521
68620
|
}
|
|
68522
68621
|
return void 0;
|
|
@@ -68672,7 +68771,7 @@ function makeWorkflowCtx(manifest, opts) {
|
|
|
68672
68771
|
const semaphore = new Semaphore(concurrency);
|
|
68673
68772
|
let finalResult;
|
|
68674
68773
|
let agentCount = opts.resumedState ? opts.resumedState.agentCount : 0;
|
|
68675
|
-
|
|
68774
|
+
const phaseState = opts.resumedState ? {
|
|
68676
68775
|
currentPhase: opts.resumedState.currentPhase,
|
|
68677
68776
|
phases: [...opts.resumedState.phases]
|
|
68678
68777
|
} : { currentPhase: void 0, phases: [] };
|