pi-crew 0.9.17 → 0.9.19
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 +113 -0
- package/dist/build-meta.json +163 -164
- package/dist/index.mjs +939 -768
- 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/extension/team-tool/run.ts +98 -2
- 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/schema/team-tool-schema.ts +21 -0
- 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/workflows/plan-execute.workflow.md +30 -0
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;
|
|
@@ -46506,6 +46604,17 @@ var init_team_tool_schema = __esm({
|
|
|
46506
46604
|
// "description-only schema" strict-provider check.
|
|
46507
46605
|
Type.Any()
|
|
46508
46606
|
),
|
|
46607
|
+
analysis: Type.Optional(
|
|
46608
|
+
Type.String({
|
|
46609
|
+
maxLength: 1e5,
|
|
46610
|
+
description: "Inline analysis/context notes from the calling session. Persisted to artifacts/{runId}/shared/analysis.md (audit trail) and auto-injected into any workflow step declaring reads: analysis.md (e.g. builtin 'plan-execute'). Mutually exclusive with analysisPath. Ignored by goal-wrapped runs and chain dispatch in v1."
|
|
46611
|
+
})
|
|
46612
|
+
),
|
|
46613
|
+
analysisPath: Type.Optional(
|
|
46614
|
+
Type.String({
|
|
46615
|
+
description: "Path to an existing markdown analysis file (resolved within cwd). Copied into shared/analysis.md. Mutually exclusive with analysis."
|
|
46616
|
+
})
|
|
46617
|
+
),
|
|
46509
46618
|
focus: Type.Optional(
|
|
46510
46619
|
Type.String({
|
|
46511
46620
|
description: "Sub-focus for the doctor action. 'zombies' runs a READ-ONLY scan for orphaned pi-crew sub-agent processes (identified by PI_CREW_KIND=subagent); it never kills and never matches the user's interactive main session."
|
|
@@ -47299,7 +47408,6 @@ function snapshotManifests(cwd) {
|
|
|
47299
47408
|
const content = fs59.readFileSync(abs);
|
|
47300
47409
|
snapshot[rel] = createHash6("sha256").update(content).digest("hex");
|
|
47301
47410
|
} catch {
|
|
47302
|
-
continue;
|
|
47303
47411
|
}
|
|
47304
47412
|
}
|
|
47305
47413
|
return snapshot;
|
|
@@ -47481,7 +47589,7 @@ function getBackgroundRunnerCommand(runnerPath, cwd, runId, loaderInput = resolv
|
|
|
47481
47589
|
};
|
|
47482
47590
|
}
|
|
47483
47591
|
async function spawnBackgroundTeamRun(manifest) {
|
|
47484
|
-
const runnerPath = path52.join(
|
|
47592
|
+
const runnerPath = path52.join(packageRoot(), "src", "runtime", "background-runner.ts");
|
|
47485
47593
|
const logPath = path52.join(manifest.stateRoot, "background.log");
|
|
47486
47594
|
fs60.mkdirSync(manifest.stateRoot, { recursive: true });
|
|
47487
47595
|
const filteredEnv = sanitizeEnvSecrets(process.env, {
|
|
@@ -47571,6 +47679,7 @@ var init_async_runner = __esm({
|
|
|
47571
47679
|
init_env_allowlist();
|
|
47572
47680
|
init_env_filter();
|
|
47573
47681
|
init_internal_error();
|
|
47682
|
+
init_paths();
|
|
47574
47683
|
init_orphan_worker_registry();
|
|
47575
47684
|
init_peer_dep();
|
|
47576
47685
|
requireFromHere = createRequire3(import.meta.url);
|
|
@@ -53140,18 +53249,6 @@ var init_branch_freshness = __esm({
|
|
|
53140
53249
|
}
|
|
53141
53250
|
});
|
|
53142
53251
|
|
|
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
53252
|
// src/runtime/coalesce-tasks.ts
|
|
53156
53253
|
function planCoalescedGroups(readyTaskIds, tasks, workflow, enabled, maxGroupSize = DEFAULT_MAX_GROUP_SIZE, maxGroupBytes = DEFAULT_MAX_GROUP_BYTES) {
|
|
53157
53254
|
if (!enabled || readyTaskIds.length === 0) return [];
|
|
@@ -53278,757 +53375,273 @@ var init_coalesce_tasks = __esm({
|
|
|
53278
53375
|
}
|
|
53279
53376
|
});
|
|
53280
53377
|
|
|
53281
|
-
// src/runtime/
|
|
53282
|
-
function
|
|
53283
|
-
if (
|
|
53284
|
-
if (
|
|
53285
|
-
|
|
53286
|
-
|
|
53287
|
-
|
|
53288
|
-
|
|
53289
|
-
|
|
53290
|
-
|
|
53291
|
-
|
|
53292
|
-
|
|
53293
|
-
|
|
53294
|
-
|
|
53295
|
-
|
|
53296
|
-
|
|
53297
|
-
|
|
53298
|
-
|
|
53299
|
-
|
|
53300
|
-
|
|
53301
|
-
|
|
53302
|
-
|
|
53303
|
-
|
|
53304
|
-
|
|
53378
|
+
// src/runtime/concurrency.ts
|
|
53379
|
+
function defaultWorkflowConcurrency(workflowName, workflowMaxConcurrency) {
|
|
53380
|
+
if (workflowMaxConcurrency !== void 0) return workflowMaxConcurrency;
|
|
53381
|
+
if (workflowName === "parallel-research") return DEFAULT_CONCURRENCY.workflow.parallelResearch;
|
|
53382
|
+
if (workflowName === "research") return DEFAULT_CONCURRENCY.workflow.research;
|
|
53383
|
+
if (workflowName === "implementation") return DEFAULT_CONCURRENCY.workflow.implementation;
|
|
53384
|
+
if (workflowName === "review") return DEFAULT_CONCURRENCY.workflow.review;
|
|
53385
|
+
if (workflowName === "default") return DEFAULT_CONCURRENCY.workflow.default;
|
|
53386
|
+
return DEFAULT_CONCURRENCY.fallback;
|
|
53387
|
+
}
|
|
53388
|
+
function positiveInteger2(value) {
|
|
53389
|
+
if (value === void 0 || !Number.isFinite(value)) return void 0;
|
|
53390
|
+
return Math.max(1, Math.trunc(value));
|
|
53391
|
+
}
|
|
53392
|
+
function resolveBatchConcurrency(input) {
|
|
53393
|
+
const workflowMax = positiveInteger2(input.workflowMaxConcurrency);
|
|
53394
|
+
const defaultConcurrency = defaultWorkflowConcurrency(input.workflowName, workflowMax);
|
|
53395
|
+
const limitMax = positiveInteger2(input.limitMaxConcurrentWorkers);
|
|
53396
|
+
const teamMax = positiveInteger2(input.teamMaxConcurrency);
|
|
53397
|
+
const requested = limitMax ?? teamMax ?? workflowMax ?? defaultWorkflowConcurrency(input.workflowName);
|
|
53398
|
+
let source;
|
|
53399
|
+
if (limitMax !== void 0) source = "limit";
|
|
53400
|
+
else if (teamMax !== void 0) source = "team";
|
|
53401
|
+
else source = "workflow";
|
|
53402
|
+
const hardCap = positiveInteger2(input.hardCap) ?? DEFAULT_CONCURRENCY.hardCap;
|
|
53403
|
+
const maxConcurrent = input.allowUnboundedConcurrency ? requested : Math.min(requested, hardCap);
|
|
53404
|
+
const readyCount = Math.max(0, Math.trunc(Number.isFinite(input.readyCount) ? input.readyCount : 0));
|
|
53405
|
+
const cappedReason = maxConcurrent < requested ? `;capped:${hardCap}` : "";
|
|
53406
|
+
const unboundedReason = input.allowUnboundedConcurrency && requested > hardCap ? `;unbounded:${hardCap}` : "";
|
|
53407
|
+
return {
|
|
53408
|
+
maxConcurrent,
|
|
53409
|
+
selectedCount: readyCount === 0 ? 0 : Math.min(readyCount, maxConcurrent),
|
|
53410
|
+
defaultConcurrency,
|
|
53411
|
+
reason: `${source}:${requested}${cappedReason}${unboundedReason};ready:${readyCount}`
|
|
53412
|
+
};
|
|
53413
|
+
}
|
|
53414
|
+
var init_concurrency = __esm({
|
|
53415
|
+
"src/runtime/concurrency.ts"() {
|
|
53416
|
+
"use strict";
|
|
53417
|
+
init_defaults();
|
|
53305
53418
|
}
|
|
53306
|
-
|
|
53307
|
-
|
|
53308
|
-
|
|
53309
|
-
|
|
53310
|
-
|
|
53311
|
-
|
|
53312
|
-
|
|
53313
|
-
|
|
53314
|
-
|
|
53419
|
+
});
|
|
53420
|
+
|
|
53421
|
+
// src/runtime/goal-achievement.ts
|
|
53422
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
53423
|
+
function workflowIsMutating(workflow) {
|
|
53424
|
+
const steps = workflow?.steps ?? [];
|
|
53425
|
+
return steps.some((step) => typeof step?.role === "string" && MUTATING_ROLES2.has(step.role));
|
|
53426
|
+
}
|
|
53427
|
+
function isGitWorkingTreeClean(cwd) {
|
|
53428
|
+
try {
|
|
53429
|
+
const repoRoot = findRepoRoot(cwd);
|
|
53430
|
+
if (!repoRoot) return { clean: "unknown", repoRoot: void 0 };
|
|
53431
|
+
const res = spawnSync2("git", ["-C", repoRoot, "status", "--porcelain"], { encoding: "utf8", timeout: 5e3 });
|
|
53432
|
+
if (res.error || res.status !== 0) return { clean: "unknown", repoRoot: void 0 };
|
|
53433
|
+
return { clean: res.stdout.trim().length === 0, repoRoot };
|
|
53434
|
+
} catch {
|
|
53435
|
+
return { clean: "unknown", repoRoot: void 0 };
|
|
53315
53436
|
}
|
|
53316
|
-
return taskIds.map((id) => ({
|
|
53317
|
-
taskId: id,
|
|
53318
|
-
text: rawOutput,
|
|
53319
|
-
strategy: "broadcast"
|
|
53320
|
-
}));
|
|
53321
53437
|
}
|
|
53322
|
-
function
|
|
53323
|
-
|
|
53324
|
-
|
|
53325
|
-
|
|
53326
|
-
|
|
53327
|
-
|
|
53328
|
-
|
|
53438
|
+
function assessGoalAchievement(manifest, tasks, workflow) {
|
|
53439
|
+
if (!workflowIsMutating(workflow)) {
|
|
53440
|
+
return {
|
|
53441
|
+
achieved: "unknown",
|
|
53442
|
+
reason: "read-only / doc-only workflow (no executor/test-engineer steps)",
|
|
53443
|
+
signals: []
|
|
53444
|
+
};
|
|
53329
53445
|
}
|
|
53330
|
-
|
|
53331
|
-
|
|
53332
|
-
|
|
53333
|
-
|
|
53334
|
-
|
|
53335
|
-
|
|
53336
|
-
|
|
53337
|
-
if (taskId) result4.set(taskId, text);
|
|
53338
|
-
}
|
|
53339
|
-
return result4;
|
|
53446
|
+
const tree = isGitWorkingTreeClean(manifest.cwd);
|
|
53447
|
+
if (tree.clean === "unknown" || !tree.repoRoot) {
|
|
53448
|
+
return {
|
|
53449
|
+
achieved: "unknown",
|
|
53450
|
+
reason: "not a git repo or git unavailable",
|
|
53451
|
+
signals: []
|
|
53452
|
+
};
|
|
53340
53453
|
}
|
|
53341
|
-
|
|
53342
|
-
|
|
53343
|
-
|
|
53344
|
-
|
|
53345
|
-
|
|
53346
|
-
|
|
53347
|
-
}
|
|
53454
|
+
if (!tree.clean) {
|
|
53455
|
+
return {
|
|
53456
|
+
achieved: true,
|
|
53457
|
+
reason: "git working tree has changes (mutating run edited project files)",
|
|
53458
|
+
signals: [`repoRoot=${tree.repoRoot}`]
|
|
53459
|
+
};
|
|
53348
53460
|
}
|
|
53349
|
-
|
|
53350
|
-
|
|
53351
|
-
|
|
53352
|
-
|
|
53353
|
-
|
|
53354
|
-
|
|
53355
|
-
|
|
53356
|
-
|
|
53357
|
-
|
|
53358
|
-
|
|
53359
|
-
|
|
53360
|
-
|
|
53361
|
-
|
|
53362
|
-
|
|
53363
|
-
|
|
53364
|
-
|
|
53461
|
+
const failedTask = tasks.find((t2) => t2.status === "failed");
|
|
53462
|
+
const signals = ["git working tree clean despite mutating workflow"];
|
|
53463
|
+
if (failedTask) signals.push(`corroborating failed task: ${failedTask.id} (${failedTask.role})`);
|
|
53464
|
+
return {
|
|
53465
|
+
// `false` = false-green detected. Whether to downgrade status is decided
|
|
53466
|
+
// by the caller based on the corroborating failed-task signal.
|
|
53467
|
+
achieved: false,
|
|
53468
|
+
reason: "code-mutating run completed but made no project edits (false-green)",
|
|
53469
|
+
signals
|
|
53470
|
+
};
|
|
53471
|
+
}
|
|
53472
|
+
function applyGoalAchievement(manifest, assessment) {
|
|
53473
|
+
const note2 = assessment.achieved === false ? `goal-achievement: FALSE-GREEN \u2014 ${assessment.reason}. ${assessment.signals.join("; ")}` : assessment.achieved === true ? `goal-achievement: OK \u2014 ${assessment.reason}` : `goal-achievement: unknown \u2014 ${assessment.reason}`;
|
|
53474
|
+
const updated = {
|
|
53475
|
+
...manifest,
|
|
53476
|
+
goalAchieved: assessment.achieved,
|
|
53477
|
+
goalAchievementNote: note2
|
|
53478
|
+
};
|
|
53479
|
+
const hasCorroboratingFailure = assessment.signals.some((s) => s.startsWith("corroborating failed task"));
|
|
53480
|
+
if (assessment.achieved === false && hasCorroboratingFailure && updated.status === "completed") {
|
|
53481
|
+
return { manifest: { ...updated, status: "failed" }, downgraded: true };
|
|
53365
53482
|
}
|
|
53366
|
-
return
|
|
53483
|
+
return { manifest: updated, downgraded: false };
|
|
53367
53484
|
}
|
|
53368
|
-
var
|
|
53369
|
-
|
|
53485
|
+
var MUTATING_ROLES2;
|
|
53486
|
+
var init_goal_achievement = __esm({
|
|
53487
|
+
"src/runtime/goal-achievement.ts"() {
|
|
53370
53488
|
"use strict";
|
|
53489
|
+
init_paths();
|
|
53490
|
+
MUTATING_ROLES2 = /* @__PURE__ */ new Set(["executor", "test-engineer"]);
|
|
53371
53491
|
}
|
|
53372
53492
|
});
|
|
53373
53493
|
|
|
53374
|
-
// src/runtime/
|
|
53375
|
-
|
|
53376
|
-
|
|
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`;
|
|
53494
|
+
// src/runtime/tool-output-pruner.ts
|
|
53495
|
+
function estimateTokens(text) {
|
|
53496
|
+
return Math.ceil(text.length / 4);
|
|
53382
53497
|
}
|
|
53383
|
-
function
|
|
53384
|
-
|
|
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`;
|
|
53498
|
+
function firstErrorLine(text) {
|
|
53499
|
+
return text.split(/\r?\n/).find((line4) => /error|failed|exception|panic/i.test(line4))?.trim();
|
|
53389
53500
|
}
|
|
53390
|
-
function
|
|
53391
|
-
|
|
53392
|
-
if (
|
|
53393
|
-
return
|
|
53501
|
+
function truncateField(value, maxLength) {
|
|
53502
|
+
if (value.length <= maxLength) return value;
|
|
53503
|
+
if (maxLength <= 1) return "\u2026";
|
|
53504
|
+
return `${value.slice(0, maxLength - 1)}\u2026`;
|
|
53394
53505
|
}
|
|
53395
|
-
function
|
|
53396
|
-
|
|
53397
|
-
|
|
53506
|
+
function resultDigest(toolName, content, isError) {
|
|
53507
|
+
const name = toolName.toLowerCase();
|
|
53508
|
+
const text = content ?? "";
|
|
53509
|
+
if (name === "bash") {
|
|
53510
|
+
const exitCode = isError ? 1 : 0;
|
|
53511
|
+
const tail = text.trim().split(/\r?\n/).filter(Boolean).at(-1) ?? "";
|
|
53512
|
+
const error = firstErrorLine(text);
|
|
53513
|
+
return [`exit=${exitCode}`, tail ? `tail=${tail}` : void 0, error ? `error=${error}` : void 0].filter((part) => part !== void 0).join("; ");
|
|
53398
53514
|
}
|
|
53399
|
-
if (
|
|
53400
|
-
|
|
53401
|
-
|
|
53402
|
-
|
|
53403
|
-
};
|
|
53515
|
+
if (name === "search" || name === "grep") {
|
|
53516
|
+
const match = text.match(/(\d+)\s+matches?/i) ?? text.match(/totalMatches["']?:\s*(\d+)/i);
|
|
53517
|
+
const files = text.match(/(\d+)\s+files?/i) ?? text.match(/filesWithMatches["']?:\s*(\d+)/i);
|
|
53518
|
+
const error = firstErrorLine(text);
|
|
53519
|
+
return [match ? `matches=${match[1]}` : void 0, files ? `files=${files[1]}` : void 0, error ? `error=${error}` : void 0].filter((part) => part !== void 0).join("; ") || "search digest unavailable";
|
|
53404
53520
|
}
|
|
53405
|
-
|
|
53406
|
-
const oldest = children[children.length - 1];
|
|
53407
|
-
const visible = oldest ? [...recent, oldest] : recent;
|
|
53408
|
-
return { visible, dropped: children.length - limit };
|
|
53521
|
+
return void 0;
|
|
53409
53522
|
}
|
|
53410
|
-
|
|
53411
|
-
const
|
|
53412
|
-
|
|
53413
|
-
|
|
53414
|
-
|
|
53415
|
-
|
|
53416
|
-
|
|
53523
|
+
function createPrunedNotice(tokens, entry) {
|
|
53524
|
+
const generic = `[Output pruned \u2014 ${tokens} tokens]`;
|
|
53525
|
+
const digest = resultDigest(entry.toolName, entry.content, entry.isError);
|
|
53526
|
+
if (!digest) return generic;
|
|
53527
|
+
const genericTokens = Math.ceil(generic.length / 4);
|
|
53528
|
+
const maxTokens = Math.max(genericTokens, Math.floor(genericTokens * DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER));
|
|
53529
|
+
const prefix = `[Output pruned \u2014 ${tokens} tokens; `;
|
|
53530
|
+
const suffix = "]";
|
|
53531
|
+
const maxChars = Math.max(0, maxTokens * 4 - prefix.length - suffix.length);
|
|
53532
|
+
return `${prefix}${truncateField(digest, maxChars)}${suffix}`;
|
|
53533
|
+
}
|
|
53534
|
+
function readBasePath(filePath) {
|
|
53535
|
+
let base = filePath;
|
|
53536
|
+
while (READ_SELECTOR_SUFFIX.test(base)) {
|
|
53537
|
+
base = base.replace(READ_SELECTOR_SUFFIX, "");
|
|
53417
53538
|
}
|
|
53418
|
-
|
|
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);
|
|
53539
|
+
return base;
|
|
53442
53540
|
}
|
|
53443
|
-
|
|
53444
|
-
|
|
53445
|
-
|
|
53446
|
-
|
|
53447
|
-
|
|
53448
|
-
|
|
53449
|
-
|
|
53450
|
-
|
|
53451
|
-
|
|
53452
|
-
|
|
53453
|
-
|
|
53454
|
-
}
|
|
53455
|
-
|
|
53456
|
-
const
|
|
53457
|
-
|
|
53458
|
-
|
|
53459
|
-
|
|
53460
|
-
|
|
53461
|
-
|
|
53462
|
-
const
|
|
53463
|
-
|
|
53464
|
-
|
|
53465
|
-
|
|
53466
|
-
|
|
53467
|
-
|
|
53468
|
-
|
|
53541
|
+
function toolTargetKey(entry) {
|
|
53542
|
+
if (!entry.target || entry.target.length === 0) return void 0;
|
|
53543
|
+
return JSON.stringify([entry.toolName, entry.target]);
|
|
53544
|
+
}
|
|
53545
|
+
function buildStalenessIndex(toolResults, fileEdits = []) {
|
|
53546
|
+
const lastResultIndexByKey = /* @__PURE__ */ new Map();
|
|
53547
|
+
for (let i2 = 0; i2 < toolResults.length; i2++) {
|
|
53548
|
+
const entry = toolResults[i2];
|
|
53549
|
+
if (entry.isError) continue;
|
|
53550
|
+
const key = toolTargetKey(entry);
|
|
53551
|
+
if (key !== void 0) lastResultIndexByKey.set(key, i2);
|
|
53552
|
+
}
|
|
53553
|
+
const lastEditIndexByPath = /* @__PURE__ */ new Map();
|
|
53554
|
+
for (const edit of fileEdits) {
|
|
53555
|
+
lastEditIndexByPath.set(edit.target, edit.index);
|
|
53556
|
+
}
|
|
53557
|
+
const staleIndices = /* @__PURE__ */ new Set();
|
|
53558
|
+
for (let i2 = 0; i2 < toolResults.length; i2++) {
|
|
53559
|
+
const entry = toolResults[i2];
|
|
53560
|
+
const key = toolTargetKey(entry);
|
|
53561
|
+
if (key !== void 0) {
|
|
53562
|
+
const lastIndex = lastResultIndexByKey.get(key);
|
|
53563
|
+
if (lastIndex !== void 0 && lastIndex > i2) {
|
|
53564
|
+
staleIndices.add(i2);
|
|
53565
|
+
continue;
|
|
53566
|
+
}
|
|
53567
|
+
}
|
|
53568
|
+
if (entry.toolName.toLowerCase() === "read" && entry.target) {
|
|
53569
|
+
const basePath = readBasePath(entry.target);
|
|
53570
|
+
const editIndex = lastEditIndexByPath.get(basePath);
|
|
53571
|
+
if (editIndex !== void 0 && editIndex > i2) {
|
|
53572
|
+
staleIndices.add(i2);
|
|
53573
|
+
}
|
|
53469
53574
|
}
|
|
53470
53575
|
}
|
|
53471
|
-
return {
|
|
53576
|
+
return { staleIndices };
|
|
53472
53577
|
}
|
|
53473
|
-
function
|
|
53474
|
-
|
|
53475
|
-
|
|
53476
|
-
|
|
53477
|
-
|
|
53478
|
-
|
|
53479
|
-
|
|
53480
|
-
|
|
53481
|
-
|
|
53482
|
-
|
|
53483
|
-
|
|
53484
|
-
|
|
53485
|
-
|
|
53486
|
-
|
|
53487
|
-
|
|
53488
|
-
|
|
53489
|
-
|
|
53490
|
-
});
|
|
53578
|
+
function pruneToolOutputs(results, config = DEFAULT_PRUNE_CONFIG) {
|
|
53579
|
+
const { staleIndices } = buildStalenessIndex(results);
|
|
53580
|
+
const staleOverridable = new Set(config.staleOverridableTools ?? []);
|
|
53581
|
+
let accumulatedTokens = 0;
|
|
53582
|
+
let tokensSaved = 0;
|
|
53583
|
+
let prunedCount = 0;
|
|
53584
|
+
const candidates = [];
|
|
53585
|
+
const prunedIds = [];
|
|
53586
|
+
for (let i2 = results.length - 1; i2 >= 0; i2--) {
|
|
53587
|
+
const entry = results[i2];
|
|
53588
|
+
const tokens = estimateTokens(entry.content);
|
|
53589
|
+
const isStale = staleIndices.has(i2);
|
|
53590
|
+
const isProtected = config.protectedTools.includes(entry.toolName) && !(isStale && staleOverridable.has(entry.toolName));
|
|
53591
|
+
const insideProtectWindow = accumulatedTokens < config.protectTokens;
|
|
53592
|
+
if (insideProtectWindow && !isStale || isProtected) {
|
|
53593
|
+
accumulatedTokens += tokens;
|
|
53594
|
+
continue;
|
|
53491
53595
|
}
|
|
53492
|
-
|
|
53493
|
-
|
|
53494
|
-
|
|
53495
|
-
|
|
53496
|
-
|
|
53497
|
-
|
|
53498
|
-
|
|
53499
|
-
lines.push({
|
|
53500
|
-
text: `${indent}- \u2026 ${node.droppedChildCount} more`,
|
|
53501
|
-
depth: childDepth,
|
|
53502
|
-
isRoot: false
|
|
53596
|
+
const notice = createPrunedNotice(tokens, entry);
|
|
53597
|
+
candidates.push({
|
|
53598
|
+
index: i2,
|
|
53599
|
+
entry,
|
|
53600
|
+
tokens,
|
|
53601
|
+
notice,
|
|
53602
|
+
savings: Math.max(0, tokens - Math.ceil(notice.length / 4))
|
|
53503
53603
|
});
|
|
53504
|
-
|
|
53505
|
-
} else {
|
|
53506
|
-
for (const child of node.children) collectLines(child, nowMs3, lines);
|
|
53604
|
+
accumulatedTokens += tokens;
|
|
53507
53605
|
}
|
|
53606
|
+
for (const candidate of candidates) {
|
|
53607
|
+
tokensSaved += candidate.savings;
|
|
53608
|
+
}
|
|
53609
|
+
if (tokensSaved < config.minimumSavings || candidates.length === 0) {
|
|
53610
|
+
return { prunedCount: 0, tokensSaved: 0, results, prunedIds: [] };
|
|
53611
|
+
}
|
|
53612
|
+
const prunedResults = [...results];
|
|
53613
|
+
for (const candidate of candidates) {
|
|
53614
|
+
prunedResults[candidate.index] = {
|
|
53615
|
+
...candidate.entry,
|
|
53616
|
+
content: candidate.notice
|
|
53617
|
+
};
|
|
53618
|
+
prunedIds.push(candidate.entry.id);
|
|
53619
|
+
prunedCount++;
|
|
53620
|
+
}
|
|
53621
|
+
return { prunedCount, tokensSaved, results: prunedResults, prunedIds };
|
|
53508
53622
|
}
|
|
53509
|
-
|
|
53510
|
-
|
|
53511
|
-
|
|
53512
|
-
|
|
53513
|
-
|
|
53514
|
-
|
|
53515
|
-
|
|
53516
|
-
|
|
53517
|
-
|
|
53518
|
-
|
|
53519
|
-
|
|
53520
|
-
|
|
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;
|
|
53623
|
+
var DEFAULT_PRUNE_CONFIG, DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER, READ_SELECTOR_SUFFIX;
|
|
53624
|
+
var init_tool_output_pruner = __esm({
|
|
53625
|
+
"src/runtime/tool-output-pruner.ts"() {
|
|
53626
|
+
"use strict";
|
|
53627
|
+
DEFAULT_PRUNE_CONFIG = {
|
|
53628
|
+
protectTokens: 4e4,
|
|
53629
|
+
minimumSavings: 2e4,
|
|
53630
|
+
protectedTools: ["read"],
|
|
53631
|
+
staleOverridableTools: ["read"]
|
|
53632
|
+
};
|
|
53633
|
+
DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER = 1.25;
|
|
53634
|
+
READ_SELECTOR_SUFFIX = /:(?:raw|conflicts|\d+(?:[-+]\d+)?(?:,\d+(?:[-+]\d+)?)*)$/;
|
|
53533
53635
|
}
|
|
53534
|
-
|
|
53535
|
-
|
|
53536
|
-
|
|
53537
|
-
|
|
53538
|
-
|
|
53539
|
-
|
|
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
|
-
// src/runtime/concurrency.ts
|
|
53766
|
-
function defaultWorkflowConcurrency(workflowName, workflowMaxConcurrency) {
|
|
53767
|
-
if (workflowMaxConcurrency !== void 0) return workflowMaxConcurrency;
|
|
53768
|
-
if (workflowName === "parallel-research") return DEFAULT_CONCURRENCY.workflow.parallelResearch;
|
|
53769
|
-
if (workflowName === "research") return DEFAULT_CONCURRENCY.workflow.research;
|
|
53770
|
-
if (workflowName === "implementation") return DEFAULT_CONCURRENCY.workflow.implementation;
|
|
53771
|
-
if (workflowName === "review") return DEFAULT_CONCURRENCY.workflow.review;
|
|
53772
|
-
if (workflowName === "default") return DEFAULT_CONCURRENCY.workflow.default;
|
|
53773
|
-
return DEFAULT_CONCURRENCY.fallback;
|
|
53774
|
-
}
|
|
53775
|
-
function positiveInteger2(value) {
|
|
53776
|
-
if (value === void 0 || !Number.isFinite(value)) return void 0;
|
|
53777
|
-
return Math.max(1, Math.trunc(value));
|
|
53778
|
-
}
|
|
53779
|
-
function resolveBatchConcurrency(input) {
|
|
53780
|
-
const workflowMax = positiveInteger2(input.workflowMaxConcurrency);
|
|
53781
|
-
const defaultConcurrency = defaultWorkflowConcurrency(input.workflowName, workflowMax);
|
|
53782
|
-
const limitMax = positiveInteger2(input.limitMaxConcurrentWorkers);
|
|
53783
|
-
const teamMax = positiveInteger2(input.teamMaxConcurrency);
|
|
53784
|
-
const requested = limitMax ?? teamMax ?? workflowMax ?? defaultWorkflowConcurrency(input.workflowName);
|
|
53785
|
-
let source;
|
|
53786
|
-
if (limitMax !== void 0) source = "limit";
|
|
53787
|
-
else if (teamMax !== void 0) source = "team";
|
|
53788
|
-
else source = "workflow";
|
|
53789
|
-
const hardCap = positiveInteger2(input.hardCap) ?? DEFAULT_CONCURRENCY.hardCap;
|
|
53790
|
-
const maxConcurrent = input.allowUnboundedConcurrency ? requested : Math.min(requested, hardCap);
|
|
53791
|
-
const readyCount = Math.max(0, Math.trunc(Number.isFinite(input.readyCount) ? input.readyCount : 0));
|
|
53792
|
-
const cappedReason = maxConcurrent < requested ? `;capped:${hardCap}` : "";
|
|
53793
|
-
const unboundedReason = input.allowUnboundedConcurrency && requested > hardCap ? `;unbounded:${hardCap}` : "";
|
|
53794
|
-
return {
|
|
53795
|
-
maxConcurrent,
|
|
53796
|
-
selectedCount: readyCount === 0 ? 0 : Math.min(readyCount, maxConcurrent),
|
|
53797
|
-
defaultConcurrency,
|
|
53798
|
-
reason: `${source}:${requested}${cappedReason}${unboundedReason};ready:${readyCount}`
|
|
53799
|
-
};
|
|
53800
|
-
}
|
|
53801
|
-
var init_concurrency = __esm({
|
|
53802
|
-
"src/runtime/concurrency.ts"() {
|
|
53803
|
-
"use strict";
|
|
53804
|
-
init_defaults();
|
|
53805
|
-
}
|
|
53806
|
-
});
|
|
53807
|
-
|
|
53808
|
-
// src/runtime/goal-achievement.ts
|
|
53809
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
53810
|
-
function workflowIsMutating(workflow) {
|
|
53811
|
-
const steps = workflow?.steps ?? [];
|
|
53812
|
-
return steps.some((step) => typeof step?.role === "string" && MUTATING_ROLES2.has(step.role));
|
|
53813
|
-
}
|
|
53814
|
-
function isGitWorkingTreeClean(cwd) {
|
|
53815
|
-
try {
|
|
53816
|
-
const repoRoot = findRepoRoot(cwd);
|
|
53817
|
-
if (!repoRoot) return { clean: "unknown", repoRoot: void 0 };
|
|
53818
|
-
const res = spawnSync2("git", ["-C", repoRoot, "status", "--porcelain"], { encoding: "utf8", timeout: 5e3 });
|
|
53819
|
-
if (res.error || res.status !== 0) return { clean: "unknown", repoRoot: void 0 };
|
|
53820
|
-
return { clean: res.stdout.trim().length === 0, repoRoot };
|
|
53821
|
-
} catch {
|
|
53822
|
-
return { clean: "unknown", repoRoot: void 0 };
|
|
53823
|
-
}
|
|
53824
|
-
}
|
|
53825
|
-
function assessGoalAchievement(manifest, tasks, workflow) {
|
|
53826
|
-
if (!workflowIsMutating(workflow)) {
|
|
53827
|
-
return {
|
|
53828
|
-
achieved: "unknown",
|
|
53829
|
-
reason: "read-only / doc-only workflow (no executor/test-engineer steps)",
|
|
53830
|
-
signals: []
|
|
53831
|
-
};
|
|
53832
|
-
}
|
|
53833
|
-
const tree = isGitWorkingTreeClean(manifest.cwd);
|
|
53834
|
-
if (tree.clean === "unknown" || !tree.repoRoot) {
|
|
53835
|
-
return {
|
|
53836
|
-
achieved: "unknown",
|
|
53837
|
-
reason: "not a git repo or git unavailable",
|
|
53838
|
-
signals: []
|
|
53839
|
-
};
|
|
53840
|
-
}
|
|
53841
|
-
if (!tree.clean) {
|
|
53842
|
-
return {
|
|
53843
|
-
achieved: true,
|
|
53844
|
-
reason: "git working tree has changes (mutating run edited project files)",
|
|
53845
|
-
signals: [`repoRoot=${tree.repoRoot}`]
|
|
53846
|
-
};
|
|
53847
|
-
}
|
|
53848
|
-
const failedTask = tasks.find((t2) => t2.status === "failed");
|
|
53849
|
-
const signals = ["git working tree clean despite mutating workflow"];
|
|
53850
|
-
if (failedTask) signals.push(`corroborating failed task: ${failedTask.id} (${failedTask.role})`);
|
|
53851
|
-
return {
|
|
53852
|
-
// `false` = false-green detected. Whether to downgrade status is decided
|
|
53853
|
-
// by the caller based on the corroborating failed-task signal.
|
|
53854
|
-
achieved: false,
|
|
53855
|
-
reason: "code-mutating run completed but made no project edits (false-green)",
|
|
53856
|
-
signals
|
|
53857
|
-
};
|
|
53858
|
-
}
|
|
53859
|
-
function applyGoalAchievement(manifest, assessment) {
|
|
53860
|
-
const note2 = assessment.achieved === false ? `goal-achievement: FALSE-GREEN \u2014 ${assessment.reason}. ${assessment.signals.join("; ")}` : assessment.achieved === true ? `goal-achievement: OK \u2014 ${assessment.reason}` : `goal-achievement: unknown \u2014 ${assessment.reason}`;
|
|
53861
|
-
const updated = {
|
|
53862
|
-
...manifest,
|
|
53863
|
-
goalAchieved: assessment.achieved,
|
|
53864
|
-
goalAchievementNote: note2
|
|
53865
|
-
};
|
|
53866
|
-
const hasCorroboratingFailure = assessment.signals.some((s) => s.startsWith("corroborating failed task"));
|
|
53867
|
-
if (assessment.achieved === false && hasCorroboratingFailure && updated.status === "completed") {
|
|
53868
|
-
return { manifest: { ...updated, status: "failed" }, downgraded: true };
|
|
53869
|
-
}
|
|
53870
|
-
return { manifest: updated, downgraded: false };
|
|
53871
|
-
}
|
|
53872
|
-
var MUTATING_ROLES2;
|
|
53873
|
-
var init_goal_achievement = __esm({
|
|
53874
|
-
"src/runtime/goal-achievement.ts"() {
|
|
53875
|
-
"use strict";
|
|
53876
|
-
init_paths();
|
|
53877
|
-
MUTATING_ROLES2 = /* @__PURE__ */ new Set(["executor", "test-engineer"]);
|
|
53878
|
-
}
|
|
53879
|
-
});
|
|
53880
|
-
|
|
53881
|
-
// src/runtime/tool-output-pruner.ts
|
|
53882
|
-
function estimateTokens(text) {
|
|
53883
|
-
return Math.ceil(text.length / 4);
|
|
53884
|
-
}
|
|
53885
|
-
function firstErrorLine(text) {
|
|
53886
|
-
return text.split(/\r?\n/).find((line4) => /error|failed|exception|panic/i.test(line4))?.trim();
|
|
53887
|
-
}
|
|
53888
|
-
function truncateField(value, maxLength) {
|
|
53889
|
-
if (value.length <= maxLength) return value;
|
|
53890
|
-
if (maxLength <= 1) return "\u2026";
|
|
53891
|
-
return `${value.slice(0, maxLength - 1)}\u2026`;
|
|
53892
|
-
}
|
|
53893
|
-
function resultDigest(toolName, content, isError) {
|
|
53894
|
-
const name = toolName.toLowerCase();
|
|
53895
|
-
const text = content ?? "";
|
|
53896
|
-
if (name === "bash") {
|
|
53897
|
-
const exitCode = isError ? 1 : 0;
|
|
53898
|
-
const tail = text.trim().split(/\r?\n/).filter(Boolean).at(-1) ?? "";
|
|
53899
|
-
const error = firstErrorLine(text);
|
|
53900
|
-
return [`exit=${exitCode}`, tail ? `tail=${tail}` : void 0, error ? `error=${error}` : void 0].filter((part) => part !== void 0).join("; ");
|
|
53901
|
-
}
|
|
53902
|
-
if (name === "search" || name === "grep") {
|
|
53903
|
-
const match = text.match(/(\d+)\s+matches?/i) ?? text.match(/totalMatches["']?:\s*(\d+)/i);
|
|
53904
|
-
const files = text.match(/(\d+)\s+files?/i) ?? text.match(/filesWithMatches["']?:\s*(\d+)/i);
|
|
53905
|
-
const error = firstErrorLine(text);
|
|
53906
|
-
return [match ? `matches=${match[1]}` : void 0, files ? `files=${files[1]}` : void 0, error ? `error=${error}` : void 0].filter((part) => part !== void 0).join("; ") || "search digest unavailable";
|
|
53907
|
-
}
|
|
53908
|
-
return void 0;
|
|
53909
|
-
}
|
|
53910
|
-
function createPrunedNotice(tokens, entry) {
|
|
53911
|
-
const generic = `[Output pruned \u2014 ${tokens} tokens]`;
|
|
53912
|
-
const digest = resultDigest(entry.toolName, entry.content, entry.isError);
|
|
53913
|
-
if (!digest) return generic;
|
|
53914
|
-
const genericTokens = Math.ceil(generic.length / 4);
|
|
53915
|
-
const maxTokens = Math.max(genericTokens, Math.floor(genericTokens * DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER));
|
|
53916
|
-
const prefix = `[Output pruned \u2014 ${tokens} tokens; `;
|
|
53917
|
-
const suffix = "]";
|
|
53918
|
-
const maxChars = Math.max(0, maxTokens * 4 - prefix.length - suffix.length);
|
|
53919
|
-
return `${prefix}${truncateField(digest, maxChars)}${suffix}`;
|
|
53920
|
-
}
|
|
53921
|
-
function readBasePath(filePath) {
|
|
53922
|
-
let base = filePath;
|
|
53923
|
-
while (READ_SELECTOR_SUFFIX.test(base)) {
|
|
53924
|
-
base = base.replace(READ_SELECTOR_SUFFIX, "");
|
|
53925
|
-
}
|
|
53926
|
-
return base;
|
|
53927
|
-
}
|
|
53928
|
-
function toolTargetKey(entry) {
|
|
53929
|
-
if (!entry.target || entry.target.length === 0) return void 0;
|
|
53930
|
-
return JSON.stringify([entry.toolName, entry.target]);
|
|
53931
|
-
}
|
|
53932
|
-
function buildStalenessIndex(toolResults, fileEdits = []) {
|
|
53933
|
-
const lastResultIndexByKey = /* @__PURE__ */ new Map();
|
|
53934
|
-
for (let i2 = 0; i2 < toolResults.length; i2++) {
|
|
53935
|
-
const entry = toolResults[i2];
|
|
53936
|
-
if (entry.isError) continue;
|
|
53937
|
-
const key = toolTargetKey(entry);
|
|
53938
|
-
if (key !== void 0) lastResultIndexByKey.set(key, i2);
|
|
53939
|
-
}
|
|
53940
|
-
const lastEditIndexByPath = /* @__PURE__ */ new Map();
|
|
53941
|
-
for (const edit of fileEdits) {
|
|
53942
|
-
lastEditIndexByPath.set(edit.target, edit.index);
|
|
53943
|
-
}
|
|
53944
|
-
const staleIndices = /* @__PURE__ */ new Set();
|
|
53945
|
-
for (let i2 = 0; i2 < toolResults.length; i2++) {
|
|
53946
|
-
const entry = toolResults[i2];
|
|
53947
|
-
const key = toolTargetKey(entry);
|
|
53948
|
-
if (key !== void 0) {
|
|
53949
|
-
const lastIndex = lastResultIndexByKey.get(key);
|
|
53950
|
-
if (lastIndex !== void 0 && lastIndex > i2) {
|
|
53951
|
-
staleIndices.add(i2);
|
|
53952
|
-
continue;
|
|
53953
|
-
}
|
|
53954
|
-
}
|
|
53955
|
-
if (entry.toolName.toLowerCase() === "read" && entry.target) {
|
|
53956
|
-
const basePath = readBasePath(entry.target);
|
|
53957
|
-
const editIndex = lastEditIndexByPath.get(basePath);
|
|
53958
|
-
if (editIndex !== void 0 && editIndex > i2) {
|
|
53959
|
-
staleIndices.add(i2);
|
|
53960
|
-
}
|
|
53961
|
-
}
|
|
53962
|
-
}
|
|
53963
|
-
return { staleIndices };
|
|
53964
|
-
}
|
|
53965
|
-
function pruneToolOutputs(results, config = DEFAULT_PRUNE_CONFIG) {
|
|
53966
|
-
const { staleIndices } = buildStalenessIndex(results);
|
|
53967
|
-
const staleOverridable = new Set(config.staleOverridableTools ?? []);
|
|
53968
|
-
let accumulatedTokens = 0;
|
|
53969
|
-
let tokensSaved = 0;
|
|
53970
|
-
let prunedCount = 0;
|
|
53971
|
-
const candidates = [];
|
|
53972
|
-
const prunedIds = [];
|
|
53973
|
-
for (let i2 = results.length - 1; i2 >= 0; i2--) {
|
|
53974
|
-
const entry = results[i2];
|
|
53975
|
-
const tokens = estimateTokens(entry.content);
|
|
53976
|
-
const isStale = staleIndices.has(i2);
|
|
53977
|
-
const isProtected = config.protectedTools.includes(entry.toolName) && !(isStale && staleOverridable.has(entry.toolName));
|
|
53978
|
-
const insideProtectWindow = accumulatedTokens < config.protectTokens;
|
|
53979
|
-
if (insideProtectWindow && !isStale || isProtected) {
|
|
53980
|
-
accumulatedTokens += tokens;
|
|
53981
|
-
continue;
|
|
53982
|
-
}
|
|
53983
|
-
const notice = createPrunedNotice(tokens, entry);
|
|
53984
|
-
candidates.push({
|
|
53985
|
-
index: i2,
|
|
53986
|
-
entry,
|
|
53987
|
-
tokens,
|
|
53988
|
-
notice,
|
|
53989
|
-
savings: Math.max(0, tokens - Math.ceil(notice.length / 4))
|
|
53990
|
-
});
|
|
53991
|
-
accumulatedTokens += tokens;
|
|
53992
|
-
}
|
|
53993
|
-
for (const candidate of candidates) {
|
|
53994
|
-
tokensSaved += candidate.savings;
|
|
53995
|
-
}
|
|
53996
|
-
if (tokensSaved < config.minimumSavings || candidates.length === 0) {
|
|
53997
|
-
return { prunedCount: 0, tokensSaved: 0, results, prunedIds: [] };
|
|
53998
|
-
}
|
|
53999
|
-
const prunedResults = [...results];
|
|
54000
|
-
for (const candidate of candidates) {
|
|
54001
|
-
prunedResults[candidate.index] = {
|
|
54002
|
-
...candidate.entry,
|
|
54003
|
-
content: candidate.notice
|
|
54004
|
-
};
|
|
54005
|
-
prunedIds.push(candidate.entry.id);
|
|
54006
|
-
prunedCount++;
|
|
54007
|
-
}
|
|
54008
|
-
return { prunedCount, tokensSaved, results: prunedResults, prunedIds };
|
|
54009
|
-
}
|
|
54010
|
-
var DEFAULT_PRUNE_CONFIG, DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER, READ_SELECTOR_SUFFIX;
|
|
54011
|
-
var init_tool_output_pruner = __esm({
|
|
54012
|
-
"src/runtime/tool-output-pruner.ts"() {
|
|
54013
|
-
"use strict";
|
|
54014
|
-
DEFAULT_PRUNE_CONFIG = {
|
|
54015
|
-
protectTokens: 4e4,
|
|
54016
|
-
minimumSavings: 2e4,
|
|
54017
|
-
protectedTools: ["read"],
|
|
54018
|
-
staleOverridableTools: ["read"]
|
|
54019
|
-
};
|
|
54020
|
-
DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER = 1.25;
|
|
54021
|
-
READ_SELECTOR_SUFFIX = /:(?:raw|conflicts|\d+(?:[-+]\d+)?(?:,\d+(?:[-+]\d+)?)*)$/;
|
|
54022
|
-
}
|
|
54023
|
-
});
|
|
54024
|
-
|
|
54025
|
-
// src/runtime/task-output-context.ts
|
|
54026
|
-
import * as fs74 from "node:fs";
|
|
54027
|
-
import * as path62 from "node:path";
|
|
54028
|
-
function containedExists(filePath, baseDir) {
|
|
53636
|
+
});
|
|
53637
|
+
|
|
53638
|
+
// src/runtime/task-output-context.ts
|
|
53639
|
+
import * as fs73 from "node:fs";
|
|
53640
|
+
import * as path61 from "node:path";
|
|
53641
|
+
function containedExists(filePath, baseDir) {
|
|
54029
53642
|
try {
|
|
54030
53643
|
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
54031
|
-
return
|
|
53644
|
+
return fs73.existsSync(safePath);
|
|
54032
53645
|
} catch {
|
|
54033
53646
|
return false;
|
|
54034
53647
|
}
|
|
@@ -54038,12 +53651,12 @@ function safeTeeName(taskId, artifactName) {
|
|
|
54038
53651
|
return `${safe(taskId)}-${safe(artifactName)}.full.txt`;
|
|
54039
53652
|
}
|
|
54040
53653
|
function teePathForArtifact(artifactsRoot, taskId, artifactName) {
|
|
54041
|
-
return
|
|
53654
|
+
return path61.join(artifactsRoot, "tee", safeTeeName(taskId, artifactName));
|
|
54042
53655
|
}
|
|
54043
53656
|
function writeTeeFile(fullOutputPath, content) {
|
|
54044
53657
|
try {
|
|
54045
|
-
|
|
54046
|
-
|
|
53658
|
+
fs73.mkdirSync(path61.dirname(fullOutputPath), { recursive: true });
|
|
53659
|
+
fs73.writeFileSync(fullOutputPath, content, "utf-8");
|
|
54047
53660
|
return true;
|
|
54048
53661
|
} catch {
|
|
54049
53662
|
return false;
|
|
@@ -54053,7 +53666,7 @@ function readIfSmallWithTee(filePath, opts = {}) {
|
|
|
54053
53666
|
const maxChars = MAX_RESULT_INLINE_BYTES;
|
|
54054
53667
|
try {
|
|
54055
53668
|
const safePath = opts.baseDir ? resolveRealContainedPath(opts.baseDir, filePath) : filePath;
|
|
54056
|
-
const content =
|
|
53669
|
+
const content = fs73.readFileSync(safePath, "utf-8");
|
|
54057
53670
|
if (content.length > maxChars) {
|
|
54058
53671
|
let fullOutputPath;
|
|
54059
53672
|
if (opts.tee && content.length > maxChars * TEE_THRESHOLD_MULTIPLIER) {
|
|
@@ -54087,15 +53700,15 @@ function readIfSmall(filePath, baseDir) {
|
|
|
54087
53700
|
}
|
|
54088
53701
|
function safeSharedName(name) {
|
|
54089
53702
|
const normalized = name.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
54090
|
-
if (!normalized || normalized.split("/").some((segment) => segment === "..") ||
|
|
53703
|
+
if (!normalized || normalized.split("/").some((segment) => segment === "..") || path61.isAbsolute(normalized))
|
|
54091
53704
|
throw new Error(`Invalid shared artifact name: ${name}`);
|
|
54092
53705
|
return normalized;
|
|
54093
53706
|
}
|
|
54094
53707
|
function sharedPath(manifest, name) {
|
|
54095
|
-
const sharedRoot =
|
|
54096
|
-
const resolved =
|
|
54097
|
-
const relative8 =
|
|
54098
|
-
if (relative8.startsWith("..") ||
|
|
53708
|
+
const sharedRoot = path61.resolve(manifest.artifactsRoot, "shared");
|
|
53709
|
+
const resolved = path61.resolve(sharedRoot, safeSharedName(name));
|
|
53710
|
+
const relative8 = path61.relative(sharedRoot, resolved);
|
|
53711
|
+
if (relative8.startsWith("..") || path61.isAbsolute(relative8)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
54099
53712
|
return resolved;
|
|
54100
53713
|
}
|
|
54101
53714
|
function tryParseJson(text) {
|
|
@@ -54110,7 +53723,7 @@ function listTaskArtifacts(manifest, taskId) {
|
|
|
54110
53723
|
const produced = manifest.artifacts.filter((a) => a.producer === taskId);
|
|
54111
53724
|
if (produced.length === 0) return void 0;
|
|
54112
53725
|
return produced.map((a) => {
|
|
54113
|
-
const relative8 =
|
|
53726
|
+
const relative8 = path61.relative(manifest.artifactsRoot, a.path);
|
|
54114
53727
|
return relative8.startsWith("..") ? a.path : relative8;
|
|
54115
53728
|
});
|
|
54116
53729
|
}
|
|
@@ -54168,7 +53781,7 @@ function pruneSharedReads(reads, dependencies, artifactsRoot) {
|
|
|
54168
53781
|
for (const artifact of produced) {
|
|
54169
53782
|
if (typeof artifact !== "string") continue;
|
|
54170
53783
|
fileEdits.push({
|
|
54171
|
-
target:
|
|
53784
|
+
target: path61.resolve(artifactsRoot, artifact),
|
|
54172
53785
|
index: reads.length + depIndex
|
|
54173
53786
|
});
|
|
54174
53787
|
}
|
|
@@ -54221,7 +53834,7 @@ function collectDependencyOutputContext(manifest, tasks, task, step) {
|
|
|
54221
53834
|
const filePath = sharedPath(manifest, name);
|
|
54222
53835
|
const teePath = teePathForArtifact(manifest.artifactsRoot, task.id, name);
|
|
54223
53836
|
const teeResult = readIfSmallWithTee(filePath, {
|
|
54224
|
-
baseDir:
|
|
53837
|
+
baseDir: path61.resolve(manifest.artifactsRoot, "shared"),
|
|
54225
53838
|
tee: { fullOutputPath: teePath }
|
|
54226
53839
|
});
|
|
54227
53840
|
if (teeResult === void 0) return { name, path: filePath, content: "" };
|
|
@@ -54733,6 +54346,502 @@ var init_retry_executor = __esm({
|
|
|
54733
54346
|
}
|
|
54734
54347
|
});
|
|
54735
54348
|
|
|
54349
|
+
// src/runtime/task-runner/output-splitter.ts
|
|
54350
|
+
function splitCoalescedOutput(rawOutput, taskIds) {
|
|
54351
|
+
if (taskIds.length === 0) return [];
|
|
54352
|
+
if (taskIds.length === 1) {
|
|
54353
|
+
return [{ taskId: taskIds[0], text: rawOutput, strategy: "delimiter" }];
|
|
54354
|
+
}
|
|
54355
|
+
const byId = /* @__PURE__ */ new Map();
|
|
54356
|
+
const delimiterHits = /* @__PURE__ */ new Set();
|
|
54357
|
+
const delimiterRegex = /<<<TASK_RESULT:([^\s>]+)>>>([\s\S]*?)<<<END_TASK_RESULT>>>/g;
|
|
54358
|
+
let match;
|
|
54359
|
+
while ((match = delimiterRegex.exec(rawOutput)) !== null) {
|
|
54360
|
+
const id = match[1];
|
|
54361
|
+
const body = match[2].trim();
|
|
54362
|
+
if (taskIds.includes(id) && !byId.has(id)) {
|
|
54363
|
+
byId.set(id, body);
|
|
54364
|
+
delimiterHits.add(id);
|
|
54365
|
+
}
|
|
54366
|
+
}
|
|
54367
|
+
if (delimiterHits.size === taskIds.length) {
|
|
54368
|
+
return taskIds.map((id) => ({
|
|
54369
|
+
taskId: id,
|
|
54370
|
+
text: byId.get(id) ?? "",
|
|
54371
|
+
strategy: "delimiter"
|
|
54372
|
+
}));
|
|
54373
|
+
}
|
|
54374
|
+
if (delimiterHits.size === 0) {
|
|
54375
|
+
const bySection = parseBySectionHeadings(rawOutput, taskIds);
|
|
54376
|
+
if (bySection.size === taskIds.length) {
|
|
54377
|
+
return taskIds.map((id) => ({
|
|
54378
|
+
taskId: id,
|
|
54379
|
+
text: bySection.get(id) ?? "",
|
|
54380
|
+
strategy: "section"
|
|
54381
|
+
}));
|
|
54382
|
+
}
|
|
54383
|
+
}
|
|
54384
|
+
return taskIds.map((id) => ({
|
|
54385
|
+
taskId: id,
|
|
54386
|
+
text: rawOutput,
|
|
54387
|
+
strategy: "broadcast"
|
|
54388
|
+
}));
|
|
54389
|
+
}
|
|
54390
|
+
function parseBySectionHeadings(rawOutput, taskIds) {
|
|
54391
|
+
const result4 = /* @__PURE__ */ new Map();
|
|
54392
|
+
const numberedRegex = /^(#{2,3})\s+Task\s+(\d+)\s+of\s+\d+.*$/gm;
|
|
54393
|
+
const sections = [];
|
|
54394
|
+
let m;
|
|
54395
|
+
while ((m = numberedRegex.exec(rawOutput)) !== null) {
|
|
54396
|
+
sections.push({ num: Number(m[2]), start: m.index + m[0].length });
|
|
54397
|
+
}
|
|
54398
|
+
if (sections.length >= taskIds.length) {
|
|
54399
|
+
sections.sort((a, b) => a.start - b.start);
|
|
54400
|
+
for (let i2 = 0; i2 < sections.length && i2 < taskIds.length; i2 += 1) {
|
|
54401
|
+
const section2 = sections[i2];
|
|
54402
|
+
const end = sections[i2 + 1]?.start ?? rawOutput.length;
|
|
54403
|
+
const text = rawOutput.slice(section2.start, end).trim();
|
|
54404
|
+
const taskId = taskIds[i2];
|
|
54405
|
+
if (taskId) result4.set(taskId, text);
|
|
54406
|
+
}
|
|
54407
|
+
return result4;
|
|
54408
|
+
}
|
|
54409
|
+
const idHeaderRegex = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
54410
|
+
while ((m = idHeaderRegex.exec(rawOutput)) !== null) {
|
|
54411
|
+
const headerText = m[2].trim();
|
|
54412
|
+
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
54413
|
+
if (matchedId && !result4.has(matchedId)) {
|
|
54414
|
+
result4.set(matchedId, "");
|
|
54415
|
+
}
|
|
54416
|
+
}
|
|
54417
|
+
if (result4.size === taskIds.length) {
|
|
54418
|
+
const headerPositions = [];
|
|
54419
|
+
const idHeaderRegex2 = /^(#{2,3})\s+Task\s+([^\s].*?)$/gm;
|
|
54420
|
+
while ((m = idHeaderRegex2.exec(rawOutput)) !== null) {
|
|
54421
|
+
const headerText = m[2].trim();
|
|
54422
|
+
const matchedId = taskIds.find((id) => headerText.includes(id));
|
|
54423
|
+
if (matchedId) headerPositions.push({ taskId: matchedId, start: m.index + m[0].length });
|
|
54424
|
+
}
|
|
54425
|
+
headerPositions.sort((a, b) => a.start - b.start);
|
|
54426
|
+
for (let i2 = 0; i2 < headerPositions.length; i2 += 1) {
|
|
54427
|
+
const cur = headerPositions[i2];
|
|
54428
|
+
const next = headerPositions[i2 + 1];
|
|
54429
|
+
const end = next ? rawOutput.lastIndexOf("\n#", next.start) : rawOutput.length;
|
|
54430
|
+
const text = rawOutput.slice(cur.start, end > 0 ? end : rawOutput.length).trim();
|
|
54431
|
+
result4.set(cur.taskId, text);
|
|
54432
|
+
}
|
|
54433
|
+
}
|
|
54434
|
+
return result4;
|
|
54435
|
+
}
|
|
54436
|
+
var init_output_splitter = __esm({
|
|
54437
|
+
"src/runtime/task-runner/output-splitter.ts"() {
|
|
54438
|
+
"use strict";
|
|
54439
|
+
}
|
|
54440
|
+
});
|
|
54441
|
+
|
|
54442
|
+
// src/runtime/team-runner-artifacts.ts
|
|
54443
|
+
function mergeArtifacts(items) {
|
|
54444
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
54445
|
+
for (const item of items) byPath.set(item.path, item);
|
|
54446
|
+
return [...byPath.values()];
|
|
54447
|
+
}
|
|
54448
|
+
var init_team_runner_artifacts = __esm({
|
|
54449
|
+
"src/runtime/team-runner-artifacts.ts"() {
|
|
54450
|
+
"use strict";
|
|
54451
|
+
}
|
|
54452
|
+
});
|
|
54453
|
+
|
|
54454
|
+
// src/runtime/workspace-tree.ts
|
|
54455
|
+
import * as fs74 from "node:fs/promises";
|
|
54456
|
+
import * as path62 from "node:path";
|
|
54457
|
+
function formatBytes2(bytes) {
|
|
54458
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
54459
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
54460
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
54461
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
54462
|
+
}
|
|
54463
|
+
function formatAge(seconds2) {
|
|
54464
|
+
if (seconds2 < 60) return "just now";
|
|
54465
|
+
if (seconds2 < 3600) return `${Math.floor(seconds2 / 60)}m`;
|
|
54466
|
+
if (seconds2 < 86400) return `${Math.floor(seconds2 / 3600)}h`;
|
|
54467
|
+
if (seconds2 < 86400 * 14) return `${Math.floor(seconds2 / 86400)}d`;
|
|
54468
|
+
return `${Math.floor(seconds2 / (86400 * 7))}w`;
|
|
54469
|
+
}
|
|
54470
|
+
function compareByRecency(a, b) {
|
|
54471
|
+
const diff = b.mtimeMs - a.mtimeMs;
|
|
54472
|
+
if (diff !== 0) return diff;
|
|
54473
|
+
return a.name.localeCompare(b.name);
|
|
54474
|
+
}
|
|
54475
|
+
function applyDirLimit(children, limit) {
|
|
54476
|
+
if (children.length <= limit) {
|
|
54477
|
+
return { visible: children, dropped: 0 };
|
|
54478
|
+
}
|
|
54479
|
+
if (limit <= 1) {
|
|
54480
|
+
return {
|
|
54481
|
+
visible: children.slice(0, limit),
|
|
54482
|
+
dropped: children.length - limit
|
|
54483
|
+
};
|
|
54484
|
+
}
|
|
54485
|
+
const recent = children.slice(0, limit - 1);
|
|
54486
|
+
const oldest = children[children.length - 1];
|
|
54487
|
+
const visible = oldest ? [...recent, oldest] : recent;
|
|
54488
|
+
return { visible, dropped: children.length - limit };
|
|
54489
|
+
}
|
|
54490
|
+
async function readChildren(rootPath, parent, excludedDirs) {
|
|
54491
|
+
const dirPath = parent.relativePath ? path62.join(rootPath, parent.relativePath) : rootPath;
|
|
54492
|
+
let names;
|
|
54493
|
+
try {
|
|
54494
|
+
names = await fs74.readdir(dirPath);
|
|
54495
|
+
} catch {
|
|
54496
|
+
return [];
|
|
54497
|
+
}
|
|
54498
|
+
const nodes = await Promise.all(
|
|
54499
|
+
names.map(async (name) => {
|
|
54500
|
+
if (name.startsWith(".")) return null;
|
|
54501
|
+
const relativePath = parent.relativePath ? `${parent.relativePath}/${name}` : name;
|
|
54502
|
+
const absolutePath = path62.join(rootPath, relativePath);
|
|
54503
|
+
try {
|
|
54504
|
+
const stat2 = await fs74.stat(absolutePath);
|
|
54505
|
+
if (stat2.isDirectory() && excludedDirs.has(name)) return null;
|
|
54506
|
+
return {
|
|
54507
|
+
name,
|
|
54508
|
+
relativePath,
|
|
54509
|
+
depth: parent.depth + 1,
|
|
54510
|
+
isDirectory: stat2.isDirectory(),
|
|
54511
|
+
mtimeMs: stat2.mtimeMs,
|
|
54512
|
+
size: stat2.size,
|
|
54513
|
+
children: [],
|
|
54514
|
+
droppedChildCount: 0
|
|
54515
|
+
};
|
|
54516
|
+
} catch {
|
|
54517
|
+
return null;
|
|
54518
|
+
}
|
|
54519
|
+
})
|
|
54520
|
+
);
|
|
54521
|
+
return nodes.filter((n) => n !== null).sort(compareByRecency);
|
|
54522
|
+
}
|
|
54523
|
+
async function collectTree(rootPath, maxDepth, dirLimit, excludedDirs) {
|
|
54524
|
+
const rootStat = await fs74.stat(rootPath);
|
|
54525
|
+
const root = {
|
|
54526
|
+
name: ".",
|
|
54527
|
+
relativePath: "",
|
|
54528
|
+
depth: 0,
|
|
54529
|
+
isDirectory: true,
|
|
54530
|
+
mtimeMs: rootStat.mtimeMs,
|
|
54531
|
+
size: rootStat.size,
|
|
54532
|
+
children: [],
|
|
54533
|
+
droppedChildCount: 0
|
|
54534
|
+
};
|
|
54535
|
+
let truncated = false;
|
|
54536
|
+
const queue = [root];
|
|
54537
|
+
let cursor = 0;
|
|
54538
|
+
while (cursor < queue.length) {
|
|
54539
|
+
const parent = queue[cursor];
|
|
54540
|
+
cursor += 1;
|
|
54541
|
+
if (!parent || parent.depth >= maxDepth) continue;
|
|
54542
|
+
const children = await readChildren(rootPath, parent, excludedDirs);
|
|
54543
|
+
const { visible, dropped } = applyDirLimit(children, dirLimit);
|
|
54544
|
+
parent.children = visible;
|
|
54545
|
+
parent.droppedChildCount = dropped;
|
|
54546
|
+
if (dropped > 0) truncated = true;
|
|
54547
|
+
for (const child of visible) {
|
|
54548
|
+
if (child.isDirectory) queue.push(child);
|
|
54549
|
+
}
|
|
54550
|
+
}
|
|
54551
|
+
return { root, truncated };
|
|
54552
|
+
}
|
|
54553
|
+
function collectLines(node, nowMs3, lines) {
|
|
54554
|
+
if (node.depth === 0) {
|
|
54555
|
+
lines.push({ text: ".", depth: 0, isRoot: true });
|
|
54556
|
+
} else {
|
|
54557
|
+
const indent = " ".repeat(node.depth);
|
|
54558
|
+
const suffix = node.isDirectory ? "/" : "";
|
|
54559
|
+
const label = `${indent}- ${node.name}${suffix}`;
|
|
54560
|
+
if (node.isDirectory) {
|
|
54561
|
+
lines.push({ text: label, depth: node.depth, isRoot: false });
|
|
54562
|
+
} else {
|
|
54563
|
+
const ageSeconds = Math.max(0, Math.floor((nowMs3 - node.mtimeMs) / 1e3));
|
|
54564
|
+
const size = formatBytes2(node.size);
|
|
54565
|
+
const age = formatAge(ageSeconds);
|
|
54566
|
+
lines.push({
|
|
54567
|
+
text: `${label} ${size} ${age}`,
|
|
54568
|
+
depth: node.depth,
|
|
54569
|
+
isRoot: false
|
|
54570
|
+
});
|
|
54571
|
+
}
|
|
54572
|
+
}
|
|
54573
|
+
if (node.droppedChildCount > 0) {
|
|
54574
|
+
const recentChildren = node.children.slice(0, -1);
|
|
54575
|
+
const oldestChild = node.children[node.children.length - 1];
|
|
54576
|
+
for (const child of recentChildren) collectLines(child, nowMs3, lines);
|
|
54577
|
+
const childDepth = node.depth + 1;
|
|
54578
|
+
const indent = " ".repeat(childDepth);
|
|
54579
|
+
lines.push({
|
|
54580
|
+
text: `${indent}- \u2026 ${node.droppedChildCount} more`,
|
|
54581
|
+
depth: childDepth,
|
|
54582
|
+
isRoot: false
|
|
54583
|
+
});
|
|
54584
|
+
if (oldestChild) collectLines(oldestChild, nowMs3, lines);
|
|
54585
|
+
} else {
|
|
54586
|
+
for (const child of node.children) collectLines(child, nowMs3, lines);
|
|
54587
|
+
}
|
|
54588
|
+
}
|
|
54589
|
+
function applyLineCap(lines, cap) {
|
|
54590
|
+
if (lines.length <= cap) return { lines, elided: 0 };
|
|
54591
|
+
const target = Math.max(1, cap - 1);
|
|
54592
|
+
const removeCount = lines.length - target;
|
|
54593
|
+
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);
|
|
54594
|
+
if (removable.length === 0) return { lines, elided: 0 };
|
|
54595
|
+
const removedIndexes = new Set(removable.map((item) => item.index));
|
|
54596
|
+
const kept = lines.filter((_, index) => !removedIndexes.has(index));
|
|
54597
|
+
kept.push({
|
|
54598
|
+
text: `\u2026 (${removable.length} lines elided)`,
|
|
54599
|
+
depth: 0,
|
|
54600
|
+
isRoot: false
|
|
54601
|
+
});
|
|
54602
|
+
return { lines: kept, elided: removable.length };
|
|
54603
|
+
}
|
|
54604
|
+
function treeCacheKey(cwd, options) {
|
|
54605
|
+
return `${path62.resolve(cwd)}|${options?.maxDepth ?? ""}|${options?.dirLimit ?? ""}|${options?.lineCap ?? ""}`;
|
|
54606
|
+
}
|
|
54607
|
+
async function buildWorkspaceTree(cwd, options) {
|
|
54608
|
+
const rootPath = path62.resolve(cwd);
|
|
54609
|
+
const cacheKey2 = treeCacheKey(cwd, options);
|
|
54610
|
+
const cached = treeCache.get(cacheKey2);
|
|
54611
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
54612
|
+
return cached.tree;
|
|
54613
|
+
}
|
|
54614
|
+
try {
|
|
54615
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
54616
|
+
const dirLimit = options?.dirLimit ?? DEFAULT_DIR_LIMIT;
|
|
54617
|
+
const lineCap = options?.lineCap ?? DEFAULT_LINE_CAP;
|
|
54618
|
+
const excludedDirs = options?.excludedDirs ?? DEFAULT_EXCLUDED_DIRS;
|
|
54619
|
+
const { root, truncated: dirTruncated } = await collectTree(rootPath, maxDepth, dirLimit, excludedDirs);
|
|
54620
|
+
const nowMs3 = Date.now();
|
|
54621
|
+
const lines = [];
|
|
54622
|
+
collectLines(root, nowMs3, lines);
|
|
54623
|
+
const { lines: capped, elided } = applyLineCap(lines, lineCap);
|
|
54624
|
+
const rendered = capped.map((l) => l.text).join("\n");
|
|
54625
|
+
const result4 = {
|
|
54626
|
+
rootPath,
|
|
54627
|
+
rendered,
|
|
54628
|
+
truncated: dirTruncated || elided > 0,
|
|
54629
|
+
totalLines: capped.length
|
|
54630
|
+
};
|
|
54631
|
+
treeCache.set(cacheKey2, {
|
|
54632
|
+
tree: result4,
|
|
54633
|
+
expiresAt: Date.now() + TREE_CACHE_TTL_MS
|
|
54634
|
+
});
|
|
54635
|
+
return result4;
|
|
54636
|
+
} catch {
|
|
54637
|
+
return emptyResult(rootPath);
|
|
54638
|
+
}
|
|
54639
|
+
}
|
|
54640
|
+
var DEFAULT_MAX_DEPTH, DEFAULT_DIR_LIMIT, DEFAULT_LINE_CAP, DEFAULT_EXCLUDED_DIRS, emptyResult, TREE_CACHE_TTL_MS, treeCache;
|
|
54641
|
+
var init_workspace_tree = __esm({
|
|
54642
|
+
"src/runtime/workspace-tree.ts"() {
|
|
54643
|
+
"use strict";
|
|
54644
|
+
DEFAULT_MAX_DEPTH = 3;
|
|
54645
|
+
DEFAULT_DIR_LIMIT = 12;
|
|
54646
|
+
DEFAULT_LINE_CAP = 120;
|
|
54647
|
+
DEFAULT_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
54648
|
+
"node_modules",
|
|
54649
|
+
".git",
|
|
54650
|
+
".next",
|
|
54651
|
+
"dist",
|
|
54652
|
+
"build",
|
|
54653
|
+
"target",
|
|
54654
|
+
".venv",
|
|
54655
|
+
".cache",
|
|
54656
|
+
".turbo"
|
|
54657
|
+
]);
|
|
54658
|
+
emptyResult = (rootPath) => ({
|
|
54659
|
+
rootPath,
|
|
54660
|
+
rendered: "",
|
|
54661
|
+
truncated: false,
|
|
54662
|
+
totalLines: 0
|
|
54663
|
+
});
|
|
54664
|
+
TREE_CACHE_TTL_MS = 3e4;
|
|
54665
|
+
treeCache = /* @__PURE__ */ new Map();
|
|
54666
|
+
}
|
|
54667
|
+
});
|
|
54668
|
+
|
|
54669
|
+
// src/runtime/run-coalesced-task-group.ts
|
|
54670
|
+
async function runCoalescedTaskGroup(input) {
|
|
54671
|
+
const { manifest, groupTasks, step, agent, signal, executeWorkers } = input;
|
|
54672
|
+
const groupId = groupTasks.map((t2) => t2.id).join("+");
|
|
54673
|
+
const firstTask = groupTasks[0];
|
|
54674
|
+
const taskIds = groupTasks.map((t2) => t2.id);
|
|
54675
|
+
let updatedTasks = input.tasks.map((t2) => {
|
|
54676
|
+
if (taskIds.includes(t2.id) && t2.status !== "running") {
|
|
54677
|
+
return { ...t2, status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
54678
|
+
}
|
|
54679
|
+
return t2;
|
|
54680
|
+
});
|
|
54681
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54682
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
54683
|
+
type: "task.coalesced_dispatch_start",
|
|
54684
|
+
runId: manifest.runId,
|
|
54685
|
+
message: `Dispatching ${groupTasks.length} coalesced tasks in 1 worker (role=${firstTask.role}, cwd=${firstTask.cwd})`,
|
|
54686
|
+
data: { groupId, role: firstTask.role, cwd: firstTask.cwd, taskIds }
|
|
54687
|
+
});
|
|
54688
|
+
const combinedPrompt = await buildCoalescedPrompt(manifest, step, groupTasks, agent);
|
|
54689
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54690
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54691
|
+
return {
|
|
54692
|
+
...t2,
|
|
54693
|
+
heartbeat: t2.heartbeat ?? createWorkerHeartbeat(t2.id)
|
|
54694
|
+
};
|
|
54695
|
+
});
|
|
54696
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54697
|
+
let rawOutput = "";
|
|
54698
|
+
let success = false;
|
|
54699
|
+
if (!executeWorkers) {
|
|
54700
|
+
rawOutput = buildScaffoldOutput(groupTasks);
|
|
54701
|
+
success = true;
|
|
54702
|
+
} else {
|
|
54703
|
+
const heartbeatTimer = setInterval(() => {
|
|
54704
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
54705
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54706
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54707
|
+
return {
|
|
54708
|
+
...t2,
|
|
54709
|
+
heartbeat: touchWorkerHeartbeat(t2.heartbeat ?? createWorkerHeartbeat(t2.id), { alive: true })
|
|
54710
|
+
};
|
|
54711
|
+
});
|
|
54712
|
+
try {
|
|
54713
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54714
|
+
} catch {
|
|
54715
|
+
}
|
|
54716
|
+
}, 15e3);
|
|
54717
|
+
try {
|
|
54718
|
+
const result4 = await runChildPi({
|
|
54719
|
+
cwd: firstTask.cwd,
|
|
54720
|
+
task: combinedPrompt,
|
|
54721
|
+
agent,
|
|
54722
|
+
signal,
|
|
54723
|
+
excludeContextBash: true,
|
|
54724
|
+
maxTurns: 5,
|
|
54725
|
+
onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e)
|
|
54726
|
+
});
|
|
54727
|
+
rawOutput = result4.rawFinalText ?? result4.stdout ?? "";
|
|
54728
|
+
success = result4.exitStatus?.exitCode === 0;
|
|
54729
|
+
} catch (err2) {
|
|
54730
|
+
rawOutput = `Worker dispatch failed: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
54731
|
+
success = false;
|
|
54732
|
+
} finally {
|
|
54733
|
+
clearInterval(heartbeatTimer);
|
|
54734
|
+
}
|
|
54735
|
+
}
|
|
54736
|
+
const split = splitCoalescedOutput(rawOutput, taskIds);
|
|
54737
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
54738
|
+
const newArtifacts = [];
|
|
54739
|
+
updatedTasks = updatedTasks.map((t2) => {
|
|
54740
|
+
if (!taskIds.includes(t2.id)) return t2;
|
|
54741
|
+
const entry = split.find((s) => s.taskId === t2.id);
|
|
54742
|
+
const ok = success && Boolean(entry?.text);
|
|
54743
|
+
const text = entry?.text ?? rawOutput;
|
|
54744
|
+
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
54745
|
+
kind: "result",
|
|
54746
|
+
relativePath: `results/${t2.id}.txt`,
|
|
54747
|
+
content: text,
|
|
54748
|
+
producer: t2.id
|
|
54749
|
+
});
|
|
54750
|
+
newArtifacts.push(resultArtifact);
|
|
54751
|
+
return {
|
|
54752
|
+
...t2,
|
|
54753
|
+
status: ok ? "completed" : "failed",
|
|
54754
|
+
finishedAt,
|
|
54755
|
+
result: {
|
|
54756
|
+
text,
|
|
54757
|
+
producer: groupId,
|
|
54758
|
+
strategy: entry?.strategy ?? "broadcast"
|
|
54759
|
+
},
|
|
54760
|
+
resultArtifact
|
|
54761
|
+
};
|
|
54762
|
+
});
|
|
54763
|
+
saveRunTasks(manifest, updatedTasks);
|
|
54764
|
+
let updatedManifest = {
|
|
54765
|
+
...manifest,
|
|
54766
|
+
artifacts: mergeArtifacts([...manifest.artifacts, ...newArtifacts])
|
|
54767
|
+
};
|
|
54768
|
+
if (success) {
|
|
54769
|
+
updatedManifest = updateRunStatus(updatedManifest, "running");
|
|
54770
|
+
}
|
|
54771
|
+
await appendEventAsync(updatedManifest.eventsPath, {
|
|
54772
|
+
type: "task.coalesced_dispatch_end",
|
|
54773
|
+
runId: manifest.runId,
|
|
54774
|
+
message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
|
|
54775
|
+
data: { groupId, taskIds, success, strategy: split[0]?.strategy }
|
|
54776
|
+
});
|
|
54777
|
+
return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
|
|
54778
|
+
}
|
|
54779
|
+
async function buildCoalescedPrompt(manifest, step, groupTasks, agent) {
|
|
54780
|
+
const tree = await buildWorkspaceTree(groupTasks[0].cwd);
|
|
54781
|
+
const treeBlock = tree.rendered ? `# Workspace Structure
|
|
54782
|
+
${tree.rendered}` : "";
|
|
54783
|
+
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.` : "";
|
|
54784
|
+
const taskBlocks = groupTasks.map((task, idx) => {
|
|
54785
|
+
return [
|
|
54786
|
+
`### Task ${idx + 1} of ${groupTasks.length} (id: ${task.id})`,
|
|
54787
|
+
`Step: ${step.id}`,
|
|
54788
|
+
`Role: ${step.role}`,
|
|
54789
|
+
`Task: ${step.task.replaceAll("{goal}", manifest.goal)}`
|
|
54790
|
+
].join("\n");
|
|
54791
|
+
}).join("\n\n---\n\n");
|
|
54792
|
+
const outputInstructions = groupTasks.map((task) => `<<<TASK_RESULT:${task.id}>>>`).join(" ... ");
|
|
54793
|
+
return [
|
|
54794
|
+
"# pi-crew Coalesced Worker Prompt",
|
|
54795
|
+
`Run ID: ${manifest.runId}`,
|
|
54796
|
+
`Team: ${manifest.team}`,
|
|
54797
|
+
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
54798
|
+
`Goal: ${manifest.goal}`,
|
|
54799
|
+
`Tasks in this batch: ${groupTasks.length}`,
|
|
54800
|
+
``,
|
|
54801
|
+
roleInstructions,
|
|
54802
|
+
``,
|
|
54803
|
+
treeBlock,
|
|
54804
|
+
``,
|
|
54805
|
+
`# Your Tasks`,
|
|
54806
|
+
`Complete ALL ${groupTasks.length} tasks below. For each, structure your final output using the delimiters shown.`,
|
|
54807
|
+
``,
|
|
54808
|
+
taskBlocks,
|
|
54809
|
+
``,
|
|
54810
|
+
`# Output Format (CRITICAL)`,
|
|
54811
|
+
`After completing all tasks, structure your final output using these delimiters:`,
|
|
54812
|
+
``,
|
|
54813
|
+
outputInstructions,
|
|
54814
|
+
``,
|
|
54815
|
+
`Wrap each task's result between the start and end delimiters:`,
|
|
54816
|
+
`<<<TASK_RESULT:{taskId}>>>`,
|
|
54817
|
+
`...your result for this task...`,
|
|
54818
|
+
`<<<END_TASK_RESULT>>>`,
|
|
54819
|
+
``,
|
|
54820
|
+
`If delimiters don't fit your workflow, use \`### Task N of M\` headings and we'll parse those instead.`
|
|
54821
|
+
].filter(Boolean).join("\n");
|
|
54822
|
+
}
|
|
54823
|
+
function buildScaffoldOutput(groupTasks) {
|
|
54824
|
+
return groupTasks.map(
|
|
54825
|
+
(task, idx) => `<<<TASK_RESULT:${task.id}>>>
|
|
54826
|
+
Scaffold result for task ${idx + 1} of ${groupTasks.length}: ${task.id}
|
|
54827
|
+
<<<END_TASK_RESULT>>>`
|
|
54828
|
+
).join("\n\n");
|
|
54829
|
+
}
|
|
54830
|
+
var init_run_coalesced_task_group = __esm({
|
|
54831
|
+
"src/runtime/run-coalesced-task-group.ts"() {
|
|
54832
|
+
"use strict";
|
|
54833
|
+
init_artifact_store();
|
|
54834
|
+
init_event_log();
|
|
54835
|
+
init_state_store();
|
|
54836
|
+
init_child_pi();
|
|
54837
|
+
init_role_permission();
|
|
54838
|
+
init_output_splitter();
|
|
54839
|
+
init_team_runner_artifacts();
|
|
54840
|
+
init_worker_heartbeat();
|
|
54841
|
+
init_workspace_tree();
|
|
54842
|
+
}
|
|
54843
|
+
});
|
|
54844
|
+
|
|
54736
54845
|
// src/runtime/runtime-policy.ts
|
|
54737
54846
|
function resolveTaskRuntimeKind(globalKind, role, isolationPolicy, env = process.env) {
|
|
54738
54847
|
if (globalKind === "scaffold") return "scaffold";
|
|
@@ -55719,7 +55828,7 @@ function hashToBase36(content, length) {
|
|
|
55719
55828
|
}
|
|
55720
55829
|
function calculateAdaptiveLength(existingCount, config = DEFAULT_CONFIG) {
|
|
55721
55830
|
for (let length = config.minLength; length <= config.maxLength; length++) {
|
|
55722
|
-
const totalPossibilities =
|
|
55831
|
+
const totalPossibilities = 36 ** length;
|
|
55723
55832
|
const probability = 1 - Math.exp(-(existingCount * existingCount) / (2 * totalPossibilities));
|
|
55724
55833
|
if (probability <= config.maxCollisionProbability) {
|
|
55725
55834
|
return length;
|
|
@@ -56812,7 +56921,8 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56812
56921
|
} : updated;
|
|
56813
56922
|
try {
|
|
56814
56923
|
return withRunLockSync(manifest, () => {
|
|
56815
|
-
|
|
56924
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
56925
|
+
flushPendingAtomicWrites();
|
|
56816
56926
|
const latest = loadRunManifestById(manifest.cwd, manifest.runId)?.tasks ?? fallbackTasks;
|
|
56817
56927
|
merged = updateTask(latest, taskWithCheckpoint);
|
|
56818
56928
|
let currentMtime;
|
|
@@ -56823,7 +56933,7 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56823
56933
|
}
|
|
56824
56934
|
if (currentMtime !== baseMtime) {
|
|
56825
56935
|
baseMtime = currentMtime;
|
|
56826
|
-
continue
|
|
56936
|
+
continue;
|
|
56827
56937
|
}
|
|
56828
56938
|
let recheckMtime;
|
|
56829
56939
|
try {
|
|
@@ -56833,7 +56943,7 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56833
56943
|
}
|
|
56834
56944
|
if (recheckMtime !== baseMtime) {
|
|
56835
56945
|
baseMtime = recheckMtime;
|
|
56836
|
-
continue
|
|
56946
|
+
continue;
|
|
56837
56947
|
}
|
|
56838
56948
|
let preWriteMtime;
|
|
56839
56949
|
try {
|
|
@@ -56843,16 +56953,16 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
56843
56953
|
}
|
|
56844
56954
|
if (preWriteMtime !== baseMtime) {
|
|
56845
56955
|
baseMtime = preWriteMtime;
|
|
56846
|
-
continue
|
|
56956
|
+
continue;
|
|
56847
56957
|
}
|
|
56848
|
-
break
|
|
56958
|
+
break;
|
|
56849
56959
|
}
|
|
56850
56960
|
if (merged === void 0) {
|
|
56851
56961
|
logInternalError("persistSingleTaskUpdate", new Error("failed to converge after 50 attempts"));
|
|
56852
56962
|
throw new Error("persistSingleTaskUpdate: failed to converge after 50 attempts");
|
|
56853
56963
|
}
|
|
56854
56964
|
try {
|
|
56855
|
-
|
|
56965
|
+
saveRunTasksCoalesced(manifest, merged);
|
|
56856
56966
|
} catch (err2) {
|
|
56857
56967
|
logInternalError("persistSingleTaskUpdate", err2);
|
|
56858
56968
|
throw err2;
|
|
@@ -56884,6 +56994,7 @@ function checkpointTask(manifest, tasks, task, phase, childPid) {
|
|
|
56884
56994
|
var init_state_helpers = __esm({
|
|
56885
56995
|
"src/runtime/task-runner/state-helpers.ts"() {
|
|
56886
56996
|
"use strict";
|
|
56997
|
+
init_atomic_write();
|
|
56887
56998
|
init_locks();
|
|
56888
56999
|
init_state_store();
|
|
56889
57000
|
init_internal_error();
|
|
@@ -60494,10 +60605,8 @@ var init_team_runner = __esm({
|
|
|
60494
60605
|
init_usage();
|
|
60495
60606
|
init_internal_error();
|
|
60496
60607
|
init_branch_freshness();
|
|
60497
|
-
init_team_runner_artifacts();
|
|
60498
60608
|
init_cancellation();
|
|
60499
60609
|
init_coalesce_tasks();
|
|
60500
|
-
init_run_coalesced_task_group();
|
|
60501
60610
|
init_concurrency();
|
|
60502
60611
|
init_crew_agent_records();
|
|
60503
60612
|
init_crew_hooks();
|
|
@@ -60512,6 +60621,7 @@ var init_team_runner = __esm({
|
|
|
60512
60621
|
init_recovery_recipes();
|
|
60513
60622
|
init_retry_executor();
|
|
60514
60623
|
init_role_permission();
|
|
60624
|
+
init_run_coalesced_task_group();
|
|
60515
60625
|
init_run_tracker();
|
|
60516
60626
|
init_runtime_policy();
|
|
60517
60627
|
init_task_display();
|
|
@@ -60519,6 +60629,7 @@ var init_team_runner = __esm({
|
|
|
60519
60629
|
init_task_graph_scheduler();
|
|
60520
60630
|
init_task_output_context();
|
|
60521
60631
|
init_task_runner();
|
|
60632
|
+
init_team_runner_artifacts();
|
|
60522
60633
|
init_usage_tracker();
|
|
60523
60634
|
init_workflow_state();
|
|
60524
60635
|
init_adaptive_plan();
|
|
@@ -68516,7 +68627,6 @@ function tryScanJson(text) {
|
|
|
68516
68627
|
try {
|
|
68517
68628
|
return JSON.parse(candidate);
|
|
68518
68629
|
} catch {
|
|
68519
|
-
continue;
|
|
68520
68630
|
}
|
|
68521
68631
|
}
|
|
68522
68632
|
return void 0;
|
|
@@ -68672,7 +68782,7 @@ function makeWorkflowCtx(manifest, opts) {
|
|
|
68672
68782
|
const semaphore = new Semaphore(concurrency);
|
|
68673
68783
|
let finalResult;
|
|
68674
68784
|
let agentCount = opts.resumedState ? opts.resumedState.agentCount : 0;
|
|
68675
|
-
|
|
68785
|
+
const phaseState = opts.resumedState ? {
|
|
68676
68786
|
currentPhase: opts.resumedState.currentPhase,
|
|
68677
68787
|
phases: [...opts.resumedState.phases]
|
|
68678
68788
|
} : { currentPhase: void 0, phases: [] };
|
|
@@ -69443,6 +69553,48 @@ ${tail}` : ""}`;
|
|
|
69443
69553
|
}, 3e3);
|
|
69444
69554
|
timer.unref();
|
|
69445
69555
|
}
|
|
69556
|
+
function resolveAnalysisText(params, cwd) {
|
|
69557
|
+
const hasInline = typeof params.analysis === "string" && params.analysis.length > 0;
|
|
69558
|
+
const hasPath = typeof params.analysisPath === "string" && params.analysisPath.length > 0;
|
|
69559
|
+
if (hasInline && hasPath) {
|
|
69560
|
+
return {
|
|
69561
|
+
error: "`analysis` and `analysisPath` are mutually exclusive. Set exactly one.",
|
|
69562
|
+
source: "none"
|
|
69563
|
+
};
|
|
69564
|
+
}
|
|
69565
|
+
if (!hasInline && !hasPath) return { source: "none" };
|
|
69566
|
+
if (hasPath) {
|
|
69567
|
+
let resolved;
|
|
69568
|
+
try {
|
|
69569
|
+
resolved = resolveContainedPath(cwd, params.analysisPath);
|
|
69570
|
+
} catch {
|
|
69571
|
+
return {
|
|
69572
|
+
error: `analysisPath must be within project directory: ${params.analysisPath}`,
|
|
69573
|
+
source: "none"
|
|
69574
|
+
};
|
|
69575
|
+
}
|
|
69576
|
+
if (!fs90.existsSync(resolved)) {
|
|
69577
|
+
return {
|
|
69578
|
+
error: `Analysis file not found: ${resolved}`,
|
|
69579
|
+
source: "none"
|
|
69580
|
+
};
|
|
69581
|
+
}
|
|
69582
|
+
const { size } = fs90.statSync(resolved);
|
|
69583
|
+
if (size > MAX_ANALYSIS_BYTES) {
|
|
69584
|
+
return {
|
|
69585
|
+
error: `Analysis file too large: ${size} bytes (max ${MAX_ANALYSIS_BYTES}). Trim the analysis or pass a summary inline.`,
|
|
69586
|
+
source: "none"
|
|
69587
|
+
};
|
|
69588
|
+
}
|
|
69589
|
+
const raw = fs90.readFileSync(resolved, "utf-8");
|
|
69590
|
+
const sanitized2 = sanitizeTaskText(raw);
|
|
69591
|
+
if (!sanitized2) return { source: "none" };
|
|
69592
|
+
return { text: sanitized2, source: "path" };
|
|
69593
|
+
}
|
|
69594
|
+
const sanitized = sanitizeTaskText(params.analysis);
|
|
69595
|
+
if (!sanitized) return { source: "none" };
|
|
69596
|
+
return { text: sanitized, source: "inline" };
|
|
69597
|
+
}
|
|
69446
69598
|
async function handleRun(params, ctx) {
|
|
69447
69599
|
if (params.chain) {
|
|
69448
69600
|
const { handleChainRun: handleChainRun2 } = await Promise.resolve().then(() => (init_chain_dispatch(), chain_dispatch_exports));
|
|
@@ -69526,11 +69678,14 @@ Commit or stash changes before using worktree mode, or use workspaceMode: 'singl
|
|
|
69526
69678
|
id: "01_agent",
|
|
69527
69679
|
role: params.role ?? "agent",
|
|
69528
69680
|
task: "{goal}",
|
|
69529
|
-
model: params.model
|
|
69681
|
+
model: params.model,
|
|
69682
|
+
reads: params.analysis || params.analysisPath ? ["analysis.md"] : void 0
|
|
69530
69683
|
}
|
|
69531
69684
|
]
|
|
69532
69685
|
} : workflows.find((item) => item.name === workflowName);
|
|
69533
69686
|
if (!baseWorkflow) return result(`Workflow '${workflowName}' not found.`, { action: "run", status: "error" }, true);
|
|
69687
|
+
const analysisParam = resolveAnalysisText(params, resolvedCtx.cwd);
|
|
69688
|
+
if (analysisParam.error) return result(analysisParam.error, { action: "run", status: "error" }, true);
|
|
69534
69689
|
const { expandParallelResearchWorkflow: expandParallelResearch } = await Promise.resolve().then(() => (init_parallel_research(), parallel_research_exports));
|
|
69535
69690
|
const workflow = directAgent ? baseWorkflow : expandParallelResearch(baseWorkflow, resolvedCtx.cwd);
|
|
69536
69691
|
if (!directAgent) {
|
|
@@ -69548,6 +69703,11 @@ Commit or stash changes before using worktree mode, or use workspaceMode: 'singl
|
|
|
69548
69703
|
if (!directAgent && workflow.source === "builtin" && isGoalWrapEnabled(resolvedCtx.cwd, workflow.name)) {
|
|
69549
69704
|
const decision2 = shouldGoalWrap(resolvedCtx.cwd, workflow);
|
|
69550
69705
|
if (decision2.enabled) {
|
|
69706
|
+
if (analysisParam.text) {
|
|
69707
|
+
console.warn(
|
|
69708
|
+
`[team-tool.run] analysis param is ignored by goal-wrapped run (workflow=${workflow.name}). The analysis artifact will not be written.`
|
|
69709
|
+
);
|
|
69710
|
+
}
|
|
69551
69711
|
return await startGoalWrappedRun(params, ctx, workflow, goal);
|
|
69552
69712
|
}
|
|
69553
69713
|
if (decision2.message) {
|
|
@@ -69623,10 +69783,19 @@ Commit or stash changes before using worktree mode, or use workspaceMode: 'singl
|
|
|
69623
69783
|
`,
|
|
69624
69784
|
producer: "team-tool"
|
|
69625
69785
|
});
|
|
69786
|
+
const analysisArtifacts = analysisParam.text ? [
|
|
69787
|
+
writeArtifact(paths.artifactsRoot, {
|
|
69788
|
+
kind: "prompt",
|
|
69789
|
+
relativePath: "shared/analysis.md",
|
|
69790
|
+
content: `${analysisParam.text}
|
|
69791
|
+
`,
|
|
69792
|
+
producer: "team-tool"
|
|
69793
|
+
})
|
|
69794
|
+
] : [];
|
|
69626
69795
|
const updatedManifest = {
|
|
69627
69796
|
...manifest,
|
|
69628
69797
|
...skillOverride !== void 0 ? { skillOverride } : {},
|
|
69629
|
-
artifacts: [goalArtifact],
|
|
69798
|
+
artifacts: [goalArtifact, ...analysisArtifacts],
|
|
69630
69799
|
summary: "Run manifest created; worker execution is not implemented yet."
|
|
69631
69800
|
};
|
|
69632
69801
|
atomicWriteJson(paths.manifestPath, updatedManifest);
|
|
@@ -70140,13 +70309,14 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
70140
70309
|
executed.manifest.status === "failed"
|
|
70141
70310
|
);
|
|
70142
70311
|
}
|
|
70143
|
-
var _cachedExecuteTeamRun, crewInitPromise;
|
|
70312
|
+
var _cachedExecuteTeamRun, crewInitPromise, MAX_ANALYSIS_BYTES;
|
|
70144
70313
|
var init_run = __esm({
|
|
70145
70314
|
"src/extension/team-tool/run.ts"() {
|
|
70146
70315
|
"use strict";
|
|
70147
70316
|
init_discover_agents();
|
|
70148
70317
|
init_config();
|
|
70149
70318
|
init_pipeline_runner();
|
|
70319
|
+
init_task_packet();
|
|
70150
70320
|
init_active_run_registry();
|
|
70151
70321
|
init_artifact_store();
|
|
70152
70322
|
init_atomic_write();
|
|
@@ -70166,6 +70336,7 @@ var init_run = __esm({
|
|
|
70166
70336
|
init_config_patch();
|
|
70167
70337
|
init_context();
|
|
70168
70338
|
init_goal_wrap();
|
|
70339
|
+
MAX_ANALYSIS_BYTES = 1e5;
|
|
70169
70340
|
}
|
|
70170
70341
|
});
|
|
70171
70342
|
|